diff --git a/.gitattributes b/.gitattributes
deleted file mode 100644
index 2c9ff103c..000000000
--- a/.gitattributes
+++ /dev/null
@@ -1,9 +0,0 @@
-# Avoids changelog conflicts by assuming additions-only, which is by far the common case.
-# In the rare case where branch b1 rebases against branch b2 and both branches
-# modified the same changelog entry, you'll end up with that changelog entry
-# 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/FUNDING.yml b/.github/FUNDING.yml
deleted file mode 100644
index dc771dd31..000000000
--- a/.github/FUNDING.yml
+++ /dev/null
@@ -1,12 +0,0 @@
-# These are supported funding model platforms
-
-github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
-patreon: 'araq' # Replace with a single Patreon username
-open_collective: 'nim' # Replace with a single Open Collective username
-ko_fi: # Replace with a single Ko-fi username
-tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
-community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
-liberapay: # Replace with a single Liberapay username
-issuehunt: # Replace with a single IssueHunt username
-otechie: # Replace with a single Otechie username
-custom: 'https://nim-lang.org/donate.html' # Replace with a single custom sponsorship URL
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.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/stale.yml b/.github/stale.yml
deleted file mode 100644
index c175f64a0..000000000
--- a/.github/stale.yml
+++ /dev/null
@@ -1,69 +0,0 @@
-# Configuration for probot-stale - https://github.com/probot/stale
-
-# Number of days of inactivity before an Issue or Pull Request becomes stale
-daysUntilStale: 365
-
-# Number of days of inactivity before an Issue or Pull Request with the stale label is closed.
-# Set to false to disable. If disabled, issues still need to be closed manually, but will remain marked as stale.
-daysUntilClose: 30
-
-# Only issues or pull requests with all of these labels are check if stale. Defaults to `[]` (disabled)
-onlyLabels: []
-
-# Issues or Pull Requests with these labels will never be considered stale. Set to `[]` to disable
-exemptLabels:
- - ARC
- - bounty
- - Codegen
- - Crash
- - Generics
- - High Priority
- - Macros
- - Next release
- - Showstopper
- - Static[T]
-
-# Set to true to ignore issues in a project (defaults to false)
-exemptProjects: false
-
-# Set to true to ignore issues in a milestone (defaults to false)
-exemptMilestones: false
-
-# Set to true to ignore issues with an assignee (defaults to false)
-exemptAssignees: false
-
-# Label to use when marking as stale
-staleLabel: stale
-
-# Comment to post when marking as stale. Set to `false` to disable
-markComment: >
- This pull request has been automatically marked as stale because it has not had
- recent activity.
- If you think it is still a valid PR, please rebase it on the latest devel;
- otherwise it will be closed. Thank you for your contributions.
-
-# Comment to post when removing the stale label.
-# unmarkComment: >
-# Your comment here.
-
-# Comment to post when closing a stale Issue or Pull Request.
-# closeComment: >
-# Your comment here.
-
-# Limit the number of actions per hour, from 1-30. Default is 30
-limitPerRun: 20
-
-# Limit to only `issues` or `pulls`
-only: pulls
-
-# Optionally, specify configuration settings that are specific to just 'issues' or 'pulls':
-# pulls:
-# daysUntilStale: 30
-# markComment: >
-# This pull request has been automatically marked as stale because it has not had
-# recent activity. It will be closed if no further activity occurs. Thank you
-# for your contributions.
-
-# issues:
-# exemptLabels:
-# - confirmed
diff --git a/.github/workflows/ci_docs.yml b/.github/workflows/ci_docs.yml
deleted file mode 100644
index cde30e5fa..000000000
--- a/.github/workflows/ci_docs.yml
+++ /dev/null
@@ -1,118 +0,0 @@
-name: Nim Docs CI
-on:
- push:
- paths:
- - 'compiler/docgen.nim'
- - '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'
-
- pull_request:
- # Run only on changes on these files.
- paths:
- - 'compiler/docgen.nim'
- - '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:
- strategy:
- fail-fast: false
- matrix:
- target: [linux, windows, osx]
- include:
- - target: linux
- os: ubuntu-20.04
- - target: windows
- os: windows-2019
- - target: osx
- os: macos-11
-
- name: ${{ matrix.target }}
- runs-on: ${{ matrix.os }}
- timeout-minutes: 60 # refs bug #18178
-
- steps:
- - name: 'Checkout'
- uses: actions/checkout@v3
- with:
- fetch-depth: 2
-
- - name: 'Install build dependencies (macOS)'
- if: runner.os == 'macOS'
- run: brew install make
-
- - name: 'Install build dependencies (Windows)'
- if: runner.os == 'Windows'
- shell: bash
- run: |
- set -e
- . ci/funs.sh
- nimInternalInstallDepsWindows
- 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 (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'
- 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.
-
- - name: 'Build csourcesAny (windows)'
- if: runner.os == 'Windows'
- shell: cmd
- run: ci/build_autogen.bat
-
- - name: 'Build koch'
- shell: bash
- run: nim c koch
-
- - name: 'Build the real compiler'
- shell: bash
- run: ./koch boot -d:release
-
- - name: 'Build documentation'
- shell: bash
- run: ./koch doc --git.commit:devel
-
- - name: 'Publish documentation to Github Pages'
- if: |
- github.event_name == 'push' && github.ref == 'refs/heads/devel' &&
- matrix.target == 'linux'
- uses: crazy-max/ghaction-github-pages@v3
- with:
- build_dir: doc/html
- env:
- GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
diff --git a/.github/workflows/ci_packages.yml b/.github/workflows/ci_packages.yml
deleted file mode 100644
index 0bec4cc21..000000000
--- a/.github/workflows/ci_packages.yml
+++ /dev/null
@@ -1,74 +0,0 @@
-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
-
-jobs:
- build:
- strategy:
- fail-fast: false
- matrix:
- os: [ubuntu-20.04, macos-11]
- cpu: [amd64]
- batch: ["allowed_failures", "0_3", "1_3", "2_3"] # list of `index_num`
- name: '${{ matrix.os }} (batch: ${{ matrix.batch }})'
- runs-on: ${{ matrix.os }}
- timeout-minutes: 60 # refs bug #18178
- env:
- NIM_TEST_PACKAGES: "1"
- NIM_TESTAMENT_BATCH: ${{ matrix.batch }}
- 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: 'koch, Run CI'
- shell: bash
- run: . ci/funs.sh && nimInternalBuildKochAndRunCI
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/.gitignore b/.gitignore
index d0c4558df..e4cec0ef6 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,39 +1,27 @@
*
!**/
!*.*
-
-# Cache
-nimcache*/
-rnimcache*/
-dnimcache*/
+nimcache/
*.o
!/icons/*.o
-*.obj
-*.ilk
-*.exp
-*.pdb
-*.lib
-*.dll
*.exe
*.so
*.dylib
*.zip
*.iss
*.log
-*.pdb
mapping.txt
tags
install.sh
deinstall.sh
-doc/html/
doc/*.html
-doc/pdf
+doc/*.pdf
doc/*.idx
/web/upload
-/build/*
+build/*
bin/*
# iOS specific wildcards.
@@ -43,70 +31,19 @@ bin/*
*.perspectivev3
*.swp
.DS_Store
-.tags
project.xcworkspace/
xcuserdata/
# Generated files.
/compile.json
-/compiler/nim.dot
+/compiler/nimrod.dot
/reject.json
/run.json
-/tools/dochack/dochack.js
-*.json
-/pkgstemp/**/*
-# for `nim doc foo.nim`
-/*.html
-lib/**/*.html
-#/testresults.html #covered by /*.html
-
+/testresults.html
/testresults.json
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
# Private directories and files (IDEs)
.*/
~*
-
-# testament cruft; TODO: generate these in a gitignore'd dir (./build) 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
-
-
-/outputExpected.txt
-/outputGotten.txt
-/t15148.txt
-/tests/vm/tfile_rw.txt
-
-/lib/pure/*.js
-
-!/.builds/
-
-!/.github
-
-# ignore debug dirs generated by dsymutil on OSX
-*.dSYM
-
-# for `nim c -r nimdoc/tester` etc; this can be in multiple places
-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
deleted file mode 100644
index 31908d223..000000000
--- a/.gitlab-ci.yml
+++ /dev/null
@@ -1,62 +0,0 @@
-# xxx unused, out of date
-
-image: ubuntu:18.04
-
-stages:
- - pre-build
- - build
- - deploy
- - test
-
-.linux_set_path: &linux_set_path_def
- before_script:
- - export PATH=$(pwd)/bin${PATH:+:$PATH}
- tags:
- - linux
-
-.windows_set_path: &win_set_path_def
- before_script:
- - set PATH=%CD%\bin;%PATH%
- tags:
- - windows
-
-
-build-windows:
- stage: build
- script:
- - ci\build.bat
- artifacts:
- paths:
- - bin\nim.exe
- - bin\nimd.exe
- - compiler\nim.exe
- - koch.exe
- expire_in: 1 week
- tags:
- - windows
-
-deploy-windows:
- stage: deploy
- script:
- - koch.exe winrelease
- artifacts:
- paths:
- - build/*.exe
- - build/*.zip
- expire_in: 1 week
- tags:
- - windows
- - fast
-
-
-
-test-windows:
- stage: test
- <<: *win_set_path_def
- script:
- - call ci\deps.bat
- - nim c testament\tester
- - testament\tester.exe all
- tags:
- - windows
- - fast
diff --git a/.travis.yml b/.travis.yml
new file mode 100644
index 000000000..f8655f940
--- /dev/null
+++ b/.travis.yml
@@ -0,0 +1,36 @@
+sudo: false
+language: c
+os:
+ - linux
+addons:
+ apt:
+ packages:
+ - libcurl4-openssl-dev
+ - libsdl1.2-dev
+ - libgc-dev
+before_script:
+ - set -e
+ - wget http://flatassembler.net/fasm-1.71.39.tgz
+ - tar xvf fasm-1.71.39.tgz
+ - git clone --depth 1 https://github.com/nim-lang/csources.git
+ - cd csources
+ - sh build.sh
+ - cd ..
+ - sed -i -e 's,cc = gcc,cc = clang,' config/nim.cfg
+ - export PATH=$(pwd)/bin:$(pwd)/fasm:$PATH
+script:
+ - nim c koch
+ - ./koch boot
+ - ./koch boot -d:release
+ - nim e install_nimble.nims
+ - nim e tests/test_nimscript.nims
+ - nimble update
+ - nimble install zip
+ - nimble install opengl
+ - nimble install sdl1
+ - nimble install jester
+ - nimble install niminst
+ - nim c --taintMode:on tests/testament/tester
+ - tests/testament/tester --pedantic all
+ - ./koch csource
+ - ./koch xz
diff --git a/azure-pipelines.yml b/azure-pipelines.yml
deleted file mode 100644
index d034e3b9b..000000000
--- a/azure-pipelines.yml
+++ /dev/null
@@ -1,168 +0,0 @@
-trigger:
- branches:
- include:
- - 'devel'
- - 'version-*'
-pr:
- branches:
- include:
- - '*'
-
-variables:
-- name: skipci
- value: false
-
-jobs:
-- job: packages
-
- timeoutInMinutes: 90 # default `60` led to lots of cancelled jobs; use 0 for unlimited (may be undesirable)
-
- strategy:
- matrix:
- Linux_amd64:
- vmImage: 'ubuntu-20.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'
- CPU: i386
- OSX_amd64:
- vmImage: 'macOS-11'
- CPU: amd64
- OSX_amd64_cpp:
- vmImage: 'macOS-11'
- CPU: amd64
- NIM_COMPILE_TO_CPP: true
- Windows_amd64_batch0_3:
- vmImage: 'windows-2019'
- CPU: amd64
- # see also: `NIM_TEST_PACKAGES`
- NIM_TESTAMENT_BATCH: "0_3"
- Windows_amd64_batch1_3:
- vmImage: 'windows-2019'
- CPU: amd64
- NIM_TESTAMENT_BATCH: "1_3"
- Windows_amd64_batch2_3:
- vmImage: 'windows-2019'
- CPU: amd64
- NIM_TESTAMENT_BATCH: "2_3"
-
- pool:
- vmImage: $(vmImage)
-
- workspace:
- clean: all
-
- 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'))
-
- - checkout: self
- fetchDepth: 2 # see D20210329T004830
-
- - bash: |
- set -e
- . ci/funs.sh
- if nimIsCiSkip; then
- echo '##vso[task.setvariable variable=skipci]true'
- fi
- displayName: 'Check whether to skip CI'
-
- - task: NodeTool@0
- inputs:
- versionSpec: '16.x'
- displayName: 'Install node.js 16.x'
- condition: and(succeeded(), eq(variables['skipci'], 'false'))
-
- - bash: |
- set -e
- . ci/funs.sh
- echo_run sudo apt-fast update -qq
- DEBIAN_FRONTEND='noninteractive' \
- echo_run 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'))
-
- - bash: |
- set -e
- . ci/funs.sh
- echo_run 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"
- # `: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 \
- 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
-
- echo_run chmod 755 bin/gcc
- echo_run 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'))
-
- - bash: brew install boehmgc make sfml
- displayName: 'Install dependencies (OSX)'
- condition: and(succeeded(), eq(variables['skipci'], 'false'), eq(variables['Agent.OS'], 'Darwin'))
-
- - bash: |
- set -e
- . ci/funs.sh
- nimInternalInstallDepsWindows
- echo_run 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'))
-
- - 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'))
- displayName: 'System information'
-
- - bash: . ci/funs.sh && nimBuildCsourcesIfNeeded CC=gcc ucpu=$(CPU)
- condition: and(succeeded(), eq(variables['skipci'], 'false'))
- displayName: 'Build csourcesAny'
-
- # 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'
-
- - 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'
- env:
- SYSTEM_ACCESSTOKEN: $(System.AccessToken)
diff --git a/bin/nim-gdb b/bin/nim-gdb
deleted file mode 100755
index d4d60e432..000000000
--- a/bin/nim-gdb
+++ /dev/null
@@ -1,25 +0,0 @@
-#!/usr/bin/env bash
-
-# Exit if anything fails
-set -e
-
-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
- 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"
-
-# Run GDB with the additional arguments that load the pretty printers
-# Set the environment variable `NIM_GDB` to overwrite the call to a
-# different/specific command (defaults to `gdb`).
-NIM_GDB="${NIM_GDB:-gdb}"
-# exec replaces the new process of bash with gdb. It is always good to
-# have fewer processes.
-exec "${NIM_GDB}" -eval-command="source $GDB_PYTHON_MODULE_PATH" "$@"
diff --git a/bin/nim-gdb.bat b/bin/nim-gdb.bat
deleted file mode 100644
index b5537dd9d..000000000
--- a/bin/nim-gdb.bat
+++ /dev/null
@@ -1,14 +0,0 @@
-@echo off
-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 @NIM_GDB=gdb.exe
-
-@echo source %@GDB_PYTHON_MODULE_PATH%> wingdbcommand.txt
-%@NIM_GDB% --command="wingdbcommand.txt" %*
-del wingdbcommand.txt /f /q
-
-EXIT /B %ERRORLEVEL%
-@echo on
diff --git a/bootstrap.sh b/bootstrap.sh
new file mode 100755
index 000000000..dd551b52d
--- /dev/null
+++ b/bootstrap.sh
@@ -0,0 +1,20 @@
+#!/bin/sh
+set -e
+set -x
+
+if [ ! -e csources/.git ]; then
+ git clone --depth 1 git://github.com/nim-lang/csources.git csources
+fi
+
+cd "csources"
+sh build.sh
+cd ".."
+
+./bin/nim c koch
+./koch boot -d:release
+./koch geninstall
+
+set +x
+
+echo
+echo 'Install Nim using "./install.sh
" or "sudo ./install.sh ".'
diff --git a/build_all.bat b/build_all.bat
deleted file mode 100644
index ef4a5f6a4..000000000
--- a/build_all.bat
+++ /dev/null
@@ -1,29 +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%
-)
- bin\nim.exe c --noNimblePath --skipUserCfg --skipParentCfg --hints:off koch
- koch boot -d:release --skipUserCfg --skipParentCfg --hints:off
- koch tools --skipUserCfg --skipParentCfg --hints:off
diff --git a/build_all.sh b/build_all.sh
deleted file mode 100755
index 83848f41a..000000000
--- a/build_all.sh
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/bin/sh
-# DO NO EDIT DIRECTLY! auto-generated by `nim r tools/ci_generate.nim`
-
-# 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 "$@"
-
-echo_run bin/nim c --noNimblePath --skipUserCfg --skipParentCfg --hints:off koch
-echo_run ./koch boot -d:release --skipUserCfg --skipParentCfg --hints:off
-echo_run ./koch tools --skipUserCfg --skipParentCfg --hints:off
-
diff --git a/changelog.md b/changelog.md
deleted file mode 100644
index 2db577d0b..000000000
--- a/changelog.md
+++ /dev/null
@@ -1,31 +0,0 @@
-# v2.2.0 - yyyy-mm-dd
-
-
-## Changes affecting backward compatibility
-
-
-## Standard library additions and changes
-
-[//]: # "Changes:"
-
-
-[//]: # "Additions:"
-- Added `parseutils.parseSize` - inverse to `strutils.formatSize` - to parse human readable sizes.
-
-[//]: # "Deprecations:"
-
-
-[//]: # "Removals:"
-
-
-## Language changes
-
-
-
-## Compiler changes
-
-
-
-
-## Tool changes
-
diff --git a/changelogs/changelog_0_18_1.md b/changelogs/changelog_0_18_1.md
deleted file mode 100644
index d0b7fd8ee..000000000
--- a/changelogs/changelog_0_18_1.md
+++ /dev/null
@@ -1,663 +0,0 @@
-## v0.18.0 - 01/03/2018
-
-### Changes affecting backwards compatibility
-
-#### Breaking changes in the standard library
-
-- The ``[]`` proc for strings now raises an ``IndexError`` exception when
- the specified slice is out of bounds. See issue
- [#6223](https://github.com/nim-lang/Nim/issues/6223) for more details.
- You can use ``substr(str, start, finish)`` to get the old behaviour back,
- see [this commit](https://github.com/nim-lang/nimbot/commit/98cc031a27ea89947daa7f0bb536bcf86462941f) for an example.
-
-- ``strutils.split`` and ``strutils.rsplit`` with an empty string and a
- separator now returns that empty string.
- See issue [#4377](https://github.com/nim-lang/Nim/issues/4377).
-
-- Arrays of char cannot be converted to ``cstring`` anymore, pointers to
- arrays of char can! This means ``$`` for arrays can finally exist
- in ``system.nim`` and do the right thing. This means ``$myArrayOfChar`` changed
- its behaviour! Compile with ``-d:nimNoArrayToString`` to see where to fix your
- code.
-
-- `reExtended` is no longer default for the `re` constructor in the `re`
- module.
-
-- The behavior of ``$`` has been changed for all standard library collections. The
- collection-to-string implementations now perform proper quoting and escaping of
- strings and chars.
-
-- `newAsyncSocket` taking an `AsyncFD` now runs `setBlocking(false)` on the
- fd.
-
-- ``mod`` and bitwise ``and`` do not produce ``range`` subtypes anymore. This
- turned out to be more harmful than helpful and the language is simpler
- without this special typing rule.
-
-- ``formatFloat``/``formatBiggestFloat`` now support formatting floats with zero
- precision digits. The previous ``precision = 0`` behavior (default formatting)
- is now available via ``precision = -1``.
-
-- Moved from stdlib into Nimble packages:
- - [``basic2d``](https://github.com/nim-lang/basic2d)
- _deprecated: use ``glm``, ``arraymancer``, ``neo``, or another package instead_
- - [``basic3d``](https://github.com/nim-lang/basic3d)
- _deprecated: use ``glm``, ``arraymancer``, ``neo``, or another package instead_
- - [``gentabs``](https://github.com/lcrees/gentabs)
- - [``libuv``](https://github.com/lcrees/libuv)
- - [``numeric``](https://github.com/lcrees/polynumeric)
- - [``poly``](https://github.com/lcrees/polynumeric)
- - [``pdcurses``](https://github.com/lcrees/pdcurses)
- - [``romans``](https://github.com/lcrees/romans)
- - [``libsvm``](https://github.com/nim-lang/libsvm_legacy)
- - [``joyent_http_parser``](https://github.com/nim-lang/joyent_http_parser)
-
-- Proc [toCountTable](https://nim-lang.org/docs/tables.html#toCountTable,openArray[A])
- now produces a `CountTable` with values correspoding to the number of occurrences
- of the key in the input. It used to produce a table with all values set to `1`.
-
- Counting occurrences in a sequence used to be:
-
- ```nim
- let mySeq = @[1, 2, 1, 3, 1, 4]
- var myCounter = initCountTable[int]()
-
- for item in mySeq:
- myCounter.inc item
- ```
-
- Now, you can simply do:
-
- ```nim
- let
- mySeq = @[1, 2, 1, 3, 1, 4]
- myCounter = mySeq.toCountTable()
- ```
-
-- If you use ``--dynlibOverride:ssl`` with OpenSSL 1.0.x, you now have to
- define ``openssl10`` symbol (``-d:openssl10``). By default OpenSSL 1.1.x is
- assumed.
-
-- ``newNativeSocket`` is now named ``createNativeSocket``.
-
-- ``newAsyncNativeSocket`` is now named ``createAsyncNativeSocket``
- and it no longer raises an OS error but returns an ``osInvalidSocket`` when
- creation fails.
-
-- The ``securehash`` module is now deprecated. Instead import ``std / sha1``.
-
-- The ``readPasswordFromStdin`` proc has been moved from the ``rdstdin``
- to the ``terminal`` module, thus it does not depend on linenoise anymore.
-
-#### Breaking changes in the compiler
-
-- ``\n`` is now only the single line feed character like in most
- other programming languages. The new platform specific newline escape sequence is
- written as ``\p``. This change only affects the Windows platform.
-
-- The overloading rules changed slightly so that constrained generics are
- preferred over unconstrained generics. (Bug #6526)
-
-- We changed how array accesses "from backwards" like ``a[^1]`` or ``a[0..^1]`` are
- implemented. These are now implemented purely in ``system.nim`` without compiler
- support. There is a new "heterogeneous" slice type ``system.HSlice`` that takes 2
- generic parameters which can be ``BackwardsIndex`` indices. ``BackwardsIndex`` is
- produced by ``system.^``.
- This means if you overload ``[]`` or ``[]=`` you need to ensure they also work
- with ``system.BackwardsIndex`` (if applicable for the accessors).
-
-- The parsing rules of ``if`` expressions were changed so that multiple
- statements are allowed in the branches. We found few code examples that
- now fail because of this change, but here is one:
-
-```nim
-t[ti] = if exp_negative: '-' else: '+'; inc(ti)
-```
-
-This now needs to be written as:
-
-```nim
-t[ti] = (if exp_negative: '-' else: '+'); inc(ti)
-```
-
-- The experimental overloading of the dot ``.`` operators now take
- an ``untyped``` parameter as the field name, it used to be
- a ``static[string]``. You can use ``when defined(nimNewDot)`` to make
- your code work with both old and new Nim versions.
- See [special-operators](https://nim-lang.org/docs/manual.html#special-operators)
- for more information.
-
-- ``yield`` (or ``await`` which is mapped to ``yield``) never worked reliably
- in an array, seq or object constructor and is now prevented at compile-time.
-
-### Library additions
-
-- **Added ``sequtils.mapLiterals`` for easier construction of array and tuple literals.**
-
-- Added ``system.runnableExamples`` to make examples in Nim's documentation easier
- to write and test. The examples are tested as the last step of
- ``nim doc``.
-
-- Implemented ``getIoHandler`` proc in the ``asyncdispatch`` module that allows
- you to retrieve the underlying IO Completion Port or ``Selector[AsyncData]``
- object in the specified dispatcher.
-
-- For string formatting / interpolation a new module
- called [strformat](https://nim-lang.org/docs/strformat.html) has been added
- to the stdlib.
-
-- The `ReadyKey` type in the selectors module now contains an ``errorCode``
- field to help distinguish between ``Event.Error`` events.
-
-- Implemented an `accept` proc that works on a `SocketHandle` in
- ``nativesockets``.
-
-- Added ``algorithm.rotateLeft``.
-
-- Added ``typetraits.$`` as an alias for ``typetraits.name``.
-
-- Added ``system.getStackTraceEntries`` that allows you to access the stack
- trace in a structured manner without string parsing.
-
-- Added ``parseutils.parseSaturatedNatural``.
-
-- Added ``macros.unpackVarargs``.
-
-- Added support for asynchronous programming for the JavaScript backend using
- the `asyncjs` module.
-
-- Added true color support for some terminals. Example:
-```nim
-import colors, terminal
-
-const Nim = "Efficient and expressive programming."
-
-var
- fg = colYellow
- bg = colBlue
- int = 1.0
-
-enableTrueColors()
-
-for i in 1..15:
- styledEcho bgColor, bg, fgColor, fg, Nim, resetStyle
- int -= 0.01
- fg = intensity(fg, int)
-
-setForegroundColor colRed
-setBackgroundColor colGreen
-styledEcho "Red on Green.", resetStyle
-```
-
-### Library changes
-
-- ``echo`` now works with strings that contain ``\0`` (the binary zero is not
- shown) and ``nil`` strings are equal to empty strings.
-
-- JSON: Deprecated `getBVal`, `getFNum`, and `getNum` in favour of
- `getBool`, `getFloat`, `getBiggestInt`. A new `getInt` procedure was also
- added.
-
-- ``rationals.toRational`` now uses an algorithm based on continued fractions.
- This means its results are more precise and it can't run into an infinite loop
- anymore.
-
-- ``os.getEnv`` now takes an optional ``default`` parameter that tells ``getEnv``
- what to return if the environment variable does not exist.
-
-- The ``random`` procs in ``random.nim`` have all been deprecated. Instead use
- the new ``rand`` procs. The module now exports the state of the random
- number generator as type ``Rand`` so multiple threads can easily use their
- own random number generators that do not require locking. For more information
- about this rename see issue [#6934](https://github.com/nim-lang/Nim/issues/6934)
-
-- ``writeStackTrace`` is now proclaimed to have no IO effect (even though it does)
- so that it is more useful for debugging purposes.
-
-- ``db_mysql`` module: ``DbConn`` is now a ``distinct`` type that doesn't expose the
- details of the underlying ``PMySQL`` type.
-
-- ``parseopt2`` is now deprecated, use ``parseopt`` instead.
-
-### Language additions
-
-- It is now possible to forward declare object types so that mutually
- recursive types can be created across module boundaries. See
- [package level objects](https://nim-lang.org/docs/manual.html#package-level-objects)
- for more information.
-
-- Added support for casting between integers of same bitsize in VM (compile time and nimscript).
- This allows to, among other things, reinterpret signed integers as unsigned.
-
-- Custom pragmas are now supported using pragma ``pragma``, please see language
- manual for details.
-
-- Standard library modules can now also be imported via the ``std`` pseudo-directory.
- This is useful in order to distinguish between standard library and nimble package
- imports:
-
- ```nim
- import std / [strutils, os, osproc]
- import someNimblePackage / [strutils, os]
- ```
-
-### Language changes
-
-- The **unary** ``<`` is now deprecated, for ``.. <`` use ``..<`` for other usages
- use the ``pred`` proc.
-
-- Bodies of ``for`` loops now get their own scope:
-
-```nim
-# now compiles:
-for i in 0..4:
- let i = i + 1
- echo i
-```
-
-- To make Nim even more robust the system iterators ``..`` and ``countup``
- now only accept a single generic type ``T``. This means the following code
- doesn't die with an "out of range" error anymore:
-
-```nim
-var b = 5.Natural
-var a = -5
-for i in a..b:
- echo i
-```
-
-- ``atomic`` and ``generic`` are no longer keywords in Nim. ``generic`` used to be
- an alias for ``concept``, ``atomic`` was not used for anything.
-
-- The memory manager now uses a variant of the TLSF algorithm that has much
- better memory fragmentation behaviour. According
- to [http://www.gii.upv.es/tlsf/](http://www.gii.upv.es/tlsf/) the maximum
- fragmentation measured is lower than 25%. As a nice bonus ``alloc`` and
- ``dealloc`` became O(1) operations.
-
-- The compiler is now more consistent in its treatment of ambiguous symbols:
- Types that shadow procs and vice versa are marked as ambiguous (bug #6693).
-
-- codegenDecl pragma now works for the JavaScript backend. It returns an empty
- string for function return type placeholders.
-
-- Extra semantic checks for procs with noreturn pragma: return type is not allowed,
- statements after call to noreturn procs are no longer allowed.
-
-- Noreturn proc calls and raising exceptions branches are now skipped during common type
- deduction in ``if`` and ``case`` expressions. The following code snippets now compile:
- ```nim
- import strutils
- let str = "Y"
- let a = case str:
- of "Y": true
- of "N": false
- else: raise newException(ValueError, "Invalid boolean")
- let b = case str:
- of nil, "": raise newException(ValueError, "Invalid boolean")
- elif str.startsWith("Y"): true
- elif str.startsWith("N"): false
- else: false
- let c = if str == "Y": true
- elif str == "N": false
- else:
- echo "invalid bool"
- quit("this is the end")
- ```
-
-- Pragmas now support call syntax, for example: ``{.exportc"myname".}`` and
- ``{.exportc("myname").}``
-
-- The ``deprecated`` pragma now supports a user-definable warning message for procs.
-
- ```nim
- proc bar {.deprecated: "use foo instead".} =
- return
-
- bar()
- ```
-
-### Tool changes
-
-- The ``nim doc`` command is now an alias for ``nim doc2``, the second version of
- the documentation generator. The old version 1 can still be accessed
- via the new ``nim doc0`` command.
-
-- Nim's ``rst2html`` command now supports the testing of code snippets via an RST
- extension that we called ``:test:``::
-
- ```rst
- .. code-block:: nim
- :test:
- # shows how the 'if' statement works
- if true: echo "yes"
- ```
-
-### Compiler changes
-
-### Bugfixes
-
-- Fixed "ReraiseError when using try/except within finally block"
- ([#5871](https://github.com/nim-lang/Nim/issues/5871))
-- Fixed "Range type inference leads to counter-intuitive behvaiour"
- ([#5854](https://github.com/nim-lang/Nim/issues/5854))
-- Fixed "JSON % operator can fail in extern procs with dynamic types"
- ([#6385](https://github.com/nim-lang/Nim/issues/6385))
-- Fixed ""intVal is not accessible" in VM"
- ([#6083](https://github.com/nim-lang/Nim/issues/6083))
-- Fixed "Add excl for OrderedSet"
- ([#2467](https://github.com/nim-lang/Nim/issues/2467))
-- Fixed "newSeqOfCap actually doesn't reserve memory"
- ([#6403](https://github.com/nim-lang/Nim/issues/6403))
-- Fixed "[Regression] Nim segfaults"
- ([#6435](https://github.com/nim-lang/Nim/issues/6435))
-- Fixed "Seq assignment is slower than expected"
- ([#6433](https://github.com/nim-lang/Nim/issues/6433))
-- Fixed "json module issues with empty dicts and lists"
- ([#6438](https://github.com/nim-lang/Nim/issues/6438))
-- Fixed "mingw installed via finish.exe fails to link if Nim located in path with whitespace"
- ([#6452](https://github.com/nim-lang/Nim/issues/6452))
-- Fixed "unittest.check does not perform short-circuit evaluation"
- ([#5784](https://github.com/nim-lang/Nim/issues/5784))
-- Fixed "Error while concatenating an array of chars."
- ([#5861](https://github.com/nim-lang/Nim/issues/5861))
-- Fixed "range initialization: [ProveInit] hint: Cannot prove that"
- ([#6474](https://github.com/nim-lang/Nim/issues/6474))
-- Fixed "scanf can call procs with side-effects multiple times"
- ([#6487](https://github.com/nim-lang/Nim/issues/6487))
-- Fixed "gcsafe detection problem"
- ([#5620](https://github.com/nim-lang/Nim/issues/5620))
-- Fixed "C++ codegen: `mitems` generates invalid code."
- ([#4910](https://github.com/nim-lang/Nim/issues/4910))
-- Fixed "strange runtime behavior on macOS"
- ([#6496](https://github.com/nim-lang/Nim/issues/6496))
-- Fixed "stdtmpl: invalid indentation after a line ending in question mark"
- ([#5070](https://github.com/nim-lang/Nim/issues/5070))
-- Fixed "Windows: NAN troubles on c backend"
- ([#6511](https://github.com/nim-lang/Nim/issues/6511))
-- Fixed "lib/nim/system/cellsets.nim(33, 31) Error: type mismatch while attempting to compile for 16bit CPUs"
- ([#3558](https://github.com/nim-lang/Nim/issues/3558))
-- Fixed "Can't compile dynlib with ``-d:useNimRtl`` and ``--threads:on``"
- ([#5143](https://github.com/nim-lang/Nim/issues/5143))
-- Fixed "var s = @[0,1,2,...] can generate thousand of single assignments in C code"
- ([#5007](https://github.com/nim-lang/Nim/issues/5007))
-- Fixed "`echo` discards everything after a null character"
- ([#1137](https://github.com/nim-lang/Nim/issues/1137))
-- Fixed "Turn off reExtended by default"
- ([#5627](https://github.com/nim-lang/Nim/issues/5627))
-- Fixed "Bad Links in docs/backends.html"
- ([#5914](https://github.com/nim-lang/Nim/issues/5914))
-- Fixed "Index out of bounds error in db_postgres when executing non parameter-substituted queries containing "?""
- ([#6571](https://github.com/nim-lang/Nim/issues/6571))
-- Fixed "Please add pipe2 support to posix stdlib"
- ([#6553](https://github.com/nim-lang/Nim/issues/6553))
-- Fixed "Return semantics vary depending on return style"
- ([#6422](https://github.com/nim-lang/Nim/issues/6422))
-- Fixed "parsecsv.open reports SIGSEGV when calling 'open' on missing file"
- ([#6148](https://github.com/nim-lang/Nim/issues/6148))
-- Fixed "VCC: Nim generates non-compilable code for system.nim"
- ([#6606](https://github.com/nim-lang/Nim/issues/6606))
-- Fixed "Generic subtype matches worse than a generic"
- ([#6526](https://github.com/nim-lang/Nim/issues/6526))
-- Fixed "formatFloat inconsistent scientific notation"
- ([#6589](https://github.com/nim-lang/Nim/issues/6589))
-- Fixed "Generated c code calls function twice"
- ([#6292](https://github.com/nim-lang/Nim/issues/6292))
-- Fixed "Range type inference leads to counter-intuitive behvaiour"
- ([#5854](https://github.com/nim-lang/Nim/issues/5854))
-- Fixed "New backward indexing is too limited"
- ([#6631](https://github.com/nim-lang/Nim/issues/6631))
-- Fixed "Table usage in a macro (SIGSEGV: Illegal storage access.)"
- ([#1860](https://github.com/nim-lang/Nim/issues/1860))
-- Fixed "Incorrect deprecation error"
- ([#6634](https://github.com/nim-lang/Nim/issues/6634))
-- Fixed "Wrong indices in arrays not starting with 0"
- ([#6675](https://github.com/nim-lang/Nim/issues/6675))
-- Fixed "if expressions"
- ([#6609](https://github.com/nim-lang/Nim/issues/6609))
-- Fixed "BackwardsIndex: converter + `[]` + unrelated type[^1]: lib/system.nim(3536, 3) Error"
- ([#6692](https://github.com/nim-lang/Nim/issues/6692))
-- Fixed "BackwardsIndex: converter + `[]` + unrelated type[^1]: lib/system.nim(3536, 3) Error"
- ([#6692](https://github.com/nim-lang/Nim/issues/6692))
-- Fixed "js backend 0.17.3: array bounds check for non zero based arrays is buggy"
- ([#6532](https://github.com/nim-lang/Nim/issues/6532))
-- Fixed "HttpClient's new API doesn't work through a proxy for https URLs"
- ([#6685](https://github.com/nim-lang/Nim/issues/6685))
-- Fixed "isServing isn't declared and isn't compiling"
- ([#6707](https://github.com/nim-lang/Nim/issues/6707))
-- Fixed "[Regression] value out of range"
- ([#6710](https://github.com/nim-lang/Nim/issues/6710))
-
-- Fixed "Error when using `multisync` macro"
- ([#6708](https://github.com/nim-lang/Nim/issues/6708))
-
-- Fixed "formatFloat inconsistent scientific notation"
- ([#6589](https://github.com/nim-lang/Nim/issues/6589))
-- Fixed "Using : (constructor arguments) for passing values to functions with default arguments causes a compiler crash."
- ([#6765](https://github.com/nim-lang/Nim/issues/6765))
-- Fixed "In-place object initialization leads to vcc incompatible code"
- ([#6757](https://github.com/nim-lang/Nim/issues/6757))
-- Fixed "Improve parseCookies doc"
- ([#5721](https://github.com/nim-lang/Nim/issues/5721))
-- Fixed "Parser regression with nested do notation inside conditional"
- ([#6166](https://github.com/nim-lang/Nim/issues/6166))
-- Fixed "Request for better error message"
- ([#6776](https://github.com/nim-lang/Nim/issues/6776))
-- Fixed "Testament tester does not execute test with `exitcode` only"
- ([#6775](https://github.com/nim-lang/Nim/issues/6775))
-- Fixed "JS integer division off by one"
- ([#6753](https://github.com/nim-lang/Nim/issues/6753))
-- Fixed "Regression: cannot prove not nil"
- ([#5781](https://github.com/nim-lang/Nim/issues/5781))
-- Fixed "SIGSEGV: Illegal storage access. (Attempt to read from nil?) in generic proc"
- ([#6073](https://github.com/nim-lang/Nim/issues/6073))
-- Fixed "Request for better error message"
- ([#6776](https://github.com/nim-lang/Nim/issues/6776))
-- Fixed "Nim #head: sorting via reference hangs compiler"
- ([#6724](https://github.com/nim-lang/Nim/issues/6724))
-- Fixed "Cannot cast pointer to char in cpp"
- ([#5979](https://github.com/nim-lang/Nim/issues/5979))
-- Fixed "asynchttpserver replies with several errors on single request"
- ([#6386](https://github.com/nim-lang/Nim/issues/6386))
-- Fixed "object variants superclass trigger bad codegen"
- ([#5521](https://github.com/nim-lang/Nim/issues/5521))
-- Fixed "JS integer division off by one"
- ([#6753](https://github.com/nim-lang/Nim/issues/6753))
-- Fixed "js backend compiler crash with tables indexed by certain types"
- ([#6568](https://github.com/nim-lang/Nim/issues/6568))
-- Fixed "Jsgen bug with is"
- ([#6445](https://github.com/nim-lang/Nim/issues/6445))
-- Fixed "Subrange definition with ..<"
- ([#6788](https://github.com/nim-lang/Nim/issues/6788))
-- Fixed "fields not initialized: array with enum index type as object field."
- ([#6682](https://github.com/nim-lang/Nim/issues/6682))
-- Fixed "Can not delete data in table when table's data type is kind of "not nil""
- ([#6555](https://github.com/nim-lang/Nim/issues/6555))
-- Fixed "tables.nim: Cannot prove that 'n' is initialized"
- ([#6121](https://github.com/nim-lang/Nim/issues/6121))
-- Fixed "issues with 'not nil' applied to a closure proc"
- ([#6489](https://github.com/nim-lang/Nim/issues/6489))
-- Fixed "`not nil` not working in some cases"
- ([#4686](https://github.com/nim-lang/Nim/issues/4686))
-- Fixed "Cannot prove '@[v]' is not nil"
- ([#3993](https://github.com/nim-lang/Nim/issues/3993))
-
-- Fixed "Feature: support TCP_NODELAY in net.sockets"
- ([#6795](https://github.com/nim-lang/Nim/issues/6795))
-- Fixed "Code that makes the compiler throw an error message and then hangs"
- ([#6820](https://github.com/nim-lang/Nim/issues/6820))
-- Fixed "Code that makes the compiler throw an error message and then hangs"
- ([#6820](https://github.com/nim-lang/Nim/issues/6820))
-- Fixed "Inconsistent behavior with sequence and string slicing"
- ([#6223](https://github.com/nim-lang/Nim/issues/6223))
-- Fixed "Wrong behavior of "split" (proc and iterator)"
- ([#4377](https://github.com/nim-lang/Nim/issues/4377))
-- Fixed "[Documentation] Invalid module name: [foo, bar]"
- ([#6831](https://github.com/nim-lang/Nim/issues/6831))
-- Fixed "The destructor is not called for temporary objects"
- ([#4214](https://github.com/nim-lang/Nim/issues/4214))
-- Fixed "Destructors does not work with implicit items iterator in for loop"
- ([#985](https://github.com/nim-lang/Nim/issues/985))
-- Fixed "Error in template when using the type of the parameter inside it"
- ([#6756](https://github.com/nim-lang/Nim/issues/6756))
-- Fixed "should json.to() respect parent attributes?"
- ([#5856](https://github.com/nim-lang/Nim/issues/5856))
-- Fixed "json 'to' macro can not marshalize into tuples"
- ([#6095](https://github.com/nim-lang/Nim/issues/6095))
-- Fixed "json.to fails with seq[T]"
- ([#6604](https://github.com/nim-lang/Nim/issues/6604))
-- Fixed "json.to() is not worth using compared to marshal.to[T]"
- ([#5848](https://github.com/nim-lang/Nim/issues/5848))
-- Fixed "Memory not being released in time, running out of memory"
- ([#6031](https://github.com/nim-lang/Nim/issues/6031))
-- Fixed "[Regression] Bad C codegen for generic code"
- ([#6889](https://github.com/nim-lang/Nim/issues/6889))
-- Fixed "rollingFileLogger deletes file on every start."
- ([#6264](https://github.com/nim-lang/Nim/issues/6264))
-- Fixed "Remove/deprecate securehash module."
- ([#6033](https://github.com/nim-lang/Nim/issues/6033))
-- Fixed "[bug or not] object construction for seq[T] failed without space after colon"
- ([#5999](https://github.com/nim-lang/Nim/issues/5999))
-- Fixed "issues with the random module"
- ([#4726](https://github.com/nim-lang/Nim/issues/4726))
-- Fixed "Reassigning local var to seq of objects results in nil element in Object's seq field"
- ([#668](https://github.com/nim-lang/Nim/issues/668))
-- Fixed "Compilation error with "newseq[string]""
- ([#6726](https://github.com/nim-lang/Nim/issues/6726))
-- Fixed "await inside array/dict literal produces invalid code - Part 2"
- ([#6626](https://github.com/nim-lang/Nim/issues/6626))
-- Fixed "terminal.eraseline() gives OverflowError on Windows"
- ([#6931](https://github.com/nim-lang/Nim/issues/6931))
-- Fixed "[Regression] `sequtils.any` conflicts with `system.any`"
- ([#6932](https://github.com/nim-lang/Nim/issues/6932))
-- Fixed "C++ codegen: `mitems` generates invalid code."
- ([#4910](https://github.com/nim-lang/Nim/issues/4910))
-- Fixed "seq.mitems produces invalid cpp codegen"
- ([#6892](https://github.com/nim-lang/Nim/issues/6892))
-- Fixed "Concepts regression"
- ([#6108](https://github.com/nim-lang/Nim/issues/6108))
-- Fixed "Generic iterable concept with array crashes compiler"
- ([#6277](https://github.com/nim-lang/Nim/issues/6277))
-- Fixed "C code generation "‘a’ is a pointer; did you mean to use ‘->’?""
- ([#6462](https://github.com/nim-lang/Nim/issues/6462))
-- Fixed "`--NimblePath` fails if a `-` in path which is not followed by a number"
- ([#6949](https://github.com/nim-lang/Nim/issues/6949))
-- Fixed ""not registered in the selector" in asyncfile.close() for something that clearly was registered"
- ([#6906](https://github.com/nim-lang/Nim/issues/6906))
-- Fixed "strange frexp behavior"
- ([#6353](https://github.com/nim-lang/Nim/issues/6353))
-
-- Fixed "noreturn branches of case statements shouldn't contribute to type"
- ([#6885](https://github.com/nim-lang/Nim/issues/6885))
-- Fixed "Type inference for 'if' statements changed"
- ([#6980](https://github.com/nim-lang/Nim/issues/6980))
-- Fixed "newest asyncdispatch recursion"
- ([#6100](https://github.com/nim-lang/Nim/issues/6100))
-- Fixed "Ambiguous identifier between set type and proc"
- ([#6965](https://github.com/nim-lang/Nim/issues/6965))
-
-- Fixed "Inconsistent behavior with sequence and string slicing"
- ([#6223](https://github.com/nim-lang/Nim/issues/6223))
-
-- Fixed "Unsupported OpenSSL library imported dynamically"
- ([#5000](https://github.com/nim-lang/Nim/issues/5000))
-- Fixed "`nim check` segfaults"
- ([#6972](https://github.com/nim-lang/Nim/issues/6972))
-- Fixed "GC deadlock"
- ([#6988](https://github.com/nim-lang/Nim/issues/6988))
-- Fixed "Create a seq without memory initialization"
- ([#6401](https://github.com/nim-lang/Nim/issues/6401))
-- Fixed "Fix bug for getch on Windows while using the arrow keys"
- ([#6966](https://github.com/nim-lang/Nim/issues/6966))
-- Fixed "runnableExamples doesn't work in templates"
- ([#7018](https://github.com/nim-lang/Nim/issues/7018))
-- Fixed "runnableExamples doesn't work with untyped statement blocks"
- ([#7019](https://github.com/nim-lang/Nim/issues/7019))
-
-- Fixed "Critical bug in parseBiggestFloat"
- ([#7060](https://github.com/nim-lang/Nim/issues/7060))
-- Fixed "[RFC] strformat.% should be gone"
- ([#7078](https://github.com/nim-lang/Nim/issues/7078))
-- Fixed "compiler crash on simple macro"
- ([#7093](https://github.com/nim-lang/Nim/issues/7093))
-- Fixed "Make newlines sane again"
- ([#7089](https://github.com/nim-lang/Nim/issues/7089))
-- Fixed "JS - Unicode enum string representation issue"
- ([#6741](https://github.com/nim-lang/Nim/issues/6741))
-- Fixed "Strange behaviour of 0.17.3 (working ok in 0.17.2)"
- ([#6989](https://github.com/nim-lang/Nim/issues/6989))
-- Fixed "Strange behaviour of 0.17.3 (working ok in 0.17.2)"
- ([#6989](https://github.com/nim-lang/Nim/issues/6989))
-- Fixed "Compiler crash: try expression with infix as"
- ([#7116](https://github.com/nim-lang/Nim/issues/7116))
-- Fixed "nimsuggest crash"
- ([#7140](https://github.com/nim-lang/Nim/issues/7140))
-- Fixed "[RFC] Reintroduce readChar"
- ([#7072](https://github.com/nim-lang/Nim/issues/7072))
-- Fixed "Copyright line needs updating"
- ([#7129](https://github.com/nim-lang/Nim/issues/7129))
-- Fixed "-0.0 doesn't result in negative zero in VM"
- ([#7079](https://github.com/nim-lang/Nim/issues/7079))
-- Fixed "Windows large filesize"
- ([#7121](https://github.com/nim-lang/Nim/issues/7121))
-- Fixed "Securehash is not parsimonious with MD5 and other hash modules"
- ([#6961](https://github.com/nim-lang/Nim/issues/6961))
-- Fixed "os.findExe() shouldn't look in current directory on posix, unless exe has a /"
- ([#6939](https://github.com/nim-lang/Nim/issues/6939))
-- Fixed "`compiles(...)` with `fatal` pragma causes compiler to exit early"
- ([#7080](https://github.com/nim-lang/Nim/issues/7080))
-- Fixed "NPE when compile macro that returns concrete value"
- ([#5450](https://github.com/nim-lang/Nim/issues/5450))
-- Fixed "Using a variable of type `int | float` causes internal compiler error"
- ([#6946](https://github.com/nim-lang/Nim/issues/6946))
-- Fixed "Unsigned integers could not be used as array indexes."
- ([#7153](https://github.com/nim-lang/Nim/issues/7153))
-- Fixed "countdown with uint causes underflow"
- ([#4220](https://github.com/nim-lang/Nim/issues/4220))
-- Fixed "Inconsistent method call syntax"
- ([#7200](https://github.com/nim-lang/Nim/issues/7200))
-- Fixed "Impossible to create an empty const array"
- ([#6853](https://github.com/nim-lang/Nim/issues/6853))
-- Fixed "Strange UINT handling"
- ([#3985](https://github.com/nim-lang/Nim/issues/3985))
-- Fixed "Bad codegen when passing arg that is part of return value destination"
- ([#6960](https://github.com/nim-lang/Nim/issues/6960))
-- Fixed "No info about gcsafety in error message when global var is accessed in async proc"
- ([#6186](https://github.com/nim-lang/Nim/issues/6186))
-- Fixed "Resolving package vs. local import ambiguities"
- ([#2819](https://github.com/nim-lang/Nim/issues/2819))
-- Fixed "Internal error with type() operator"
- ([#3711](https://github.com/nim-lang/Nim/issues/3711))
-- Fixed "newAsyncSocket should raise an OS error plus other inconsistencies"
- ([#4995](https://github.com/nim-lang/Nim/issues/4995))
-- Fixed "mapLiterals fails with negative values"
- ([#7215](https://github.com/nim-lang/Nim/issues/7215))
-- Fixed "fmWrite doesn't truncate file with openAsync, unlike open()"
- ([#5531](https://github.com/nim-lang/Nim/issues/5531))
-- Fixed "Move libsvm to an external nimble module"
- ([#5786](https://github.com/nim-lang/Nim/issues/5786))
-- Fixed "Prevent acceptAddr gotcha with newSocket"
- ([#7227](https://github.com/nim-lang/Nim/issues/7227))
-- Fixed "strtabs.getOrDefault is inconsistent with tables.getOrDefault"
- ([#4265](https://github.com/nim-lang/Nim/issues/4265))
-
-- Fixed "Code falling through into exception handler when no exception thrown."
- ([#7232](https://github.com/nim-lang/Nim/issues/7232))
-- Fixed "the new generic inference rules are broken"
- ([#7247](https://github.com/nim-lang/Nim/issues/7247))
-- Fixed "Odd `..<` regression"
- ([#6992](https://github.com/nim-lang/Nim/issues/6992))
-- Fixed "Different proc type inferred from default parameter"
- ([#4659](https://github.com/nim-lang/Nim/issues/4659))
-- Fixed "Different proc type inferred from default parameter"
- ([#4659](https://github.com/nim-lang/Nim/issues/4659))
-- Fixed "Testament sometimes ignores test failures"
- ([#7236](https://github.com/nim-lang/Nim/issues/7236))
-- Fixed "New Allocator Fails On >=4GB Requests"
- ([#7120](https://github.com/nim-lang/Nim/issues/7120))
-- Fixed "User pragmas hide effect specifications from sempass2"
- ([#7216](https://github.com/nim-lang/Nim/issues/7216))
-- Fixed "C++: SIGABRT instead of IndexError for out-of-bounds"
- ([#6512](https://github.com/nim-lang/Nim/issues/6512))
-- Fixed "An uncaught exception in cpp mode doesn't show the exception name/msg"
- ([#6431](https://github.com/nim-lang/Nim/issues/6431))
diff --git a/changelogs/changelog_0_19_0.md b/changelogs/changelog_0_19_0.md
deleted file mode 100644
index 45cefe933..000000000
--- a/changelogs/changelog_0_19_0.md
+++ /dev/null
@@ -1,253 +0,0 @@
-## v0.19.X - XX/XX/2018
-
-### Changes affecting backwards compatibility
-
-- The stdlib module ``future`` has been renamed to ``sugar``.
-- ``macros.callsite`` is now deprecated. Since the introduction of ``varargs``
- parameters this became unnecessary.
-- Anonymous tuples with a single element can now be written as ``(1,)`` with a
- trailing comma. The underlying AST is ``nnkTupleConstr(newLit 1)`` for this
- example. ``nnkTupleConstr`` is a new node kind your macros need to be able
- to deal with!
-- Indexing into a ``cstring`` for the JS target is now mapped
- to ``charCodeAt``.
-- Assignments that would "slice" an object into its supertype are now prevented
- at runtime. Use ``ref object`` with inheritance rather than ``object`` with
- inheritance to prevent this issue.
-- The ``not nil`` type annotation now has to be enabled explicitly
- via ``{.experimental: "notnil".}`` as we are still not pleased with how this
- feature works with Nim's containers.
-- The parser now warns about inconsistent spacing around binary operators as
- these can easily be confused with unary operators. This warning will likely
- become an error in the future.
-- The ``'c`` and ``'C'`` suffix for octal literals is now deprecated to
- bring the language in line with the standard library (e.g. ``parseOct``).
-- The dot style for import paths (e.g ``import path.to.module`` instead of
- ``import path/to/module``) has been deprecated.
-
-#### Breaking changes in the standard library
-
-- ``re.split`` for empty regular expressions now yields every character in
- the string which is what other programming languages chose to do.
-- The returned tuple of ``system.instantiationInfo`` now has a third field
- containing the column of the instantiation.
-
-- ``cookies.setCookie`` no longer assumes UTC for the expiration date.
-- ``strutils.formatEng`` does not distinguish between ``nil`` and ``""``
- strings anymore for its ``unit`` parameter. Instead the space is controlled
- by a new parameter ``useUnitSpace``.
-
-- The ``times.parse`` and ``times.format`` procs have been rewritten.
- The proc signatures are the same so it should generally not break anything.
- However, the new implementation is a bit stricter, which is a breaking change.
- For example ``parse("2017-01-01 foo", "yyyy-MM-dd")`` will now raise an error.
-
-- ``proc `-`*(a, b: Time): int64`` in the ``times`` module has changed return type
- to ``times.Duration`` in order to support higher time resolutions.
- The proc is no longer deprecated.
-
-- The ``times.Timezone`` is now an immutable ref-type that must be initialized
- with an explicit constructor (``newTimezone``).
-
-- ``posix.Timeval.tv_sec`` has changed type to ``posix.Time``.
-
-- ``math.`mod` `` for floats now behaves the same as ``mod`` for integers
- (previously it used floor division like Python). Use ``math.floorMod`` for the old behavior.
-
-- For string inputs, ``unicode.isUpper`` and ``unicode.isLower`` now require a
- second mandatory parameter ``skipNonAlpha``.
-
-- For string inputs, ``strutils.isUpperAscii`` and ``strutils.isLowerAscii`` now
- require a second mandatory parameter ``skipNonAlpha``.
-
-- ``osLastError`` is now marked with ``sideEffect``
-- The procs ``parseHexInt`` and ``parseOctInt`` now fail on empty strings
- and strings containing only valid prefixes, e.g. "0x" for hex integers.
-
-- ``terminal.setCursorPos`` and ``terminal.setCursorXPos`` now work correctly
- with 0-based coordinates on POSIX (previously, you needed to use
- 1-based coordinates on POSIX for correct behaviour; the Windows behaviour
- was always correct).
-
-- ``lineInfoObj`` now returns absolute path instead of project path.
- It's used by ``lineInfo``, ``check``, ``expect``, ``require``, etc.
-
-- ``net.sendTo`` no longer returns an int and now raises an ``OSError``.
-- `threadpool`'s `await` and derivatives have been renamed to `blockUntil`
- to avoid confusions with `await` from the `async` macro.
-
-
-#### Breaking changes in the compiler
-
-- The undocumented ``#? braces`` parsing mode was removed.
-- The undocumented PHP backend was removed.
-- The default location of ``nimcache`` for the native code targets was
- changed. Read [the compiler user guide](https://nim-lang.org/docs/nimc.html#generated-c-code-directory)
- for more information.
-
-### Library additions
-
-- ``re.split`` now also supports the ``maxsplit`` parameter for consistency
- with ``strutils.split``.
-- Added ``system.toOpenArray`` in order to support zero-copy slicing
- operations. This is currently not yet available for the JavaScript target.
-- Added ``getCurrentDir``, ``findExe``, ``cpDir`` and ``mvDir`` procs to
- ``nimscript``.
-- The ``times`` module now supports up to nanosecond time resolution when available.
-- Added the type ``times.Duration`` for representing fixed durations of time.
-- Added the proc ``times.convert`` for converting between different time units,
- e.g days to seconds.
-- Added the proc ``algorithm.binarySearch[T, K]`` with the ```cmp``` parameter.
-- Added the proc ``algorithm.upperBound``.
-- Added inverse hyperbolic functions, ``math.arcsinh``, ``math.arccosh`` and ``math.arctanh`` procs.
-- Added cotangent, secant and cosecant procs ``math.cot``, ``math.sec`` and ``math.csc``; and their hyperbolic, inverse and inverse hyperbolic functions, ``math.coth``, ``math.sech``, ``math.csch``, ``math.arccot``, ``math.arcsec``, ``math.arccsc``, ``math.arccoth``, ``math.arcsech`` and ``math.arccsch`` procs.
-- Added the procs ``math.floorMod`` and ``math.floorDiv`` for floor based integer division.
-- Added the procs ``rationals.`div```, ``rationals.`mod```, ``rationals.floorDiv`` and ``rationals.floorMod`` for rationals.
-- Added the proc ``math.prod`` for product of elements in openArray.
-- Added the proc ``parseBinInt`` to parse a binary integer from a string, which returns the value.
-- ``parseOct`` and ``parseBin`` in parseutils now also support the ``maxLen`` argument similar to ``parseHexInt``.
-- Added the proc ``flush`` for memory mapped files.
-- Added the ``MemMapFileStream``.
-- Added a simple interpreting event parser template ``eventParser`` to the ``pegs`` module.
-- Added ``macros.copyLineInfo`` to copy lineInfo from other node.
-- Added ``system.ashr`` an arithmetic right shift for integers.
-
-### Library changes
-
-- ``macros.astGenRepr``, ``macros.lispRepr`` and ``macros.treeRepr``
- now escapes the content of string literals consistently.
-- ``macros.NimSym`` and ``macros.NimIdent`` is now deprecated in favor
- of the more general ``NimNode``.
-- ``macros.getImpl`` now includes the pragmas of types, instead of omitting them.
-- ``macros.hasCustomPragma`` and ``macros.getCustomPragmaVal`` now
- also support ``ref`` and ``ptr`` types, pragmas on types and variant
- fields.
-- ``system.SomeReal`` is now called ``SomeFloat`` for consistency and
- correctness.
-- ``algorithm.smartBinarySearch`` and ``algorithm.binarySearch`` is
- now joined in ``binarySearch``. ``smartbinarySearch`` is now
- deprecated.
-- The `terminal` module now exports additional procs for generating ANSI color
- codes as strings.
-- Added the parameter ``val`` for the ``CritBitTree[int].inc`` proc.
-- An exception raised from a ``test`` block of ``unittest`` now shows its type in
- error message.
-- The ``compiler/nimeval`` API was rewritten to simplify the "compiler as an
- API". Using the Nim compiler and its VM as a scripting engine has never been
- easier. See ``tests/compilerapi/tcompilerapi.nim`` for an example of how to
- use the Nim VM in a native Nim application.
-- Added the parameter ``val`` for the ``CritBitTree[T].incl`` proc.
-- The proc ``tgamma`` was renamed to ``gamma``. ``tgamma`` is deprecated.
-- The ``pegs`` module now exports getters for the fields of its ``Peg`` and ``NonTerminal``
- object types. ``Peg``s with child nodes now have the standard ``items`` and ``pairs``
- iterators.
-- The ``accept`` socket procedure defined in the ``net`` module can now accept
- a nil socket.
-
-### Language additions
-
-- Dot calls combined with explicit generic instantiations can now be written
- as ``x.y[:z]`` which is transformed into ``y[z](x)`` by the parser.
-- ``func`` is now an alias for ``proc {.noSideEffect.}``.
-- In order to make ``for`` loops and iterators more flexible to use Nim now
- supports so called "for-loop macros". See
- the [manual](manual.html#macros-for-loop-macros) for more details.
- This feature enables a Python-like generic ``enumerate`` implementation.
-
-- Case statements can now be rewritten via macros. See the [manual](manual.html#macros-case-statement-macros) for more information.
- This feature enables custom pattern matchers.
-
-
-- the `typedesc` special type has been renamed to just `type`.
-- `static` and `type` are now also modifiers similar to `ref` and `ptr`.
- They denote the special types `static[T]` and `type[T]`.
-- Forcing compile-time evaluation with `static` now supports specifying
- the desired target type (as a concrete type or as a type class)
-- The `type` operator now supports checking that the supplied expression
- matches an expected type constraint.
-
-### Language changes
-
-- The `importcpp` pragma now allows importing the listed fields of generic
- C++ types. Support for numeric parameters have also been added through
- the use of `static[T]` types.
- (#6415)
-
-- Native C++ exceptions can now be imported with `importcpp` pragma.
- Imported exceptions can be raised and caught just like Nim exceptions.
- More details in language manual.
-
-- ``nil`` for strings/seqs is finally gone. Instead the default value for
- these is ``"" / @[]``. Use ``--nilseqs:on`` for a transition period.
-
-- Accessing the binary zero terminator in Nim's native strings
- is now invalid. Internally a Nim string still has the trailing zero for
- zero-copy interoperability with ``cstring``. Compile your code with the
- new switch ``--laxStrings:on`` if you need a transition period.
-
-- The command syntax now supports keyword arguments after the first comma.
-
-- Thread-local variables can now be declared inside procs. This implies all
- the effects of the ``global`` pragma.
-
-- Nim now supports the ``except`` clause in the export statement.
-
-- Range float types, example ``range[0.0 .. Inf]``. More details in language manual.
-- The ``{.this.}`` pragma has been deprecated. It never worked within generics and
- we found the resulting code harder to read than the more explicit ``obj.field``
- syntax.
-- "Memory regions" for pointer types have been deprecated, they were hardly used
- anywhere. Note that this has **nothing** to do with the ``--gc:regions`` switch
- of managing memory.
-
-- The exception hierarchy was slightly reworked, ``SystemError`` was renamed to
- ``CatchableError`` and is the new base class for any exception that is guaranteed to
- be catchable. This change should have minimal impact on most existing Nim code.
-
-
-### Tool changes
-
-- ``jsondoc2`` has been renamed ``jsondoc``, similar to how ``doc2`` was renamed
- ``doc``. The old ``jsondoc`` can still be invoked with ``jsondoc0``.
-
-### Compiler changes
-
-- The VM's instruction count limit was raised to 3 million instructions in
- order to support more complex computations at compile-time.
-
-- Support for hot code reloading has been implemented for the JavaScript
- target. To use it, compile your code with `--hotCodeReloading:on` and use a
- helper library such as LiveReload or BrowserSync.
-
-- A new compiler option `--cppCompileToNamespace` puts the generated C++ code
- into the namespace "Nim" in order to avoid naming conflicts with existing
- C++ code. This is done for all Nim code - internal and exported.
-
-- Added ``macros.getProjectPath`` and ``os.putEnv`` procs to Nim's virtual
- machine.
-
-- The ``deadCodeElim`` option is now always turned on and the switch has no
- effect anymore, but is recognized for backwards compatibility.
-
-- ``experimental`` is now a pragma / command line switch that can enable specific
- language extensions, it is not an all-or-nothing switch anymore.
-
-- Nintendo Switch was added as a new platform target. See [the compiler user guide](https://nim-lang.org/docs/nimc.html)
- for more info.
-
-- macros.bindSym now capable to accepts not only literal string or string constant expression.
- bindSym enhancement make it also can accepts computed string or ident node inside macros /
- compile time functions / static blocks. Only in templates / regular code it retains it's old behavior.
- This new feature can be accessed via {.experimental: "dynamicBindSym".} pragma/switch.
-
-- On Posix systems the global system wide configuration is now put under ``/etc/nim/nim.cfg``,
- it used to be ``/etc/nim.cfg``. Usually it does not exist, however.
-
-- On Posix systems the user configuration is now looked under ``$XDG_CONFIG_HOME/nim/nim.cfg``
- (if ``XDG_CONFIG_HOME`` is not defined, then under ``~/.config/nim/nim.cfg``). It used to be
- ``$XDG_CONFIG_DIR/nim.cfg`` (and ``~/.config/nim.cfg``).
-
- Similarly, on Windows, the user configuration is now looked under ``%APPDATA%/nim/nim.cfg``.
- This used to be ``%APPDATA%/nim.cfg``.
-
-### Bugfixes
diff --git a/changelogs/changelog_0_20_0.md b/changelogs/changelog_0_20_0.md
deleted file mode 100644
index 9ddfd36dc..000000000
--- a/changelogs/changelog_0_20_0.md
+++ /dev/null
@@ -1,294 +0,0 @@
-## v0.20.0 - 2019-06-06
-
-
-### Changes affecting backwards compatibility
-
-- `shr` is now sign preserving. Use `-d:nimOldShiftRight` to enable
- the old behavior globally.
-
-- The ``isLower``, ``isUpper`` family of procs in strutils/unicode
- operating on **strings** have been
- deprecated since it was unclear what these do. Note that the much more
- useful procs that operate on ``char`` or ``Rune`` are not affected.
-
-- `strutils.editDistance` has been deprecated,
- use `editdistance.editDistance` or `editdistance.editDistanceAscii`
- instead.
-
-- The OpenMP parallel iterator \``||`\` now supports any `#pragma omp directive`
- and not just `#pragma omp parallel for`. See
- [OpenMP documentation](https://www.openmp.org/wp-content/uploads/OpenMP-4.5-1115-CPP-web.pdf).
-
- The default annotation is `parallel for`, if you used OpenMP without annotation
- the change is transparent, if you used annotations you will have to prefix
- your previous annotations with `parallel for`.
-
- Furthermore, an overload with positive stepping is available.
-
-- The `unchecked` pragma was removed, instead use `system.UncheckedArray`.
-
-- The undocumented ``#? strongSpaces`` parsing mode has been removed.
-
-- The `not` operator is now always a unary operator, this means that code like
- ``assert not isFalse(3)`` compiles.
-
-- `getImpl` on a `var` or `let` symbol will now return the full `IdentDefs`
- tree from the symbol declaration instead of just the initializer portion.
-
-- Methods are now ordinary "single" methods, only the first parameter is
- used to select the variant at runtime. For backwards compatibility
- use the new `--multimethods:on` switch.
-
-- Generic methods are now deprecated; they never worked well.
-
-- Compile time checks for integer and float conversions are now stricter.
- For example, `const x = uint32(-1)` now gives a compile time error instead
- of being equivalent to `const x = 0xFFFFFFFF'u32`.
-
-- Using `typed` as the result type in templates/macros now means
- "expression with a type". The old meaning of `typed` is preserved
- as `void` or no result type at all.
-
-- A bug allowed `macro foo(): int = 123` to compile even though a
- macro has to return a `NimNode`. This has been fixed.
-
-- With the exception of `uint` and `uint64`, conversion to unsigned types
- are now range checked during runtime.
-
-- Macro arguments of type `typedesc` are now passed to the macro as
- `NimNode` like every other type except `static`. Use `typed` for a
- behavior that is identical in new and old
- Nim. See the RFC [Pass typedesc as NimNode to macros](https://github.com/nim-lang/RFCs/issues/148)
- for more details.
-
-
-#### Breaking changes in the standard library
-
-- `osproc.execProcess` now also takes a `workingDir` parameter.
-
-- `std/sha1.secureHash` now accepts `openArray[char]`, not `string`. (Former
- successful matches should keep working, though former failures will not.)
-
-- `options.UnpackError` is no longer a ref type and inherits from `system.Defect`
- instead of `system.ValueError`.
-
-- `system.ValueError` now inherits from `system.CatchableError` instead of `system.Defect`.
-
-- The procs `parseutils.parseBiggestInt`, `parseutils.parseInt`,
- `parseutils.parseBiggestUInt` and `parseutils.parseUInt` now raise a
- `ValueError` when the parsed integer is outside of the valid range.
- Previously they sometimes raised an `OverflowError` and sometimes they
- returned `0`.
-
-- The procs `parseutils.parseBin`, `parseutils.parseOct` and `parseutils.parseHex`
- were not clearing their `var` parameter `number` and used to push its value to
- the left when storing the parsed string into it. Now they always set the value
- of the parameter to `0` before storing the result of the parsing, unless the
- string to parse is not valid (then the value of `number` is not changed).
-
-- `streams.StreamObject` now restricts its fields to only raise `system.Defect`,
- `system.IOError` and `system.OSError`.
- This change only affects custom stream implementations.
-
-- nre's `RegexMatch.{captureBounds,captures}[]` no longer return `Option` or
- `nil`/`""`, respectively. Use the newly added `n in p.captures` method to
- check if a group is captured, otherwise you'll receive an exception.
-
-- nre's `RegexMatch.{captureBounds,captures}.toTable` no longer accept a
- default parameter. Instead uncaptured entries are left empty. Use
- `Table.getOrDefault()` if you need defaults.
-
-- nre's `RegexMatch.captures.{items,toSeq}` now returns an `Option[string]`
- instead of a `string`. With the removal of `nil` strings, this is the only
- way to indicate a missing match. Inside your loops, instead
- of `capture == ""` or `capture == nil`, use `capture.isSome` to check if a capture is
- present, and `capture.get` to get its value.
-
-- nre's `replace()` no longer throws `ValueError` when the replacement string
- has missing captures. It instead throws `KeyError` for named captures, and
- `IndexError` for unnamed captures. This is consistent with
- `RegexMatch.{captureBounds,captures}[]`.
-
-- `splitFile` now correctly handles edge cases, see #10047.
-
-- `isNil` is no longer false for undefined in the JavaScript backend:
- now it's true for both nil and undefined.
- Use `isNull` or `isUndefined` if you need exact equality:
- `isNil` is consistent with `===`, `isNull` and `isUndefined` with `==`.
-
-- several deprecated modules were removed: `ssl`, `matchers`, `httpserver`,
- `unsigned`, `actors`, `parseurl`
-
-- two poorly documented and not used modules (`subexes`, `scgi`) were moved to
- graveyard (they are available as Nimble packages)
-
-- procs `string.add(int)` and `string.add(float)` which implicitly convert
- ints and floats to string have been deprecated.
- Use `string.addInt(int)` and `string.addFloat(float)` instead.
-
-- ``case object`` branch transitions via ``system.reset`` are deprecated.
- Compile your code with ``-d:nimOldCaseObjects`` for a transition period.
-
-- base64 module: The default parameter `newLine` for the `encode` procs
- was changed from `"\13\10"` to the empty string `""`.
-
-
-#### Breaking changes in the compiler
-
-- The compiler now implements the "generic symbol prepass" for `when` statements
- in generics, see bug #8603. This means that code like this does not compile
- anymore:
-
-```nim
-proc enumToString*(enums: openArray[enum]): string =
- # typo: 'e' instead 'enums'
- when e.low.ord >= 0 and e.high.ord < 256:
- result = newString(enums.len)
- else:
- result = newString(enums.len * 2)
-```
-
-- ``discard x`` is now illegal when `x` is a function symbol.
-
-- Implicit imports via ``--import: module`` in a config file are now restricted
- to the main package.
-
-
-### Library additions
-
-- There is a new stdlib module `std/editdistance` as a replacement for the
- deprecated `strutils.editDistance`.
-
-- There is a new stdlib module `std/wordwrap` as a replacement for the
- deprecated `strutils.wordwrap`.
-
-- Added `split`, `splitWhitespace`, `size`, `alignLeft`, `align`,
- `strip`, `repeat` procs and iterators to `unicode.nim`.
-
-- Added `or` for `NimNode` in `macros`.
-
-- Added `system.typeof` for more control over how `type` expressions
- can be deduced.
-
-- Added `macros.isInstantiationOf` for checking if the proc symbol
- is instantiation of generic proc symbol.
-
-- Added the parameter ``isSorted`` for the ``sequtils.deduplicate`` proc.
-
-- There is a new stdlib module `std/diff` to compute the famous "diff"
- of two texts by line.
-
-- Added `os.relativePath`.
-
-- Added `parseopt.remainingArgs`.
-
-- Added `os.getCurrentCompilerExe` (implemented as `getAppFilename` at CT),
- can be used to retrieve the currently executing compiler.
-
-- Added `xmltree.toXmlAttributes`.
-
-- Added ``std/sums`` module for fast summation functions.
-
-- Added `Rusage`, `getrusage`, `wait4` to the posix interface.
-
-- Added the `posix_utils` module.
-
-- Added `system.default`.
-
-- Added `sequtils.items` for closure iterators, allows closure iterators
- to be used by the `mapIt`, `filterIt`, `allIt`, `anyIt`, etc.
-
-
-### Library changes
-
-- The string output of `macros.lispRepr` proc has been tweaked
- slightly. The `dumpLisp` macro in this module now outputs an
- indented proper Lisp, devoid of commas.
-
-- Added `macros.signatureHash` that returns a stable identifier
- derived from the signature of a symbol.
-
-- In `strutils` empty strings now no longer matched as substrings
- anymore.
-
-- The `Complex` type is now a generic object and not a tuple anymore.
-
-- The `ospaths` module is now deprecated, use `os` instead. Note that
- `os` is available in a NimScript environment but unsupported
- operations produce a compile-time error.
-
-- The `parseopt` module now supports a new flag `allowWhitespaceAfterColon`
- (default value: true) that can be set to `false` for better Posix
- interoperability. (Bug #9619.)
-
-- `os.joinPath` and `os.normalizePath` handle edge cases like ``"a/b/../../.."``
- differently.
-
-- `securehash` was moved to `lib/deprecated`.
-
-- The switch ``-d:useWinAnsi`` is not supported anymore.
-
-- In `times` module, procs `format` and `parse` accept a new optional
- `DateTimeLocale` argument for formatting/parsing dates in other languages.
-
-
-### Language additions
-
-- Vm support for float32<->int32 and float64<->int64 casts was added.
-- There is a new pragma block `noSideEffect` that works like
- the `gcsafe` pragma block.
-- added `os.getCurrentProcessId`.
-- User defined pragmas are now allowed in the pragma blocks.
-- Pragma blocks are no longer eliminated from the typed AST tree to preserve
- pragmas for further analysis by macros.
-- Custom pragmas are now supported for `var` and `let` symbols.
-- Tuple unpacking is now supported for constants and for loop variables.
-- Case object branches can be initialized with a runtime discriminator if
- possible discriminator values are constrained within a case statement.
-
-### Language changes
-
-- The standard extension for SCF (source code filters) files was changed from
- ``.tmpl`` to ``.nimf``,
- it's more recognizable and allows tools like Github to recognize it as Nim,
- see [#9647](https://github.com/nim-lang/Nim/issues/9647).
- The previous extension will continue to work.
-
-- Pragma syntax is now consistent. Previous syntax where type pragmas did not
- follow the type name is now deprecated. Also pragma before generic parameter
- list is deprecated to be consistent with how pragmas are used with a proc. See
- [#8514](https://github.com/nim-lang/Nim/issues/8514) and
- [#1872](https://github.com/nim-lang/Nim/issues/1872) for further details.
-
-- Hash sets and tables are initialized by default. The explicit `initHashSet`,
- `initTable`, etc. are not needed anymore.
-
-
-### Tool changes
-
-- `jsondoc` now includes a `moduleDescription` field with the module
- description. `jsondoc0` shows comments as its own objects as shown in the
- documentation.
-- `nimpretty`: --backup now defaults to `off` instead of `on` and the flag was
- undocumented; use `git` instead of relying on backup files.
-- `koch` now defaults to build the latest *stable* Nimble version unless you
- explicitly ask for the latest master version via `--latest`.
-
-
-### Compiler changes
-
-- The deprecated `fmod` proc is now unavailable on the VM.
-- A new `--outdir` option was added.
-- The compiled JavaScript file for the project produced by executing `nim js`
- will no longer be placed in the nimcache directory.
-- The `--hotCodeReloading` has been implemented for the native targets.
- The compiler also provides a new more flexible API for handling the
- hot code reloading events in the code.
-- The compiler now supports a ``--expandMacro:macroNameHere`` switch
- for easy introspection into what a macro expands into.
-- The `-d:release` switch now does not disable runtime checks anymore.
- For a release build that also disables runtime checks
- use `-d:release -d:danger` or simply `-d:danger`.
-
-
-### Bugfixes
diff --git a/changelogs/changelog_0_20_2.md b/changelogs/changelog_0_20_2.md
deleted file mode 100644
index 4f09ae3c5..000000000
--- a/changelogs/changelog_0_20_2.md
+++ /dev/null
@@ -1,55 +0,0 @@
-# v0.20.2 - 2019-07-17
-
-
-## Changes affecting backwards compatibility
-
-- All `strutils.rfind` procs now take `start` and `last` like `strutils.find`
- with the same data slice/index meaning. This is backwards compatible for
- calls *not* changing the `rfind` `start` parameter from its default. (#11487)
-
- In the unlikely case that you were using `rfind X, start=N`, or `rfind X, N`,
- then you need to change that to `rfind X, last=N` or `rfind X, 0, N`. (This
- should minimize gotchas porting code from other languages like Python or C++.)
-
-- On Windows stderr/stdout/stdin are not opened as binary files anymore. Use the switch
- `-d:nimBinaryStdFiles` for a transition period.
-
-### Breaking changes in the standard library
-
-- Mac OS X / BSD: TSa_Family is now the ``uint8`` type, so type
- conversions like ``x.sin_family = uint16 toInt(nativesockets.AF_INET)``
- need to be changed into ``x.sin_family = TSa_Family toInt(nativesockets.AF_INET)``.
-
-
-### Breaking changes in the compiler
-
-
-## Library additions
-
-- `toOpenArray` is now available for the JS target.
-
-## Library changes
-
-- Fix async IO operations stalling even after socket is closed. (#11232)
-
-- More informative error message for `streams.openFileStream`. (#11438)
-
-
-## Language additions
-
-
-## Language changes
-
-
-### Tool changes
-
-
-### Compiler changes
-
-- Better error message for IndexError for empty containers. (#11476)
-
-- Fix regression in semfold for old right shift. (#11477)
-
-- Fix for passing tuples as static params to macros. (#11423)
-
-## Bugfixes
diff --git a/changelogs/changelog_1_0_0.md b/changelogs/changelog_1_0_0.md
deleted file mode 100644
index 5a9185ecb..000000000
--- a/changelogs/changelog_1_0_0.md
+++ /dev/null
@@ -1,138 +0,0 @@
-# v1.0 - 2019-09-23
-
-
-## Changes affecting backwards compatibility
-
-- The switch ``-d:nimBinaryStdFiles`` does not exist anymore. Instead
- stdin/stdout/stderr are binary files again. This change only affects
- Windows.
-
-- On Windows console applications the code-page is set at program startup
- to UTF-8. Use the new switch `-d:nimDontSetUtf8CodePage` to disable this
- feature.
-
-- The language definition and compiler are now stricter about ``gensym``'ed
- symbols in hygienic templates. See the section in the
- [manual](https://nim-lang.org/docs/manual.html#templates-hygiene-in-templates)
- for further details. Use the compiler switch `--oldgensym:on` for a
- transition period.
-
-
-### Breaking changes in the standard library
-
-- We removed `unicode.Rune16` without any deprecation period as the name
- was wrong (see the [RFC](https://github.com/nim-lang/RFCs/issues/151) for details)
- and we didn't find any usages of it in the wild. If you still need it, add this
- piece of code to your project:
-```nim
-type
- Rune16* = distinct int16
-```
-
-- `exportc` now uses C instead of C++ mangling with `nim cpp`, matching behavior
- of `importc`, see #10578.
- Use the new `exportcpp` to mangle as C++ when using `nim cpp`.
-
-
-### Breaking changes in the compiler
-
-- A bug allowing `int` to be implicitly converted to range types of smaller size
- (e.g `range[0'i8..10'i8]`) has been fixed.
-
-
-
-## Library additions
-
-- `encodings.getCurrentEncoding` now distinguishes between the console's
- encoding and the OS's encoding. This distinction is only meaningful on
- Windows.
-- Added `system.getOsFileHandle` which is usually more useful
- than `system.getFileHandle`. This distinction is only meaningful on
- Windows.
-- Added a `json.parseJsonFragments` iterator that can be used to speedup
- JSON processing substantially when there are JSON fragments separated
- by whitespace.
-
-
-
-## Library changes
-
-- Added `os.delEnv` and `nimscript.delEnv`. (#11466)
-
-- Enabled Oid usage in hashtables. (#11472)
-
-- Added `unsafeColumnAt` procs, that return unsafe cstring from InstantRow. (#11647)
-
-- Make public `Sha1Digest` and `Sha1State` types and `newSha1State`,
- `update` and `finalize` procedures from `sha1` module. (#11694)
-
-- Added the `std/monotimes` module which implements monotonic timestamps.
-
-- Consistent error handling of two `exec` overloads. (#10967)
-
-- Officially the following modules now have an unstable API:
- - std/varints
- - core/allocators
- - core/hotcodereloading
- - asyncstreams
- - base64
- - browsers
- - collections/rtarrays
- - collections/sharedlist
- - collections/sharedtable
- - concurrency/atomics
- - concurrency/cpuload
- - concurrency/threadpool
- - coro
- - endians
- - httpcore
- - parsesql
- - pathnorm
- - reservedmem
- - typetraits
-
- Every other stdlib module is API stable with respect to version 1.
-
-
-
-## Language additions
-
-- Inline iterators returning `lent T` types are now supported, similarly to
- iterators returning `var T`:
-```nim
-iterator myitems[T](x: openarray[T]): lent T
-iterator mypairs[T](x: openarray[T]): tuple[idx: int, val: lent T]
-```
-
-- Added an `importjs` pragma that can now be used instead of `importcpp`
- and `importc` to import symbols from JavaScript. `importjs` for routines always
- takes a "pattern" for maximum flexibility.
-
-
-
-## Language changes
-
-- `uint64` is now finally a regular ordinal type. This means `high(uint64)` compiles
- and yields the correct value.
-
-
-### Tool changes
-
-- The Nim compiler now does not recompile the Nim project via ``nim c -r`` if
- no dependent Nim file changed. This feature can be overridden by
- the ``--forceBuild`` command line option.
-- The Nim compiler now warns about unused module imports. You can use a
- top level ``{.used.}`` pragma in the module that you want to be importable
- without producing this warning.
-- The "testament" testing tool's name was changed
- from `tester` to `testament` and is generally available as a tool to run Nim
- tests automatically.
-
-
-### Compiler changes
-
-- VM can now cast integer type arbitrarily. (#11459)
-
-
-
-## Bugfixes
diff --git a/changelogs/changelog_1_0_2.md b/changelogs/changelog_1_0_2.md
deleted file mode 100644
index aa01c503f..000000000
--- a/changelogs/changelog_1_0_2.md
+++ /dev/null
@@ -1,56 +0,0 @@
-# v1.0.2 - 2019-10-23
-
-
-## Bugfixes
-
-* fixes the --verbosity:2 regression
-* Fixed "Fail to compile a file twice under Windows (v1.0 bug)." [#12242](https://github.com/nim-lang/Nim/issues/12242)
-* fix nimpretty removing space before pragma
-* JS: gensym is stricter for 'this'
-* Fixed "VM Assertion Error with newruntime" [#12294](https://github.com/nim-lang/Nim/issues/12294)
-* Fixed "Assertion error when running `nim check` on compiler/nim.nim" [#12281](https://github.com/nim-lang/Nim/issues/12281)
-* Fixed "Compiler crash with empty array and generic instantiation with int as parameter" [#12264](https://github.com/nim-lang/Nim/issues/12264)
-* Fixed "Regression in JS backend codegen "Error: request to generate code for .compileTime proc"" [#12240](https://github.com/nim-lang/Nim/issues/12240)
-* Fix how `relativePath` handle case sensitiviy
-* Fixed "SIGSEGV in compiler when using generic types and seqs" [#12336](https://github.com/nim-lang/Nim/issues/12336)
-* Fixed "[1.0.0] weird interaction between `import os` and casting integer to char on macosx trigger bad codegen" [#12291](https://github.com/nim-lang/Nim/issues/12291)
-* VM: no special casing for big endian machines
-* Fixed "`internal error: environment misses` with a simple template inside one of Jester macros" [#12323](https://github.com/nim-lang/Nim/issues/12323)
-* nimsuggest: fix tcp socket leak
-* nimsuggest: fix tcp socket leak for epc backend
-* Fixed "`writeFile` and `write(f, str)` skip null bytes on Windows" [#12315](https://github.com/nim-lang/Nim/issues/12315)
-* Fixed "Crash in intsets symmetric_difference" [#12366](https://github.com/nim-lang/Nim/issues/12366)
-* Fixed "[regression] VM crash when dealing with var param of a proc result" [#12244](https://github.com/nim-lang/Nim/issues/12244)
-* fixes a koch regression that made 'koch boot --listcmd' not work anymore
-* Fixed "[regression] inconsistent signed int `mod` operator between runtime, compiletime, and semfold" [#12332](https://github.com/nim-lang/Nim/issues/12332)
-* Fixed "Boehm disables interior pointer checking" [#12286](https://github.com/nim-lang/Nim/issues/12286)
-* Fixes semCustomPragma when nkSym
-* Fixed yield in nkCheckedFieldExpr
-* Fixed "`randomize()` from `random` not working on JS" [#12418](https://github.com/nim-lang/Nim/issues/12418)
-* Fixed "Compiler crash with invalid object variant" [#12379](https://github.com/nim-lang/Nim/issues/12379)
-* fix type's case in random.nim
-* Fixed "Update docs with a better way to signal unimplemented methods" [#10804](https://github.com/nim-lang/Nim/issues/10804)
-* Fixed "Nim language manual, push pragma is not explained well" [#10824](https://github.com/nim-lang/Nim/issues/10824)
-* Fixed "[regression] Importing more than one module with same name from different packages produce bad codegen" [#12420](https://github.com/nim-lang/Nim/issues/12420)
-* Namespace unittest enums to avoid name conflicts
-* Fixed "VM checks unsigned integers for overflow." [#12310](https://github.com/nim-lang/Nim/issues/12310)
-* Fixed "line directive is not generated for first line of function definition" [#12426](https://github.com/nim-lang/Nim/issues/12426)
-
-
-
-## Documentation improvements
-
-* threadpool: fix link in docs (#12258)
-* Fix spellings (#12277)
-* fix #12278, don't expose internal PCRE documentation
-* Fixed "Documentation of quitprocs is wrong" [#12279(https://github.com/nim-lang/Nim/issues/12279)
-* Fix typo in docs
-* Fix reference to parseSpec proc in readme
-* [doc/tut1] removed discard discussion in comments
-* Documentation improvements around the db interface
-* Easier build instructions for windows - just run `build_all.bat`.
-* fix a few dead links and a missing sentence in documentation
-* Macro docs additions
-* Updated the code example in the os module to use better grammar.
-* Mention "lambdas" and `=>` in the manual
-* Better documentation on Garbage Collector
diff --git a/changelogs/changelog_1_2_0.md b/changelogs/changelog_1_2_0.md
deleted file mode 100644
index 1f76df0b4..000000000
--- a/changelogs/changelog_1_2_0.md
+++ /dev/null
@@ -1,535 +0,0 @@
-# v1.2.0 - 2020-04-02
-
-
-## Standard library additions and changes
-- Added overloaded `strformat.fmt` macro that use specified characters as
- delimiter instead of '{' and '}'.
-- Added new procs in `tables.nim`: `OrderedTable.pop`, `CountTable.del`,
- `CountTable.pop`, `Table.pop`.
-- Added `strtabs.clear` overload that reuses the existing mode.
-- Added `browsers.osOpen` const alias for the operating system specific *"open"* command.
-- Added `sugar.dup` for turning in-place algorithms like `sort` and `shuffle`
- into operations that work on a copy of the data and return the mutated copy,
- like the existing `sorted` does.
-- Added `sugar.collect` that does comprehension for seq/set/table collections.
-- Added `sugar.capture` for capturing some local loop variables when creating a
- closure. This is an enhanced version of `closureScope`.
-- Added `typetraits.tupleLen` to get number of elements of a tuple/type tuple,
- and `typetraits.get` to get the ith element of a type tuple.
-- Added `typetraits.genericParams` to return a tuple of generic params from a
- generic instantiation.
-- `options` now treats `proc` like other pointer types, meaning `nil` proc variables
- are converted to `None`.
-- Added `os.normalizePathEnd` for additional path sanitization.
-- Added `times.fromUnixFloat,toUnixFloat`, sub-second resolution versions of
- `fromUnix`,`toUnixFloat`.
-- Added `wrapnils` module for chains of field-access and indexing where the LHS
- can be nil. This simplifies code by reducing need for if-else branches around
- intermediate maybe nil values. E.g. `echo ?.n.typ.kind`.
-- Added `minIndex`, `maxIndex` and `unzip` to the `sequtils` module.
-- Added `os.isRelativeTo` to tell whether a path is relative to another.
-- Added `resetOutputFormatters` to `unittest`.
-- Added `expectIdent` to the `macros` module.
-- Added `os.isValidFilename` that returns `true` if `filename` argument is valid
- for cross-platform use.
-- Added `times.isLeapDay`.
-- `base64` adds URL-Safe Base64, implements RFC-4648 Section-7.
-- Added a new module, `std / compilesettings` for querying the compiler about
- diverse configuration settings.
-- Added `net.getPeerCertificates` and `asyncnet.getPeerCertificates` for
- retrieving the verified certificate chain of the peer we are connected to
- through an SSL-wrapped `Socket`/`AsyncSocket`.
-- Added `browsers.openDefaultBrowser` without URL, implements IETF RFC-6694 Section-3.
-- Added `jsconsole.trace`, `jsconsole.table`, `jsconsole.exception` for JavaScript target.
-- Added `distinctBase` overload for values: `assert 12.MyInt.distinctBase == 12`
-- Added new module `std/stackframes`, in particular `setFrameMsg`, which enables
- custom runtime annotation of stackframes, see #13351 for examples.
- Turn on/off via `--stackTraceMsgs:on/off`.
-- Added `sequtils.countIt`, allowing for counting items using a predicate.
-- Added a `with` macro for easy function chaining that's available everywhere,
- there is no need to concern your APIs with returning the first argument
- to enable "chaining", instead use the dedicated macro `with` that
- was designed for it. For example:
-
-```nim
-import std/with
-
-type
- Foo = object
- col, pos: string
-
-proc setColor(f: var Foo; r, g, b: int) = f.col = $(r, g, b)
-proc setPosition(f: var Foo; x, y: float) = f.pos = $(x, y)
-
-var f: Foo
-with(f, setColor(2, 3, 4), setPosition(0.0, 1.0))
-echo f
-```
-
-- `macros.newLit` now works for ref object types.
-- `macro pragmas` can now be used in type sections.
-- 5 new pragmas were added to Nim in order to make the upcoming tooling more
- convenient to use. Nim compiler checks these pragmas for syntax but otherwise
- ignores them. The pragmas are `requires`, `ensures`, `assume`, `assert`, `invariant`.
-- `system.writeFile` has been overloaded to also support `openarray[byte]`.
-- `asyncdispatch.drain` now properly takes into account `selector.hasPendingOperations`
- and only returns once all pending async operations are guaranteed to have completed.
-- `sequtils.zip` now returns a sequence of anonymous tuples i.e. those tuples
- now do not have fields named "a" and "b".
-- `distinctBase` has been moved from `sugar` to `typetraits` and now it is
- implemented as compiler type trait instead of macro. `distinctBase` in sugar
- module is now deprecated.
-- `CountTable.mget` has been removed from `tables.nim`. It didn't work, and it
- was an oversight to be included in v1.0.
-- `tables.merge(CountTable, CountTable): CountTable` has been removed.
- It didn't work well together with the existing inplace version of the same proc
- (`tables.merge(var CountTable, CountTable)`).
- It was an oversight to be included in v1.0.
-- `asyncdispatch.drain` now consistently uses the passed timeout value for all
- iterations of the event loop, and not just the first iteration.
- This is more consistent with the other asyncdispatch APIs, and allows
- `asyncdispatch.drain` to be more efficient.
-- `base64.encode` and `base64.decode` were made faster by about 50%.
-- `htmlgen` adds [MathML](https://wikipedia.org/wiki/MathML) support
- (ISO 40314).
-- `macros.eqIdent` is now invariant to export markers and backtick quotes.
-- `htmlgen.html` allows `lang` in the `` tag and common valid attributes.
-- `macros.basename` and `basename=` got support for `PragmaExpr`,
- so that an expression like `MyEnum {.pure.}` is handled correctly.
-- `httpclient.maxredirects` changed from `int` to `Natural`, because negative values
- serve no purpose whatsoever.
-- `httpclient.newHttpClient` and `httpclient.newAsyncHttpClient` added `headers`
- argument to set initial HTTP Headers, instead of a hardcoded empty `newHttpHeader()`.
-- `parseutils.parseUntil` has now a different behaviour if the `until` parameter is
- empty. This was required for intuitive behaviour of the strscans module
- (see bug #13605).
-- `strutils.formatFloat` with `precision = 0` has the same behavior in all
- backends, and it is compatible with Python's behavior,
- e.g. `formatFloat(3.14159, precision = 0)` is now `3`, not `3.`.
-- `times.parse` now only uses input to compute its result, and not `now`:
- `parse("2020", "YYYY", utc())` is now `2020-01-01T00:00:00Z` instead of
- `2020-03-02T00:00:00Z` if run on 03-02; it also doesn't crash anymore when
- used on 29th, 30th, 31st of each month.
-- `httpcore.==(string, HttpCode)` is now deprecated due to lack of practical
- usage. The `$` operator can be used to obtain the string form of `HttpCode`
- for comparison if desired.
-- `std/oswalkdir` was buggy, it's now deprecated and reuses `std/os` procs.
-- `os.walkDir` and `os.walkDirRec` now have new flag, `checkDir` (default: false).
- If it is set to true, it will throw if input dir is invalid instead of a noop
- (which is the default behaviour, as it was before this change),
- `os.walkDirRec` only throws if top-level dir is invalid, but ignores errors for
- subdirs, otherwise it would be impossible to resume iteration.
-- The `FD` variant of `selector.unregister` for `ioselector_epoll` and
- `ioselector_select` now properly handle the `Event.User` select event type.
-- `joinPath` path normalization when `/` is the first argument works correctly:
- `assert "/" / "/a" == "/a"`. Fixed the edge case: `assert "" / "" == ""`.
-- `xmltree` now adds indentation consistently to child nodes for any number
- of children nodes.
-- `os.splitPath()` behavior synchronized with `os.splitFile()` to return "/"
- as the dir component of `/root_sub_dir` instead of the empty string.
-- The deprecated `lc` macro has been removed from `sugar`. It is now replaced
- with the more powerful `collect` macro.
-- `os.relativePath("foo", "foo")` is now `"."`, not `""`, as `""` means invalid
- path and shouldn't be conflated with `"."`; use `-d:nimOldRelativePathBehavior`
- to restore the old behavior.
-- `os.joinPath(a, b)` now honors trailing slashes in `b` (or `a` if `b` = "").
-- `base64.encode` no longer supports `lineLen` and `newLine`.
- Use `base64.encodeMime` instead.
-
-
-### Breaking changes
-
-- `net.newContext` now performs SSL Certificate checking on Linux and OSX.
- Define `nimDisableCertificateValidation` to disable it globally.
-
-
-
-## Language changes
-
-- An `align` pragma can now be used for variables and object fields, similar
- to the `alignas` declaration modifier in C/C++.
-- The `=sink` type bound operator is now optional. The compiler can now use a
- combination of `=destroy` and `copyMem` to move objects efficiently.
-- Unsigned integer operators have been fixed to allow promotion of the first operand.
-- Conversions to unsigned integers are unchecked at runtime, imitating earlier Nim
- versions. The documentation was improved to acknowledge this special case.
- See https://github.com/nim-lang/RFCs/issues/175 for more details.
-- There is a new syntax for lvalue references: `var b {.byaddr.} = expr` enabled by
- `import std/decls`.
-- `var a {.foo.}: MyType = expr` now lowers to `foo(a, MyType, expr)` for
- non-builtin pragmas, enabling things like lvalue references (see `decls.byaddr`).
-
-
-
-## Compiler changes
-
-- The generated JS code uses spaces, instead of mixing spaces and tabs.
-- The Nim compiler now supports the ``--asm`` command option for easier
- inspection of the produced assembler code.
-- The Nim compiler now supports a new pragma called ``.localPassc`` to
- pass specific compiler options to the C(++) backend for the C(++) file
- that was produced from the current Nim module.
-- The compiler now inferes "sink parameters". To disable this for a specific routine,
- annotate it with `.nosinks`. To disable it for a section of code, use
- `{.push sinkInference: off.}`...`{.pop.}`.
-- The compiler now supports a new switch `--panics:on` that turns runtime
- errors like `IndexError` or `OverflowError` into fatal errors that **cannot**
- be caught via Nim's `try` statement. `--panics:on` can improve the
- runtime efficiency and code size of your program significantly.
-- The compiler now warns about inheriting directly from `system.Exception` as
- this is **very bad** style. You should inherit from `ValueError`, `IOError`,
- `OSError` or from a different specific exception type that inherits from
- `CatchableError` and cannot be confused with a `Defect`.
-- The error reporting for Nim's effect system has been improved.
-- Implicit conversions for `const` behave correctly now, meaning that code like
- `const SOMECONST = 0.int; procThatTakesInt32(SOMECONST)` will be illegal now.
- Simply write `const SOMECONST = 0` instead.
-- The `{.dynlib.}` pragma is now required for exporting symbols when making
- shared objects on POSIX and macOS, which make it consistent with the behavior
- on Windows.
-- The compiler is now more strict about type conversions concerning proc
- types: Type conversions cannot be used to hide `.raise` effects or side
- effects, instead a `cast` must be used. With the flag `--useVersion:1.0` the
- old behaviour is emulated.
-- The Nim compiler now implements a faster way to detect overflows based
- on GCC's `__builtin_sadd_overflow` family of functions. (Clang also
- supports these). Some versions of GCC lack this feature and unfortunately
- we cannot detect this case reliably. So if you get compilation errors like
- "undefined reference to `__builtin_saddll_overflow`" compile your programs
- with `-d:nimEmulateOverflowChecks`.
-
-
-
-
-## Bugfixes
-
-- Fixed "`nimgrep --nocolor` is ignored on posix; should be instead: `--nimgrep --color=[auto]|true|false`"
- ([#7591](https://github.com/nim-lang/Nim/issues/7591))
-- Fixed "Runtime index on const array (of converted obj) causes C-compiler error"
- ([#10514](https://github.com/nim-lang/Nim/issues/10514))
-- Fixed "windows x86 with vcc compile error with "asmNoStackFrame""
- ([#12298](https://github.com/nim-lang/Nim/issues/12298))
-- Fixed "[TODO] regression: Error: Locks requires --threads:on option"
- ([#12330](https://github.com/nim-lang/Nim/issues/12330))
-- Fixed "Add --cc option to --help or --fullhelp output"
- ([#12010](https://github.com/nim-lang/Nim/issues/12010))
-- Fixed "questionable `csize` definition in `system.nim`"
- ([#12187](https://github.com/nim-lang/Nim/issues/12187))
-- Fixed "os.getAppFilename() returns incorrect results on OpenBSD"
- ([#12389](https://github.com/nim-lang/Nim/issues/12389))
-- Fixed "HashSet[uint64] slow insertion depending on values"
- ([#11764](https://github.com/nim-lang/Nim/issues/11764))
-- Fixed "Differences between compiling 'classic call syntax' vs 'method call syntax' ."
- ([#12453](https://github.com/nim-lang/Nim/issues/12453))
-- Fixed "c -d:nodejs --> SIGSEGV: Illegal storage access"
- ([#12502](https://github.com/nim-lang/Nim/issues/12502))
-- Fixed "Closure iterator crashes on --newruntime due to "dangling references""
- ([#12443](https://github.com/nim-lang/Nim/issues/12443))
-- Fixed "No `=destroy` for elements of closure environments other than for latest devel --gc:destructors"
- ([#12577](https://github.com/nim-lang/Nim/issues/12577))
-- Fixed "strutils:formatBiggestFloat() gives different results in JS mode"
- ([#8242](https://github.com/nim-lang/Nim/issues/8242))
-- Fixed "Regression (devel): the new `csize_t` definition isn't consistently used, nor tested thoroughly..."
- ([#12597](https://github.com/nim-lang/Nim/issues/12597))
-- Fixed "tables.take() is defined only for `Table` and missed for other table containers"
- ([#12519](https://github.com/nim-lang/Nim/issues/12519))
-- Fixed "`pthread_key_t` errors on OpenBSD"
- ([#12135](https://github.com/nim-lang/Nim/issues/12135))
-- Fixed "newruntime: simple seq pop at ct results in compile error"
- ([#12644](https://github.com/nim-lang/Nim/issues/12644))
-- Fixed "[Windows] finish.exe C:\Users\\.nimble\bin is not in your PATH environment variable."
- ([#12319](https://github.com/nim-lang/Nim/issues/12319))
-- Fixed "Error with strformat + asyncdispatch + const"
- ([#12612](https://github.com/nim-lang/Nim/issues/12612))
-- Fixed "MultipartData needs $"
- ([#11863](https://github.com/nim-lang/Nim/issues/11863))
-- Fixed "Nim stdlib style issues with --styleCheck:error"
- ([#12687](https://github.com/nim-lang/Nim/issues/12687))
-- Fixed "new $nimbleDir path substitution yields unexpected search paths"
- ([#12767](https://github.com/nim-lang/Nim/issues/12767))
-- Fixed "Regression: inlined procs now get multiple rounds of destructor injection"
- ([#12766](https://github.com/nim-lang/Nim/issues/12766))
-- Fixed "newruntime: compiler generates defective code"
- ([#12669](https://github.com/nim-lang/Nim/issues/12669))
-- Fixed "broken windows modules path handling because of 'os.relativePath' breaking changes"
- ([#12734](https://github.com/nim-lang/Nim/issues/12734))
-- Fixed "for loop tuple syntax not rendered correctly"
- ([#12740](https://github.com/nim-lang/Nim/issues/12740))
-- Fixed "Crash when trying to use `type.name[0]`"
- ([#12804](https://github.com/nim-lang/Nim/issues/12804))
-- Fixed "Enums should be considered Trivial types in Atomics"
- ([#12812](https://github.com/nim-lang/Nim/issues/12812))
-- Fixed "Produce static/const initializations for variables when possible"
- ([#12216](https://github.com/nim-lang/Nim/issues/12216))
-- Fixed "Assigning descriminator field leads to internal assert with --gc:destructors"
- ([#12821](https://github.com/nim-lang/Nim/issues/12821))
-- Fixed "nimsuggest `use` command does not return all instances of symbol"
- ([#12832](https://github.com/nim-lang/Nim/issues/12832))
-- Fixed "@[] is a problem for --gc:destructors"
- ([#12820](https://github.com/nim-lang/Nim/issues/12820))
-- Fixed "Codegen ICE in allPathsAsgnResult"
- ([#12827](https://github.com/nim-lang/Nim/issues/12827))
-- Fixed "seq[Object with ref and destructor type] doesn't work in old runtime"
- ([#12882](https://github.com/nim-lang/Nim/issues/12882))
-- Fixed "Destructor not invoked because it is instantiated too late, old runtime"
- ([#12883](https://github.com/nim-lang/Nim/issues/12883))
-- Fixed "The collect macro does not handle if/case correctly"
- ([#12874](https://github.com/nim-lang/Nim/issues/12874))
-- Fixed "allow typed/untyped params in magic procs (even if not in stdlib)"
- ([#12911](https://github.com/nim-lang/Nim/issues/12911))
-- Fixed "ARC/newruntime memory corruption"
- ([#12899](https://github.com/nim-lang/Nim/issues/12899))
-- Fixed "tasyncclosestall.nim still flaky test: Address already in use"
- ([#12919](https://github.com/nim-lang/Nim/issues/12919))
-- Fixed "newruntime and computed goto: variables inside the loop are in generated code uninitialised"
- ([#12785](https://github.com/nim-lang/Nim/issues/12785))
-- Fixed "osx: dsymutil needs to be called for debug builds to keep debug info"
- ([#12735](https://github.com/nim-lang/Nim/issues/12735))
-- Fixed "codegen ICE with ref objects, gc:destructors"
- ([#12826](https://github.com/nim-lang/Nim/issues/12826))
-- Fixed "mutable iterator cannot yield named tuples"
- ([#12945](https://github.com/nim-lang/Nim/issues/12945))
-- Fixed "parsecfg stores "\r\n" line breaks just as "\n""
- ([#12970](https://github.com/nim-lang/Nim/issues/12970))
-- Fixed "db_postgres.getValue issues warning when no rows found"
- ([#12973](https://github.com/nim-lang/Nim/issues/12973))
-- Fixed "ARC: Unpacking tuple with seq causes segfault"
- ([#12989](https://github.com/nim-lang/Nim/issues/12989))
-- Fixed "ARC/newruntime: strutils.join on seq with only empty strings causes segfault"
- ([#12965](https://github.com/nim-lang/Nim/issues/12965))
-- Fixed "regression (1.0.4): `{.push exportc.}` wrongly affects generic instantiations, causing codegen errors"
- ([#12985](https://github.com/nim-lang/Nim/issues/12985))
-- Fixed "cdt, crash with --gc:arc, works fine with default gc"
- ([#12978](https://github.com/nim-lang/Nim/issues/12978))
-- Fixed "ARC: No indexError thrown on out-of-bound seq access, SIGSEGV instead"
- ([#12961](https://github.com/nim-lang/Nim/issues/12961))
-- Fixed "ARC/async: Returning in a try-block results in wrong codegen"
- ([#12956](https://github.com/nim-lang/Nim/issues/12956))
-- Fixed "asm keyword is generating wrong output C code when --cc:tcc"
- ([#12988](https://github.com/nim-lang/Nim/issues/12988))
-- Fixed "Destructor not invoked"
- ([#13026](https://github.com/nim-lang/Nim/issues/13026))
-- Fixed "ARC/newruntime: Adding inherited var ref object to seq with base type causes segfault"
- ([#12964](https://github.com/nim-lang/Nim/issues/12964))
-- Fixed "Style check error with JS compiler target"
- ([#13032](https://github.com/nim-lang/Nim/issues/13032))
-- Fixed "regression(1.0.4): undeclared identifier: 'readLines'; plus another regression and bug"
- ([#13013](https://github.com/nim-lang/Nim/issues/13013))
-- Fixed "regression(1.04) `invalid pragma: since` with nim js"
- ([#12996](https://github.com/nim-lang/Nim/issues/12996))
-- Fixed "Sink to MemMove optimization in injectdestructors"
- ([#13002](https://github.com/nim-lang/Nim/issues/13002))
-- Fixed "--gc:arc: `catch` doesn't work with exception subclassing"
- ([#13072](https://github.com/nim-lang/Nim/issues/13072))
-- Fixed "nim c --gc:arc --exceptions:{setjmp,goto} incorrectly handles raise; `nim cpp --gc:arc` is ok"
- ([#13070](https://github.com/nim-lang/Nim/issues/13070))
-- Fixed "typetraits feature request - get subtype of a generic type"
- ([#6454](https://github.com/nim-lang/Nim/issues/6454))
-- Fixed "CountTable inconsistencies between keys() and len() after setting value to 0"
- ([#12813](https://github.com/nim-lang/Nim/issues/12813))
-- Fixed "{.align.} pragma is not applied if there is a generic field"
- ([#13122](https://github.com/nim-lang/Nim/issues/13122))
-- Fixed "ARC, finalizer, allow rebinding the same function multiple times"
- ([#13112](https://github.com/nim-lang/Nim/issues/13112))
-- Fixed "`nim doc` treats `export localSymbol` incorrectly"
- ([#13100](https://github.com/nim-lang/Nim/issues/13100))
-- Fixed "--gc:arc SIGSEGV (double free?)"
- ([#13119](https://github.com/nim-lang/Nim/issues/13119))
-- Fixed "codegen bug with arc"
- ([#13105](https://github.com/nim-lang/Nim/issues/13105))
-- Fixed "symbols not defined in the grammar"
- ([#10665](https://github.com/nim-lang/Nim/issues/10665))
-- Fixed "[JS] Move is not defined"
- ([#9674](https://github.com/nim-lang/Nim/issues/9674))
-- Fixed "[TODO] pathutils.`/` can return invalid AbsoluteFile"
- ([#13121](https://github.com/nim-lang/Nim/issues/13121))
-- Fixed "regression(1.04) `nim doc main.nim` generates broken html (no css)"
- ([#12998](https://github.com/nim-lang/Nim/issues/12998))
-- Fixed "Wrong supportsCopyMem on string in type section"
- ([#13095](https://github.com/nim-lang/Nim/issues/13095))
-- Fixed "Arc, finalizer, out of memory"
- ([#13157](https://github.com/nim-lang/Nim/issues/13157))
-- Fixed "`--genscript` messes up nimcache and future nim invocations"
- ([#13144](https://github.com/nim-lang/Nim/issues/13144))
-- Fixed "--gc:arc with --exceptions:goto for "nim c" generate invalid c code"
- ([#13186](https://github.com/nim-lang/Nim/issues/13186))
-- Fixed "[regression] duplicate member `_i1` codegen bug"
- ([#13195](https://github.com/nim-lang/Nim/issues/13195))
-- Fixed "RTree investigations with valgrind for --gc:arc"
- ([#13110](https://github.com/nim-lang/Nim/issues/13110))
-- Fixed "relativePath("foo", ".") returns wrong path"
- ([#13211](https://github.com/nim-lang/Nim/issues/13211))
-- Fixed "asyncftpclient - problem with welcome.msg"
- ([#4684](https://github.com/nim-lang/Nim/issues/4684))
-- Fixed "Unclear error message, lowest priority"
- ([#13256](https://github.com/nim-lang/Nim/issues/13256))
-- Fixed "Channel messages are corrupted"
- ([#13219](https://github.com/nim-lang/Nim/issues/13219))
-- Fixed "Codegen bug with exportc and case objects"
- ([#13281](https://github.com/nim-lang/Nim/issues/13281))
-- Fixed "[bugfix] fix #11590: c compiler warnings silently ignored, giving undefined behavior"
- ([#11591](https://github.com/nim-lang/Nim/issues/11591))
-- Fixed "[CI] tnetdial flaky test"
- ([#13132](https://github.com/nim-lang/Nim/issues/13132))
-- Fixed "Cross-Compiling with -d:mingw fails to locate compiler under OSX"
- ([#10717](https://github.com/nim-lang/Nim/issues/10717))
-- Fixed "`nim doc --project` broken with imports below main project file or duplicate names"
- ([#13150](https://github.com/nim-lang/Nim/issues/13150))
-- Fixed "regression: isNamedTuple(MyGenericTuple[int]) is false, should be true"
- ([#13349](https://github.com/nim-lang/Nim/issues/13349))
-- Fixed "--gc:arc codegen bug copying objects bound to C structs with missing C struct fields"
- ([#13269](https://github.com/nim-lang/Nim/issues/13269))
-- Fixed "write requires conversion to string"
- ([#13182](https://github.com/nim-lang/Nim/issues/13182))
-- Fixed "Some remarks to stdlib documentation"
- ([#13352](https://github.com/nim-lang/Nim/issues/13352))
-- Fixed "a `check` in unittest generated by template doesn't show actual value"
- ([#6736](https://github.com/nim-lang/Nim/issues/6736))
-- Fixed "Implicit return with case expression fails with 'var' return."
- ([#3339](https://github.com/nim-lang/Nim/issues/3339))
-- Fixed "Segfault with closure on arc"
- ([#13314](https://github.com/nim-lang/Nim/issues/13314))
-- Fixed "[Macro] Crash on malformed case statement with multiple else"
- ([#13255](https://github.com/nim-lang/Nim/issues/13255))
-- Fixed "regression: `echo 'discard' | nim c -r -` generates a file '-' ; `-` should be treated specially"
- ([#13374](https://github.com/nim-lang/Nim/issues/13374))
-- Fixed "on OSX, debugging (w gdb or lldb) a nim program crashes at the 1st call to `execCmdEx`"
- ([#9634](https://github.com/nim-lang/Nim/issues/9634))
-- Fixed "Internal error in getTypeDescAux"
- ([#13378](https://github.com/nim-lang/Nim/issues/13378))
-- Fixed "gc:arc mode breaks tuple let"
- ([#13368](https://github.com/nim-lang/Nim/issues/13368))
-- Fixed "Nim compiler hangs for certain C/C++ compiler errors"
- ([#8648](https://github.com/nim-lang/Nim/issues/8648))
-- Fixed "htmlgen does not support `data-*` attributes"
- ([#13444](https://github.com/nim-lang/Nim/issues/13444))
-- Fixed "[gc:arc] setLen will cause string not to be null-terminated."
- ([#13457](https://github.com/nim-lang/Nim/issues/13457))
-- Fixed "joinPath("", "") is "/" ; should be """
- ([#13455](https://github.com/nim-lang/Nim/issues/13455))
-- Fixed "[CI] flaky test on windows: tests/osproc/texitcode.nim"
- ([#13449](https://github.com/nim-lang/Nim/issues/13449))
-- Fixed "Casting to float32 on NimVM is broken"
- ([#13479](https://github.com/nim-lang/Nim/issues/13479))
-- Fixed "`--hints:off` doesn't work (doesn't override ~/.config/nim.cfg)"
- ([#8312](https://github.com/nim-lang/Nim/issues/8312))
-- Fixed "joinPath("", "") is "/" ; should be """
- ([#13455](https://github.com/nim-lang/Nim/issues/13455))
-- Fixed "tables.values is broken"
- ([#13496](https://github.com/nim-lang/Nim/issues/13496))
-- Fixed "global user config can override project specific config"
- ([#9405](https://github.com/nim-lang/Nim/issues/9405))
-- Fixed "Non deterministic macros and id consistency problem"
- ([#12627](https://github.com/nim-lang/Nim/issues/12627))
-- Fixed "try expression doesn't work with return on expect branch"
- ([#13490](https://github.com/nim-lang/Nim/issues/13490))
-- Fixed "CI will break every 4 years on feb 28: times doesn't handle leap years properly"
- ([#13543](https://github.com/nim-lang/Nim/issues/13543))
-- Fixed "[minor] `nimgrep --word` doesn't work with operators (eg misses `1 +% 2`)"
- ([#13528](https://github.com/nim-lang/Nim/issues/13528))
-- Fixed "`as` is usable as infix operator but its existence and precedence are not documented"
- ([#13409](https://github.com/nim-lang/Nim/issues/13409))
-- Fixed "JSON unmarshalling drops seq's items"
- ([#13531](https://github.com/nim-lang/Nim/issues/13531))
-- Fixed "os.joinPath returns wrong path when head ends '\' or '/' and tail starts '..'."
- ([#13579](https://github.com/nim-lang/Nim/issues/13579))
-- Fixed "Block-local types with the same name lead to bad codegen (sighashes regression)"
- ([#5170](https://github.com/nim-lang/Nim/issues/5170))
-- Fixed "tuple codegen error"
- ([#12704](https://github.com/nim-lang/Nim/issues/12704))
-- Fixed "newHttpHeaders does not accept repeated headers"
- ([#13573](https://github.com/nim-lang/Nim/issues/13573))
-- Fixed "regression: --incremental:on fails on simplest example"
- ([#13319](https://github.com/nim-lang/Nim/issues/13319))
-- Fixed "strscan can't get value of last element in format"
- ([#13605](https://github.com/nim-lang/Nim/issues/13605))
-- Fixed "hashes_examples crashes with "Bus Error" (unaligned access) on sparc64"
- ([#12508](https://github.com/nim-lang/Nim/issues/12508))
-- Fixed "gc:arc bug with re-used `seq[T]`"
- ([#13596](https://github.com/nim-lang/Nim/issues/13596))
-- Fixed "`raise CatchableError` is broken with --gc:arc when throwing inside a proc"
- ([#13599](https://github.com/nim-lang/Nim/issues/13599))
-- Fixed "cpp --gc:arc --exceptions:goto fails to raise with discard"
- ([#13436](https://github.com/nim-lang/Nim/issues/13436))
-- Fixed "terminal doesn't compile with -d:useWinAnsi"
- ([#13607](https://github.com/nim-lang/Nim/issues/13607))
-- Fixed "Parsing "sink ptr T" - region needs to be an object type"
- ([#12757](https://github.com/nim-lang/Nim/issues/12757))
-- Fixed "gc:arc + threads:on + closures compilation error"
- ([#13519](https://github.com/nim-lang/Nim/issues/13519))
-- Fixed "[ARC] segmentation fault"
- ([#13240](https://github.com/nim-lang/Nim/issues/13240))
-- Fixed "times.toDateTime buggy on 29th, 30th and 31th of each month"
- ([#13558](https://github.com/nim-lang/Nim/issues/13558))
-- Fixed "Deque misbehaves on VM"
- ([#13310](https://github.com/nim-lang/Nim/issues/13310))
-- Fixed "Nimscript listFiles should throw exception when path is not found"
- ([#12676](https://github.com/nim-lang/Nim/issues/12676))
-- Fixed "koch boot fails if even an empty config.nims is present in ~/.config/nims/ [devel regression]"
- ([#13633](https://github.com/nim-lang/Nim/issues/13633))
-- Fixed "nim doc generates lots of false positive LockLevel warnings"
- ([#13218](https://github.com/nim-lang/Nim/issues/13218))
-- Fixed "Arrays are passed by copy to iterators, causing crashes, unnecessary allocations and slowdowns"
- ([#12747](https://github.com/nim-lang/Nim/issues/12747))
-- Fixed "Range types always uses signed integer as a base type"
- ([#13646](https://github.com/nim-lang/Nim/issues/13646))
-- Fixed "Generate c code cannot compile with recent devel version"
- ([#13645](https://github.com/nim-lang/Nim/issues/13645))
-- Fixed "[regression] VM: Error: cannot convert -1 to uint64"
- ([#13661](https://github.com/nim-lang/Nim/issues/13661))
-- Fixed "Spurious raiseException(Exception) detected"
- ([#13654](https://github.com/nim-lang/Nim/issues/13654))
-- Fixed "gc:arc memory leak"
- ([#13659](https://github.com/nim-lang/Nim/issues/13659))
-- Fixed "Error: cannot convert -1 to uint (inside tuples)"
- ([#13671](https://github.com/nim-lang/Nim/issues/13671))
-- Fixed "strformat issue with --gc:arc"
- ([#13622](https://github.com/nim-lang/Nim/issues/13622))
-- Fixed "astToStr doesn't work inside generics"
- ([#13524](https://github.com/nim-lang/Nim/issues/13524))
-- Fixed "oswalkdir.walkDirRec wont return folders"
- ([#11458](https://github.com/nim-lang/Nim/issues/11458))
-- Fixed "`echo 'echo 1' | nim c -r -` silently gives wrong results (nimBetterRun not updated for stdin)"
- ([#13412](https://github.com/nim-lang/Nim/issues/13412))
-- Fixed "gc:arc destroys the global variable accidentally."
- ([#13691](https://github.com/nim-lang/Nim/issues/13691))
-- Fixed "[minor] sigmatch errors should be sorted, for reproducible errors"
- ([#13538](https://github.com/nim-lang/Nim/issues/13538))
-- Fixed "Exception when converting csize to clong"
- ([#13698](https://github.com/nim-lang/Nim/issues/13698))
-- Fixed "ARC: variables are no copied on the thread spawn causing crashes"
- ([#13708](https://github.com/nim-lang/Nim/issues/13708))
-- Fixed "Illegal distinct seq causes compiler crash"
- ([#13720](https://github.com/nim-lang/Nim/issues/13720))
-- Fixed "cyclic seq definition crashes the compiler"
- ([#13715](https://github.com/nim-lang/Nim/issues/13715))
-- Fixed "Iterator with openArray parameter make the argument evaluated many times"
- ([#13417](https://github.com/nim-lang/Nim/issues/13417))
-- Fixed "net/asyncnet: Unable to access peer's certificate chain"
- ([#13299](https://github.com/nim-lang/Nim/issues/13299))
-- Fixed "Accidentally "SIGSEGV: Illegal storage access" error after arc optimizations (#13325)"
- ([#13709](https://github.com/nim-lang/Nim/issues/13709))
-- Fixed "Base64 Regression"
- ([#13722](https://github.com/nim-lang/Nim/issues/13722))
-- Fixed "A regression (?) with --gc:arc and repr"
- ([#13731](https://github.com/nim-lang/Nim/issues/13731))
-- Fixed "Internal compiler error when using the new variable pragmas"
- ([#13737](https://github.com/nim-lang/Nim/issues/13737))
-- Fixed "bool conversion produces vcc 2019 warning at cpp compilation stage"
- ([#13744](https://github.com/nim-lang/Nim/issues/13744))
-- Fixed "Compiler "does not detect" a type recursion error in the wrong code, remaining frozen"
- ([#13763](https://github.com/nim-lang/Nim/issues/13763))
-- Fixed "[minor] regression: `Foo[0.0] is Foo[-0.0]` is now false"
- ([#13730](https://github.com/nim-lang/Nim/issues/13730))
-- Fixed "`nim doc` - only whitespace on first line causes segfault"
- ([#13631](https://github.com/nim-lang/Nim/issues/13631))
-- Fixed "hashset regression"
- ([#13794](https://github.com/nim-lang/Nim/issues/13794))
-- Fixed "`os.getApplFreebsd` could return incorrect paths in the case of a long path"
- ([#13806](https://github.com/nim-lang/Nim/issues/13806))
-- Fixed "Destructors are not inherited"
- ([#13810](https://github.com/nim-lang/Nim/issues/13810))
-- Fixed "io.readLines AssertionError on devel"
- ([#13829](https://github.com/nim-lang/Nim/issues/13829))
-- Fixed "exceptions:goto accidentally reset the variable during exception handling"
- ([#13782](https://github.com/nim-lang/Nim/issues/13782))
diff --git a/changelogs/changelog_1_4_0.md b/changelogs/changelog_1_4_0.md
deleted file mode 100644
index 77f66ffde..000000000
--- a/changelogs/changelog_1_4_0.md
+++ /dev/null
@@ -1,874 +0,0 @@
-# v1.4.0 - 2020-10-16
-
-
-
-## Standard library additions and changes
-
-- Added some enhancements to `std/jsonutils` module.
- * Added a possibility to deserialize JSON arrays directly to `HashSet` and
- `OrderedSet` types and respectively to serialize those types to JSON arrays
- via `jsonutils.fromJson` and `jsonutils.toJson` procedures.
- * Added a possibility to deserialize JSON `null` objects to Nim option objects
- and respectively to serialize Nim option object to JSON object if `isSome`
- or to JSON null object if `isNone` via `jsonutils.fromJson` and
- `jsonutils.toJson` procedures.
- * Added a `Joptions` parameter to `jsonutils.fromJson` currently
- containing two boolean options `allowExtraKeys` and `allowMissingKeys`.
- - If `allowExtraKeys` is `true` Nim's object to which the JSON is parsed is
- not required to have a field for every JSON key.
- - If `allowMissingKeys` is `true` Nim's object to which JSON is parsed is
- allowed to have fields without corresponding JSON keys.
-- Added `bindParams`, `bindParam` to `db_sqlite` for binding parameters into a `SqlPrepared` statement.
-- Added `tryInsert`,`insert` procs to `db_*` libs which accept primary key column name.
-- Added `xmltree.newVerbatimText` support create `style`'s,`script`'s text.
-- `uri` module now implements RFC-2397.
-- Added [DOM Parser](https://developer.mozilla.org/en-US/docs/Web/API/DOMParser)
- to the `dom` module for the JavaScript target.
-- The default hash for `Ordinal` has changed to something more bit-scrambling.
- `import hashes; proc hash(x: myInt): Hash = hashIdentity(x)` recovers the old
- one in an instantiation context while `-d:nimIntHash1` recovers it globally.
-- `deques.peekFirst` and `deques.peekLast` now have `var Deque[T] -> var T` overloads.
-- File handles created from high-level abstractions in the stdlib will no longer
- be inherited by child processes. In particular, these modules are affected:
- `asyncdispatch`, `asyncnet`, `system`, `nativesockets`, `net` and `selectors`.
-
- For `asyncdispatch`, `asyncnet`, `net` and `nativesockets`, an `inheritable`
- flag has been added to all `proc`s that create sockets, allowing the user to
- control whether the resulting socket is inheritable. This flag is provided to
- ease the writing of multi-process servers, where sockets inheritance is
- desired.
-
- For a transition period, define `nimInheritHandles` to enable file handle
- inheritance by default. This flag does **not** affect the `selectors` module
- due to the differing semantics between operating systems.
-
- `asyncdispatch.setInheritable`, `system.setInheritable` and
- `nativesockets.setInheritable` are also introduced for setting file handle or
- socket inheritance. Not all platforms have these `proc`s defined.
-
-- The file descriptors created for internal bookkeeping by `ioselector_kqueue`
- and `ioselector_epoll` will no longer be leaked to child processes.
-
-- `strutils.formatFloat` with `precision = 0` has been restored to the version
- 1 behaviour that produces a trailing dot, e.g. `formatFloat(3.14159, precision = 0)`
- is now `3.`, not `3`.
-- Added `commonPrefixLen` to `critbits`.
-
-- `relativePath(rel, abs)` and `relativePath(abs, rel)` used to silently give wrong results
- (see #13222); instead they now use `getCurrentDir` to resolve those cases,
- and this can now throw in edge cases where `getCurrentDir` throws.
- `relativePath` also now works for js with `-d:nodejs`.
-
-- JavaScript and NimScript standard library changes: `streams.StringStream` is
- now supported in JavaScript, with the limitation that any buffer `pointer`s
- used must be castable to `ptr string`, any incompatible pointer type will not
- work. The `lexbase` and `streams` modules used to fail to compile on
- NimScript due to a bug, but this has been fixed.
-
- The following modules now compile on both JS and NimScript: `parsecsv`,
- `parsecfg`, `parsesql`, `xmlparser`, `htmlparser` and `ropes`. Additionally
- supported for JS is `cstrutils.startsWith` and `cstrutils.endsWith`, for
- NimScript: `json`, `parsejson`, `strtabs` and `unidecode`.
-
-- Added `streams.readStr` and `streams.peekStr` overloads to
- accept an existing string to modify, which avoids memory
- allocations, similar to `streams.readLine` (#13857).
-
-- Added high-level `asyncnet.sendTo` and `asyncnet.recvFrom` UDP functionality.
-
-- `dollars.$` now works for unsigned ints with `nim js`.
-
-- Improvements to the `bitops` module, including bitslices, non-mutating versions
- of the original masking functions, `mask`/`masked`, and varargs support for
- `bitand`, `bitor`, and `bitxor`.
-
-- `sugar.=>` and `sugar.->` changes: Previously `(x, y: int)` was transformed
- into `(x: auto, y: int)`, it now becomes `(x: int, y: int)` for consistency
- with regular proc definitions (although you cannot use semicolons).
-
- Pragmas and using a name are now allowed on the lefthand side of `=>`. Here
- is an example of these changes:
- ```nim
- import sugar
-
- foo(x, y: int) {.noSideEffect.} => x + y
-
- # is transformed into
-
- proc foo(x: int, y: int): auto {.noSideEffect.} = x + y
- ```
-
-- The fields of `times.DateTime` are now private, and are accessed with getters and deprecated setters.
-
-- The `times` module now handles the default value for `DateTime` more consistently.
- Most procs raise an assertion error when given
- an uninitialized `DateTime`, the exceptions are `==` and `$` (which returns `"Uninitialized DateTime"`).
- The proc `times.isInitialized` has been added which can be used to check if
- a `DateTime` has been initialized.
-
-- Fix a bug where calling `close` on io streams in `osproc.startProcess` was a noop and led to
- hangs if a process had both reads from stdin and writes (e.g. to stdout).
-
-- The callback that is passed to `system.onThreadDestruction` must now be `.raises: []`.
-- The callback that is assigned to `system.onUnhandledException` must now be `.gcsafe`.
-
-- `osproc.execCmdEx` now takes an optional `input` for stdin, `workingDir` and `env`
- parameters.
-
-- Added a `ssl_config` module containing lists of secure ciphers as recommended by
- [Mozilla OpSec](https://wiki.mozilla.org/Security/Server_Side_TLS)
-
-- `net.newContext` now defaults to the list of ciphers targeting
- ["Intermediate compatibility"](https://wiki.mozilla.org/Security/Server_Side_TLS#Intermediate_compatibility_.28recommended.29)
- per Mozilla's recommendation instead of `ALL`. This change should protect
- users from the use of weak and insecure ciphers while still provides
- adequate compatibility with the majority of the Internet.
-
-- A new module `std/jsonutils` with hookable `jsonTo,toJson,fromJson` operations for json
- serialization/deserialization of custom types was added.
-
-- A new proc `heapqueue.find[T](heap: HeapQueue[T], x: T): int` to get index of element ``x``
- was added.
-- Added `rstgen.rstToLatex` a convenience proc for `renderRstToOut` and `initRstGenerator`.
-- Added `os.normalizeExe`.
-- `macros.newLit` now preserves named vs unnamed tuples.
-- Added `random.gauss`, that uses the ratio of uniforms method of sampling from a Gaussian distribution.
-- Added `typetraits.elementType` to get the element type of an iterable.
-- `typetraits.$` changes: `$(int,)` is now `"(int,)"` instead of `"(int)"`;
- `$tuple[]` is now `"tuple[]"` instead of `"tuple"`;
- `$((int, float), int)` is now `"((int, float), int)"` instead of `"(tuple of (int, float), int)"`
-- Added `macros.extractDocCommentsAndRunnables` helper.
-
-- `strformat.fmt` and `strformat.&` support `specifier =`. `fmt"{expr=}"` now
- expands to `fmt"expr={expr}"`.
-- Deprecations: instead of `os.existsDir` use `dirExists`, instead of `os.existsFile` use `fileExists`.
-
-- Added the `jsre` module, [Regular Expressions for the JavaScript target.](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions).
-- Made `maxLines` argument `Positive` in `logging.newRollingFileLogger`,
- because negative values will result in a new file being created for each logged
- line which doesn't make sense.
-- Changed `log` in `logging` to use proper log level for JavaScript,
- e.g. `debug` uses `console.debug`, `info` uses `console.info`, `warn` uses `console.warn`, etc.
-- Tables, HashSets, SharedTables and deques don't require anymore that the passed
- initial size must be a power of two - this is done internally.
- Proc `rightSize` for Tables and HashSets is deprecated, as it is not needed anymore.
- `CountTable.inc` takes `val: int` again not `val: Positive`; i.e. it can "count down" again.
-- Removed deprecated symbols from `macros` module, some of which were deprecated already in `0.15`.
-- Removed `sugar.distinctBase`, deprecated since `0.19`. Use `typetraits.distinctBase`.
-- `asyncdispatch.PDispatcher.handles` is exported so that an external low-level libraries can access it.
-
-- `std/with`, `sugar.dup` now support object field assignment expressions:
- ```nim
- import std/with
-
- type Foo = object
- x, y: int
-
- var foo = Foo()
- with foo:
- x = 10
- y = 20
-
- echo foo
- ```
-
-- Proc `math.round` is no longer deprecated. The advice to use `strformat` instead
- cannot be applied to every use case. The limitations and the (lack of) reliability
- of `round` are well documented.
-
-- Added `getprotobyname` to `winlean`. Added `getProtoByname` to `nativesockets` which returns a protocol code
- from the database that matches the protocol `name`.
-
-- Added missing attributes and methods to `dom.Navigator` like `deviceMemory`, `onLine`, `vibrate()`, etc.
-
-- Added `strutils.indentation` and `strutils.dedent` which enable indented string literals:
- ```nim
- import strutils
- echo dedent """
- This
- is
- cool!
- """
- ```
-
-- Added `initUri(isIpv6: bool)` to `uri` module, now `uri` supports parsing ipv6 hostname.
-
-- Added `readLines(p: Process)` to `osproc`.
-
-- Added the below `toX` procs for collections. The usage is similar to procs such as
- `sets.toHashSet` and `tables.toTable`. Previously, it was necessary to create the
- respective empty collection and add items manually.
- * `critbits.toCritBitTree`, which creates a `CritBitTree` from an `openArray` of
- items or an `openArray` of pairs.
- * `deques.toDeque`, which creates a `Deque` from an `openArray`.
- * `heapqueue.toHeapQueue`, which creates a `HeapQueue` from an `openArray`.
- * `intsets.toIntSet`, which creates an `IntSet` from an `openArray`.
-
-- Added `progressInterval` argument to `asyncftpclient.newAsyncFtpClient` to control the interval
- at which progress callbacks are called.
-
-- Added `os.copyFileToDir`.
-
-## Language changes
-
-- The `=destroy` hook no longer has to reset its target, as the compiler now automatically inserts
- `wasMoved` calls where needed.
-- The `=` hook is now called `=copy` for clarity. The old name `=` is still available so there
- is no need to update your code. This change was backported to 1.2 too so you can use the
- more readable `=copy` without loss of compatibility.
-
-- In the newruntime it is now allowed to assign to the discriminator field
- without restrictions as long as the case object doesn't have a custom destructor.
- The discriminator value doesn't have to be a constant either. If you have a
- custom destructor for a case object and you do want to freely assign discriminator
- fields, it is recommended to refactor the object into 2 objects like this:
-
- ```nim
- type
- MyObj = object
- case kind: bool
- of true: y: ptr UncheckedArray[float]
- of false: z: seq[int]
-
- proc `=destroy`(x: MyObj) =
- if x.kind and x.y != nil:
- deallocShared(x.y)
- ```
- Refactor into:
- ```nim
- type
- MySubObj = object
- val: ptr UncheckedArray[float]
- MyObj = object
- case kind: bool
- of true: y: MySubObj
- of false: z: seq[int]
-
- proc `=destroy`(x: MySubObj) =
- if x.val != nil:
- deallocShared(x.val)
- ```
-- `getImpl` on enum type symbols now returns field syms instead of idents. This helps
- with writing typed macros. The old behavior for backwards compatibility can be restored
- with `--useVersion:1.0`.
-- The typed AST for proc headers will now have the arguments be syms instead of idents.
- This helps with writing typed macros. The old behaviour for backwards compatibility can
- be restored with `--useVersion:1.0`.
-- ``let`` statements can now be used without a value if declared with
- ``importc``/``importcpp``/``importjs``/``importobjc``.
-- The keyword `from` is now usable as an operator.
-- Exceptions inheriting from `system.Defect` are no longer tracked with
- the `.raises: []` exception tracking mechanism. This is more consistent with the
- built-in operations. The following always used to compile (and still does):
- ```nim
- proc mydiv(a, b): int {.raises: [].} =
- a div b # can raise an DivByZeroDefect
- ```
-
- Now also this compiles:
- ```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` `Defects` become unrecoverable errors.
-
-- Added the `thiscall` calling convention as specified by Microsoft, mostly for hooking purposes.
-- Deprecated the `{.unroll.}` pragma, because it was always ignored by the compiler anyway.
-- Removed the deprecated `strutils.isNilOrWhitespace`.
-- Removed the deprecated `sharedtables.initSharedTable`.
-- Removed the deprecated `asyncdispatch.newAsyncNativeSocket`.
-- Removed the deprecated `dom.releaseEvents` and `dom.captureEvents`.
-
-- Removed `sharedlist.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)
- for more information.
-
-- "for-loop macros" (see [the manual](manual.html#macros-for-loop-macros)) are no longer
- an experimental feature. In other words, you don't have to write pragma
- `{.experimental: "forLoopMacros".}` if you want to use them.
-
-- Added the ``.noalias`` pragma. It is mapped to C's ``restrict`` keyword for the increased
- performance this keyword can enable.
-
-- `items` no longer compiles with enums with holes as its behavior was error prone, see #14004.
-- `system.deepcopy` has to be enabled explicitly for `--gc:arc` and `--gc:orc` via
- `--deepcopy:on`.
-
-- Added the `std/effecttraits` module for introspection of the inferred effects.
- We hope this enables `async` macros that are precise about the possible exceptions that
- can be raised.
-- The pragma blocks `{.gcsafe.}: ...` and `{.noSideEffect.}: ...` can now also be
- written as `{.cast(gcsafe).}: ...` and `{.cast(noSideEffect).}: ...`. This is the new
- preferred way of writing these, emphasizing their unsafe nature.
-
-
-## Compiler changes
-
-- Specific warnings can now be turned into errors via `--warningAsError[X]:on|off`.
-- The `define` and `undef` pragmas have been de-deprecated.
-- New command: `nim r main.nim [args...]` which compiles and runs main.nim, and implies `--usenimcache`
- so that the output is saved to $nimcache/main$exeExt, using the same logic as `nim c -r` to
- avoid recompilations when sources don't change.
- Example:
- ```bash
- nim r compiler/nim.nim --help # only compiled the first time
- echo 'import os; echo getCurrentCompilerExe()' | nim r - # this works too
- nim r compiler/nim.nim --fullhelp # no recompilation
- nim r --nimcache:/tmp main # binary saved to /tmp/main
- ```
-- `--hint:processing` is now supported and means `--hint:processing:on`
- (likewise with other hints and warnings), which is consistent with all other bool flags.
- (since 1.3.3).
-- `nim doc -r main` and `nim rst2html -r main` now call `openDefaultBrowser`.
-- Added the new hint `--hint:msgOrigin` will show where a compiler msg (hint|warning|error)
- was generated; this helps in particular when it's non obvious where it came from
- either because multiple locations generate the same message, or because the
- message involves runtime formatting.
-- Added the new flag `--backend:js|c|cpp|objc` (or -b:js etc), to change the backend; can be
- used with any command (e.g. nim r, doc, check etc); safe to re-assign.
-- Added the new flag `--doccmd:cmd` to pass additional flags for runnableExamples,
- e.g.: `--doccmd:-d:foo --threads`
- use `--doccmd:skip` to skip runnableExamples and rst test snippets.
-- Added the new flag `--usenimcache` to output binary files to nimcache.
-- `runnableExamples "-b:cpp -r:off": code` is now supported, allowing to override
- how an example is compiled and run, for example to change the backend.
-- `nim doc` now outputs under `$projectPath/htmldocs` when `--outdir` is unspecified
- (with or without `--project`); passing `--project` now automatically generates
- an index and enables search.
- See [docgen](docgen.html#introduction-quick-start) for details.
-- Removed the `--oldNewlines` switch.
-- Removed the `--laxStrings` switch for mutating the internal zero terminator on strings.
-- Removed the `--oldast` switch.
-- Removed the `--oldgensym` switch.
-- `$getType(untyped)` is now "untyped" instead of "expr", `$getType(typed)` is
- now "typed" instead of "stmt".
-- Sink inference is now disabled per default and has to enabled explicitly via
- `--sinkInference:on`. *Note*: For the standard library sink inference remains
- enabled. This change is most relevant for the `--gc:arc`, `--gc:orc` memory
- management modes.
-
-
-## Tool changes
-
-- `nimsuggest` now returns both the forward declaration and the
- implementation location upon a `def` query. Previously the behavior was
- to return the forward declaration only.
-
-
-## Bugfixes
-
-- Fixed "repr() not available for uint{,8,16,32,64} under --gc:arc"
- ([#13872](https://github.com/nim-lang/Nim/issues/13872))
-- Fixed "Critical: 1 completed Future, multiple await: Only 1 await will be awakened (the last one)"
- ([#13889](https://github.com/nim-lang/Nim/issues/13889))
-- Fixed "crash on openarray interator with argument in stmtListExpr"
- ([#13739](https://github.com/nim-lang/Nim/issues/13739))
-- Fixed "Some compilers on Windows don't work"
- ([#13910](https://github.com/nim-lang/Nim/issues/13910))
-- Fixed "httpclient hangs if it recieves an HTTP 204 (No Content)"
- ([#13894](https://github.com/nim-lang/Nim/issues/13894))
-- Fixed ""distinct uint64" type corruption on 32-bit, when using {.borrow.} operators"
- ([#13902](https://github.com/nim-lang/Nim/issues/13902))
-- Fixed "Regression: impossible to use typed pragmas with proc types"
- ([#13909](https://github.com/nim-lang/Nim/issues/13909))
-- Fixed "openssl wrapper corrupts stack on OpenSSL 1.1.1f + Android"
- ([#13903](https://github.com/nim-lang/Nim/issues/13903))
-- Fixed "C compile error with --gc:arc on version 1.2.0 "unknown type name 'TGenericSeq'"
- ([#13863](https://github.com/nim-lang/Nim/issues/13863))
-- Fixed "var return type for proc doesn't work at c++ backend"
- ([#13848](https://github.com/nim-lang/Nim/issues/13848))
-- Fixed "TimeFormat() should raise an error but craches at compilation time"
- ([#12864](https://github.com/nim-lang/Nim/issues/12864))
-- Fixed "gc:arc cannot fully support threadpool with FlowVar"
- ([#13781](https://github.com/nim-lang/Nim/issues/13781))
-- Fixed "simple 'var openarray[char]' assignment crash when the openarray source is a local string and using gc:arc"
- ([#14003](https://github.com/nim-lang/Nim/issues/14003))
-- Fixed "Cant use expressions with `when` in `type` sections."
- ([#14007](https://github.com/nim-lang/Nim/issues/14007))
-- Fixed "`for a in MyEnum` gives incorrect results with enum with holes"
- ([#14001](https://github.com/nim-lang/Nim/issues/14001))
-- Fixed "Trivial crash"
- ([#12741](https://github.com/nim-lang/Nim/issues/12741))
-- Fixed "Enum with holes cannot be used as Table index"
- ([#12834](https://github.com/nim-lang/Nim/issues/12834))
-- Fixed "spawn proc that uses typedesc crashes the compiler"
- ([#14014](https://github.com/nim-lang/Nim/issues/14014))
-- Fixed "Docs Search `Results` box styling is not Dark Mode Friendly"
- ([#13972](https://github.com/nim-lang/Nim/issues/13972))
-- Fixed "--gc:arc -d:useSysAssert undeclared identifier `cstderr` with newSeq"
- ([#14038](https://github.com/nim-lang/Nim/issues/14038))
-- Fixed "issues in the manual"
- ([#12486](https://github.com/nim-lang/Nim/issues/12486))
-- Fixed "Annoying warning: inherit from a more precise exception type like ValueError, IOError or OSError [InheritFromException]"
- ([#14052](https://github.com/nim-lang/Nim/issues/14052))
-- Fixed "relativePath("foo", "/") and relativePath("/", "foo") is wrong"
- ([#13222](https://github.com/nim-lang/Nim/issues/13222))
-- Fixed "[regression] `parseEnum` does not work anymore for enums with holes"
- ([#14030](https://github.com/nim-lang/Nim/issues/14030))
-- Fixed "Exception types in the stdlib should inherit from `CatchableError` or `Defect`, not `Exception`"
- ([#10288](https://github.com/nim-lang/Nim/issues/10288))
-- Fixed "Make debugSend and debugRecv procs public in smtp.nim"
- ([#12189](https://github.com/nim-lang/Nim/issues/12189))
-- Fixed "xmltree need add raw text, when add style element"
- ([#14064](https://github.com/nim-lang/Nim/issues/14064))
-- Fixed "raises requirement does not propagate to derived methods"
- ([#8481](https://github.com/nim-lang/Nim/issues/8481))
-- Fixed "tests/stdlib/tgetaddrinfo.nim fails on NetBSD"
- ([#14091](https://github.com/nim-lang/Nim/issues/14091))
-- Fixed "tests/niminaction/Chapter8/sdl/sdl_test.nim fails on NetBSD"
- ([#14088](https://github.com/nim-lang/Nim/issues/14088))
-- Fixed "Incorrect escape sequence for example in jsffi library documentation"
- ([#14110](https://github.com/nim-lang/Nim/issues/14110))
-- Fixed "HCR: Can not link exported const, in external library"
- ([#13915](https://github.com/nim-lang/Nim/issues/13915))
-- Fixed "Cannot import std/unidecode"
- ([#14112](https://github.com/nim-lang/Nim/issues/14112))
-- Fixed "macOS: dsymutil should not be called on static libraries"
- ([#14132](https://github.com/nim-lang/Nim/issues/14132))
-- Fixed "nim jsondoc -o:doc.json filename.nim fails when sequences without a type are used"
- ([#14066](https://github.com/nim-lang/Nim/issues/14066))
-- Fixed "algorithm.sortedByIt template corrupts tuple input under --gc:arc"
- ([#14079](https://github.com/nim-lang/Nim/issues/14079))
-- Fixed "Invalid C code with lvalue conversion"
- ([#14160](https://github.com/nim-lang/Nim/issues/14160))
-- Fixed "strformat: doc example fails"
- ([#14054](https://github.com/nim-lang/Nim/issues/14054))
-- Fixed "Nim doc fail to run for nim 1.2.0 (nim 1.0.4 is ok)"
- ([#13986](https://github.com/nim-lang/Nim/issues/13986))
-- Fixed "Exception when converting csize to clong"
- ([#13698](https://github.com/nim-lang/Nim/issues/13698))
-- Fixed "[Documentation] overloading using named arguments works but is not documented"
- ([#11932](https://github.com/nim-lang/Nim/issues/11932))
-- Fixed "import os + use of existsDir/dirExists/existsFile/fileExists/findExe in config.nims causes "ambiguous call' error"
- ([#14142](https://github.com/nim-lang/Nim/issues/14142))
-- Fixed "import os + use of existsDir/dirExists/existsFile/fileExists/findExe in config.nims causes "ambiguous call' error"
- ([#14142](https://github.com/nim-lang/Nim/issues/14142))
-- Fixed "runnableExamples doc gen crashes compiler with `except Exception as e` syntax"
- ([#14177](https://github.com/nim-lang/Nim/issues/14177))
-- Fixed "[ARC] Segfault with cyclic references (?)"
- ([#14159](https://github.com/nim-lang/Nim/issues/14159))
-- Fixed "Semcheck regression when accessing a static parameter in proc"
- ([#14136](https://github.com/nim-lang/Nim/issues/14136))
-- Fixed "iterator walkDir doesn't work with -d:useWinAnsi"
- ([#14201](https://github.com/nim-lang/Nim/issues/14201))
-- Fixed "cas is wrong for tcc"
- ([#14151](https://github.com/nim-lang/Nim/issues/14151))
-- Fixed "proc execCmdEx doesn't work with -d:useWinAnsi"
- ([#14203](https://github.com/nim-lang/Nim/issues/14203))
-- Fixed "Use -d:nimEmulateOverflowChecks by default?"
- ([#14209](https://github.com/nim-lang/Nim/issues/14209))
-- Fixed "Old sequences with destructor objects bug"
- ([#14217](https://github.com/nim-lang/Nim/issues/14217))
-- Fixed "[ARC] ICE when changing the discriminant of a return value"
- ([#14244](https://github.com/nim-lang/Nim/issues/14244))
-- Fixed "[ARC] ICE with static objects"
- ([#14236](https://github.com/nim-lang/Nim/issues/14236))
-- Fixed "[ARC] "internal error: environment misses: a" in a finalizer"
- ([#14243](https://github.com/nim-lang/Nim/issues/14243))
-- Fixed "[ARC] compile failure using repr with object containing `ref seq[string]`"
- ([#14270](https://github.com/nim-lang/Nim/issues/14270))
-- Fixed "[ARC] implicit move on last use happening on non-last use"
- ([#14269](https://github.com/nim-lang/Nim/issues/14269))
-- Fixed "[ARC] Compiler crash with a recursive non-ref object variant"
- ([#14294](https://github.com/nim-lang/Nim/issues/14294))
-- Fixed "htmlparser.parseHtml behaves differently using --gc:arc or --gc:orc"
- ([#13946](https://github.com/nim-lang/Nim/issues/13946))
-- Fixed "Invalid return value of openProcess is NULL rather than INVALID_HANDLE_VALUE(-1) in windows"
- ([#14289](https://github.com/nim-lang/Nim/issues/14289))
-- Fixed "ARC codegen bug with inline iterators"
- ([#14219](https://github.com/nim-lang/Nim/issues/14219))
-- Fixed "Building koch on OpenBSD fails unless the Nim directory is in `$PATH`"
- ([#13758](https://github.com/nim-lang/Nim/issues/13758))
-- Fixed "[gc:arc] case object assignment SIGSEGV: destroy not called for primitive type "
- ([#14312](https://github.com/nim-lang/Nim/issues/14312))
-- Fixed "Crash when using thread and --gc:arc "
- ([#13881](https://github.com/nim-lang/Nim/issues/13881))
-- Fixed "Getting "Warning: Cannot prove that 'result' is initialized" for an importcpp'd proc with `var T` return type"
- ([#14314](https://github.com/nim-lang/Nim/issues/14314))
-- Fixed "`nim cpp -r --gc:arc` segfaults on caught AssertionError"
- ([#13071](https://github.com/nim-lang/Nim/issues/13071))
-- Fixed "tests/async/tasyncawait.nim is recently very flaky"
- ([#14320](https://github.com/nim-lang/Nim/issues/14320))
-- Fixed "Documentation nonexistent quitprocs module"
- ([#14331](https://github.com/nim-lang/Nim/issues/14331))
-- Fixed "SIGSEV encountered when creating threads in a loop w/ --gc:arc"
- ([#13935](https://github.com/nim-lang/Nim/issues/13935))
-- Fixed "nim-gdb is missing from all released packages"
- ([#13104](https://github.com/nim-lang/Nim/issues/13104))
-- Fixed "sysAssert error with gc:arc on 3 line program"
- ([#13862](https://github.com/nim-lang/Nim/issues/13862))
-- Fixed "compiler error with inline async proc and pragma"
- ([#13998](https://github.com/nim-lang/Nim/issues/13998))
-- Fixed "[ARC] Compiler crash when adding to a seq[ref Object]"
- ([#14333](https://github.com/nim-lang/Nim/issues/14333))
-- Fixed "nimvm: sysFatal: unhandled exception: 'sons' is not accessible using discriminant 'kind' of type 'TNode' [FieldError]"
- ([#14340](https://github.com/nim-lang/Nim/issues/14340))
-- Fixed "[Regression] karax events are not firing "
- ([#14350](https://github.com/nim-lang/Nim/issues/14350))
-- Fixed "odbcsql module has some wrong integer types"
- ([#9771](https://github.com/nim-lang/Nim/issues/9771))
-- Fixed "db_sqlite needs sqlPrepared"
- ([#13559](https://github.com/nim-lang/Nim/issues/13559))
-- Fixed "[Regression] `createThread` is not GC-safe"
- ([#14370](https://github.com/nim-lang/Nim/issues/14370))
-- Fixed "Broken example on hot code reloading"
- ([#14380](https://github.com/nim-lang/Nim/issues/14380))
-- Fixed "runnableExamples block with `except` on specified error fails with `nim doc`"
- ([#12746](https://github.com/nim-lang/Nim/issues/12746))
-- Fixed "compiler as a library: findNimStdLibCompileTime fails to find system.nim"
- ([#12293](https://github.com/nim-lang/Nim/issues/12293))
-- Fixed "5 bugs with importcpp exceptions"
- ([#14369](https://github.com/nim-lang/Nim/issues/14369))
-- Fixed "Docs shouldn't collapse pragmas inside runnableExamples/code blocks"
- ([#14174](https://github.com/nim-lang/Nim/issues/14174))
-- Fixed "Bad codegen/emit for hashes.hiXorLo in some contexts."
- ([#14394](https://github.com/nim-lang/Nim/issues/14394))
-- Fixed "Boehm GC does not scan thread-local storage"
- ([#14364](https://github.com/nim-lang/Nim/issues/14364))
-- Fixed "RVO not exception safe"
- ([#14126](https://github.com/nim-lang/Nim/issues/14126))
-- Fixed "runnableExamples that are only compiled"
- ([#10731](https://github.com/nim-lang/Nim/issues/10731))
-- Fixed "`foldr` raises IndexError when called on sequence"
- ([#14404](https://github.com/nim-lang/Nim/issues/14404))
-- Fixed "moveFile does not overwrite destination file"
- ([#14057](https://github.com/nim-lang/Nim/issues/14057))
-- Fixed "doc2 outputs in current work dir"
- ([#6583](https://github.com/nim-lang/Nim/issues/6583))
-- Fixed "[docgen] proc doc comments silently omitted after 1st runnableExamples"
- ([#9227](https://github.com/nim-lang/Nim/issues/9227))
-- Fixed "`nim doc --project` shows '@@/' instead of '../' for relative paths to submodules"
- ([#14448](https://github.com/nim-lang/Nim/issues/14448))
-- Fixed "re, nre have wrong `start` semantics"
- ([#14284](https://github.com/nim-lang/Nim/issues/14284))
-- Fixed "runnableExamples should preserve source code doc comments, strings, and (maybe) formatting"
- ([#8871](https://github.com/nim-lang/Nim/issues/8871))
-- Fixed "`nim doc ..` fails when runnableExamples uses `$` [devel] [regression]"
- ([#14485](https://github.com/nim-lang/Nim/issues/14485))
-- Fixed "`items` is 20%~30% slower than iteration via an index"
- ([#14421](https://github.com/nim-lang/Nim/issues/14421))
-- Fixed "ARC: unreliable setLen "
- ([#14495](https://github.com/nim-lang/Nim/issues/14495))
-- Fixed "lent is unsafe: after #14447 you can modify variables with "items" loop for sequences"
- ([#14498](https://github.com/nim-lang/Nim/issues/14498))
-- Fixed "`var op = fn()` wrongly gives warning `ObservableStores` with `object of RootObj` type"
- ([#14514](https://github.com/nim-lang/Nim/issues/14514))
-- Fixed "Compiler assertion"
- ([#14562](https://github.com/nim-lang/Nim/issues/14562))
-- Fixed "Can't get `ord` of a value of a Range type in the JS backend "
- ([#14570](https://github.com/nim-lang/Nim/issues/14570))
-- Fixed "js: can't take addr of param (including implicitly via `lent`)"
- ([#14576](https://github.com/nim-lang/Nim/issues/14576))
-- Fixed "{.noinit.} ignored in for loop -> bad codegen for non-movable types"
- ([#14118](https://github.com/nim-lang/Nim/issues/14118))
-- Fixed "generic destructor gives: `Error: unresolved generic parameter`"
- ([#14315](https://github.com/nim-lang/Nim/issues/14315))
-- Fixed "Memory leak with arc gc"
- ([#14568](https://github.com/nim-lang/Nim/issues/14568))
-- Fixed "escape analysis broken with `lent`"
- ([#14557](https://github.com/nim-lang/Nim/issues/14557))
-- Fixed "`wrapWords` seems to ignore linebreaks when wrapping, leaving breaks in the wrong place"
- ([#14579](https://github.com/nim-lang/Nim/issues/14579))
-- Fixed "`lent` gives wrong results with -d:release"
- ([#14578](https://github.com/nim-lang/Nim/issues/14578))
-- Fixed "Nested await expressions regression: `await a(await expandValue())` doesnt compile"
- ([#14279](https://github.com/nim-lang/Nim/issues/14279))
-- Fixed "windows CI docs fails with strange errors"
- ([#14545](https://github.com/nim-lang/Nim/issues/14545))
-- Fixed "[CI] tests/async/tioselectors.nim flaky test for freebsd + OSX CI"
- ([#13166](https://github.com/nim-lang/Nim/issues/13166))
-- Fixed "seq.setLen sometimes doesn't zero memory"
- ([#14655](https://github.com/nim-lang/Nim/issues/14655))
-- Fixed "`nim dump` is roughly 100x slower in 1.3 versus 1.2"
- ([#14179](https://github.com/nim-lang/Nim/issues/14179))
-- Fixed "Regression: devel docgen cannot generate document for method"
- ([#14691](https://github.com/nim-lang/Nim/issues/14691))
-- Fixed "recently flaky tests/async/t7758.nim"
- ([#14685](https://github.com/nim-lang/Nim/issues/14685))
-- Fixed "Bind no longer working in generic procs."
- ([#11811](https://github.com/nim-lang/Nim/issues/11811))
-- Fixed "The pegs module doesn't work with generics!"
- ([#14718](https://github.com/nim-lang/Nim/issues/14718))
-- Fixed "Defer is not properly working for asynchronous procedures."
- ([#13899](https://github.com/nim-lang/Nim/issues/13899))
-- Fixed "Add an ARC test with threads in a loop"
- ([#14690](https://github.com/nim-lang/Nim/issues/14690))
-- Fixed "[goto exceptions] {.noReturn.} pragma is not detected in a case expression"
- ([#14458](https://github.com/nim-lang/Nim/issues/14458))
-- Fixed "[exceptions:goto] C compiler error with dynlib pragma calling a proc"
- ([#14240](https://github.com/nim-lang/Nim/issues/14240))
-- Fixed "Cannot borrow var float64 in infix assignment"
- ([#14440](https://github.com/nim-lang/Nim/issues/14440))
-- Fixed "lib/pure/memfiles.nim: compilation error with --taintMode:on"
- ([#14760](https://github.com/nim-lang/Nim/issues/14760))
-- Fixed "newWideCString allocates a multiple of the memory needed"
- ([#14750](https://github.com/nim-lang/Nim/issues/14750))
-- Fixed "Nim source archive install: 'install.sh' fails with error: cp: cannot stat 'bin/nim-gdb': No such file or directory"
- ([#14748](https://github.com/nim-lang/Nim/issues/14748))
-- Fixed "`nim cpp -r tests/exception/t9657` hangs"
- ([#10343](https://github.com/nim-lang/Nim/issues/10343))
-- Fixed "Detect tool fails on FreeBSD"
- ([#14715](https://github.com/nim-lang/Nim/issues/14715))
-- Fixed "compiler crash: `findUnresolvedStatic` "
- ([#14802](https://github.com/nim-lang/Nim/issues/14802))
-- Fixed "seq namespace (?) regression"
- ([#4796](https://github.com/nim-lang/Nim/issues/4796))
-- Fixed "Possible out of bounds string access in std/colors parseColor and isColor"
- ([#14839](https://github.com/nim-lang/Nim/issues/14839))
-- Fixed "compile error on latest devel with orc and ssl"
- ([#14647](https://github.com/nim-lang/Nim/issues/14647))
-- Fixed "[minor] `$` wrong for type tuple"
- ([#13432](https://github.com/nim-lang/Nim/issues/13432))
-- Fixed "Documentation missing on devel asyncftpclient"
- ([#14846](https://github.com/nim-lang/Nim/issues/14846))
-- Fixed "nimpretty is confused with a trailing comma in enum definition"
- ([#14401](https://github.com/nim-lang/Nim/issues/14401))
-- Fixed "Output arguments get ignored when compiling with --app:staticlib"
- ([#12745](https://github.com/nim-lang/Nim/issues/12745))
-- Fixed "[ARC] destructive move destroys the object too early"
- ([#14396](https://github.com/nim-lang/Nim/issues/14396))
-- Fixed "highlite.getNextToken() crashes if the buffer string is "echo "\"""
- ([#14830](https://github.com/nim-lang/Nim/issues/14830))
-- Fixed "Memory corruption with --gc:arc with a seq of objects with an empty body."
- ([#14472](https://github.com/nim-lang/Nim/issues/14472))
-- Fixed "Stropped identifiers don't work as field names in tuple literals"
- ([#14911](https://github.com/nim-lang/Nim/issues/14911))
-- Fixed "Please revert my commit"
- ([#14930](https://github.com/nim-lang/Nim/issues/14930))
-- Fixed "[ARC] C compiler error with inline iterators and imports"
- ([#14864](https://github.com/nim-lang/Nim/issues/14864))
-- Fixed "AsyncHttpClient segfaults with gc:orc, possibly memory corruption"
- ([#14402](https://github.com/nim-lang/Nim/issues/14402))
-- Fixed "[ARC] Template with a block evaluating to a GC'd value results in a compiler crash"
- ([#14899](https://github.com/nim-lang/Nim/issues/14899))
-- Fixed "[ARC] Weird issue with if expressions and templates"
- ([#14900](https://github.com/nim-lang/Nim/issues/14900))
-- Fixed "xmlparser does not compile on devel"
- ([#14805](https://github.com/nim-lang/Nim/issues/14805))
-- Fixed "returning lent T from a var T param gives codegen errors or SIGSEGV"
- ([#14878](https://github.com/nim-lang/Nim/issues/14878))
-- Fixed "[ARC] Weird issue with if expressions and templates"
- ([#14900](https://github.com/nim-lang/Nim/issues/14900))
-- Fixed "threads:on + gc:orc + unittest = C compiler errors"
- ([#14865](https://github.com/nim-lang/Nim/issues/14865))
-- Fixed "mitems, mpairs doesn't work at compile time anymore"
- ([#12129](https://github.com/nim-lang/Nim/issues/12129))
-- Fixed "strange result from executing code in const expression"
- ([#10465](https://github.com/nim-lang/Nim/issues/10465))
-- Fixed "Same warning printed 3 times"
- ([#11009](https://github.com/nim-lang/Nim/issues/11009))
-- Fixed "type alias for generic typeclass doesn't work"
- ([#4668](https://github.com/nim-lang/Nim/issues/4668))
-- Fixed "exceptions:goto Bug devel codegen lvalue NIM_FALSE=NIM_FALSE"
- ([#14925](https://github.com/nim-lang/Nim/issues/14925))
-- Fixed "the --useVersion:1.0 no longer works in devel"
- ([#14912](https://github.com/nim-lang/Nim/issues/14912))
-- Fixed "template declaration of iterator doesn't compile"
- ([#4722](https://github.com/nim-lang/Nim/issues/4722))
-- Fixed "Compiler crash on type inheritance with static generic parameter and equality check"
- ([#12571](https://github.com/nim-lang/Nim/issues/12571))
-- Fixed "Nim crashes while handling a cast in async circumstances."
- ([#13815](https://github.com/nim-lang/Nim/issues/13815))
-- Fixed "[ARC] Internal compiler error when calling an iterator from an inline proc "
- ([#14383](https://github.com/nim-lang/Nim/issues/14383))
-- Fixed ""Cannot instantiate" error when template uses generic type"
- ([#5926](https://github.com/nim-lang/Nim/issues/5926))
-- Fixed "Different raises behaviour for newTerminal between Linux and Windows"
- ([#12759](https://github.com/nim-lang/Nim/issues/12759))
-- Fixed "Expand on a type (that defines a proc type) in error message "
- ([#6608](https://github.com/nim-lang/Nim/issues/6608))
-- Fixed "unittest require quits program with an exit code of 0"
- ([#14475](https://github.com/nim-lang/Nim/issues/14475))
-- Fixed "Range type: Generics vs concrete type, semcheck difference."
- ([#8426](https://github.com/nim-lang/Nim/issues/8426))
-- Fixed "[Macro] Type mismatch when parameter name is the same as a field"
- ([#13253](https://github.com/nim-lang/Nim/issues/13253))
-- Fixed "Generic instantiation failure when converting a sequence of circular generic types to strings"
- ([#10396](https://github.com/nim-lang/Nim/issues/10396))
-- Fixed "initOptParser ignores argument after value option with empty value."
- ([#13086](https://github.com/nim-lang/Nim/issues/13086))
-- Fixed "[ARC] proc with both explicit and implicit return results in a C compiler error"
- ([#14985](https://github.com/nim-lang/Nim/issues/14985))
-- Fixed "Alias type forgets implicit generic params depending on order"
- ([#14990](https://github.com/nim-lang/Nim/issues/14990))
-- Fixed "[ARC] sequtils.insert has different behaviour between ARC/refc"
- ([#14994](https://github.com/nim-lang/Nim/issues/14994))
-- Fixed "The documentation for "hot code reloading" references a non-existent npm package"
- ([#13621](https://github.com/nim-lang/Nim/issues/13621))
-- Fixed "existsDir deprecated but breaking `dir` undeclared"
- ([#15006](https://github.com/nim-lang/Nim/issues/15006))
-- Fixed "uri.decodeUrl crashes on incorrectly formatted input"
- ([#14082](https://github.com/nim-lang/Nim/issues/14082))
-- Fixed "testament incorrectly reports time for tests, leading to wrong conclusions"
- ([#14822](https://github.com/nim-lang/Nim/issues/14822))
-- Fixed "Calling peekChar with Stream returned from osproc.outputStream generate runtime error"
- ([#14906](https://github.com/nim-lang/Nim/issues/14906))
-- Fixed "localPassC pragma should come *after* other flags"
- ([#14194](https://github.com/nim-lang/Nim/issues/14194))
-- Fixed ""Could not load" dynamic library at runtime because of hidden dependency"
- ([#2408](https://github.com/nim-lang/Nim/issues/2408))
-- Fixed "--gc:arc generate invalid code for {.global.} (`«nimErr_» in NIM_UNLIKELY`)"
- ([#14480](https://github.com/nim-lang/Nim/issues/14480))
-- Fixed "Using `^` from stdlib/math along with converters gives a match for types that aren't SomeNumber"
- ([#15033](https://github.com/nim-lang/Nim/issues/15033))
-- Fixed "[ARC] Weird exception behaviour from doAssertRaises"
- ([#15026](https://github.com/nim-lang/Nim/issues/15026))
-- Fixed "[ARC] Compiler crash declaring a finalizer proc directly in 'new'"
- ([#15044](https://github.com/nim-lang/Nim/issues/15044))
-- Fixed "[ARC] C compiler error when creating a var of a const seq"
- ([#15036](https://github.com/nim-lang/Nim/issues/15036))
-- Fixed "code with named arguments in proc of winim/com can not been compiled"
- ([#15056](https://github.com/nim-lang/Nim/issues/15056))
-- Fixed "javascript backend produces javascript code with syntax error in object syntax"
- ([#14534](https://github.com/nim-lang/Nim/issues/14534))
-- Fixed "--gc:arc should be ignored in JS mode."
- ([#14684](https://github.com/nim-lang/Nim/issues/14684))
-- Fixed "arc: C compilation error with imported global code using a closure iterator"
- ([#12990](https://github.com/nim-lang/Nim/issues/12990))
-- Fixed "[ARC] Crash when modifying a string with mitems iterator"
- ([#15052](https://github.com/nim-lang/Nim/issues/15052))
-- Fixed "[ARC] SIGSEGV when calling a closure as a tuple field in a seq"
- ([#15038](https://github.com/nim-lang/Nim/issues/15038))
-- Fixed "pass varargs[seq[T]] to iterator give empty seq "
- ([#12576](https://github.com/nim-lang/Nim/issues/12576))
-- Fixed "Compiler crashes when using string as object variant selector with else branch"
- ([#14189](https://github.com/nim-lang/Nim/issues/14189))
-- Fixed "JS compiler error related to implicit return and return var type"
- ([#11354](https://github.com/nim-lang/Nim/issues/11354))
-- Fixed "`nkRecWhen` causes internalAssert in semConstructFields"
- ([#14698](https://github.com/nim-lang/Nim/issues/14698))
-- Fixed "Memory leaks with async (closure iterators?) under ORC"
- ([#15076](https://github.com/nim-lang/Nim/issues/15076))
-- Fixed "strutil.insertSep() fails on negative numbers"
- ([#11352](https://github.com/nim-lang/Nim/issues/11352))
-- Fixed "Constructing a uint64 range on a 32-bit machine leads to incorrect codegen"
- ([#14616](https://github.com/nim-lang/Nim/issues/14616))
-- Fixed "heapqueue pushpop() proc doesn't compile"
- ([#14139](https://github.com/nim-lang/Nim/issues/14139))
-- Fixed "[ARC] SIGSEGV when trying to swap in a literal/const string"
- ([#15112](https://github.com/nim-lang/Nim/issues/15112))
-- Fixed "Defer and --gc:arc"
- ([#15071](https://github.com/nim-lang/Nim/issues/15071))
-- Fixed "internal error: compiler/semobjconstr.nim(324, 20) example"
- ([#15111](https://github.com/nim-lang/Nim/issues/15111))
-- Fixed "[ARC] Sequence "disappears" with a table inside of a table with an object variant"
- ([#15122](https://github.com/nim-lang/Nim/issues/15122))
-- Fixed "[ARC] SIGSEGV with tuple assignment caused by cursor inference"
- ([#15130](https://github.com/nim-lang/Nim/issues/15130))
-- Fixed "Issue with --gc:arc at compile time"
- ([#15129](https://github.com/nim-lang/Nim/issues/15129))
-- Fixed "Writing an empty string to an AsyncFile raises an IndexDefect"
- ([#15148](https://github.com/nim-lang/Nim/issues/15148))
-- Fixed "Compiler is confused about call convention of function with nested closure"
- ([#5688](https://github.com/nim-lang/Nim/issues/5688))
-- Fixed "Nil check on each field fails in generic function"
- ([#15101](https://github.com/nim-lang/Nim/issues/15101))
-- Fixed "{.nimcall.} convention won't avoid the creation of closures"
- ([#8473](https://github.com/nim-lang/Nim/issues/8473))
-- Fixed "smtp.nim(161, 40) Error: type mismatch: got but expected 'SslContext = void'"
- ([#15177](https://github.com/nim-lang/Nim/issues/15177))
-- Fixed "[strscans] scanf doesn't match a single character with $+ if it's the end of the string"
- ([#15064](https://github.com/nim-lang/Nim/issues/15064))
-- Fixed "Crash and incorrect return values when using readPasswordFromStdin on Windows."
- ([#15207](https://github.com/nim-lang/Nim/issues/15207))
-- Fixed "Possible capture error with fieldPairs and genericParams"
- ([#15221](https://github.com/nim-lang/Nim/issues/15221))
-- Fixed "The StmtList processing of template parameters can lead to unexpected errors"
- ([#5691](https://github.com/nim-lang/Nim/issues/5691))
-- Fixed "[ARC] C compiler error when passing a var openArray to a sink openArray"
- ([#15035](https://github.com/nim-lang/Nim/issues/15035))
-- Fixed "Inconsistent unsigned -> signed RangeDefect usage across integer sizes"
- ([#15210](https://github.com/nim-lang/Nim/issues/15210))
-- Fixed "toHex results in RangeDefect exception when used with large uint64"
- ([#15257](https://github.com/nim-lang/Nim/issues/15257))
-- Fixed "Arc sink arg crash"
- ([#15238](https://github.com/nim-lang/Nim/issues/15238))
-- Fixed "SQL escape in db_mysql is not enough"
- ([#15219](https://github.com/nim-lang/Nim/issues/15219))
-- Fixed "Mixing 'return' with expressions is allowed in 1.2"
- ([#15280](https://github.com/nim-lang/Nim/issues/15280))
-- Fixed "os.getFileInfo() causes ICE with --gc:arc on Windows"
- ([#15286](https://github.com/nim-lang/Nim/issues/15286))
-- Fixed "[ARC] Sequence "disappears" with a table inside of a table with an object variant"
- ([#15122](https://github.com/nim-lang/Nim/issues/15122))
-- Fixed "Documentation regression jsre module missing"
- ([#15183](https://github.com/nim-lang/Nim/issues/15183))
-- Fixed "CountTable.smallest/largest() on empty table either asserts or gives bogus answer"
- ([#15021](https://github.com/nim-lang/Nim/issues/15021))
-- Fixed "[Regression] Parser regression"
- ([#15305](https://github.com/nim-lang/Nim/issues/15305))
-- Fixed "[ARC] SIGSEGV with tuple unpacking caused by cursor inference"
- ([#15147](https://github.com/nim-lang/Nim/issues/15147))
-- Fixed "LwIP/FreeRTOS compile error - missing SIGPIPE and more "
- ([#15302](https://github.com/nim-lang/Nim/issues/15302))
-- Fixed "Memory leaks with async (closure iterators?) under ORC"
- ([#15076](https://github.com/nim-lang/Nim/issues/15076))
-- Fixed "Bug compiling with --gc:arg or --gc:orc"
- ([#15325](https://github.com/nim-lang/Nim/issues/15325))
-- Fixed "memory corruption in tmarshall.nim"
- ([#9754](https://github.com/nim-lang/Nim/issues/9754))
-- Fixed "typed macros break generic proc definitions"
- ([#15326](https://github.com/nim-lang/Nim/issues/15326))
-- Fixed "nim doc2 ignores --docSeeSrcUrl parameter"
- ([#6071](https://github.com/nim-lang/Nim/issues/6071))
-- Fixed "The decodeData Iterator from cgi module crash"
- ([#15369](https://github.com/nim-lang/Nim/issues/15369))
-- Fixed "|| iterator generates invalid code when compiling with --debugger:native"
- ([#9710](https://github.com/nim-lang/Nim/issues/9710))
-- Fixed "Wrong number of variables"
- ([#15360](https://github.com/nim-lang/Nim/issues/15360))
-- Fixed "Coercions with distinct types should traverse pointer modifiers transparently."
- ([#7165](https://github.com/nim-lang/Nim/issues/7165))
-- Fixed "Error with distinct generic TableRef"
- ([#6060](https://github.com/nim-lang/Nim/issues/6060))
-- Fixed "Support images in nim docgen"
- ([#6430](https://github.com/nim-lang/Nim/issues/6430))
-- Fixed "Regression. Double sem check for procs."
- ([#15389](https://github.com/nim-lang/Nim/issues/15389))
-- Fixed "uri.nim url with literal ipv6 address is printed wrong, and cannot parsed again"
- ([#15333](https://github.com/nim-lang/Nim/issues/15333))
-- Fixed "[ARC] Object variant gets corrupted with cursor inference"
- ([#15361](https://github.com/nim-lang/Nim/issues/15361))
-- Fixed "`nim doc ..` compiler crash (regression 0.19.6 => 1.0)"
- ([#14474](https://github.com/nim-lang/Nim/issues/14474))
-- Fixed "cannot borrow result; what it borrows from is potentially mutated"
- ([#15403](https://github.com/nim-lang/Nim/issues/15403))
-- Fixed "memory corruption for seq.add(seq) with gc:arc and d:useMalloc "
- ([#14983](https://github.com/nim-lang/Nim/issues/14983))
-- Fixed "DocGen HTML output appears improperly when encountering text immediately after/before inline monospace; in some cases won't compile"
- ([#11537](https://github.com/nim-lang/Nim/issues/11537))
-- Fixed "Deepcopy in arc crashes"
- ([#15405](https://github.com/nim-lang/Nim/issues/15405))
-- Fixed "pop pragma takes invalid input"
- ([#15430](https://github.com/nim-lang/Nim/issues/15430))
-- Fixed "tests/stdlib/tgetprotobyname fails on NetBSD"
- ([#15452](https://github.com/nim-lang/Nim/issues/15452))
-- Fixed "defer doesnt work with block, break and await"
- ([#15243](https://github.com/nim-lang/Nim/issues/15243))
-- Fixed "tests/stdlib/tssl failing on NetBSD"
- ([#15493](https://github.com/nim-lang/Nim/issues/15493))
-- Fixed "strictFuncs doesn't seem to catch simple ref mutation"
- ([#15508](https://github.com/nim-lang/Nim/issues/15508))
-- Fixed "Sizeof of case object is incorrect. Showstopper"
- ([#15516](https://github.com/nim-lang/Nim/issues/15516))
-- Fixed "[ARC] Internal error when trying to use a parallel for loop"
- ([#15512](https://github.com/nim-lang/Nim/issues/15512))
-- Fixed "[ARC] Type-bound assign op is not being generated"
- ([#15510](https://github.com/nim-lang/Nim/issues/15510))
-- Fixed "[ARC] Crash when adding openArray proc argument to a local seq"
- ([#15511](https://github.com/nim-lang/Nim/issues/15511))
-- Fixed "VM: const case object gets some fields zeroed out at runtime"
- ([#13081](https://github.com/nim-lang/Nim/issues/13081))
-- Fixed "regression(1.2.6 => devel): VM: const case object field access gives: 'sons' is not accessible"
- ([#15532](https://github.com/nim-lang/Nim/issues/15532))
-- Fixed "Csources: huge size increase (x2.3) in 0.20"
- ([#12027](https://github.com/nim-lang/Nim/issues/12027))
-- Fixed "Out of date error message for GC options"
- ([#15547](https://github.com/nim-lang/Nim/issues/15547))
-- Fixed "dbQuote additional escape regression"
- ([#15560](https://github.com/nim-lang/Nim/issues/15560))
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
deleted file mode 100644
index 524599b11..000000000
--- a/changelogs/changelog_X_XX_X.md
+++ /dev/null
@@ -1,23 +0,0 @@
-# v1.xx.x - yyyy-mm-dd
-
-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
-
-
-## Compiler changes
-
-
-## Tool changes
-
diff --git a/changelogs/readme.md b/changelogs/readme.md
deleted file mode 100644
index 3ff1e1b7b..000000000
--- a/changelogs/readme.md
+++ /dev/null
@@ -1,61 +0,0 @@
-# Update changelog
-After each release, call:
-```
-git mv changelog.md changelogs/changelog_0_19_0.md # use correct version
-cp changelogs/changelog_X_XX_X.md changelog.md
-git add changelog.md
-```
-
-## Recent changelogs are saved here (with their git history)
-
-## Older changelogs are stored in https://github.com/nim-lang/website
-
-## source files:
-* https://github.com/nim-lang/website/blob/master/jekyll/_posts/2009-12-21-version-086-released.md
-* https://github.com/nim-lang/website/blob/master/jekyll/_posts/2010-03-14-version-088-released.md
-* https://github.com/nim-lang/website/blob/master/jekyll/_posts/2010-10-20-version-0810-released.md
-* https://github.com/nim-lang/website/blob/master/jekyll/_posts/2011-07-10-version-0812-released.md
-* https://github.com/nim-lang/website/blob/master/jekyll/_posts/2012-02-09-version-0814-released.md
-* https://github.com/nim-lang/website/blob/master/jekyll/_posts/2012-09-23-version-090-released.md
-* https://github.com/nim-lang/website/blob/master/jekyll/_posts/2013-05-20-version-092-released.md
-* https://github.com/nim-lang/website/blob/master/jekyll/_posts/2014-04-21-version-094-released.md
-* https://github.com/nim-lang/website/blob/master/jekyll/_posts/2014-10-19-version-096-released.md
-* https://github.com/nim-lang/website/blob/master/jekyll/_posts/2014-12-29-version-0102-released.md
-* https://github.com/nim-lang/website/blob/master/jekyll/_posts/2015-04-30-version-0110-released.md
-* https://github.com/nim-lang/website/blob/master/jekyll/_posts/2015-05-04-version-0112-released.md
-* https://github.com/nim-lang/website/blob/master/jekyll/_posts/2015-10-27-version-0120-released.md
-* https://github.com/nim-lang/website/blob/master/jekyll/_posts/2016-01-18-version-0130-released.md
-* https://github.com/nim-lang/website/blob/master/jekyll/_posts/2016-06-07-version-0140-released.md
-* https://github.com/nim-lang/website/blob/master/jekyll/_posts/2016-06-09-version-0142-released.md
-* https://github.com/nim-lang/website/blob/master/jekyll/_posts/2016-09-30-version-0150-released.md
-* https://github.com/nim-lang/website/blob/master/jekyll/_posts/2016-10-23-version-0152-released.md
-* https://github.com/nim-lang/website/blob/master/jekyll/_posts/2017-01-08-version-0160-released.md
-* https://github.com/nim-lang/website/blob/master/jekyll/_posts/2017-05-17-version-0170-released.md
-* https://github.com/nim-lang/website/blob/master/jekyll/_posts/2017-09-07-version-0172-released.md
-* https://github.com/nim-lang/website/blob/master/jekyll/_posts/2018-03-01-version-0180-released.md
-* https://github.com/nim-lang/website/blob/master/jekyll/_posts/2018-09-26-version-0190-released.md
-
-## urls:
-* https://nim-lang.org/blog/2009/12/21/version-086-released.html
-* https://nim-lang.org/blog/2010/03/14/version-088-released.html
-* https://nim-lang.org/blog/2010/10/20/version-0810-released.html
-* https://nim-lang.org/blog/2011/07/10/version-0812-released.html
-* https://nim-lang.org/blog/2012/02/09/version-0814-released.html
-* https://nim-lang.org/blog/2012/09/23/version-090-released.html
-* https://nim-lang.org/blog/2013/05/20/version-092-released.html
-* https://nim-lang.org/blog/2014/04/21/version-094-released.html
-* https://nim-lang.org/blog/2014/10/19/version-096-released.html
-* https://nim-lang.org/blog/2014/12/29/version-0102-released.html
-* https://nim-lang.org/blog/2015/04/30/version-0110-released.html
-* https://nim-lang.org/blog/2015/05/04/version-0112-released.html
-* https://nim-lang.org/blog/2015/10/27/version-0120-released.html
-* https://nim-lang.org/blog/2016/01/18/version-0130-released.html
-* https://nim-lang.org/blog/2016/06/07/version-0140-released.html
-* https://nim-lang.org/blog/2016/06/09/version-0142-released.html
-* https://nim-lang.org/blog/2016/09/30/version-0150-released.html
-* https://nim-lang.org/blog/2016/10/23/version-0152-released.html
-* https://nim-lang.org/blog/2017/01/08/version-0160-released.html
-* https://nim-lang.org/blog/2017/05/17/version-0170-released.html
-* https://nim-lang.org/blog/2017/09/07/version-0172-released.html
-* https://nim-lang.org/blog/2018/03/01/version-0180-released.html
-* https://nim-lang.org/blog/2018/09/26/version-0190-released.html
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_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/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/compiler.nimble b/compiler.nimble
new file mode 100644
index 000000000..c63fe49bf
--- /dev/null
+++ b/compiler.nimble
@@ -0,0 +1,11 @@
+[Package]
+name = "compiler"
+version = "0.14.3"
+author = "Andreas Rumpf"
+description = "Compiler package providing the compiler sources as a library."
+license = "MIT"
+
+InstallDirs = "doc, compiler"
+
+[Deps]
+Requires: "nim >= 0.13.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..4b592ee60 100644
--- a/compiler/aliases.nim
+++ b/compiler/aliases.nim
@@ -10,10 +10,7 @@
## Simple alias analysis for the HLO and the code generators.
import
- ast, astalgo, types, trees, intsets
-
-when defined(nimPreviewSlimSystem):
- import std/assertions
+ ast, astalgo, types, trees, intsets, msgs
type
TAnalysisResult* = enum
@@ -25,22 +22,22 @@ proc isPartOfAux(n: PNode, b: PType, marker: var IntSet): TAnalysisResult =
result = arNo
case n.kind
of nkRecList:
- for i in 0..= 2 and b.len >= 2:
+ if len(a) >= 2 and len(b) >= 2:
# array accesses:
if result == arYes and isDeepConstExpr(a[1]) and isDeepConstExpr(b[1]):
# we know it's the same array and we have 2 constant indexes;
@@ -187,21 +178,5 @@ proc isPartOf*(a, b: PNode): TAnalysisResult =
result = isPartOf(a[0], b)
if result == arNo: result = arMaybe
else: discard
- of nkObjConstr:
- result = arNo
- for i in 1.. 0:
- result = isPartOf(a, b[0])
else: discard
+
diff --git a/compiler/ast.nim b/compiler/ast.nim
index f93c8d910..c3bb7cd62 100644
--- a/compiler/ast.nim
+++ b/compiler/ast.nim
@@ -10,27 +10,26 @@
# abstract syntax tree + symbol table
import
- lineinfos, hashes, options, ropes, idents, int128, tables
-from strutils import toLowerAscii
-
-when defined(nimPreviewSlimSystem):
- import std/assertions
-
-export int128
+ msgs, hashes, nversion, options, strutils, securehash, ropes, idents, lists,
+ intsets, idgen
type
TCallingConvention* = enum
- ccNimCall = "nimcall" # nimcall, also the default
- ccStdCall = "stdcall" # procedure is stdcall
- ccCDecl = "cdecl" # cdecl
- ccSafeCall = "safecall" # safecall
- ccSysCall = "syscall" # system call
- ccInline = "inline" # proc should be inlined
- ccNoInline = "noinline" # proc should not be inlined
- ccFastCall = "fastcall" # fastcall (pass parameters in registers)
- ccThisCall = "thiscall" # thiscall (parameters are pushed right-to-left)
- ccClosure = "closure" # proc has a closure
- ccNoConvention = "noconv" # needed for generating proper C procs sometimes
+ ccDefault, # proc has no explicit calling convention
+ ccStdCall, # procedure is stdcall
+ ccCDecl, # cdecl
+ ccSafeCall, # safecall
+ ccSysCall, # system call
+ ccInline, # proc should be inlined
+ ccNoInline, # proc should not be inlined
+ ccFastCall, # fastcall (pass parameters in registers)
+ ccClosure, # proc has a closure
+ ccNoConvention # needed for generating proper C procs sometimes
+
+const
+ CallingConvToStr*: array[TCallingConvention, string] = ["", "stdcall",
+ "cdecl", "safecall", "syscall", "inline", "noinline", "fastcall",
+ "closure", "noconv"]
type
TNodeKind* = enum # order is extremely important, because ranges are used
@@ -63,8 +62,8 @@ type
nkTripleStrLit, # a triple string literal """
nkNilLit, # the nil literal
# end of atoms
- nkComesFrom, # "comes from" template/macro information for
- # better stack trace generation
+ nkMetaNode_Obsolete, # difficult to explain; represents itself
+ # (used for macros)
nkDotCall, # used to temporarily flag a nkCall node;
# this is used
# for transforming ``s.len`` to ``len(s)``
@@ -185,7 +184,7 @@ type
nkStmtListExpr, # a statement list followed by an expr; this is used
# to allow powerful multi-line templates
nkBlockExpr, # a statement block ending in an expr; this is used
- # to allow powerful multi-line templates that open a
+ # to allowe powerful multi-line templates that open a
# temporary scope
nkStmtListType, # a statement list ending in a type; for macros
nkBlockType, # a statement block ending in a type; for macros
@@ -207,31 +206,25 @@ 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
nkPattern, # a special pattern; used for matching
- nkHiddenTryStmt, # a hidden try statement
+ nkReturnToken, # token used for interpretation
nkClosure, # (prc, env)-pair (internally used for code gen)
nkGotoState, # used for the state machine (for iterators)
nkState, # give a label to a code section (for iterators)
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 # already 32 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
@@ -240,11 +233,8 @@ type
sfGlobal, # symbol is at global scope
sfForward, # symbol is forward declared
- sfWasForwarded, # symbol had a forward declaration
- # (implies it's too dangerous to patch its type signature)
sfImportc, # symbol is external; imported
sfExportc, # symbol is exported (under a specified name)
- sfMangleCpp, # mangle as cpp (combines with `sfExportc`)
sfVolatile, # variable is volatile
sfRegister, # variable should be placed in a register
sfPure, # object is "pure" that means it has no type-information
@@ -259,65 +249,35 @@ 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
- sfExplain, # provide more diagnostics when this symbol is used
sfError, # usage of symbol should trigger a compile-time error
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
- # deprecated and unused, except for the con
+ sfDeadCodeElim, # dead code elimination for the module is turned on
sfBorrow, # proc is borrowed
sfInfixCall, # symbol needs infix call syntax in target language;
# for interfacing with C++, JS
sfNamedParamCall, # symbol needs named parameter call syntax in target
# language; for interfacing with Objective C
sfDiscardable, # returned value may be discarded implicitly
- sfOverriden, # proc is overridden
- sfCallsite # A flag for template symbols to tell the
- # compiler it should use line information from
- # the calling side of the macro, not from the
- # implementation.
+ sfOverriden, # proc is overriden
sfGenSym # symbol is 'gensym'ed; do not add to symbol table
- sfNonReloadable # symbol will be left as-is when hot code reloading is on -
- # meaning that it won't be renamed and/or changed in any way
- sfGeneratedOp # proc is a generated '='; do not inject destructors in it
- # variable is generated closure environment; requires early
- # destruction for --newruntime.
- sfTemplateParam # symbol is a template parameter
- sfCursor # variable/field is a cursor, see RFC 177 for details
- 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]
const
+ sfFakeConst* = sfDeadCodeElim # const cannot be put into a data section
+ sfDispatcher* = sfDeadCodeElim # copied method symbol is the dispatcher
sfNoInit* = sfMainModule # don't generate code to init the variable
- sfAllUntyped* = sfVolatile # macro or template is immediately expanded \
- # in a generic context
+ sfImmediate* = sfDeadCodeElim
+ # macro or template is immediately expanded
+ # without considering any possible overloads
sfDirty* = sfPure
# template is not hygienic (old styled template)
@@ -330,18 +290,15 @@ const
sfNoForward* = sfRegister
# forward declarations are not required (per module)
- sfReorder* = sfForward
- # reordering pass is enabled
sfCompileToCpp* = sfInfixCall # compile the module as C++ code
sfCompileToObjc* = sfNamedParamCall # compile the module as Objective-C code
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
- sfCustomPragma* = sfRegister # symbol is custom pragma template
- sfTemplateRedefinition* = sfExportc # symbol is a redefinition of an earlier template
+ sfIsSelf* = sfOverriden # param is 'self'
const
# getting ready for the future expr/stmt merge
@@ -350,25 +307,17 @@ const
nkEffectList* = nkArgList
# hacks ahead: an nkEffectList is a node with 4 children:
exceptionEffects* = 0 # exceptions at position 0
- requiresEffects* = 1 # 'requires' annotation
- ensuresEffects* = 2 # 'ensures' annotation
+ usesEffects* = 1 # read effects at position 1
+ writeEffects* = 2 # write effects at position 2
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
- nkLastBlockStmts* = {nkRaiseStmt, nkReturnStmt, nkBreakStmt, nkContinueStmt}
- # these must be last statements in a block
+ effectListLen* = 4 # list of effects list
type
TTypeKind* = enum # order is important!
# Don't forget to change hti.nim if you make a change here
# XXX put this into an include file to avoid this issue!
- # several types are no longer used (guess which), but a
- # spot in the sequence is kept for backwards compatibility
- # (apparently something with bootstrapping)
- # if you need to add a type, they can apparently be reused
tyNone, tyBool, tyChar,
- tyEmpty, tyAlias, tyNil, tyUntyped, tyTyped, tyTypeDesc,
+ tyEmpty, tyArrayConstr, tyNil, tyExpr, tyStmt, tyTypeDesc,
tyGenericInvocation, # ``T[a, b]`` for types to invoke
tyGenericBody, # ``T[a, b, body]`` last parameter is the body
tyGenericInst, # ``T[a, b, realInstance]`` instantiated generic type
@@ -391,73 +340,64 @@ 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,
- tyOwned, tySink, tyLent,
- tyVarargs,
- tyUncheckedArray
- # An array with boundaries [0,+∞]
-
+ tyBigNum,
+ tyConst, tyMutable, tyVarargs,
+ tyIter, # unused
tyProxy # used as errornous type (for idetools)
- tyBuiltInTypeClass
+ tyBuiltInTypeClass #\
# Type such as the catch-all object, tuple, seq, etc
- tyUserTypeClass
+ tyUserTypeClass #\
# the body of a user-defined type class
- tyUserTypeClassInst
+ tyUserTypeClassInst #\
# Instance of a parametric user-defined type class.
# Structured similarly to tyGenericInst.
# tyGenericInst represents concrete types, while
# this is still a "generic param" that will bind types
# and resolves them during sigmatch and instantiation.
- tyCompositeTypeClass
+ tyCompositeTypeClass #\
# Type such as seq[Number]
# The notes for tyUserTypeClassInst apply here as well
# sons[0]: the original expression used by the user.
# sons[1]: fully expanded and instantiated meta type
# (potentially following aliases)
- tyInferred
- # In the initial state `base` stores a type class constraining
- # the types that can be inferred. After a candidate type is
- # selected, it's stored in `lastSon`. Between `base` and `lastSon`
- # there may be 0, 2 or more types that were also considered as
- # possible candidates in the inference process (i.e. lastSon will
- # be updated to store a type best conforming to all candidates)
-
- tyAnd, tyOr, tyNot
+ tyAnd, tyOr, tyNot #\
# boolean type classes such as `string|int`,`not seq`,
# `Sortable and Enumable`, etc
- tyAnything
+ tyAnything #\
# a type class matching any type
- tyStatic
+ tyStatic #\
# a value known at compile type (the underlying type is .base)
- tyFromExpr
+ tyFromExpr #\
# This is a type representing an expression that depends
# on generic parameters (the expression is stored in t.n)
# It will be converted to a real type only during generic
# instantiation and prior to this it has the potential to
# be any type.
- tyConcept
- # new style concept.
-
- tyVoid
- # now different from tyEmpty, hurray!
- tyIterable
+ tyFieldAccessor #\
+ # Expressions such as Type.field (valid in contexts such
+ # as the `is` operator and magics like `high` and `low`).
+ # Could be lifted to a single argument proc returning the
+ # field value.
+ # sons[0]: type of containing object or tuple
+ # sons[1]: field type
+ # .n: nkDotExpr storing the field name
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
@@ -471,13 +411,7 @@ const
tyUserTypeClass, tyUserTypeClassInst,
tyAnd, tyOr, tyNot, tyAnything}
- tyMetaTypes* = {tyGenericParam, tyTypeDesc, tyUntyped} + tyTypeClasses
- tyUserTypeClasses* = {tyUserTypeClass, tyUserTypeClassInst}
- # 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?
+ tyMetaTypes* = {tyGenericParam, tyTypeDesc, tyExpr} + tyTypeClasses
type
TTypeKinds* = set[TTypeKind]
@@ -499,27 +433,15 @@ type
nfExplicitCall # x.y() was used instead of x.y
nfExprCall # this is an attempt to call a regular expression
nfIsRef # this node is a 'ref' node; used for the VM
- nfIsPtr # this node is a 'ptr' node; used for the VM
- nfPreventCg # this node should be ignored by the codegen
- nfBlockArg # this a stmtlist appearing in a call (e.g. a do block)
- nfFromTemplate # a top-level node returned from a template
- nfDefaultParam # an automatically inserter default parameter
- 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)
+ nfIsCursor # this node is attached a cursor; used for idetools
TNodeFlags* = set[TNodeFlag]
- TTypeFlag* = enum # keep below 32 for efficiency reasons (now: 46)
+ TTypeFlag* = enum # keep below 32 for efficiency reasons (now: 28)
tfVarargs, # procedure has C styled varargs
- # tyArray type represeting a varargs list
tfNoSideEffect, # procedure type does not allow side effects
tfFinal, # is the object final?
tfInheritable, # is the object inheritable?
- tfHasOwned, # type contains an 'owned' type and must be moved
+ tfAcyclic, # type is acyclic (for GC optimization)
tfEnumHasHoles, # enum cannot be mapped into a range
tfShallow, # type can be shallow copied on assignment
tfThread, # proc type is marked as ``thread``; alias for ``gcsafe``
@@ -530,11 +452,7 @@ type
# proc foo(T: typedesc, list: seq[T]): var T
# proc foo(L: static[int]): array[L, int]
# can be attached to ranges to indicate that the range
- # can be attached to generic procs with free standing
- # type parameters: e.g. proc foo[T]()
# depends on unresolved static params.
- tfResolved # marks a user type class, after it has been bound to a
- # concrete type (lastSon becomes the concrete type)
tfRetType, # marks return types in proc (used to detect type classes
# used as return types for return type inference)
tfCapturesEnv, # whether proc really captures some environment
@@ -543,11 +461,9 @@ type
tfIterator, # type is really an iterator, not a tyProc
tfPartial, # type is declared as 'partial'
tfNotNil, # type cannot be 'nil'
- tfRequiresInit, # type constains a "not nil" constraint somewhere or
- # a `requiresInit` field, so the default zero init
- # is not appropriate
- tfNeedsFullInit, # object type marked with {.requiresInit.}
- # all fields must be initialized
+
+ tfNeedsInit, # type constains a "not nil" constraint somewhere or some
+ # other type so that it requires initialization
tfVarIsPtr, # 'var' type is translated like 'ptr' even in C++ mode
tfHasMeta, # type contains "wildcard" sub-types such as generic params
# or other type classes
@@ -556,10 +472,6 @@ type
tfHasStatic
tfGenericTypeParam
tfImplicitTypeParam
- tfInferrableStatic
- tfConceptMatchedTypeSym
- tfExplicit # for typedescs, marks types explicitly prefixed with the
- # `type` operator (e.g. type int)
tfWildcard # consider a proc like foo[T, I](x: Type[T, I])
# T and I here can bind to both typedesc and static types
# before this is determined, we'll consider them to be a
@@ -568,21 +480,6 @@ type
tfBorrowDot # distinct type borrows '.'
tfTriggersCompileTime # uses the NimNode type which make the proc
# implicitly '.compiletime'
- tfRefsAnonObj # used for 'ref object' and 'ptr object'
- tfCovariant # covariant generic param mimicking a ptr type
- tfWeakCovariant # covariant generic param mimicking a seq/array type
- tfContravariant # contravariant generic param
- tfCheckedForDestructor # type was checked for having a destructor.
- # If it has one, t.destructor is not nil.
- tfAcyclic # object type was annotated as .acyclic
- tfIncompleteStruct # treat this type as if it had sizeof(pointer)
- tfCompleteStruct
- # (for importc types); type is fully specified, allowing to compute
- # sizeof, alignof, offsetof at CT
- tfExplicitCallConv
- tfIsConstructor
- tfEffectSystemWorkaround
- tfIsOutParam
TTypeFlags* = set[TTypeFlag]
@@ -603,7 +500,6 @@ type
skConst, # a constant
skResult, # special 'result' variable
skProc, # a proc
- skFunc, # a func
skMethod, # a method
skIterator, # an iterator
skConverter, # a type converter
@@ -622,157 +518,144 @@ type
TSymKinds* = set[TSymKind]
const
- routineKinds* = {skProc, skFunc, skMethod, skIterator,
+ routineKinds* = {skProc, skMethod, skIterator,
skConverter, skMacro, skTemplate}
- ExportableSymKinds* = {skVar, skLet, skConst, skType, skEnumField, skStub, skAlias} + routineKinds
-
+ tfIncompleteStruct* = tfVarargs
+ tfUncheckedArray* = tfVarargs
tfUnion* = tfNoSideEffect
tfGcSafe* = tfThread
tfObjHasKids* = tfEnumHasHoles
+ tfOldSchoolExprStmt* = tfVarargs # for now used to distinguish \
+ # 'varargs[expr]' from 'varargs[untyped]'. Eventually 'expr' will be
+ # deprecated and this mess can be cleaned up.
+ tfVoid* = tfVarargs # for historical reasons we conflated 'void' with
+ # 'empty' ('@[]' has the type 'seq[empty]').
tfReturnsNew* = tfInheritable
skError* = skUnknown
-var
- eqTypeFlags* = {tfIterator, tfNotNil, tfVarIsPtr, tfGcSafe, tfNoSideEffect, tfIsOutParam}
- ## 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}.
+ # type flags that are essential for type equality:
+ eqTypeFlags* = {tfIterator, tfNotNil, tfVarIsPtr}
type
TMagic* = enum # symbols that require compiler magic:
mNone,
- mDefined, mDeclared, mDeclaredInScope, mCompiles, mArrGet, mArrPut, mAsgn,
- mLow, mHigh, mSizeOf, mAlignOf, mOffsetOf, mTypeTrait,
- mIs, mOf, mAddr, mType, mTypeOf,
- mPlugin, mEcho, mShallowCopy, mSlurp, mStaticExec, mStatic,
+ mDefined, mDefinedInScope, mCompiles, mArrGet, mArrPut, mAsgn,
+ mLow, mHigh, mSizeOf, mTypeTrait, mIs, mOf, mAddr, mTypeOf, mRoof, mPlugin,
+ mEcho, mShallowCopy, mSlurp, mStaticExec,
mParseExprToAst, mParseStmtToAst, mExpandToAst, mQuoteAst,
- mInc, mDec, mOrd,
- mNew, mNewFinalize, mNewSeq, mNewSeqOfCap,
+ mUnaryLt, mInc, mDec, mOrd,
+ mNew, mNewFinalize, mNewSeq,
mLengthOpenArray, mLengthStr, mLengthArray, mLengthSeq,
+ mXLenStr, mXLenSeq,
mIncl, mExcl, mCard, mChr,
mGCref, mGCunref,
mAddI, mSubI, mMulI, mDivI, mModI,
mSucc, mPred,
mAddF64, mSubF64, mMulF64, mDivF64,
- mShrI, mShlI, mAshrI, mBitandI, mBitorI, mBitxorI,
+ mShrI, mShlI, mBitandI, mBitorI, mBitxorI,
mMinI, mMaxI,
+ mMinF64, mMaxF64,
mAddU, mSubU, mMulU, mDivU, mModU,
mEqI, mLeI, mLtI,
mEqF64, mLeF64, mLtF64,
mLeU, mLtU,
+ mLeU64, mLtU64,
mEqEnum, mLeEnum, mLtEnum,
mEqCh, mLeCh, mLtCh,
mEqB, mLeB, mLtB,
- mEqRef, mLePtr, mLtPtr,
+ mEqRef, mEqUntracedRef, mLePtr, mLtPtr,
mXor, mEqCString, mEqProc,
mUnaryMinusI, mUnaryMinusI64, mAbsI, mNot,
mUnaryPlusI, mBitnotI,
- mUnaryPlusF64, mUnaryMinusF64,
- mCharToStr, mBoolToStr,
- mIntToStr, mInt64ToStr, mFloatToStr, # for compiling nimStdlibVersion < 1.5.1 (not bootstrapping)
- mCStrToStr,
+ mUnaryPlusF64, mUnaryMinusF64, mAbsF64,
+ mZe8ToI, mZe8ToI64,
+ mZe16ToI, mZe16ToI64,
+ mZe32ToI64, mZeIToI64,
+ mToU8, mToU16, mToU32,
+ mToFloat, mToBiggestFloat,
+ mToInt, mToBiggestInt,
+ mCharToStr, mBoolToStr, mIntToStr, mInt64ToStr, mFloatToStr, mCStrToStr,
mStrToStr, mEnumToStr,
mAnd, mOr,
- mImplies, mIff, mExists, mForall, mOld,
mEqStr, mLeStr, mLtStr,
- mEqSet, mLeSet, mLtSet, mMulSet, mPlusSet, mMinusSet,
+ mEqSet, mLeSet, mLtSet, mMulSet, mPlusSet, mMinusSet, mSymDiffSet,
mConStrStr, mSlice,
mDotDot, # this one is only necessary to give nice compile time warnings
mFields, mFieldPairs, mOmpParFor,
mAppendStrCh, mAppendStrStr, mAppendSeqElem,
- mInSet, mRepr, mExit,
+ mInRange, mInSet, mRepr, mExit,
mSetLengthStr, mSetLengthSeq,
mIsPartOf, mAstToStr, mParallel,
- mSwap, mIsNil, mArrToSeq, mOpenArrayToSeq,
+ mSwap, mIsNil, mArrToSeq, mCopyStr, mCopyStrLast,
mNewString, mNewStringOfCap, mParseBiggestFloat,
- mMove, mWasMoved, mDestroy, mTrace,
- mDefault, mUnown, mFinished, mIsolate, mAccessEnv, mAccessTypeField, mReset,
+ 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,
mBool, mChar, mString, mCstring,
- mPointer, mNil, mExpr, mStmt, mTypeDesc,
- mVoidType, mPNimrodNode, mSpawn, mDeepCopy,
+ mPointer, mEmptySet, mIntSetBaseType, mNil, mExpr, mStmt, mTypeDesc,
+ mVoidType, mPNimrodNode, mShared, mGuarded, mLock, mSpawn, mDeepCopy,
mIsMainModule, mCompileDate, mCompileTime, mProcCall,
- mCpuEndian, mHostOS, mHostCPU, mBuildOS, mBuildCPU, mAppType,
+ mCpuEndian, mHostOS, mHostCPU, mAppType,
+ mNaN, mInf, mNegInf,
mCompileOption, mCompileOptionArg,
- mNLen, mNChild, mNSetChild, mNAdd, mNAddMultiple, mNDel,
- mNKind, mNSymKind,
-
- mNccValue, mNccInc, mNcsAdd, mNcsIncl, mNcsLen, mNcsAt,
- mNctPut, mNctLen, mNctGet, mNctHasNext, mNctNext,
-
+ mNLen, mNChild, mNSetChild, mNAdd, mNAddMultiple, mNDel, mNKind,
mNIntVal, mNFloatVal, mNSymbol, mNIdent, mNGetType, mNStrVal, mNSetIntVal,
- mNSetFloatVal, mNSetSymbol, mNSetIdent, mNSetStrVal, mNLineInfo,
- mNNewNimNode, mNCopyNimNode, mNCopyNimTree, mStrToIdent, mNSigHash, mNSizeOf,
- mNBindSym, mNCallSite,
- mEqIdent, mEqNimrodNode, mSameNodeType, mGetImpl, mNGenSym,
+ mNSetFloatVal, mNSetSymbol, mNSetIdent, mNSetType, mNSetStrVal, mNLineInfo,
+ mNNewNimNode, mNCopyNimNode, mNCopyNimTree, mStrToIdent, mIdentToStr,
+ mNBindSym, mLocals, mNCallSite,
+ mEqIdent, mEqNimrodNode, mSameNodeType, mGetImpl,
mNHint, mNWarning, mNError,
- mInstantiationInfo, mGetTypeInfo, mGetTypeInfoV2,
- mNimvm, mIntDefine, mStrDefine, mBoolDefine, mGenericDefine, mRunnableExamples,
- mException, mBuiltinType, mSymOwner, mUncheckedArray, mGetImplTransf,
- mSymIsInstantiationOf, mNodeId, mPrivateAccess, mZeroDefault
-
+ mInstantiationInfo, mGetTypeInfo, mNGenSym,
+ mNimvm
+# 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,
+ ctfeWhitelist* = {mNone, mUnaryLt, mSucc,
mPred, mInc, mDec, mOrd, mLengthOpenArray,
- mLengthStr, mLengthArray, mLengthSeq,
- mArrGet, mArrPut, mAsgn, mDestroy,
+ mLengthStr, mLengthArray, mLengthSeq, mXLenStr, mXLenSeq,
+ mArrGet, mArrPut, mAsgn,
mIncl, mExcl, mCard, mChr,
mAddI, mSubI, mMulI, mDivI, mModI,
mAddF64, mSubF64, mMulF64, mDivF64,
mShrI, mShlI, mBitandI, mBitorI, mBitxorI,
mMinI, mMaxI,
+ mMinF64, mMaxF64,
mAddU, mSubU, mMulU, mDivU, mModU,
mEqI, mLeI, mLtI,
mEqF64, mLeF64, mLtF64,
mLeU, mLtU,
+ mLeU64, mLtU64,
mEqEnum, mLeEnum, mLtEnum,
mEqCh, mLeCh, mLtCh,
mEqB, mLeB, mLtB,
- mEqRef, mEqProc, mLePtr, mLtPtr, mEqCString, mXor,
+ mEqRef, mEqProc, mEqUntracedRef, mLePtr, mLtPtr, mEqCString, mXor,
mUnaryMinusI, mUnaryMinusI64, mAbsI, mNot, mUnaryPlusI, mBitnotI,
- mUnaryPlusF64, mUnaryMinusF64,
- mCharToStr, mBoolToStr,
- mIntToStr, mInt64ToStr, mFloatToStr,
- mCStrToStr,
+ mUnaryPlusF64, mUnaryMinusF64, mAbsF64,
+ mZe8ToI, mZe8ToI64,
+ mZe16ToI, mZe16ToI64,
+ mZe32ToI64, mZeIToI64,
+ mToU8, mToU16, mToU32,
+ mToFloat, mToBiggestFloat,
+ mToInt, mToBiggestInt,
+ mCharToStr, mBoolToStr, mIntToStr, mInt64ToStr, mFloatToStr, mCStrToStr,
mStrToStr, mEnumToStr,
mAnd, mOr,
mEqStr, mLeStr, mLtStr,
- mEqSet, mLeSet, mLtSet, mMulSet, mPlusSet, mMinusSet,
+ mEqSet, mLeSet, mLtSet, mMulSet, mPlusSet, mMinusSet, mSymDiffSet,
mConStrStr, mAppendStrCh, mAppendStrStr, mAppendSeqElem,
- mInSet, mRepr, mOpenArrayToSeq}
-
- generatedMagics* = {mNone, mIsolate, mFinished, mOpenArrayToSeq}
- ## magics that are generated as normal procs in the backend
+ mInRange, mInSet, mRepr,
+ mCopyStr, mCopyStrLast}
+ # magics that require special semantic checking and
+ # thus cannot be overloaded (also documented in the spec!):
+ SpecialSemMagics* = {
+ mDefined, mDefinedInScope, mCompiles, mLow, mHigh, mSizeOf, mIs, mOf,
+ mShallowCopy, mExpandToAst, mParallel, mSpawn, mAstToStr}
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
- itemId*: ItemId
- PIdObj* = ref TIdObj
-
PNode* = ref TNode
TNodeSeq* = seq[PNode]
PType* = ref TType
@@ -796,12 +679,12 @@ type
ident*: PIdent
else:
sons*: TNodeSeq
- when defined(nimsuggest):
- endInfo*: TLineInfo
+ comment*: string
+ TSymSeq* = seq[PSym]
TStrTable* = object # a table[PIdent] of PSym
counter*: int
- data*: seq[PSym]
+ data*: TSymSeq
# -------------- backend information -------------------------------
TLocKind* = enum
@@ -818,7 +701,7 @@ type
locOther # location is something other
TLocFlag* = enum
lfIndirect, # backend introduced a pointer
- lfFullExternalName, # only used when 'conf.cmd == cmdNimfix': Indicates
+ lfFullExternalName, # only used when 'gCmd == cmdPretty': Indicates
# that the symbol has been imported via 'importc: "fullname"' and
# no format string.
lfNoDeepCopy, # no need for a deep copy
@@ -828,10 +711,6 @@ type
lfHeader, # include header file for symbol
lfImportCompilerProc, # ``importc`` of a compilerproc
lfSingleUse # no location yet and will only be used once
- lfEnforceDeref # a copyMem is required to dereference if this a
- # ptr array due to C array limitations.
- # See #1181, #6422, #11171
- lfPrepareForMutation # string location is about to be mutated (V2)
TStorageLoc* = enum
OnUnknown, # location is unknown (stack, heap or static)
OnStatic, # in a static section
@@ -841,61 +720,76 @@ type
TLocFlags* = set[TLocFlag]
TLoc* = object
k*: TLocKind # kind of location
- storage*: TStorageLoc
+ s*: TStorageLoc
flags*: TLocFlags # location's flags
- lode*: PNode # Node where the location came from; can be faked
- r*: Rope # rope value of location (code generators)
+ t*: PType # type of location
+ r*: Rope # rope value of location (code generators)
+ heapRoot*: Rope # keeps track of the enclosing heap object that
+ # owns this location (required by GC algorithms
+ # employing heap snapshots or sliding views)
# ---------------- end of backend information ------------------------------
TLibKind* = enum
libHeader, libDynamic
-
- TLib* = object # also misused for headers!
- # keep in sync with PackedLib
+ TLib* = object of lists.TListEntry # also misused for headers!
kind*: TLibKind
generated*: bool # needed for the backends:
isOverriden*: bool
name*: Rope
path*: PNode # can be a string literal!
-
CompilesId* = int ## id that is used for the caching logic within
## ``system.compiles``. See the seminst module.
TInstantiation* = object
sym*: PSym
concreteTypes*: seq[PType]
+ usedBy*: seq[int32] # list of modules using the generic
+ # needed in caas mode for purging the cache
+ # XXX: it's possible to switch to a
+ # simple ref count here
compilesId*: CompilesId
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
PLib* = ref TLib
- TSym* {.acyclic.} = object of TIdObj # Keep in sync with PackedSym
+ TSym* {.acyclic.} = object of TIdObj
# proc and type instantiations are cached in the generic symbol
case kind*: TSymKind
+ of skType, skGenericParam:
+ typeInstCache*: seq[PType]
+ typScope*: PScope
of routineKinds:
- #procInstCache*: seq[PInstantiation]
- gcUnsafetyReason*: PSym # for better error messages regarding gcsafe
- transformedBody*: PNode # cached body after transf pass
+ procInstCache*: seq[PInstantiation]
+ gcUnsafetyReason*: PSym # for better error messages wrt gcsafe
+ #scope*: PScope # the scope where the proc was defined
+ of skModule:
+ # 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
- alignment*: int # for alignment
else: nil
magic*: TMagic
typ*: PType
name*: PIdent
info*: TLineInfo
- when defined(nimsuggest):
- endInfo*: TLineInfo
owner*: PSym
flags*: TSymFlags
ast*: PNode # syntax tree of proc, iterator, etc.:
@@ -910,43 +804,30 @@ type
position*: int # used for many different things:
# for enum fields its position;
# for fields its offset
- # for parameters its position (starting with 0)
+ # for parameters its position
# for a conditional:
# 1 iff the symbol is defined, else 0
# (or not in symbol table)
# for modules, an unique index corresponding
# to the module's fileIdx
# for variables a slot index for the evaluator
+ # for routines a superop-ID
offset*: int # offset of record field
loc*: TLoc
annex*: PLib # additional fields (seldom used, so we use a
- # reference to another object to save space)
- when hasFFI:
- cname*: string # resolved C declaration name in importc decl, e.g.:
- # proc fun() {.importc: "$1aux".} => cname = funaux
+ # reference to another object to safe space)
constraint*: PNode # additional constraints like 'lit|result'; also
# misused for the codegenDecl pragma in the hope
# it won't cause problems
- # for skModule the string literal to output for
- # deprecated modules.
when defined(nimsuggest):
allUsages*: seq[TLineInfo]
TTypeSeq* = seq[PType]
-
- TTypeAttachedOp* = enum ## as usual, order is important here
- attachedWasMoved,
- attachedDestructor,
- attachedAsgn,
- attachedSink,
- attachedTrace,
- attachedDeepCopy
-
+ TLockLevel* = distinct int16
TType* {.acyclic.} = object of TIdObj # \
# types are identical iff they have the
# same id; there may be multiple copies of a type
# in memory!
- # Keep in sync with PackedType
kind*: TTypeKind # kind of type
callConv*: TCallingConvention # for procs
flags*: TTypeFlags # flags of the type
@@ -955,7 +836,7 @@ type
# for range types a nkRange node
# for record types a nkRecord node
# for enum types a list of symbols
- # if kind == tyInt: it is an 'int literal(x)' type
+ # for tyInt it can be the int literal
# for procs and tyGenericBody, it's the
# formal param list
# for concepts, the concept body
@@ -963,20 +844,24 @@ 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
+ destructor*: PSym # destructor. warning: nil here may not necessary
+ # mean that there is no destructor.
+ # see instantiateDestructor in semdestruct.nim
+ deepCopy*: PSym # overriden 'deepCopy' operation
+ assignment*: PSym # overriden '=' operator
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.
TPair* = object
key*, val*: RootRef
TPairSeq* = seq[TPair]
+ TTable* = object # the same as table[PObject] of PObject
+ counter*: int
+ data*: TPairSeq
TIdPair* = object
key*: PIdObj
@@ -1015,73 +900,40 @@ 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}
+ OverloadableSyms* = {skProc, skMethod, skIterator,
+ skConverter, skModule, skTemplate, skMacro}
GenericTypes*: TTypeKinds = {tyGenericInvocation, tyGenericBody,
tyGenericParam}
- StructuralEquivTypes*: TTypeKinds = {tyNil, tyTuple, tyArray,
- tySet, tyRange, tyPtr, tyRef, tyVar, tyLent, tySequence, tyProc, tyOpenArray,
+ StructuralEquivTypes*: TTypeKinds = {tyArrayConstr, tyNil, tyTuple, tyArray,
+ tySet, tyRange, tyPtr, tyRef, tyVar, tySequence, tyProc, tyOpenArray,
tyVarargs}
ConcreteTypes*: TTypeKinds = { # types of the expr that may occur in::
# var x = expr
tyBool, tyChar, tyEnum, tyArray, tyObject,
- tySet, tyTuple, tyRange, tyPtr, tyRef, tyVar, tyLent, tySequence, tyProc,
+ tySet, tyTuple, tyRange, tyPtr, tyRef, tyVar, 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,
+ tyFloat..tyFloat128, tyUInt..tyUInt64}
+ ConstantDataTypes*: TTypeKinds = {tyArrayConstr, tyArray, tySet,
tyTuple, tySequence}
- NilableTypes*: TTypeKinds = {tyPointer, tyCstring, tyRef, tyPtr,
- tyProc, tyError} # TODO
- PtrLikeKinds*: TTypeKinds = {tyPointer, tyPtr} # for VM
+ NilableTypes*: TTypeKinds = {tyPointer, tyCString, tyRef, tyPtr, tySequence,
+ tyProc, tyString, tyError}
+ ExportableSymKinds* = {skVar, skConst, skProc, 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}
+ nfIsRef, nfIsCursor, nfLL}
namePos* = 0
patternPos* = 1 # empty except for term rewriting macros
genericParamsPos* = 2
@@ -1090,195 +942,71 @@ const
miscPos* = 5 # used for undocumented and hacky stuff
bodyPos* = 6 # position of body; use rodread.getBody() instead!
resultPos* = 7
- dispatcherPos* = 8
-
- nfAllFieldsSet* = nfBase2
+ dispatcherPos* = 8 # caution: if method has no 'result' it can be position 7!
nkCallKinds* = {nkCall, nkInfix, nkPrefix, nkPostfix,
nkCommand, nkCallStrLit, nkHiddenCallConv}
nkIdentKinds* = {nkIdent, nkSym, nkAccQuoted, nkOpenSymChoice,
nkClosedSymChoice}
- nkPragmaCallKinds* = {nkExprColonExpr, nkCall, nkCallStrLit}
nkLiterals* = {nkCharLit..nkTripleStrLit}
- nkFloatLiterals* = {nkFloatLit..nkFloat128Lit}
nkLambdaKinds* = {nkLambda, nkDo}
- declarativeDefs* = {nkProcDef, nkFuncDef, nkMethodDef, nkIteratorDef, nkConverterDef}
- routineDefs* = declarativeDefs + {nkMacroDef, nkTemplateDef}
+ declarativeDefs* = {nkProcDef, nkMethodDef, nkIteratorDef, nkConverterDef}
procDefs* = nkLambdaKinds + declarativeDefs
- callableDefs* = nkLambdaKinds + routineDefs
nkSymChoices* = {nkClosedSymChoice, nkOpenSymChoice}
nkStrKinds* = {nkStrLit..nkTripleStrLit}
skLocalVars* = {skVar, skLet, skForVar, skParam, skResult}
- skProcKinds* = {skProc, skFunc, skTemplate, skMacro, skIterator,
+ skProcKinds* = {skProc, skTemplate, skMacro, skIterator,
skMethod, skConverter}
- defaultSize = -1
- defaultAlignment = -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
-
-const
- moduleShift = when defined(cpu32): 20 else: 24
-
-template id*(a: PIdObj): int =
- let x = a
- (x.itemId.module.int shl moduleShift) + x.itemId.item.int
-
-type
- IdGenerator* = ref object # unfortunately, we really need the 'shared mutable' aspect here.
- module*: int32
- symId*: int32
- typeId*: int32
- sealed*: bool
-
-const
- PackageModuleId* = -3'i32
-
-proc idGeneratorFromModule*(m: PSym): IdGenerator =
- assert m.kind == skModule
- 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)
-
-when false:
- proc nextId*(x: IdGenerator): ItemId {.inline.} =
- inc x.item
- result = x[]
-
-when false:
- proc storeBack*(dest: var IdGenerator; src: IdGenerator) {.inline.} =
- assert dest.ItemId.module == src.ItemId.module
- if dest.ItemId.item > src.ItemId.item:
- echo dest.ItemId.item, " ", src.ItemId.item, " ", src.ItemId.module
- assert dest.ItemId.item <= src.ItemId.item
- dest = src
-
var ggDebug* {.deprecated.}: bool ## convenience switch for trying out things
+var
+ gMainPackageId*: int
+ gMainPackageNotes*: TNoteKinds
proc isCallExpr*(n: PNode): bool =
result = n.kind in nkCallKinds
proc discardSons*(father: PNode)
-type Indexable = PNode | PType
-
-proc len*(n: Indexable): int {.inline.} =
- result = n.sons.len
+proc len*(n: PNode): int {.inline.} =
+ if isNil(n.sons): result = 0
+ else: result = len(n.sons)
proc safeLen*(n: PNode): int {.inline.} =
## works even for leaves.
- if n.kind in {nkNone..nkNilLit}: result = 0
- else: result = n.len
+ if n.kind in {nkNone..nkNilLit} or isNil(n.sons): result = 0
+ else: result = len(n.sons)
-proc safeArrLen*(n: PNode): int {.inline.} =
- ## works for array-like objects (strings passed as openArray in VM).
- if n.kind in {nkStrLit..nkTripleStrLit}: result = n.strVal.len
- elif n.kind in {nkNone..nkFloat128Lit}: result = 0
- else: result = n.len
-
-proc add*(father, son: Indexable) =
+proc add*(father, son: PNode) =
assert son != nil
- father.sons.add(son)
+ if isNil(father.sons): father.sons = @[]
+ add(father.sons, son)
-proc addAllowNil*(father, son: Indexable) {.inline.} =
- father.sons.add(son)
+proc `[]`*(n: PNode, i: int): PNode {.inline.} =
+ result = n.sons[i]
-template `[]`*(n: Indexable, i: int): Indexable = n.sons[i]
-template `[]=`*(n: Indexable, i: int; x: Indexable) = n.sons[i] = x
+template `-|`*(b, s: expr): expr =
+ (if b >= 0: b else: s.len + b)
-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()
-
-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
-
proc newTree*(kind: TNodeKind; children: varargs[PNode]): PNode =
result = newNode(kind)
- if children.len > 0:
- result.info = children[0].info
result.sons = @children
-proc newTreeI*(kind: TNodeKind; info: TLineInfo; children: varargs[PNode]): PNode =
- result = newNodeI(kind, info)
- if children.len > 0:
- result.info = children[0].info
- result.sons = @children
+proc newIntNode*(kind: TNodeKind, intVal: BiggestInt): PNode =
+ result = newNode(kind)
+ result.intVal = intVal
-proc newTreeIT*(kind: TNodeKind; info: TLineInfo; typ: PType; children: varargs[PNode]): PNode =
- result = newNodeIT(kind, info, typ)
- if children.len > 0:
- result.info = children[0].info
- result.sons = @children
+proc newIntTypeNode*(kind: TNodeKind, intVal: BiggestInt, typ: PType): PNode =
+ result = newIntNode(kind, intVal)
+ result.typ = typ
-template previouslyInferred*(t: PType): PType =
- if t.sons.len > 1: t.lastSon else: nil
+proc newFloatNode*(kind: TNodeKind, floatVal: BiggestFloat): PNode =
+ result = newNode(kind)
+ result.floatVal = floatVal
-when false:
- import tables, strutils
- var x: CountTable[string]
+proc newStrNode*(kind: TNodeKind, strVal: string): PNode =
+ result = newNode(kind)
+ result.strVal = strVal
- 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 =
+proc newSym*(symKind: TSymKind, name: PIdent, owner: PSym,
+ info: TLineInfo): 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
+ new(result)
+ result.name = name
+ result.kind = symKind
+ result.flags = {}
+ result.info = info
+ result.options = gOptions
+ result.owner = owner
+ result.offset = - 1
+ result.id = getID()
+ when debugIds:
+ registerId(result)
+ #if result.id == 93289:
+ # writeStacktrace()
+ # MessageOut(name.s & " has id: " & toString(result.id))
-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}:
- s.ast[2]
- else:
- s.ast
+var emptyNode* = newNode(nkEmpty)
+# There is a single empty node that is shared! Do not overwrite it!
+
+var anyGlobal* = newSym(skVar, getIdent("*"), nil, unknownLineInfo())
proc isMetaType*(t: PType): bool =
return t.kind in tyMetaTypes or
(t.kind == tyStatic and t.n == nil) or
tfHasMeta in t.flags
-proc isUnresolvedStatic*(t: PType): bool =
- return t.kind == tyStatic and t.n == nil
-
proc linkTo*(t: PType, s: PSym): PType {.discardable.} =
t.sym = s
s.typ = t
@@ -1375,13 +1072,13 @@ proc linkTo*(s: PSym, t: PType): PSym {.discardable.} =
s.typ = t
result = s
-template fileIdx*(c: PSym): FileIndex =
+template fileIdx*(c: PSym): int32 =
# XXX: this should be used only on module symbols
- c.position.FileIndex
+ c.position.int32
template filename*(c: PSym): string =
# XXX: this should be used only on module symbols
- c.position.FileIndex.toFilename
+ c.position.int32.toFilename
proc appendToModule*(m: PSym, n: PNode) =
## The compiler will use this internally to add nodes that will be
@@ -1399,21 +1096,30 @@ const # for all kind of hash tables:
proc copyStrTable*(dest: var TStrTable, src: TStrTable) =
dest.counter = src.counter
- setLen(dest.data, src.data.len)
- for i in 0..high(src.data): dest.data[i] = src.data[i]
+ if isNil(src.data): return
+ setLen(dest.data, len(src.data))
+ for i in countup(0, high(src.data)): dest.data[i] = src.data[i]
proc copyIdTable*(dest: var TIdTable, src: TIdTable) =
dest.counter = src.counter
- newSeq(dest.data, src.data.len)
- for i in 0..high(src.data): dest.data[i] = src.data[i]
+ if isNil(src.data): return
+ newSeq(dest.data, len(src.data))
+ for i in countup(0, high(src.data)): dest.data[i] = src.data[i]
+
+proc copyTable*(dest: var TTable, src: TTable) =
+ dest.counter = src.counter
+ if isNil(src.data): return
+ setLen(dest.data, len(src.data))
+ for i in countup(0, high(src.data)): dest.data[i] = src.data[i]
proc copyObjectSet*(dest: var TObjectSet, src: TObjectSet) =
dest.counter = src.counter
- setLen(dest.data, src.data.len)
- for i in 0..high(src.data): dest.data[i] = src.data[i]
+ if isNil(src.data): return
+ setLen(dest.data, len(src.data))
+ for i in countup(0, high(src.data)): dest.data[i] = src.data[i]
proc discardSons*(father: PNode) =
- father.sons = @[]
+ father.sons = nil
proc withInfo*(n: PNode, info: TLineInfo): PNode =
n.info = info
@@ -1436,100 +1142,116 @@ proc newSymNode*(sym: PSym, info: TLineInfo): PNode =
result.typ = sym.typ
result.info = info
-proc newIntNode*(kind: TNodeKind, intVal: BiggestInt): PNode =
+proc newNodeI*(kind: TNodeKind, info: TLineInfo): PNode =
+ new(result)
+ result.kind = kind
+ result.info = info
+ when defined(useNodeIds):
+ result.id = gNodeId
+ if result.id == nodeIdToDebug:
+ echo "KIND ", result.kind
+ writeStackTrace()
+ inc gNodeId
+
+proc newNodeI*(kind: TNodeKind, info: TLineInfo, children: int): PNode =
+ new(result)
+ result.kind = kind
+ result.info = info
+ if children > 0:
+ newSeq(result.sons, children)
+ when defined(useNodeIds):
+ result.id = gNodeId
+ if result.id == nodeIdToDebug:
+ echo "KIND ", result.kind
+ writeStackTrace()
+ inc gNodeId
+
+proc newNode*(kind: TNodeKind, info: TLineInfo, sons: TNodeSeq = @[],
+ typ: PType = nil): PNode =
+ new(result)
+ result.kind = kind
+ result.info = info
+ result.typ = typ
+ # XXX use shallowCopy here for ownership transfer:
+ result.sons = sons
+ 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 =
result = newNode(kind)
- result.intVal = intVal
-
-proc newIntNode*(kind: TNodeKind, intVal: Int128): PNode =
- result = newNode(kind)
- result.intVal = castToInt64(intVal)
-
-proc lastSon*(n: Indexable): Indexable = n.sons[^1]
-
-proc skipTypes*(t: PType, kinds: TTypeKinds): PType =
- ## Used throughout the compiler code to test whether a type tree contains or
- ## doesn't contain a specific type/types - it is often the case that only the
- ## last child nodes of a type tree need to be searched. This is a really hot
- ## path within the compiler!
- result = t
- while result.kind in kinds: result = lastSon(result)
-
-proc newIntTypeNode*(intVal: BiggestInt, typ: PType): PNode =
- let kind = skipTypes(typ, abstractVarRange).kind
- case kind
- of tyInt: result = newNode(nkIntLit)
- of tyInt8: result = newNode(nkInt8Lit)
- of tyInt16: result = newNode(nkInt16Lit)
- of tyInt32: result = newNode(nkInt32Lit)
- of tyInt64: result = newNode(nkInt64Lit)
- of tyChar: result = newNode(nkCharLit)
- of tyUInt: result = newNode(nkUIntLit)
- of tyUInt8: result = newNode(nkUInt8Lit)
- of tyUInt16: result = newNode(nkUInt16Lit)
- of tyUInt32: result = newNode(nkUInt32Lit)
- of tyUInt64: result = newNode(nkUInt64Lit)
- of tyBool, tyEnum:
- # XXX: does this really need to be the kind nkIntLit?
- result = newNode(nkIntLit)
- of tyStatic: # that's a pre-existing bug, will fix in another PR
- result = newNode(nkIntLit)
- else: doAssert false, $kind
- result.intVal = intVal
+ result.info = info
result.typ = typ
-proc newIntTypeNode*(intVal: Int128, typ: PType): PNode =
- # XXX: introduce range check
- newIntTypeNode(castToInt64(intVal), typ)
+proc addSon*(father, son: PNode) =
+ assert son != nil
+ if isNil(father.sons): father.sons = @[]
+ add(father.sons, son)
-proc newFloatNode*(kind: TNodeKind, floatVal: BiggestFloat): PNode =
- result = newNode(kind)
- result.floatVal = floatVal
-
-proc newStrNode*(kind: TNodeKind, strVal: string): PNode =
- result = newNode(kind)
- result.strVal = strVal
-
-proc newStrNode*(strVal: string; info: TLineInfo): PNode =
- result = newNodeI(nkStrLit, info)
- result.strVal = strVal
+var emptyParams = newNode(nkFormalParams)
+emptyParams.addSon(emptyNode)
proc newProcNode*(kind: TNodeKind, info: TLineInfo, body: PNode,
- params,
+ params = emptyParams,
name, pattern, genericParams,
- pragmas, exceptions: PNode): PNode =
+ pragmas, exceptions = ast.emptyNode): PNode =
result = newNodeI(kind, info)
result.sons = @[name, pattern, genericParams, params,
pragmas, exceptions, body]
const
- AttachedOpToStr*: array[TTypeAttachedOp, string] = [
- "=wasMoved", "=destroy", "=copy", "=sink", "=trace", "=deepcopy"]
+ UnspecifiedLockLevel* = TLockLevel(-1'i16)
+ MaxLockLevel* = 1000'i16
+ UnknownLockLevel* = TLockLevel(1001'i16)
-proc `$`*(s: PSym): string =
- if s != nil:
- result = s.name.s & "@" & $s.id
- else:
- result = ""
-
-proc newType*(kind: TTypeKind, id: ItemId; owner: PSym): PType =
- result = PType(kind: kind, owner: owner, size: defaultSize,
- align: defaultAlignment, itemId: id,
- uniqueId: id)
- when false:
- if result.itemId.module == 55 and result.itemId.item == 2:
- echo "KNID ", kind
- writeStackTrace()
+proc `$`*(x: TLockLevel): string =
+ if x.ord == UnspecifiedLockLevel.ord: result = ""
+ elif x.ord == UnknownLockLevel.ord: result = ""
+ else: result = $int16(x)
+proc newType*(kind: TTypeKind, owner: PSym): PType =
+ new(result)
+ result.kind = kind
+ result.owner = owner
+ result.size = - 1
+ result.align = 2 # default alignment
+ result.id = getID()
+ result.lockLevel = UnspecifiedLockLevel
+ when debugIds:
+ registerId(result)
+ #if result.id == 92231:
+ # echo "KNID ", kind
+ # writeStackTrace()
+ # messageOut(typeKindToStr[kind] & ' has id: ' & toString(result.id))
proc mergeLoc(a: var TLoc, b: TLoc) =
- if a.k == low(typeof(a.k)): a.k = b.k
- 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.k == low(a.k): a.k = b.k
+ if a.s == low(a.s): a.s = b.s
+ a.flags = a.flags + b.flags
+ if a.t == nil: a.t = b.t
+ if a.r == nil: a.r = b.r
+ #if a.a == 0: a.a = b.a
-proc newSons*(father: Indexable, length: int) =
- setLen(father.sons, length)
+proc newSons*(father: PNode, length: int) =
+ if isNil(father.sons):
+ newSeq(father.sons, length)
+ else:
+ setLen(father.sons, length)
+
+proc newSons*(father: PType, length: int) =
+ if isNil(father.sons):
+ newSeq(father.sons, length)
+ else:
+ setLen(father.sons, length)
+
+proc sonsLen*(n: PType): int = n.sons.len
+proc len*(n: PType): int = n.sons.len
+proc sonsLen*(n: PNode): int = n.sons.len
+proc lastSon*(n: PNode): PNode = n.sons[^1]
+proc lastSon*(n: PType): PType = n.sons[^1]
proc assignType*(dest, src: PType) =
dest.kind = src.kind
@@ -1538,52 +1260,63 @@ proc assignType*(dest, src: PType) =
dest.n = src.n
dest.size = src.size
dest.align = src.align
+ dest.destructor = src.destructor
+ dest.deepCopy = src.deepCopy
+ dest.assignment = src.assignment
+ 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 = dest.sym.flags + src.sym.flags
if dest.sym.annex == nil: dest.sym.annex = src.sym.annex
mergeLoc(dest.sym.loc, src.sym.loc)
else:
dest.sym = src.sym
- newSons(dest, src.len)
- for i in 0..= nkNone and n.kind <= nkNilLit
proc isEmptyType*(t: PType): bool {.inline.} =
- ## 'void' and 'typed' types are often equivalent to 'nil' these days:
- result = t == nil or t.kind in {tyVoid, tyTyped}
+ ## 'void' and 'stmt' types are often equivalent to 'nil' these days:
+ result = t == nil or t.kind in {tyEmpty, tyStmt}
proc makeStmtList*(n: PNode): PNode =
if n.kind == nkStmtList:
@@ -1954,188 +1599,23 @@ proc makeStmtList*(n: PNode): PNode =
proc skipStmtList*(n: PNode): PNode =
if n.kind in {nkStmtList, nkStmtListExpr}:
- for i in 0..` and
- ## returned. Otherwise ``typ`` is simply returned as-is.
- result = typ
- if typ.kind != kind:
- result = newType(kind, nextTypeId(idgen), typ.owner)
- rawAddSon(result, typ)
+proc createMagic*(name: string, m: TMagic): PSym =
+ result = newSym(skProc, getIdent(name), nil, unknownLineInfo())
+ result.magic = m
-proc toRef*(typ: PType; idgen: IdGenerator): PType =
- ## If ``typ`` is a tyObject then it is converted into a `ref ` and
- ## returned. Otherwise ``typ`` is simply returned as-is.
- result = typ
- if typ.skipTypes({tyAlias, tyGenericInst}).kind == tyObject:
- result = newType(tyRef, nextTypeId(idgen), typ.owner)
- rawAddSon(result, typ)
-
-proc toObject*(typ: PType): PType =
- ## If ``typ`` is a tyRef then its immediate son is returned (which in many
- ## cases should be a ``tyObject``).
- ## Otherwise ``typ`` is simply returned as-is.
- let t = typ.skipTypes({tyAlias, tyGenericInst})
- if t.kind == tyRef: t.lastSon
- else: typ
-
-proc toObjectFromRefPtrGeneric*(typ: PType): PType =
- #[
- See also `toObject`.
- Finds the underlying `object`, even in cases like these:
- type
- B[T] = object f0: int
- A1[T] = ref B[T]
- A2[T] = ref object f1: int
- A3 = ref object f2: int
- A4 = object f3: int
- ]#
- result = typ
- while true:
- case result.kind
- of tyGenericBody: result = result.lastSon
- of tyRef, tyPtr, tyGenericInst, tyGenericInvocation, tyAlias: result = result[0]
- # automatic dereferencing is deep, refs #18298.
- else: break
- assert result.sym != nil
-
-proc isImportedException*(t: PType; conf: ConfigRef): bool =
- assert t != nil
-
- if conf.exc != excCpp:
- return false
-
- let base = t.skipTypes({tyAlias, tyPtr, tyDistinct, tyGenericInst})
-
- if base.sym != nil and {sfCompileToCpp, sfImportc} * base.sym.flags != {}:
- result = true
-
-proc isInfixAs*(n: PNode): bool =
- return n.kind == nkInfix and n[0].kind == nkIdent and n[0].ident.s == "as"
-
-proc skipColon*(n: PNode): PNode =
- result = n
- if n.kind == nkExprColonExpr:
- result = n[1]
-
-proc findUnresolvedStatic*(n: PNode): PNode =
- # n.typ == nil: see issue #14802
- if n.kind == nkSym and n.typ != nil and n.typ.kind == tyStatic and n.typ.n == nil:
- return n
-
- for son in n:
- let n = son.findUnresolvedStatic
- if n != nil: return n
-
- return nil
+let
+ opNot* = createMagic("not", mNot)
+ opContains* = createMagic("contains", mInSet)
when false:
proc containsNil*(n: PNode): bool =
# only for debugging
if n.isNil: return true
- for i in 0.. 0))
-
-proc toHumanStrImpl[T](kind: T, num: static int): string =
- result = $kind
- result = result[num..^1]
- result[0] = result[0].toLowerAscii
-
-proc toHumanStr*(kind: TSymKind): string =
- ## strips leading `sk`
- result = toHumanStrImpl(kind, 2)
-
-proc toHumanStr*(kind: TTypeKind): string =
- ## strips leading `tk`
- result = toHumanStrImpl(kind, 2)
-
-proc skipAddr*(n: PNode): PNode {.inline.} =
- (if n.kind == nkHiddenAddr: n[0] else: n)
-
-proc isNewStyleConcept*(n: PNode): bool {.inline.} =
- assert n.kind == nkTypeClassTy
- result = n[0].kind == nkEmpty
-
-proc isOutParam*(t: PType): bool {.inline.} = tfIsOutParam in t.flags
-
-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, nkMixinStmt, nkBindStmt}
diff --git a/compiler/astalgo.nim b/compiler/astalgo.nim
index 4e09fab02..3ca44ea7e 100644
--- a/compiler/astalgo.nim
+++ b/compiler/astalgo.nim
@@ -12,72 +12,71 @@
# the data structures here are used in various places of the compiler.
import
- ast, hashes, intsets, options, lineinfos, ropes, idents, rodutils,
- msgs
-
-import strutils except addf
-
-when defined(nimPreviewSlimSystem):
- import std/assertions
+ ast, hashes, intsets, strutils, options, msgs, ropes, idents, rodutils
proc hashNode*(p: RootRef): Hash
-proc treeToYaml*(conf: ConfigRef; n: PNode, indent: int = 0, maxRecDepth: int = - 1): Rope
+proc treeToYaml*(n: PNode, indent: int = 0, maxRecDepth: int = - 1): Rope
# Convert a tree into its YAML representation; this is used by the
# YAML code generator and it is invaluable for debugging purposes.
# If maxRecDepht <> -1 then it won't print the whole graph.
-proc typeToYaml*(conf: ConfigRef; n: PType, indent: int = 0, maxRecDepth: int = - 1): Rope
-proc symToYaml*(conf: ConfigRef; n: PSym, indent: int = 0, maxRecDepth: int = - 1): Rope
-proc lineInfoToStr*(conf: ConfigRef; info: TLineInfo): Rope
+proc typeToYaml*(n: PType, indent: int = 0, maxRecDepth: int = - 1): Rope
+proc symToYaml*(n: PSym, indent: int = 0, maxRecDepth: int = - 1): Rope
+proc lineInfoToStr*(info: TLineInfo): Rope
+# ----------------------- node sets: ---------------------------------------
+proc objectSetContains*(t: TObjectSet, obj: RootRef): bool
+ # returns true whether n is in t
+proc objectSetIncl*(t: var TObjectSet, obj: RootRef)
+ # include an element n in the table t
+proc objectSetContainsOrIncl*(t: var TObjectSet, obj: RootRef): bool
+ # more are not needed ...
+
+# ----------------------- (key, val)-Hashtables ----------------------------
+proc tablePut*(t: var TTable, key, val: RootRef)
+proc tableGet*(t: TTable, key: RootRef): RootRef
+type
+ TCmpProc* = proc (key, closure: RootRef): bool {.nimcall.} # true if found
+
+proc tableSearch*(t: TTable, key, closure: RootRef,
+ comparator: TCmpProc): RootRef
+ # return val as soon as comparator returns true; if this never happens,
+ # nil is returned
+
+# ----------------------- str table -----------------------------------------
+proc strTableContains*(t: TStrTable, n: PSym): bool
+proc strTableAdd*(t: var TStrTable, n: PSym)
+proc strTableGet*(t: TStrTable, name: PIdent): PSym
+
+type
+ TTabIter*{.final.} = object # consider all fields here private
+ h*: Hash # current hash
+
+proc initTabIter*(ti: var TTabIter, tab: TStrTable): PSym
+proc nextIter*(ti: var TTabIter, tab: TStrTable): PSym
+ # usage:
+ # var
+ # i: TTabIter
+ # s: PSym
+ # s = InitTabIter(i, table)
+ # while s != nil:
+ # ...
+ # s = NextIter(i, table)
+ #
+
+type
+ TIdentIter*{.final.} = object # iterator over all syms with same identifier
+ h*: Hash # current hash
+ name*: PIdent
+
+
+proc initIdentIter*(ti: var TIdentIter, tab: TStrTable, s: PIdent): PSym
+proc nextIdentIter*(ti: var TIdentIter, tab: TStrTable): PSym
# these are for debugging only: They are not really deprecated, but I want
# the warning so that release versions do not contain debugging statements:
-proc debug*(n: PSym; conf: ConfigRef = nil) {.exportc: "debugSym", deprecated.}
-proc debug*(n: PType; conf: ConfigRef = nil) {.exportc: "debugType", deprecated.}
-proc debug*(n: PNode; conf: ConfigRef = nil) {.exportc: "debugNode", deprecated.}
-
-proc typekinds*(t: PType) {.deprecated.} =
- var t = t
- var s = ""
- while t != nil and t.len > 0:
- s.add $t.kind
- s.add " "
- t = t.lastSon
- echo s
-
-template debug*(x: PSym|PType|PNode) {.deprecated.} =
- when compiles(c.config):
- debug(c.config, x)
- elif compiles(c.graph.config):
- debug(c.graph.config, x)
- else:
- error()
-
-template debug*(x: auto) {.deprecated.} =
- echo x
-
-template mdbg*: bool {.deprecated.} =
- when compiles(c.graph):
- c.module.fileIdx == c.graph.config.projectMainIdx
- elif compiles(c.module):
- c.module.fileIdx == c.config.projectMainIdx
- elif compiles(c.c.module):
- c.c.module.fileIdx == c.c.config.projectMainIdx
- elif compiles(m.c.module):
- m.c.module.fileIdx == m.c.config.projectMainIdx
- elif compiles(cl.c.module):
- cl.c.module.fileIdx == cl.c.config.projectMainIdx
- elif compiles(p):
- when compiles(p.lex):
- p.lex.fileIdx == p.lex.config.projectMainIdx
- else:
- p.module.module.fileIdx == p.config.projectMainIdx
- elif compiles(m.module.fileIdx):
- m.module.fileIdx == m.config.projectMainIdx
- elif compiles(L.fileIdx):
- L.fileIdx == L.config.projectMainIdx
- else:
- error()
+proc debug*(n: PSym) {.deprecated.}
+proc debug*(n: PType) {.deprecated.}
+proc debug*(n: PNode) {.deprecated.}
# --------------------------- ident tables ----------------------------------
proc idTableGet*(t: TIdTable, key: PIdObj): RootRef
@@ -91,7 +90,9 @@ proc idNodeTablePut*(t: var TIdNodeTable, key: PIdObj, val: PNode)
# ---------------------------------------------------------------------------
+proc getSymFromList*(list: PNode, ident: PIdent, start: int = 0): PSym
proc lookupInRecord*(n: PNode, field: PIdent): PSym
+proc getModule*(s: PSym): PSym
proc mustRehash*(length, counter: int): bool
proc nextTry*(h, maxHash: Hash): Hash {.inline.}
@@ -115,22 +116,22 @@ proc iiTablePut*(t: var TIITable, key, val: int)
# implementation
-proc skipConvCastAndClosure*(n: PNode): PNode =
+proc skipConvAndClosure*(n: PNode): PNode =
result = n
while true:
case result.kind
of nkObjUpConv, nkObjDownConv, nkChckRange, nkChckRangeF, nkChckRange64,
nkClosure:
- result = result[0]
- of nkHiddenStdConv, nkHiddenSubConv, nkConv, nkCast:
- result = result[1]
+ result = result.sons[0]
+ of nkHiddenStdConv, nkHiddenSubConv, nkConv:
+ result = result.sons[1]
else: break
proc sameValue*(a, b: PNode): bool =
result = false
case a.kind
of nkCharLit..nkUInt64Lit:
- if b.kind in {nkCharLit..nkUInt64Lit}: result = getInt(a) == getInt(b)
+ if b.kind in {nkCharLit..nkUInt64Lit}: result = a.intVal == b.intVal
of nkFloatLit..nkFloat64Lit:
if b.kind in {nkFloatLit..nkFloat64Lit}: result = a.floatVal == b.floatVal
of nkStrLit..nkTripleStrLit:
@@ -144,8 +145,8 @@ proc leValue*(a, b: PNode): bool =
# a <= b?
result = false
case a.kind
- of nkCharLit..nkUInt64Lit:
- if b.kind in {nkCharLit..nkUInt64Lit}: result = getInt(a) <= getInt(b)
+ of nkCharLit..nkUInt32Lit:
+ if b.kind in {nkCharLit..nkUInt32Lit}: result = a.intVal <= b.intVal
of nkFloatLit..nkFloat64Lit:
if b.kind in {nkFloatLit..nkFloat64Lit}: result = a.floatVal <= b.floatVal
of nkStrLit..nkTripleStrLit:
@@ -165,76 +166,36 @@ proc lookupInRecord(n: PNode, field: PIdent): PSym =
result = nil
case n.kind
of nkRecList:
- for i in 0.. 0 and a[alen] != '`': dec(alen)
- if alen <= 0: alen = a.len
-
- var i = 1
- var j = 1
- while true:
- while i < alen and a[i] == '_': inc i
- while j < b.len and b[j] == '_': inc j
- var aa = if i < alen: toLowerAscii(a[i]) else: '\0'
- var bb = if j < b.len: toLowerAscii(b[j]) else: '\0'
- if aa != bb: return false
-
- # the characters are identical:
- if i >= alen:
- # both cursors at the end:
- if j >= b.len: return true
- # not yet at the end of 'b':
- return false
- elif j >= b.len:
- return false
- inc i
- inc j
-
-proc getNamedParamFromList*(list: PNode, ident: PIdent): PSym =
- ## Named parameters are special because a named parameter can be
- ## gensym'ed and then they have '\`' suffix that we need to
- ## ignore, see compiler / evaltempl.nim, snippet:
- ## ```
- ## 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: add(result, ",")
+ addf(result, "$N$1$2",
+ [istr, symToYamlAux(n.data[i], marker, indent + 2, maxRecDepth - 1)])
+ inc(mycount)
+ if mycount > 0: addf(result, "$N$1", [rspaces(indent)])
+ add(result, "]")
+ assert(mycount == n.counter)
-proc symToYamlAux(conf: ConfigRef; n: PSym, marker: var IntSet, indent: int,
+proc ropeConstr(indent: int, c: openArray[Rope]): Rope =
+ # array of (name, value) pairs
+ var istr = rspaces(indent + 2)
+ result = rope("{")
+ var i = 0
+ while i <= high(c):
+ if i > 0: add(result, ",")
+ addf(result, "$N$1\"$2\": $3", [istr, c[i], c[i + 1]])
+ inc(i, 2)
+ addf(result, "$N$1}", [rspaces(indent)])
+
+proc symToYamlAux(n: PSym, marker: var IntSet, indent: int,
maxRecDepth: int): Rope =
if n == nil:
result = rope("null")
elif containsOrIncl(marker, n.id):
- result = "\"$1\"" % [rope(n.name.s)]
+ result = "\"$1 @$2\"" % [rope(n.name.s), rope(
+ strutils.toHex(cast[ByteAddress](n), sizeof(n) * 2))]
else:
- var ast = treeToYamlAux(conf, n.ast, marker, indent + 2, maxRecDepth - 1)
- #rope("typ"), typeToYamlAux(conf, n.typ, marker,
- # indent + 2, maxRecDepth - 1),
- let istr = rspaces(indent + 2)
- result = rope("{")
- result.addf("$N$1\"kind\": $2", [istr, makeYamlString($n.kind)])
- result.addf("$N$1\"name\": $2", [istr, makeYamlString(n.name.s)])
- result.addf("$N$1\"typ\": $2", [istr, typeToYamlAux(conf, n.typ, marker, indent + 2, maxRecDepth - 1)])
- if conf != nil:
- # if we don't pass the config, we probably don't care about the line info
- result.addf("$N$1\"info\": $2", [istr, lineInfoToStr(conf, n.info)])
- if card(n.flags) > 0:
- result.addf("$N$1\"flags\": $2", [istr, flagsToStr(n.flags)])
- result.addf("$N$1\"magic\": $2", [istr, makeYamlString($n.magic)])
- result.addf("$N$1\"ast\": $2", [istr, ast])
- result.addf("$N$1\"options\": $2", [istr, flagsToStr(n.options)])
- result.addf("$N$1\"position\": $2", [istr, rope(n.position)])
- result.addf("$N$1\"k\": $2", [istr, makeYamlString($n.loc.k)])
- result.addf("$N$1\"storage\": $2", [istr, makeYamlString($n.loc.storage)])
- if card(n.loc.flags) > 0:
- result.addf("$N$1\"flags\": $2", [istr, makeYamlString($n.loc.flags)])
- result.addf("$N$1\"r\": $2", [istr, n.loc.r])
- result.addf("$N$1\"lode\": $2", [istr, treeToYamlAux(conf, n.loc.lode, marker, indent + 2, maxRecDepth - 1)])
- result.addf("$N$1}", [rspaces(indent)])
+ var ast = treeToYamlAux(n.ast, marker, indent + 2, maxRecDepth - 1)
+ result = ropeConstr(indent, [rope("kind"),
+ makeYamlString($n.kind),
+ rope("name"), makeYamlString(n.name.s),
+ rope("typ"), typeToYamlAux(n.typ, marker,
+ indent + 2, maxRecDepth - 1),
+ rope("info"), lineInfoToStr(n.info),
+ rope("flags"), flagsToStr(n.flags),
+ rope("magic"), makeYamlString($n.magic),
+ rope("ast"), ast, rope("options"),
+ flagsToStr(n.options), rope("position"),
+ rope(n.position)])
-proc typeToYamlAux(conf: ConfigRef; n: PType, marker: var IntSet, indent: int,
+proc typeToYamlAux(n: PType, marker: var IntSet, indent: int,
maxRecDepth: int): Rope =
- var sonsRope: Rope
if n == nil:
- sonsRope = rope("null")
+ result = rope("null")
elif containsOrIncl(marker, n.id):
- sonsRope = "\"$1 @$2\"" % [rope($n.kind), rope(
- strutils.toHex(cast[int](n), sizeof(n) * 2))]
+ result = "\"$1 @$2\"" % [rope($n.kind), rope(
+ strutils.toHex(cast[ByteAddress](n), sizeof(n) * 2))]
else:
- if n.len > 0:
- sonsRope = rope("[")
- for i in 0.. 0: sonsRope.add(",")
- sonsRope.addf("$N$1$2", [rspaces(indent + 4), typeToYamlAux(conf, n[i],
+ if sonsLen(n) > 0:
+ result = rope("[")
+ for i in countup(0, sonsLen(n) - 1):
+ if i > 0: add(result, ",")
+ addf(result, "$N$1$2", [rspaces(indent + 4), typeToYamlAux(n.sons[i],
marker, indent + 4, maxRecDepth - 1)])
- sonsRope.addf("$N$1]", [rspaces(indent + 2)])
+ addf(result, "$N$1]", [rspaces(indent + 2)])
else:
- sonsRope = rope("null")
+ result = rope("null")
+ result = ropeConstr(indent, [rope("kind"),
+ makeYamlString($n.kind),
+ rope("sym"), symToYamlAux(n.sym, marker,
+ indent + 2, maxRecDepth - 1), rope("n"), treeToYamlAux(n.n, marker,
+ indent + 2, maxRecDepth - 1), rope("flags"), flagsToStr(n.flags),
+ rope("callconv"),
+ makeYamlString(CallingConvToStr[n.callConv]),
+ rope("size"), rope(n.size),
+ rope("align"), rope(n.align),
+ rope("sons"), result])
- let istr = rspaces(indent + 2)
- result = rope("{")
- result.addf("$N$1\"kind\": $2", [istr, makeYamlString($n.kind)])
- result.addf("$N$1\"sym\": $2", [istr, symToYamlAux(conf, n.sym, marker, indent + 2, maxRecDepth - 1)])
- result.addf("$N$1\"n\": $2", [istr, treeToYamlAux(conf, n.n, marker, indent + 2, maxRecDepth - 1)])
- if card(n.flags) > 0:
- result.addf("$N$1\"flags\": $2", [istr, flagsToStr(n.flags)])
- result.addf("$N$1\"callconv\": $2", [istr, makeYamlString($n.callConv)])
- result.addf("$N$1\"size\": $2", [istr, rope(n.size)])
- result.addf("$N$1\"align\": $2", [istr, rope(n.align)])
- result.addf("$N$1\"sons\": $2", [istr, sonsRope])
-
-proc treeToYamlAux(conf: ConfigRef; n: PNode, marker: var IntSet, indent: int,
+proc treeToYamlAux(n: PNode, marker: var IntSet, indent: int,
maxRecDepth: int): Rope =
if n == nil:
result = rope("null")
@@ -362,310 +334,137 @@ proc treeToYamlAux(conf: ConfigRef; n: PNode, marker: var IntSet, indent: int,
var istr = rspaces(indent + 2)
result = "{$N$1\"kind\": $2" % [istr, makeYamlString($n.kind)]
if maxRecDepth != 0:
- if conf != nil:
- result.addf(",$N$1\"info\": $2", [istr, lineInfoToStr(conf, n.info)])
+ addf(result, ",$N$1\"info\": $2", [istr, lineInfoToStr(n.info)])
case n.kind
of nkCharLit..nkInt64Lit:
- result.addf(",$N$1\"intVal\": $2", [istr, rope(n.intVal)])
+ addf(result, ",$N$1\"intVal\": $2", [istr, rope(n.intVal)])
of nkFloatLit, nkFloat32Lit, nkFloat64Lit:
- result.addf(",$N$1\"floatVal\": $2",
+ addf(result, ",$N$1\"floatVal\": $2",
[istr, rope(n.floatVal.toStrMaxPrecision)])
of nkStrLit..nkTripleStrLit:
- result.addf(",$N$1\"strVal\": $2", [istr, makeYamlString(n.strVal)])
+ if n.strVal.isNil:
+ addf(result, ",$N$1\"strVal\": null", [istr])
+ else:
+ addf(result, ",$N$1\"strVal\": $2", [istr, makeYamlString(n.strVal)])
of nkSym:
- result.addf(",$N$1\"sym\": $2",
- [istr, symToYamlAux(conf, n.sym, marker, indent + 2, maxRecDepth)])
+ addf(result, ",$N$1\"sym\": $2",
+ [istr, symToYamlAux(n.sym, marker, indent + 2, maxRecDepth)])
of nkIdent:
if n.ident != nil:
- result.addf(",$N$1\"ident\": $2", [istr, makeYamlString(n.ident.s)])
+ addf(result, ",$N$1\"ident\": $2", [istr, makeYamlString(n.ident.s)])
else:
- result.addf(",$N$1\"ident\": null", [istr])
+ addf(result, ",$N$1\"ident\": null", [istr])
else:
- if n.len > 0:
- result.addf(",$N$1\"sons\": [", [istr])
- for i in 0.. 0: result.add(",")
- result.addf("$N$1$2", [rspaces(indent + 4), treeToYamlAux(conf, n[i],
+ if sonsLen(n) > 0:
+ addf(result, ",$N$1\"sons\": [", [istr])
+ for i in countup(0, sonsLen(n) - 1):
+ if i > 0: add(result, ",")
+ addf(result, "$N$1$2", [rspaces(indent + 4), treeToYamlAux(n.sons[i],
marker, indent + 4, maxRecDepth - 1)])
- result.addf("$N$1]", [istr])
- result.addf(",$N$1\"typ\": $2",
- [istr, typeToYamlAux(conf, n.typ, marker, indent + 2, maxRecDepth)])
- result.addf("$N$1}", [rspaces(indent)])
+ addf(result, "$N$1]", [istr])
+ addf(result, ",$N$1\"typ\": $2",
+ [istr, typeToYamlAux(n.typ, marker, indent + 2, maxRecDepth)])
+ addf(result, "$N$1}", [rspaces(indent)])
-proc treeToYaml(conf: ConfigRef; n: PNode, indent: int = 0, maxRecDepth: int = - 1): Rope =
+proc treeToYaml(n: PNode, indent: int = 0, maxRecDepth: int = - 1): Rope =
var marker = initIntSet()
- result = treeToYamlAux(conf, n, marker, indent, maxRecDepth)
+ result = treeToYamlAux(n, marker, indent, maxRecDepth)
-proc typeToYaml(conf: ConfigRef; n: PType, indent: int = 0, maxRecDepth: int = - 1): Rope =
+proc typeToYaml(n: PType, indent: int = 0, maxRecDepth: int = - 1): Rope =
var marker = initIntSet()
- result = typeToYamlAux(conf, n, marker, indent, maxRecDepth)
+ result = typeToYamlAux(n, marker, indent, maxRecDepth)
-proc symToYaml(conf: ConfigRef; n: PSym, indent: int = 0, maxRecDepth: int = - 1): Rope =
+proc symToYaml(n: PSym, indent: int = 0, maxRecDepth: int = - 1): Rope =
var marker = initIntSet()
- result = symToYamlAux(conf, n, marker, indent, maxRecDepth)
+ result = symToYamlAux(n, marker, indent, maxRecDepth)
-import tables
-
-const backrefStyle = "\e[90m"
-const enumStyle = "\e[34m"
-const numberStyle = "\e[33m"
-const stringStyle = "\e[32m"
-const resetStyle = "\e[0m"
-
-type
- DebugPrinter = object
- conf: ConfigRef
- visited: Table[pointer, int]
- renderSymType: bool
- indent: int
- currentLine: int
- firstItem: bool
- useColor: bool
- res: string
-
-proc indentMore(this: var DebugPrinter) =
- this.indent += 2
-
-proc indentLess(this: var DebugPrinter) =
- this.indent -= 2
-
-proc newlineAndIndent(this: var DebugPrinter) =
- this.res.add "\n"
- this.currentLine += 1
- for i in 0.."
- if this.useColor:
- this.res.add resetStyle
- return
+ result = rope($n.kind)
+ if n.sym != nil:
+ add(result, " ")
+ add(result, n.sym.name.s)
+ if n.kind in IntegralTypes and n.n != nil:
+ add(result, ", node: ")
+ add(result, debugTree(n.n, 2, maxRecDepth-1, renderType=true))
+ if (n.kind != tyString) and (sonsLen(n) > 0) and maxRecDepth != 0:
+ add(result, "(")
+ for i in countup(0, sonsLen(n) - 1):
+ if i > 0: add(result, ", ")
+ if n.sons[i] == nil:
+ add(result, "null")
+ else:
+ add(result, debugType(n.sons[i], maxRecDepth-1))
+ if n.kind == tyObject and n.n != nil:
+ add(result, ", node: ")
+ add(result, debugTree(n.n, 2, maxRecDepth-1, renderType=true))
+ add(result, ")")
-proc value(this: var DebugPrinter; value: PType)
-proc value(this: var DebugPrinter; value: PNode)
-proc value(this: var DebugPrinter; value: PSym) =
- earlyExit(this, value)
-
- this.openCurly
- this.key("kind")
- this.value(value.kind)
- this.key("name")
- this.value(value.name.s)
- this.key("id")
- this.value(value.id)
- if value.kind in {skField, skEnumField, skParam}:
- this.key("position")
- this.value(value.position)
-
- if card(value.flags) > 0:
- this.key("flags")
- this.value(value.flags)
-
- if this.renderSymType and value.typ != nil:
- this.key "typ"
- this.value(value.typ)
-
- this.closeCurly
-
-proc value(this: var DebugPrinter; value: PType) =
- earlyExit(this, value)
-
- this.openCurly
- this.key "kind"
- this.value value.kind
-
- this.key "id"
- this.value value.id
-
- if value.sym != nil:
- this.key "sym"
- this.value value.sym
- #this.value value.sym.name.s
-
- if card(value.flags) > 0:
- this.key "flags"
- this.value value.flags
-
- if value.kind in IntegralTypes and value.n != nil:
- this.key "n"
- this.value value.n
-
- if value.len > 0:
- this.key "sons"
- this.openBracket
- for i in 0.. 0:
- this.key "comment"
- this.value value.comment
- when defined(useNodeIds):
- this.key "id"
- this.value value.id
- if this.conf != nil:
- this.key "info"
- this.value $lineInfoToStr(this.conf, value.info)
- if value.flags != {}:
- this.key "flags"
- this.value value.flags
-
- if value.typ != nil:
- this.key "typ"
- this.value value.typ.kind
+proc debugTree(n: PNode, indent: int, maxRecDepth: int;
+ renderType=false): Rope =
+ if n == nil:
+ result = rope("null")
else:
- this.key "typ"
- this.value "nil"
+ var istr = rspaces(indent + 2)
+ result = "{$N$1\"kind\": $2" %
+ [istr, makeYamlString($n.kind)]
+ if maxRecDepth != 0:
+ case n.kind
+ of nkCharLit..nkUInt64Lit:
+ addf(result, ",$N$1\"intVal\": $2", [istr, rope(n.intVal)])
+ of nkFloatLit, nkFloat32Lit, nkFloat64Lit:
+ addf(result, ",$N$1\"floatVal\": $2",
+ [istr, rope(n.floatVal.toStrMaxPrecision)])
+ of nkStrLit..nkTripleStrLit:
+ if n.strVal.isNil:
+ addf(result, ",$N$1\"strVal\": null", [istr])
+ else:
+ addf(result, ",$N$1\"strVal\": $2", [istr, makeYamlString(n.strVal)])
+ of nkSym:
+ addf(result, ",$N$1\"sym\": $2_$3",
+ [istr, rope(n.sym.name.s), rope(n.sym.id)])
+ # [istr, symToYaml(n.sym, indent, maxRecDepth),
+ # rope(n.sym.id)])
+ if renderType and n.sym.typ != nil:
+ addf(result, ",$N$1\"typ\": $2", [istr, debugType(n.sym.typ, 2)])
+ of nkIdent:
+ if n.ident != nil:
+ addf(result, ",$N$1\"ident\": $2", [istr, makeYamlString(n.ident.s)])
+ else:
+ addf(result, ",$N$1\"ident\": null", [istr])
+ else:
+ if sonsLen(n) > 0:
+ addf(result, ",$N$1\"sons\": [", [istr])
+ for i in countup(0, sonsLen(n) - 1):
+ if i > 0: add(result, ",")
+ addf(result, "$N$1$2", [rspaces(indent + 4), debugTree(n.sons[i],
+ indent + 4, maxRecDepth - 1, renderType)])
+ addf(result, "$N$1]", [istr])
+ addf(result, ",$N$1\"info\": $2", [istr, lineInfoToStr(n.info)])
+ addf(result, "$N$1}", [rspaces(indent)])
- case value.kind
- of nkCharLit..nkUInt64Lit:
- this.key "intVal"
- this.value value.intVal
- of nkFloatLit, nkFloat32Lit, nkFloat64Lit:
- this.key "floatVal"
- this.value value.floatVal.toStrMaxPrecision
- of nkStrLit..nkTripleStrLit:
- this.key "strVal"
- this.value value.strVal
- of nkSym:
- this.key "sym"
- this.value value.sym
- #this.value value.sym.name.s
- of nkIdent:
- if value.ident != nil:
- this.key "ident"
- this.value value.ident.s
+proc debug(n: PSym) =
+ if n == nil:
+ echo("null")
+ elif n.kind == skUnknown:
+ echo("skUnknown")
else:
- if this.renderSymType and value.typ != nil:
- this.key "typ"
- this.value value.typ
- if value.len > 0:
- this.key "sons"
- this.openBracket
- for i in 0..= 0: result = t.data[index].val
+ else: result = nil
+
+proc tableRawInsert(data: var TPairSeq, key, val: RootRef) =
+ var h: Hash = hashNode(key) and high(data)
+ while data[h].key != nil:
+ assert(data[h].key != key)
+ h = nextTry(h, high(data))
+ assert(data[h].key == nil)
+ data[h].key = key
+ data[h].val = val
+
+proc tableEnlarge(t: var TTable) =
+ var n: TPairSeq
+ newSeq(n, len(t.data) * GrowthFactor)
+ for i in countup(0, high(t.data)):
+ if t.data[i].key != nil: tableRawInsert(n, t.data[i].key, t.data[i].val)
+ swap(t.data, n)
+
+proc tablePut(t: var TTable, key, val: RootRef) =
+ var index = tableRawGet(t, key)
+ if index >= 0:
+ t.data[index].val = val
+ else:
+ if mustRehash(len(t.data), t.counter): tableEnlarge(t)
+ tableRawInsert(t.data, key, val)
+ inc(t.counter)
+
+proc strTableContains(t: TStrTable, n: PSym): bool =
var h: Hash = n.name.h and high(t.data) # start with real hash value
while t.data[h] != nil:
if (t.data[h] == n):
@@ -728,18 +576,35 @@ proc strTableContains*(t: TStrTable, n: PSym): bool =
h = nextTry(h, high(t.data))
result = false
-proc strTableRawInsert(data: var seq[PSym], n: PSym) =
+proc strTableRawInsert(data: var TSymSeq, n: PSym) =
var h: Hash = n.name.h and high(data)
- while data[h] != nil:
- if data[h] == n:
- # allowed for 'export' feature:
- #InternalError(n.info, "StrTableRawInsert: " & n.name.s)
- return
- h = nextTry(h, high(data))
- assert(data[h] == nil)
- data[h] = n
+ if sfImmediate notin n.flags:
+ # fast path:
+ while data[h] != nil:
+ if data[h] == n:
+ # allowed for 'export' feature:
+ #InternalError(n.info, "StrTableRawInsert: " & n.name.s)
+ return
+ h = nextTry(h, high(data))
+ assert(data[h] == nil)
+ data[h] = n
+ else:
+ # slow path; we have to ensure immediate symbols are preferred for
+ # symbol lookups.
+ # consider the chain: foo (immediate), foo, bar, bar (immediate)
+ # then bar (immediate) gets replaced with foo (immediate) and the non
+ # immediate foo is picked! Thus we need to replace it with the first
+ # slot that has in fact the same identifier stored in it!
+ var favPos = -1
+ while data[h] != nil:
+ if data[h] == n: return
+ if favPos < 0 and data[h].name.id == n.name.id: favPos = h
+ h = nextTry(h, high(data))
+ assert(data[h] == nil)
+ data[h] = n
+ if favPos >= 0: swap data[h], data[favPos]
-proc symTabReplaceRaw(data: var seq[PSym], prevSym: PSym, newSym: PSym) =
+proc symTabReplaceRaw(data: var TSymSeq, prevSym: PSym, newSym: PSym) =
assert prevSym.name.h == newSym.name.h
var h: Hash = prevSym.name.h and high(data)
while data[h] != nil:
@@ -753,22 +618,27 @@ proc symTabReplace*(t: var TStrTable, prevSym: PSym, newSym: PSym) =
symTabReplaceRaw(t.data, prevSym, newSym)
proc strTableEnlarge(t: var TStrTable) =
- var n: seq[PSym]
- newSeq(n, t.data.len * GrowthFactor)
- for i in 0..high(t.data):
+ var n: TSymSeq
+ newSeq(n, len(t.data) * GrowthFactor)
+ for i in countup(0, high(t.data)):
if t.data[i] != nil: strTableRawInsert(n, t.data[i])
swap(t.data, n)
-proc strTableAdd*(t: var TStrTable, n: PSym) =
- if mustRehash(t.data.len, t.counter): strTableEnlarge(t)
+proc strTableAdd(t: var TStrTable, n: PSym) =
+ if mustRehash(len(t.data), t.counter): strTableEnlarge(t)
strTableRawInsert(t.data, n)
inc(t.counter)
-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.
+proc reallySameIdent(a, b: string): bool {.inline.} =
+ when defined(nimfix):
+ result = a[0] == b[0]
+ else:
+ result = true
+
+proc strTableIncl*(t: var TStrTable, n: PSym; onConflictKeepOld=false): bool {.discardable.} =
+ # 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
@@ -779,29 +649,24 @@ proc strTableInclReportConflict*(t: var TStrTable, n: PSym;
# and overloading: (var x=@[]; x).mapIt(it).
# So it is possible the very same sym is added multiple
# times to the symbol table which we allow here with the 'it == n' check.
- if it.name.id == n.name.id:
- if it == n: return nil
+ if it.name.id == n.name.id and reallySameIdent(it.name.s, n.name.s):
+ if it == n: return false
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
- elif mustRehash(t.data.len, t.counter):
+ return true # found it
+ elif mustRehash(len(t.data), t.counter):
strTableEnlarge(t)
strTableRawInsert(t.data, n)
else:
assert(t.data[h] == nil)
t.data[h] = n
inc(t.counter)
- result = nil
+ result = false
-proc strTableIncl*(t: var TStrTable, n: PSym;
- onConflictKeepOld = false): bool {.discardable.} =
- result = strTableInclReportConflict(t, n, onConflictKeepOld) != nil
-
-proc strTableGet*(t: TStrTable, name: PIdent): PSym =
+proc strTableGet(t: TStrTable, name: PIdent): PSym =
var h: Hash = name.h and high(t.data)
while true:
result = t.data[h]
@@ -809,36 +674,25 @@ proc strTableGet*(t: TStrTable, name: PIdent): PSym =
if result.name.id == name.id: break
h = nextTry(h, high(t.data))
-
-type
- TIdentIter* = object # iterator over all syms with same identifier
- h*: Hash # current hash
- 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
- h = nextTry(h, high(tab.data))
- if h == start:
- p = nil
- break
- p = tab.data[h]
- if p != nil:
- result = p # increase the count
- else:
- result = nil
- ti.h = nextTry(h, high(tab.data))
-
-proc initIdentIter*(ti: var TIdentIter, tab: TStrTable, s: PIdent): PSym =
+proc initIdentIter(ti: var TIdentIter, tab: TStrTable, s: PIdent): PSym =
ti.h = s.h
ti.name = s
if tab.counter == 0: result = nil
else: result = nextIdentIter(ti, tab)
+proc nextIdentIter(ti: var TIdentIter, tab: TStrTable): PSym =
+ var h = ti.h and high(tab.data)
+ var start = h
+ result = tab.data[h]
+ while result != nil:
+ if result.name.id == ti.name.id: break
+ h = nextTry(h, high(tab.data))
+ if h == start:
+ result = nil
+ break
+ result = tab.data[h]
+ ti.h = nextTry(h, high(tab.data))
+
proc nextIdentExcluding*(ti: var TIdentIter, tab: TStrTable,
excluding: IntSet): PSym =
var h: Hash = ti.h and high(tab.data)
@@ -862,33 +716,20 @@ proc firstIdentExcluding*(ti: var TIdentIter, tab: TStrTable, s: PIdent,
if tab.counter == 0: result = nil
else: result = nextIdentExcluding(ti, tab, excluding)
-type
- TTabIter* = object
- h: Hash
+proc initTabIter(ti: var TTabIter, tab: TStrTable): PSym =
+ ti.h = 0 # we start by zero ...
+ if tab.counter == 0:
+ result = nil # FIX 1: removed endless loop
+ else:
+ result = nextIter(ti, tab)
-proc nextIter*(ti: var TTabIter, tab: TStrTable): PSym =
- # usage:
- # var
- # i: TTabIter
- # s: PSym
- # s = InitTabIter(i, table)
- # while s != nil:
- # ...
- # s = NextIter(i, table)
- #
+proc nextIter(ti: var TTabIter, tab: TStrTable): PSym =
result = nil
while (ti.h <= high(tab.data)):
result = tab.data[ti.h]
inc(ti.h) # ... and increment by one always
if result != nil: break
-proc initTabIter*(ti: var TTabIter, tab: TStrTable): PSym =
- ti.h = 0
- if tab.counter == 0:
- result = nil
- else:
- result = nextIter(ti, tab)
-
iterator items*(tab: TStrTable): PSym =
var it: TTabIter
var s = initTabIter(it, tab)
@@ -897,7 +738,7 @@ iterator items*(tab: TStrTable): PSym =
s = nextIter(it, tab)
proc hasEmptySlot(data: TIdPairSeq): bool =
- for h in 0..high(data):
+ for h in countup(0, high(data)):
if data[h].key == nil:
return true
result = false
@@ -950,9 +791,9 @@ proc idTablePut(t: var TIdTable, key: PIdObj, val: RootRef) =
assert(t.data[index].key != nil)
t.data[index].val = val
else:
- if mustRehash(t.data.len, t.counter):
- newSeq(n, t.data.len * GrowthFactor)
- for i in 0..high(t.data):
+ if mustRehash(len(t.data), t.counter):
+ newSeq(n, len(t.data) * GrowthFactor)
+ for i in countup(0, high(t.data)):
if t.data[i].key != nil:
idTableRawInsert(n, t.data[i].key, t.data[i].val)
assert(hasEmptySlot(n))
@@ -961,7 +802,7 @@ proc idTablePut(t: var TIdTable, key: PIdObj, val: RootRef) =
inc(t.counter)
iterator idTablePairs*(t: TIdTable): tuple[key: PIdObj, val: RootRef] =
- for i in 0..high(t.data):
+ for i in 0 .. high(t.data):
if not isNil(t.data[i].key): yield (t.data[i].key, t.data[i].val)
proc idNodeTableRawGet(t: TIdNodeTable, key: PIdObj): int =
@@ -979,6 +820,10 @@ proc idNodeTableGet(t: TIdNodeTable, key: PIdObj): PNode =
if index >= 0: result = t.data[index].val
else: result = nil
+proc idNodeTableGetLazy*(t: TIdNodeTable, key: PIdObj): PNode =
+ if not isNil(t.data):
+ result = idNodeTableGet(t, key)
+
proc idNodeTableRawInsert(data: var TIdNodePairSeq, key: PIdObj, val: PNode) =
var h: Hash
h = key.id and high(data)
@@ -995,24 +840,28 @@ proc idNodeTablePut(t: var TIdNodeTable, key: PIdObj, val: PNode) =
assert(t.data[index].key != nil)
t.data[index].val = val
else:
- if mustRehash(t.data.len, t.counter):
+ if mustRehash(len(t.data), t.counter):
var n: TIdNodePairSeq
- newSeq(n, t.data.len * GrowthFactor)
- for i in 0..high(t.data):
+ newSeq(n, len(t.data) * GrowthFactor)
+ for i in countup(0, high(t.data)):
if t.data[i].key != nil:
idNodeTableRawInsert(n, t.data[i].key, t.data[i].val)
swap(t.data, n)
idNodeTableRawInsert(t.data, key, val)
inc(t.counter)
+proc idNodeTablePutLazy*(t: var TIdNodeTable, key: PIdObj, val: PNode) =
+ if isNil(t.data): initIdNodeTable(t)
+ idNodeTablePut(t, key, val)
+
iterator pairs*(t: TIdNodeTable): tuple[key: PIdObj, val: PNode] =
- for i in 0..high(t.data):
+ for i in 0 .. high(t.data):
if not isNil(t.data[i].key): yield (t.data[i].key, t.data[i].val)
proc initIITable(x: var TIITable) =
x.counter = 0
newSeq(x.data, StartSize)
- for i in 0.. 0:
- result.add ", "
- result.add sym.name.s
-
-proc isDiscriminantField*(n: PNode): bool =
- if n.kind == nkCheckedFieldExpr: sfDiscriminant in n[0][1].sym.flags
- elif n.kind == nkDotExpr: sfDiscriminant in n[1].sym.flags
- else: false
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)
+ x[int(elem div ElemSize)] = x[int(elem div ElemSize)] or
+ toU8(int(1 shl (elem mod ElemSize)))
-proc bitSetExcl*(x: var TBitSet, elem: BiggestInt) =
- x[int(elem.divElemSize)] = x[int(elem.divElemSize)] and
- not(One shl elem.modElemSize)
+proc bitSetExcl(x: var TBitSet, elem: BiggestInt) =
+ x[int(elem div ElemSize)] = x[int(elem div ElemSize)] and
+ not toU8(int(1 shl (elem mod ElemSize)))
-proc bitSetInit*(b: var TBitSet, length: int) =
+proc bitSetInit(b: var TBitSet, length: int) =
newSeq(b, length)
-proc bitSetUnion*(x: var TBitSet, y: TBitSet) =
- for i in 0..high(x): x[i] = x[i] or y[i]
+proc bitSetUnion(x: var TBitSet, y: TBitSet) =
+ for i in countup(0, high(x)): x[i] = x[i] or y[i]
-proc bitSetDiff*(x: var TBitSet, y: TBitSet) =
- for i in 0..high(x): x[i] = x[i] and not y[i]
+proc bitSetDiff(x: var TBitSet, y: TBitSet) =
+ for i in countup(0, high(x)): x[i] = x[i] and not y[i]
-proc bitSetSymDiff*(x: var TBitSet, y: TBitSet) =
- for i in 0..high(x): x[i] = x[i] xor y[i]
+proc bitSetSymDiff(x: var TBitSet, y: TBitSet) =
+ for i in countup(0, high(x)): x[i] = x[i] xor y[i]
-proc bitSetIntersect*(x: var TBitSet, y: TBitSet) =
- for i in 0..high(x): x[i] = x[i] and y[i]
+proc bitSetIntersect(x: var TBitSet, y: TBitSet) =
+ for i in countup(0, high(x)): x[i] = x[i] and y[i]
-proc bitSetEquals*(x, y: TBitSet): bool =
- for i in 0..high(x):
+proc bitSetEquals(x, y: TBitSet): bool =
+ for i in countup(0, high(x)):
if x[i] != y[i]:
return false
result = true
-proc bitSetContains*(x, y: TBitSet): bool =
- for i in 0..high(x):
- if (x[i] and not y[i]) != Zero:
+proc bitSetContains(x, y: TBitSet): bool =
+ for i in countup(0, high(x)):
+ if (x[i] and not y[i]) != int8(0):
return false
result = true
-
-# Number of set bits for all values of int8
-const populationCount: array[uint8, uint8] = block:
- var arr: array[uint8, uint8]
-
- proc countSetBits(x: uint8): uint8 =
- return
- ( x and 0b00000001'u8) +
- ((x and 0b00000010'u8) shr 1) +
- ((x and 0b00000100'u8) shr 2) +
- ((x and 0b00001000'u8) shr 3) +
- ((x and 0b00010000'u8) shr 4) +
- ((x and 0b00100000'u8) shr 5) +
- ((x and 0b01000000'u8) shr 6) +
- ((x and 0b10000000'u8) shr 7)
-
-
- for it in low(uint8)..high(uint8):
- arr[it] = countSetBits(cast[uint8](it))
-
- arr
-
-proc bitSetCard*(x: TBitSet): BiggestInt =
- for it in x:
- result.inc int(populationCount[it])
diff --git a/compiler/btrees.nim b/compiler/btrees.nim
deleted file mode 100644
index c79442249..000000000
--- a/compiler/btrees.nim
+++ /dev/null
@@ -1,179 +0,0 @@
-#
-#
-# The Nim Compiler
-# (c) Copyright 2018 Andreas Rumpf
-#
-# See the file "copying.txt", included in this
-# distribution, for details about the copyright.
-#
-
-## 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
- entries: int
- keys: array[M, Key]
- case isInternal: bool
- of false:
- vals: array[M, Val]
- of true:
- links: array[M, Node[Key, Val]]
- BTree*[Key, Val] = object
- root: Node[Key, Val]
- entries: int ## number of key-value pairs
-
-proc initBTree*[Key, Val](): BTree[Key, Val] =
- BTree[Key, Val](root: Node[Key, Val](entries: 0, isInternal: false))
-
-template less(a, b): bool = cmp(a, b) < 0
-template eq(a, b): bool = cmp(a, b) == 0
-
-proc getOrDefault*[Key, Val](b: BTree[Key, Val], key: Key): Val =
- var x = b.root
- while x.isInternal:
- for j in 0.. 0: result.add(indent & "(" & $h.keys[j] & ")\n")
- toString(h.links[j], indent & " ", result)
-
-proc `$`[Key, Val](b: BTree[Key, Val]): string =
- result = ""
- toString(b.root, "", result)
-
-proc hasNext*[Key, Val](b: BTree[Key, Val]; index: int): bool = index < b.entries
-
-proc countSubTree[Key, Val](it: Node[Key, Val]): int =
- if it.isInternal:
- result = 0
- for k in 0.. i:
- it = it.links[k]
- dec i, (sum - c)
- break
- result = (it.keys[i], it.vals[i], index+1)
-
-iterator pairs*[Key, Val](b: BTree[Key, Val]): (Key, Val) =
- var i = 0
- while hasNext(b, i):
- let (k, v, i2) = next(b, i)
- i = i2
- yield (k, v)
-
-proc len*[Key, Val](b: BTree[Key, Val]): int {.inline.} = b.entries
diff --git a/compiler/canonicalizer.nim b/compiler/canonicalizer.nim
new file mode 100644
index 000000000..089bce302
--- /dev/null
+++ b/compiler/canonicalizer.nim
@@ -0,0 +1,415 @@
+#
+#
+# The Nim Compiler
+# (c) Copyright 2015 Andreas Rumpf
+#
+# See the file "copying.txt", included in this
+# distribution, for details about the copyright.
+#
+
+## This module implements the canonalization for the various caching mechanisms.
+
+import strutils, db_sqlite, md5
+
+var db: DbConn
+
+# We *hash* the relevant information into 128 bit hashes. This should be good
+# enough to prevent any collisions.
+
+type
+ TUid = distinct MD5Digest
+
+# For name mangling we encode these hashes via a variant of base64 (called
+# 'base64a') and prepend the *primary* identifier to ease the debugging pain.
+# So a signature like:
+#
+# proc gABI(c: PCtx; n: PNode; opc: TOpcode; a, b: TRegister; imm: BiggestInt)
+#
+# is mangled into:
+# gABI_MTdmOWY5MTQ1MDcyNGQ3ZA
+#
+# This is a good compromise between correctness and brevity. ;-)
+
+const
+ cb64 = [
+ "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N",
+ "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z",
+ "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n",
+ "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z",
+ "0", "1", "2", "3", "4", "5", "6", "7", "8", "9",
+ "_A", "_B"]
+
+proc toBase64a(s: cstring, len: int): string =
+ ## encodes `s` into base64 representation. After `lineLen` characters, a
+ ## `newline` is added.
+ result = newStringOfCap(((len + 2) div 3) * 4)
+ var i = 0
+ while i < s.len - 2:
+ let a = ord(s[i])
+ let b = ord(s[i+1])
+ let c = ord(s[i+2])
+ result.add cb64[a shr 2]
+ result.add cb64[((a and 3) shl 4) or ((b and 0xF0) shr 4)]
+ result.add cb64[((b and 0x0F) shl 2) or ((c and 0xC0) shr 6)]
+ result.add cb64[c and 0x3F]
+ inc(i, 3)
+ if i < s.len-1:
+ let a = ord(s[i])
+ let b = ord(s[i+1])
+ result.add cb64[a shr 2]
+ result.add cb64[((a and 3) shl 4) or ((b and 0xF0) shr 4)]
+ result.add cb64[((b and 0x0F) shl 2)]
+ elif i < s.len:
+ let a = ord(s[i])
+ result.add cb64[a shr 2]
+ result.add cb64[(a and 3) shl 4]
+
+proc toBase64a(u: TUid): string = toBase64a(cast[cstring](u), sizeof(u))
+
+proc `&=`(c: var MD5Context, s: string) = md5Update(c, s, s.len)
+
+proc hashSym(c: var MD5Context, s: PSym) =
+ if sfAnon in s.flags or s.kind == skGenericParam:
+ c &= ":anon"
+ else:
+ var it = s.owner
+ while it != nil:
+ hashSym(c, it)
+ c &= "."
+ it = s.owner
+ c &= s.name.s
+
+proc hashTree(c: var MD5Context, n: PNode) =
+ if n == nil:
+ c &= "\255"
+ return
+ var k = n.kind
+ md5Update(c, cast[cstring](addr(k)), 1)
+ # we really must not hash line information. 'n.typ' is debatable but
+ # shouldn't be necessary for now and avoids potential infinite recursions.
+ case n.kind
+ of nkEmpty, nkNilLit, nkType: discard
+ of nkIdent:
+ c &= n.ident.s
+ of nkSym:
+ hashSym(c, n.sym)
+ of nkCharLit..nkUInt64Lit:
+ var v = n.intVal
+ md5Update(c, cast[cstring](addr(v)), sizeof(v))
+ of nkFloatLit..nkFloat64Lit:
+ var v = n.floatVal
+ md5Update(c, cast[cstring](addr(v)), sizeof(v))
+ of nkStrLit..nkTripleStrLit:
+ c &= n.strVal
+ else:
+ for i 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:
+ add(result, '[')
+ encodeVInt(ord(t.kind), result)
+ add(result, '+')
+ encodeVInt(t.id, result)
+ if t.n != nil:
+ encodeNode(w, unknownLineInfo(), t.n, result)
+ if t.flags != {}:
+ add(result, '$')
+ encodeVInt(cast[int32](t.flags), result)
+ if t.callConv != low(t.callConv):
+ add(result, '?')
+ encodeVInt(ord(t.callConv), result)
+ if t.owner != nil:
+ add(result, '*')
+ encodeVInt(t.owner.id, result)
+ pushSym(w, t.owner)
+ if t.sym != nil:
+ add(result, '&')
+ encodeVInt(t.sym.id, result)
+ pushSym(w, t.sym)
+ if t.size != - 1:
+ add(result, '/')
+ encodeVBiggestInt(t.size, result)
+ if t.align != 2:
+ add(result, '=')
+ encodeVInt(t.align, result)
+ encodeLoc(w, t.loc, result)
+ for i in countup(0, sonsLen(t) - 1):
+ if t.sons[i] == nil:
+ add(result, "^()")
+ else:
+ add(result, '^')
+ encodeVInt(t.sons[i].id, result)
+ pushType(w, t.sons[i])
+
+proc encodeLib(w: PRodWriter, lib: PLib, info: TLineInfo, result: var string) =
+ add(result, '|')
+ encodeVInt(ord(lib.kind), result)
+ add(result, '|')
+ encodeStr($lib.name, result)
+ add(result, '|')
+ encodeNode(w, info, lib.path, result)
+
+proc encodeSym(w: PRodWriter, s: PSym, result: var string) =
+ if s == nil:
+ # nil nodes have to be stored too:
+ result.add("{}")
+ return
+ # we need no surrounding {} here because the symbol is in a line of its own
+ encodeVInt(ord(s.kind), result)
+ result.add('+')
+ encodeVInt(s.id, result)
+ result.add('&')
+ encodeStr(s.name.s, result)
+ if s.typ != nil:
+ result.add('^')
+ encodeVInt(s.typ.id, result)
+ pushType(w, s.typ)
+ result.add('?')
+ if s.info.col != -1'i16: encodeVInt(s.info.col, result)
+ result.add(',')
+ if s.info.line != -1'i16: encodeVInt(s.info.line, result)
+ result.add(',')
+ encodeVInt(fileIdx(w, toFilename(s.info)), result)
+ if s.owner != nil:
+ result.add('*')
+ encodeVInt(s.owner.id, result)
+ pushSym(w, s.owner)
+ if s.flags != {}:
+ result.add('$')
+ encodeVInt(cast[int32](s.flags), result)
+ if s.magic != mNone:
+ result.add('@')
+ encodeVInt(ord(s.magic), result)
+ if s.options != w.options:
+ result.add('!')
+ encodeVInt(cast[int32](s.options), result)
+ if s.position != 0:
+ result.add('%')
+ encodeVInt(s.position, result)
+ if s.offset != - 1:
+ result.add('`')
+ encodeVInt(s.offset, result)
+ encodeLoc(w, s.loc, result)
+ if s.annex != nil: encodeLib(w, s.annex, s.info, result)
+ if s.constraint != nil:
+ add(result, '#')
+ encodeNode(w, unknownLineInfo(), s.constraint, result)
+ # lazy loading will soon reload the ast lazily, so the ast needs to be
+ # the last entry of a symbol:
+ if s.ast != nil:
+ # we used to attempt to save space here by only storing a dummy AST if
+ # it is not necessary, but Nim's heavy compile-time evaluation features
+ # make that unfeasible nowadays:
+ encodeNode(w, s.info, s.ast, result)
+
+
+proc createDb() =
+ db.exec(sql"""
+ create table if not exists Module(
+ id integer primary key,
+ name varchar(256) not null,
+ fullpath varchar(256) not null,
+ interfHash varchar(256) not null,
+ fullHash varchar(256) not null,
+
+ created timestamp not null default (DATETIME('now')),
+ );""")
+
+ db.exec(sql"""
+ create table if not exists Symbol(
+ id integer primary key,
+ module integer not null,
+ name varchar(max) not null,
+ data varchar(max) not null,
+ created timestamp not null default (DATETIME('now')),
+
+ foreign key (module) references module(id)
+ );""")
+
+ db.exec(sql"""
+ create table if not exists Type(
+ id integer primary key,
+ module integer not null,
+ name varchar(max) not null,
+ data varchar(max) not null,
+ created timestamp not null default (DATETIME('now')),
+
+ foreign key (module) references module(id)
+ );""")
+
+
+ #db.exec(sql"""
+ # --create unique index if not exists TsstNameIx on TestResult(name);
+ # """, [])
+
diff --git a/compiler/ccgcalls.nim b/compiler/ccgcalls.nim
index 48e7fd290..bd17f85e4 100644
--- a/compiler/ccgcalls.nim
+++ b/compiler/ccgcalls.nim
@@ -9,531 +9,258 @@
#
# included from cgen.nim
-proc canRaiseDisp(p: BProc; n: PNode): bool =
- # we assume things like sysFatal cannot raise themselves
- if n.kind == nkSym and {sfNeverRaises, sfImportc, sfCompilerProc} * n.sym.flags != {}:
- result = false
- elif optPanics in p.config.globalOptions or
- (n.kind == nkSym and sfSystemModule in getModule(n.sym).flags and
- sfSystemRaisesDefect notin n.sym.flags):
- # we know we can be strict:
- result = canRaise(n)
- else:
- # we have to be *very* conservative:
- result = canRaiseConservative(n)
-
-proc preventNrvo(p: BProc; dest, le, ri: PNode): bool =
- proc locationEscapes(p: BProc; le: PNode; inTryStmt: bool): bool =
- var n = le
- while true:
- # do NOT follow nkHiddenDeref here!
- case n.kind
- of nkSym:
- # we don't own the location so it escapes:
- if n.sym.owner != p.prc:
- return true
- elif inTryStmt and sfUsedInFinallyOrExcept in n.sym.flags:
- # it is also an observable store if the location is used
- # in 'except' or 'finally'
- return true
- return false
- of nkDotExpr, nkBracketExpr, nkObjUpConv, nkObjDownConv,
- nkCheckedFieldExpr:
- n = n[0]
- of nkHiddenStdConv, nkHiddenSubConv, nkConv:
- n = n[1]
- else:
- # cannot analyse the location; assume the worst
- return true
-
+proc leftAppearsOnRightSide(le, ri: PNode): bool =
if le != nil:
- for i in 1.. 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:
- skipped = true
- q = q.lastSon
+ let q = skipConv(n)
if getMagic(q) == mSlice:
# magic: pass slice to openArray:
- if skipped:
- q = skipConv(n)
- while q.kind == nkStmtListExpr and q.len > 0:
- for i in 0..Field0, $1->Field1" % [rdLoc(a)]
+ var b, c: TLoc
+ initLocExpr(p, q[1], a)
+ initLocExpr(p, q[2], b)
+ initLocExpr(p, q[3], c)
+ let fmt =
+ case skipTypes(a.t, abstractVar+{tyPtr}).kind
+ of tyOpenArray, tyVarargs, tyArray, tyArrayConstr:
+ "($1)+($2), ($3)-($2)+1"
+ of tyString, tySequence:
+ if skipTypes(n.typ, abstractInst).kind == tyVar and
+ not compileToCpp(p.module):
+ "(*$1)->data+($2), ($3)-($2)+1"
else:
- result.add "$1.Field0, $1.Field1" % [rdLoc(a)]
- else:
- result.add "$1, $1Len_0" % [rdLoc(a)]
+ "$1->data+($2), ($3)-($2)+1"
+ else: (internalError("openArrayLoc: " & typeToString(a.t)); "")
+ result = fmt % [rdLoc(a), rdLoc(b), rdLoc(c)]
+ else:
+ initLocExpr(p, n, a)
+ case skipTypes(a.t, abstractVar).kind
+ of tyOpenArray, tyVarargs:
+ result = "$1, $1Len0" % [rdLoc(a)]
of tyString, tySequence:
- let ntyp = skipTypes(n.typ, abstractInst)
- if formalType.skipTypes(abstractInst).kind in {tyVar} and ntyp.kind == tyString and
- optSeqDestructors in p.config.globalOptions:
- linefmt(p, cpsStmts, "#nimPrepareStrMutationV2($1);$n", [byRefLoc(p, a)])
- 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)]
+ if skipTypes(n.typ, abstractInst).kind == tyVar and
+ not compileToCpp(p.module):
+ result = "(*$1)->data, (*$1)->$2" % [a.rdLoc, lenField(p)]
else:
- result.add "($4) ? ($1$3) : NIM_NIL, $2" %
- [a.rdLoc, lenExpr(p, a), dataField(p), dataFieldAccessor(p, a.rdLoc)]
- of tyArray:
- result.add "$1, $2" % [rdLoc(a), rope(lengthOrd(p.config, a.t))]
+ result = "$1->data, $1->$2" % [a.rdLoc, lenField(p)]
+ of tyArray, tyArrayConstr:
+ result = "$1, $2" % [rdLoc(a), rope(lengthOrd(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)]
- of tyArray:
- result.add "$1, $2" % [rdLoc(a), rope(lengthOrd(p.config, lastSon(a.t)))]
- else:
- internalError(p.config, "openArrayLoc: " & typeToString(a.t))
- else: internalError(p.config, "openArrayLoc: " & typeToString(a.t))
+ result = "(*$1)->data, (*$1)->$2" % [a.rdLoc, lenField(p)]
+ of tyArray, tyArrayConstr:
+ result = "$1, $2" % [rdLoc(a), rope(lengthOrd(lastSon(a.t)))]
+ else:
+ internalError("openArrayLoc: " & typeToString(a.t))
+ else: internalError("openArrayLoc: " & typeToString(a.t))
-proc withTmpIfNeeded(p: BProc, a: TLoc, needsTmp: bool): TLoc =
- # Bug https://github.com/status-im/nimbus-eth2/issues/1549
- # Aliasing is preferred over stack overflows.
- # Also don't regress for non ARC-builds, too risky.
- if needsTmp and a.lode.typ != nil and p.config.selectedGC in {gcArc, gcOrc} and
- getSize(p.config, a.lode.typ) < 1024:
- getTemp(p, a.lode.typ, result, needsInit=false)
- genAssignment(p, result, a, {})
- 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): Rope {.inline.} =
var a: TLoc
- initLocExpr(p, n[0], a)
- appcg(p.module, result, "#nimToCStringConv($1)", [withTmpIfNeeded(p, a, needsTmp).rdLoc])
+ initLocExpr(p, n.sons[0], a)
+ result = "$1->data" % [a.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): Rope =
var a: TLoc
if n.kind == nkStringToCString:
- genArgStringToCString(p, n, result, needsTmp)
+ result = genArgStringToCString(p, n)
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)
- elif ccgIntroducedPtr(p.config, param, call[0].typ[0]):
+ var n = if n.kind != nkHiddenAddr: n else: n.sons[0]
+ result = openArrayLoc(p, n)
+ elif ccgIntroducedPtr(param):
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)
- elif p.module.compileToCpp and param.typ.kind in {tyVar} and
+ result = addrLoc(a)
+ elif p.module.compileToCpp and param.typ.kind == tyVar and
n.kind == nkHiddenAddr:
- initLocExprSingleUse(p, n[0], a)
+ initLocExprSingleUse(p, n.sons[0], a)
# if the proc is 'importc'ed but not 'importcpp'ed then 'var T' still
# means '*T'. See posix.nim for lots of examples that do that in the wild.
- let callee = call[0]
+ let callee = call.sons[0]
if callee.kind == nkSym and
- {sfImportc, sfInfixCall, sfCompilerProc} * callee.sym.flags == {sfImportc} and
+ {sfImportC, sfInfixCall, sfCompilerProc} * callee.sym.flags == {sfImportC} and
{lfHeader, lfNoDecl} * callee.sym.loc.flags != {}:
- addAddrLoc(p.config, a, result)
+ result = addrLoc(a)
else:
- addRdLoc(a, result)
+ result = rdLoc(a)
else:
initLocExprSingleUse(p, n, a)
- addRdLoc(withTmpIfNeeded(p, a, needsTmp), result)
- #assert result != nil
+ result = rdLoc(a)
-proc genArgNoParam(p: BProc, n: PNode; result: var Rope; needsTmp = false) =
+proc genArgNoParam(p: BProc, n: PNode): Rope =
var a: TLoc
if n.kind == nkStringToCString:
- genArgStringToCString(p, n, result, needsTmp)
+ result = genArgStringToCString(p, n)
else:
initLocExprSingleUse(p, n, a)
- addRdLoc(withTmpIfNeeded(p, a, needsTmp), result)
+ result = rdLoc(a)
-import aliasanalysis
-
-proc potentialAlias(n: PNode, potentialWrites: seq[PNode]): bool =
- for p in potentialWrites:
- if p.aliases(n) != no or n.aliases(p) != no:
- return true
-
-proc skipTrivialIndirections(n: PNode): PNode =
- result = n
- while true:
- case result.kind
- of nkDerefExpr, nkHiddenDeref, nkAddr, nkHiddenAddr, nkObjDownConv, nkObjUpConv:
- result = result[0]
- of nkHiddenStdConv, nkHiddenSubConv:
- result = result[1]
- else: break
-
-proc getPotentialWrites(n: PNode; mutate: bool; result: var seq[PNode]) =
- case n.kind:
- of nkLiterals, nkIdent, nkFormalParams: discard
- of nkSym:
- if mutate: result.add n
- of nkAsgn, nkFastAsgn, nkSinkAsgn:
- getPotentialWrites(n[0], true, result)
- getPotentialWrites(n[1], mutate, result)
- 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(", ")
+ let rawProc = getRawProcType(p, typ)
+ let callPattern = if tfIterator in typ.flags: PatIter else: PatProc
+ if typ.sons[0] != nil:
+ if isInvalidReturnType(typ.sons[0]):
+ if sonsLen(ri) > 1: add(pl, ~", ")
# 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 leftAppearsOnRightSide(le, ri):
# Great, we can use 'd':
if d.k == locNone:
- getTemp(p, typ[0], d, needsInit=true)
- elif d.k notin {locTemp} and not hasNoInit(ri):
+ getTemp(p, typ.sons[0], d, needsInit=true)
+ elif d.k notin {locExpr, locTemp} and not hasNoInit(ri):
# reset before pass as 'result' var:
- discard "resetLoc(p, d)"
- pl.add(addrLoc(p.config, d))
+ resetLoc(p, d)
+ add(pl, addrLoc(d))
genCallPattern()
else:
var tmp: TLoc
- getTemp(p, typ[0], tmp, needsInit=true)
- pl.add(addrLoc(p.config, tmp))
+ getTemp(p, typ.sons[0], tmp, needsInit=true)
+ add(pl, addrLoc(tmp))
genCallPattern()
- if canRaise: raiseExit(p)
genAssignment(p, d, tmp, {}) # no need for deep copying
- elif isHarmlessStore(p, canRaise, d):
- if d.k == locNone: getTemp(p, typ[0], d)
- assert(d.t != nil) # generate an assignment to d:
- var list: TLoc
- initLoc(list, locCall, d.lode, OnUnknown)
- if tfIterator in typ.flags:
- list.r = PatIter % [rdLoc(op), pl, pl.addComma, rawProc]
- else:
- list.r = PatProc % [rdLoc(op), pl, pl.addComma, rawProc]
- genAssignment(p, d, list, {}) # no need for deep copying
- if canRaise: raiseExit(p)
else:
- var tmp: TLoc
- getTemp(p, typ[0], tmp)
+ if d.k == locNone: getTemp(p, typ.sons[0], d)
assert(d.t != nil) # generate an assignment to d:
var list: TLoc
- initLoc(list, locCall, d.lode, OnUnknown)
- if tfIterator in typ.flags:
- list.r = PatIter % [rdLoc(op), pl, pl.addComma, rawProc]
- else:
- list.r = PatProc % [rdLoc(op), pl, pl.addComma, rawProc]
- genAssignment(p, tmp, list, {})
- if canRaise: raiseExit(p)
- genAssignment(p, d, tmp, {})
+ initLoc(list, locCall, d.t, OnUnknown)
+ list.r = callPattern % [op.r, pl, pl.addComma, rawProc]
+ genAssignment(p, d, list, {}) # no need for deep copying
else:
genCallPattern()
- if canRaise: raiseExit(p)
-proc genOtherArg(p: BProc; ri: PNode; i: int; typ: PType; result: var Rope;
- argsCounter: var int) =
- if i < typ.len:
+proc genOtherArg(p: BProc; ri: PNode; i: int; typ: PType): Rope =
+ if i < sonsLen(typ):
# '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]
+ let paramType = typ.n.sons[i]
assert(paramType.kind == nkSym)
if paramType.typ.isCompileTimeOnly:
- discard
- 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 = nil
+ elif typ.sons[i].kind == tyVar and ri.sons[i].kind == nkHiddenAddr:
+ result = genArgNoParam(p, ri.sons[i][0])
else:
- if argsCounter > 0: result.add ", "
- genArgNoParam(p, ri[i], result) #, typ.n[i].sym)
- inc argsCounter
+ result = genArgNoParam(p, ri.sons[i]) #, typ.n.sons[i].sym)
else:
- if tfVarargs notin typ.flags:
- localError(p.config, ri.info, "wrong argument count")
- else:
- if argsCounter > 0: result.add ", "
- genArgNoParam(p, ri[i], result)
- inc argsCounter
+ result = genArgNoParam(p, ri.sons[i])
discard """
Dot call syntax in C++
@@ -577,99 +304,99 @@ proc skipAddrDeref(node: PNode): PNode =
var isAddr = false
case n.kind
of nkAddr, nkHiddenAddr:
- n = n[0]
+ n = n.sons[0]
isAddr = true
of nkDerefExpr, nkHiddenDeref:
- n = n[0]
+ n = n.sons[0]
else: return n
- if n.kind == nkObjDownConv: n = n[0]
+ if n.kind == nkObjDownConv: n = n.sons[0]
if isAddr and n.kind in {nkDerefExpr, nkHiddenDeref}:
- result = n[0]
+ result = n.sons[0]
elif n.kind in {nkAddr, nkHiddenAddr}:
- result = n[0]
+ result = n.sons[0]
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.
- internalAssert p.config, i < typ.len
- assert(typ.n[i].kind == nkSym)
+ internalAssert i < sonsLen(typ)
+ assert(typ.n.sons[i].kind == nkSym)
# if the parameter is lying (tyVar) and thus we required an additional deref,
# skip the deref:
var ri = ri[i]
while ri.kind == nkObjDownConv: ri = ri[0]
- let t = typ[i].skipTypes({tyGenericInst, tyAlias, tySink})
- if t.kind in {tyVar}:
+ let t = typ.sons[i].skipTypes({tyGenericInst})
+ if t.kind == 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.sons[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
- for k in j..= start:
- result.add(substr(pat, start, i - 1))
+ add(result, substr(pat, start, i - 1))
proc genInfixCall(p: BProc, le, ri: PNode, d: var TLoc) =
- var op: TLoc
- initLocExpr(p, ri[0], op)
+ var op, a: TLoc
+ initLocExpr(p, ri.sons[0], op)
# getUniqueType() is too expensive here:
- var typ = skipTypes(ri[0].typ, abstractInst)
+ var typ = skipTypes(ri.sons[0].typ, abstractInst)
assert(typ.kind == tyProc)
- assert(typ.len == typ.n.len)
+ var length = sonsLen(ri)
+ assert(sonsLen(typ) == sonsLen(typ.n))
# don't call '$' here for efficiency:
- let pat = $ri[0].sym.loc.r
- internalAssert p.config, pat.len > 0
+ let pat = ri.sons[0].sym.loc.r.data
+ internalAssert pat != nil
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:
+ var typ = skipTypes(ri.sons[0].typ, abstractInst)
+ if typ.sons[0] != nil:
if p.module.compileToCpp and lfSingleUse in d.flags:
# do not generate spurious temporaries for C++! For C we're better off
# with them to prevent undefined behaviour and because the codegen
@@ -703,130 +430,121 @@ proc genInfixCall(p: BProc, le, ri: PNode, d: var TLoc) =
d.r = pl
excl d.flags, lfSingleUse
else:
- if d.k == locNone: getTemp(p, typ[0], d)
+ if d.k == locNone: getTemp(p, typ.sons[0], d)
assert(d.t != nil) # generate an assignment to d:
var list: TLoc
- initLoc(list, locCall, d.lode, OnUnknown)
+ initLoc(list, locCall, d.t, OnUnknown)
list.r = pl
genAssignment(p, d, list, {}) # no need for deep copying
else:
- pl.add(";\n")
+ add(pl, ~";$n")
line(p, cpsStmts, pl)
else:
- var pl = newRopeAppender()
- var argsCounter = 0
+ var pl: Rope = nil
+ #var param = typ.n.sons[1].sym
if 1 < ri.len:
- genThisArg(p, ri, 1, typ, pl)
- pl.add(op.r)
- var params = newRopeAppender()
- for i in 2.. 0
+ let pat = ri.sons[0].sym.loc.r.data
+ internalAssert pat != nil
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)
+ add(pl, op.r)
+ if length > 1:
+ add(pl, ~": ")
+ add(pl, genArg(p, ri.sons[1], typ.n.sons[1].sym, ri))
start = 2
else:
- if ri.len > 1:
- genArg(p, ri[1], typ.n[1].sym, ri, pl)
- pl.add(" ")
- pl.add(op.r)
- if ri.len > 2:
- pl.add(": ")
- genArg(p, ri[2], typ.n[2].sym, ri, pl)
- for i in start..= typ.len:
- internalError(p.config, ri.info, "varargs for objective C method?")
- assert(typ.n[i].kind == nkSym)
- var param = typ.n[i].sym
- pl.add(" ")
- pl.add(param.name.s)
- pl.add(": ")
- genArg(p, ri[i], param, ri, pl)
- if typ[0] != nil:
- if isInvalidReturnType(p.config, typ):
- if ri.len > 1: pl.add(" ")
+ if length > 1:
+ add(pl, genArg(p, ri.sons[1], typ.n.sons[1].sym, ri))
+ add(pl, ~" ")
+ add(pl, op.r)
+ if length > 2:
+ add(pl, ~": ")
+ add(pl, genArg(p, ri.sons[2], typ.n.sons[2].sym, ri))
+ for i in countup(start, length-1):
+ assert(sonsLen(typ) == sonsLen(typ.n))
+ if i >= sonsLen(typ):
+ internalError(ri.info, "varargs for objective C method?")
+ assert(typ.n.sons[i].kind == nkSym)
+ var param = typ.n.sons[i].sym
+ add(pl, ~" ")
+ add(pl, param.name.s)
+ add(pl, ~": ")
+ add(pl, genArg(p, ri.sons[i], param, ri))
+ if typ.sons[0] != nil:
+ if isInvalidReturnType(typ.sons[0]):
+ if sonsLen(ri) > 1: add(pl, ~" ")
# 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(addrLoc(p.config, d))
- pl.add("];\n")
+ if d.k == locNone: getTemp(p, typ.sons[0], d, needsInit=true)
+ add(pl, ~"Result: ")
+ add(pl, addrLoc(d))
+ add(pl, ~"];$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")
+ getTemp(p, typ.sons[0], tmp, needsInit=true)
+ add(pl, addrLoc(tmp))
+ add(pl, ~"];$n")
line(p, cpsStmts, pl)
genAssignment(p, d, tmp, {}) # no need for deep copying
else:
- pl.add("]")
- if d.k == locNone: getTemp(p, typ[0], d)
+ add(pl, ~"]")
+ if d.k == locNone: getTemp(p, typ.sons[0], d)
assert(d.t != nil) # generate an assignment to d:
var list: TLoc
- initLoc(list, locCall, ri, OnUnknown)
+ initLoc(list, locCall, nil, OnUnknown)
list.r = pl
genAssignment(p, d, list, {}) # no need for deep copying
else:
- pl.add("];\n")
+ add(pl, ~"];$n")
line(p, cpsStmts, pl)
-proc notYetAlive(n: PNode): bool {.inline.} =
- let r = getRoot(n)
- result = r != nil and r.loc.lode == nil
-
-proc isInactiveDestructorCall(p: BProc, e: PNode): bool =
- #[ Consider this example.
-
- var :tmpD_3281815
- try:
- if true:
- return
- let args_3280013 =
- wasMoved_3281816(:tmpD_3281815)
- `=_3280036`(:tmpD_3281815, [1])
- :tmpD_3281815
- finally:
- `=destroy_3280027`(args_3280013)
-
- We want to return early but the 'finally' section is traversed before
- the 'let args = ...' statement. We exploit this to generate better
- code for 'return'. ]#
- result = e.len == 2 and e[0].kind == nkSym and
- e[0].sym.name.s == "=destroy" and notYetAlive(e[1].skipAddr)
+proc genCall(p: BProc, e: PNode, d: var TLoc) =
+ if e.sons[0].typ.skipTypes({tyGenericInst}).callConv == ccClosure:
+ genClosureCall(p, nil, e, d)
+ elif e.sons[0].kind == nkSym and sfInfixCall in e.sons[0].sym.flags:
+ genInfixCall(p, nil, e, d)
+ elif e.sons[0].kind == nkSym and sfNamedParamCall in e.sons[0].sym.flags:
+ genNamedParamCall(p, e, d)
+ else:
+ genPrefixCall(p, nil, e, d)
+ postStmtActions(p)
+ when false:
+ if d.s == onStack and containsGarbageCollectedRef(d.t): keepAlive(p, d)
proc genAsgnCall(p: BProc, le, ri: PNode, d: var TLoc) =
- if p.withinBlockLeaveActions > 0 and isInactiveDestructorCall(p, ri):
- return
- if ri[0].typ.skipTypes({tyGenericInst, tyAlias, tySink, tyOwned}).callConv == ccClosure:
+ if ri.sons[0].typ.skipTypes({tyGenericInst}).callConv == ccClosure:
genClosureCall(p, le, ri, d)
- elif ri[0].kind == nkSym and sfInfixCall in ri[0].sym.flags:
+ elif ri.sons[0].kind == nkSym and sfInfixCall in ri.sons[0].sym.flags:
genInfixCall(p, le, ri, d)
- elif ri[0].kind == nkSym and sfNamedParamCall in ri[0].sym.flags:
+ elif ri.sons[0].kind == nkSym and sfNamedParamCall in ri.sons[0].sym.flags:
genNamedParamCall(p, ri, d)
else:
genPrefixCall(p, le, ri, d)
+ postStmtActions(p)
+ when false:
+ if d.s == onStack and containsGarbageCollectedRef(d.t): keepAlive(p, d)
-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..0a67ca0af 100644
--- a/compiler/ccgexprs.nim
+++ b/compiler/ccgexprs.nim
@@ -9,153 +9,143 @@
# included from cgen.nim
-when defined(nimCompilerStacktraceHints):
- import std/stackframes
-
-proc getNullValueAuxT(p: BProc; orig, t: PType; obj, constOrNil: PNode,
- result: var Rope; count: var int;
- isConst: bool, info: TLineInfo)
-
# -------------------------- 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 = rfmt(nil, "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 = rfmt(nil, "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 int32Literal(i: int): Rope =
+ if i == int(low(int32)):
+ result = ~"(-2147483647 -1)"
+ else:
+ result = rope(i)
-proc genLiteral(p: BProc, n: PNode, ty: PType; result: var Rope) =
+proc genHexLiteral(v: PNode): Rope =
+ # hex literals are unsigned in C
+ # so we don't generate hex literals any longer.
+ if v.kind notin {nkIntLit..nkUInt64Lit}:
+ internalError(v.info, "genHexLiteral")
+ result = intLiteral(v.intVal)
+
+proc getStrLit(m: BModule, s: string): Rope =
+ discard cgsym(m, "TGenericSeq")
+ result = getTempName(m)
+ addf(m.s[cfsData], "STRING_LITERAL($1, $2, $3);$n",
+ [result, makeCString(s), rope(len(s))])
+
+proc genLiteral(p: BProc, n: PNode, ty: PType): Rope =
+ if ty == nil: internalError(n.info, "genLiteral: ty is nil")
case n.kind
of nkCharLit..nkUInt64Lit:
- var k: TTypeKind
- if ty != nil:
- k = skipTypes(ty, abstractVarRange).kind
- else:
- case n.kind
- of nkCharLit: k = tyChar
- of nkUInt64Lit: k = tyUInt64
- of nkInt64Lit: k = tyInt64
- else: k = tyNil # don't go into the case variant that uses 'ty'
- case k
+ case skipTypes(ty, abstractVarRange).kind
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,
+ skipTypes(ty, abstractVarRange)), 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 t = skipTypes(ty, abstractVarRange)
+ if t.kind == tyProc and t.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(
+ addf(p.module.s[cfsData],
"static NIM_CONST $1 $2 = {NIM_NIL,NIM_NIL};$n",
- [getTypeDesc(p.module, ty), tmpName])
- result.add tmpName
- elif k in {tyPointer, tyNil, tyProc}:
- result.add rope("NIM_NIL")
+ [getTypeDesc(p.module, t), result])
else:
- result.add "(($1) NIM_NIL)" % [getTypeDesc(p.module, ty)]
+ result = rope("NIM_NIL")
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)
- of tyString:
- # with the new semantics for not 'nil' strings, we can map "" to nil and
- # save tons of allocations:
- if n.strVal.len == 0 and optSeqDestructors notin p.config.globalOptions:
- genNilStringLiteral(p.module, n.info, result)
+ if n.strVal.isNil:
+ result = ropecg(p.module, "((#NimStringDesc*) NIM_NIL)", [])
+ elif skipTypes(ty, abstractVarRange).kind == tyString:
+ let id = nodeTableTestOrSet(p.module.dataCache, n, p.module.labels)
+ if id == p.module.labels:
+ # string literal not found in the cache:
+ result = ropecg(p.module, "((#NimStringDesc*) &$1)",
+ [getStrLit(p.module, n.strVal)])
else:
- genStringLiteral(p.module, n, result)
+ result = ropecg(p.module, "((#NimStringDesc*) &$1$2)",
+ [p.module.tmpBase, rope(id)])
else:
- result.add makeCString(n.strVal)
- of nkFloatLit, nkFloat64Lit:
- if ty.kind == tyFloat32:
- result.add rope(n.floatVal.float32.toStrMaxPrecision)
- else:
- result.add rope(n.floatVal.toStrMaxPrecision)
- of nkFloat32Lit:
- result.add rope(n.floatVal.float32.toStrMaxPrecision)
+ result = makeCString(n.strVal)
+ of nkFloatLit..nkFloat64Lit:
+ result = rope(n.floatVal.toStrMaxPrecision)
else:
- internalError(p.config, n.info, "genLiteral(" & $n.kind & ')')
+ internalError(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 =
+proc bitSetToWord(s: TBitSet, size: int): BiggestInt =
result = 0
- for j in 0.. 8:
- var res = "{\n"
- for i in 0.. 8:
+ result = "{$n" % []
+ for i in countup(0, size - 1):
+ if i < size - 1:
+ # not last iteration?
+ if (i + 1) mod 8 == 0: frmt = "0x$1,$n"
+ else: frmt = "0x$1, "
+ else:
+ frmt = "0x$1}$n"
+ addf(result, frmt, [rope(toHex(ze64(cs[i]), 2))])
+ else:
+ result = intLiteral(bitSetToWord(cs, size))
+ # result := rope('0x' + ToHex(bitSetToWord(cs, size), size * 2))
+
+proc genSetNode(p: BProc, n: PNode): Rope =
+ var cs: TBitSet
+ var size = int(getSize(n.typ))
+ toBitSet(n, cs)
if size > 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
+ addf(p.module.s[cfsData], "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
case n.kind
of nkSym:
case n.sym.kind
@@ -169,43 +159,46 @@ proc getStorageLoc(n: PNode): TStorageLoc =
else: result = OnUnknown
else: result = OnUnknown
of nkDerefExpr, nkHiddenDeref:
- case n[0].typ.kind
- of tyVar, tyLent: result = OnUnknown
+ case n.sons[0].typ.kind
+ of tyVar: result = OnUnknown
of tyPtr: result = OnStack
of tyRef: result = OnHeap
- else: doAssert(false, "getStorageLoc")
+ else: internalError(n.info, "getStorageLoc")
of nkBracketExpr, nkDotExpr, nkObjDownConv, nkObjUpConv:
- result = getStorageLoc(n[0])
+ result = getStorageLoc(n.sons[0])
else: result = OnUnknown
-proc canMove(p: BProc, n: PNode; dest: TLoc): bool =
- # for now we're conservative here:
- if n.kind == nkBracket:
- # This needs to be kept consistent with 'const' seq code
- # generation!
- 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:
- # Empty strings are codegen'd as NIM_NIL so it's just a pointer copy
- return true
- result = n.kind in nkCallKinds
- #if not result and dest.k == locTemp:
- # return true
-
- #if result:
- # echo n.info, " optimized ", n
- # result = false
-
-proc genRefAssign(p: BProc, dest, src: TLoc) =
- if (dest.storage == OnStack and p.config.selectedGC != gcGo) or not usesWriteBarrier(p.config):
- linefmt(p, cpsStmts, "$1 = $2;$n", [rdLoc(dest), rdLoc(src)])
- elif dest.storage == OnHeap:
- linefmt(p, cpsStmts, "#asgnRef((void**) $1, $2);$n",
- [addrLoc(p.config, dest), rdLoc(src)])
+proc genRefAssign(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) =
+ if dest.s == OnStack or not usesNativeGC():
+ linefmt(p, cpsStmts, "$1 = $2;$n", rdLoc(dest), rdLoc(src))
+ if needToKeepAlive in flags: keepAlive(p, dest)
+ elif dest.s == OnHeap:
+ # location is on heap
+ # now the writer barrier is inlined for performance:
+ #
+ # if afSrcIsNotNil in flags:
+ # UseMagic(p.module, 'nimGCref')
+ # lineF(p, cpsStmts, 'nimGCref($1);$n', [rdLoc(src)])
+ # elif afSrcIsNil notin flags:
+ # UseMagic(p.module, 'nimGCref')
+ # lineF(p, cpsStmts, 'if ($1) nimGCref($1);$n', [rdLoc(src)])
+ # if afDestIsNotNil in flags:
+ # UseMagic(p.module, 'nimGCunref')
+ # lineF(p, cpsStmts, 'nimGCunref($1);$n', [rdLoc(dest)])
+ # elif afDestIsNil notin flags:
+ # UseMagic(p.module, 'nimGCunref')
+ # lineF(p, cpsStmts, 'if ($1) nimGCunref($1);$n', [rdLoc(dest)])
+ # lineF(p, cpsStmts, '$1 = $2;$n', [rdLoc(dest), rdLoc(src)])
+ if canFormAcycle(dest.t):
+ linefmt(p, cpsStmts, "#asgnRef((void**) $1, $2);$n",
+ addrLoc(dest), rdLoc(src))
+ else:
+ linefmt(p, cpsStmts, "#asgnRefNoCycle((void**) $1, $2);$n",
+ addrLoc(dest), rdLoc(src))
else:
linefmt(p, cpsStmts, "#unsureAsgnRef((void**) $1, $2);$n",
- [addrLoc(p.config, dest), rdLoc(src)])
+ addrLoc(dest), rdLoc(src))
+ if needToKeepAlive in flags: keepAlive(p, dest)
proc asgnComplexity(n: PNode): int =
if n != nil:
@@ -220,32 +213,33 @@ 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
+ result.s = a.s
+ result.t = t
result.r = rdLoc(a) & "." & field
+ result.heapRoot = a.heapRoot
proc genOptAsgnTuple(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) =
let newflags =
- if src.storage == OnStatic:
+ if src.s == OnStatic:
flags + {needToCopy}
elif tfShallow in dest.t.flags:
flags - {needToCopy}
else:
flags
let t = skipTypes(dest.t, abstractInst).getUniqueType()
- for i in 0.. $3){ #raiseOverflow(); ",
- [result, first, last])
- raiseInstr(p, p.s(cpsStmts))
- linefmt p, cpsStmts, "}$n", []
-
- result
+ result = getTempName(p.module)
+ linefmt(p, cpsLocals, "$1 $2;$n", storage, result)
+ lineCg(p, cpsStmts, frmt, result, rdCharLoc(a), rdCharLoc(b))
+ if size < platform.intSize or t.kind in {tyRange, tyEnum}:
+ linefmt(p, cpsStmts, "if ($1 < $2 || $1 > $3) #raiseOverflow();$n",
+ result, intLiteral(firstOrd(t)), intLiteral(lastOrd(t)))
proc binaryArithOverflow(p: BProc, e: PNode, d: var TLoc, m: TMagic) =
const
prc: array[mAddI..mPred, string] = [
- "nimAddInt", "nimSubInt",
- "nimMulInt", "nimDivInt", "nimModInt",
- "nimAddInt", "nimSubInt"
- ]
+ "$# = #addInt($#, $#);$n", "$# = #subInt($#, $#);$n",
+ "$# = #mulInt($#, $#);$n", "$# = #divInt($#, $#);$n",
+ "$# = #modInt($#, $#);$n",
+ "$# = #addInt($#, $#);$n", "$# = #subInt($#, $#);$n"]
prc64: array[mAddI..mPred, string] = [
- "nimAddInt64", "nimSubInt64",
- "nimMulInt64", "nimDivInt64", "nimModInt64",
- "nimAddInt64", "nimSubInt64"
- ]
- opr: array[mAddI..mPred, string] = ["+", "-", "*", "/", "%", "+", "-"]
+ "$# = #addInt64($#, $#);$n", "$# = #subInt64($#, $#);$n",
+ "$# = #mulInt64($#, $#);$n", "$# = #divInt64($#, $#);$n",
+ "$# = #modInt64($#, $#);$n",
+ "$# = #addInt64($#, $#);$n", "$# = #subInt64($#, $#);$n"]
+ opr: array[mAddI..mPred, string] = [
+ "($#)($# + $#)", "($#)($# - $#)", "($#)($# * $#)",
+ "($#)($# / $#)", "($#)($# % $#)",
+ "($#)($# + $#)", "($#)($# - $#)"]
var a, b: TLoc
- assert(e[1].typ != nil)
- assert(e[2].typ != nil)
- initLocExpr(p, e[1], a)
- initLocExpr(p, e[2], b)
+ assert(e.sons[1].typ != nil)
+ assert(e.sons[2].typ != nil)
+ initLocExpr(p, e.sons[1], a)
+ initLocExpr(p, e.sons[2], b)
# skipping 'range' is correct here as we'll generate a proper range check
# later via 'chckRange'
let t = e.typ.skipTypes(abstractRange)
if optOverflowCheck notin p.options:
- let res = "($1)($2 $3 $4)" % [getTypeDesc(p.module, e.typ), rdLoc(a), rope(opr[m]), rdLoc(b)]
- putIntoDest(p, d, e, res)
+ let res = opr[m] % [getTypeDesc(p.module, t), rdLoc(a), rdLoc(b)]
+ putIntoDest(p, d, e.typ, res)
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)
+ let res = binaryArithOverflowRaw(p, t, a, b,
+ if t.kind == tyInt64: prc64[m] else: prc[m])
+ putIntoDest(p, d, e.typ, "($#)($#)" % [getTypeDesc(p.module, t), res])
proc unaryArithOverflow(p: BProc, e: PNode, d: var TLoc, m: TMagic) =
+ const
+ opr: array[mUnaryMinusI..mAbsI, string] = [
+ mUnaryMinusI: "((NI$2)-($1))",
+ mUnaryMinusI64: "-($1)",
+ mAbsI: "($1 > 0? ($1) : -($1))"]
var
a: TLoc
t: PType
- assert(e[1].typ != nil)
- initLocExpr(p, e[1], a)
+ assert(e.sons[1].typ != nil)
+ initLocExpr(p, e.sons[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", []
-
- case m
- of mUnaryMinusI:
- putIntoDest(p, d, e, "((NI$2)-($1))" % [rdLoc(a), rope(getSize(p.config, t) * 8)])
- of mUnaryMinusI64:
- putIntoDest(p, d, e, "-($1)" % [rdLoc(a)])
- of mAbsI:
- putIntoDest(p, d, e, "($1 > 0? ($1) : -($1))" % [rdLoc(a)])
- else:
- assert(false, $m)
+ linefmt(p, cpsStmts, "if ($1 == $2) #raiseOverflow();$n",
+ rdLoc(a), intLiteral(firstOrd(t)))
+ putIntoDest(p, d, e.typ, opr[m] % [rdLoc(a), rope(getSize(t) * 8)])
proc binaryArith(p: BProc, e: PNode, d: var TLoc, op: TMagic) =
+ const
+ binArithTab: array[mAddF64..mXor, string] = [
+ "(($4)($1) + ($4)($2))", # AddF64
+ "(($4)($1) - ($4)($2))", # SubF64
+ "(($4)($1) * ($4)($2))", # MulF64
+ "(($4)($1) / ($4)($2))", # DivF64
+
+ "($4)((NU$3)($1) >> (NU$3)($2))", # ShrI
+ "($4)((NU$3)($1) << (NU$3)($2))", # ShlI
+ "($4)($1 & $2)", # BitandI
+ "($4)($1 | $2)", # BitorI
+ "($4)($1 ^ $2)", # BitxorI
+ "(($1 <= $2) ? $1 : $2)", # MinI
+ "(($1 >= $2) ? $1 : $2)", # MaxI
+ "(($1 <= $2) ? $1 : $2)", # MinF64
+ "(($1 >= $2) ? $1 : $2)", # MaxF64
+ "($4)((NU$3)($1) + (NU$3)($2))", # AddU
+ "($4)((NU$3)($1) - (NU$3)($2))", # SubU
+ "($4)((NU$3)($1) * (NU$3)($2))", # MulU
+ "($4)((NU$3)($1) / (NU$3)($2))", # DivU
+ "($4)((NU$3)($1) % (NU$3)($2))", # ModU
+ "($1 == $2)", # EqI
+ "($1 <= $2)", # LeI
+ "($1 < $2)", # LtI
+ "($1 == $2)", # EqF64
+ "($1 <= $2)", # LeF64
+ "($1 < $2)", # LtF64
+ "((NU$3)($1) <= (NU$3)($2))", # LeU
+ "((NU$3)($1) < (NU$3)($2))", # LtU
+ "((NU64)($1) <= (NU64)($2))", # LeU64
+ "((NU64)($1) < (NU64)($2))", # LtU64
+ "($1 == $2)", # EqEnum
+ "($1 <= $2)", # LeEnum
+ "($1 < $2)", # LtEnum
+ "((NU8)($1) == (NU8)($2))", # EqCh
+ "((NU8)($1) <= (NU8)($2))", # LeCh
+ "((NU8)($1) < (NU8)($2))", # LtCh
+ "($1 == $2)", # EqB
+ "($1 <= $2)", # LeB
+ "($1 < $2)", # LtB
+ "($1 == $2)", # EqRef
+ "($1 == $2)", # EqPtr
+ "($1 <= $2)", # LePtr
+ "($1 < $2)", # LtPtr
+ "($1 != $2)"] # Xor
var
a, b: TLoc
- s, k: BiggestInt
- assert(e[1].typ != nil)
- assert(e[2].typ != nil)
- initLocExpr(p, e[1], a)
- initLocExpr(p, e[2], b)
+ s: BiggestInt
+ assert(e.sons[1].typ != nil)
+ assert(e.sons[2].typ != nil)
+ initLocExpr(p, e.sons[1], a)
+ initLocExpr(p, e.sons[2], b)
# BUGFIX: cannot use result-type here, as it may be a boolean
- s = max(getSize(p.config, a.t), getSize(p.config, b.t)) * 8
- k = getSize(p.config, a.t) * 8
-
- template applyFormat(frmt: untyped) =
- putIntoDest(p, d, e, frmt % [
- rdLoc(a), rdLoc(b), rope(s),
- getSimpleTypeDesc(p.module, e.typ), rope(k)]
- )
-
- case op
- of mAddF64: applyFormat("(($4)($1) + ($4)($2))")
- of mSubF64: applyFormat("(($4)($1) - ($4)($2))")
- of mMulF64: applyFormat("(($4)($1) * ($4)($2))")
- of mDivF64: applyFormat("(($4)($1) / ($4)($2))")
- of mShrI: applyFormat("($4)((NU$5)($1) >> (NU$3)($2))")
- of mShlI: applyFormat("($4)((NU$3)($1) << (NU$3)($2))")
- of mAshrI: applyFormat("($4)((NI$3)($1) >> (NU$3)($2))")
- of mBitandI: applyFormat("($4)($1 & $2)")
- of mBitorI: applyFormat("($4)($1 | $2)")
- of mBitxorI: applyFormat("($4)($1 ^ $2)")
- of mMinI: applyFormat("(($1 <= $2) ? $1 : $2)")
- of mMaxI: applyFormat("(($1 >= $2) ? $1 : $2)")
- of mAddU: applyFormat("($4)((NU$3)($1) + (NU$3)($2))")
- of mSubU: applyFormat("($4)((NU$3)($1) - (NU$3)($2))")
- of mMulU: applyFormat("($4)((NU$3)($1) * (NU$3)($2))")
- of mDivU: applyFormat("($4)((NU$3)($1) / (NU$3)($2))")
- of mModU: applyFormat("($4)((NU$3)($1) % (NU$3)($2))")
- of mEqI: applyFormat("($1 == $2)")
- of mLeI: applyFormat("($1 <= $2)")
- of mLtI: applyFormat("($1 < $2)")
- of mEqF64: applyFormat("($1 == $2)")
- of mLeF64: applyFormat("($1 <= $2)")
- of mLtF64: applyFormat("($1 < $2)")
- of mLeU: applyFormat("((NU$3)($1) <= (NU$3)($2))")
- of mLtU: applyFormat("((NU$3)($1) < (NU$3)($2))")
- of mEqEnum: applyFormat("($1 == $2)")
- of mLeEnum: applyFormat("($1 <= $2)")
- of mLtEnum: applyFormat("($1 < $2)")
- of mEqCh: applyFormat("((NU8)($1) == (NU8)($2))")
- of mLeCh: applyFormat("((NU8)($1) <= (NU8)($2))")
- of mLtCh: applyFormat("((NU8)($1) < (NU8)($2))")
- of mEqB: applyFormat("($1 == $2)")
- of mLeB: applyFormat("($1 <= $2)")
- of mLtB: applyFormat("($1 < $2)")
- of mEqRef: applyFormat("($1 == $2)")
- of mLePtr: applyFormat("($1 <= $2)")
- of mLtPtr: applyFormat("($1 < $2)")
- of mXor: applyFormat("($1 != $2)")
- else:
- assert(false, $op)
+ s = max(getSize(a.t), getSize(b.t)) * 8
+ putIntoDest(p, d, e.typ,
+ binArithTab[op] % [rdLoc(a), rdLoc(b), rope(s),
+ getSimpleTypeDesc(p.module, e.typ)])
proc genEqProc(p: BProc, e: PNode, d: var TLoc) =
var a, b: TLoc
- assert(e[1].typ != nil)
- assert(e[2].typ != nil)
- initLocExpr(p, e[1], a)
- initLocExpr(p, e[2], b)
- if a.t.skipTypes(abstractInstOwned).callConv == ccClosure:
- putIntoDest(p, d, e,
- "($1.ClP_0 == $2.ClP_0 && $1.ClE_0 == $2.ClE_0)" % [rdLoc(a), rdLoc(b)])
+ assert(e.sons[1].typ != nil)
+ assert(e.sons[2].typ != nil)
+ initLocExpr(p, e.sons[1], a)
+ initLocExpr(p, e.sons[2], b)
+ if a.t.skipTypes(abstractInst).callConv == ccClosure:
+ putIntoDest(p, d, e.typ,
+ "($1.ClPrc == $2.ClPrc && $1.ClEnv == $2.ClEnv)" % [rdLoc(a), rdLoc(b)])
else:
- putIntoDest(p, d, e, "($1 == $2)" % [rdLoc(a), rdLoc(b)])
+ putIntoDest(p, d, e.typ, "($1 == $2)" % [rdLoc(a), rdLoc(b)])
proc genIsNil(p: BProc, e: PNode, d: var TLoc) =
- let t = skipTypes(e[1].typ, abstractRange)
+ let t = skipTypes(e.sons[1].typ, abstractRange)
if t.kind == tyProc and t.callConv == ccClosure:
- unaryExpr(p, e, d, "($1.ClP_0 == 0)")
+ unaryExpr(p, e, d, "$1.ClPrc == 0")
else:
- unaryExpr(p, e, d, "($1 == 0)")
+ unaryExpr(p, e, d, "$1 == 0")
proc unaryArith(p: BProc, e: PNode, d: var TLoc, op: TMagic) =
+ const
+ unArithTab: array[mNot..mToBiggestInt, string] = ["!($1)", # Not
+ "$1", # UnaryPlusI
+ "($3)((NU$2) ~($1))", # BitnotI
+ "$1", # UnaryPlusF64
+ "-($1)", # UnaryMinusF64
+ "($1 > 0? ($1) : -($1))", # AbsF64; BUGFIX: fabs() makes problems
+ # for Tiny C, so we don't use it
+ "(($3)(NU)(NU8)($1))", # mZe8ToI
+ "(($3)(NU64)(NU8)($1))", # mZe8ToI64
+ "(($3)(NU)(NU16)($1))", # mZe16ToI
+ "(($3)(NU64)(NU16)($1))", # mZe16ToI64
+ "(($3)(NU64)(NU32)($1))", # mZe32ToI64
+ "(($3)(NU64)(NU)($1))", # mZeIToI64
+ "(($3)(NU8)(NU)($1))", # ToU8
+ "(($3)(NU16)(NU)($1))", # ToU16
+ "(($3)(NU32)(NU64)($1))", # ToU32
+ "((double) ($1))", # ToFloat
+ "((double) ($1))", # ToBiggestFloat
+ "float64ToInt32($1)", # ToInt
+ "float64ToInt64($1)"] # ToBiggestInt
var
a: TLoc
t: PType
- assert(e[1].typ != nil)
- initLocExpr(p, e[1], a)
+ assert(e.sons[1].typ != nil)
+ initLocExpr(p, e.sons[1], a)
t = skipTypes(e.typ, abstractRange)
-
- template applyFormat(frmt: untyped) =
- putIntoDest(p, d, e, frmt % [rdLoc(a), rope(getSize(p.config, t) * 8),
+ putIntoDest(p, d, e.typ,
+ unArithTab[op] % [rdLoc(a), rope(getSize(t) * 8),
getSimpleTypeDesc(p.module, e.typ)])
- case op
- of mNot:
- applyFormat("!($1)")
- of mUnaryPlusI:
- applyFormat("$1")
- of mBitnotI:
- applyFormat("($3)((NU$2) ~($1))")
- of mUnaryPlusF64:
- applyFormat("$1")
- of mUnaryMinusF64:
- applyFormat("-($1)")
- else:
- assert false, $op
proc isCppRef(p: BProc; typ: PType): bool {.inline.} =
result = p.module.compileToCpp and
- skipTypes(typ, abstractInstOwned).kind in {tyVar} and
- tfVarIsPtr notin skipTypes(typ, abstractInstOwned).flags
+ skipTypes(typ, abstractInst).kind == tyVar and
+ tfVarIsPtr notin skipTypes(typ, abstractInst).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:
+proc genDeref(p: BProc, e: PNode, d: var TLoc; enforceDeref=false) =
+ let mt = mapType(e.sons[0].typ)
+ if mt in {ctArray, ctPtrToArray} and not enforceDeref:
# XXX the amount of hacks for C's arrays is incredible, maybe we should
# simply wrap them in a struct? --> Losing auto vectorization then?
- expr(p, e[0], d)
- if e[0].typ.skipTypes(abstractInstOwned).kind == tyRef:
- d.storage = OnHeap
+ #if e[0].kind != nkBracketExpr:
+ # message(e.info, warnUser, "CAME HERE " & renderTree(e))
+ expr(p, e.sons[0], d)
else:
var a: TLoc
- var typ = e[0].typ
- if typ.kind in {tyUserTypeClass, tyUserTypeClassInst} and typ.isResolvedUserTypeClass:
- typ = typ.lastSon
- typ = typ.skipTypes(abstractInstOwned)
- if typ.kind in {tyVar} and tfVarIsPtr notin typ.flags and p.module.compileToCpp and e[0].kind == nkHiddenAddr:
+ let typ = skipTypes(e.sons[0].typ, abstractInst)
+ if typ.kind == tyVar and tfVarIsPtr notin typ.flags and p.module.compileToCpp and e.sons[0].kind == nkHiddenAddr:
initLocExprSingleUse(p, e[0][0], d)
return
else:
- initLocExprSingleUse(p, e[0], a)
+ initLocExprSingleUse(p, e.sons[0], a)
if d.k == locNone:
# dest = *a; <-- We do not know that 'dest' is on the heap!
- # It is completely wrong to set 'd.storage' here, unless it's not yet
+ # It is completely wrong to set 'd.s' here, unless it's not yet
# been assigned to.
case typ.kind
of tyRef:
- d.storage = OnHeap
- of tyVar, tyLent:
- d.storage = OnUnknown
+ d.s = OnHeap
+ of tyVar:
+ d.s = OnUnknown
if tfVarIsPtr notin typ.flags and p.module.compileToCpp and
e.kind == nkHiddenDeref:
- putIntoDest(p, d, e, rdLoc(a), a.storage)
+ putIntoDest(p, d, e.typ, rdLoc(a), a.s)
return
of tyPtr:
- d.storage = OnUnknown # BUGFIX!
+ d.s = OnUnknown # BUGFIX!
else:
- internalError(p.config, e.info, "genDeref " & $typ.kind)
+ internalError(e.info, "genDeref " & $typ.kind)
elif p.module.compileToCpp:
- if typ.kind in {tyVar} and tfVarIsPtr notin typ.flags and
+ if typ.kind == tyVar and tfVarIsPtr notin typ.flags and
e.kind == nkHiddenDeref:
- putIntoDest(p, d, e, rdLoc(a), a.storage)
+ putIntoDest(p, d, e.typ, rdLoc(a), a.s)
return
- if mt == ctPtrToArray and lfEnforceDeref in d.flags:
+ if enforceDeref and mt == ctPtrToArray:
# we lie about the type for better C interop: 'ptr array[3,T]' is
# translated to 'ptr T', but for deref'ing this produces wrong code.
# See tmissingderef. So we get rid of the deref instead. The codegen
# ends up using 'memcpy' for the array assignment,
# so the '&' and '*' cancel out:
- putIntoDest(p, d, e, rdLoc(a), a.storage)
+ putIntoDest(p, d, a.t.sons[0], rdLoc(a), a.s)
else:
- putIntoDest(p, d, e, "(*$1)" % [rdLoc(a)], a.storage)
-
-proc cowBracket(p: BProc; n: PNode) =
- if n.kind == nkBracketExpr and optSeqDestructors in p.config.globalOptions:
- let strCandidate = n[0]
- if strCandidate.typ.skipTypes(abstractInst).kind == tyString:
- var a: TLoc
- initLocExpr(p, strCandidate, a)
- linefmt(p, cpsStmts, "#nimPrepareStrMutationV2($1);$n", [byRefLoc(p, a)])
-
-proc cow(p: BProc; n: PNode) {.inline.} =
- if n.kind == nkHiddenAddr: cowBracket(p, n[0])
+ putIntoDest(p, d, e.typ, "(*$1)" % [rdLoc(a)], a.s)
proc genAddr(p: BProc, e: PNode, d: var TLoc) =
# careful 'addr(myptrToArray)' needs to get the ampersand:
- if e[0].typ.skipTypes(abstractInstOwned).kind in {tyRef, tyPtr}:
+ if e.sons[0].typ.skipTypes(abstractInst).kind in {tyRef, tyPtr}:
var a: TLoc
- initLocExpr(p, e[0], a)
- putIntoDest(p, d, e, "&" & a.r, a.storage)
+ initLocExpr(p, e.sons[0], a)
+ putIntoDest(p, d, e.typ, "&" & a.r, a.s)
#Message(e.info, warnUser, "HERE NEW &")
- elif mapType(p.config, e[0].typ, mapTypeChooser(e[0])) == ctArray or isCppRef(p, e.typ):
- expr(p, e[0], d)
+ elif mapType(e.sons[0].typ) == ctArray or isCppRef(p, e.sons[0].typ):
+ expr(p, e.sons[0], d)
else:
var a: TLoc
- initLocExpr(p, e[0], a)
- putIntoDest(p, d, e, addrLoc(p.config, a), a.storage)
+ initLocExpr(p, e.sons[0], a)
+ putIntoDest(p, d, e.typ, addrLoc(a), a.s)
template inheritLocation(d: var TLoc, a: TLoc) =
- if d.k == locNone: d.storage = a.storage
+ if d.k == locNone: d.s = a.s
+ if d.heapRoot == nil:
+ d.heapRoot = if a.heapRoot != nil: a.heapRoot else: a.r
-proc genRecordFieldAux(p: BProc, e: PNode, d, a: var TLoc) =
- initLocExpr(p, e[0], a)
- if e[1].kind != nkSym: internalError(p.config, e.info, "genRecordFieldAux")
+proc genRecordFieldAux(p: BProc, e: PNode, d, a: var TLoc): PType =
+ initLocExpr(p, e.sons[0], a)
+ if e.sons[1].kind != nkSym: internalError(e.info, "genRecordFieldAux")
d.inheritLocation(a)
discard getTypeDesc(p.module, a.t) # fill the record's fields.loc
+ result = a.t.getUniqueType
proc genTupleElem(p: BProc, e: PNode, d: var TLoc) =
var
a: TLoc
i: int
- initLocExpr(p, e[0], a)
- let tupType = a.t.skipTypes(abstractInst+{tyVar})
- assert tupType.kind == tyTuple
+ initLocExpr(p, e.sons[0], a)
d.inheritLocation(a)
discard getTypeDesc(p.module, a.t) # fill the record's fields.loc
+ var ty = a.t.getUniqueType
var r = rdLoc(a)
- case e[1].kind
- of nkIntLit..nkUInt64Lit: i = int(e[1].intVal)
- else: internalError(p.config, e.info, "genTupleElem")
- r.addf(".Field$1", [rope(i)])
- putIntoDest(p, d, e, r, a.storage)
+ case e.sons[1].kind
+ of nkIntLit..nkUInt64Lit: i = int(e.sons[1].intVal)
+ else: internalError(e.info, "genTupleElem")
+ addf(r, ".Field$1", [rope(i)])
+ putIntoDest(p, d, ty.sons[i], r, a.s)
-proc lookupFieldAgain(p: BProc, ty: PType; field: PSym; r: var Rope;
- resTyp: ptr PType = nil): PSym =
+proc lookupFieldAgain(p: BProc, ty: PType; field: PSym; r: var Rope): PSym =
var ty = ty
- assert r != ""
+ assert r != nil
while ty != nil:
- ty = ty.skipTypes(skipPtrs)
assert(ty.kind in {tyTuple, tyObject})
result = lookupInRecord(ty.n, field.name)
- if result != nil:
- if resTyp != nil: resTyp[] = ty
- break
- if not p.module.compileToCpp: r.add(".Sup")
- ty = ty[0]
- if result == nil: internalError(p.config, field.info, "genCheckedRecordField")
+ if result != nil: break
+ if not p.module.compileToCpp: add(r, ".Sup")
+ ty = getUniqueType(ty.sons[0])
+ if result == nil: internalError(field.info, "genCheckedRecordField")
proc genRecordField(p: BProc, e: PNode, d: var TLoc) =
var a: TLoc
- genRecordFieldAux(p, e, d, a)
+ var ty = genRecordFieldAux(p, e, d, a)
var r = rdLoc(a)
- var f = e[1].sym
- let ty = skipTypes(a.t, abstractInstOwned + tyUserTypeClasses)
+ var f = e.sons[1].sym
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)
- putIntoDest(p, d, e, r, a.storage)
+ addf(r, ".Field$1", [rope(f.position)])
+ putIntoDest(p, d, f.typ, r, a.s)
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
- putIntoDest(p, d, e, r, a.storage)
- r.freeze
+ let field = lookupFieldAgain(p, ty, f, r)
+ if field.loc.r == nil: internalError(e.info, "genRecordField 3")
+ addf(r, ".$1", [field.loc.r])
+ putIntoDest(p, d, field.typ, r, a.s)
+ #d.s = a.s
proc genInExprAux(p: BProc, e: PNode, a, b, d: var TLoc)
-proc genFieldCheck(p: BProc, e: PNode, obj: Rope, field: PSym) =
+proc genFieldCheck(p: BProc, e: PNode, obj: Rope, field: PSym;
+ origTy: PType) =
var test, u, v: TLoc
- for i in 1..= 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", []
+ if firstOrd(ty) == 0:
+ if (firstOrd(b.t) < firstOrd(ty)) or (lastOrd(b.t) > lastOrd(ty)):
+ linefmt(p, cpsStmts, "if ((NU)($1) > (NU)($2)) #raiseIndexError();$n",
+ rdCharLoc(b), intLiteral(lastOrd(ty)))
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) #raiseIndexError();$n",
+ rdCharLoc(b), first, intLiteral(lastOrd(ty)))
else:
let idx = getOrdValue(y)
- if idx < firstOrd(p.config, ty) or idx > lastOrd(p.config, ty):
- localError(p.config, x.info, formatErrorIndexBound(idx, firstOrd(p.config, ty), lastOrd(p.config, ty)))
+ if idx < firstOrd(ty) or idx > lastOrd(ty):
+ localError(x.info, errIndexOutOfBounds)
d.inheritLocation(a)
- putIntoDest(p, d, n,
- ropecg(p.module, "$1[($2)- $3]", [rdLoc(a), rdCharLoc(b), first]), a.storage)
+ putIntoDest(p, d, elemType(skipTypes(ty, abstractVar)),
+ rfmt(nil, "$1[($2)- $3]", rdLoc(a), rdCharLoc(b), first), a.s)
-proc genCStringElem(p: BProc, n, x, y: PNode, d: var TLoc) =
+proc genCStringElem(p: BProc, x, y: PNode, d: var TLoc) =
var a, b: TLoc
initLocExpr(p, x, a)
initLocExpr(p, y, b)
- inheritLocation(d, a)
- putIntoDest(p, d, n,
- ropecg(p.module, "$1[$2]", [rdLoc(a), rdCharLoc(b)]), a.storage)
+ var ty = skipTypes(a.t, abstractVarRange)
+ if d.k == locNone: d.s = a.s
+ putIntoDest(p, d, elemType(skipTypes(ty, abstractVar)),
+ rfmt(nil, "$1[$2]", rdLoc(a), rdCharLoc(b)), a.s)
-proc genBoundsCheck(p: BProc; arr, a, b: TLoc) =
- let ty = skipTypes(arr.t, abstractVarRange)
- case ty.kind
- of tyOpenArray, tyVarargs:
- 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)])
- 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", []
-
- of tyArray:
- var first = newRopeAppender()
- intLiteral(firstOrd(p.config, ty), first)
- var last = newRopeAppender()
- intLiteral(lastOrd(p.config, ty), last)
- linefmt(p, cpsStmts,
- "if ($2-$1 != -1 && " &
- "($2-$1 < -1 || $1 < $3 || $1 > $4 || $2 < $3 || $2 > $4)){ #raiseIndexError(); ",
- [rdCharLoc(a), rdCharLoc(b), first, last])
-
- raiseInstr(p, p.s(cpsStmts))
- linefmt p, cpsStmts, "}$n", []
-
- of tySequence, tyString:
- linefmt(p, cpsStmts,
- "if ($2-$1 != -1 && " &
- "($1 < 0 || $1 >= $3 || $2 < 0 || $2 >= $3)){ #raiseIndexError4($1, $2, $3); ",
- [rdLoc(a), rdLoc(b), lenExpr(p, arr)])
- raiseInstr(p, p.s(cpsStmts))
- linefmt p, cpsStmts, "}$n", []
-
- else: discard
-
-proc genOpenArrayElem(p: BProc, n, x, y: PNode, d: var TLoc) =
+proc genOpenArrayElem(p: BProc, x, y: PNode, d: var TLoc) =
var a, b: TLoc
initLocExpr(p, x, a)
- initLocExpr(p, y, b)
- 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", []
+ initLocExpr(p, y, b) # emit range check:
+ if optBoundsCheck in p.options:
+ linefmt(p, cpsStmts, "if ((NU)($1) >= (NU)($2Len0)) #raiseIndexError();$n",
+ rdLoc(b), rdLoc(a)) # BUGFIX: ``>=`` and not ``>``!
+ if d.k == locNone: d.s = a.s
+ putIntoDest(p, d, elemType(skipTypes(a.t, abstractVar)),
+ rfmt(nil, "$1[$2]", rdLoc(a), rdCharLoc(b)), a.s)
- 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", []
-
- inheritLocation(d, a)
- putIntoDest(p, d, n,
- ropecg(p.module, "$1.Field0[$2]", [rdLoc(a), rdCharLoc(b)]), a.storage)
-
-proc genSeqElem(p: BProc, n, x, y: PNode, d: var TLoc) =
+proc genSeqElem(p: BProc, x, y: PNode, d: var TLoc) =
var a, b: TLoc
initLocExpr(p, x, a)
initLocExpr(p, y, b)
@@ -1114,51 +882,31 @@ 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 d.k == locNone: d.storage = OnHeap
+ if ty.kind == tyString:
+ linefmt(p, cpsStmts,
+ "if ((NU)($1) > (NU)($2->$3)) #raiseIndexError();$n",
+ rdLoc(b), rdLoc(a), lenField(p))
+ else:
+ linefmt(p, cpsStmts,
+ "if ((NU)($1) >= (NU)($2->$3)) #raiseIndexError();$n",
+ rdLoc(b), rdLoc(a), lenField(p))
+ if d.k == locNone: d.s = OnHeap
+ d.heapRoot = a.r
if skipTypes(a.t, abstractVar).kind in {tyRef, tyPtr}:
- a.r = ropecg(p.module, "(*$1)", [a.r])
-
- if lfPrepareForMutation in d.flags and ty.kind == tyString and
- optSeqDestructors in p.config.globalOptions:
- linefmt(p, cpsStmts, "#nimPrepareStrMutationV2($1);$n", [byRefLoc(p, a)])
- putIntoDest(p, d, n,
- ropecg(p.module, "$1$3[$2]", [rdLoc(a), rdCharLoc(b), dataField(p)]), a.storage)
+ a.r = rfmt(nil, "(*$1)", a.r)
+ putIntoDest(p, d, elemType(skipTypes(a.t, abstractVar)),
+ rfmt(nil, "$1->data[$2]", rdLoc(a), rdCharLoc(b)), a.s)
proc genBracketExpr(p: BProc; n: PNode; d: var TLoc) =
- var ty = skipTypes(n[0].typ, abstractVarRange + tyUserTypeClasses)
+ var ty = skipTypes(n.sons[0].typ, abstractVarRange)
if ty.kind in {tyRef, tyPtr}: ty = skipTypes(ty.lastSon, abstractVarRange)
case ty.kind
- of tyUncheckedArray: genUncheckedArrayElem(p, n, n[0], n[1], d)
- 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 tyArray, tyArrayConstr: genArrayElem(p, n.sons[0], n.sons[1], d)
+ of tyOpenArray, tyVarargs: genOpenArrayElem(p, n.sons[0], n.sons[1], d)
+ of tySequence, tyString: genSeqElem(p, n.sons[0], n.sons[1], d)
+ of tyCString: genCStringElem(p, n.sons[0], n.sons[1], d)
of tyTuple: genTupleElem(p, n, d)
- else: internalError(p.config, n.info, "expr(nkBracketExpr, " & $ty.kind & ')')
- discard getTypeDesc(p.module, n.typ)
-
-proc isSimpleExpr(n: PNode): bool =
- # calls all the way down --> can stay expression based
- case n.kind
- of nkCallKinds, nkDotExpr, nkPar, nkTupleConstr,
- nkObjConstr, nkBracket, nkCurly, nkHiddenDeref, nkDerefExpr, nkHiddenAddr,
- nkHiddenStdConv, nkHiddenSubConv, nkConv, nkAddr:
- for c in n:
- if not isSimpleExpr(c): return false
- result = true
- of nkStmtListExpr:
- for i in 0.. 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]))
- p.module.includeHeader("")
- p.module.includeHeader("")
- linefmt(p, cpsStmts, """Genode::log($1);$n""", [args])
- else:
- if n.len == 0:
- linefmt(p, cpsStmts, "#echoBinSafe(NIM_NIL, $1);$n", [n.len])
+ internalAssert n.kind == nkBracket
+ discard lists.includeStr(p.module.headerFiles, "")
+ var args: Rope = nil
+ var a: TLoc
+ for i in countup(0, n.len-1):
+ if n.sons[i].skipConv.kind == nkNilLit:
+ add(args, ", \"nil\"")
else:
- var a: TLoc
- initLocExpr(p, n, a)
- linefmt(p, cpsStmts, "#echoBinSafe($1, $2);$n", [a.rdLoc, n.len])
- when false:
- p.module.includeHeader("")
- linefmt(p, cpsStmts, "printf($1$2);$n",
- makeCString(repeat("%s", n.len) & "\L"), [args])
- linefmt(p, cpsStmts, "fflush(stdout);$n", [])
+ initLocExpr(p, n.sons[i], a)
+ addf(args, ", $1? ($1)->data:\"nil\"", [rdLoc(a)])
+ linefmt(p, cpsStmts, "printf($1$2);$n",
+ makeCString(repeat("%s", n.len) & tnl), args)
+ linefmt(p, cpsStmts, "fflush(stdout);$n")
-proc gcUsage(conf: ConfigRef; n: PNode) =
- if conf.selectedGC == gcNone: message(conf, n.info, warnGcMem, n.renderTree)
-
-proc strLoc(p: BProc; d: TLoc): Rope =
- if optSeqDestructors in p.config.globalOptions:
- result = byRefLoc(p, d)
- else:
- result = rdLoc(d)
+proc gcUsage(n: PNode) =
+ if gSelectedGC == gcNone: message(n.info, warnGcMem, n.renderTree)
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,32 +988,32 @@ 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 = ""
- for i in 0..$2 + ", [rdLoc(a), lenField(p)])
+ add(appends, rfmt(p.module, "#appendString($1, $2);$n", tmp.r, rdLoc(a)))
+ linefmt(p, cpsStmts, "$1 = #rawNewString($2$3);$n", tmp.r, lens, rope(L))
+ add(p.s(cpsStmts), appends)
if d.k == locNone:
d = tmp
+ keepAlive(p, tmp)
else:
- genAssignment(p, d, tmp, {}) # no need for deep copying
- gcUsage(p.config, e)
+ genAssignment(p, d, tmp, {needToKeepAlive}) # no need for deep copying
+ gcUsage(e)
proc genStrAppend(p: BProc, e: PNode, d: var TLoc) =
#
- # 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?
#
# {
@@ -1318,991 +1024,612 @@ proc genStrAppend(p: BProc, e: PNode, d: var TLoc) =
# appendChar(s, 'z');
# }
var
- a, dest, call: TLoc
+ a, dest: TLoc
appends, lens: Rope
assert(d.k == locNone)
var L = 0
- initLocExpr(p, e[1], dest)
- for i in 0..$2 + ", [rdLoc(a), lenField(p)])
+ add(appends, rfmt(p.module, "#appendString($1, $2);$n",
+ rdLoc(dest), rdLoc(a)))
+ linefmt(p, cpsStmts, "$1 = #resizeString($1, $2$3);$n",
+ rdLoc(dest), lens, rope(L))
+ keepAlive(p, dest)
+ add(p.s(cpsStmts), appends)
+ gcUsage(e)
proc genSeqElemAppend(p: BProc, e: PNode, d: var TLoc) =
# seq &= x -->
# seq = (typeof seq) incrSeq(&seq->Sup, sizeof(x));
# seq->data[seq->len-1] = x;
- var a, b, dest, tmpL, call: TLoc
- initLocExpr(p, e[1], a)
- initLocExpr(p, e[2], b)
- let seqType = skipTypes(e[1].typ, {tyVar})
- initLoc(call, locCall, e, OnHeap)
- if not p.module.compileToCpp:
- const seqAppendPattern = "($2) #incrSeqV3((TGenericSeq*)($1), $3)"
- call.r = ropecg(p.module, seqAppendPattern, [rdLoc(a),
- getTypeDesc(p.module, e[1].typ),
- genTypeInfoV1(p.module, seqType, e.info)])
- else:
- const seqAppendPattern = "($2) #incrSeqV3($1, $3)"
- call.r = ropecg(p.module, seqAppendPattern, [rdLoc(a),
- getTypeDesc(p.module, e[1].typ),
- genTypeInfoV1(p.module, seqType, e.info)])
- # emit the write barrier if required, but we can always move here, so
- # use 'genRefAssign' for the seq.
- genRefAssign(p, a, call)
+ let seqAppendPattern = if not p.module.compileToCpp:
+ "$1 = ($2) #incrSeqV2(&($1)->Sup, sizeof($3));$n"
+ else:
+ "$1 = ($2) #incrSeqV2($1, sizeof($3));$n"
+ var a, b, dest: TLoc
+ initLocExpr(p, e.sons[1], a)
+ initLocExpr(p, e.sons[2], b)
+ let bt = skipTypes(e.sons[2].typ, abstractVar)
+ lineCg(p, cpsStmts, seqAppendPattern, [
+ rdLoc(a),
+ getTypeDesc(p.module, skipTypes(e.sons[1].typ, abstractVar)),
+ getTypeDesc(p.module, bt)])
+ keepAlive(p, a)
#if bt != b.t:
# echo "YES ", e.info, " new: ", typeToString(bt), " old: ", typeToString(b.t)
- initLoc(dest, locExpr, e[2], OnHeap)
- getIntTemp(p, tmpL)
- lineCg(p, cpsStmts, "$1 = $2->$3++;$n", [tmpL.r, rdLoc(a), lenField(p)])
- dest.r = ropecg(p.module, "$1$3[$2]", [rdLoc(a), tmpL.r, dataField(p)])
- genAssignment(p, dest, b, {needToCopy})
- gcUsage(p.config, e)
+ initLoc(dest, locExpr, bt, OnHeap)
+ dest.r = rfmt(nil, "$1->data[$1->$2]", rdLoc(a), lenField(p))
+ genAssignment(p, dest, b, {needToCopy, afDestIsNil})
+ lineCg(p, cpsStmts, "++$1->$2;$n", rdLoc(a), lenField(p))
+ gcUsage(e)
proc genReset(p: BProc, n: PNode) =
var a: TLoc
- initLocExpr(p, n[1], a)
- specializeReset(p, a)
- when false:
- linefmt(p, cpsStmts, "#genericReset((void*)$1, $2);$n",
- [addrLoc(p.config, a),
- genTypeInfoV1(p.module, skipTypes(a.t, {tyVar}), n.info)])
+ initLocExpr(p, n.sons[1], a)
+ linefmt(p, cpsStmts, "#genericReset((void*)$1, $2);$n",
+ addrLoc(a), genTypeInfo(p.module, skipTypes(a.t, abstractVarRange)))
-proc genDefault(p: BProc; n: PNode; d: var TLoc) =
- if d.k == locNone: getTemp(p, n.typ, d, needsInit=true)
- else: resetLoc(p, d)
-
-proc rawGenNew(p: BProc, a: var TLoc, sizeExpr: Rope; needsInit: bool) =
+proc rawGenNew(p: BProc, a: TLoc, sizeExpr: Rope) =
var sizeExpr = sizeExpr
- let typ = a.t
+ let refType = skipTypes(a.t, abstractVarRange)
var b: TLoc
- initLoc(b, locExpr, a.lode, OnHeap)
- let refType = typ.skipTypes(abstractInstOwned)
- assert refType.kind == tyRef
- let bt = refType.lastSon
- if sizeExpr == "":
- sizeExpr = "sizeof($1)" % [getTypeDesc(p.module, bt)]
-
- if optTinyRtti in p.config.globalOptions:
- if needsInit:
- b.r = ropecg(p.module, "($1) #nimNewObj($2, NIM_ALIGNOF($3))",
- [getTypeDesc(p.module, typ), sizeExpr, getTypeDesc(p.module, bt)])
+ initLoc(b, locExpr, a.t, OnHeap)
+ if sizeExpr.isNil:
+ sizeExpr = "sizeof($1)" %
+ [getTypeDesc(p.module, skipTypes(refType.sons[0], abstractRange))]
+ let args = [getTypeDesc(p.module, refType),
+ genTypeInfo(p.module, refType),
+ sizeExpr]
+ if a.s == OnHeap and usesNativeGC():
+ # use newObjRC1 as an optimization; and we don't need 'keepAlive' either
+ if canFormAcycle(a.t):
+ linefmt(p, cpsStmts, "if ($1) #nimGCunref($1);$n", a.rdLoc)
else:
- b.r = ropecg(p.module, "($1) #nimNewObjUninit($2, NIM_ALIGNOF($3))",
- [getTypeDesc(p.module, typ), sizeExpr, getTypeDesc(p.module, bt)])
- genAssignment(p, a, b, {})
+ linefmt(p, cpsStmts, "if ($1) #nimGCunrefNoCycle($1);$n", a.rdLoc)
+ b.r = ropecg(p.module, "($1) #newObjRC1($2, $3)", args)
+ linefmt(p, cpsStmts, "$1 = $2;$n", a.rdLoc, b.rdLoc)
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):
- # 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:
- 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)
- p.module.s[cfsTypeInit3].addf("$1->finalizer = (void*)$2;$n", [ti, rdLoc(f)])
-
- if a.storage == OnHeap and usesWriteBarrier(p.config):
- if canFormAcycle(a.t):
- linefmt(p, cpsStmts, "if ($1) { #nimGCunrefRC1($1); $1 = NIM_NIL; }$n", [a.rdLoc])
- else:
- linefmt(p, cpsStmts, "if ($1) { #nimGCunrefNoCycle($1); $1 = NIM_NIL; }$n", [a.rdLoc])
- if p.config.selectedGC == gcGo:
- # newObjRC1() would clash with unsureAsgnRef() - which is used by gcGo to
- # implement the write barrier
- b.r = ropecg(p.module, "($1) #newObj($2, $3)", [getTypeDesc(p.module, typ), ti, sizeExpr])
- linefmt(p, cpsStmts, "#unsureAsgnRef((void**) $1, $2);$n",
- [addrLoc(p.config, a), b.rdLoc])
- else:
- # use newObjRC1 as an optimization
- b.r = ropecg(p.module, "($1) #newObjRC1($2, $3)", [getTypeDesc(p.module, typ), ti, sizeExpr])
- linefmt(p, cpsStmts, "$1 = $2;$n", [a.rdLoc, b.rdLoc])
- else:
- b.r = ropecg(p.module, "($1) #newObj($2, $3)", [getTypeDesc(p.module, typ), ti, sizeExpr])
- genAssignment(p, a, b, {})
- # set the object type:
- genObjectInit(p, cpsStmts, bt, a, constructRefObj)
+ b.r = ropecg(p.module, "($1) #newObj($2, $3)", args)
+ genAssignment(p, a, b, {needToKeepAlive}) # set the object type:
+ let bt = skipTypes(refType.sons[0], abstractRange)
+ genObjectInit(p, cpsStmts, bt, a, false)
proc genNew(p: BProc, e: PNode) =
var a: TLoc
- initLocExpr(p, e[1], a)
+ initLocExpr(p, e.sons[1], a)
# 'genNew' also handles 'unsafeNew':
if e.len == 3:
var se: TLoc
- initLocExpr(p, e[2], se)
- rawGenNew(p, a, se.rdLoc, needsInit = true)
+ initLocExpr(p, e.sons[2], se)
+ rawGenNew(p, a, se.rdLoc)
else:
- rawGenNew(p, a, "", needsInit = true)
- gcUsage(p.config, e)
+ rawGenNew(p, a, nil)
+ gcUsage(e)
-proc genNewSeqAux(p: BProc, dest: TLoc, length: Rope; lenIsZero: bool) =
+proc genNewSeqAux(p: BProc, dest: TLoc, length: Rope) =
let seqtype = skipTypes(dest.t, abstractVarRange)
+ let args = [getTypeDesc(p.module, seqtype),
+ genTypeInfo(p.module, seqtype), length]
var call: TLoc
- initLoc(call, locExpr, dest.lode, OnHeap)
- if dest.storage == OnHeap and usesWriteBarrier(p.config):
+ initLoc(call, locExpr, dest.t, OnHeap)
+ if dest.s == OnHeap and usesNativeGC():
if canFormAcycle(dest.t):
- linefmt(p, cpsStmts, "if ($1) { #nimGCunrefRC1($1); $1 = NIM_NIL; }$n", [dest.rdLoc])
+ linefmt(p, cpsStmts, "if ($1) #nimGCunref($1);$n", dest.rdLoc)
else:
- linefmt(p, cpsStmts, "if ($1) { #nimGCunrefNoCycle($1); $1 = NIM_NIL; }$n", [dest.rdLoc])
- if not lenIsZero:
- if p.config.selectedGC == gcGo:
- # we need the write barrier
- call.r = ropecg(p.module, "($1) #newSeq($2, $3)", [getTypeDesc(p.module, seqtype),
- genTypeInfoV1(p.module, seqtype, dest.lode.info), length])
- linefmt(p, cpsStmts, "#unsureAsgnRef((void**) $1, $2);$n", [addrLoc(p.config, dest), call.rdLoc])
- else:
- call.r = ropecg(p.module, "($1) #newSeqRC1($2, $3)", [getTypeDesc(p.module, seqtype),
- genTypeInfoV1(p.module, seqtype, dest.lode.info), length])
- linefmt(p, cpsStmts, "$1 = $2;$n", [dest.rdLoc, call.rdLoc])
+ linefmt(p, cpsStmts, "if ($1) #nimGCunrefNoCycle($1);$n", dest.rdLoc)
+ call.r = ropecg(p.module, "($1) #newSeqRC1($2, $3)", args)
+ linefmt(p, cpsStmts, "$1 = $2;$n", dest.rdLoc, call.rdLoc)
else:
- if lenIsZero:
- call.r = rope"NIM_NIL"
- else:
- call.r = ropecg(p.module, "($1) #newSeq($2, $3)", [getTypeDesc(p.module, seqtype),
- genTypeInfoV1(p.module, seqtype, dest.lode.info), length])
- genAssignment(p, dest, call, {})
+ call.r = ropecg(p.module, "($1) #newSeq($2, $3)", args)
+ genAssignment(p, dest, call, {needToKeepAlive})
proc genNewSeq(p: BProc, e: PNode) =
var a, b: TLoc
- initLocExpr(p, e[1], a)
- initLocExpr(p, e[2], b)
- if optSeqDestructors in p.config.globalOptions:
- let seqtype = skipTypes(e[1].typ, abstractVarRange)
- linefmt(p, cpsStmts, "$1.len = $2; $1.p = ($4*) #newSeqPayload($2, sizeof($3), NIM_ALIGNOF($3));$n",
- [a.rdLoc, b.rdLoc,
- getTypeDesc(p.module, seqtype.lastSon),
- getSeqPayloadType(p.module, seqtype)])
- else:
- let lenIsZero = e[2].kind == nkIntLit and e[2].intVal == 0
- genNewSeqAux(p, a, b.rdLoc, lenIsZero)
- gcUsage(p.config, e)
-
-proc genNewSeqOfCap(p: BProc; e: PNode; d: var TLoc) =
- let seqtype = skipTypes(e.typ, abstractVarRange)
- var a: TLoc
- initLocExpr(p, e[1], a)
- if optSeqDestructors in p.config.globalOptions:
- if d.k == locNone: getTemp(p, e.typ, d, needsInit=false)
- linefmt(p, cpsStmts, "$1.len = 0; $1.p = ($4*) #newSeqPayload($2, sizeof($3), NIM_ALIGNOF($3));$n",
- [d.rdLoc, a.rdLoc, getTypeDesc(p.module, seqtype.lastSon),
- getSeqPayloadType(p.module, seqtype),
- ])
- else:
- putIntoDest(p, d, e, ropecg(p.module,
- "($1)#nimNewSeqOfCap($2, $3)", [
- getTypeDesc(p.module, seqtype),
- genTypeInfoV1(p.module, seqtype, e.info), a.rdLoc]))
- gcUsage(p.config, e)
-
-proc rawConstExpr(p: BProc, n: PNode; d: var TLoc) =
- let t = n.typ
- discard getTypeDesc(p.module, t) # so that any fields are initialized
- let id = nodeTableTestOrSet(p.module.dataCache, n, p.module.labels)
- fillLoc(d, locData, n, p.module.tmpBase & rope(id), OnStatic)
- 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", [])
-
-proc handleConstExpr(p: BProc, n: PNode, d: var TLoc): bool =
- if d.k == locNone and n.len > ord(n.kind == nkObjConstr) and n.isDeepConstExpr:
- rawConstExpr(p, n, d)
- result = true
- else:
- result = false
+ initLocExpr(p, e.sons[1], a)
+ initLocExpr(p, e.sons[2], b)
+ genNewSeqAux(p, a, b.rdLoc)
+ gcUsage(e)
proc genObjConstr(p: BProc, e: PNode, d: var TLoc) =
- #echo renderTree e, " ", e.isDeepConstExpr
- # inheritance in C++ does not allow struct initialization so
- # we skip this step here:
- if not p.module.compileToCpp and optSeqDestructors notin p.config.globalOptions:
- # disabled optimization: it is wrong for C++ and now also
- # causes trouble for --gc:arc, see bug #13240
- #[
- var box: seq[Thing]
- for i in 0..3:
- box.add Thing(s1: "121") # pass by sink can mutate Thing.
- ]#
- if handleConstExpr(p, e, d): return
- var t = e.typ.skipTypes(abstractInstOwned)
- let isRef = t.kind == tyRef
-
- # check if we need to construct the object in a temporary
- var useTemp =
- isRef or
- (d.k notin {locTemp,locLocalVar,locGlobalVar,locParam,locField}) or
- (isPartOf(d.lode, e) != arNo)
-
var tmp: TLoc
- var r: Rope
- if useTemp:
- getTemp(p, t, tmp)
- r = rdLoc(tmp)
- if isRef:
- rawGenNew(p, tmp, "", needsInit = nfAllFieldsSet notin e.flags)
- t = t.lastSon.skipTypes(abstractInstOwned)
- r = "(*$1)" % [r]
- gcUsage(p.config, e)
- else:
- constructLoc(p, tmp)
+ var t = e.typ.skipTypes(abstractInst)
+ getTemp(p, t, tmp)
+ let isRef = t.kind == tyRef
+ var r = rdLoc(tmp)
+ if isRef:
+ rawGenNew(p, tmp, nil)
+ t = t.lastSon.skipTypes(abstractInst)
+ r = "(*$1)" % [r]
+ gcUsage(e)
else:
- resetLoc(p, d)
- r = rdLoc(d)
+ constructLoc(p, tmp)
discard getTypeDesc(p.module, t)
let ty = getUniqueType(t)
- for i in 1..data[$2]", rdLoc(d), intLiteral(i))
+ arr.s = OnHeap # we know that sequences are on the heap
+ expr(p, t.sons[i], arr)
+ gcUsage(t)
+
+proc genArrToSeq(p: BProc, t: PNode, d: var TLoc) =
var elem, a, arr: TLoc
- if n[1].kind == nkBracket:
- n[1].typ = n.typ
- genSeqConstr(p, n[1], d)
+ if t.kind == nkBracket:
+ t.sons[1].typ = t.typ
+ genSeqConstr(p, t.sons[1], d)
return
if d.k == locNone:
- getTemp(p, n.typ, d)
- initLocExpr(p, n[1], a)
+ getTemp(p, t.typ, d)
# generate call to newSeq before adding the elements per hand:
- let L = toInt(lengthOrd(p.config, n[1].typ))
- 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 d, L, getTypeDesc(p.module, seqtype.lastSon),
- getSeqPayloadType(p.module, seqtype)])
- else:
- var lit = newRopeAppender()
- intLiteral(L, lit)
- genNewSeqAux(p, d, lit, L == 0)
- # bug #5007; do not produce excessive C source code:
- if L < 10:
- for i in 0..data[$2]", rdLoc(d), intLiteral(i))
+ elem.s = OnHeap # we know that sequences are on the heap
+ initLoc(arr, locExpr, elemType(skipTypes(t.sons[1].typ, abstractInst)), a.s)
+ arr.r = rfmt(nil, "$1[$2]", rdLoc(a), intLiteral(i))
+ genAssignment(p, elem, arr, {afDestIsNil, needToCopy})
proc genNewFinalize(p: BProc, e: PNode) =
var
a, b, f: TLoc
refType, bt: PType
ti: Rope
- refType = skipTypes(e[1].typ, abstractVarRange)
- initLocExpr(p, e[1], a)
- initLocExpr(p, e[2], f)
- initLoc(b, locExpr, a.lode, OnHeap)
- ti = genTypeInfo(p.config, p.module, refType, e.info)
- p.module.s[cfsTypeInit3].addf("$1->finalizer = (void*)$2;$n", [ti, rdLoc(f)])
+ oldModule: BModule
+ refType = skipTypes(e.sons[1].typ, abstractVarRange)
+ initLocExpr(p, e.sons[1], a)
+ initLocExpr(p, e.sons[2], f)
+ initLoc(b, locExpr, a.t, OnHeap)
+ ti = genTypeInfo(p.module, refType)
+ addf(p.module.s[cfsTypeInit3], "$1->finalizer = (void*)$2;$n", [ti, rdLoc(f)])
b.r = ropecg(p.module, "($1) #newObj($2, sizeof($3))", [
getTypeDesc(p.module, refType),
ti, getTypeDesc(p.module, skipTypes(refType.lastSon, abstractRange))])
- genAssignment(p, a, b, {}) # set the object type:
+ genAssignment(p, a, b, {needToKeepAlive}) # set the object type:
bt = skipTypes(refType.lastSon, abstractRange)
- genObjectInit(p, cpsStmts, bt, a, constructRefObj)
- gcUsage(p.config, e)
+ genObjectInit(p, cpsStmts, bt, a, false)
+ gcUsage(e)
-proc genOfHelper(p: BProc; dest: PType; a: Rope; info: TLineInfo; result: var 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])
+proc genOfHelper(p: BProc; dest: PType; a: Rope): Rope =
+ # unfortunately 'genTypeInfo' sets tfObjHasKids as a side effect, so we
+ # have to call it here first:
+ let ti = genTypeInfo(p.module, dest)
+ if tfFinal in dest.flags or (objHasKidsValid in p.module.flags and
+ tfObjHasKids notin dest.flags):
+ result = "$1.m_type == $2" % [a, ti]
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]
- else:
- 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])
+ discard cgsym(p.module, "TNimType")
+ inc p.module.labels
+ let cache = "Nim_OfCheck_CACHE" & p.module.labels.rope
+ addf(p.module.s[cfsVars], "static TNimType* $#[2];$n", [cache])
+ result = rfmt(p.module, "#isObjWithCache($#.m_type, $#, $#)", a, ti, cache)
+ when false:
+ # former version:
+ result = rfmt(p.module, "#isObj($1.m_type, $2)",
+ a, genTypeInfo(p.module, dest))
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 t = skipTypes(a.t, abstractInstOwned)
- while t.kind in {tyVar, tyLent, tyPtr, tyRef}:
- if t.kind notin {tyVar, tyLent}: nilCheck = r
- if t.kind notin {tyVar, tyLent} or not p.module.compileToCpp:
- r = ropecg(p.module, "(*$1)", [r])
- t = skipTypes(t.lastSon, typedescInst+{tyOwned})
- discard getTypeDesc(p.module, t)
+ var nilCheck: Rope = nil
+ var t = skipTypes(a.t, abstractInst)
+ while t.kind in {tyVar, tyPtr, tyRef}:
+ if t.kind != tyVar: nilCheck = r
+ if t.kind != tyVar or not p.module.compileToCpp:
+ r = rfmt(nil, "(*$1)", r)
+ t = skipTypes(t.lastSon, typedescInst)
if not p.module.compileToCpp:
- while t.kind == tyObject and t[0] != nil:
- r.add(".Sup")
- t = skipTypes(t[0], skipPtrs)
+ while t.kind == tyObject and t.sons[0] != nil:
+ add(r, ~".Sup")
+ t = skipTypes(t.sons[0], typedescInst)
if isObjLackingTypeField(t):
- globalError(p.config, x.info,
+ globalError(x.info, errGenerated,
"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 = rfmt(p.module, "(($1) && ($2))", nilCheck, genOfHelper(p, dest, r))
else:
- ofExpr.add ro
- ofExpr.add ")"
-
- putIntoDest(p, d, x, ofExpr, a.storage)
+ r = rfmt(p.module, "($1)", genOfHelper(p, dest, r))
+ putIntoDest(p, d, getSysType(tyBool), r, a.s)
proc genOf(p: BProc, n: PNode, d: var TLoc) =
- genOf(p, n[1], n[2].typ, d)
+ genOf(p, n.sons[1], n.sons[2].typ, d)
proc genRepr(p: BProc, e: PNode, d: var TLoc) =
- if optTinyRtti in p.config.globalOptions:
- localError(p.config, e.info, "'repr' is not available for --newruntime")
var a: TLoc
- initLocExpr(p, e[1], a)
- var t = skipTypes(e[1].typ, abstractVarRange)
+ initLocExpr(p, e.sons[1], a)
+ var t = skipTypes(e.sons[1].typ, abstractVarRange)
case t.kind
of tyInt..tyInt64, tyUInt..tyUInt64:
- putIntoDest(p, d, e,
- ropecg(p.module, "#reprInt((NI64)$1)", [rdLoc(a)]), a.storage)
+ putIntoDest(p, d, e.typ,
+ ropecg(p.module, "#reprInt((NI64)$1)", [rdLoc(a)]), a.s)
of tyFloat..tyFloat128:
- putIntoDest(p, d, e, ropecg(p.module, "#reprFloat($1)", [rdLoc(a)]), a.storage)
+ putIntoDest(p, d, e.typ, ropecg(p.module, "#reprFloat($1)", [rdLoc(a)]), a.s)
of tyBool:
- putIntoDest(p, d, e, ropecg(p.module, "#reprBool($1)", [rdLoc(a)]), a.storage)
+ putIntoDest(p, d, e.typ, ropecg(p.module, "#reprBool($1)", [rdLoc(a)]), a.s)
of tyChar:
- putIntoDest(p, d, e, ropecg(p.module, "#reprChar($1)", [rdLoc(a)]), a.storage)
+ putIntoDest(p, d, e.typ, ropecg(p.module, "#reprChar($1)", [rdLoc(a)]), a.s)
of tyEnum, tyOrdinal:
- putIntoDest(p, d, e,
+ putIntoDest(p, d, e.typ,
ropecg(p.module, "#reprEnum((NI)$1, $2)", [
- rdLoc(a), genTypeInfoV1(p.module, t, e.info)]), a.storage)
+ rdLoc(a), genTypeInfo(p.module, t)]), a.s)
of tyString:
- putIntoDest(p, d, e, ropecg(p.module, "#reprStr($1)", [rdLoc(a)]), a.storage)
+ putIntoDest(p, d, e.typ, ropecg(p.module, "#reprStr($1)", [rdLoc(a)]), a.s)
of tySet:
- putIntoDest(p, d, e, ropecg(p.module, "#reprSet($1, $2)", [
- addrLoc(p.config, a), genTypeInfoV1(p.module, t, e.info)]), a.storage)
+ putIntoDest(p, d, e.typ, ropecg(p.module, "#reprSet($1, $2)", [
+ addrLoc(a), genTypeInfo(p.module, t)]), a.s)
of tyOpenArray, tyVarargs:
var b: TLoc
- case skipTypes(a.t, abstractVarRange).kind
+ case a.t.kind
of tyOpenArray, tyVarargs:
- putIntoDest(p, b, e, "$1, $1Len_0" % [rdLoc(a)], a.storage)
+ putIntoDest(p, b, e.typ, "$1, $1Len0" % [rdLoc(a)], a.s)
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)
- of tyArray:
- putIntoDest(p, b, e,
- "$1, $2" % [rdLoc(a), rope(lengthOrd(p.config, a.t))], a.storage)
- else: internalError(p.config, e[0].info, "genRepr()")
- putIntoDest(p, d, e,
+ putIntoDest(p, b, e.typ,
+ "$1->data, $1->$2" % [rdLoc(a), lenField(p)], a.s)
+ of tyArray, tyArrayConstr:
+ putIntoDest(p, b, e.typ,
+ "$1, $2" % [rdLoc(a), rope(lengthOrd(a.t))], a.s)
+ else: internalError(e.sons[0].info, "genRepr()")
+ putIntoDest(p, d, e.typ,
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:
- putIntoDest(p, d, e,
+ genTypeInfo(p.module, elemType(t))]), a.s)
+ of tyCString, tyArray, tyArrayConstr, tyRef, tyPtr, tyPointer, tyNil,
+ tySequence:
+ putIntoDest(p, d, e.typ,
ropecg(p.module, "#reprAny($1, $2)", [
- rdLoc(a), genTypeInfoV1(p.module, t, e.info)]), a.storage)
- of tyEmpty, tyVoid:
- localError(p.config, e.info, "'repr' doesn't support 'void' type")
+ rdLoc(a), genTypeInfo(p.module, t)]), a.s)
+ of tyEmpty:
+ localError(e.info, "'repr' doesn't support 'void' type")
else:
- putIntoDest(p, d, e, ropecg(p.module, "#reprAny($1, $2)",
- [addrLoc(p.config, a), genTypeInfoV1(p.module, t, e.info)]),
- a.storage)
- gcUsage(p.config, e)
-
-proc rdMType(p: BProc; a: TLoc; nilCheck: var Rope; result: var Rope; enforceV1 = false) =
- var derefs = 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} or not p.module.compileToCpp:
- derefs = "(*$1)" % [derefs]
- 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:
- result.add(".Sup")
- t = skipTypes(t[0], skipPtrs)
- result.add ".m_type"
- if optTinyRtti in p.config.globalOptions and enforceV1:
- result.add "->typeInfoV1"
+ putIntoDest(p, d, e.typ, ropecg(p.module, "#reprAny($1, $2)",
+ [addrLoc(a), genTypeInfo(p.module, t)]), a.s)
+ gcUsage(e)
proc genGetTypeInfo(p: BProc, e: PNode, d: var TLoc) =
- cgsym(p.module, "TNimType")
- let t = e[1].typ
- # ordinary static type information
- putIntoDest(p, d, e, genTypeInfoV1(p.module, t, e.info))
+ var t = skipTypes(e.sons[1].typ, abstractVarRange)
+ putIntoDest(p, d, e.typ, genTypeInfo(p.module, t))
-proc genGetTypeInfoV2(p: BProc, e: PNode, d: var TLoc) =
- let t = e[1].typ
- if isFinal(t) or e[0].sym.name.s != "getDynamicTypeInfo":
- # ordinary static type information
- putIntoDest(p, d, e, genTypeInfoV2(p.module, t, e.info))
- else:
- 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)
-
-proc genAccessTypeField(p: BProc; e: PNode; d: var TLoc) =
+proc genDollar(p: BProc, n: PNode, d: var TLoc, frmt: string) =
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)
-
-template genDollar(p: BProc, n: PNode, d: var TLoc, frmt: string) =
- var a: TLoc
- initLocExpr(p, n[1], a)
+ initLocExpr(p, n.sons[1], a)
a.r = ropecg(p.module, frmt, [rdLoc(a)])
- a.flags.excl lfIndirect # this flag should not be propagated here (not just for HCR)
if d.k == locNone: getTemp(p, n.typ, d)
- genAssignment(p, d, a, {})
- gcUsage(p.config, n)
+ genAssignment(p, d, a, {needToKeepAlive})
+ gcUsage(n)
proc genArrayLen(p: BProc, e: PNode, d: var TLoc, op: TMagic) =
- var a = e[1]
- if a.kind == nkHiddenAddr: a = a[0]
- var typ = skipTypes(a.typ, abstractVar + tyUserTypeClasses)
+ var a = e.sons[1]
+ if a.kind == nkHiddenAddr: a = a.sons[0]
+ var typ = skipTypes(a.typ, abstractVar)
case typ.kind
of tyOpenArray, tyVarargs:
- # 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:
- putIntoDest(p, d, e, ropecg(p.module, "($2)-($1)+1", [rdLoc(b), rdLoc(c)]))
+ if op == mHigh: unaryExpr(p, e, d, "($1Len0-1)")
+ else: unaryExpr(p, e, d, "$1Len0")
+ of tyCString:
+ useStringh(p.module)
+ if op == mHigh: unaryExpr(p, e, d, "($1 ? (strlen($1)-1) : -1)")
+ else: unaryExpr(p, e, d, "($1 ? strlen($1) : 0)")
+ of tyString, tySequence:
+ if not p.module.compileToCpp:
+ if op == mHigh: unaryExpr(p, e, d, "($1 ? ($1->Sup.len-1) : -1)")
+ else: unaryExpr(p, e, d, "($1 ? $1->Sup.len : 0)")
else:
- if not reifiedOpenArray(a):
- if op == mHigh: unaryExpr(p, e, d, "($1Len_0-1)")
- else: unaryExpr(p, e, d, "$1Len_0")
- else:
- if op == mHigh: unaryExpr(p, e, d, "($1.Field1-1)")
- else: unaryExpr(p, e, d, "$1.Field1")
- 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:
- var a: TLoc
- initLocExpr(p, e[1], a)
- var x = lenExpr(p, a)
- if op == mHigh: x = "($1-1)" % [x]
- putIntoDest(p, d, e, x)
- of tySequence:
- # we go through a temporary here because people write bullshit code.
- var a, tmp: TLoc
- initLocExpr(p, e[1], a)
- getIntTemp(p, tmp)
- var x = lenExpr(p, a)
- if op == mHigh: x = "($1-1)" % [x]
- lineCg(p, cpsStmts, "$1 = $2;$n", [tmp.r, x])
- putIntoDest(p, d, e, tmp.r)
- of tyArray:
+ if op == mHigh: unaryExpr(p, e, d, "($1 ? ($1->len-1) : -1)")
+ else: unaryExpr(p, e, d, "($1 ? $1->len : 0)")
+ of tyArray, tyArrayConstr:
# YYY: length(sideeffect) is optimized away incorrectly?
- if op == mHigh: putIntoDest(p, d, e, rope(lastOrd(p.config, typ)))
- else: putIntoDest(p, d, e, rope(lengthOrd(p.config, typ)))
- else: internalError(p.config, e.info, "genArrayLen()")
-
-proc makePtrType(baseType: PType; idgen: IdGenerator): PType =
- result = newType(tyPtr, nextTypeId idgen, baseType.owner)
- addSonSkipIntLit(result, baseType, idgen)
-
-proc makeAddr(n: PNode; idgen: IdGenerator): PNode =
- if n.kind == nkHiddenAddr:
- result = n
- else:
- result = newTree(nkHiddenAddr, n)
- result.typ = makePtrType(n.typ, idgen)
+ if op == mHigh: putIntoDest(p, d, e.typ, rope(lastOrd(typ)))
+ else: putIntoDest(p, d, e.typ, rope(lengthOrd(typ)))
+ else: internalError(e.info, "genArrayLen()")
proc genSetLengthSeq(p: BProc, e: PNode, d: var TLoc) =
- if optSeqDestructors in p.config.globalOptions:
- e[1] = makeAddr(e[1], p.module.idgen)
- genCall(p, e, d)
- return
- var a, b, call: TLoc
+ var a, b: TLoc
assert(d.k == locNone)
- var x = e[1]
- if x.kind in {nkAddr, nkHiddenAddr}: x = x[0]
- initLocExpr(p, x, a)
- initLocExpr(p, e[2], b)
- let t = skipTypes(e[1].typ, {tyVar})
+ initLocExpr(p, e.sons[1], a)
+ initLocExpr(p, e.sons[2], b)
+ var t = skipTypes(e.sons[1].typ, abstractVar)
+ let setLenPattern = if not p.module.compileToCpp:
+ "$1 = ($3) #setLengthSeq(&($1)->Sup, sizeof($4), $2);$n"
+ else:
+ "$1 = ($3) #setLengthSeq($1, sizeof($4), $2);$n"
- initLoc(call, locCall, e, OnHeap)
- if not p.module.compileToCpp:
- const setLenPattern = "($3) #setLengthSeqV2(&($1)->Sup, $4, $2)"
- call.r = ropecg(p.module, setLenPattern, [
+ lineCg(p, cpsStmts, setLenPattern, [
rdLoc(a), rdLoc(b), getTypeDesc(p.module, t),
- genTypeInfoV1(p.module, t.skipTypes(abstractInst), e.info)])
-
- else:
- const setLenPattern = "($3) #setLengthSeqV2($1, $4, $2)"
- call.r = ropecg(p.module, setLenPattern, [
- rdLoc(a), rdLoc(b), getTypeDesc(p.module, t),
- genTypeInfoV1(p.module, t.skipTypes(abstractInst), e.info)])
-
- genAssignment(p, a, call, {})
- gcUsage(p.config, e)
+ getTypeDesc(p.module, t.sons[0])])
+ keepAlive(p, a)
+ gcUsage(e)
proc genSetLengthStr(p: BProc, e: PNode, d: var TLoc) =
- if optSeqDestructors in p.config.globalOptions:
- binaryStmtAddr(p, e, d, "setLengthStrV2")
- else:
- var a, b, call: TLoc
- if d.k != locNone: internalError(p.config, e.info, "genSetLengthStr")
- initLocExpr(p, e[1], a)
- initLocExpr(p, e[2], b)
-
- initLoc(call, locCall, e, OnHeap)
- call.r = ropecg(p.module, "#setLengthStr($1, $2)", [
- rdLoc(a), rdLoc(b)])
- genAssignment(p, a, call, {})
- gcUsage(p.config, e)
+ binaryStmt(p, e, d, "$1 = #setLengthStr($1, $2);$n")
+ keepAlive(p, d)
+ gcUsage(e)
proc genSwap(p: BProc, e: PNode, d: var TLoc) =
# swap(a, b) -->
# temp = a
# a = b
# b = temp
- cowBracket(p, e[1])
- cowBracket(p, e[2])
var a, b, tmp: TLoc
- getTemp(p, skipTypes(e[1].typ, abstractVar), tmp)
- initLocExpr(p, e[1], a) # eval a
- initLocExpr(p, e[2], b) # eval b
+ getTemp(p, skipTypes(e.sons[1].typ, abstractVar), tmp)
+ initLocExpr(p, e.sons[1], a) # eval a
+ initLocExpr(p, e.sons[2], b) # eval b
genAssignment(p, tmp, a, {})
genAssignment(p, a, b, {})
genAssignment(p, b, tmp, {})
-proc rdSetElemLoc(conf: ConfigRef; a: TLoc, typ: PType; result: var Rope) =
+proc rdSetElemLoc(a: TLoc, setType: 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)
- let setType = typ.skipTypes(abstractPtrs)
+ result = rdCharLoc(a)
assert(setType.kind == tySet)
- if firstOrd(conf, setType) != 0:
- result.add " - "
- result.add rope(firstOrd(conf, setType))
- result.add ")"
+ if firstOrd(setType) != 0:
+ result = "($1- $2)" % [result, rope(firstOrd(setType))]
-proc fewCmps(conf: ConfigRef; s: PNode): bool =
+proc fewCmps(s: PNode): bool =
# this function estimates whether it is better to emit code
# for constructing the set or generating a bunch of comparisons directly
- if s.kind != nkCurly: return false
- if (getSize(conf, s.typ) <= conf.target.intSize) and (nfAllConst in s.flags):
+ if s.kind != nkCurly: internalError(s.info, "fewCmps")
+ if (getSize(s.typ) <= platform.intSize) and (nfAllConst in s.flags):
result = false # it is better to emit the set generation code
elif elemType(s.typ).kind in {tyInt, tyInt16..tyInt64}:
result = true # better not emit the set if int is basetype!
else:
- result = s.len <= 8 # 8 seems to be a good value
+ result = sonsLen(s) <= 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])
+proc binaryExprIn(p: BProc, e: PNode, a, b, d: var TLoc, frmt: string) =
+ putIntoDest(p, d, e.typ, frmt % [rdLoc(a), rdSetElemLoc(b, a.t)])
proc genInExprAux(p: BProc, e: PNode, a, b, d: var TLoc) =
- case int(getSize(p.config, skipTypes(e[1].typ, abstractVar)))
- of 1: binaryExprIn(p, e, a, b, d, "(($1 &((NU8)1<<((NU)($2)&7U)))!=0)")
- of 2: binaryExprIn(p, e, a, b, d, "(($1 &((NU16)1<<((NU)($2)&15U)))!=0)")
- of 4: binaryExprIn(p, e, a, b, d, "(($1 &((NU32)1<<((NU)($2)&31U)))!=0)")
+ case int(getSize(skipTypes(e.sons[1].typ, abstractVar)))
+ of 1: binaryExprIn(p, e, a, b, d, "(($1 &(1U<<((NU)($2)&7U)))!=0)")
+ of 2: binaryExprIn(p, e, a, b, d, "(($1 &(1U<<((NU)($2)&15U)))!=0)")
+ of 4: binaryExprIn(p, e, a, b, d, "(($1 &(1U<<((NU)($2)&31U)))!=0)")
of 8: binaryExprIn(p, e, a, b, d, "(($1 &((NU64)1<<((NU)($2)&63U)))!=0)")
else: binaryExprIn(p, e, a, b, d, "(($1[(NU)($2)>>3] &(1U<<((NU)($2)&7U)))!=0)")
-template binaryStmtInExcl(p: BProc, e: PNode, d: var TLoc, frmt: string) =
+proc binaryStmtInExcl(p: BProc, e: PNode, d: var TLoc, frmt: string) =
var a, b: TLoc
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])
+ initLocExpr(p, e.sons[1], a)
+ initLocExpr(p, e.sons[2], b)
+ lineF(p, cpsStmts, frmt, [rdLoc(a), rdSetElemLoc(b, a.t)])
proc genInOp(p: BProc, e: PNode, d: var TLoc) =
var a, b, x, y: TLoc
- if (e[1].kind == nkCurly) and fewCmps(p.config, e[1]):
+ if (e.sons[1].kind == nkCurly) and fewCmps(e.sons[1]):
# a set constructor but not a constant set:
# do not emit the set, but generate a bunch of comparisons; and if we do
# so, we skip the unnecessary range check: This is a semantical extension
# that code now relies on. :-/ XXX
- let ea = if e[2].kind in {nkChckRange, nkChckRange64}:
- e[2][0]
+ let ea = if e.sons[2].kind in {nkChckRange, nkChckRange64}:
+ e.sons[2].sons[0]
else:
- e[2]
+ e.sons[2]
initLocExpr(p, ea, a)
- initLoc(b, locExpr, e, OnUnknown)
- if e[1].len > 0:
- b.r = rope("(")
- for i in 0..= $2 && $1 <= $3",
- [rdCharLoc(a), rdCharLoc(x), rdCharLoc(y)])
- else:
- initLocExpr(p, it, x)
- b.r.addf("$1 == $2", [rdCharLoc(a), rdCharLoc(x)])
- if i < e[1].len - 1: b.r.add(" || ")
- b.r.add(")")
- else:
- # handle the case of an empty set
- b.r = rope("0")
- putIntoDest(p, d, e, b.r)
+ initLoc(b, locExpr, e.typ, OnUnknown)
+ b.r = rope("(")
+ var length = sonsLen(e.sons[1])
+ for i in countup(0, length - 1):
+ if e.sons[1].sons[i].kind == nkRange:
+ initLocExpr(p, e.sons[1].sons[i].sons[0], x)
+ initLocExpr(p, e.sons[1].sons[i].sons[1], y)
+ addf(b.r, "$1 >= $2 && $1 <= $3",
+ [rdCharLoc(a), rdCharLoc(x), rdCharLoc(y)])
+ else:
+ initLocExpr(p, e.sons[1].sons[i], x)
+ addf(b.r, "$1 == $2", [rdCharLoc(a), rdCharLoc(x)])
+ if i < length - 1: add(b.r, " || ")
+ add(b.r, ")")
+ putIntoDest(p, d, e.typ, b.r)
else:
- assert(e[1].typ != nil)
- assert(e[2].typ != nil)
- initLocExpr(p, e[1], a)
- initLocExpr(p, e[2], b)
+ assert(e.sons[1].typ != nil)
+ assert(e.sons[2].typ != nil)
+ initLocExpr(p, e.sons[1], a)
+ initLocExpr(p, e.sons[2], b)
genInExprAux(p, e, a, b, d)
proc genSetOp(p: BProc, e: PNode, d: var TLoc, op: TMagic) =
const
- lookupOpr: array[mLeSet..mMinusSet, string] = [
+ lookupOpr: array[mLeSet..mSymDiffSet, string] = [
"for ($1 = 0; $1 < $2; $1++) { $n" &
- " $3 = (($4[$1] & ~ $5[$1]) == 0);$n" &
- " if (!$3) break;}$n",
- "for ($1 = 0; $1 < $2; $1++) { $n" &
- " $3 = (($4[$1] & ~ $5[$1]) == 0);$n" &
- " if (!$3) break;}$n" &
- "if ($3) $3 = (#nimCmpMem($4, $5, $2) != 0);$n",
- "&",
- "|",
- "& ~"]
+ " $3 = (($4[$1] & ~ $5[$1]) == 0);$n" &
+ " if (!$3) break;}$n", "for ($1 = 0; $1 < $2; $1++) { $n" &
+ " $3 = (($4[$1] & ~ $5[$1]) == 0);$n" & " if (!$3) break;}$n" &
+ "if ($3) $3 = (memcmp($4, $5, $2) != 0);$n",
+ "&", "|", "& ~", "^"]
var a, b, i: TLoc
- var setType = skipTypes(e[1].typ, abstractVar)
- var size = int(getSize(p.config, setType))
+ var setType = skipTypes(e.sons[1].typ, abstractVar)
+ var size = int(getSize(setType))
case size
of 1, 2, 4, 8:
case op
of mIncl:
- case size
- of 1: binaryStmtInExcl(p, e, d, "$1 |= ((NU8)1)<<(($2) & 7);$n")
- of 2: binaryStmtInExcl(p, e, d, "$1 |= ((NU16)1)<<(($2) & 15);$n")
- of 4: binaryStmtInExcl(p, e, d, "$1 |= ((NU32)1)<<(($2) & 31);$n")
- of 8: binaryStmtInExcl(p, e, d, "$1 |= ((NU64)1)<<(($2) & 63);$n")
- else: assert(false, $size)
+ var ts = "NU" & $(size * 8)
+ binaryStmtInExcl(p, e, d,
+ "$1 |= ((" & ts & ")1)<<(($2)%(sizeof(" & ts & ")*8));$n")
of mExcl:
- case size
- of 1: binaryStmtInExcl(p, e, d, "$1 &= ~(((NU8)1) << (($2) & 7));$n")
- of 2: binaryStmtInExcl(p, e, d, "$1 &= ~(((NU16)1) << (($2) & 15));$n")
- of 4: binaryStmtInExcl(p, e, d, "$1 &= ~(((NU32)1) << (($2) & 31));$n")
- of 8: binaryStmtInExcl(p, e, d, "$1 &= ~(((NU64)1) << (($2) & 63));$n")
- else: assert(false, $size)
+ var ts = "NU" & $(size * 8)
+ binaryStmtInExcl(p, e, d, "$1 &= ~(((" & ts & ")1) << (($2) % (sizeof(" &
+ ts & ")*8)));$n")
of mCard:
if size <= 4: unaryExprChar(p, e, d, "#countBits32($1)")
else: unaryExprChar(p, e, d, "#countBits64($1)")
- of mLtSet: binaryExprChar(p, e, d, "((($1 & ~ $2)==0)&&($1 != $2))")
+ of mLtSet: binaryExprChar(p, e, d, "(($1 & ~ $2 ==0)&&($1 != $2))")
of mLeSet: binaryExprChar(p, e, d, "(($1 & ~ $2)==0)")
of mEqSet: binaryExpr(p, e, d, "($1 == $2)")
of mMulSet: binaryExpr(p, e, d, "($1 & $2)")
of mPlusSet: binaryExpr(p, e, d, "($1 | $2)")
of mMinusSet: binaryExpr(p, e, d, "($1 & ~ $2)")
+ of mSymDiffSet: binaryExpr(p, e, d, "($1 ^ $2)")
of mInSet:
genInOp(p, e, d)
- else: internalError(p.config, e.info, "genSetOp()")
+ else: internalError(e.info, "genSetOp()")
else:
case op
of mIncl: binaryStmtInExcl(p, e, d, "$1[(NU)($2)>>3] |=(1U<<($2&7U));$n")
of mExcl: binaryStmtInExcl(p, e, d, "$1[(NU)($2)>>3] &= ~(1U<<($2&7U));$n")
- 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]))
+ of mCard: unaryExprChar(p, e, d, "#cardSet($1, " & $size & ')')
of mLtSet, mLeSet:
- getTemp(p, getSysType(p.module.g.graph, unknownLineInfo, tyInt), i) # our counter
- initLocExpr(p, e[1], a)
- initLocExpr(p, e[2], b)
- if d.k == locNone: getTemp(p, getSysType(p.module.g.graph, unknownLineInfo, tyBool), d)
- if op == mLtSet:
- linefmt(p, cpsStmts, lookupOpr[mLtSet],
- [rdLoc(i), size, rdLoc(d), rdLoc(a), rdLoc(b)])
- else:
- linefmt(p, cpsStmts, lookupOpr[mLeSet],
- [rdLoc(i), size, rdLoc(d), rdLoc(a), rdLoc(b)])
+ getTemp(p, getSysType(tyInt), i) # our counter
+ initLocExpr(p, e.sons[1], a)
+ initLocExpr(p, e.sons[2], b)
+ if d.k == locNone: getTemp(p, getSysType(tyBool), d)
+ lineF(p, cpsStmts, lookupOpr[op],
+ [rdLoc(i), rope(size), rdLoc(d), rdLoc(a), rdLoc(b)])
of mEqSet:
- var a, b: TLoc
- assert(e[1].typ != nil)
- assert(e[2].typ != nil)
- initLocExpr(p, e[1], a)
- initLocExpr(p, e[2], b)
- putIntoDest(p, d, e, ropecg(p.module, "(#nimCmpMem($1, $2, $3)==0)", [a.rdCharLoc, b.rdCharLoc, size]))
- of mMulSet, mPlusSet, mMinusSet:
+ useStringh(p.module)
+ binaryExprChar(p, e, d, "(memcmp($1, $2, " & $(size) & ")==0)")
+ of mMulSet, mPlusSet, mMinusSet, mSymDiffSet:
# we inline the simple for loop for better code generation:
- getTemp(p, getSysType(p.module.g.graph, unknownLineInfo, tyInt), i) # our counter
- initLocExpr(p, e[1], a)
- initLocExpr(p, e[2], b)
- if d.k == locNone: getTemp(p, setType, d)
+ getTemp(p, getSysType(tyInt), i) # our counter
+ initLocExpr(p, e.sons[1], a)
+ initLocExpr(p, e.sons[2], b)
+ if d.k == locNone: getTemp(p, a.t, d)
lineF(p, cpsStmts,
"for ($1 = 0; $1 < $2; $1++) $n" &
" $3[$1] = $4[$1] $6 $5[$1];$n", [
rdLoc(i), rope(size), rdLoc(d), rdLoc(a), rdLoc(b),
rope(lookupOpr[op])])
of mInSet: genInOp(p, e, d)
- else: internalError(p.config, e.info, "genSetOp")
+ else: internalError(e.info, "genSetOp")
proc genOrd(p: BProc, e: PNode, d: var TLoc) =
unaryExprChar(p, e, d, "$1")
proc genSomeCast(p: BProc, e: PNode, d: var TLoc) =
const
- ValueTypes = {tyTuple, tyObject, tyArray, tyOpenArray, tyVarargs, tyUncheckedArray}
+ ValueTypes = {tyTuple, tyObject, tyArray, tyOpenArray, tyVarargs,
+ tyArrayConstr}
# we use whatever C gives us. Except if we have a value-type, we need to go
# through its address:
var a: TLoc
- initLocExpr(p, e[1], a)
- let etyp = skipTypes(e.typ, abstractRange+{tyOwned})
- let srcTyp = skipTypes(e[1].typ, abstractRange)
+ initLocExpr(p, e.sons[1], a)
+ let etyp = skipTypes(e.typ, abstractRange)
if etyp.kind in ValueTypes and lfIndirect notin a.flags:
- putIntoDest(p, d, e, "(*($1*) ($2))" %
- [getTypeDesc(p.module, e.typ), addrLoc(p.config, a)], a.storage)
- elif etyp.kind == tyProc and etyp.callConv == ccClosure and srcTyp.callConv != ccClosure:
- putIntoDest(p, d, e, "(($1) ($2))" %
- [getClosureType(p.module, etyp, clHalfWithEnv), rdCharLoc(a)], a.storage)
+ putIntoDest(p, d, e.typ, "(*($1*) ($2))" %
+ [getTypeDesc(p.module, e.typ), addrLoc(a)], a.s)
+ elif etyp.kind == tyProc and etyp.callConv == ccClosure:
+ putIntoDest(p, d, e.typ, "(($1) ($2))" %
+ [getClosureType(p.module, etyp, clHalfWithEnv), rdCharLoc(a)], a.s)
else:
- # C++ does not like direct casts from pointer to shorter integral types
- if srcTyp.kind in {tyPtr, tyPointer} and etyp.kind in IntegralTypes:
- putIntoDest(p, d, e, "(($1) (ptrdiff_t) ($2))" %
- [getTypeDesc(p.module, e.typ), rdCharLoc(a)], a.storage)
- elif optSeqDestructors in p.config.globalOptions and etyp.kind in {tySequence, tyString}:
- putIntoDest(p, d, e, "(*($1*) (&$2))" %
- [getTypeDesc(p.module, e.typ), rdCharLoc(a)], a.storage)
- 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)
+ putIntoDest(p, d, e.typ, "(($1) ($2))" %
+ [getTypeDesc(p.module, e.typ), rdCharLoc(a)], a.s)
proc genCast(p: BProc, e: PNode, d: var TLoc) =
- const ValueTypes = {tyFloat..tyFloat128, tyTuple, tyObject, tyArray}
+ const floatTypes = {tyFloat..tyFloat128}
let
destt = skipTypes(e.typ, abstractRange)
- srct = skipTypes(e[1].typ, abstractRange)
- if destt.kind in ValueTypes or srct.kind in ValueTypes:
+ srct = skipTypes(e.sons[1].typ, abstractRange)
+ if destt.kind in floatTypes or srct.kind in floatTypes:
# 'cast' and some float type involved? --> use a union.
inc(p.labels)
var lbl = p.labels.rope
var tmp: TLoc
tmp.r = "LOC$1.source" % [lbl]
linefmt(p, cpsLocals, "union { $1 source; $2 dest; } LOC$3;$n",
- [getTypeDesc(p.module, e[1].typ), getTypeDesc(p.module, e.typ), lbl])
+ getTypeDesc(p.module, srct), getTypeDesc(p.module, destt), lbl)
tmp.k = locExpr
- tmp.lode = lodeTyp srct
- tmp.storage = OnStack
+ tmp.t = srct
+ tmp.s = OnStack
tmp.flags = {}
- expr(p, e[1], tmp)
- putIntoDest(p, d, e, "LOC$#.dest" % [lbl], tmp.storage)
+ expr(p, e.sons[1], tmp)
+ putIntoDest(p, d, e.typ, "LOC$#.dest" % [lbl], tmp.s)
else:
# I prefer the shorter cast version for pointer types -> generate less
# C code; plus it's the right thing to do for closures:
genSomeCast(p, e, d)
-proc genRangeChck(p: BProc, n: PNode, d: var TLoc) =
+proc genRangeChck(p: BProc, n: PNode, d: var TLoc, magic: string) =
var a: TLoc
var dest = skipTypes(n.typ, abstractVar)
- initLocExpr(p, n[0], a)
- if optRangeCheck notin p.options or (dest.kind in {tyUInt..tyUInt64} and
- checkUnsignedConversions notin p.config.legacyFeatures):
- discard "no need to generate a check because it was disabled"
+ # range checks for unsigned turned out to be buggy and annoying:
+ if optRangeCheck notin p.options or dest.skipTypes({tyRange}).kind in
+ {tyUInt..tyUInt64}:
+ initLocExpr(p, n.sons[0], a)
+ putIntoDest(p, d, n.typ, "(($1) ($2))" %
+ [getTypeDesc(p.module, dest), rdCharLoc(a)], a.s)
else:
- let n0t = n[0].typ
-
- # 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", []
-
- else:
- let raiser =
- case skipTypes(n.typ, abstractVarRange).kind
- of tyUInt..tyUInt64, tyChar: "raiseRangeErrorU"
- of tyFloat..tyFloat128: "raiseRangeErrorF"
- else: "raiseRangeErrorI"
- cgsym(p.module, raiser)
-
- let boundaryCast =
- if n0t.skipTypes(abstractVarRange).kind in {tyUInt, tyUInt32, tyUInt64}:
- "(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", []
-
- putIntoDest(p, d, n, "(($1) ($2))" %
- [getTypeDesc(p.module, dest), rdCharLoc(a)], a.storage)
+ initLocExpr(p, n.sons[0], a)
+ putIntoDest(p, d, dest, ropecg(p.module, "(($1)#$5($2, $3, $4))", [
+ getTypeDesc(p.module, dest), rdCharLoc(a),
+ genLiteral(p, n.sons[1], dest), genLiteral(p, n.sons[2], dest),
+ rope(magic)]), a.s)
proc genConv(p: BProc, e: PNode, d: var TLoc) =
- let destType = e.typ.skipTypes({tyVar, tyLent, tyGenericInst, tyAlias, tySink})
- if sameBackendType(destType, e[1].typ):
- expr(p, e[1], d)
+ let destType = e.typ.skipTypes({tyVar, tyGenericInst})
+ if compareTypes(destType, e.sons[1].typ, dcEqIgnoreDistinct):
+ expr(p, e.sons[1], d)
else:
genSomeCast(p, e, d)
proc convStrToCStr(p: BProc, n: PNode, d: var TLoc) =
var a: TLoc
- initLocExpr(p, n[0], a)
- putIntoDest(p, d, n,
- ropecg(p.module, "#nimToCStringConv($1)", [rdLoc(a)]),
-# "($1 ? $1->data : (NCSTRING)\"\")" % [a.rdLoc],
- a.storage)
+ initLocExpr(p, n.sons[0], a)
+ putIntoDest(p, d, skipTypes(n.typ, abstractVar), "$1->data" % [rdLoc(a)], a.s)
proc convCStrToStr(p: BProc, n: PNode, d: var TLoc) =
var a: TLoc
- initLocExpr(p, n[0], a)
- putIntoDest(p, d, n,
- ropecg(p.module, "#cstrToNimstr($1)", [rdLoc(a)]),
- a.storage)
- gcUsage(p.config, n)
+ initLocExpr(p, n.sons[0], a)
+ putIntoDest(p, d, skipTypes(n.typ, abstractVar),
+ ropecg(p.module, "#cstrToNimstr($1)", [rdLoc(a)]), a.s)
+ gcUsage(n)
proc genStrEquals(p: BProc, e: PNode, d: var TLoc) =
var x: TLoc
- var a = e[1]
- var b = e[2]
- if a.kind in {nkStrLit..nkTripleStrLit} and a.strVal == "":
- initLocExpr(p, e[2], x)
- putIntoDest(p, d, e,
- ropecg(p.module, "($1 == 0)", [lenExpr(p, x)]))
- elif b.kind in {nkStrLit..nkTripleStrLit} and b.strVal == "":
- initLocExpr(p, e[1], x)
- putIntoDest(p, d, e,
- ropecg(p.module, "($1 == 0)", [lenExpr(p, x)]))
+ var a = e.sons[1]
+ var b = e.sons[2]
+ if (a.kind == nkNilLit) or (b.kind == nkNilLit):
+ binaryExpr(p, e, d, "($1 == $2)")
+ elif (a.kind in {nkStrLit..nkTripleStrLit}) and (a.strVal == ""):
+ initLocExpr(p, e.sons[2], x)
+ putIntoDest(p, d, e.typ,
+ rfmt(nil, "(($1) && ($1)->$2 == 0)", rdLoc(x), lenField(p)))
+ elif (b.kind in {nkStrLit..nkTripleStrLit}) and (b.strVal == ""):
+ initLocExpr(p, e.sons[1], x)
+ putIntoDest(p, d, e.typ,
+ rfmt(nil, "(($1) && ($1)->$2 == 0)", rdLoc(x), lenField(p)))
else:
binaryExpr(p, e, d, "#eqStrings($1, $2)")
@@ -2310,126 +1637,25 @@ proc binaryFloatArith(p: BProc, e: PNode, d: var TLoc, m: TMagic) =
if {optNaNCheck, optInfCheck} * p.options != {}:
const opr: array[mAddF64..mDivF64, string] = ["+", "-", "*", "/"]
var a, b: TLoc
- assert(e[1].typ != nil)
- assert(e[2].typ != nil)
- initLocExpr(p, e[1], a)
- initLocExpr(p, e[2], b)
- putIntoDest(p, d, e, ropecg(p.module, "(($4)($2) $1 ($4)($3))",
- [opr[m], rdLoc(a), rdLoc(b),
- getSimpleTypeDesc(p.module, e[1].typ)]))
+ assert(e.sons[1].typ != nil)
+ assert(e.sons[2].typ != nil)
+ initLocExpr(p, e.sons[1], a)
+ initLocExpr(p, e.sons[2], b)
+ putIntoDest(p, d, e.typ, rfmt(nil, "(($4)($2) $1 ($4)($3))",
+ rope(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, "#nanCheck($1);$n", rdLoc(d))
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, "#infCheck($1);$n", rdLoc(d))
else:
binaryArith(p, e, d, m)
-proc skipAddr(n: PNode): PNode =
- result = if n.kind in {nkAddr, nkHiddenAddr}: n[0] else: n
-
-proc genWasMoved(p: BProc; n: PNode) =
- var a: TLoc
- let n1 = n[1].skipAddr
- if p.withinBlockLeaveActions > 0 and notYetAlive(n1):
- discard
- else:
- initLocExpr(p, n1, a)
- resetLoc(p, a)
- #linefmt(p, cpsStmts, "#nimZeroMem((void*)$1, sizeof($2));$n",
- # [addrLoc(p.config, a), getTypeDesc(p.module, a.t)])
-
-proc genMove(p: BProc; n: PNode; d: var TLoc) =
- var a: TLoc
- initLocExpr(p, n[1].skipAddr, a)
- if n.len == 4:
- # generated by liftdestructors:
- var src: TLoc
- initLocExpr(p, n[2], src)
- linefmt(p, cpsStmts, "if ($1.p != $2.p) {", [rdLoc(a), rdLoc(src)])
- genStmts(p, n[3])
- linefmt(p, cpsStmts, "}$n$1.len = $2.len; $1.p = $2.p;$n", [rdLoc(a), rdLoc(src)])
- else:
- if d.k == locNone: getTemp(p, n.typ, d)
- genAssignment(p, d, a, {})
- resetLoc(p, a)
-
-proc genDestroy(p: BProc; n: PNode) =
- if optSeqDestructors in p.config.globalOptions:
- let arg = n[1].skipAddr
- let t = arg.typ.skipTypes(abstractInst)
- case t.kind
- of tyString:
- var a: TLoc
- initLocExpr(p, arg, a)
- if optThreads in p.config.globalOptions:
- linefmt(p, cpsStmts, "if ($1.p && !($1.p->cap & NIM_STRLIT_FLAG)) {$n" &
- " #deallocShared($1.p);$n" &
- "}$n", [rdLoc(a)])
- else:
- linefmt(p, cpsStmts, "if ($1.p && !($1.p->cap & NIM_STRLIT_FLAG)) {$n" &
- " #dealloc($1.p);$n" &
- "}$n", [rdLoc(a)])
- of tySequence:
- var a: TLoc
- initLocExpr(p, arg, a)
- linefmt(p, cpsStmts, "if ($1.p && !($1.p->cap & NIM_STRLIT_FLAG)) {$n" &
- " #alignedDealloc($1.p, NIM_ALIGNOF($2));$n" &
- "}$n",
- [rdLoc(a), getTypeDesc(p.module, t.lastSon)])
- 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:
- internalError(p.config, n.info, "destructor turned out to be not trivial")
- discard "ignore calls to the default destructor"
-
-proc genDispose(p: BProc; n: PNode) =
- when false:
- let elemType = n[1].typ.skipTypes(abstractVar).lastSon
-
- var a: TLoc
- initLocExpr(p, n[1].skipAddr, a)
-
- if isFinal(elemType):
- if elemType.destructor != nil:
- var destroyCall = newNodeI(nkCall, n.info)
- genStmts(p, destroyCall)
- lineFmt(p, cpsStmts, "#nimRawDispose($1, NIM_ALIGNOF($2))", [rdLoc(a), getTypeDesc(p.module, elemType)])
- else:
- # ``nimRawDisposeVirtual`` calls the ``finalizer`` which is the same as the
- # destructor, but it uses the runtime type. Afterwards the memory is freed:
- 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})
- 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:
- localError(p.config, e.info, "invalid context for 'toOpenArray'; " &
- "'toOpenArray' is only valid within a call expression")
-
-proc genEnumToStr(p: BProc, e: PNode, d: var TLoc) =
- 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 n = copyTree(e)
- n[0] = newSymNode(toStrProc)
- expr(p, n, d)
-
proc genMagicExpr(p: BProc, e: PNode, d: var TLoc, op: TMagic) =
+ var line, filen: Rope
case op
of mOr, mAnd: genAndOr(p, e, d, op)
- of mNot..mUnaryMinusF64: unaryArith(p, e, d, op)
+ of mNot..mToBiggestInt: unaryArith(p, e, d, op)
of mUnaryMinusI..mAbsI: unaryArithOverflow(p, e, d, op)
of mAddF64..mDivF64: binaryFloatArith(p, e, d, op)
of mShrI..mXor: binaryArith(p, e, d, op)
@@ -2437,47 +1663,39 @@ proc genMagicExpr(p: BProc, e: PNode, d: var TLoc, op: TMagic) =
of mAddI..mPred: binaryArithOverflow(p, e, d, op)
of mRepr: genRepr(p, e, d)
of mGetTypeInfo: genGetTypeInfo(p, e, d)
- of mGetTypeInfoV2: genGetTypeInfoV2(p, e, d)
of mSwap: genSwap(p, e, d)
+ of mUnaryLt:
+ if optOverflowCheck notin p.options: unaryExpr(p, e, d, "($1 - 1)")
+ else: unaryExpr(p, e, d, "#subInt($1, 1)")
of mInc, mDec:
- const opr: array[mInc..mDec, string] = ["+=", "-="]
- const fun64: array[mInc..mDec, string] = ["nimAddInt64", "nimSubInt64"]
- const fun: array[mInc..mDec, string] = ["nimAddInt","nimSubInt"]
- let underlying = skipTypes(e[1].typ, {tyGenericInst, tyAlias, tySink, tyVar, tyLent, tyRange, tyDistinct})
+ const opr: array [mInc..mDec, string] = ["$1 += $2;$n", "$1 -= $2;$n"]
+ const fun64: array [mInc..mDec, string] = ["$# = #addInt64($#, $#);$n",
+ "$# = #subInt64($#, $#);$n"]
+ const fun: array [mInc..mDec, string] = ["$# = #addInt($#, $#);$n",
+ "$# = #subInt($#, $#);$n"]
+ let underlying = skipTypes(e.sons[1].typ, {tyGenericInst, tyVar, tyRange})
if optOverflowCheck notin p.options or underlying.kind in {tyUInt..tyUInt64}:
binaryStmt(p, e, d, opr[op])
else:
var a, b: TLoc
- assert(e[1].typ != nil)
- assert(e[2].typ != nil)
- initLocExpr(p, e[1], a)
- initLocExpr(p, e[2], b)
+ assert(e.sons[1].typ != nil)
+ assert(e.sons[2].typ != nil)
+ initLocExpr(p, e.sons[1], a)
+ initLocExpr(p, e.sons[2], b)
- let ranged = skipTypes(e[1].typ, {tyGenericInst, tyAlias, tySink, tyVar, tyLent, tyDistinct})
+ let ranged = skipTypes(e.sons[1].typ, {tyGenericInst, tyVar})
let res = binaryArithOverflowRaw(p, ranged, a, b,
if underlying.kind == tyInt64: fun64[op] else: fun[op])
-
- putIntoDest(p, a, e[1], "($#)($#)" % [
+ putIntoDest(p, a, ranged, "($#)($#)" % [
getTypeDesc(p.module, ranged), res])
of mConStrStr: genStrConcat(p, e, d)
of mAppendStrCh:
- if optSeqDestructors in p.config.globalOptions:
- binaryStmtAddr(p, e, d, "nimAddCharV1")
- else:
- var dest, b, call: TLoc
- initLoc(call, locCall, e, OnHeap)
- initLocExpr(p, e[1], dest)
- initLocExpr(p, e[2], b)
- call.r = ropecg(p.module, "#addChar($1, $2)", [rdLoc(dest), rdLoc(b)])
- genAssignment(p, dest, call, {})
+ binaryStmt(p, e, d, "$1 = #addChar($1, $2);$n")
+ # strictly speaking we need to generate "keepAlive" here too, but this
+ # very likely not needed and would slow down the code too much I fear
of mAppendStrStr: genStrAppend(p, e, d)
- of mAppendSeqElem:
- if optSeqDestructors in p.config.globalOptions:
- e[1] = makeAddr(e[1], p.module.idgen)
- genCall(p, e, d)
- else:
- genSeqElemAppend(p, e, d)
+ of mAppendSeqElem: genSeqElemAppend(p, e, d)
of mEqStr: genStrEquals(p, e, d)
of mLeStr: binaryExpr(p, e, d, "(#cmpStrings($1, $2) <= 0)")
of mLtStr: binaryExpr(p, e, d, "(#cmpStrings($1, $2) < 0)")
@@ -2486,358 +1704,254 @@ 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 mEnumToStr:
- if optTinyRtti in p.config.globalOptions:
- genEnumToStr(p, e, d)
- else:
- genRepr(p, e, d)
+ of mStrToStr: expr(p, e.sons[1], d)
+ of mEnumToStr: genRepr(p, e, d)
of mOf: genOf(p, e, d)
of mNew: genNew(p, e)
- of mNewFinalize:
- if optTinyRtti in p.config.globalOptions:
- var a: TLoc
- initLocExpr(p, e[1], a)
- rawGenNew(p, a, "", 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 mNewSeqOfCap: genNewSeqOfCap(p, e, d)
+ of mNewFinalize: genNewFinalize(p, e)
+ of mNewSeq: genNewSeq(p, e)
of mSizeOf:
- let t = e[1].typ.skipTypes({tyTypeDesc})
- putIntoDest(p, d, e, "((NI)sizeof($1))" % [getTypeDesc(p.module, t, skVar)])
- of mAlignOf:
- let t = e[1].typ.skipTypes({tyTypeDesc})
- putIntoDest(p, d, e, "((NI)NIM_ALIGNOF($1))" % [getTypeDesc(p.module, t, skVar)])
- of mOffsetOf:
- var dotExpr: PNode
- if e[1].kind == nkDotExpr:
- dotExpr = e[1]
- elif e[1].kind == nkCheckedFieldExpr:
- dotExpr = e[1][0]
- else:
- internalError(p.config, e.info, "unknown ast")
- let t = dotExpr[0].typ.skipTypes({tyTypeDesc})
- let tname = getTypeDesc(p.module, t, skVar)
- let member =
- if t.kind == tyTuple:
- "Field" & rope(dotExpr[1].sym.position)
- else: dotExpr[1].sym.loc.r
- putIntoDest(p,d,e, "((NI)offsetof($1, $2))" % [tname, member])
+ let t = e.sons[1].typ.skipTypes({tyTypeDesc})
+ putIntoDest(p, d, e.typ, "((NI)sizeof($1))" % [getTypeDesc(p.module, t)])
of mChr: genSomeCast(p, e, d)
of mOrd: genOrd(p, e, d)
of mLengthArray, mHigh, mLengthStr, mLengthSeq, mLengthOpenArray:
genArrayLen(p, e, d, op)
- of mGCref: unaryStmt(p, e, d, "if ($1) { #nimGCref($1); }$n")
- of mGCunref: unaryStmt(p, e, d, "if ($1) { #nimGCunref($1); }$n")
+ of mXLenStr, mXLenSeq:
+ if not p.module.compileToCpp:
+ unaryExpr(p, e, d, "($1->Sup.len)")
+ else:
+ unaryExpr(p, e, d, "$1->len")
+ of mGCref: unaryStmt(p, e, d, "#nimGCref($1);$n")
+ of mGCunref: unaryStmt(p, e, d, "#nimGCunref($1);$n")
of mSetLengthStr: genSetLengthStr(p, e, d)
of mSetLengthSeq: genSetLengthSeq(p, e, d)
of mIncl, mExcl, mCard, mLtSet, mLeSet, mEqSet, mMulSet, mPlusSet, mMinusSet,
mInSet:
genSetOp(p, e, d, op)
- of mNewString, mNewStringOfCap, mExit, mParseBiggestFloat:
- var opr = e[0].sym
- # Why would anyone want to set nodecl to one of these hardcoded magics?
- # - not sure, and it wouldn't work if the symbol behind the magic isn't
- # somehow forward-declared from some other usage, but it is *possible*
+ of mNewString, mNewStringOfCap, mCopyStr, mCopyStrLast, mExit,
+ mParseBiggestFloat:
+ var opr = e.sons[0].sym
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
- # 2 forward declarations. That happens because the magic symbol and the original
- # one that shall be used have different ids (even though a call to one is
- # actually a call to the other) so checking into m.declaredProtos with the 2 different ids doesn't work.
- # Why would 2 identical forward declarations be a problem?
- # - in the case of hot code-reloading we generate function pointers instead
- # of forward declarations and in C++ it is an error to redefine a global
- 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)
- # 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)])
+ discard cgsym(p.module, $opr.loc.r)
genCall(p, e, d)
- of mDefault, mZeroDefault: genDefault(p, e, d)
of mReset: genReset(p, e)
of mEcho: genEcho(p, e[1].skipConv)
of mArrToSeq: genArrToSeq(p, e, d)
of mNLen..mNError, mSlurp..mQuoteAst:
- localError(p.config, e.info, strutils.`%`(errXMustBeCompileTime, e[0].sym.name.s))
+ localError(e.info, errXMustBeCompileTime, e.sons[0].sym.name.s)
of mSpawn:
- when defined(leanCompiler):
- p.config.quitOrRaise "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)
+ let n = lowerings.wrapProcForSpawn(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"
- else:
- let n = semparallel.liftParallel(p.module.g.graph, p.module.idgen, p.module.module, e)
- expr(p, n, d)
+ let n = semparallel.liftParallel(p.module.module, e)
+ expr(p, n, d)
of mDeepCopy:
- if p.config.selectedGC in {gcArc, gcOrc} and optEnableDeepCopy notin p.config.globalOptions:
- localError(p.config, e.info,
- "for --gc:arc|orc 'deepcopy' support has to be enabled with --deepcopy:on")
-
var a, b: TLoc
let x = if e[1].kind in {nkAddr, nkHiddenAddr}: e[1][0] else: e[1]
initLocExpr(p, x, a)
- initLocExpr(p, e[2], b)
+ initLocExpr(p, e.sons[2], b)
genDeepCopy(p, a, b)
of mDotDot, mEqCString: genCall(p, e, d)
- of mWasMoved: genWasMoved(p, e)
- 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: internalError(e.info, "genMagicExpr: " & $op)
+
+proc genConstExpr(p: BProc, n: PNode): Rope
+proc handleConstExpr(p: BProc, n: PNode, d: var TLoc): bool =
+ if nfAllConst in n.flags and d.k == locNone and n.len > 0 and n.isDeepConstExpr:
+ var t = getUniqueType(n.typ)
+ discard getTypeDesc(p.module, t) # so that any fields are initialized
+ let id = nodeTableTestOrSet(p.module.dataCache, n, p.module.labels)
+ fillLoc(d, locData, t, p.module.tmpBase & rope(id), OnStatic)
+ if id == p.module.labels:
+ # expression not found in the cache:
+ inc(p.module.labels)
+ addf(p.module.s[cfsData], "NIM_CONST $1 $2 = $3;$n",
+ [getTypeDesc(p.module, t), d.r, genConstExpr(p, n)])
+ result = true
else:
- when defined(debugMagics):
- echo p.prc.name.s, " ", p.prc.id, " ", p.prc.flags, " ", p.prc.ast[genericParamsPos].kind
- internalError(p.config, e.info, "genMagicExpr: " & $op)
+ result = false
proc genSetConstr(p: BProc, e: PNode, d: var TLoc) =
# example: { a..b, c, d, e, f..g }
# we have to emit an expression of the form:
- # nimZeroMem(tmp, sizeof(tmp)); inclRange(tmp, a, b); incl(tmp, c);
+ # memset(tmp, 0, sizeof(tmp)); inclRange(tmp, a, b); incl(tmp, c);
# incl(tmp, d); incl(tmp, e); inclRange(tmp, f, g);
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.typ, genSetNode(p, e))
else:
if d.k == locNone: getTemp(p, e.typ, d)
- if getSize(p.config, e.typ) > 8:
+ if getSize(e.typ) > 8:
# big set:
- linefmt(p, cpsStmts, "#nimZeroMem($1, sizeof($2));$n",
- [rdLoc(d), getTypeDesc(p.module, e.typ)])
- for it in e.sons:
- if it.kind == nkRange:
- 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)
+ useStringh(p.module)
+ lineF(p, cpsStmts, "memset($1, 0, sizeof($1));$n", [rdLoc(d)])
+ for i in countup(0, sonsLen(e) - 1):
+ if e.sons[i].kind == nkRange:
+ getTemp(p, getSysType(tyInt), idx) # our counter
+ initLocExpr(p, e.sons[i].sons[0], a)
+ initLocExpr(p, e.sons[i].sons[1], b)
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(a, e.typ), rdSetElemLoc(b, e.typ)])
else:
- initLocExpr(p, it, a)
- var aa = newRopeAppender()
- rdSetElemLoc(p.config, a, e.typ, aa)
+ initLocExpr(p, e.sons[i], a)
lineF(p, cpsStmts, "$1[(NU)($2)>>3] |=(1U<<((NU)($2)&7U));$n",
- [rdLoc(d), aa])
+ [rdLoc(d), rdSetElemLoc(a, e.typ)])
else:
# small set
- var ts = "NU" & $(getSize(p.config, e.typ) * 8)
+ var ts = "NU" & $(getSize(e.typ) * 8)
lineF(p, cpsStmts, "$1 = 0;$n", [rdLoc(d)])
- for it in e.sons:
- if it.kind == nkRange:
- 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)
-
+ for i in countup(0, sonsLen(e) - 1):
+ if e.sons[i].kind == nkRange:
+ getTemp(p, getSysType(tyInt), idx) # our counter
+ initLocExpr(p, e.sons[i].sons[0], a)
+ initLocExpr(p, e.sons[i].sons[1], b)
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)])
+ "$2 |=((" & ts & ")(1)<<(($1)%(sizeof(" & ts & ")*8)));$n", [
+ rdLoc(idx), rdLoc(d), rdSetElemLoc(a, e.typ),
+ rdSetElemLoc(b, e.typ)])
else:
- initLocExpr(p, it, a)
- var aa = newRopeAppender()
- rdSetElemLoc(p.config, a, e.typ, aa)
+ initLocExpr(p, e.sons[i], a)
lineF(p, cpsStmts,
- "$1 |=(($3)(1)<<(($2)%(sizeof($3)*8)));$n",
- [rdLoc(d), aa, rope(ts)])
+ "$1 |=((" & ts & ")(1)<<(($2)%(sizeof(" & ts & ")*8)));$n",
+ [rdLoc(d), rdSetElemLoc(a, e.typ)])
proc genTupleConstr(p: BProc, n: PNode, d: var TLoc) =
var rec: TLoc
if not handleConstExpr(p, n, d):
- let t = n.typ
+ var t = getUniqueType(n.typ)
discard getTypeDesc(p.module, t) # so that any fields are initialized
if d.k == locNone: getTemp(p, t, d)
- for i in 0.. 0: exprOrStmt
- if frameName != "":
- p.s(cpsStmts).add deinitFrameNoDebug(p, frameName)
-
proc genStmtListExpr(p: BProc, n: PNode, d: var TLoc) =
- genStmtListExprImpl:
- expr(p, n[^1], d)
-
-proc genStmtList(p: BProc, n: PNode) =
- genStmtListExprImpl:
- genStmts(p, n[^1])
-
-from parampatterns import isLValue
+ var length = sonsLen(n)
+ for i in countup(0, length - 2): genStmts(p, n.sons[i])
+ if length > 0: expr(p, n.sons[length - 1], d)
proc upConv(p: BProc, n: PNode, d: var TLoc) =
var a: TLoc
- initLocExpr(p, n[0], a)
- let dest = skipTypes(n.typ, abstractPtrs)
+ initLocExpr(p, n.sons[0], a)
+ var 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 r = rdLoc(a)
+ var nilCheck: Rope = nil
+ var t = skipTypes(a.t, abstractInst)
+ while t.kind in {tyVar, tyPtr, tyRef}:
+ if t.kind != tyVar: nilCheck = r
+ if t.kind != tyVar or not p.module.compileToCpp:
+ r = "(*$1)" % [r]
+ t = skipTypes(t.lastSon, abstractInst)
+ if not p.module.compileToCpp:
+ while t.kind == tyObject and t.sons[0] != nil:
+ add(r, ".Sup")
+ t = skipTypes(t.sons[0], abstractInst)
+ if nilCheck != nil:
+ linefmt(p, cpsStmts, "if ($1) #chckObj($2.m_type, $3);$n",
+ nilCheck, r, genTypeInfo(p.module, dest))
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", []
-
- if n[0].typ.kind != tyObject:
- if n.isLValue:
- putIntoDest(p, d, n,
- "(*(($1*) (&($2))))" % [getTypeDesc(p.module, n.typ), rdLoc(a)], a.storage)
- else:
- putIntoDest(p, d, n,
- "(($1) ($2))" % [getTypeDesc(p.module, n.typ), rdLoc(a)], a.storage)
+ linefmt(p, cpsStmts, "#chckObj($1.m_type, $2);$n",
+ r, genTypeInfo(p.module, dest))
+ if n.sons[0].typ.kind != tyObject:
+ putIntoDest(p, d, n.typ,
+ "(($1) ($2))" % [getTypeDesc(p.module, n.typ), rdLoc(a)], a.s)
else:
- putIntoDest(p, d, n, "(*($1*) ($2))" %
- [getTypeDesc(p.module, dest), addrLoc(p.config, a)], a.storage)
+ putIntoDest(p, d, n.typ, "(*($1*) ($2))" %
+ [getTypeDesc(p.module, dest), addrLoc(a)], a.s)
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:
+ expr(p, n.sons[0], d) # downcast does C++ for us
else:
+ var dest = skipTypes(n.typ, abstractPtrs)
+
+ var arg = n.sons[0]
+ while arg.kind == nkObjDownConv: arg = arg.sons[0]
+
+ var src = skipTypes(arg.typ, abstractPtrs)
var a: TLoc
initLocExpr(p, arg, a)
- var r = rdLoc(a) & (if isRef: "->Sup" else: ".Sup")
- for i in 2..abs(inheritanceDiff(dest, src)): r.add(".Sup")
- putIntoDest(p, d, n, if isRef: "&" & r else: r, a.storage)
+ var r = rdLoc(a)
+ let isRef = skipTypes(arg.typ, abstractInst).kind in {tyRef, tyPtr, tyVar}
+ if isRef:
+ add(r, "->Sup")
+ else:
+ add(r, ".Sup")
+ for i in countup(2, abs(inheritanceDiff(dest, src))): add(r, ".Sup")
+ 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, abstractInst).kind in {tyRef, tyPtr, tyVar}:
+ getTemp(p, n.typ, d)
+ linefmt(p, cpsStmts, "$1 = &$2;$n", rdLoc(d), r)
+ else:
+ r = "&" & r
+ putIntoDest(p, d, n.typ, r, a.s)
+ else:
+ putIntoDest(p, d, n.typ, r, a.s)
proc exprComplexConst(p: BProc, n: PNode, d: var TLoc) =
- let t = n.typ
+ var t = getUniqueType(n.typ)
discard getTypeDesc(p.module, t) # so that any fields are initialized
let id = nodeTableTestOrSet(p.module.dataCache, n, p.module.labels)
let tmp = p.module.tmpBase & rope(id)
@@ -2845,179 +1959,87 @@ 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", [])
+ addf(p.module.s[cfsData], "NIM_CONST $1 $2 = $3;$n",
+ [getTypeDesc(p.module, t), tmp, genConstExpr(p, n)])
if d.k == locNone:
- fillLoc(d, locData, n, tmp, OnStatic)
+ fillLoc(d, locData, t, tmp, OnStatic)
else:
- putDataIntoDest(p, d, n, tmp)
- # This fixes bug #4551, but we really need better dataflow
- # analysis to make this 100% safe.
- 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)
+ putDataIntoDest(p, d, t, tmp)
proc expr(p: BProc, n: PNode, d: var TLoc) =
- when defined(nimCompilerStacktraceHints):
- setFrameMsg p.config$n.info & " " & $n.kind
- p.currLineInfo = n.info
-
case n.kind
of nkSym:
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)
+ fillProcLoc(sym)
genProcPrototype(p.module, sym)
else:
genProc(p.module, sym)
putLocIntoDest(p, d, sym.loc)
- of skProc, skConverter, skIterator, skFunc:
+ of skProc, skConverter, skIterator:
#if sym.kind == skIterator:
# echo renderTree(sym.getBody, {renderIds})
if sfCompileTime in sym.flags:
- localError(p.config, n.info, "request to generate code for .compileTime proc: " &
+ localError(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:
- internalError(p.config, n.info, "expr: proc not init " & sym.name.s)
+ genProc(p.module, sym)
+ if sym.loc.r == nil or sym.loc.t == nil:
+ internalError(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))
+ if sfFakeConst in sym.flags:
+ if sfGlobal in sym.flags: genVarPrototype(p.module, sym)
putLocIntoDest(p, d, sym.loc)
+ elif isSimpleConst(sym.typ):
+ putIntoDest(p, d, n.typ, genLiteral(p, sym.ast, sym.typ), OnStatic)
else:
genComplexConst(p, sym, d)
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
- # remains for robustness
- putIntoDest(p, d, n, rope(sym.position))
+ putIntoDest(p, d, n.typ, rope(sym.position))
of skVar, skForVar, skResult, skLet:
- if {sfGlobal, sfThread} * sym.flags != {}:
- genVarPrototype(p.module, n)
- if sfCompileTime in sym.flags:
- genSingleVar(p, sym, n, astdef(sym))
-
- if sym.loc.r == "" or sym.loc.t == nil:
+ if sfGlobal in sym.flags: genVarPrototype(p.module, sym)
+ 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
+ internalError n.info, "expr: var not init " & sym.name.s & "_" & $sym.id
if sfThread in sym.flags:
accessThreadLocalVar(p, sym)
- if emulatedThreadVars(p.config):
- putIntoDest(p, d, sym.loc.lode, "NimTV_->" & sym.loc.r)
+ if emulatedThreadVars():
+ putIntoDest(p, d, sym.loc.t, "NimTV->" & sym.loc.r)
else:
putLocIntoDest(p, d, sym.loc)
else:
putLocIntoDest(p, d, sym.loc)
of skTemp:
- when false:
- # this is more harmful than helpful.
- if sym.loc.r == "":
- # we now support undeclared 'skTemp' variables for easier
- # transformations in other parts of the compiler:
- assignLocalVar(p, n)
- if sym.loc.r == "" 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)
+ internalError(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})
- internalError(p.config, n.info, "expr: param not init " & sym.name.s & "_" & $sym.id)
+ internalError(n.info, "expr: param not init " & sym.name.s & "_" & $sym.id)
putLocIntoDest(p, d, sym.loc)
- else: internalError(p.config, n.info, "expr(" & $sym.kind & "); unknown symbol")
+ else: internalError(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.typ, 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.typ, genLiteral(p, n))
+ of nkIntLit..nkUInt64Lit,
+ nkFloatLit..nkFloat128Lit, nkCharLit:
+ putIntoDest(p, d, n.typ, genLiteral(p, n))
of nkCall, nkHiddenCallConv, nkInfix, nkPrefix, nkPostfix, nkCommand,
nkCallStrLit:
- genLineDir(p, n) # may be redundant, it is generated in fixupCall as well
- let op = n[0]
+ genLineDir(p, n)
+ let op = n.sons[0]
if n.typ.isNil:
# discard the value:
var a: TLoc
@@ -3033,9 +2055,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.typ, genSetNode(p, n))
else:
genSetConstr(p, n, d)
of nkBracket:
@@ -3045,87 +2065,66 @@ proc expr(p: BProc, n: PNode, d: var TLoc) =
genSeqConstr(p, n, d)
else:
genArrayConstr(p, n, d)
- of nkPar, nkTupleConstr:
- if n.typ != nil and n.typ.kind == tyProc and n.len == 2:
- genClosure(p, n, d)
- elif isDeepConstExpr(n) and n.len != 0:
+ of nkPar:
+ if isDeepConstExpr(n) and n.len != 0:
exprComplexConst(p, n, d)
else:
genTupleConstr(p, n, d)
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)
of nkCheckedFieldExpr: genCheckedRecordField(p, n, d)
of nkBlockExpr, nkBlockStmt: genBlock(p, n, d)
of nkStmtListExpr: genStmtListExpr(p, n, d)
- of nkStmtList: genStmtList(p, n)
+ of nkStmtList:
+ for i in countup(0, sonsLen(n) - 1): genStmts(p, n.sons[i])
of nkIfExpr, nkIfStmt: genIf(p, n, d)
of nkWhen:
# This should be a "when nimvm" node.
- expr(p, n[1][0], d)
+ expr(p, n.sons[1].sons[0], d)
of nkObjDownConv: downConv(p, n, d)
of nkObjUpConv: upConv(p, n, d)
- of nkChckRangeF, nkChckRange64, nkChckRange: genRangeChck(p, n, d)
+ of nkChckRangeF: genRangeChck(p, n, d, "chckRangeF")
+ of nkChckRange64: genRangeChck(p, n, d, "chckRange64")
+ of nkChckRange: genRangeChck(p, n, d, "chckRange")
of nkStringToCString: convStrToCStr(p, n, d)
of nkCStringToString: convCStrToStr(p, n, d)
of nkLambdaKinds:
- var sym = n[namePos].sym
+ var sym = n.sons[namePos].sym
genProc(p.module, sym)
- if sym.loc.r == "" or sym.loc.lode == nil:
- internalError(p.config, n.info, "expr: proc not init " & sym.name.s)
+ if sym.loc.r == nil or sym.loc.t == nil:
+ internalError(n.info, "expr: proc not init " & sym.name.s)
putLocIntoDest(p, d, sym.loc)
of nkClosure: genClosure(p, n, d)
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 nkForStmt: internalError(p.config, n.info, "for statement not eliminated")
+ of nkConstSection: genConstStmt(p, n)
+ of nkForStmt: internalError(n.info, "for statement not eliminated")
of nkCaseStmt: genCase(p, n, d)
of nkReturnStmt: genReturnStmt(p, n)
of nkBreakStmt: genBreakStmt(p, n)
- of nkAsgn:
- cow(p, n[1])
- if nfPreventCg notin n.flags:
- genAsgn(p, n, fastAsgn=false)
- of nkFastAsgn, nkSinkAsgn:
- cow(p, n[1])
- if nfPreventCg notin n.flags:
- # transf is overly aggressive with 'nkFastAsgn', so we work around here.
- # See tests/run/tcnstseq3 for an example that would fail otherwise.
- genAsgn(p, n, fastAsgn=p.prc != nil)
+ of nkAsgn: genAsgn(p, n, fastAsgn=false)
+ of nkFastAsgn:
+ # transf is overly aggressive with 'nkFastAsgn', so we work around here.
+ # See tests/run/tcnstseq3 for an example that would fail otherwise.
+ genAsgn(p, n, fastAsgn=p.prc != nil)
of nkDiscardStmt:
- let ex = n[0]
- if ex.kind != nkEmpty:
+ if n.sons[0].kind != nkEmpty:
genLineDir(p, n)
var a: TLoc
- initLocExprSingleUse(p, ex, a)
- line(p, cpsStmts, "(void)(" & a.r & ");\L")
+ initLocExpr(p, n.sons[0], a)
of nkAsmStmt: genAsmStmt(p, n)
- of nkTryStmt, nkHiddenTryStmt:
- case p.config.exc
- of excGoto:
- genTryGoto(p, n, d)
- of excCpp:
+ of nkTryStmt:
+ if p.module.compileToCpp and optNoCppExceptions notin gGlobalOptions:
genTryCpp(p, n, d)
else:
- genTrySetjmp(p, n, d)
+ genTry(p, n, d)
of nkRaiseStmt: genRaiseStmt(p, n)
of nkTypeSection:
# we have to emit the type information for object types here to support
@@ -3133,344 +2132,80 @@ proc expr(p: BProc, n: PNode, d: var TLoc) =
genTypeSection(p.module, n)
of nkCommentStmt, nkIteratorDef, nkIncludeStmt,
nkImportStmt, nkImportExceptStmt, nkExportStmt, nkExportExceptStmt,
- nkFromStmt, nkTemplateDef, nkMacroDef, nkStaticStmt:
+ nkFromStmt, nkTemplateDef, nkMacroDef:
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 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:
- if ({sfExportc, sfCompilerProc} * prc.flags == {sfExportc}) or
+ of nkPragmaBlock: expr(p, n.lastSon, d)
+ of nkProcDef, nkMethodDef, nkConverterDef:
+ if n.sons[genericParamsPos].kind == nkEmpty:
+ var prc = n.sons[namePos].sym
+ # 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 (optDeadCodeElim notin gGlobalOptions and
+ sfDeadCodeElim notin getModule(prc).flags) or
+ ({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)
+ # we have not only the header:
+ if prc.getBody.kind != nkEmpty or lfDynamicLib in prc.loc.flags:
+ genProc(p.module, prc)
of nkParForStmt: genParForStmt(p, n)
of nkState: genState(p, n)
- of nkGotoState:
- # simply never set it back to 0 here from here on...
- 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")
+ of nkGotoState: genGotoState(p, n)
+ of nkBreakState: genBreakState(p, n)
+ else: internalError(n.info, "expr(" & $n.kind & "); unknown node kind")
-proc getDefaultValue(p: BProc; typ: PType; info: TLineInfo; result: var 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,
- tyTyped, tyTypeDesc, tyStatic, tyRef, tyNil:
- result.add rope"NIM_NIL"
- of tyString, tySequence:
- if optSeqDestructors in p.config.globalOptions:
- result.add "{0, NIM_NIL}"
- else:
- result.add "NIM_NIL"
- of tyProc:
- if t.callConv != ccClosure:
- result.add "NIM_NIL"
- else:
- result.add "{NIM_NIL, NIM_NIL}"
- of tyObject:
- var count = 0
- result.add "{"
- getNullValueAuxT(p, t, t, t.n, nil, result, count, true, info)
- result.add "}"
- of tyTuple:
- result.add "{"
- if p.vccAndC and t.len == 0:
- result.add "0"
- for i in 0.. 0: result.add ", "
- getDefaultValue(p, t[i], info, result)
- result.add "}"
- of tyArray:
- result.add "{"
- for i in 0.. 0: result.add ", "
- getDefaultValue(p, t.sons[1], info, result)
- result.add "}"
- #result = rope"{}"
- of tyOpenArray, tyVarargs:
- result.add "{NIM_NIL, 0}"
- of tySet:
- if mapSetType(p.config, t) == ctArray: result.add "{}"
- else: result.add "0"
- else:
- globalError(p.config, info, "cannot create null element for: " & $t.kind)
+proc genNamedConstExpr(p: BProc, n: PNode): Rope =
+ if n.kind == nkExprColonExpr: result = genConstExpr(p, n.sons[1])
+ else: result = genConstExpr(p, n)
-proc caseObjDefaultBranch(obj: PNode; branch: Int128): int =
- for i in 1 ..< obj.len:
- for j in 0 .. obj[i].len - 2:
- if obj[i][j].kind == nkRange:
- let x = getOrdValue(obj[i][j][0])
- let y = getOrdValue(obj[i][j][1])
- if branch >= x and branch <= y:
- return i
- elif getOrdValue(obj[i][j]) == branch:
- return i
- if obj[i].len == 1:
- # else branch
- return i
- assert(false, "unreachable")
+proc genConstSimpleList(p: BProc, n: PNode): Rope =
+ var length = sonsLen(n)
+ result = rope("{")
+ for i in countup(ord(n.kind == nkObjConstr), length - 2):
+ addf(result, "$1,$n", [genNamedConstExpr(p, n.sons[i])])
+ if length > 0: add(result, genNamedConstExpr(p, n.sons[length - 1]))
+ addf(result, "}$n", [])
-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) =
- case obj.kind
- of nkRecList:
- for it in obj.sons:
- getNullValueAux(p, t, it, constOrNil, result, count, isConst, info)
- of nkRecCase:
- getNullValueAux(p, t, obj[0], constOrNil, result, count, isConst, info)
- if count > 0: result.add ", "
- var branch = Zero
- if constOrNil != nil:
- ## find kind value, default is zero if not specified
- for i in 1.. 0: result.add ", "
- inc count
- let field = obj.sym
- 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)
- 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"
- 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)
- result.add("}\n")
-
-proc genConstSeq(p: BProc, n: PNode, t: PType; isConst: bool; result: var Rope) =
- var data = "{{$1, $1 | NIM_STRLIT_FLAG}" % [n.len.rope]
- let base = t.skipTypes(abstractInst)[0]
+proc genConstSeq(p: BProc, n: PNode, t: PType): Rope =
+ var data = "{{$1, $1}" % [n.len.rope]
if n.len > 0:
# array part needs extra curlies:
data.add(", {")
- for i in 0.. 0: data.addf(",$n", [])
- genBracedInit(p, n[i], isConst, base, data)
+ data.add genConstExpr(p, n.sons[i])
data.add("}")
data.add("}")
- let tmpName = getTempName(p.module)
+ result = getTempName(p.module)
- appcg(p.module, cfsStrData,
- "static $5 struct {$n" &
+ appcg(p.module, cfsData,
+ "NIM_CONST struct {$n" &
" #TGenericSeq Sup;$n" &
" $1 data[$2];$n" &
"} $3 = $4;$n", [
- getTypeDesc(p.module, base), n.len, tmpName, data,
- if isConst: "NIM_CONST" else: ""])
+ getTypeDesc(p.module, t.sons[0]), n.len.rope, result, data])
- 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) =
- 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("}")
-
- let payload = getTempName(p.module)
-
- appcg(p.module, cfsStrData,
- "static $5 struct {$n" &
- " NI cap; $1 data[$2];$n" &
- "} $3 = {$2 | NIM_STRLIT_FLAG, $4};$n", [
- getTypeDesc(p.module, base), n.len, payload, data,
- if isConst: "const" else: ""])
- result.add "{$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 genConstExpr(p: BProc, n: PNode): Rope =
case n.kind
of nkHiddenStdConv, nkHiddenSubConv:
- genBracedInit(p, n[1], isConst, n.typ, result)
+ result = genConstExpr(p, n.sons[1])
+ of nkCurly:
+ var cs: TBitSet
+ toBitSet(n, cs)
+ result = genRawSetData(cs, int(getSize(n.typ)))
+ of nkBracket, nkPar, nkClosure, nkObjConstr:
+ var t = skipTypes(n.typ, abstractInst)
+ if t.kind == tySequence:
+ result = genConstSeq(p, n, t)
+ else:
+ result = genConstSimpleList(p, n)
else:
- var ty = tyNone
- var typ: PType = nil
- if optionalType == nil:
- if n.kind in nkStrKinds:
- ty = tyString
- else:
- internalError(p.config, n.info, "node has no type")
- else:
- typ = skipTypes(optionalType, abstractInstOwned + {tyStatic})
- ty = typ.kind
- case ty
- of tySet:
- let cs = toBitSet(p.config, n)
- genRawSetData(cs, int(getSize(p.config, n.typ)), result)
- of tySequence:
- if optSeqDestructors in p.config.globalOptions:
- genConstSeqV2(p, n, typ, isConst, result)
- else:
- genConstSeq(p, n, typ, isConst, result)
- of tyProc:
- if typ.callConv == ccClosure and n.safeLen > 1 and n[1].kind == nkNilLit:
- # n.kind could be: nkClosure, nkTupleConstr and maybe others; `n.safeLen`
- # guards against the case of `nkSym`, refs bug #14340.
- # Conversion: nimcall -> closure.
- # this hack fixes issue that nkNilLit is expanded to {NIM_NIL,NIM_NIL}
- # this behaviour is needed since closure_var = nil must be
- # expanded to {NIM_NIL,NIM_NIL}
- # in VM closures are initialized with nkPar(nkNilLit, nkNilLit)
- # 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}"
- else:
- var d: TLoc
- initLocExpr(p, n[0], d)
- result.add "{(($1) $2),NIM_NIL}" % [getClosureType(p.module, typ, clHalfWithEnv), rdLoc(d)]
- else:
- var d: TLoc
- initLocExpr(p, n, d)
- result.add rdLoc(d)
- of tyArray, tyVarargs:
- genConstSimpleList(p, n, isConst, result)
- of tyTuple:
- genConstTuple(p, n, isConst, typ, result)
- of tyOpenArray:
- if n.kind != nkBracket:
- internalError(p.config, n.info, "const openArray expression is not an array construction")
-
- var data = newRopeAppender()
- genConstSimpleList(p, n, isConst, data)
-
- let payload = getTempName(p.module)
- let ctype = getTypeDesc(p.module, typ[0])
- let arrLen = n.len
- appcg(p.module, cfsStrData,
- "static $5 $1 $3[$2] = $4;$n", [
- ctype, arrLen, payload, data,
- if isConst: "const" else: ""])
- result.add "{($1*)&$2, $3}" % [ctype, payload, rope arrLen]
-
- of tyObject:
- genConstObjConstr(p, n, isConst, result)
- of tyString, tyCstring:
- if optSeqDestructors in p.config.globalOptions and n.kind != nkNilLit and ty == tyString:
- genStringLiteralV2Const(p.module, n, isConst, result)
- else:
- var d: TLoc
- initLocExpr(p, n, d)
- result.add rdLoc(d)
- else:
- var d: TLoc
- initLocExpr(p, n, d)
- result.add rdLoc(d)
+ var d: TLoc
+ initLocExpr(p, n, d)
+ result = rdLoc(d)
diff --git a/compiler/ccgliterals.nim b/compiler/ccgliterals.nim
deleted file mode 100644
index cbef6771f..000000000
--- a/compiler/ccgliterals.nim
+++ /dev/null
@@ -1,118 +0,0 @@
-#
-#
-# The Nim Compiler
-# (c) Copyright 2018 Andreas Rumpf
-#
-# See the file "copying.txt", included in this
-# distribution, for details about the copyright.
-#
-
-# included from cgen.nim
-
-## This include file contains the logic to produce constant string
-## and seq literals. The code here is responsible that
-## ``const x = ["a", "b"]`` works without hidden runtime creation code.
-## The price is that seqs and strings are not purely a library
-## implementation.
-
-template detectVersion(field, corename) =
- if m.g.field == 0:
- let core = getCompilerProc(m.g.graph, corename)
- if core == nil or core.kind != skConst:
- m.g.field = 1
- else:
- m.g.field = toInt(ast.getInt(core.astdef))
- result = m.g.field
-
-proc detectStrVersion(m: BModule): int =
- detectVersion(strVersion, "nimStrVersion")
-
-proc detectSeqVersion(m: BModule): int =
- detectVersion(seqVersion, "nimSeqVersion")
-
-# ----- 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 genStringLiteralV1(m: BModule; n: PNode; result: var Rope) =
- if s.isNil:
- appcg(m, result, "((#NimStringDesc*) NIM_NIL)", [])
- else:
- let id = nodeTableTestOrSet(m.dataCache, n, m.labels)
- if id == m.labels:
- # string literal not found in the cache:
- appcg(m, result, "((#NimStringDesc*) &", [])
- genStringLiteralDataOnlyV1(m, n.strVal, result)
- result.add ")"
- else:
- appcg(m, result, "((#NimStringDesc*) &$1$2)",
- [m.tmpBase, id])
-
-# ------ Version 2: destructor based strings and seqs -----------------------
-
-proc genStringLiteralDataOnlyV2(m: BModule, s: string; result: Rope; isConst: bool) =
- m.s[cfsStrData].addf("static $4 struct {$n" &
- " NI cap; NIM_CHAR data[$2+1];$n" &
- "} $1 = { $2 | NIM_STRLIT_FLAG, $3 };$n",
- [result, rope(s.len), makeCString(s),
- rope(if isConst: "const" else: "")])
-
-proc genStringLiteralV2(m: BModule; n: PNode; isConst: bool; result: var Rope) =
- let id = nodeTableTestOrSet(m.dataCache, n, m.labels)
- if id == m.labels:
- let pureLit = getTempName(m)
- genStringLiteralDataOnlyV2(m, n.strVal, pureLit, isConst)
- let tmp = getTempName(m)
- result.add tmp
- cgsym(m, "NimStrPayload")
- 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: "")])
- 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),
- rope(if isConst: "const" else: "")])
-
-proc genStringLiteralV2Const(m: BModule; n: PNode; isConst: bool; result: var Rope) =
- let id = nodeTableTestOrSet(m.dataCache, n, m.labels)
- var pureLit: Rope
- if id == m.labels:
- pureLit = getTempName(m)
- cgsym(m, "NimStrPayload")
- cgsym(m, "NimStringV2")
- # string literal not found in the cache:
- genStringLiteralDataOnlyV2(m, n.strVal, pureLit, isConst)
- else:
- pureLit = m.tmpBase & rope(id)
- result.addf "{$1, (NimStrPayload*)&$2}", [rope(n.strVal.len), pureLit]
-
-# ------ Version selector ---------------------------------------------------
-
-proc genStringLiteralDataOnly(m: BModule; s: string; info: TLineInfo;
- isConst: bool; result: var Rope) =
- case detectStrVersion(m)
- of 0, 1: genStringLiteralDataOnlyV1(m, s, result)
- of 2:
- let tmp = getTempName(m)
- genStringLiteralDataOnlyV2(m, s, tmp, isConst)
- result.add tmp
- else:
- localError(m.config, info, "cannot determine how to produce code for string literal")
-
-proc genNilStringLiteral(m: BModule; info: TLineInfo; result: var Rope) =
- appcg(m, result, "((#NimStringDesc*) NIM_NIL)", [])
-
-proc genStringLiteral(m: BModule; n: PNode; result: var Rope) =
- case detectStrVersion(m)
- of 0, 1: genStringLiteralV1(m, n, result)
- of 2: genStringLiteralV2(m, n, isConst = true, result)
- else:
- localError(m.config, n.info, "cannot determine how to produce code for string literal")
diff --git a/compiler/ccgmerge.nim b/compiler/ccgmerge.nim
new file mode 100644
index 000000000..2e77cd2a6
--- /dev/null
+++ b/compiler/ccgmerge.nim
@@ -0,0 +1,299 @@
+#
+#
+# The Nim Compiler
+# (c) Copyright 2012 Andreas Rumpf
+#
+# See the file "copying.txt", included in this
+# distribution, for details about the copyright.
+#
+
+## This module implements the merge operation of 2 different C files. This
+## is needed for incremental compilation.
+
+import
+ ast, astalgo, ropes, options, strutils, nimlexbase, msgs, cgendata, rodutils,
+ intsets, platform, llstream
+
+# Careful! Section marks need to contain a tabulator so that they cannot
+# be part of C string literals.
+
+const
+ CFileSectionNames: array[TCFileSection, string] = [
+ cfsMergeInfo: "",
+ cfsHeaders: "NIM_merge_HEADERS",
+ 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",
+ cfsVars: "NIM_merge_VARS",
+ cfsProcs: "NIM_merge_PROCS",
+ cfsInitProc: "NIM_merge_INIT_PROC",
+ cfsTypeInit1: "NIM_merge_TYPE_INIT1",
+ cfsTypeInit2: "NIM_merge_TYPE_INIT2",
+ cfsTypeInit3: "NIM_merge_TYPE_INIT3",
+ cfsDebugInit: "NIM_merge_DEBUG_INIT",
+ cfsDynLibInit: "NIM_merge_DYNLIB_INIT",
+ cfsDynLibDeinit: "NIM_merge_DYNLIB_DEINIT",
+ ]
+ CProcSectionNames: array[TCProcSection, string] = [
+ cpsLocals: "NIM_merge_PROC_LOCALS",
+ cpsInit: "NIM_merge_PROC_INIT",
+ cpsStmts: "NIM_merge_PROC_BODY"
+ ]
+ NimMergeEndMark = "/*\tNIM_merge_END:*/"
+
+proc genSectionStart*(fs: TCFileSection): Rope =
+ if compilationCachePresent:
+ result = rope(tnl)
+ add(result, "/*\t")
+ add(result, CFileSectionNames[fs])
+ add(result, ":*/")
+ add(result, tnl)
+
+proc genSectionEnd*(fs: TCFileSection): Rope =
+ if compilationCachePresent:
+ result = rope(NimMergeEndMark & tnl)
+
+proc genSectionStart*(ps: TCProcSection): Rope =
+ if compilationCachePresent:
+ result = rope(tnl)
+ add(result, "/*\t")
+ add(result, CProcSectionNames[ps])
+ add(result, ":*/")
+ add(result, tnl)
+
+proc genSectionEnd*(ps: TCProcSection): Rope =
+ if compilationCachePresent:
+ result = rope(NimMergeEndMark & tnl)
+
+proc writeTypeCache(a: TIdTable, s: var string) =
+ var i = 0
+ for id, value in pairs(a):
+ if i == 10:
+ i = 0
+ s.add(tnl)
+ else:
+ s.add(' ')
+ encodeVInt(id, s)
+ s.add(':')
+ encodeStr($Rope(value), s)
+ inc i
+ s.add('}')
+
+proc writeIntSet(a: IntSet, s: var string) =
+ var i = 0
+ for x in items(a):
+ if i == 10:
+ i = 0
+ s.add(tnl)
+ else:
+ s.add(' ')
+ encodeVInt(x, s)
+ inc i
+ s.add('}')
+
+proc genMergeInfo*(m: BModule): Rope =
+ if optSymbolFiles notin gGlobalOptions: return nil
+ var s = "/*\tNIM_merge_INFO:"
+ s.add(tnl)
+ s.add("typeCache:{")
+ writeTypeCache(m.typeCache, s)
+ s.add("declared:{")
+ writeIntSet(m.declaredThings, s)
+ s.add("typeInfo:{")
+ writeIntSet(m.typeInfoMarker, s)
+ s.add("labels:")
+ encodeVInt(m.labels, s)
+ s.add(" flags:")
+ encodeVInt(cast[int](m.flags), s)
+ s.add(tnl)
+ s.add("*/")
+ result = s.rope
+
+template `^`(pos: int): expr = L.buf[pos]
+
+proc skipWhite(L: var TBaseLexer) =
+ var pos = L.bufpos
+ while true:
+ case ^pos
+ of CR: pos = nimlexbase.handleCR(L, pos)
+ of LF: pos = nimlexbase.handleLF(L, pos)
+ of ' ': inc pos
+ else: break
+ L.bufpos = pos
+
+proc skipUntilCmd(L: var TBaseLexer) =
+ var pos = L.bufpos
+ while true:
+ case ^pos
+ of CR: pos = nimlexbase.handleCR(L, pos)
+ of LF: pos = nimlexbase.handleLF(L, pos)
+ of '\0': break
+ of '/':
+ if ^(pos+1) == '*' and ^(pos+2) == '\t':
+ inc pos, 3
+ break
+ inc pos
+ else: inc pos
+ L.bufpos = pos
+
+proc atEndMark(buf: cstring, pos: int): bool =
+ var s = 0
+ while s < NimMergeEndMark.len and buf[pos+s] == NimMergeEndMark[s]: inc s
+ result = s == NimMergeEndMark.len
+
+proc readVerbatimSection(L: var TBaseLexer): Rope =
+ var pos = L.bufpos
+ var buf = L.buf
+ var r = newStringOfCap(30_000)
+ while true:
+ case buf[pos]
+ of CR:
+ pos = nimlexbase.handleCR(L, pos)
+ buf = L.buf
+ r.add(tnl)
+ of LF:
+ pos = nimlexbase.handleLF(L, pos)
+ buf = L.buf
+ r.add(tnl)
+ of '\0':
+ internalError("ccgmerge: expected: " & NimMergeEndMark)
+ break
+ else:
+ if atEndMark(buf, pos):
+ inc pos, NimMergeEndMark.len
+ break
+ r.add(buf[pos])
+ inc pos
+ L.bufpos = pos
+ result = r.rope
+
+proc readKey(L: var TBaseLexer, result: var string) =
+ var pos = L.bufpos
+ var buf = L.buf
+ setLen(result, 0)
+ while buf[pos] in IdentChars:
+ result.add(buf[pos])
+ inc pos
+ if buf[pos] != ':': internalError("ccgmerge: ':' expected")
+ L.bufpos = pos + 1 # skip ':'
+
+proc newFakeType(id: int): PType =
+ new(result)
+ result.id = id
+
+proc readTypeCache(L: var TBaseLexer, result: var TIdTable) =
+ if ^L.bufpos != '{': internalError("ccgmerge: '{' expected")
+ inc L.bufpos
+ while ^L.bufpos != '}':
+ skipWhite(L)
+ var key = decodeVInt(L.buf, L.bufpos)
+ if ^L.bufpos != ':': internalError("ccgmerge: ':' expected")
+ inc L.bufpos
+ var value = decodeStr(L.buf, L.bufpos)
+ # XXX little hack: we create a "fake" type object with the correct Id
+ # better would be to adapt the data structure to not even store the
+ # object as key, but only the Id
+ idTablePut(result, newFakeType(key), value.rope)
+ inc L.bufpos
+
+proc readIntSet(L: var TBaseLexer, result: var IntSet) =
+ if ^L.bufpos != '{': internalError("ccgmerge: '{' expected")
+ inc L.bufpos
+ while ^L.bufpos != '}':
+ skipWhite(L)
+ var key = decodeVInt(L.buf, L.bufpos)
+ result.incl(key)
+ inc L.bufpos
+
+proc processMergeInfo(L: var TBaseLexer, m: BModule) =
+ var k = newStringOfCap("typeCache".len)
+ while true:
+ skipWhite(L)
+ if ^L.bufpos == '*' and ^(L.bufpos+1) == '/':
+ inc(L.bufpos, 2)
+ break
+ readKey(L, k)
+ case k
+ of "typeCache": readTypeCache(L, m.typeCache)
+ of "declared": readIntSet(L, m.declaredThings)
+ of "typeInfo": readIntSet(L, m.typeInfoMarker)
+ of "labels": m.labels = decodeVInt(L.buf, L.bufpos)
+ of "flags":
+ m.flags = cast[set[CodegenFlag]](decodeVInt(L.buf, L.bufpos) != 0)
+ else: internalError("ccgmerge: unknown key: " & k)
+
+when not defined(nimhygiene):
+ {.pragma: inject.}
+
+template withCFile(cfilename: string, body: stmt) {.immediate.} =
+ var s = llStreamOpen(cfilename, fmRead)
+ if s == nil: return
+ var L {.inject.}: TBaseLexer
+ openBaseLexer(L, s)
+ var k {.inject.} = newStringOfCap("NIM_merge_FORWARD_TYPES".len)
+ while true:
+ skipUntilCmd(L)
+ if ^L.bufpos == '\0': break
+ body
+ closeBaseLexer(L)
+
+proc readMergeInfo*(cfilename: string, m: BModule) =
+ ## reads the merge meta information into `m`.
+ withCFile(cfilename):
+ readKey(L, k)
+ if k == "NIM_merge_INFO":
+ processMergeInfo(L, m)
+ break
+
+type
+ TMergeSections = object
+ f: TCFileSections
+ p: TCProcSections
+
+proc readMergeSections(cfilename: string, m: var TMergeSections) =
+ ## reads the merge sections into `m`.
+ withCFile(cfilename):
+ readKey(L, k)
+ if k == "NIM_merge_INFO":
+ discard
+ elif ^L.bufpos == '*' and ^(L.bufpos+1) == '/':
+ inc(L.bufpos, 2)
+ # read back into section
+ skipWhite(L)
+ var verbatim = readVerbatimSection(L)
+ skipWhite(L)
+ var sectionA = CFileSectionNames.find(k)
+ if sectionA > 0 and sectionA <= high(TCFileSection).int:
+ m.f[TCFileSection(sectionA)] = verbatim
+ else:
+ var sectionB = CProcSectionNames.find(k)
+ if sectionB >= 0 and sectionB <= high(TCProcSection).int:
+ m.p[TCProcSection(sectionB)] = verbatim
+ else:
+ internalError("ccgmerge: unknown section: " & k)
+ else:
+ internalError("ccgmerge: '*/' expected")
+
+proc mergeRequired*(m: BModule): bool =
+ for i in cfsHeaders..cfsProcs:
+ if m.s[i] != nil:
+ #echo "not empty: ", i, " ", m.s[i]
+ return true
+ for i in low(TCProcSection)..high(TCProcSection):
+ if m.initProc.s(i) != nil:
+ #echo "not empty: ", i, " ", m.initProc.s[i]
+ return true
+
+proc mergeFiles*(cfilename: string, m: BModule) =
+ ## merges the C file with the old version on hard disc.
+ var old: TMergeSections
+ readMergeSections(cfilename, old)
+ # do the merge; old section before new section:
+ for i in low(TCFileSection)..high(TCFileSection):
+ m.s[i] = old.f[i] & m.s[i]
+ for i in low(TCProcSection)..high(TCProcSection):
+ m.initProc.s(i) = old.p[i] & m.initProc.s(i)
diff --git a/compiler/ccgmerge_unused.nim b/compiler/ccgmerge_unused.nim
deleted file mode 100644
index a1413034f..000000000
--- a/compiler/ccgmerge_unused.nim
+++ /dev/null
@@ -1,283 +0,0 @@
-#
-#
-# The Nim Compiler
-# (c) Copyright 2012 Andreas Rumpf
-#
-# See the file "copying.txt", included in this
-# distribution, for details about the copyright.
-#
-
-## This module implements the merge operation of 2 different C files. This
-## is needed for incremental compilation.
-
-import
- ast, ropes, options, strutils, nimlexbase, cgendata, rodutils,
- intsets, llstream, tables, modulegraphs, pathutils
-
-# Careful! Section marks need to contain a tabulator so that they cannot
-# be part of C string literals.
-
-const
- CFileSectionNames: array[TCFileSection, string] = [
- cfsHeaders: "NIM_merge_HEADERS",
- cfsFrameDefines: "NIM_merge_FRAME_DEFINES",
- cfsForwardTypes: "NIM_merge_FORWARD_TYPES",
- cfsTypes: "NIM_merge_TYPES",
- cfsSeqTypes: "NIM_merge_SEQ_TYPES",
- cfsTypeInfo: "NIM_merge_TYPE_INFO",
- cfsProcHeaders: "NIM_merge_PROC_HEADERS",
- cfsData: "NIM_merge_DATA",
- cfsVars: "NIM_merge_VARS",
- cfsProcs: "NIM_merge_PROCS",
- cfsInitProc: "NIM_merge_INIT_PROC",
- cfsDatInitProc: "NIM_merge_DATINIT_PROC",
- cfsTypeInit1: "NIM_merge_TYPE_INIT1",
- cfsTypeInit3: "NIM_merge_TYPE_INIT3",
- cfsDynLibInit: "NIM_merge_DYNLIB_INIT"
- ]
- CProcSectionNames: array[TCProcSection, string] = [
- cpsLocals: "NIM_merge_PROC_LOCALS",
- cpsInit: "NIM_merge_PROC_INIT",
- cpsStmts: "NIM_merge_PROC_BODY"
- ]
- NimMergeEndMark = "/*\tNIM_merge_END:*/"
-
-proc genSectionStart*(fs: TCFileSection; conf: ConfigRef): Rope =
- # useful for debugging and only adds at most a few lines in each file
- result.add("\n/* section: ")
- result.add(CFileSectionNames[fs])
- result.add(" */\n")
- if compilationCachePresent(conf):
- result = nil
- result.add("\n/*\t")
- result.add(CFileSectionNames[fs])
- result.add(":*/\n")
-
-proc genSectionEnd*(fs: TCFileSection; conf: ConfigRef): Rope =
- if compilationCachePresent(conf):
- result = rope(NimMergeEndMark & "\n")
-
-proc genSectionStart*(ps: TCProcSection; conf: ConfigRef): Rope =
- if compilationCachePresent(conf):
- result = rope("")
- result.add("\n/*\t")
- result.add(CProcSectionNames[ps])
- result.add(":*/\n")
-
-proc genSectionEnd*(ps: TCProcSection; conf: ConfigRef): Rope =
- if compilationCachePresent(conf):
- result = rope(NimMergeEndMark & "\n")
-
-proc writeTypeCache(a: TypeCache, s: var string) =
- var i = 0
- for id, value in pairs(a):
- if i == 10:
- i = 0
- s.add('\L')
- else:
- s.add(' ')
- encodeStr($id, s)
- s.add(':')
- encodeStr($value, s)
- inc i
- s.add('}')
-
-proc writeIntSet(a: IntSet, s: var string) =
- var i = 0
- for x in items(a):
- if i == 10:
- i = 0
- s.add('\L')
- else:
- s.add(' ')
- encodeVInt(x, s)
- inc i
- s.add('}')
-
-proc genMergeInfo*(m: BModule): Rope =
- if not compilationCachePresent(m.config): return nil
- var s = "/*\tNIM_merge_INFO:\n"
- s.add("typeCache:{")
- writeTypeCache(m.typeCache, s)
- s.add("declared:{")
- writeIntSet(m.declaredThings, s)
- when false:
- s.add("typeInfo:{")
- writeIntSet(m.typeInfoMarker, s)
- s.add("labels:")
- encodeVInt(m.labels, s)
- s.add(" flags:")
- encodeVInt(cast[int](m.flags), s)
- s.add("\n*/")
- result = s.rope
-
-template `^`(pos: int): untyped = L.buf[pos]
-
-proc skipWhite(L: var TBaseLexer) =
- var pos = L.bufpos
- while true:
- case ^pos
- of CR: pos = nimlexbase.handleCR(L, pos)
- of LF: pos = nimlexbase.handleLF(L, pos)
- of ' ': inc pos
- else: break
- L.bufpos = pos
-
-proc skipUntilCmd(L: var TBaseLexer) =
- var pos = L.bufpos
- while true:
- case ^pos
- of CR: pos = nimlexbase.handleCR(L, pos)
- of LF: pos = nimlexbase.handleLF(L, pos)
- of '\0': break
- of '/':
- if ^(pos+1) == '*' and ^(pos+2) == '\t':
- inc pos, 3
- break
- inc pos
- else: inc pos
- L.bufpos = pos
-
-proc atEndMark(buf: cstring, pos: int): bool =
- var s = 0
- while s < NimMergeEndMark.len and buf[pos+s] == NimMergeEndMark[s]: inc s
- result = s == NimMergeEndMark.len
-
-proc readVerbatimSection(L: var TBaseLexer): Rope =
- var pos = L.bufpos
- var r = newStringOfCap(30_000)
- while true:
- case L.buf[pos]
- of CR:
- pos = nimlexbase.handleCR(L, pos)
- r.add('\L')
- of LF:
- pos = nimlexbase.handleLF(L, pos)
- r.add('\L')
- of '\0':
- doAssert(false, "ccgmerge: expected: " & NimMergeEndMark)
- break
- else:
- if atEndMark(L.buf, pos):
- inc pos, NimMergeEndMark.len
- break
- r.add(L.buf[pos])
- inc pos
- L.bufpos = pos
- result = r.rope
-
-proc readKey(L: var TBaseLexer, result: var string) =
- var pos = L.bufpos
- setLen(result, 0)
- while L.buf[pos] in IdentChars:
- result.add(L.buf[pos])
- inc pos
- if L.buf[pos] != ':': doAssert(false, "ccgmerge: ':' expected")
- L.bufpos = pos + 1 # skip ':'
-
-proc readTypeCache(L: var TBaseLexer, result: var TypeCache) =
- if ^L.bufpos != '{': doAssert(false, "ccgmerge: '{' expected")
- inc L.bufpos
- while ^L.bufpos != '}':
- skipWhite(L)
- var key = decodeStr(L.buf, L.bufpos)
- if ^L.bufpos != ':': doAssert(false, "ccgmerge: ':' expected")
- inc L.bufpos
- discard decodeStr(L.buf, L.bufpos)
- inc L.bufpos
-
-proc readIntSet(L: var TBaseLexer, result: var IntSet) =
- if ^L.bufpos != '{': doAssert(false, "ccgmerge: '{' expected")
- inc L.bufpos
- while ^L.bufpos != '}':
- skipWhite(L)
- var key = decodeVInt(L.buf, L.bufpos)
- result.incl(key)
- inc L.bufpos
-
-proc processMergeInfo(L: var TBaseLexer, m: BModule) =
- var k = newStringOfCap("typeCache".len)
- while true:
- skipWhite(L)
- if ^L.bufpos == '*' and ^(L.bufpos+1) == '/':
- inc(L.bufpos, 2)
- break
- readKey(L, k)
- case k
- of "typeCache": readTypeCache(L, m.typeCache)
- of "declared": readIntSet(L, m.declaredThings)
- of "typeInfo":
- when false: readIntSet(L, m.typeInfoMarker)
- of "labels": m.labels = decodeVInt(L.buf, L.bufpos)
- of "flags":
- m.flags = cast[set[CodegenFlag]](decodeVInt(L.buf, L.bufpos) != 0)
- else: doAssert(false, "ccgmerge: unknown key: " & k)
-
-template withCFile(cfilename: AbsoluteFile, body: untyped) =
- var s = llStreamOpen(cfilename, fmRead)
- if s == nil: return
- var L {.inject.}: TBaseLexer
- openBaseLexer(L, s)
- var k {.inject.} = newStringOfCap("NIM_merge_FORWARD_TYPES".len)
- while true:
- skipUntilCmd(L)
- if ^L.bufpos == '\0': break
- body
- closeBaseLexer(L)
-
-proc readMergeInfo*(cfilename: AbsoluteFile, m: BModule) =
- ## reads the merge meta information into `m`.
- withCFile(cfilename):
- readKey(L, k)
- if k == "NIM_merge_INFO":
- processMergeInfo(L, m)
- break
-
-type
- TMergeSections = object
- f: TCFileSections
- p: TCProcSections
-
-proc readMergeSections(cfilename: AbsoluteFile, m: var TMergeSections) =
- ## reads the merge sections into `m`.
- withCFile(cfilename):
- readKey(L, k)
- if k == "NIM_merge_INFO":
- discard
- elif ^L.bufpos == '*' and ^(L.bufpos+1) == '/':
- inc(L.bufpos, 2)
- # read back into section
- skipWhite(L)
- var verbatim = readVerbatimSection(L)
- skipWhite(L)
- var sectionA = CFileSectionNames.find(k)
- if sectionA > 0 and sectionA <= high(TCFileSection).int:
- m.f[TCFileSection(sectionA)] = verbatim
- else:
- var sectionB = CProcSectionNames.find(k)
- if sectionB >= 0 and sectionB <= high(TCProcSection).int:
- m.p[TCProcSection(sectionB)] = verbatim
- else:
- doAssert(false, "ccgmerge: unknown section: " & k)
- else:
- doAssert(false, "ccgmerge: '*/' expected")
-
-proc mergeRequired*(m: BModule): bool =
- for i in cfsHeaders..cfsProcs:
- if m.s[i] != nil:
- #echo "not empty: ", i, " ", m.s[i]
- return true
- for i in TCProcSection:
- if m.initProc.s(i) != nil:
- #echo "not empty: ", i, " ", m.initProc.s[i]
- return true
-
-proc mergeFiles*(cfilename: AbsoluteFile, m: BModule) =
- ## merges the C file with the old version on hard disc.
- var old: TMergeSections
- readMergeSections(cfilename, old)
- # do the merge; old section before new section:
- for i in TCFileSection:
- m.s[i] = old.f[i] & m.s[i]
- for i in TCProcSection:
- m.initProc.s(i) = old.p[i] & m.initProc.s(i)
diff --git a/compiler/ccgreset.nim b/compiler/ccgreset.nim
deleted file mode 100644
index 5e6456704..000000000
--- a/compiler/ccgreset.nim
+++ /dev/null
@@ -1,107 +0,0 @@
-#
-#
-# The Nim Compiler
-# (c) Copyright 2020 Andreas Rumpf
-#
-# See the file "copying.txt", included in this
-# distribution, for details about the copyright.
-#
-
-# included from cgen.nim
-
-## Code specialization instead of the old, incredibly slow 'genericReset'
-## implementation.
-
-proc specializeResetT(p: BProc, accessor: Rope, typ: PType)
-
-proc specializeResetN(p: BProc, accessor: Rope, n: PNode;
- typ: PType) =
- if n == nil: return
- case n.kind
- of nkRecList:
- for i in 0.. 2
- # do not close and reopen blocks if this is a 'global' but inside of a block (if/while/block)
- forHcr = forHcr and not isGlobalInBlock
-
- if forHcr:
- # check with the boolean if the initializing code for the tuple should be ran
- lineCg(p, cpsStmts, "if ($1)$n", [hcrCond])
- startBlock(p)
-
+ useLowering = true; break
+ if useLowering:
+ genStmts(p, lowerTupleUnpacking(n, p.prc))
+ return
genLineDir(p, n)
- initLocExpr(p, n[^1], tup)
- var t = tup.t.skipTypes(abstractInst)
- for i in 0.. 0:
- result.addf("FR_.len+=$1;$n", [b.frameLen.rope])
+ result.addf("FR.len+=$1;$n", [b.frameLen.rope])
result.add(b.sections[cpsInit])
result.add(b.sections[cpsStmts])
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])
+ add(p.blocks[topBlock-1].sections[cpsStmts], 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
@@ -166,14 +117,13 @@ proc endBlock(p: BProc, blockEnd: Rope) =
proc endBlock(p: BProc) =
let topBlock = p.blocks.len - 1
+ var blockEnd = if p.blocks[topBlock].label != nil:
+ rfmt(nil, "} $1: ;$n", p.blocks[topBlock].label)
+ else:
+ ~"}$n"
let frameLen = p.blocks[topBlock].frameLen
- var blockEnd: Rope
if frameLen > 0:
- blockEnd.addf("FR_.len-=$1;$n", [frameLen.rope])
- if p.blocks[topBlock].label.len != 0:
- blockEnd.addf("} $1: ;$n", [p.blocks[topBlock].label])
- else:
- blockEnd.addf("}$n", [])
+ blockEnd.addf("FR.len-=$1;$n", [frameLen.rope])
endBlock(p, blockEnd)
proc genSimpleBlock(p: BProc, stmts: PNode) {.inline.} =
@@ -186,55 +136,15 @@ proc exprBlock(p: BProc, n: PNode, d: var TLoc) =
expr(p, n, d)
endBlock(p)
-template preserveBreakIdx(body: untyped): untyped =
+template preserveBreakIdx(body: stmt): stmt {.immediate.} =
var oldBreakIdx = p.breakIdx
body
p.breakIdx = oldBreakIdx
proc genState(p: BProc, n: PNode) =
- internalAssert p.config, n.len == 1
- let n0 = n[0]
- if n0.kind == nkIntLit:
- let idx = n[0].intVal
- linefmt(p, cpsStmts, "STATE$1: ;$n", [idx])
- elif n0.kind == nkStrLit:
- linefmt(p, cpsStmts, "$1: ;$n", [n0.strVal])
-
-proc blockLeaveActions(p: BProc, howManyTrys, howManyExcepts: int) =
- # Called by return and break stmts.
- # Deals with issues faced when jumping out of try/except/finally stmts.
-
- var stack = newSeq[tuple[fin: PNode, inExcept: bool, label: Natural]](0)
-
- inc p.withinBlockLeaveActions
- for i in 1..howManyTrys:
- let tryStmt = p.nestedTryStmts.pop
- if p.config.exc == excSetjmp:
- # Pop safe points generated by try
- if not tryStmt.inExcept:
- linefmt(p, cpsStmts, "#popSafePoint();$n", [])
-
- # Pop this try-stmt of the list of nested trys
- # so we don't infinite recurse on it in the next step.
- stack.add(tryStmt)
-
- # Find finally-stmt for this try-stmt
- # and generate a copy of its sons
- var finallyStmt = tryStmt.fin
- if finallyStmt != nil:
- genStmts(p, finallyStmt[0])
-
- dec p.withinBlockLeaveActions
-
- # push old elements again:
- for i in countdown(howManyTrys-1, 0):
- p.nestedTryStmts.add(stack[i])
-
- # Pop exceptions that was handled by the
- # except-blocks we are in
- if noSafePoints notin p.flags:
- for i in countdown(howManyExcepts-1, 0):
- linefmt(p, cpsStmts, "#popCurrentException();$n", [])
+ internalAssert n.len == 1 and n.sons[0].kind == nkIntLit
+ let idx = n.sons[0].intVal
+ linefmt(p, cpsStmts, "STATE$1: ;$n", idx.rope)
proc genGotoState(p: BProc, n: PNode) =
# we resist the temptation to translate it into duff's device as it later
@@ -243,177 +153,127 @@ proc genGotoState(p: BProc, n: PNode) =
# case 0: goto STATE0;
# ...
var a: TLoc
- initLocExpr(p, n[0], a)
+ initLocExpr(p, n.sons[0], a)
lineF(p, cpsStmts, "switch ($1) {$n", [rdLoc(a)])
- p.flags.incl beforeRetNeeded
- lineF(p, cpsStmts, "case -1:$n", [])
- blockLeaveActions(p,
- howManyTrys = p.nestedTryStmts.len,
- howManyExcepts = p.inExceptBlockLen)
- lineF(p, cpsStmts, " goto BeforeRet_;$n", [])
- var statesCounter = lastOrd(p.config, n[0].typ)
- if n.len >= 2 and n[1].kind == nkIntLit:
- statesCounter = getInt(n[1])
- let prefix = if n.len == 3 and n[2].kind == nkStrLit: n[2].strVal.rope
- else: rope"STATE"
- for i in 0i64..toInt64(statesCounter):
- lineF(p, cpsStmts, "case $2: goto $1$2;$n", [prefix, rope(i)])
+ p.beforeRetNeeded = true
+ lineF(p, cpsStmts, "case -1: goto BeforeRet;$n", [])
+ for i in 0 .. lastOrd(n.sons[0].typ):
+ lineF(p, cpsStmts, "case $1: goto STATE$1;$n", [rope(i)])
lineF(p, cpsStmts, "}$n", [])
-proc genBreakState(p: BProc, n: PNode, d: var TLoc) =
+proc genBreakState(p: BProc, n: PNode) =
var a: TLoc
- initLoc(d, locExpr, n, OnUnknown)
-
- if n[0].kind == nkClosure:
- initLocExpr(p, n[0][1], a)
- d.r = "(((NI*) $1)[1] < 0)" % [rdLoc(a)]
+ if n.sons[0].kind == nkClosure:
+ # XXX this produces quite inefficient code!
+ initLocExpr(p, n.sons[0].sons[1], a)
+ lineF(p, cpsStmts, "if (((NI*) $1)[0] < 0) break;$n", [rdLoc(a)])
else:
- initLocExpr(p, n[0], a)
- # the environment is guaranteed to contain the 'state' field at offset 1:
- d.r = "((((NI*) $1.ClE_0)[1]) < 0)" % [rdLoc(a)]
+ initLocExpr(p, n.sons[0], a)
+ # the environment is guaranteed to contain the 'state' field at offset 0:
+ lineF(p, cpsStmts, "if ((((NI*) $1.ClEnv)[0]) < 0) break;$n", [rdLoc(a)])
+ # lineF(p, cpsStmts, "if (($1) < 0) break;$n", [rdLoc(a)])
+
+proc genVarPrototypeAux(m: BModule, sym: PSym)
proc genGotoVar(p: BProc; value: PNode) =
if value.kind notin {nkCharLit..nkUInt64Lit}:
- localError(p.config, value.info, "'goto' target must be a literal value")
+ localError(value.info, "'goto' target must be a literal value")
else:
lineF(p, cpsStmts, "goto NIMSTATE_$#;$n", [value.intVal.rope])
-proc genBracedInit(p: BProc, n: PNode; isConst: bool; optionalType: PType; result: var Rope)
-
-proc potentialValueInit(p: BProc; v: PSym; value: PNode; result: var Rope) =
- if lfDynamicLib in v.loc.flags or sfThread in v.flags or p.hcrOn:
- discard "nothing to do"
- elif sfGlobal in v.flags and value != nil and isDeepConstExpr(value, p.module.compileToCpp) and
- p.withinLoop == 0 and not containsGarbageCollectedRef(v.typ):
- #echo "New code produced for ", v.name.s, " ", p.config $ value.info
- genBracedInit(p, value, isConst = false, v.typ, result)
-
-proc genSingleVar(p: BProc, v: PSym; vn, value: PNode) =
- if sfGoto in v.flags:
+proc genSingleVar(p: BProc, a: PNode) =
+ var v = a.sons[0].sym
+ if {sfCompileTime, sfGoto} * v.flags != {}:
# translate 'var state {.goto.} = X' into 'goto LX':
- genGotoVar(p, value)
+ if sfGoto in v.flags: genGotoVar(p, a.sons[2])
return
var targetProc = p
- var valueAsRope = ""
- potentialValueInit(p, v, value, valueAsRope)
if sfGlobal in v.flags:
if v.flags * {sfImportc, sfExportc} == {sfImportc} and
- value.kind == nkEmpty and
+ a.sons[2].kind == nkEmpty and
v.loc.flags * {lfHeader, lfNoDecl} != {}:
return
if sfPure in v.flags:
# v.owner.kind != skModule:
targetProc = p.module.preInitProc
- assignGlobalVar(targetProc, vn, valueAsRope)
+ assignGlobalVar(targetProc, v)
# XXX: be careful here.
# Global variables should not be zeromem-ed within loops
# (see bug #20).
# 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:
- var loc = v.loc
-
- # When the native TLS is unavailable, a global thread-local variable needs
- # one more layer of indirection in order to access the TLS block.
- # Only do this for complex types that may need a call to `objectInit`
- if sfThread in v.flags and emulatedThreadVars(p.config) and
- isComplexValueType(v.typ):
- initLocExprSingleUse(p.module.preInitProc, vn, loc)
- genObjectInit(p.module.preInitProc, cpsInit, v.typ, loc, constructObj)
+ genObjectInit(p.module.preInitProc, cpsInit, v.typ, v.loc, true)
# Alternative construction using default constructor (which may zeromem):
# 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)
+ if sfExportc in v.flags and generatedHeader != nil:
+ genVarPrototypeAux(generatedHeader, v)
+ registerGcRoot(p, v)
else:
- let imm = isAssignedImmediately(p.config, value)
+ let value = a.sons[2]
+ let imm = isAssignedImmediately(value)
if imm and p.module.compileToCpp and p.splitDecls == 0 and
not containsHiddenPointer(v.typ):
# C++ really doesn't like things like 'Foo f; f = x' as that invokes a
# parameterless constructor followed by an assignment operator. So we
- # generate better code here: 'Foo f = x;'
- genLineDir(p, vn)
- let decl = localVarDecl(p, vn)
+ # generate better code here:
+ genLineDir(p, a)
+ let decl = localVarDecl(p, v)
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
- let typ = skipTypes(value[0].typ, abstractInst)
+ var params: Rope
+ let typ = skipTypes(value.sons[0].typ, abstractInst)
assert(typ.kind == tyProc)
- for i in 1.. 3 and v.owner.kind == skModule:
- # put it in the locals section - mainly because of loops which
- # use the var in a call to resetLoc() in the statements section
- lineCg(targetProc, cpsLocals, "hcrRegisterGlobal($3, \"$1\", sizeof($2), $4, (void**)&$1);$n",
- [v.loc.r, rdLoc(v.loc), getModuleDllPath(p.module, v), traverseProc])
- # nothing special left to do later on - let's avoid closing and reopening blocks
- forHcr = false
-
- # we close and reopen the global if (nim_hcr_do_init_) blocks in the main Init function
- # for the module so we can have globals and top-level code be interleaved and still
- # be able to re-run it but without the top level code - just the init of globals
- if forHcr:
- lineCg(targetProc, cpsStmts, "if (hcrRegisterGlobal($3, \"$1\", sizeof($2), $4, (void**)&$1))$N",
- [v.loc.r, rdLoc(v.loc), getModuleDllPath(p.module, v), traverseProc])
- startBlock(targetProc)
- if value.kind != nkEmpty and valueAsRope.len == 0:
- genLineDir(targetProc, vn)
- loadInto(targetProc, vn, value, v.loc)
- if forHcr:
- endBlock(targetProc)
-
-proc genSingleVar(p: BProc, a: PNode) =
- let v = a[0].sym
- if sfCompileTime in v.flags:
- # fix issue #12640
- # {.global, compileTime.} pragma in proc
- if sfGlobal in v.flags and p.prc != nil and p.prc.kind == skProc:
- discard
- else:
- return
- genSingleVar(p, v, a[0], a[2])
+ if a.sons[2].kind != nkEmpty:
+ genLineDir(targetProc, a)
+ loadInto(targetProc, a.sons[0], a.sons[2], v.loc)
proc genClosureVar(p: BProc, a: PNode) =
- var immediateAsgn = a[2].kind != nkEmpty
- var v: TLoc
- initLocExpr(p, a[0], v)
- genLineDir(p, a)
+ var immediateAsgn = a.sons[2].kind != nkEmpty
if immediateAsgn:
- loadInto(p, a[0], a[2], v)
- elif sfNoInit notin a[0][1].sym.flags:
- constructLoc(p, v)
+ var v: TLoc
+ initLocExpr(p, a.sons[0], v)
+ genLineDir(p, a)
+ loadInto(p, a.sons[0], a.sons[2], v)
proc genVarStmt(p: BProc, n: PNode) =
- for it in n.sons:
- if it.kind == nkCommentStmt: continue
- if it.kind == nkIdentDefs:
+ for i in countup(0, sonsLen(n) - 1):
+ var a = n.sons[i]
+ if a.kind == nkCommentStmt: continue
+ if a.kind == nkIdentDefs:
# can be a lifted var nowadays ...
- if it[0].kind == nkSym:
- genSingleVar(p, it)
+ if a.sons[0].kind == nkSym:
+ genSingleVar(p, a)
else:
- genClosureVar(p, it)
+ genClosureVar(p, a)
else:
- genVarTuple(p, it)
+ genVarTuple(p, a)
+
+proc genConstStmt(p: BProc, t: PNode) =
+ for i in countup(0, sonsLen(t) - 1):
+ var it = t.sons[i]
+ if it.kind == nkCommentStmt: continue
+ if it.kind != nkConstDef: internalError(t.info, "genConstStmt")
+ var c = it.sons[0].sym
+ if c.typ.containsCompileTimeOnly: continue
+ if sfFakeConst in c.flags:
+ genSingleVar(p, it)
+ elif c.typ.kind in ConstantDataTypes and lfNoDecl notin c.loc.flags and
+ c.ast.len != 0:
+ if not emitLazily(c): requestConstImpl(p, c)
proc genIf(p: BProc, n: PNode, d: var TLoc) =
#
@@ -434,505 +294,423 @@ proc genIf(p: BProc, n: PNode, d: var TLoc) =
getTemp(p, n.typ, d)
genLineDir(p, n)
let lend = getLabel(p)
- for it in n.sons:
- # bug #4230: avoid false sharing between branches:
- if d.k == locTemp and isEmptyType(n.typ): d.k = locNone
+ for i in countup(0, sonsLen(n) - 1):
+ let it = n.sons[i]
if it.len == 2:
- startBlock(p)
- initLocExprSingleUse(p, it[0], a)
+ when newScopeForIf: startBlock(p)
+ initLocExprSingleUse(p, it.sons[0], a)
lelse = getLabel(p)
inc(p.labels)
lineF(p, cpsStmts, "if (!$1) goto $2;$n",
[rdLoc(a), lelse])
+ when not newScopeForIf: startBlock(p)
if p.module.compileToCpp:
# avoid "jump to label crosses initialization" error:
- p.s(cpsStmts).add "{"
- expr(p, it[1], d)
- p.s(cpsStmts).add "}"
+ add(p.s(cpsStmts), "{")
+ expr(p, it.sons[1], d)
+ add(p.s(cpsStmts), "}")
else:
- expr(p, it[1], d)
+ expr(p, it.sons[1], d)
endBlock(p)
- if n.len > 1:
+ if sonsLen(n) > 1:
lineF(p, cpsStmts, "goto $1;$n", [lend])
fixLabel(p, lelse)
elif it.len == 1:
startBlock(p)
- expr(p, it[0], d)
+ expr(p, it.sons[0], d)
endBlock(p)
- else: internalError(p.config, n.info, "genIf()")
- if n.len > 1: fixLabel(p, lend)
+ else: internalError(n.info, "genIf()")
+ if sonsLen(n) > 1: fixLabel(p, lend)
+
+
+proc blockLeaveActions(p: BProc, howManyTrys, howManyExcepts: int) =
+ # Called by return and break stmts.
+ # Deals with issues faced when jumping out of try/except/finally stmts,
+
+ var stack: seq[PNode]
+ newSeq(stack, 0)
+
+ var alreadyPoppedCnt = p.inExceptBlock
+ for i in countup(1, howManyTrys):
+ if not p.module.compileToCpp:
+ # Pop safe points generated by try
+ if alreadyPoppedCnt > 0:
+ dec alreadyPoppedCnt
+ else:
+ linefmt(p, cpsStmts, "#popSafePoint();$n")
+
+ # Pop this try-stmt of the list of nested trys
+ # so we don't infinite recurse on it in the next step.
+ var tryStmt = p.nestedTryStmts.pop
+ stack.add(tryStmt)
+
+ # Find finally-stmt for this try-stmt
+ # and generate a copy of its sons
+ var finallyStmt = lastSon(tryStmt)
+ if finallyStmt.kind == nkFinally:
+ genStmts(p, finallyStmt.sons[0])
+
+ # push old elements again:
+ for i in countdown(howManyTrys-1, 0):
+ p.nestedTryStmts.add(stack[i])
+
+ if not p.module.compileToCpp:
+ # Pop exceptions that was handled by the
+ # except-blocks we are in
+ for i in countdown(howManyExcepts-1, 0):
+ linefmt(p, cpsStmts, "#popCurrentException();$n")
proc genReturnStmt(p: BProc, t: PNode) =
- if nfPreventCg in t.flags: return
- p.flags.incl beforeRetNeeded
+ p.beforeRetNeeded = true
genLineDir(p, t)
- if (t[0].kind != nkEmpty): genStmts(p, t[0])
+ if (t.sons[0].kind != nkEmpty): genStmts(p, t.sons[0])
blockLeaveActions(p,
howManyTrys = p.nestedTryStmts.len,
- howManyExcepts = p.inExceptBlockLen)
- if (p.finallySafePoints.len > 0) and noSafePoints notin p.flags:
+ howManyExcepts = p.inExceptBlock)
+ if (p.finallySafePoints.len > 0):
# If we're in a finally block, and we came here by exception
# consume it before we return.
- var safePoint = p.finallySafePoints[^1]
- linefmt(p, cpsStmts, "if ($1.status != 0) #popCurrentException();$n", [safePoint])
- lineF(p, cpsStmts, "goto BeforeRet_;$n", [])
+ var safePoint = p.finallySafePoints[p.finallySafePoints.len-1]
+ linefmt(p, cpsStmts, "if ($1.status != 0) #popCurrentException();$n", safePoint)
+ lineF(p, cpsStmts, "goto BeforeRet;$n", [])
proc genGotoForCase(p: BProc; caseStmt: PNode) =
- for i in 1.. 10_000:
- localError(p.config, it.info,
+ localError(it.info,
"case statement has too many cases for computed goto"); return
- arraySize = toInt(aSize)
- if firstOrd(p.config, it[0].typ) != 0:
- localError(p.config, it.info,
+ arraySize = aSize.int
+ if firstOrd(it.sons[0].typ) != 0:
+ localError(it.info,
"case statement has to start at 0 for computed goto"); return
if casePos < 0:
- localError(p.config, n.info, "no case statement found for computed goto"); return
+ localError(n.info, "no case statement found for computed goto"); return
var id = p.labels+1
inc p.labels, arraySize+1
- let tmp = "TMP$1_" % [id.rope]
+ let tmp = "TMP$1" % [id.rope]
var gotoArray = "static void* $#[$#] = {" % [tmp, arraySize.rope]
for i in 1..arraySize-1:
- gotoArray.addf("&&TMP$#_, ", [rope(id+i)])
- gotoArray.addf("&&TMP$#_};$n", [rope(id+arraySize)])
+ gotoArray.addf("&&TMP$#, ", [(id+i).rope])
+ gotoArray.addf("&&TMP$#};$n", [(id+arraySize).rope])
line(p, cpsLocals, gotoArray)
- for j in 0..= 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
+ internalError(t.info, "no loop to break")
+ let label = assignLabel(p.blocks[idx])
blockLeaveActions(p,
p.nestedTryStmts.len - p.blocks[idx].nestedTryStmts,
- p.inExceptBlockLen - p.blocks[idx].nestedExceptStmts)
+ p.inExceptBlock - 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
- if nimErrorFlagDisabled notin p.flags:
- p.flags.incl nimErrorFlagAccessed
- if p.nestedTryStmts.len == 0:
- p.flags.incl beforeRetNeeded
- # easy case, simply goto 'ret':
- lineCg(p, cpsStmts, "if (NIM_UNLIKELY(*nimErr_)) goto BeforeRet_;$n", [])
- else:
- lineCg(p, cpsStmts, "if (NIM_UNLIKELY(*nimErr_)) goto LA$1_;$n",
- [p.nestedTryStmts[^1].label])
-
-proc finallyActions(p: BProc) =
- if p.config.exc != excGoto and p.nestedTryStmts.len > 0 and p.nestedTryStmts[^1].inExcept:
- # if the current try stmt have a finally block,
- # we must execute it before reraising
- let finallyBlock = p.nestedTryStmts[^1].fin
- if finallyBlock != nil:
- genSimpleBlock(p, finallyBlock[0])
-
-proc raiseInstr(p: BProc; result: var Rope) =
- if p.config.exc == excGoto:
- let L = p.nestedTryStmts.len
- if L == 0:
- p.flags.incl beforeRetNeeded
- # easy case, simply goto 'ret':
- result.add ropecg(p.module, "goto BeforeRet_;$n", [])
- else:
- # raise inside an 'except' must go to the finally block,
- # raise outside an 'except' block must go to the 'except' list.
- result.add ropecg(p.module, "goto LA$1_;$n",
- [p.nestedTryStmts[L-1].label])
- # + ord(p.nestedTryStmts[L-1].inExcept)])
+proc getRaiseFrmt(p: BProc): string =
+ result = "#raiseException((#Exception*)$1, $2);$n"
proc genRaiseStmt(p: BProc, t: PNode) =
- if t[0].kind != nkEmpty:
+ if p.inExceptBlock > 0:
+ # if the current try stmt have a finally block,
+ # we must execute it before reraising
+ var finallyBlock = p.nestedTryStmts[p.nestedTryStmts.len - 1].lastSon
+ if finallyBlock.kind == nkFinally:
+ genSimpleBlock(p, finallyBlock.sons[0])
+ if t.sons[0].kind != nkEmpty:
var a: TLoc
- initLocExprSingleUse(p, t[0], a)
- finallyActions(p)
+ initLocExpr(p, t.sons[0], a)
var e = rdLoc(a)
- discard getTypeDesc(p.module, t[0].typ)
- var typ = skipTypes(t[0].typ, abstractPtrs)
- # XXX For reasons that currently escape me, this is only required by the new
- # C++ based exception handling:
- if p.config.exc == excCpp:
- blockLeaveActions(p, howManyTrys = 0, howManyExcepts = p.inExceptBlockLen)
+ var typ = skipTypes(t.sons[0].typ, abstractPtrs)
genLineDir(p, t)
- if isImportedException(typ, p.config):
- lineF(p, cpsStmts, "throw $1;$n", [e])
- else:
- lineCg(p, cpsStmts, "#raiseExceptionEx((#Exception*)$1, $2, $3, $4, $5);$n",
- [e, makeCString(typ.sym.name.s),
- makeCString(if p.prc != nil: p.prc.name.s else: p.module.module.name.s),
- quotedFilename(p.config, t.info), toLinenumber(t.info)])
- if optOwnedRefs in p.config.globalOptions:
- lineCg(p, cpsStmts, "$1 = NIM_NIL;$n", [e])
+ lineCg(p, cpsStmts, getRaiseFrmt(p), [e, makeCString(typ.sym.name.s)])
else:
- finallyActions(p)
genLineDir(p, t)
# reraise the last exception:
- if p.config.exc == excCpp:
- line(p, cpsStmts, "throw;\n")
+ if p.module.compileToCpp and optNoCppExceptions notin gGlobalOptions:
+ line(p, cpsStmts, ~"throw;$n")
else:
- linefmt(p, cpsStmts, "#reraiseException();$n", [])
- raiseInstr(p, p.s(cpsStmts))
+ linefmt(p, cpsStmts, "#reraiseException();$n")
-template genCaseGenericBranch(p: BProc, b: PNode, e: TLoc,
+proc genCaseGenericBranch(p: BProc, b: PNode, e: TLoc,
rangeFormat, eqFormat: FormatStr, labl: TLabel) =
- var x, y: TLoc
- for i in 0.. stringCaseThreshold:
var bitMask = math.nextPowerOfTwo(strings) - 1
var branches: seq[Rope]
newSeq(branches, bitMask + 1)
var a: TLoc
- initLocExpr(p, t[0], a) # fist pass: generate ifs+goto:
+ initLocExpr(p, t.sons[0], a) # fist pass: gnerate ifs+goto:
var labId = p.labels
- for i in 1.. RangeExpandLimit:
+ if (b.sons[i].kind == nkRange) and
+ b.sons[i].sons[1].intVal - b.sons[i].sons[0].intVal > RangeExpandLimit:
return true
proc ifSwitchSplitPoint(p: BProc, n: PNode): int =
- for i in 1.. 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:
lineF(p, cpsStmts, "switch ($1) {$n", [rdCharLoc(a)])
var hasDefault = false
- for i in splitPoint+1..= $2 && $1 <= $3) goto $4;$n",
"if ($1 == $2) goto $3;$n")
else:
- if t[0].kind == nkSym and sfGoto in t[0].sym.flags:
+ if t.sons[0].kind == nkSym and sfGoto in t.sons[0].sym.flags:
genGotoForCase(p, t)
else:
genOrdinalCase(p, t, d)
-proc genRestoreFrameAfterException(p: BProc) =
- if optStackTrace in p.module.config.options:
- if hasCurFramePointer notin p.flags:
- p.flags.incl hasCurFramePointer
- p.procSec(cpsLocals).add(ropecg(p.module, "\tTFrame* _nimCurFrame;$n", []))
- p.procSec(cpsInit).add(ropecg(p.module, "\t_nimCurFrame = #getFrame();$n", []))
- linefmt(p, cpsStmts, "#setFrame(_nimCurFrame);$n", [])
+proc hasGeneralExceptSection(t: PNode): bool =
+ var length = sonsLen(t)
+ var i = 1
+ while (i < length) and (t.sons[i].kind == nkExceptBranch):
+ var blen = sonsLen(t.sons[i])
+ if blen == 1:
+ return true
+ inc(i)
+ result = false
proc genTryCpp(p: BProc, t: PNode, d: var TLoc) =
- #[ code to generate:
-
- std::exception_ptr error = nullptr;
- try {
- body;
- } catch (Exception e) {
- error = std::current_exception();
- if (ofExpr(e, TypeHere)) {
-
- error = nullptr; // handled
- } else if (...) {
-
- } else {
- throw;
- }
- } catch(...) {
- // C++ exception occured, not under Nim's control.
- }
- {
- /* finally: */
- printf('fin!\n');
- if (error) std::rethrow_exception(error); // re-raise the exception
- }
- ]#
- p.module.includeHeader("")
-
- if not isEmptyType(t.typ) and d.k == locNone:
- getTemp(p, t.typ, d)
- genLineDir(p, t)
-
- inc(p.labels, 2)
- let etmp = p.labels
-
- p.procSec(cpsInit).add(ropecg(p.module, "\tstd::exception_ptr T$1_ = nullptr;", [etmp]))
-
- let fin = if t[^1].kind == nkFinally: t[^1] else: nil
- p.nestedTryStmts.add((fin, false, 0.Natural))
-
- if t.kind == nkHiddenTryStmt:
- lineCg(p, cpsStmts, "try {$n", [])
- expr(p, t[0], d)
- lineCg(p, cpsStmts, "}$n", [])
- else:
- startBlock(p, "try {$n")
- expr(p, t[0], d)
- endBlock(p)
-
- # First pass: handle Nim based exceptions:
- lineCg(p, cpsStmts, "catch (#Exception* T$1_) {$n", [etmp+1])
- genRestoreFrameAfterException(p)
- # an unhandled exception happened!
- lineCg(p, cpsStmts, "T$1_ = std::current_exception();$n", [etmp])
- p.nestedTryStmts[^1].inExcept = true
- var hasImportedCppExceptions = false
- var i = 1
- var hasIf = false
- var hasElse = false
- while (i < t.len) and (t[i].kind == nkExceptBranch):
- # bug #4230: avoid false sharing between branches:
- if d.k == locTemp and isEmptyType(t.typ): d.k = locNone
- if t[i].len == 1:
- hasImportedCppExceptions = true
- # general except section:
- hasElse = true
- if hasIf: lineF(p, cpsStmts, "else ", [])
- startBlock(p)
- # we handled the error:
- linefmt(p, cpsStmts, "T$1_ = nullptr;$n", [etmp])
- expr(p, t[i][0], d)
- linefmt(p, cpsStmts, "#popCurrentException();$n", [])
- endBlock(p)
- else:
- var orExpr = newRopeAppender()
- var exvar = PNode(nil)
- for j in 0..$1, $2, $3)", [memberName, checkFor, $genDisplayElem(MD5Digest(hashType(typeNode.typ, p.config)))])
- else:
- let checkFor = genTypeInfoV1(p.module, typeNode.typ, typeNode.info)
- appcg(p.module, orExpr, "#isObj(#nimBorrowCurrentException()->$1, $2)", [memberName, checkFor])
-
- if orExpr.len != 0:
- if hasIf:
- startBlock(p, "else if ($1) {$n", [orExpr])
- else:
- startBlock(p, "if ($1) {$n", [orExpr])
- hasIf = true
- if exvar != nil:
- fillLocalName(p, exvar.sym)
- fillLoc(exvar.sym.loc, locTemp, exvar, OnStack)
- linefmt(p, cpsStmts, "$1 $2 = T$3_;$n", [getTypeDesc(p.module, exvar.sym.typ),
- rdLoc(exvar.sym.loc), rope(etmp+1)])
- # we handled the error:
- linefmt(p, cpsStmts, "T$1_ = nullptr;$n", [etmp])
- expr(p, t[i][^1], d)
- linefmt(p, cpsStmts, "#popCurrentException();$n", [])
- endBlock(p)
- inc(i)
- if hasIf and not hasElse:
- linefmt(p, cpsStmts, "else throw;$n", [etmp])
- linefmt(p, cpsStmts, "}$n", [])
-
- # Second pass: handle C++ based exceptions:
- template genExceptBranchBody(body: PNode) {.dirty.} =
- genRestoreFrameAfterException(p)
- #linefmt(p, cpsStmts, "T$1_ = std::current_exception();$n", [etmp])
- expr(p, body, d)
-
- var catchAllPresent = false
- incl p.flags, noSafePoints # mark as not needing 'popCurrentException'
- if hasImportedCppExceptions:
- for i in 1.. 0 and t[^1].kind == nkFinally:
- if not catchAllPresent:
- startBlock(p, "catch (...) {", [])
- genRestoreFrameAfterException(p)
- linefmt(p, cpsStmts, "T$1_ = std::current_exception();$n", [etmp])
- endBlock(p)
-
- startBlock(p)
- genStmts(p, t[^1][0])
- linefmt(p, cpsStmts, "if (T$1_) std::rethrow_exception(T$1_);$n", [etmp])
- endBlock(p)
-
-proc genTryCppOld(p: BProc, t: PNode, d: var TLoc) =
- # There are two versions we generate, depending on whether we
- # catch C++ exceptions, imported via .importcpp or not. The
- # code can be easier if there are no imported C++ exceptions
- # to deal with.
-
# code to generate:
#
+ # XXX: There should be a standard dispatch algorithm
+ # that's used both here and with multi-methods
+ #
# try
# {
# myDiv(4, 9);
- # } catch (NimExceptionType1&) {
- # body
- # } catch (NimExceptionType2&) {
- # finallyPart()
- # raise;
- # }
- # catch(...) {
- # general_handler_body
- # }
- # finallyPart();
-
- template genExceptBranchBody(body: PNode) {.dirty.} =
- genRestoreFrameAfterException(p)
- expr(p, body, d)
-
+ # } catch (NimException& exp) {
+ # if (isObj(exp, EIO) {
+ # ...
+ # } else if (isObj(exp, ESystem) {
+ # ...
+ # finallyPart()
+ # raise;
+ # } else {
+ # // general handler
+ # }
+ # }
+ # finallyPart();
if not isEmptyType(t.typ) and d.k == locNone:
getTemp(p, t.typ, d)
genLineDir(p, t)
- cgsym(p.module, "popCurrentExceptionEx")
- let fin = if t[^1].kind == nkFinally: t[^1] else: nil
- p.nestedTryStmts.add((fin, false, 0.Natural))
+ let exc = getTempName(p.module)
+ if getCompilerProc("Exception") != nil:
+ discard cgsym(p.module, "Exception")
+ else:
+ discard cgsym(p.module, "E_Base")
+ add(p.nestedTryStmts, t)
startBlock(p, "try {$n")
- expr(p, t[0], d)
- endBlock(p)
-
+ expr(p, t.sons[0], d)
+ let length = sonsLen(t)
+ endBlock(p, ropecg(p.module, "} catch (NimException& $1) {$n", [exc]))
+ if optStackTrace in p.options:
+ linefmt(p, cpsStmts, "#setFrame((TFrame*)&FR);$n")
+ inc p.inExceptBlock
+ var i = 1
var catchAllPresent = false
-
- p.nestedTryStmts[^1].inExcept = true
- for i in 1.. 1: addf(p.s(cpsStmts), "else ", [])
+ if blen == 1:
# general except section:
catchAllPresent = true
- startBlock(p, "catch (...) {$n")
- genExceptBranchBody(t[i][0])
- endBlock(p)
- else:
- for j in 0.. 1: lineF(p, cpsStmts, "else", [])
startBlock(p)
- # we handled the exception, remember this:
- linefmt(p, cpsStmts, "*nimErr_ = NIM_FALSE;$n", [])
- expr(p, t[i][0], d)
+ expr(p, t.sons[i].sons[0], d)
+ linefmt(p, cpsStmts, "#popCurrentException();$n")
+ endBlock(p)
else:
- var orExpr = newRopeAppender()
- for j in 0..$1, $2, $3)", [memberName, checkFor, $genDisplayElem(MD5Digest(hashType(t[i][j].typ, p.config)))])
- else:
- let checkFor = genTypeInfoV1(p.module, t[i][j].typ, t[i][j].info)
- appcg(p.module, orExpr, "#isObj(#nimBorrowCurrentException()->$1, $2)", [memberName, checkFor])
-
- if i > 1: line(p, cpsStmts, "else ")
- startBlock(p, "if ($1) {$n", [orExpr])
- # we handled the exception, remember this:
- linefmt(p, cpsStmts, "*nimErr_ = NIM_FALSE;$n", [])
- expr(p, t[i][^1], d)
-
- linefmt(p, cpsStmts, "#popCurrentException();$n", [])
- linefmt(p, cpsStmts, "LA$1_:;$n", [nextExcept])
- endBlock(p)
-
+ var orExpr: Rope = nil
+ for j in countup(0, blen - 2):
+ assert(t.sons[i].sons[j].kind == nkType)
+ if orExpr != nil: add(orExpr, "||")
+ appcg(p.module, orExpr,
+ "#isObj($1.exp->m_type, $2)",
+ [exc, genTypeInfo(p.module, t.sons[i].sons[j].typ)])
+ lineF(p, cpsStmts, "if ($1) ", [orExpr])
+ startBlock(p)
+ expr(p, t.sons[i].sons[blen-1], d)
+ linefmt(p, cpsStmts, "#popCurrentException();$n")
+ endBlock(p)
inc(i)
- discard pop(p.nestedTryStmts)
- endBlock(p)
- if i < t.len and t[i].kind == nkFinally:
+ # reraise the exception if there was no catch all
+ # and none of the handlers matched
+ if not catchAllPresent:
+ if i > 1: lineF(p, cpsStmts, "else ", [])
startBlock(p)
- if not bodyCanRaise(p, t[i][0]):
- # this is an important optimization; most destroy blocks are detected not to raise an
- # exception and so we help the C optimizer by not mutating nimErr_ pointlessly:
- genStmts(p, t[i][0])
- else:
- # pretend we did handle the error for the safe execution of the 'finally' section:
- p.procSec(cpsLocals).add(ropecg(p.module, "NIM_BOOL oldNimErrFin$1_;$n", [lab]))
- linefmt(p, cpsStmts, "oldNimErrFin$1_ = *nimErr_; *nimErr_ = NIM_FALSE;$n", [lab])
- genStmts(p, t[i][0])
- # this is correct for all these cases:
- # 1. finally is run during ordinary control flow
- # 2. finally is run after 'except' block handling: these however set the
- # error back to nil.
- # 3. finally is run for exception handling code without any 'except'
- # handler present or only handlers that did not match.
- linefmt(p, cpsStmts, "*nimErr_ = oldNimErrFin$1_;$n", [lab])
- endBlock(p)
- raiseExit(p)
- if hasExcept: inc p.withinTryWithExcept
+ var finallyBlock = t.lastSon
+ if finallyBlock.kind == nkFinally:
+ #expr(p, finallyBlock.sons[0], d)
+ genStmts(p, finallyBlock.sons[0])
-proc genTrySetjmp(p: BProc, t: PNode, d: var TLoc) =
+ line(p, cpsStmts, ~"throw;$n")
+ endBlock(p)
+
+ lineF(p, cpsStmts, "}$n", []) # end of catch block
+ dec p.inExceptBlock
+
+ discard pop(p.nestedTryStmts)
+ if (i < length) and (t.sons[i].kind == nkFinally):
+ genSimpleBlock(p, t.sons[i].sons[0])
+
+proc genTry(p: BProc, t: PNode, d: var TLoc) =
# code to generate:
#
# XXX: There should be a standard dispatch algorithm
@@ -1372,271 +884,238 @@ proc genTrySetjmp(p: BProc, t: PNode, d: var TLoc) =
#
if not isEmptyType(t.typ) and d.k == locNone:
getTemp(p, t.typ, d)
- let quirkyExceptions = p.config.exc == excQuirky or
- (t.kind == nkHiddenTryStmt and sfSystemModule in p.module.module.flags)
- if not quirkyExceptions:
- p.module.includeHeader("")
- else:
- p.flags.incl noSafePoints
+ discard lists.includeStr(p.module.headerFiles, "")
genLineDir(p, t)
- cgsym(p.module, "Exception")
- var safePoint: Rope
- if not quirkyExceptions:
- safePoint = getTempName(p.module)
- linefmt(p, cpsLocals, "#TSafePoint $1;$n", [safePoint])
- linefmt(p, cpsStmts, "#pushSafePoint(&$1);$n", [safePoint])
- if isDefined(p.config, "nimStdSetjmp"):
- 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])
- else:
- linefmt(p, cpsStmts, "$1.status = setjmp($1.context);$n", [safePoint])
- lineCg(p, cpsStmts, "if ($1.status == 0) {$n", [safePoint])
- let fin = if t[^1].kind == nkFinally: t[^1] else: nil
- p.nestedTryStmts.add((fin, quirkyExceptions, 0.Natural))
- expr(p, t[0], d)
- if not quirkyExceptions:
- linefmt(p, cpsStmts, "#popSafePoint();$n", [])
- lineCg(p, cpsStmts, "}$n", [])
- startBlock(p, "else {$n")
- linefmt(p, cpsStmts, "#popSafePoint();$n", [])
- genRestoreFrameAfterException(p)
- elif 1 < t.len and t[1].kind == nkExceptBranch:
- startBlock(p, "if (#nimBorrowCurrentException()) {$n")
+ var safePoint = getTempName(p.module)
+ if getCompilerProc("Exception") != nil:
+ discard cgsym(p.module, "Exception")
else:
- startBlock(p)
- p.nestedTryStmts[^1].inExcept = true
+ discard cgsym(p.module, "E_Base")
+ linefmt(p, cpsLocals, "#TSafePoint $1;$n", safePoint)
+ linefmt(p, cpsStmts, "#pushSafePoint(&$1);$n", safePoint)
+ if isDefined("nimStdSetjmp"):
+ linefmt(p, cpsStmts, "$1.status = setjmp($1.context);$n", safePoint)
+ elif isDefined("nimSigSetjmp"):
+ linefmt(p, cpsStmts, "$1.status = sigsetjmp($1.context, 0);$n", safePoint)
+ elif isDefined("nimRawSetjmp"):
+ linefmt(p, cpsStmts, "$1.status = _setjmp($1.context);$n", safePoint)
+ else:
+ linefmt(p, cpsStmts, "$1.status = setjmp($1.context);$n", safePoint)
+ startBlock(p, "if ($1.status == 0) {$n", [safePoint])
+ var length = sonsLen(t)
+ add(p.nestedTryStmts, t)
+ expr(p, t.sons[0], d)
+ linefmt(p, cpsStmts, "#popSafePoint();$n")
+ endBlock(p)
+ startBlock(p, "else {$n")
+ linefmt(p, cpsStmts, "#popSafePoint();$n")
+ if optStackTrace in p.options:
+ linefmt(p, cpsStmts, "#setFrame((TFrame*)&FR);$n")
+ inc p.inExceptBlock
var i = 1
- while (i < t.len) and (t[i].kind == nkExceptBranch):
- # bug #4230: avoid false sharing between branches:
- if d.k == locTemp and isEmptyType(t.typ): d.k = locNone
- if t[i].len == 1:
+ while (i < length) and (t.sons[i].kind == nkExceptBranch):
+ var blen = sonsLen(t.sons[i])
+ if blen == 1:
# general except section:
if i > 1: lineF(p, cpsStmts, "else", [])
startBlock(p)
- if not quirkyExceptions:
- linefmt(p, cpsStmts, "$1.status = 0;$n", [safePoint])
- expr(p, t[i][0], d)
- linefmt(p, cpsStmts, "#popCurrentException();$n", [])
+ linefmt(p, cpsStmts, "$1.status = 0;$n", safePoint)
+ expr(p, t.sons[i].sons[0], d)
+ linefmt(p, cpsStmts, "#popCurrentException();$n")
endBlock(p)
else:
- var orExpr = newRopeAppender()
- for j in 0..$1, $2, $3)", [memberName, checkFor, $genDisplayElem(MD5Digest(hashType(t[i][j].typ, p.config)))])
- else:
- let checkFor = genTypeInfoV1(p.module, t[i][j].typ, t[i][j].info)
- appcg(p.module, orExpr, "#isObj(#nimBorrowCurrentException()->$1, $2)", [memberName, checkFor])
-
+ var orExpr: Rope = nil
+ for j in countup(0, blen - 2):
+ assert(t.sons[i].sons[j].kind == nkType)
+ if orExpr != nil: add(orExpr, "||")
+ let isObjFormat = if not p.module.compileToCpp:
+ "#isObj(#getCurrentException()->Sup.m_type, $1)"
+ else: "#isObj(#getCurrentException()->m_type, $1)"
+ appcg(p.module, orExpr, isObjFormat,
+ [genTypeInfo(p.module, t.sons[i].sons[j].typ)])
if i > 1: line(p, cpsStmts, "else ")
startBlock(p, "if ($1) {$n", [orExpr])
- if not quirkyExceptions:
- linefmt(p, cpsStmts, "$1.status = 0;$n", [safePoint])
- expr(p, t[i][^1], d)
- linefmt(p, cpsStmts, "#popCurrentException();$n", [])
+ linefmt(p, cpsStmts, "$1.status = 0;$n", safePoint)
+ expr(p, t.sons[i].sons[blen-1], d)
+ linefmt(p, cpsStmts, "#popCurrentException();$n")
endBlock(p)
inc(i)
+ dec p.inExceptBlock
discard pop(p.nestedTryStmts)
endBlock(p) # end of else block
- if i < t.len and t[i].kind == nkFinally:
+ if i < length and t.sons[i].kind == nkFinally:
p.finallySafePoints.add(safePoint)
- startBlock(p)
- genStmts(p, t[i][0])
- # pretend we handled the exception in a 'finally' so that we don't
- # re-raise the unhandled one but instead keep the old one (it was
- # not popped either):
- if not quirkyExceptions and getCompilerProc(p.module.g.graph, "nimLeaveFinally") != nil:
- linefmt(p, cpsStmts, "if ($1.status != 0) #nimLeaveFinally();$n", [safePoint])
- endBlock(p)
+ genSimpleBlock(p, t.sons[i].sons[0])
discard pop(p.finallySafePoints)
- if not quirkyExceptions:
- linefmt(p, cpsStmts, "if ($1.status != 0) #reraiseException();$n", [safePoint])
+ 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
+ for i in countup(0, sonsLen(t) - 1):
+ case t.sons[i].kind
of nkStrLit..nkTripleStrLit:
- res.add(it.strVal)
+ res.add(t.sons[i].strVal)
of nkSym:
- var sym = it.sym
- if sym.kind in {skProc, skFunc, skIterator, skMethod}:
+ var sym = t.sons[i].sym
+ if sym.kind in {skProc, skIterator, skMethod}:
var a: TLoc
- initLocExpr(p, it, a)
+ initLocExpr(p, t.sons[i], a)
res.add($rdLoc(a))
elif sym.kind == skType:
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)
- of nkTypeOfExpr:
- res.add($getTypeDesc(p.module, it.typ))
- else:
- discard getTypeDesc(p.module, skipTypes(it.typ, abstractPtrs))
- var a: TLoc
- initLocExpr(p, it, a)
- res.add($a.rdLoc)
+ var r = sym.loc.r
+ if r == nil:
+ # if no name has already been given,
+ # it doesn't matter much:
+ r = mangleName(sym)
+ sym.loc.r = r # but be consequent!
+ res.add($r)
+ else: internalError(t.sons[i].info, "genAsmOrEmitStmt()")
- if isAsmStmt and hasGnuAsm in CC[p.config.cCompiler].props:
+ if isAsmStmt and hasGnuAsm in CC[cCompiler].props:
for x in splitLines(res):
var j = 0
- while j < x.len and x[j] in {' ', '\t'}: inc(j)
- if j < x.len:
- if x[j] in {'"', ':'}:
- # don't modify the line if already in quotes or
- # some clobber register list:
- result.add(x); result.add("\L")
- else:
- # ignore empty lines
- result.add("\"")
- result.add(x.replace("\"", "\\\""))
- result.add("\\n\"\n")
+ while x[j] in {' ', '\t'}: inc(j)
+ if x[j] in {'"', ':'}:
+ # don't modify the line if already in quotes or
+ # some clobber register list:
+ add(result, x); add(result, tnl)
+ elif x[j] != '\0':
+ # ignore empty lines
+ add(result, "\"")
+ add(result, x)
+ add(result, "\\n\"\n")
else:
- res.add("\L")
- result.add res.rope
+ res.add(tnl)
+ 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:
if p.prc == nil:
# top level asm statement?
- p.module.s[cfsProcHeaders].add runtimeFormat(CC[p.config.cCompiler].asmStmtFrmt, [s])
+ addf(p.module.s[cfsProcHeaders], CC[cCompiler].asmStmtFrmt, [s])
else:
- addIndent p, p.s(cpsStmts)
- p.s(cpsStmts).add runtimeFormat(CC[p.config.cCompiler].asmStmtFrmt, [s])
+ lineF(p, cpsStmts, CC[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 n.len >= 1 and n.sons[0].kind in {nkStrLit..nkTripleStrLit}:
+ let sec = n.sons[0].strVal
+ 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.sons[1])
if p.prc == nil:
# top level emit pragma?
let section = determineSection(t[1])
- genCLineDir(p.module.s[section], t.info, p.config)
- p.module.s[section].add(s)
+ genCLineDir(p.module.s[section], t.info)
+ add(p.module.s[section], s)
else:
genLineDir(p, t)
line(p, cpsStmts, s)
+var
+ breakPointId: int = 0
+ gBreakpoints: Rope # later the breakpoints are inserted into the main proc
+
+proc genBreakPoint(p: BProc, t: PNode) =
+ var name: string
+ if optEndb in p.options:
+ if t.kind == nkExprColonExpr:
+ assert(t.sons[1].kind in {nkStrLit..nkTripleStrLit})
+ name = normalize(t.sons[1].strVal)
+ else:
+ inc(breakPointId)
+ name = "bp" & $breakPointId
+ genLineDir(p, t) # BUGFIX
+ appcg(p.module, gBreakpoints,
+ "#dbgRegisterBreakpoint($1, (NCSTRING)$2, (NCSTRING)$3);$n", [
+ rope(toLinenumber(t.info)), makeCString(toFilename(t.info)),
+ makeCString(name)])
+
+proc genWatchpoint(p: BProc, n: PNode) =
+ if optEndb notin p.options: return
+ var a: TLoc
+ initLocExpr(p, n.sons[1], a)
+ let typ = skipTypes(n.sons[1].typ, abstractVarRange)
+ lineCg(p, cpsStmts, "#dbgRegisterWatchpoint($1, (NCSTRING)$2, $3);$n",
+ [a.addrLoc, makeCString(renderTree(n.sons[1])),
+ genTypeInfo(p.module, typ)])
+
proc genPragma(p: BProc, n: PNode) =
- for i in 0..$3; $1++) {$n",
+ [i.r, accessor, rope(if c.p.module.compileToCpp: "len" else: "Sup.len")])
+ genTraverseProc(c, "$1->data[$2]" % [accessor, i.r], typ.sons[0])
+ lineF(p, cpsStmts, "}$n", [])
- lineF(p, cpsStmts, "for ($1 = 0; $1 < $2; $1++) {$n",
- [i.r, lenExpr(c.p, a)])
- let oldLen = p.s(cpsStmts).len
- genTraverseProc(c, "$1$3[$2]" % [accessor, i.r, dataField(c.p)], typ[0])
- if p.s(cpsStmts).len == oldLen:
- # do not emit dummy long loops for faster debug builds:
- p.s(cpsStmts) = oldCode
- else:
- lineF(p, cpsStmts, "}$n", [])
-
-proc genTraverseProc(m: BModule, origTyp: PType; sig: SigHash): Rope =
+proc genTraverseProc(m: BModule, typ: PType, reason: TTypeInfoReason): Rope =
var c: TTraversalClosure
var p = newProc(nil, m)
- result = "Marker_" & getTypeName(m, origTyp, sig)
- let
- hcrOn = m.hcrOn
- typ = origTyp.skipTypes(abstractInstOwned)
- markerName = if hcrOn: result & "_actual" else: result
- header = "static N_NIMCALL(void, $1)(void* p, NI op)" % [markerName]
- t = getTypeDesc(m, typ)
+ result = getTempName(m)
+ case reason
+ of tiNew: c.visitorFrmt = "#nimGCvisit((void*)$1, op);$n"
+ else: assert false
+
+ let header = "N_NIMCALL(void, $1)(void* p, NI op)" % [result]
+
+ let t = getTypeDesc(m, typ)
lineF(p, cpsLocals, "$1 a;$n", [t])
lineF(p, cpsInit, "a = ($1)p;$n", [t])
c.p = p
- c.visitorFrmt = "op" # "#nimGCvisit((void*)$1, op);$n"
-
assert typ.kind != tyTypeDesc
if typ.kind == tySequence:
genTraverseProcSeq(c, "a".rope, typ)
else:
- if skipTypes(typ[0], typedescInst+{tyOwned}).kind == tyArray:
+ if skipTypes(typ.sons[0], typedescInst).kind in {tyArrayConstr, tyArray}:
# C's arrays are broken beyond repair:
- genTraverseProc(c, "a".rope, typ[0])
+ genTraverseProc(c, "a".rope, typ.sons[0])
else:
- genTraverseProc(c, "(*a)".rope, typ[0])
+ genTraverseProc(c, "(*a)".rope, typ.sons[0])
- let generatedProc = "$1 {$n$2$3$4}\n" %
+ let generatedProc = "$1 {$n$2$3$4}$n" %
[header, p.s(cpsLocals), p.s(cpsInit), p.s(cpsStmts)]
- m.s[cfsProcHeaders].addf("$1;\n", [header])
+ m.s[cfsProcHeaders].addf("$1;$n", [header])
m.s[cfsProcs].add(generatedProc)
- if hcrOn:
- m.s[cfsProcHeaders].addf("N_NIMCALL_PTR(void, $1)(void*, NI);\n", [result])
- m.s[cfsDynLibInit].addf("\t$1 = (N_NIMCALL_PTR(void, )(void*, NI)) hcrRegisterProc($3, \"$1\", (void*)$2);\n",
- [result, markerName, getModuleDllPath(m)])
-
-proc genTraverseProcForGlobal(m: BModule, s: PSym; info: TLineInfo): Rope =
- discard genTypeInfoV1(m, s.loc.t, info)
+proc genTraverseProcForGlobal(m: BModule, s: PSym): Rope =
+ discard genTypeInfo(m, s.loc.t)
var c: TTraversalClosure
var p = newProc(nil, m)
- var sLoc = rdLoc(s.loc)
+ var sLoc = s.loc.r
result = getTempName(m)
- if sfThread in s.flags and emulatedThreadVars(m.config):
+ if sfThread in s.flags and emulatedThreadVars():
accessThreadLocalVar(p, s)
- sLoc = "NimTV_->" & sLoc
+ sLoc = "NimTV->" & sLoc
- c.visitorFrmt = "0" # "#nimGCvisit((void*)$1, 0);$n"
+ c.visitorFrmt = "#nimGCvisit((void*)$1, 0);$n"
c.p = p
- let header = "static N_NIMCALL(void, $1)(void)" % [result]
+ let header = "N_NIMCALL(void, $1)()" % [result]
genTraverseProc(c, sLoc, s.loc.t)
let generatedProc = "$1 {$n$2$3$4}$n" %
diff --git a/compiler/ccgtypes.nim b/compiler/ccgtypes.nim
index 98c1fc55f..e637b6c24 100644
--- a/compiler/ccgtypes.nim
+++ b/compiler/ccgtypes.nim
@@ -1,7 +1,7 @@
#
#
# The Nim Compiler
-# (c) Copyright 2017 Andreas Rumpf
+# (c) Copyright 2016 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
@@ -11,8 +11,7 @@
# ------------------------- Name Mangling --------------------------------
-import sighashes, modulegraphs
-import std/md5
+import debuginfo
proc isKeyword(w: PIdent): bool =
# Nim and C++ share some keywords
@@ -23,211 +22,170 @@ proc isKeyword(w: PIdent): bool =
ord(wInline): return true
else: return false
-proc mangleField(m: BModule; name: PIdent): string =
+proc mangleField(name: PIdent): string =
result = mangle(name.s)
- # fields are tricky to get right and thanks to generic types producing
- # duplicates we can end up mangling the same field multiple times. However
- # if we do so, the 'cppDefines' table might be modified in the meantime
- # meaning we produce inconsistent field names (see bug #5404).
- # Hence we do not check for ``m.g.config.cppDefines.contains(result)`` here
- # anymore:
if isKeyword(name):
- result.add "_0"
+ result[0] = result[0].toUpper # Mangling makes everything lowercase,
+ # but some identifiers are C keywords
-proc fillBackendName(m: BModule; s: PSym) =
- if s.loc.r == "":
- var result = s.name.s.mangle.rope
- result.add "__"
- result.add m.g.graph.ifaces[s.itemId.module].uniqueName
- result.add "_"
- result.add rope s.itemId.item
- if m.hcrOn:
- result.add "_"
- result.add(idOrSig(s, m.module.name.s.mangle, m.sigConflicts, m.config))
- s.loc.r = result
- writeMangledName(m.ndi, s, m.config)
+proc hashOwner(s: PSym): FilenameHash =
+ var m = s
+ while m.kind != skModule: m = m.owner
+ let p = m.owner
+ assert p.kind == skPackage
+ result = gDebugInfo.register(p.name.s, m.name.s)
-proc fillParamName(m: BModule; s: PSym) =
- if s.loc.r == "":
- var res = s.name.s.mangle
- res.add idOrSig(s, res, m.sigConflicts, m.config)
- # Take into account if HCR is on because of the following scenario:
- # if a module gets imported and it has some more importc symbols in it,
- # some param names might receive the "_0" suffix to distinguish from what
- # is newly available. That might lead to changes in the C code in nimcache
- # that contain only a parameter name change, but that is enough to mandate
- # recompilation of that source file and thus a new shared object will be
- # relinked. That may lead to a module getting reloaded which wasn't intended
- # and that may be fatal when parts of the current active callstack when
- # performCodeReload() was called are from the module being reloaded
- # unintentionally - example (3 modules which import one another):
- # main => proxy => reloadable
- # we call performCodeReload() in proxy to reload only changes in reloadable
- # but there is a new import which introduces an importc symbol `socket`
- # and a function called in main or proxy uses `socket` as a parameter name.
- # That would lead to either needing to reload `proxy` or to overwrite the
- # executable file for the main module, which is running (or both!) -> error.
- s.loc.r = res.rope
- writeMangledName(m.ndi, s, m.config)
-
-proc fillLocalName(p: BProc; s: PSym) =
- assert s.kind in skLocalVars+{skTemp}
- #assert sfGlobal notin s.flags
- if s.loc.r == "":
- var key = s.name.s.mangle
- let counter = p.sigConflicts.getOrDefault(key)
- var result = key.rope
- if s.kind == skTemp:
- # speed up conflict search for temps (these are quite common):
- if counter != 0: result.add "_" & rope(counter+1)
- elif counter != 0 or isKeyword(s.name) or p.module.g.config.cppDefines.contains(key):
- result.add "_" & rope(counter+1)
- p.sigConflicts.inc(key)
- s.loc.r = result
- if s.kind != skTemp: writeMangledName(p.module.ndi, s, p.config)
-
-proc scopeMangledParam(p: BProc; param: PSym) =
- ## parameter generation only takes BModule, not a BProc, so we have to
- ## remember these parameter names are already in scope to be able to
- ## generate unique identifiers reliably (consider that ``var a = a`` is
- ## even an idiom in Nim).
- var key = param.name.s.mangle
- p.sigConflicts.inc(key)
-
-const
- irrelevantForBackend = {tyGenericBody, tyGenericInst, tyGenericInvocation,
- tyDistinct, tyRange, tyStatic, tyAlias, tySink,
- tyInferred, tyOwned}
-
-proc typeName(typ: PType; result: var 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
-
-proc getTypeName(m: BModule; typ: PType; sig: SigHash): Rope =
- var t = typ
- while true:
- if t.sym != nil and {sfImportc, sfExportc} * t.sym.flags != {}:
- return t.sym.loc.r
-
- if t.kind in irrelevantForBackend:
- t = t.lastSon
+proc mangleName(s: PSym): Rope =
+ result = s.loc.r
+ if result == nil:
+ let keepOrigName = s.kind in skLocalVars - {skForVar} and
+ {sfFromGeneric, sfGlobal, sfShadowed, sfGenSym} * s.flags == {} and
+ not isKeyword(s.name)
+ # Even with all these inefficient checks, the bootstrap
+ # time is actually improved. This is probably because so many
+ # rope concatenations are now eliminated.
+ #
+ # sfFromGeneric is needed in order to avoid multiple
+ # definitions of certain variables generated in transf with
+ # names such as:
+ # `r`, `res`
+ # I need to study where these come from.
+ #
+ # about sfShadowed:
+ # consider the following Nim code:
+ # var x = 10
+ # block:
+ # var x = something(x)
+ # The generated C code will be:
+ # NI x;
+ # x = 10;
+ # {
+ # NI x;
+ # x = something(x); // Oops, x is already shadowed here
+ # }
+ # Right now, we work-around by not keeping the original name
+ # of the shadowed variable, but we can do better - we can
+ # create an alternative reference to it in the outer scope and
+ # use that in the inner scope.
+ #
+ # about isCKeyword:
+ # Nim variable names can be C keywords.
+ # We need to avoid such names in the generated code.
+ #
+ # about sfGlobal:
+ # This seems to be harder - a top level extern variable from
+ # another modules can have the same name as a local one.
+ # Maybe we should just implement sfShadowed for them too.
+ #
+ # about skForVar:
+ # These are not properly scoped now - we need to add blocks
+ # around for loops in transf
+ result = s.name.s.mangle.rope
+ if keepOrigName:
+ result.add "0"
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
- else:
- when defined(debugSigHashes):
- # check consistency:
- var tn = newRopeAppender()
- typ.typeName(tn)
- assert($typ.loc.r == $(tn & $sig))
- result = typ.loc.r
- if result == "": internalError(m.config, "getTypeName: " & $typ.kind)
+ add(result, ~"_")
+ add(result, rope(s.id))
+ add(result, ~"_")
+ add(result, rope(hashOwner(s).BiggestInt))
+ s.loc.r = result
-proc mapSetType(conf: ConfigRef; typ: PType): TCTypeKind =
- case int(getSize(conf, typ))
+proc typeName(typ: PType): Rope =
+ result = if typ.sym != nil: typ.sym.name.s.mangle.rope
+ else: ~"TY"
+
+proc getTypeName(typ: PType): Rope =
+ if typ.sym != nil and {sfImportc, sfExportc} * typ.sym.flags != {}:
+ result = typ.sym.loc.r
+ else:
+ if typ.loc.r == nil:
+ typ.loc.r = typ.typeName & typ.id.rope
+ result = typ.loc.r
+ if result == nil: internalError("getTypeName: " & $typ.kind)
+
+proc mapSetType(typ: PType): TCTypeKind =
+ case int(getSize(typ))
of 1: result = ctInt8
of 2: result = ctInt16
of 4: result = ctInt32
of 8: result = ctInt64
else: result = ctArray
-proc mapType(conf: ConfigRef; typ: PType; kind: TSymKind): TCTypeKind =
+proc mapType(typ: PType): TCTypeKind =
## Maps a Nim type to a C type
case typ.kind
- of tyNone, tyTyped: result = ctVoid
+ of tyNone, tyStmt: result = ctVoid
of tyBool: result = ctBool
of tyChar: result = ctChar
- of tyNil: result = ctPtr
- of tySet: result = mapSetType(conf, typ)
- of tyOpenArray, tyVarargs:
- if kind == skParam: result = ctArray
- else: result = ctStruct
- of tyArray, tyUncheckedArray: result = ctArray
+ of tySet: result = mapSetType(typ)
+ of tyOpenArray, tyArrayConstr, tyArray, tyVarargs: result = ctArray
of tyObject, tyTuple: result = ctStruct
- of tyUserTypeClasses:
- doAssert typ.isResolvedUserTypeClass
- return mapType(conf, typ.lastSon, kind)
of tyGenericBody, tyGenericInst, tyGenericParam, tyDistinct, tyOrdinal,
- tyTypeDesc, tyAlias, tySink, tyInferred, tyOwned:
- result = mapType(conf, lastSon(typ), kind)
+ tyConst, tyMutable, tyIter, tyTypeDesc:
+ result = mapType(lastSon(typ))
of tyEnum:
- if firstOrd(conf, typ) < 0:
+ if firstOrd(typ) < 0:
result = ctInt32
else:
- case int(getSize(conf, typ))
+ case int(getSize(typ))
of 1: result = ctUInt8
of 2: result = ctUInt16
of 4: result = ctInt32
of 8: result = ctInt64
- else: result = ctInt32
- of tyRange: result = mapType(conf, typ[0], kind)
- of tyPtr, tyVar, tyLent, tyRef:
+ else: internalError("mapType")
+ of tyRange: result = mapType(typ.sons[0])
+ of tyPtr, tyVar, tyRef:
var base = skipTypes(typ.lastSon, typedescInst)
case base.kind
- of tyOpenArray, tyArray, tyVarargs, tyUncheckedArray: result = ctPtrToArray
- of tySet:
- if mapSetType(conf, base) == ctArray: result = ctPtrToArray
- else: result = ctPtr
+ of tyOpenArray, tyArrayConstr, tyArray, tyVarargs: result = ctPtrToArray
+ #of tySet:
+ # if mapSetType(base) == ctArray: result = ctPtrToArray
+ # else: result = ctPtr
+ # XXX for some reason this breaks the pegs module
else: result = ctPtr
of tyPointer: result = ctPtr
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: internalError("mapType")
-proc mapReturnType(conf: ConfigRef; typ: PType): TCTypeKind =
+proc mapReturnType(typ: PType): TCTypeKind =
#if skipTypes(typ, typedescInst).kind == tyArray: result = ctPtr
#else:
- result = mapType(conf, typ, skResult)
+ result = mapType(typ)
proc isImportedType(t: PType): bool =
result = t.sym != nil and sfImportc in t.sym.flags
proc isImportedCppType(t: PType): bool =
- let x = t.skipTypes(irrelevantForBackend)
- result = (t.sym != nil and sfInfixCall in t.sym.flags) or
- (x.sym != nil and sfInfixCall in x.sym.flags)
+ result = t.sym != nil and sfInfixCall in t.sym.flags
-proc getTypeDescAux(m: BModule, origTyp: PType, check: var IntSet; kind: TSymKind): Rope
+proc getTypeDescAux(m: BModule, typ: PType, check: var IntSet): Rope
+proc needsComplexAssignment(typ: PType): bool =
+ result = containsGarbageCollectedRef(typ)
proc isObjLackingTypeField(typ: PType): bool {.inline.} =
result = (typ.kind == tyObject) and ((tfFinal in typ.flags) and
- (typ[0] == nil) or isPureObject(typ))
+ (typ.sons[0] == nil) or isPureObject(typ))
-proc isInvalidReturnType(conf: ConfigRef; typ: PType, isProc = true): bool =
+proc isInvalidReturnType(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)
+ case mapType(rettype)
of ctArray:
result = not (skipTypes(rettype, typedescInst).kind in
- {tyVar, tyLent, tyRef, tyPtr})
+ {tyVar, tyRef, tyPtr})
of ctStruct:
let t = skipTypes(rettype, typedescInst)
if rettype.isImportedCppType or t.isImportedCppType: return false
- result = containsGarbageCollectedRef(t) or
+ result = needsComplexAssignment(t) or
(t.kind == tyObject and not isObjLackingTypeField(t))
else: result = false
@@ -236,36 +194,46 @@ const
"N_STDCALL", "N_CDECL", "N_SAFECALL",
"N_SYSCALL", # this is probably not correct for all platforms,
# but one can #define it to what one wants
- "N_INLINE", "N_NOINLINE", "N_FASTCALL", "N_THISCALL", "N_CLOSURE", "N_NOCONV"]
+ "N_INLINE", "N_NOINLINE", "N_FASTCALL", "N_CLOSURE", "N_NOCONV"]
-proc cacheGetType(tab: TypeCache; sig: SigHash): Rope =
+proc cacheGetType(tab: TIdTable, key: PType): Rope =
# returns nil if we need to declare this type
# since types are now unique via the ``getUniqueType`` mechanism, this slow
# linear search is not necessary anymore:
- result = tab.getOrDefault(sig)
+ result = Rope(idTableGet(tab, key))
-proc addAbiCheck(m: BModule, t: PType, name: Rope) =
- if isDefined(m.config, "checkAbi") and (let size = getSize(m.config, t); size != szUnknownSize):
- var msg = "backend & Nim disagree on size for: "
- msg.addTypeHeader(m.config, t)
- var msg2 = ""
- msg2.addQuoted msg # not a hostspot so extra allocation doesn't matter
- 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 getTempName(m: BModule): Rope =
+ result = m.tmpBase & rope(m.labels)
+ inc m.labels
+proc ccgIntroducedPtr(s: PSym): bool =
+ var pt = skipTypes(s.typ, typedescInst)
+ assert skResult != s.kind
+ if tfByRef in pt.flags: return true
+ elif tfByCopy in pt.flags: return false
+ case pt.kind
+ of tyObject:
+ if (optByRef in s.options) or (getSize(pt) > platform.floatSize * 2):
+ result = true # requested anyway
+ elif (tfFinal in pt.flags) and (pt.sons[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(pt) > platform.floatSize*2) or (optByRef in s.options)
+ else: result = false
-proc fillResult(conf: ConfigRef; param: PNode, proctype: PType) =
- fillLoc(param.sym.loc, locParam, param, "Result",
+proc fillResult(param: PSym) =
+ fillLoc(param.loc, locParam, param.typ, ~"Result",
OnStack)
- let t = param.sym.typ
- if mapReturnType(conf, t) != ctArray and isInvalidReturnType(conf, proctype):
- incl(param.sym.loc.flags, lfIndirect)
- param.sym.loc.storage = OnUnknown
+ if mapReturnType(param.typ) != ctArray and isInvalidReturnType(param.typ):
+ incl(param.loc.flags, lfIndirect)
+ param.loc.s = OnUnknown
-proc typeNameOrLiteral(m: BModule; t: PType, literal: string): Rope =
+proc typeNameOrLiteral(t: PType, literal: string): Rope =
if t.sym != nil and sfImportc in t.sym.flags and t.sym.magic == mNone:
- useHeader(m, t.sym)
- result = t.sym.loc.r
+ result = getTypeName(t)
else:
result = rope(literal)
@@ -277,144 +245,71 @@ proc getSimpleTypeDesc(m: BModule, typ: PType): Rope =
"NU", "NU8", "NU16", "NU32", "NU64"]
case typ.kind
of tyPointer:
- result = typeNameOrLiteral(m, typ, "void*")
+ result = typeNameOrLiteral(typ, "void*")
of tyString:
- case detectStrVersion(m)
- of 2:
- cgsym(m, "NimStrPayload")
- cgsym(m, "NimStringV2")
- result = typeNameOrLiteral(m, typ, "NimStringV2")
- else:
- cgsym(m, "NimStringDesc")
- result = typeNameOrLiteral(m, typ, "NimStringDesc*")
- 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*")
+ discard cgsym(m, "NimStringDesc")
+ result = typeNameOrLiteral(typ, "NimStringDesc*")
+ of tyCString: result = typeNameOrLiteral(typ, "NCSTRING")
+ of tyBool: result = typeNameOrLiteral(typ, "NIM_BOOL")
+ of tyChar: result = typeNameOrLiteral(typ, "NIM_CHAR")
+ of tyNil: result = typeNameOrLiteral(typ, "0")
of tyInt..tyUInt64:
- result = typeNameOrLiteral(m, typ, NumericalTypeToStr[typ.kind])
- of tyDistinct, tyRange, tyOrdinal: result = getSimpleTypeDesc(m, typ[0])
- of tyStatic:
- if typ.n != nil: result = getSimpleTypeDesc(m, lastSon typ)
- else: internalError(m.config, "tyStatic for getSimpleTypeDesc")
- of tyGenericInst, tyAlias, tySink, tyOwned:
- result = getSimpleTypeDesc(m, lastSon typ)
- else: result = ""
-
- if result != "" and typ.isImportedType():
- let sig = hashType(typ, m.config)
- if cacheGetType(m.typeCache, sig) == "":
- m.typeCache[sig] = result
+ result = typeNameOrLiteral(typ, NumericalTypeToStr[typ.kind])
+ of tyDistinct, tyRange, tyOrdinal: result = getSimpleTypeDesc(m, typ.sons[0])
+ else: result = nil
proc pushType(m: BModule, typ: PType) =
- for i in 0..high(m.typeStack):
- # pointer equality is good enough here:
- if m.typeStack[i] == typ: return
- m.typeStack.add(typ)
+ add(m.typeStack, typ)
-proc getTypePre(m: BModule, typ: PType; sig: SigHash): Rope =
+proc getTypePre(m: BModule, typ: PType): 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, typ)
proc structOrUnion(t: PType): Rope =
- let cachedUnion = rope("union")
- let cachedStruct = rope("struct")
- let t = t.skipTypes({tyAlias, tySink})
- if tfUnion in t.flags: cachedUnion
- else: cachedStruct
+ (if tfUnion in t.flags: rope("union") else: rope("struct"))
-proc addForwardStructFormat(m: BModule, structOrUnion: Rope, typename: Rope) =
- if m.compileToCpp:
- m.s[cfsForwardTypes].addf "$1 $2;$n", [structOrUnion, typename]
- else:
- m.s[cfsForwardTypes].addf "typedef $1 $2 $2;$n", [structOrUnion, typename]
+proc getForwardStructFormat(m: BModule): string =
+ if m.compileToCpp: result = "$1 $2;$n"
+ else: result = "typedef $1 $2 $2;$n"
-proc seqStar(m: BModule): string =
- if optSeqDestructors in m.config.globalOptions: result = ""
- else: result = "*"
-
-proc getTypeForward(m: BModule, typ: PType; sig: SigHash): Rope =
- result = cacheGetType(m.forwTypeCache, sig)
- if result != "": return
- result = getTypePre(m, typ, sig)
- if result != "": return
- let concrete = typ.skipTypes(abstractInst)
- case concrete.kind
+proc getTypeForward(m: BModule, typ: PType): Rope =
+ result = cacheGetType(m.forwTypeCache, typ)
+ if result != nil: return
+ result = getTypePre(m, typ)
+ if result != nil: return
+ case typ.kind
of tySequence, tyTuple, tyObject:
- result = getTypeName(m, typ, sig)
- m.forwTypeCache[sig] = result
- if not isImportedType(concrete):
- addForwardStructFormat(m, structOrUnion(typ), result)
- else:
- pushType(m, concrete)
- doAssert m.forwTypeCache[sig] == result
- else: internalError(m.config, "getTypeForward(" & $typ.kind & ')')
+ result = getTypeName(typ)
+ if not isImportedType(typ):
+ addf(m.s[cfsForwardTypes], getForwardStructFormat(m),
+ [structOrUnion(typ), result])
+ idTablePut(m.forwTypeCache, typ, result)
+ else: internalError("getTypeForward(" & $typ.kind & ')')
-proc getTypeDescWeak(m: BModule; t: PType; check: var IntSet; kind: TSymKind): Rope =
+proc getTypeDescWeak(m: BModule; t: PType; check: var IntSet): Rope =
## like getTypeDescAux but creates only a *weak* dependency. In other words
## we know we only need a pointer to it so we only generate a struct forward
## declaration:
- let etB = t.skipTypes(abstractInst)
+ var etB = t.skipTypes(abstractInst)
case etB.kind
of tyObject, tyTuple:
if isImportedCppType(etB) and t.kind == tyGenericInst:
- result = getTypeDescAux(m, t, check, kind)
+ result = getTypeDescAux(m, t, check)
else:
- result = getTypeForward(m, t, hashType(t, m.config))
- pushType(m, t)
+ let x = getUniqueType(etB)
+ result = getTypeForward(m, x)
+ pushType(m, x)
of tySequence:
- let sig = hashType(t, m.config)
- 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 == "":
- result = getTypeName(m, t, sig)
- if not isImportedType(t):
- m.forwTypeCache[sig] = result
- addForwardStructFormat(m, rope"struct", result)
- let payload = result & "_Content"
- addForwardStructFormat(m, rope"struct", payload)
-
- if cacheGetType(m.typeCache, sig) == "":
- m.typeCache[sig] = result
- #echo "adding ", sig, " ", typeToString(t), " ", m.module.name.s
- appcg(m, m.s[cfsTypes],
- "struct $1 {$N" &
- " NI len; $1_Content* p;$N" &
- "};$N", [result])
- else:
- result = getTypeForward(m, t, sig) & seqStar(m)
- pushType(m, t)
+ let x = getUniqueType(etB)
+ result = getTypeForward(m, x) & "*"
+ pushType(m, x)
else:
- result = getTypeDescAux(m, t, check, kind)
-
-proc getSeqPayloadType(m: BModule; t: PType): Rope =
- var check = initIntSet()
- result = getTypeDescWeak(m, t, check, skParam) & "_Content"
- #result = getTypeForward(m, t, hashType(t)) & "_Content"
-
-proc seqV2ContentType(m: BModule; t: PType; check: var IntSet) =
- let sig = hashType(t, m.config)
- let result = cacheGetType(m.typeCache, sig)
- if result == "":
- discard getTypeDescAux(m, t, check, skVar)
- else:
- # little hack for now to prevent multiple definitions of the same
- # Seq_Content:
- appcg(m, m.s[cfsTypes], """$N
-$3ifndef $2_Content_PP
-$3define $2_Content_PP
-struct $2_Content { NI cap; $1 data[SEQ_DECL_SIZE];};
-$3endif$N
- """, [getTypeDescAux(m, t.skipTypes(abstractInst)[0], check, skVar), result, rope"#"])
+ result = getTypeDescAux(m, t, check)
proc paramStorageLoc(param: PSym): TStorageLoc =
- if param.typ.skipTypes({tyVar, tyLent, tyTypeDesc}).kind notin {
- tyArray, tyOpenArray, tyVarargs}:
+ if param.typ.skipTypes({tyVar, tyTypeDesc}).kind notin {tyArray, tyOpenArray, tyVarargs, tyArrayConstr}:
result = OnStack
else:
result = OnUnknown
@@ -422,217 +317,181 @@ 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.sons[0] == nil or isInvalidReturnType(t.sons[0]):
+ rettype = ~"void"
else:
- rettype = getTypeDescAux(m, t[0], check, skResult)
- for i in 1.. 0:
- result.addf "NIM_ALIGN($1) ", [rope(field.alignment)]
+ sname = mangleRecFieldName(field, rectype)
+ if accessExpr != nil: ae = "$1.$2" % [accessExpr, sname]
+ else: ae = sname
+ fillLoc(field.loc, locField, field.typ, ae, OnUnknown)
# for importcpp'ed objects, we only need to set field.loc, but don't
# have to recurse via 'getTypeDescAux'. And not doing so prevents problems
# with heavily templatized C++ code:
if not isImportedCppType(rectype):
- let noAlias = if sfNoalias in field.flags: " NIM_NOALIAS" else: ""
-
- let fieldType = field.loc.lode.typ.skipTypes(abstractInst)
- if fieldType.kind == tyUncheckedArray:
- result.addf("$1 $2[SEQ_DECL_SIZE];$n",
- [getTypeDescAux(m, fieldType.elemType, check, skField), sname])
+ let fieldType = field.loc.t.skipTypes(abstractInst)
+ if fieldType.kind == tyArray and tfUncheckedArray in fieldType.flags:
+ addf(result, "$1 $2[SEQ_DECL_SIZE];$n",
+ [getTypeDescAux(m, fieldType.elemType, check), sname])
elif fieldType.kind == tySequence:
# we need to use a weak dependency here for trecursive_table.
- result.addf("$1$3 $2;$n", [getTypeDescWeak(m, field.loc.t, check, skField), sname, noAlias])
+ addf(result, "$1 $2;$n", [getTypeDescWeak(m, field.loc.t, check), sname])
elif field.bitsize != 0:
- result.addf("$1$4 $2:$3;$n", [getTypeDescAux(m, field.loc.t, check, skField), sname, rope($field.bitsize), noAlias])
+ addf(result, "$1 $2:$3;$n", [getTypeDescAux(m, field.loc.t, check), sname, rope($field.bitsize)])
else:
# don't use fieldType here because we need the
# tyGenericInst for C++ template support
- result.addf("$1$3 $2;$n", [getTypeDescAux(m, field.loc.t, check, skField), sname, noAlias])
- else: internalError(m.config, n.info, "genRecordFieldsAux()")
+ addf(result, "$1 $2;$n", [getTypeDescAux(m, field.loc.t, check), sname])
+ else: internalError(n.info, "genRecordFieldsAux()")
proc getRecordFields(m: BModule, typ: PType, check: var IntSet): Rope =
- result = newRopeAppender()
- genRecordFieldsAux(m, typ.n, typ, check, result)
-
-proc fillObjectFields*(m: BModule; typ: PType) =
- # sometimes generic objects are not consistently merged. We patch over
- # this fact here.
- var check = initIntSet()
- discard getRecordFields(m, typ, check)
-
-proc mangleDynLibProc(sym: PSym): Rope
+ result = genRecordFieldsAux(m, typ.n, nil, typ, check)
proc getRecordDesc(m: BModule, typ: PType, name: Rope,
check: var IntSet): Rope =
# declare the record:
var hasField = false
- if tfPacked in typ.flags:
- if hasAttribute in CC[m.config.cCompiler].props:
- result = structOrUnion(typ) & " __attribute__((__packed__))"
- else:
- result = "#pragma pack(push, 1)\L" & structOrUnion(typ)
- else:
- result = structOrUnion(typ)
+ var attribute: Rope =
+ if tfPacked in typ.flags: rope(CC[cCompiler].packedPragma)
+ else: nil
- result.add " "
- result.add name
+ result = ropecg(m, CC[cCompiler].structStmtFmt,
+ [structOrUnion(typ), name, attribute])
if typ.kind == tyObject:
- if typ[0] == nil:
- if lacksMTypeField(typ):
+
+ if typ.sons[0] == nil:
+ if (typ.sym != nil and sfPure in typ.sym.flags) or tfFinal in typ.flags:
appcg(m, result, " {$n", [])
else:
- if optTinyRtti in m.config.globalOptions:
- appcg(m, result, " {$n#TNimTypeV2* m_type;$n", [])
- else:
- appcg(m, result, " {$n#TNimType* m_type;$n", [])
+ appcg(m, result, " {$n#TNimType* m_type;$n", [name, attribute])
hasField = true
elif m.compileToCpp:
appcg(m, result, " : public $1 {$n",
- [getTypeDescAux(m, typ[0].skipTypes(skipPtrs), check, skField)])
- if typ.isException and m.config.exc == excCpp:
- when false:
- appcg(m, result, "virtual void raise() { throw *this; }$n", []) # required for polymorphic exceptions
- if typ.sym.magic == mException:
- # Add cleanup destructor to Exception base class
- appcg(m, result, "~$1();$n", [name])
- # define it out of the class body and into the procs section so we don't have to
- # artificially forward-declare popCurrentExceptionEx (very VERY troublesome for HCR)
- appcg(m, cfsProcs, "inline $1::~$1() {if(this->raiseId) #popCurrentExceptionEx(this->raiseId);}$n", [name])
+ [getTypeDescAux(m, typ.sons[0], check)])
hasField = true
else:
appcg(m, result, " {$n $1 Sup;$n",
- [getTypeDescAux(m, typ[0].skipTypes(skipPtrs), check, skField)])
+ [getTypeDescAux(m, typ.sons[0], check)])
hasField = true
else:
- result.addf(" {$n", [name])
+ addf(result, " {$n", [name])
let desc = getRecordFields(m, typ, check)
- if desc == "" and not hasField:
- result.addf("char dummy;$n", [])
+ if desc == nil and not hasField:
+ addf(result, "char dummy;$n", [])
else:
- result.add(desc)
- result.add("};\L")
- if tfPacked in typ.flags and hasAttribute notin CC[m.config.cCompiler].props:
- result.add "#pragma pack(pop)\L"
+ add(result, desc)
+ add(result, "};" & tnl)
proc getTupleDesc(m: BModule, typ: PType, name: Rope,
check: var IntSet): Rope =
result = "$1 $2 {$n" % [structOrUnion(typ), name]
- var desc: Rope = ""
- for i in 0..= typ.len:
- doAssert false, "invalid apostrophe type parameter index"
+ # XXX: we should catch this earlier and report it as a semantic error
+ if idx >= typ.len: internalError "invalid apostrophe type parameter index"
- result = typ[idx]
+ result = typ.sons[idx]
for i in 1..stars:
if result != nil and result.len > 0:
- result = if result.kind == tyGenericInst: result[1]
+ result = if result.kind == tyGenericInst: result.sons[1]
else: result.elemType
-proc getOpenArrayDesc(m: BModule, t: PType, check: var IntSet; kind: TSymKind): Rope =
- let sig = hashType(t, m.config)
- if kind == skParam:
- result = getTypeDescWeak(m, t[0], check, kind) & "*"
- else:
- result = cacheGetType(m.typeCache, sig)
- if result == "":
- result = getTypeName(m, t, sig)
- m.typeCache[sig] = result
- let elemType = getTypeDescWeak(m, t[0], check, kind)
- m.s[cfsTypes].addf("typedef struct {$n$2* Field0;$nNI Field1;$n} $1;$n",
- [result, elemType])
-
-proc getTypeDescAux(m: BModule, origTyp: PType, check: var IntSet; kind: TSymKind): Rope =
+proc getTypeDescAux(m: BModule, typ: PType, check: var IntSet): Rope =
# returns only the type's name
-
- var t = origTyp.skipTypes(irrelevantForBackend-{tyOwned})
+ var t = getUniqueType(typ)
+ if t == nil: internalError("getTypeDescAux: t == nil")
+ if t.sym != nil: useHeader(m, t.sym)
+ result = getTypePre(m, t)
+ if result != nil: return
if containsOrIncl(check, t.id):
- if not (isImportedCppType(origTyp) or isImportedCppType(t)):
- internalError(m.config, "cannot generate C type for: " & typeToString(origTyp))
+ if not (isImportedCppType(typ) or isImportedCppType(t)):
+ internalError("cannot generate C type for: " & typeToString(typ))
# XXX: this BUG is hard to fix -> we need to introduce helper structs,
# but determining when this needs to be done is hard. We should split
# C type generation into an analysis and a code generation phase somehow.
- if t.sym != nil: useHeader(m, t.sym)
- if t != origTyp and origTyp.sym != nil: useHeader(m, origTyp.sym)
- let sig = hashType(origTyp, m.config)
-
- defer: # defer is the simplest in this case
- if isImportedType(t) and not m.typeABICache.containsOrIncl(sig):
- addAbiCheck(m, t, result)
-
- result = getTypePre(m, t, sig)
- if result != "" and t.kind != tyOpenArray:
- excl(check, t.id)
- return
case t.kind
- of tyRef, tyPtr, tyVar, tyLent:
- var star = if t.kind in {tyVar} and tfVarIsPtr notin origTyp.flags and
+ of tyRef, tyPtr, tyVar:
+ var star = if t.kind == tyVar and tfVarIsPtr notin typ.flags and
compileToCpp(m): "&" else: "*"
- var et = origTyp.skipTypes(abstractInst).lastSon
+ var et = t.lastSon
var etB = et.skipTypes(abstractInst)
- if mapType(m.config, t, kind) == ctPtrToArray and (etB.kind != tyOpenArray or kind == skParam):
- if etB.kind == tySet:
- et = getSysType(m.g.graph, unknownLineInfo, tyUInt8)
- else:
- et = elemType(etB)
+ if etB.kind in {tyArrayConstr, tyArray, tyOpenArray, tyVarargs}:
+ # this is correct! sets have no proper base type, so we treat
+ # ``var set[char]`` in `getParamTypeDesc`
+ et = elemType(etB)
etB = et.skipTypes(abstractInst)
star[0] = '*'
case etB.kind
of tyObject, tyTuple:
if isImportedCppType(etB) and et.kind == tyGenericInst:
- result = getTypeDescAux(m, et, check, kind) & star
+ result = getTypeDescAux(m, et, check) & star
else:
# no restriction! We have a forward declaration for structs
- let name = getTypeForward(m, et, hashType(et, m.config))
+ let x = getUniqueType(etB)
+ let name = getTypeForward(m, x)
result = name & star
- m.typeCache[sig] = result
+ idTablePut(m.typeCache, t, result)
+ pushType(m, x)
of tySequence:
- if optSeqDestructors in m.config.globalOptions:
- result = getTypeDescWeak(m, et, check, kind) & star
- m.typeCache[sig] = result
- else:
- # no restriction! We have a forward declaration for structs
- let name = getTypeForward(m, et, hashType(et, m.config))
- result = name & seqStar(m) & star
- m.typeCache[sig] = result
- pushType(m, et)
+ # no restriction! We have a forward declaration for structs
+ let x = getUniqueType(etB)
+ let name = getTypeForward(m, x)
+ result = name & "*" & star
+ idTablePut(m.typeCache, t, result)
+ pushType(m, x)
else:
# else we have a strong dependency :-(
- result = getTypeDescAux(m, et, check, kind) & star
- m.typeCache[sig] = result
+ result = getTypeDescAux(m, et, check) & star
+ idTablePut(m.typeCache, t, result)
of tyOpenArray, tyVarargs:
- result = getOpenArrayDesc(m, t, check, kind)
- of tyEnum:
- result = cacheGetType(m.typeCache, sig)
- if result == "":
- result = getTypeName(m, origTyp, sig)
+ result = getTypeDescWeak(m, t.sons[0], check) & "*"
+ idTablePut(m.typeCache, t, result)
+ of tyRange, tyEnum:
+ let t = if t.kind == tyRange: t.lastSon else: t
+ result = cacheGetType(m.typeCache, t)
+ if result == nil:
+ result = getTypeName(t)
if not (isImportedCppType(t) or
(sfImportc in t.sym.flags and t.sym.magic == mNone)):
- m.typeCache[sig] = result
+ idTablePut(m.typeCache, t, result)
var size: int
- if firstOrd(m.config, t) < 0:
- m.s[cfsTypes].addf("typedef NI32 $1;$n", [result])
+ if firstOrd(t) < 0:
+ addf(m.s[cfsTypes], "typedef NI32 $1;$n", [result])
size = 4
else:
- size = int(getSize(m.config, t))
+ size = int(getSize(t))
case size
- of 1: m.s[cfsTypes].addf("typedef NU8 $1;$n", [result])
- of 2: m.s[cfsTypes].addf("typedef NU16 $1;$n", [result])
- of 4: m.s[cfsTypes].addf("typedef NI32 $1;$n", [result])
- of 8: m.s[cfsTypes].addf("typedef NI64 $1;$n", [result])
- else: internalError(m.config, t.sym.info, "getTypeDescAux: enum")
- when false:
- let owner = hashOwner(t.sym)
- if not gDebugInfo.hasEnum(t.sym.name.s, t.sym.info.line, owner):
- var vals: seq[(string, int)] = @[]
- for i in 0.. 1: result.add(" COMMA ")
- addResultType(tt[i])
+ result.add(getTypeDescAux(m, typ.sons[i], check))
result.add("> ")
# always call for sideeffects:
assert t.kind != tyTuple
discard getRecordDesc(m, t, result, check)
- # 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
- m.s[cfsTypes].addf("typedef $1 $2;$n", [result, typedefName])
- m.typeCache[sig] = typedefName
- result = typedefName
else:
- result = cacheGetType(m.forwTypeCache, sig)
- if result == "":
- result = getTypeName(m, origTyp, sig)
- m.forwTypeCache[sig] = result
+ result = cacheGetType(m.forwTypeCache, t)
+ if result == nil:
+ result = getTypeName(t)
if not isImportedType(t):
- addForwardStructFormat(m, structOrUnion(t), result)
- assert m.forwTypeCache[sig] == result
- m.typeCache[sig] = result # always call for sideeffects:
- if not incompleteType(t):
- let recdesc = if t.kind != tyTuple: getRecordDesc(m, t, result, check)
- else: getTupleDesc(m, t, result, check)
- if not isImportedType(t):
- m.s[cfsTypes].add(recdesc)
- elif tfIncompleteStruct notin t.flags:
- discard # addAbiCheck(m, t, result) # already handled elsewhere
+ addf(m.s[cfsForwardTypes], getForwardStructFormat(m),
+ [structOrUnion(t), result])
+ idTablePut(m.forwTypeCache, t, result)
+ idTablePut(m.typeCache, t, result) # always call for sideeffects:
+ let recdesc = if t.kind != tyTuple: getRecordDesc(m, t, result, check)
+ else: getTupleDesc(m, t, result, check)
+ if not isImportedType(t): add(m.s[cfsTypes], recdesc)
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)
- m.typeCache[sig] = result
+ result = getTypeName(t.lastSon) & "Set"
+ idTablePut(m.typeCache, t, result)
if not isImportedType(t):
- let s = int(getSize(m.config, t))
+ let s = int(getSize(t))
case s
- of 1, 2, 4, 8: m.s[cfsTypes].addf("typedef NU$2 $1;$n", [result, rope(s*8)])
- else: m.s[cfsTypes].addf("typedef NU8 $1[$2];$n",
- [result, rope(getSize(m.config, t))])
- of tyGenericInst, tyDistinct, tyOrdinal, tyTypeDesc, tyAlias, tySink, tyOwned,
- tyUserTypeClass, tyUserTypeClassInst, tyInferred:
- result = getTypeDescAux(m, lastSon(t), check, kind)
+ of 1, 2, 4, 8: addf(m.s[cfsTypes], "typedef NU$2 $1;$n", [result, rope(s*8)])
+ else: addf(m.s[cfsTypes], "typedef NU8 $1[$2];$n",
+ [result, rope(getSize(t))])
+ of tyGenericInst, tyDistinct, tyOrdinal, tyConst, tyMutable,
+ tyIter, tyTypeDesc:
+ result = getTypeDescAux(m, lastSon(t), check)
else:
- internalError(m.config, "getTypeDescAux(" & $t.kind & ')')
- result = ""
+ internalError("getTypeDescAux(" & $t.kind & ')')
+ result = nil
# fixes bug #145:
excl(check, t.id)
-proc getTypeDesc(m: BModule, typ: PType; kind = skParam): Rope =
+proc getTypeDesc(m: BModule, typ: PType): Rope =
var check = initIntSet()
- result = getTypeDescAux(m, typ, check, kind)
+ result = getTypeDescAux(m, typ, check)
type
- TClosureTypeKind = enum ## In C closures are mapped to 3 different things.
- clHalf, ## fn(args) type without the trailing 'void* env' parameter
- clHalfWithEnv, ## fn(args, void* env) type with trailing 'void* env' parameter
- clFull ## struct {fn(args, void* env), env}
+ TClosureTypeKind = enum
+ clHalf, clHalfWithEnv, clFull
proc getClosureType(m: BModule, t: PType, kind: TClosureTypeKind): Rope =
assert t.kind == tyProc
@@ -928,79 +727,62 @@ proc getClosureType(m: BModule, t: PType, kind: TClosureTypeKind): Rope =
genProcParams(m, t, rettype, desc, check, declareEnvironment=kind != clHalf)
if not isImportedType(t):
if t.callConv != ccClosure or kind != clFull:
- m.s[cfsTypes].addf("typedef $1_PTR($2, $3) $4;$n",
+ addf(m.s[cfsTypes], "typedef $1_PTR($2, $3) $4;$n",
[rope(CallingConvToStr[t.callConv]), rettype, result, desc])
else:
- m.s[cfsTypes].addf("typedef struct {$n" &
- "N_NIMCALL_PTR($2, ClP_0) $3;$n" &
- "void* ClE_0;$n} $1;$n",
+ addf(m.s[cfsTypes], "typedef struct {$n" &
+ "N_NIMCALL_PTR($2, ClPrc) $3;$n" &
+ "void* ClEnv;$n} $1;$n",
[result, rettype, desc])
+proc getTypeDesc(m: BModule, magic: string): Rope =
+ var sym = magicsys.getCompilerProc(magic)
+ if sym != nil:
+ result = getTypeDesc(m, sym.typ)
+ else:
+ rawMessage(errSystemNeeds, magic)
+ result = nil
+
proc finishTypeDescriptions(m: BModule) =
var i = 0
- var check = initIntSet()
- while i < m.typeStack.len:
- let t = m.typeStack[i]
- if optSeqDestructors in m.config.globalOptions and t.skipTypes(abstractInst).kind == tySequence:
- seqV2ContentType(m, t, check)
- else:
- discard getTypeDescAux(m, t, check, skParam)
+ while i < len(m.typeStack):
+ discard getTypeDesc(m, m.typeStack[i])
inc(i)
- m.typeStack.setLen 0
-template cgDeclFrmt*(s: PSym): string =
- s.constraint.strVal
+template cgDeclFrmt*(s: PSym): string = s.constraint.strVal
-proc isReloadable(m: BModule, prc: PSym): bool =
- return m.hcrOn and sfNonReloadable notin prc.flags
-
-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): Rope =
+ var
+ rettype, params: Rope
+ genCLineDir(result, prc.info)
# 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:
+ result.add "static "
var check = initIntSet()
- fillBackendName(m, prc)
- fillLoc(prc.loc, locProc, prc.ast[namePos], OnUnknown)
- var rettype, params: Rope
+ fillLoc(prc.loc, locProc, prc.typ, mangleName(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
- let asPtrStr = rope(if asPtr: "_PTR" else: "")
- var name = prc.loc.r
- if isReloadable(m, prc) and not asPtr:
- name.add("_actual")
# 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,
+ addf(result, "$1($2, $3)$4",
+ [rope(CallingConvToStr[prc.typ.callConv]), rettype, prc.loc.r,
params])
else:
- let asPtrStr = if asPtr: (rope("(*") & name & ")") else: name
- result.add runtimeFormat(prc.cgDeclFrmt, [rettype, asPtrStr, params])
+ result = prc.cgDeclFrmt % [rettype, prc.loc.r, params]
# ------------------ type info generation -------------------------------------
-proc genTypeInfoV1(m: BModule, t: PType; info: TLineInfo): Rope
+proc genTypeInfo(m: BModule, t: PType): Rope
proc getNimNode(m: BModule): Rope =
result = "$1[$2]" % [m.typeNodesName, rope(m.typeNodes)]
inc(m.typeNodes)
-proc tiNameForHcr(m: BModule, name: Rope): Rope =
- return if m.hcrOn: "(*".rope & name & ")" else: name
-
-proc genTypeInfoAuxBase(m: BModule; typ, origType: PType;
- name, base: Rope; info: TLineInfo) =
+proc genTypeInfoAuxBase(m: BModule; typ, origType: PType; name, base: Rope) =
var nimtypeKind: int
#allocMemTI(m, typ, name)
if isObjLackingTypeField(typ):
@@ -1008,607 +790,268 @@ proc genTypeInfoAuxBase(m: BModule; typ, origType: PType;
else:
nimtypeKind = ord(typ.kind)
- let nameHcr = tiNameForHcr(m, name)
-
var size: Rope
- if tfIncompleteStruct in typ.flags:
- size = rope"void*"
- else:
- size = getTypeDesc(m, origType, skVar)
- m.s[cfsTypeInit3].addf(
- "$1.size = sizeof($2);$n$1.align = NIM_ALIGNOF($2);$n$1.kind = $3;$n$1.base = $4;$n",
- [nameHcr, size, rope(nimtypeKind), base]
- )
+ if tfIncompleteStruct in typ.flags: size = rope"void*"
+ elif m.compileToCpp: size = getTypeDesc(m, origType)
+ else: size = getTypeDesc(m, typ)
+ addf(m.s[cfsTypeInit3],
+ "$1.size = sizeof($2);$n" & "$1.kind = $3;$n" & "$1.base = $4;$n",
+ [name, size, rope(nimtypeKind), base])
# compute type flags for GC optimization
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")
- if isDefined(m.config, "nimTypeNames"):
- var typename = typeToString(if origType.typeInst != nil: origType.typeInst
- else: origType, preferName)
- if typename == "ref object" and origType.skipTypes(skipPtrs).sym != nil:
- 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")
- m.s[cfsTypeInit3].addf("$1.nextType = nimTypeRoot; nimTypeRoot=&$1;$n",
- [nameHcr])
+ addf(m.s[cfsTypeInit3], "$1.flags = $2;$n", [name, rope(flags)])
+ discard cgsym(m, "TNimType")
+ addf(m.s[cfsVars], "TNimType $1; /* $2 */$n",
+ [name, rope(typeToString(typ))])
- if m.hcrOn:
- m.s[cfsStrData].addf("static TNimType* $1;$n", [name])
- m.hcrCreateTypeInfosProc.addf("\thcrRegisterGlobal($2, \"$1\", sizeof(TNimType), NULL, (void**)&$1);$n",
- [name, getModuleDllPath(m, m.module)])
- else:
- m.s[cfsStrData].addf("N_LIB_PRIVATE TNimType $1;$n", [name])
-
-proc genTypeInfoAux(m: BModule, typ, origType: PType, name: Rope;
- info: TLineInfo) =
+proc genTypeInfoAux(m: BModule, typ, origType: PType, name: Rope) =
var base: Rope
- if typ.len > 0 and typ.lastSon != nil:
- var x = typ.lastSon
- if typ.kind == tyObject: x = x.skipTypes(skipPtrs)
- if typ.kind == tyPtr and x.kind == tyObject and incompleteType(x):
- base = rope("0")
- else:
- base = genTypeInfoV1(m, x, info)
+ if (sonsLen(typ) > 0) and (typ.sons[0] != nil):
+ base = genTypeInfo(m, typ.sons[0])
else:
base = rope("0")
- genTypeInfoAuxBase(m, typ, origType, name, base, info)
+ genTypeInfoAuxBase(m, typ, origType, name, base)
proc discriminatorTableName(m: BModule, objtype: PType, d: PSym): Rope =
# bugfix: we need to search the type that contains the discriminator:
- var objtype = objtype.skipTypes(abstractPtrs)
+ var objtype = objtype
while lookupInRecord(objtype.n, d.name) == nil:
- objtype = objtype[0].skipTypes(abstractPtrs)
+ objtype = objtype.sons[0]
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)]
-
-proc rope(arg: Int128): Rope = rope($arg)
+ internalError(d.info, "anonymous obj with discriminator")
+ result = "NimDT_$1_$2" % [rope(objtype.id), rope(d.name.s.mangle)]
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)]
+ result = "TNimNode* $1[$2];$n" % [tmp, rope(lengthOrd(d.typ)+1)]
-proc genTNimNodeArray(m: BModule, name: Rope, size: Rope) =
- if m.hcrOn:
- m.s[cfsData].addf("static TNimNode** $1;$n", [name])
- m.hcrCreateTypeInfosProc.addf("\thcrRegisterGlobal($3, \"$1\", sizeof(TNimNode*) * $2, NULL, (void**)&$1);$n",
- [name, size, getModuleDllPath(m, m.module)])
- else:
- m.s[cfsTypeInit1].addf("static TNimNode* $1[$2];$n", [name, size])
-
-proc genObjectFields(m: BModule, typ, origType: PType, n: PNode, expr: Rope;
- info: TLineInfo) =
+proc genObjectFields(m: BModule, typ: PType, n: PNode, expr: Rope) =
case n.kind
of nkRecList:
- if n.len == 1:
- genObjectFields(m, typ, origType, n[0], expr, info)
- elif n.len > 0:
- var tmp = getTempName(m) & "_" & $n.len
- genTNimNodeArray(m, tmp, rope(n.len))
- for i in 0.. 0:
+ var tmp = getTempName(m)
+ addf(m.s[cfsTypeInit1], "static TNimNode* $1[$2];$n", [tmp, rope(L)])
+ for i in countup(0, L-1):
var tmp2 = getNimNode(m)
- m.s[cfsTypeInit3].addf("$1[$2] = &$3;$n", [tmp, rope(i), tmp2])
- genObjectFields(m, typ, origType, n[i], tmp2, info)
- m.s[cfsTypeInit3].addf("$1.len = $2; $1.kind = 2; $1.sons = &$3[0];$n",
- [expr, rope(n.len), tmp])
+ addf(m.s[cfsTypeInit3], "$1[$2] = &$3;$n", [tmp, rope(i), tmp2])
+ genObjectFields(m, typ, n.sons[i], tmp2)
+ addf(m.s[cfsTypeInit3], "$1.len = $2; $1.kind = 2; $1.sons = &$3[0];$n",
+ [expr, rope(L), tmp])
else:
- m.s[cfsTypeInit3].addf("$1.len = $2; $1.kind = 2;$n", [expr, rope(n.len)])
+ addf(m.s[cfsTypeInit3], "$1.len = $2; $1.kind = 2;$n", [expr, rope(L)])
of nkRecCase:
- assert(n[0].kind == nkSym)
- var field = n[0].sym
+ assert(n.sons[0].kind == nkSym)
+ var field = n.sons[0].sym
var tmp = discriminatorTableName(m, typ, field)
- var L = lengthOrd(m.config, field.typ)
+ var L = lengthOrd(field.typ)
assert L > 0
- if field.loc.r == "": fillObjectFields(m, typ)
- if field.loc.t == nil:
- internalError(m.config, n.info, "genObjectFields")
- m.s[cfsTypeInit3].addf("$1.kind = 3;$n" &
+ addf(m.s[cfsTypeInit3], "$1.kind = 3;$n" &
"$1.offset = offsetof($2, $3);$n" & "$1.typ = $4;$n" &
"$1.name = $5;$n" & "$1.sons = &$6[0];$n" &
- "$1.len = $7;$n", [expr, getTypeDesc(m, origType, skVar), field.loc.r,
- genTypeInfoV1(m, field.typ, info),
+ "$1.len = $7;$n", [expr, getTypeDesc(m, typ), field.loc.r,
+ genTypeInfo(m, field.typ),
makeCString(field.name.s),
tmp, rope(L)])
- m.s[cfsData].addf("TNimNode* $1[$2];$n", [tmp, rope(L+1)])
- for i in 1.. 0:
- var tmp = getTempName(m) & "_" & $typ.len
- genTNimNodeArray(m, tmp, rope(typ.len))
- for i in 0.. 0:
+ var tmp = getTempName(m)
+ addf(m.s[cfsTypeInit1], "static TNimNode* $1[$2];$n", [tmp, rope(length)])
+ for i in countup(0, length - 1):
+ var a = typ.sons[i]
var tmp2 = getNimNode(m)
- m.s[cfsTypeInit3].addf("$1[$2] = &$3;$n", [tmp, rope(i), tmp2])
- m.s[cfsTypeInit3].addf("$1.kind = 1;$n" &
+ addf(m.s[cfsTypeInit3], "$1[$2] = &$3;$n", [tmp, rope(i), tmp2])
+ addf(m.s[cfsTypeInit3], "$1.kind = 1;$n" &
"$1.offset = offsetof($2, Field$3);$n" &
"$1.typ = $4;$n" &
"$1.name = \"Field$3\";$n",
- [tmp2, getTypeDesc(m, origType, skVar), rope(i), genTypeInfoV1(m, a, info)])
- m.s[cfsTypeInit3].addf("$1.len = $2; $1.kind = 2; $1.sons = &$3[0];$n",
- [expr, rope(typ.len), tmp])
+ [tmp2, getTypeDesc(m, typ), rope(i), genTypeInfo(m, a)])
+ addf(m.s[cfsTypeInit3], "$1.len = $2; $1.kind = 2; $1.sons = &$3[0];$n",
+ [expr, rope(length), tmp])
else:
- m.s[cfsTypeInit3].addf("$1.len = $2; $1.kind = 2;$n",
- [expr, rope(typ.len)])
- m.s[cfsTypeInit3].addf("$1.node = &$2;$n", [tiNameForHcr(m, name), expr])
+ addf(m.s[cfsTypeInit3], "$1.len = $2; $1.kind = 2;$n",
+ [expr, rope(length)])
+ addf(m.s[cfsTypeInit3], "$1.node = &$2;$n", [name, expr])
-proc genEnumInfo(m: BModule, typ: PType, name: Rope; info: TLineInfo) =
+proc genEnumInfo(m: BModule, typ: PType, name: Rope) =
# Type information for enumerations is quite heavy, so we do some
# optimizations here: The ``typ`` field is never set, as it is redundant
# anyway. We generate a cstring array and a loop over it. Exceptional
# positions will be reset after the loop.
- genTypeInfoAux(m, typ, typ, name, info)
- var nodePtrs = getTempName(m) & "_" & $typ.n.len
- genTNimNodeArray(m, nodePtrs, rope(typ.n.len))
+ genTypeInfoAux(m, typ, typ, name)
+ var nodePtrs = getTempName(m)
+ var length = sonsLen(typ.n)
+ addf(m.s[cfsTypeInit1], "static TNimNode* $1[$2];$n",
+ [nodePtrs, rope(length)])
var enumNames, specialCases: Rope
var firstNimNode = m.typeNodes
var hasHoles = false
- for i in 0..= 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
-
- 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 =
+proc genTypeInfo(m: BModule, t: PType): Rope =
let origType = t
- # distinct types can have their own destructors
- var t = skipTypes(origType, irrelevantForBackend + tyUserTypeClasses - {tyDistinct})
+ var t = getUniqueType(t)
+ result = "NTI$1" % [rope(t.id)]
+ if containsOrIncl(m.typeInfoMarker, t.id):
+ return "(&".rope & result & ")".rope
- let prefixTI = if m.hcrOn: "(" else: "(&"
-
- let sig = hashType(origType, m.config)
- result = m.typeInfoMarkerV2.getOrDefault(sig)
- if result != "":
- return prefixTI.rope & result & ")".rope
-
- let marker = m.g.typeInfoMarkerV2.getOrDefault(sig)
- if marker.str != "":
- cgsym(m, "TNimTypeV2")
- declareNimType(m, "TNimTypeV2", marker.str, marker.owner)
- # also store in local type section:
- m.typeInfoMarkerV2[sig] = marker.str
- return prefixTI.rope & marker.str & ")".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):
+ # getUniqueType doesn't skip tyDistinct when that has an overriden operation:
+ while t.kind == tyDistinct: t = t.lastSon
+ 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)
+ discard genTypeInfo(owner.bmod, t)
# reference the type info as extern here
- 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)
- result = prefixTI.rope & result & ")".rope
-
-proc openArrayToTuple(m: BModule; t: PType): PType =
- result = newType(tyTuple, nextTypeId m.idgen, t.owner)
- let p = newType(tyPtr, nextTypeId m.idgen, t.owner)
- let a = newType(tyUncheckedArray, nextTypeId m.idgen, t.owner)
- a.add t.lastSon
- p.add a
- 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..= gcMarkAndSweep:
+ let markerProc = genTraverseProc(m, t, tiNew)
+ addf(m.s[cfsTypeInit3], "$1.marker = $2;$n", [result, markerProc])
+ of tyPtr, tyRange: genTypeInfoAux(m, t, t, result)
+ of tyArrayConstr, tyArray: genArrayInfo(m, t, result)
+ of tySet: genSetInfo(m, t, result)
+ of tyEnum: genEnumInfo(m, t, result)
+ of tyObject: genObjectInfo(m, t, origType, result)
of tyTuple:
# if t.n != nil: genObjectInfo(m, t, result)
# else:
# BUGFIX: use consistently RTTI without proper field names; otherwise
# results are not deterministic!
- genTupleInfo(m, t, origType, result, info)
- of tyOpenArray:
- let x = openArrayToTuple(m, t)
- genTupleInfo(m, x, origType, result, info)
- else: internalError(m.config, "genTypeInfoV1(" & $t.kind & ')')
-
- var op = getAttachedOp(m.g.graph, t, attachedDeepCopy)
- if op == nil:
- op = getAttachedOp(m.g.graph, origType, attachedDeepCopy)
- if op != nil:
- genDeepCopyProc(m, op, result)
-
- if optTinyRtti in m.config.globalOptions and t.kind == tyObject and sfImportc notin t.sym.flags:
- let v2info = genTypeInfoV2(m, origType, info)
- addf(m.s[cfsTypeInit3], "$1->typeInfoV1 = (void*)&$2; $2.typeInfoV2 = (void*)$1;$n", [
- v2info, result])
-
- result = prefixTI.rope & result & ")".rope
+ genTupleInfo(m, t, result)
+ else: internalError("genTypeInfo(" & $t.kind & ')')
+ if t.deepCopy != nil:
+ genDeepCopyProc(m, t.deepCopy, result)
+ elif origType.deepCopy != nil:
+ genDeepCopyProc(m, origType.deepCopy, result)
+ result = "(&".rope & result & ")".rope
proc genTypeSection(m: BModule, n: PNode) =
discard
-
-proc genTypeInfo*(config: ConfigRef, m: BModule, t: PType; info: TLineInfo): Rope =
- if optTinyRtti in config.globalOptions:
- result = genTypeInfoV2(m, t, info)
- else:
- result = genTypeInfoV1(m, t, info)
diff --git a/compiler/ccgutils.nim b/compiler/ccgutils.nim
index d86ebe461..6dfd7b52c 100644
--- a/compiler/ccgutils.nim
+++ b/compiler/ccgutils.nim
@@ -10,138 +10,206 @@
# This module declares some helpers for the C code generator.
import
- ast, types, hashes, strutils, msgs, wordrecg,
- platform, trees, options, cgendata
-
-when defined(nimPreviewSlimSystem):
- import std/assertions
+ ast, astalgo, ropes, lists, hashes, strutils, types, msgs, wordrecg,
+ platform, trees
proc getPragmaStmt*(n: PNode, w: TSpecialWord): PNode =
case n.kind
of nkStmtList:
- for i in 0.. 0):
+ # we have to do a slow linear search because types may need
+ # to be compared by their structure:
+ for h in countup(0, high(tab.data)):
+ var t = PType(tab.data[h].key)
+ if t != nil:
+ if sameType(t, key):
+ return tab.data[h].val
proc makeSingleLineCString*(s: string): string =
result = "\""
for c in items(s):
- c.toCChar(result)
+ result.add(c.toCChar)
result.add('\"')
proc mangle*(name: string): string =
+ ## Lowercases the given name and manges any non-alphanumeric characters
+ ## so they are represented as `HEX____`. If the name starts with a number,
+ ## `N` is prepended
result = newStringOfCap(name.len)
- var start = 0
- if name[0] in Digits:
- result.add("X" & name[0])
- start = 1
- var requiresUnderscore = false
- template special(x) =
- result.add x
- requiresUnderscore = true
- for i in start.. 0 and i < name.len-1 and name[i+1] in Digits:
- discard
- else:
- result.add(c)
- of '$': special "dollar"
- of '%': special "percent"
- of '&': special "amp"
- of '^': special "roof"
- of '!': special "emark"
- of '?': special "qmark"
- of '*': special "star"
- of '+': special "plus"
- of '-': special "minus"
- of '/': special "slash"
- of '\\': special "backslash"
- of '=': special "eq"
- of '<': special "lt"
- of '>': special "gt"
- of '~': special "tilde"
- of ':': special "colon"
- of '.': special "dot"
- of '@': special "at"
- of '|': special "bar"
+ discard
+ of 'a'..'z', '0'..'9':
+ add(result, c)
else:
- result.add("X" & toHex(ord(c), 2))
- requiresUnderscore = true
- if requiresUnderscore:
- result.add "_"
+ add(result, "HEX" & toHex(ord(c), 2))
-proc mapSetType(conf: ConfigRef; typ: PType): TCTypeKind =
- case int(getSize(conf, typ))
- of 1: result = ctInt8
- of 2: result = ctInt16
- of 4: result = ctInt32
- of 8: result = ctInt64
- else: result = ctArray
-
-proc ccgIntroducedPtr*(conf: ConfigRef; s: PSym, retType: PType): bool =
- var pt = skipTypes(s.typ, typedescInst)
- assert skResult != s.kind
-
- 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 makeLLVMString*(s: string): Rope =
+ const MaxLineLength = 64
+ result = nil
+ var res = "c\""
+ for i in countup(0, len(s) - 1):
+ if (i + 1) mod MaxLineLength == 0:
+ add(result, rope(res))
+ setLen(res, 0)
+ case s[i]
+ of '\0'..'\x1F', '\x80'..'\xFF', '\"', '\\':
+ add(res, '\\')
+ add(res, toHex(ord(s[i]), 2))
+ else: add(res, s[i])
+ add(res, "\\00\"")
+ add(result, rope(res))
+initTypeTables()
diff --git a/compiler/cgen.nim b/compiler/cgen.nim
index d2095016c..589fcb515 100644
--- a/compiler/cgen.nim
+++ b/compiler/cgen.nim
@@ -11,270 +11,178 @@
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
+ nversion, nimsets, msgs, securehash, bitsets, idents, lists, types,
+ ccgutils, os, ropes, math, passes, rodread, wordrecg, treetab, cgmeth,
+ condsyms, rodutils, renderer, idgen, cgendata, ccgmerge, semfold, aliases,
+ lowerings, semparallel
-import pipelineutils
-
-when defined(nimPreviewSlimSystem):
- import std/assertions
-
-when not defined(leanCompiler):
- import spawn, semparallel
-
-import strutils except `%`, addf # collides with ropes.`%`
-
-from ic / ic import ModuleBackendFlag
-import dynlib
-
-when not declared(dynlib.libCandidates):
- proc libCandidates(s: string, dest: var seq[string]) =
- ## 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:
- var prefix = substr(s, 0, le - 1)
- var suffix = substr(s, ri + 1)
- for middle in split(substr(s, le + 1, ri - 1), '|'):
- libCandidates(prefix & middle & suffix, dest)
- else:
- dest.add(s)
+import strutils except `%` # collides with ropes.`%`
when options.hasTinyCBackend:
import tccgen
-proc hcrOn(m: BModule): bool = m.config.hcrOn
-proc hcrOn(p: BProc): bool = p.module.config.hcrOn
+# implementation
+
+var
+ generatedHeader: BModule
proc addForwardedProc(m: BModule, prc: PSym) =
- m.g.forwardedProcs.add(prc)
+ m.forwardedProcs.add(prc)
+ inc(gForwardedProcsCounter)
+
+proc getCgenModule(s: PSym): BModule =
+ result = if s.position >= 0 and s.position < gModules.len: gModules[s.position]
+ else: nil
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 = gModules[ms.position]
-proc initLoc(result: var TLoc, k: TLocKind, lode: PNode, s: TStorageLoc) =
+proc emitLazily(s: PSym): bool {.inline.} =
+ result = optDeadCodeElim in gGlobalOptions or
+ sfDeadCodeElim in getModule(s).flags
+
+proc initLoc(result: var TLoc, k: TLocKind, typ: PType, s: TStorageLoc) =
result.k = k
- result.storage = s
- result.lode = lode
- result.r = ""
+ result.s = s
+ result.t = typ
+ result.r = nil
result.flags = {}
-proc fillLoc(a: var TLoc, k: TLocKind, lode: PNode, r: Rope, s: TStorageLoc) {.inline.} =
+proc fillLoc(a: var TLoc, k: TLocKind, typ: PType, 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 == "": a.r = r
-
-proc fillLoc(a: var TLoc, k: TLocKind, lode: PNode, s: TStorageLoc) {.inline.} =
- # fills the loc if it is not already initialized
- if a.k == locNone:
- a.k = k
- a.lode = lode
- a.storage = s
-
-proc t(a: TLoc): PType {.inline.} =
- if a.lode.kind == nkSym:
- result = a.lode.sym.typ
- else:
- result = a.lode.typ
-
-proc lodeTyp(t: PType): PNode =
- result = newNode(nkEmpty)
- result.typ = t
+ a.t = typ
+ a.s = s
+ if a.r == nil: a.r = r
proc isSimpleConst(typ: PType): bool =
let t = skipTypes(typ, abstractVar)
result = t.kind notin
- {tyTuple, tyObject, tyArray, tySet, tySequence} and not
+ {tyTuple, tyObject, tyArray, tyArrayConstr, tySet, tySequence} and not
(t.kind == tyProc and t.callConv == ccClosure)
+proc useStringh(m: BModule) =
+ if includesStringh notin m.flags:
+ incl m.flags, includesStringh
+ discard lists.includeStr(m.headerFiles, "")
+
proc useHeader(m: BModule, sym: PSym) =
if lfHeader in sym.loc.flags:
assert(sym.annex != nil)
- let str = getStr(sym.annex.path)
- m.includeHeader(str)
+ discard lists.includeStr(m.headerFiles, getStr(sym.annex.path))
-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])
-
-proc getModuleDllPath(m: BModule, s: PSym): Rope =
- result = getModuleDllPath(m.g.modules[s.itemId.module])
-
-import macros
-
-proc cgFormatValue(result: var string; value: string) =
- result.add value
-
-proc cgFormatValue(result: var string; value: BiggestInt) =
- result.addInt value
-
-proc cgFormatValue(result: var string; value: Int128) =
- result.addInt128 value
-
-# TODO: please document
-macro ropecg(m: BModule, frmt: static[FormatStr], args: untyped): Rope =
- args.expectKind nnkBracket
- # echo "ropecg ", newLit(frmt).repr, ", ", args.repr
+proc ropecg(m: BModule, frmt: FormatStr, args: varargs[Rope]): Rope =
var i = 0
- result = nnkStmtListExpr.newTree()
-
- result.add quote do:
- assert `m` != nil
-
- let resVar = genSym(nskVar, "res")
- # during `koch boot` the median of all generates strings from this
- # macro is around 40 bytes in length.
- result.add newVarStmt(resVar, newCall(bindSym"newStringOfCap", newLit(80)))
- let formatValue = bindSym"cgFormatValue"
-
+ var length = len(frmt)
+ result = nil
var num = 0
- var strLit = ""
-
- template flushStrLit() =
- if strLit != "":
- result.add newCall(ident "add", resVar, newLit(strLit))
- strLit.setLen 0
-
- while i < frmt.len:
+ while i < length:
if frmt[i] == '$':
inc(i) # skip '$'
case frmt[i]
of '$':
- strLit.add '$'
+ add(result, "$")
inc(i)
of '#':
- flushStrLit()
inc(i)
- result.add newCall(formatValue, resVar, args[num])
- inc(num)
- of '^':
- flushStrLit()
- inc(i)
- result.add newCall(formatValue, resVar, args[^1])
+ add(result, args[num])
inc(num)
of '0'..'9':
var j = 0
while true:
j = (j * 10) + ord(frmt[i]) - ord('0')
inc(i)
- if i >= frmt.len or not (frmt[i] in {'0'..'9'}): break
+ if i >= length or not (frmt[i] in {'0'..'9'}): break
num = j
- if j > args.len:
- error("ropes: invalid format string " & newLit(frmt).repr & " args.len: " & $args.len)
-
- flushStrLit()
- result.add newCall(formatValue, resVar, args[j-1])
+ if j > high(args) + 1:
+ internalError("ropes: invalid format string $" & $j)
+ add(result, args[j-1])
of 'n':
- flushStrLit()
- result.add quote do:
- if optLineDir notin `m`.config.options:
- `resVar`.add("\L")
+ if optLineDir notin gOptions: add(result, rnl)
inc(i)
of 'N':
- strLit.add "\L"
+ add(result, rnl)
inc(i)
- else:
- error("ropes: invalid format string $" & frmt[i])
+ else: internalError("ropes: invalid format string $" & frmt[i])
elif frmt[i] == '#' and frmt[i+1] in IdentStartChars:
inc(i)
var j = i
while frmt[j] in IdentChars: inc(j)
- var ident = newLit(substr(frmt, i, j-1))
+ var ident = substr(frmt, i, j-1)
i = j
- flushStrLit()
- result.add newCall(formatValue, resVar, newCall(ident"cgsymValue", m, ident))
+ add(result, cgsym(m, ident))
elif frmt[i] == '#' and frmt[i+1] == '$':
inc(i, 2)
var j = 0
while frmt[i] in Digits:
j = (j * 10) + ord(frmt[i]) - ord('0')
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("#")
-
+ add(result, cgsym(m, $args[j-1]))
var start = i
- while i < frmt.len:
+ while i < length:
if frmt[i] != '$' and frmt[i] != '#': inc(i)
else: break
if i - 1 >= start:
- strLit.add(substr(frmt, start, i - 1))
+ add(result, substr(frmt, start, i - 1))
- flushStrLit()
- result.add newCall(ident"rope", resVar)
+template rfmt(m: BModule, fmt: string, args: varargs[Rope]): expr =
+ ropecg(m, fmt, args)
-proc addIndent(p: BProc; result: var Rope) =
- for i in 0..= 0
- if optLineDir in conf.options and line > 0:
- r.addf("$N#line $2 $1$N",
+ if optLineDir in gOptions:
+ addf(r, "$N#line $2 $1$N",
[rope(makeSingleLineCString(filename)), rope(line)])
-proc genCLineDir(r: var Rope, info: TLineInfo; conf: ConfigRef) =
- if optLineDir in conf.options:
- genCLineDir(r, toFullPath(conf, info), info.safeLineNm, conf)
+proc genCLineDir(r: var Rope, info: TLineInfo) =
+ genCLineDir(r, info.toFullPath, info.safeLineNm)
proc freshLineInfo(p: BProc; info: TLineInfo): bool =
if p.lastLineInfo.line != info.line or
@@ -284,91 +192,45 @@ proc freshLineInfo(p: BProc; info: TLineInfo): bool =
result = true
proc genLineDir(p: BProc, t: PNode) =
- let line = t.info.safeLineNm
-
- if optEmbedOrigSrc in p.config.globalOptions:
- p.s(cpsStmts).add("//" & sourceLine(p.config, t.info) & "\L")
- genCLineDir(p.s(cpsStmts), t.info, p.config)
- if ({optLineTrace, optStackTrace} * p.options == {optLineTrace, optStackTrace}) and
- (p.prc == nil or sfPure notin p.prc.flags) and t.info.fileIndex != InvalidFileIdx:
+ var line = t.info.safeLineNm
+ if optEmbedOrigSrc in gGlobalOptions:
+ add(p.s(cpsStmts), ~"//" & t.info.sourceLine & rnl)
+ genCLineDir(p.s(cpsStmts), t.info.toFullPath, line)
+ if ({optStackTrace, optEndb} * p.options == {optStackTrace, optEndb}) and
+ (p.prc == nil or sfPure notin p.prc.flags):
if freshLineInfo(p, t.info):
- linefmt(p, cpsStmts, "nimln_($1, $2);$n",
- [line, quotedFilename(p.config, t.info)])
+ linefmt(p, cpsStmts, "#endb($1, $2);$n",
+ line.rope, makeCString(toFilename(t.info)))
+ elif ({optLineTrace, optStackTrace} * p.options ==
+ {optLineTrace, optStackTrace}) and
+ (p.prc == nil or sfPure notin p.prc.flags) and t.info.fileIndex >= 0:
+ if freshLineInfo(p, t.info):
+ linefmt(p, cpsStmts, "nimln($1, $2);$n",
+ line.rope, t.info.quotedFilename)
+
+proc postStmtActions(p: BProc) {.inline.} =
+ add(p.s(cpsStmts), p.module.injectStmt)
proc accessThreadLocalVar(p: BProc, s: PSym)
-proc emulatedThreadVars(conf: ConfigRef): bool {.inline.}
+proc emulatedThreadVars(): bool {.inline.}
proc genProc(m: BModule, prc: PSym)
-proc raiseInstr(p: BProc; result: var Rope)
-template compileToCpp(m: BModule): untyped =
- m.config.backend == backendCpp or sfCompileToCpp in m.module.flags
+template compileToCpp(m: BModule): expr =
+ gCmd == cmdCompileToCpp or sfCompileToCpp in m.module.flags
-proc getTempName(m: BModule): Rope =
- result = m.tmpBase & rope(m.labels)
- inc m.labels
-
-proc rdLoc(a: TLoc): Rope =
- # 'read' location (deref if indirect)
- if lfIndirect in a.flags:
- result = "(*" & a.r & ")"
- else:
- result = a.r
-
-proc addRdLoc(a: TLoc; result: var Rope) =
- if lfIndirect in a.flags:
- result.add "(*" & a.r & ")"
- else:
- result.add a.r
-
-proc lenField(p: BProc): Rope {.inline.} =
- result = rope(if p.module.compileToCpp: "len" else: "Sup.len")
-
-proc lenExpr(p: BProc; a: TLoc): Rope =
- if optSeqDestructors in p.config.globalOptions:
- result = rdLoc(a) & ".len"
- else:
- result = "($1 ? $1->$2 : 0)" % [rdLoc(a), lenField(p)]
-
-proc dataFieldAccessor(p: BProc, sym: Rope): Rope =
- if optSeqDestructors in p.config.globalOptions:
- result = "(" & sym & ").p"
- else:
- result = sym
-
-proc dataField(p: BProc): Rope =
- if optSeqDestructors in p.config.globalOptions:
- result = rope".p->data"
- else:
- result = rope"->data"
-
-include ccgliterals
-include ccgtypes
+include "ccgtypes.nim"
# ------------------------------ Manager of temporaries ------------------
-template mapTypeChooser(n: PNode): TSymKind =
- (if n.kind == nkSym: n.sym.kind else: skVar)
+proc rdLoc(a: TLoc): Rope =
+ # 'read' location (deref if indirect)
+ result = a.r
+ if lfIndirect in a.flags: result = "(*$1)" % [result]
-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 =
- if lfIndirect notin a.flags and mapType(conf, a.t, mapTypeChooser(a)) != ctArray:
- result = "(&" & a.r & ")"
- else:
- result = a.r
-
-proc byRefLoc(p: BProc; a: TLoc): Rope =
- 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
+proc addrLoc(a: TLoc): Rope =
+ result = a.r
+ if lfIndirect notin a.flags and mapType(a.t) != ctArray:
+ result = "(&" & result & ")"
proc rdCharLoc(a: TLoc): Rope =
# read a location that may need a char-cast:
@@ -376,137 +238,80 @@ proc rdCharLoc(a: TLoc): Rope =
if skipTypes(a.t, abstractRange).kind == tyChar:
result = "((NU8)($1))" % [result]
-type
- TAssignmentFlag = enum
- needToCopy
- TAssignmentFlags = set[TAssignmentFlag]
-
-proc genObjConstr(p: BProc, e: PNode, d: var TLoc)
-proc rawConstExpr(p: BProc, n: PNode; d: var TLoc)
-proc genAssignment(p: BProc, dest, src: TLoc, flags: TAssignmentFlags)
-
-type
- ObjConstrMode = enum
- constructObj,
- constructRefObj
-
-proc genObjectInit(p: BProc, section: TCProcSection, t: PType, a: var TLoc,
- mode: ObjConstrMode) =
- #if optNimV2 in p.config.globalOptions: return
+proc genObjectInit(p: BProc, section: TCProcSection, t: PType, a: TLoc,
+ takeAddr: bool) =
case analyseObjectWithTypeField(t)
of frNone:
discard
of frHeader:
var r = rdLoc(a)
- if mode == constructRefObj: r = "(*$1)" % [r]
+ if not takeAddr: r = "(*$1)" % [r]
var s = skipTypes(t, abstractInst)
if not p.module.compileToCpp:
- while s.kind == tyObject and s[0] != nil:
- r.add(".Sup")
- s = skipTypes(s[0], skipPtrs)
- if optTinyRtti in p.config.globalOptions:
- linefmt(p, section, "$1.m_type = $2;$n", [r, genTypeInfoV2(p.module, t, a.lode.info)])
- else:
- linefmt(p, section, "$1.m_type = $2;$n", [r, genTypeInfoV1(p.module, t, a.lode.info)])
+ while (s.kind == tyObject) and (s.sons[0] != nil):
+ add(r, ".Sup")
+ s = skipTypes(s.sons[0], abstractInst)
+ linefmt(p, section, "$1.m_type = $2;$n", r, genTypeInfo(p.module, t))
of frEmbedded:
- if optTinyRtti in p.config.globalOptions:
- var tmp: TLoc
- if mode == constructRefObj:
- let objType = t.skipTypes(abstractInst+{tyRef})
- rawConstExpr(p, newNodeIT(nkType, a.lode.info, objType), tmp)
- linefmt(p, cpsStmts,
- "#nimCopyMem((void*)$1, (NIM_CONST void*)&$2, sizeof($3));$n",
- [rdLoc(a), rdLoc(tmp), getTypeDesc(p.module, objType, mapTypeChooser(a))])
- else:
- rawConstExpr(p, newNodeIT(nkType, a.lode.info, t), tmp)
- genAssignment(p, a, tmp, {})
- else:
- # worst case for performance:
- var r = if mode == constructObj: addrLoc(p.config, a) else: rdLoc(a)
- linefmt(p, section, "#objectInit($1, $2);$n", [r, genTypeInfoV1(p.module, t, a.lode.info)])
+ # worst case for performance:
+ var r = if takeAddr: addrLoc(a) else: rdLoc(a)
+ linefmt(p, section, "#objectInit($1, $2);$n", r, genTypeInfo(p.module, t))
- if isException(t):
- var r = rdLoc(a)
- if mode == constructRefObj: r = "(*$1)" % [r]
- var s = skipTypes(t, abstractInst)
- if not p.module.compileToCpp:
- while s.kind == tyObject and s[0] != nil and s.sym.magic != mException:
- r.add(".Sup")
- s = skipTypes(s[0], skipPtrs)
- linefmt(p, section, "$1.name = $2;$n", [r, makeCString(t.skipTypes(abstractInst).sym.name.s)])
+type
+ TAssignmentFlag = enum
+ needToCopy, needForSubtypeCheck, afDestIsNil, afDestIsNotNil, afSrcIsNil,
+ afSrcIsNotNil, needToKeepAlive
+ TAssignmentFlags = set[TAssignmentFlag]
-proc genRefAssign(p: BProc, dest, src: TLoc)
+proc genRefAssign(p: BProc, dest, src: TLoc, flags: TAssignmentFlags)
proc isComplexValueType(t: PType): bool {.inline.} =
- let t = t.skipTypes(abstractInst + tyUserTypeClasses)
- result = t.kind in {tyArray, tySet, tyTuple, tyObject, tyOpenArray} or
+ result = t.kind in {tyArray, tyArrayConstr, tySet, tyTuple, tyObject} or
(t.kind == tyProc and t.callConv == ccClosure)
-include ccgreset
-
proc resetLoc(p: BProc, loc: var TLoc) =
- let containsGcRef = optSeqDestructors notin p.config.globalOptions and containsGarbageCollectedRef(loc.t)
+ let containsGcRef = containsGarbageCollectedRef(loc.t)
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)])
- elif not isComplexValueType(typ):
+ if not isComplexValueType(typ):
if containsGcRef:
var nilLoc: TLoc
- initLoc(nilLoc, locTemp, loc.lode, OnStack)
+ initLoc(nilLoc, locTemp, loc.t, OnStack)
nilLoc.r = rope("NIM_NIL")
- genRefAssign(p, loc, nilLoc)
+ genRefAssign(p, loc, nilLoc, {afSrcIsNil})
else:
- linefmt(p, cpsStmts, "$1 = 0;$n", [rdLoc(loc)])
+ linefmt(p, cpsStmts, "$1 = 0;$n", rdLoc(loc))
else:
- if loc.storage != OnStack and containsGcRef:
- specializeReset(p, loc)
- when false:
- linefmt(p, cpsStmts, "#genericReset((void*)$1, $2);$n",
- [addrLoc(p.config, loc), genTypeInfoV1(p.module, loc.t, loc.lode.info)])
+ if optNilCheck in p.options:
+ linefmt(p, cpsStmts, "#chckNil((void*)$1);$n", addrLoc(loc))
+ if loc.s != OnStack:
+ linefmt(p, cpsStmts, "#genericReset((void*)$1, $2);$n",
+ addrLoc(loc), genTypeInfo(p.module, loc.t))
# XXX: generated reset procs should not touch the m_type
# field, so disabling this should be safe:
- genObjectInit(p, cpsStmts, loc.t, loc, constructObj)
+ genObjectInit(p, cpsStmts, loc.t, loc, true)
else:
- # array passed as argument decayed into pointer, bug #7332
- # so we use getTypeDesc here rather than rdLoc(loc)
- linefmt(p, cpsStmts, "#nimZeroMem((void*)$1, sizeof($2));$n",
- [addrLoc(p.config, loc),
- getTypeDesc(p.module, loc.t, mapTypeChooser(loc))])
+ useStringh(p.module)
+ linefmt(p, cpsStmts, "memset((void*)$1, 0, sizeof($2));$n",
+ addrLoc(loc), rdLoc(loc))
# XXX: We can be extra clever here and call memset only
# on the bytes following the m_type field?
- genObjectInit(p, cpsStmts, loc.t, loc, constructObj)
+ genObjectInit(p, cpsStmts, loc.t, loc, true)
-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))])
+proc constructLoc(p: BProc, loc: TLoc, isTemp = false) =
+ let typ = skipTypes(loc.t, abstractRange)
+ if not isComplexValueType(typ):
+ linefmt(p, cpsStmts, "$1 = ($2)0;$n", rdLoc(loc),
+ getTypeDesc(p.module, typ))
else:
if not isTemp or containsGarbageCollectedRef(loc.t):
- # don't use nimZeroMem for temporary values for performance if we can
+ # don't use memset for temporary values for performance if we can
# avoid it:
- if not isOrHasImportedCppType(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)
+ if not isImportedCppType(typ):
+ useStringh(p.module)
+ linefmt(p, cpsStmts, "memset((void*)$1, 0, sizeof($2));$n",
+ addrLoc(loc), rdLoc(loc))
+ genObjectInit(p, cpsStmts, loc.t, loc, true)
proc initLocalVar(p: BProc, v: PSym, immediateAsgn: bool) =
if sfNoInit notin v.flags:
@@ -522,90 +327,93 @@ 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])
+ result.r = "LOC" & rope(p.labels)
+ linefmt(p, cpsLocals, "$1 $2;$n", getTypeDesc(p.module, t), result.r)
result.k = locTemp
- result.lode = lodeTyp t
- result.storage = OnStack
+ #result.a = - 1
+ result.t = t
+ #result.t = getUniqueType(t)
+ result.s = OnStack
result.flags = {}
constructLoc(p, result, not needsInit)
+
+proc keepAlive(p: BProc, toKeepAlive: TLoc) =
when false:
- # XXX Introduce a compiler switch in order to detect these easily.
- if getSize(p.config, t) > 1024 * 1024:
- if p.prc != nil:
- echo "ENORMOUS TEMPORARY! ", p.config $ p.prc.info
- else:
- echo "ENORMOUS TEMPORARY! ", p.config $ p.lastLineInfo
- writeStackTrace()
+ # deactivated because of the huge slowdown this causes; GC will take care
+ # of interior pointers instead
+ if optRefcGC notin gGlobalOptions: return
+ var result: TLoc
+ var fid = rope(p.gcFrameId)
+ result.r = "GCFRAME.F" & fid
+ addf(p.gcFrameType, " $1 F$2;$n",
+ [getTypeDesc(p.module, toKeepAlive.t), fid])
+ inc(p.gcFrameId)
+ result.k = locTemp
+ #result.a = -1
+ result.t = toKeepAlive.t
+ result.s = OnStack
+ result.flags = {}
-proc getTempCpp(p: BProc, t: PType, result: var TLoc; value: Rope) =
- inc(p.labels)
- result.r = "T" & rope(p.labels) & "_"
- linefmt(p, cpsStmts, "$1 $2 = $3;$n", [getTypeDesc(p.module, t, skVar), result.r, value])
- result.k = locTemp
- result.lode = lodeTyp t
- result.storage = OnStack
- result.flags = {}
+ if not isComplexValueType(skipTypes(toKeepAlive.t, abstractVarRange)):
+ linefmt(p, cpsStmts, "$1 = $2;$n", rdLoc(result), rdLoc(toKeepAlive))
+ else:
+ useStringh(p.module)
+ linefmt(p, cpsStmts,
+ "memcpy((void*)$1, (NIM_CONST void*)$2, sizeof($3));$n",
+ addrLoc(result), addrLoc(toKeepAlive), rdLoc(result))
-proc getIntTemp(p: BProc, result: var TLoc) =
- inc(p.labels)
- result.r = "T" & rope(p.labels) & "_"
- linefmt(p, cpsLocals, "NI $1;$n", [result.r])
- result.k = locTemp
- result.storage = OnStack
- result.lode = lodeTyp getSysType(p.module.g.graph, unknownLineInfo, tyInt)
- result.flags = {}
+proc initGCFrame(p: BProc): Rope =
+ if p.gcFrameId > 0: result = "struct {$1} GCFRAME;$n" % [p.gcFrameType]
-proc localVarDecl(p: BProc; n: PNode): Rope =
- let s = n.sym
+proc deinitGCFrame(p: BProc): Rope =
+ if p.gcFrameId > 0:
+ result = ropecg(p.module,
+ "if (((NU)&GCFRAME) < 4096) #nimGCFrame(&GCFRAME);$n")
+
+proc localDebugInfo(p: BProc, s: PSym) =
+ if {optStackTrace, optEndb} * p.options != {optStackTrace, optEndb}: return
+ # XXX work around a bug: No type information for open arrays possible:
+ if skipTypes(s.typ, abstractVar).kind in {tyOpenArray, tyVarargs}: return
+ var a = "&" & s.loc.r
+ if s.kind == skParam and ccgIntroducedPtr(s): a = s.loc.r
+ lineF(p, cpsInit,
+ "FR.s[$1].address = (void*)$3; FR.s[$1].typ = $4; FR.s[$1].name = $2;$n",
+ [p.maxFrameLen.rope, makeCString(normalize(s.name.s)), a,
+ genTypeInfo(p.module, s.loc.t)])
+ inc(p.maxFrameLen)
+ inc p.blocks[p.blocks.len-1].frameLen
+
+proc localVarDecl(p: BProc; s: PSym): Rope =
if s.loc.k == locNone:
- fillLocalName(p, s)
- fillLoc(s.loc, locLocalVar, n, OnStack)
+ fillLoc(s.loc, locLocalVar, s.typ, mangleName(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)
+ result = getTypeDesc(p.module, s.loc.t)
if s.constraint.isNil:
- if sfRegister in s.flags: result.add(" register")
+ if sfRegister in s.flags: add(result, " register")
#elif skipTypes(s.typ, abstractInst).kind in GcTypeKinds:
- # decl.add(" GC_GUARD")
- if sfVolatile in s.flags: result.add(" volatile")
- if sfNoalias in s.flags: result.add(" NIM_NOALIAS")
- result.add(" ")
- result.add(s.loc.r)
+ # add(decl, " GC_GUARD")
+ if sfVolatile in s.flags: add(result, " volatile")
+ add(result, " ")
+ add(result, s.loc.r)
else:
- result = runtimeFormat(s.cgDeclFrmt, [result, s.loc.r])
+ result = s.cgDeclFrmt % [result, s.loc.r]
-proc assignLocalVar(p: BProc, n: PNode) =
+proc assignLocalVar(p: BProc, s: PSym) =
#assert(s.loc.k == locNone) # not yet assigned
# 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, s) & ";" & tnl
line(p, cpsLocals, decl)
+ localDebugInfo(p, s)
include ccgthreadvars
proc varInDynamicLib(m: BModule, sym: PSym)
+proc mangleDynLibProc(sym: PSym): Rope
-proc treatGlobalDifferentlyForHCR(m: BModule, s: PSym): bool =
- return m.hcrOn and {sfThread, sfGlobal} * s.flags == {sfGlobal} and
- ({lfNoDecl, lfHeader} * s.loc.flags == {})
- # and s.owner.kind == skModule # owner isn't always a module (global pragma on local var)
- # and s.loc.k == locGlobalVar # loc isn't always initialized when this proc is used
-
-proc assignGlobalVar(p: BProc, n: PNode; value: Rope) =
- let s = n.sym
+proc assignGlobalVar(p: BProc, s: PSym) =
if s.loc.k == locNone:
- fillBackendName(p.module, s)
- fillLoc(s.loc, locGlobalVar, n, OnHeap)
- if treatGlobalDifferentlyForHCR(p.module, s): incl(s.loc.flags, lfIndirect)
+ fillLoc(s.loc, locGlobalVar, s.typ, mangleName(s), OnHeap)
if lfDynamicLib in s.loc.flags:
var q = findPendingModule(p.module, s)
@@ -613,126 +421,72 @@ proc assignGlobalVar(p: BProc, n: PNode; value: Rope) =
varInDynamicLib(q, s)
else:
s.loc.r = mangleDynLibProc(s)
- if value != "":
- internalError(p.config, n.info, ".dynlib variables cannot have a value")
return
useHeader(p.module, s)
if lfNoDecl in s.loc.flags: return
- if not containsOrIncl(p.module.declaredThings, s.id):
- if sfThread in s.flags:
- declareThreadVar(p.module, s, sfImportc in s.flags)
- if value != "":
- internalError(p.config, n.info, ".threadvar variables cannot have a value")
+ if sfThread in s.flags:
+ declareThreadVar(p.module, s, sfImportc in s.flags)
+ else:
+ var decl: Rope = nil
+ var td = getTypeDesc(p.module, s.loc.t)
+ if s.constraint.isNil:
+ if sfImportc in s.flags: add(decl, "extern ")
+ add(decl, td)
+ if sfRegister in s.flags: add(decl, " register")
+ if sfVolatile in s.flags: add(decl, " volatile")
+ addf(decl, " $1;$n", [s.loc.r])
else:
- var decl: Rope = ""
- var td = getTypeDesc(p.module, s.loc.t, skVar)
- if s.constraint.isNil:
- if s.kind in {skLet, skVar, skField, skForVar} and s.alignment > 0:
- decl.addf "NIM_ALIGN($1) ", [rope(s.alignment)]
- if p.hcrOn: decl.add("static ")
- 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 ")
- 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 != "":
- decl.addf(" $1 = $2;$n", [s.loc.r, value])
- else:
- decl.addf(" $1;$n", [s.loc.r])
- else:
- if value != "":
- decl = runtimeFormat(s.cgDeclFrmt & " = $#;$n", [td, s.loc.r, value])
- else:
- decl = runtimeFormat(s.cgDeclFrmt & ";$n", [td, s.loc.r])
- p.module.s[cfsVars].add(decl)
- if p.withinLoop > 0 and value == "":
+ decl = (s.cgDeclFrmt & ";$n") % [td, s.loc.r]
+ add(p.module.s[cfsVars], decl)
+ if p.withinLoop > 0:
# fixes tests/run/tzeroarray:
resetLoc(p, s.loc)
+ if p.module.module.options * {optStackTrace, optEndb} ==
+ {optStackTrace, optEndb}:
+ appcg(p.module, p.module.s[cfsDebugInit],
+ "#dbgRegisterGlobal($1, &$2, $3);$n",
+ [makeCString(normalize(s.owner.name.s & '.' & s.name.s)),
+ s.loc.r, genTypeInfo(p.module, s.typ)])
-proc assignParam(p: BProc, s: PSym, retType: PType) =
- assert(s.loc.r != "")
- scopeMangledParam(p, s)
+proc assignParam(p: BProc, s: PSym) =
+ assert(s.loc.r != nil)
+ localDebugInfo(p, s)
-proc fillProcLoc(m: BModule; n: PNode) =
- let sym = n.sym
+proc fillProcLoc(sym: PSym) =
if sym.loc.k == locNone:
- fillBackendName(m, sym)
- fillLoc(sym.loc, locProc, n, OnStack)
+ fillLoc(sym.loc, locProc, sym.typ, mangleName(sym), OnStack)
proc getLabel(p: BProc): TLabel =
inc(p.labels)
- result = "LA" & rope(p.labels) & "_"
+ result = "LA" & rope(p.labels)
proc fixLabel(p: BProc, labl: TLabel) =
lineF(p, cpsStmts, "$1: ;$n", [labl])
-proc genVarPrototype(m: BModule, n: PNode)
+proc genVarPrototype(m: BModule, sym: PSym)
proc requestConstImpl(p: BProc, sym: PSym)
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 raiseExit(p: BProc)
+proc genAssignment(p: BProc, dest, src: TLoc, flags: TAssignmentFlags)
+proc intLiteral(i: BiggestInt): Rope
+proc genLiteral(p: BProc, n: PNode): Rope
+proc genOtherArg(p: BProc; ri: PNode; i: int; typ: PType): Rope
proc initLocExpr(p: BProc, e: PNode, result: var TLoc) =
- initLoc(result, locNone, e, OnUnknown)
+ initLoc(result, locNone, e.typ, OnUnknown)
expr(p, e, result)
proc initLocExprSingleUse(p: BProc, e: PNode, result: var TLoc) =
- initLoc(result, locNone, e, OnUnknown)
- if e.kind in nkCallKinds and (e[0].kind != nkSym or e[0].sym.magic == mNone):
- # We cannot check for tfNoSideEffect here because of mutable parameters.
- discard "bug #8202; enforce evaluation order for nested calls for C++ too"
- # We may need to consider that 'f(g())' cannot be rewritten to 'tmp = g(); f(tmp)'
- # if 'tmp' lacks a move/assignment operator.
- if e[0].kind == nkSym and sfCompileToCpp in e[0].sym.flags:
- result.flags.incl lfSingleUse
- else:
- result.flags.incl lfSingleUse
+ initLoc(result, locNone, e.typ, OnUnknown)
+ result.flags.incl lfSingleUse
expr(p, e, result)
-include ccgcalls, "ccgstmts.nim"
+proc lenField(p: BProc): Rope =
+ result = rope(if p.module.compileToCpp: "len" else: "Sup.len")
-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_);
-
- $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_);
-
- $1define 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")
- 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")
- 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",
- [frame, procname, filename, line])
-
-proc deinitFrameNoDebug(p: BProc; frame: Rope): Rope =
- result = ropecg(p.module, "\t#popFrameOfAddr(&$1);$n", [frame])
-
-proc deinitFrame(p: BProc): Rope =
- result = ropecg(p.module, "\t#popFrame();$n", [])
-
-include ccgexprs
+include ccgcalls, "ccgstmts.nim", "ccgexprs.nim"
# ----------------------------- dynamic library handling -----------------
# We don't finalize dynamic libs as the OS does this for us.
@@ -747,56 +501,42 @@ 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])
+ addf(m.s[cfsVars], "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 = ""
- for i in 0..high(s):
+ rawMessage(hintDependency, lib.path.strVal)
+ var loadlib: Rope = nil
+ for i in countup(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", []
+ if i > 0: add(loadlib, "||")
+ appcg(m, loadlib, "($1 = #nimLoadLibrary((#NimStringDesc*) &$2))$n",
+ [tmp, getStrLit(m, s[i])])
appcg(m, m.s[cfsDynLibInit],
- "if (!($1)) #nimLoadLibraryError(",
- [loadlib])
- genStringLiteral(m, lib.path, m.s[cfsDynLibInit])
- m.s[cfsDynLibInit].addf ");$n", []
-
+ "if (!($1)) #nimLoadLibraryError((#NimStringDesc*) &$2);$n",
+ [loadlib, getStrLit(m, lib.path.strVal)])
else:
var p = newProc(nil, m)
- p.options.excl optStackTrace
- p.flags.incl nimErrorFlagDisabled
+ p.options = p.options - {optStackTrace, optEndb}
var dest: TLoc
- initLoc(dest, locTemp, lib.path, OnStack)
- dest.r = getTempName(m)
- appcg(m, m.s[cfsDynLibInit],"$1 $2;$n",
- [getTypeDesc(m, lib.path.typ, skVar), rdLoc(dest)])
- expr(p, lib.path, dest)
-
- m.s[cfsVars].add(p.s(cpsLocals))
- m.s[cfsDynLibInit].add(p.s(cpsInit))
- m.s[cfsDynLibInit].add(p.s(cpsStmts))
+ initLocExpr(p, lib.path, dest)
+ add(m.s[cfsVars], p.s(cpsLocals))
+ add(m.s[cfsDynLibInit], p.s(cpsInit))
+ add(m.s[cfsDynLibInit], p.s(cpsStmts))
appcg(m, m.s[cfsDynLibInit],
"if (!($1 = #nimLoadLibrary($2))) #nimLoadLibraryError($2);$n",
[tmp, rdLoc(dest)])
- if lib.name == "": internalError(m.config, "loadDynamicLib")
+ if lib.name == nil: internalError("loadDynamicLib")
proc mangleDynLibProc(sym: PSym): Rope =
- # we have to build this as a single rope in order not to trip the
- # optimization in genInfixCall, see test tests/cpp/t8241.nim
if sfCompilerProc in sym.flags:
# NOTE: sym.loc.r is the external name!
result = rope(sym.name.s)
else:
- result = rope(strutils.`%`("Dl_$1_", $sym.id))
+ result = "Dl_$1" % [rope(sym.id)]
proc symInDynamicLib(m: BModule, sym: PSym) =
var lib = sym.annex
@@ -812,27 +552,27 @@ proc symInDynamicLib(m: BModule, sym: PSym) =
var a: TLoc
initLocExpr(m.initProc, n[0], a)
var params = rdLoc(a) & "("
- for i in 1.. 0:
+ discard cgsym(p.module, "VarSlot")
+ result = rfmt(nil, "\tnimfrs($1, $2, $3, $4)$N",
+ procname, filename, p.maxFrameLen.rope,
+ p.blocks[0].frameLen.rope)
+ else:
+ result = rfmt(nil, "\tnimfr($1, $2)$N", procname, filename)
+
+proc deinitFrame(p: BProc): Rope =
+ result = rfmt(p.module, "\t#popFrame();$n")
proc closureSetup(p: BProc, prc: PSym) =
if tfCapturesEnv notin prc.typ.flags: return
# prc.ast[paramsPos].last contains the type we're after:
var ls = lastSon(prc.ast[paramsPos])
if ls.kind != nkSym:
- internalError(p.config, prc.info, "closure generation failed")
+ internalError(prc.info, "closure generation failed")
var env = ls.sym
#echo "created environment: ", env.id, " for ", prc.name.s
- assignLocalVar(p, ls)
+ assignLocalVar(p, env)
# generate cast assignment:
- if p.config.selectedGC == gcGo:
- linefmt(p, cpsStmts, "#unsureAsgnRef((void**) $1, ($2) ClE_0);$n",
- [addrLoc(p.config, env.loc), getTypeDesc(p.module, env.typ)])
- else:
- linefmt(p, cpsStmts, "$1 = ($2) ClE_0;$n",
- [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
- else:
- for i in 0.. 0:
- result = easyResultAsgn(n[0])
- if result != nil: incl n.flags, nfPreventCg
- else: discard
-
-type
- InitResultEnum = enum Unknown, InitSkippable, InitRequired
-
-proc allPathsAsgnResult(n: PNode): InitResultEnum =
- # Exceptions coming from calls don't have not be considered here:
- #
- # proc bar(): string = raise newException(...)
- #
- # proc foo(): string =
- # # optimized out: 'reset(result)'
- # result = bar()
- #
- # try:
- # a = foo()
- # except:
- # echo "a was not written to"
- #
- template allPathsInBranch(it) =
- let a = allPathsAsgnResult(it)
- case a
- of InitRequired: return InitRequired
- of InitSkippable: discard
- of Unknown:
- # sticky, but can be overwritten by InitRequired:
- result = Unknown
-
- result = Unknown
- case n.kind
- of nkStmtList, nkStmtListExpr:
- for it in n:
- result = allPathsAsgnResult(it)
- if result != Unknown: return result
- of nkAsgn, nkFastAsgn, nkSinkAsgn:
- if n[0].kind == nkSym and n[0].sym.kind == skResult:
- if not containsResult(n[1]): result = InitSkippable
- else: result = InitRequired
- elif containsResult(n):
- result = InitRequired
- of nkReturnStmt:
- if n.len > 0:
- if n[0].kind == nkEmpty and result != InitSkippable:
- # This is a bare `return` statement, if `result` was not initialized
- # anywhere else (or if we're not sure about this) let's require it to be
- # initialized. This avoids cases like #9286 where this heuristic lead to
- # wrong code being generated.
- result = InitRequired
- else: result = allPathsAsgnResult(n[0])
- of nkIfStmt, nkIfExpr:
- var exhaustive = false
- result = InitSkippable
- for it in n:
- # Every condition must not use 'result':
- if it.len == 2 and containsResult(it[0]):
- return InitRequired
- if it.len == 1: exhaustive = true
- allPathsInBranch(it.lastSon)
- # if the 'if' statement is not exhaustive and yet it touched 'result'
- # in some way, say Unknown.
- if not exhaustive: result = Unknown
- of nkCaseStmt:
- if containsResult(n[0]): return InitRequired
- result = InitSkippable
- var exhaustive = skipTypes(n[0].typ,
- abstractVarRange-{tyTypeDesc}).kind notin {tyFloat..tyFloat128, tyString, tyCstring}
- for i in 1..= prc.ast.len:
- internalError(m.config, prc.info, "proc has no result symbol")
- let resNode = prc.ast[resultPos]
- let res = resNode.sym # get result symbol
- if not isInvalidReturnType(m.config, prc.typ):
+ internalError(prc.info, "proc has no result symbol")
+ var res = prc.ast.sons[resultPos].sym # get result symbol
+ if not isInvalidReturnType(prc.typ.sons[0]):
if sfNoInit in prc.flags: incl(res.flags, sfNoInit)
- if sfNoInit in prc.flags and p.module.compileToCpp and (let val = easyResultAsgn(procBody); val != nil):
- var decl = localVarDecl(p, resNode)
- var a: TLoc
- initLocExprSingleUse(p, val, a)
- linefmt(p, cpsStmts, "$1 = $2;$n", [decl, rdLoc(a)])
- else:
- # declare the result symbol:
- assignLocalVar(p, resNode)
- assert(res.loc.r != "")
- initLocalVar(p, res, immediateAsgn=false)
- returnStmt = ropecg(p.module, "\treturn $1;$n", [rdLoc(res.loc)])
+ # declare the result symbol:
+ assignLocalVar(p, res)
+ assert(res.loc.r != nil)
+ returnStmt = rfmt(nil, "\treturn $1;$n", rdLoc(res.loc))
+ initLocalVar(p, res, immediateAsgn=false)
else:
- fillResult(p.config, resNode, prc.typ)
- assignParam(p, res, prc.typ[0])
- # We simplify 'unsureAsgn(result, nil); unsureAsgn(result, x)'
- # to 'unsureAsgn(result, x)'
- # Sketch why this is correct: If 'result' points to a stack location
- # the 'unsureAsgn' is a nop. If it points to a global variable the
- # global is either 'nil' or points to valid memory and so the RC operation
- # succeeds without touching not-initialized memory.
- if sfNoInit in prc.flags: discard
- elif allPathsAsgnResult(procBody) == InitSkippable: discard
- else:
- resetLoc(p, res.loc)
+ fillResult(res)
+ assignParam(p, res)
if skipTypes(res.typ, abstractInst).kind == tyArray:
#incl(res.loc.flags, lfIndirect)
- res.loc.storage = OnUnknown
+ res.loc.s = OnUnknown
- for i in 1.. 0:
- m.s[cfsVars].addf "NIM_ALIGN($1) ", [rope(sym.alignment)]
- m.s[cfsVars].add(if m.hcrOn: "static " else: "extern ")
- m.s[cfsVars].add(getTypeDesc(m, sym.loc.t, skVar))
- if m.hcrOn: m.s[cfsVars].add("*")
- if lfDynamicLib in sym.loc.flags: m.s[cfsVars].add("*")
- if sfRegister in sym.flags: m.s[cfsVars].add(" register")
- if sfVolatile in sym.flags: m.s[cfsVars].add(" volatile")
- if sfNoalias in sym.flags: m.s[cfsVars].add(" NIM_NOALIAS")
- m.s[cfsVars].addf(" $1;$n", [sym.loc.r])
- if m.hcrOn: m.initProc.procSec(cpsLocals).addf(
- "\t$1 = ($2*)hcrGetGlobal($3, \"$1\");$n", [sym.loc.r,
- getTypeDesc(m, sym.loc.t, skVar), getModuleDllPath(m, sym)])
+ add(m.s[cfsVars], "extern ")
+ add(m.s[cfsVars], getTypeDesc(m, sym.loc.t))
+ if lfDynamicLib in sym.loc.flags: add(m.s[cfsVars], "*")
+ if sfRegister in sym.flags: add(m.s[cfsVars], " register")
+ if sfVolatile in sym.flags: add(m.s[cfsVars], " volatile")
+ addf(m.s[cfsVars], " $1;$n", [sym.loc.r])
-proc addNimDefines(result: var Rope; conf: ConfigRef) {.inline.} =
- result.addf("#define NIM_INTBITS $1\L", [
- platform.CPU[conf.target.targetCPU].intSize.rope])
- if conf.cppCustomNamespace.len > 0:
- result.add("#define USE_NIM_NAMESPACE ")
- result.add(conf.cppCustomNamespace)
- result.add("\L")
- if conf.isDefined("nimEmulateOverflowChecks"):
- result.add("#define NIM_EmulateOverflowChecks\L")
+proc genVarPrototype(m: BModule, sym: PSym) =
+ genVarPrototypeAux(m, sym)
-proc headerTop(): Rope =
- result = "/* Generated by Nim Compiler v$1 */$N" % [rope(VersionAsString)]
+proc addIntTypes(result: var Rope) {.inline.} =
+ addf(result, "#define NIM_INTBITS $1" & tnl, [
+ platform.CPU[targetCPU].intSize.rope])
-proc getCopyright(conf: ConfigRef; cfile: Cfile): Rope =
- result = headerTop()
- if optCompileOnly notin conf.globalOptions:
- result.add ("/* Compiled for: $1, $2, $3 */$N" &
- "/* Command for C compiler:$n $4 */$N") %
- [rope(platform.OS[conf.target.targetOS].name),
- rope(platform.CPU[conf.target.targetCPU].name),
- rope(extccomp.CC[conf.cCompiler].name),
- rope(getCompileCFileCmd(conf, cfile))]
-
-proc getFileHeader(conf: ConfigRef; cfile: Cfile): Rope =
- result = getCopyright(conf, cfile)
- 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 getSomeInitName(m: BModule, suffix: string): Rope =
- if not m.hcrOn:
- result = getSomeNameForModule(m)
- 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"
+proc getCopyright(cfile: string): Rope =
+ if optCompileOnly in gGlobalOptions:
+ result = ("/* Generated by Nim Compiler v$1 */$N" &
+ "/* (c) 2015 Andreas Rumpf */$N" &
+ "/* The generated code is subject to the original license. */$N") %
+ [rope(VersionAsString)]
else:
- result = getSomeInitName(m, "Init000")
+ result = ("/* Generated by Nim Compiler v$1 */$N" &
+ "/* (c) 2015 Andreas Rumpf */$N" &
+ "/* The generated code is subject to the original license. */$N" &
+ "/* Compiled for: $2, $3, $4 */$N" &
+ "/* Command for C compiler:$n $5 */$N") %
+ [rope(VersionAsString),
+ rope(platform.OS[targetOS].name),
+ rope(platform.CPU[targetCPU].name),
+ rope(extccomp.CC[extccomp.cCompiler].name),
+ rope(getCompileCFileCmd(cfile))]
-proc getDatInitName(m: BModule): Rope = getSomeInitName(m, "DatInit000")
-proc getHcrInitName(m: BModule): Rope = getSomeInitName(m, "HcrInit000")
+proc getFileHeader(cfile: string): Rope =
+ result = getCopyright(cfile)
+ addIntTypes(result)
-proc hcrGetProcLoadCode(m: BModule, sym, prefix, handle, getProcFunc: string): Rope
+proc genFilenames(m: BModule): Rope =
+ discard cgsym(m, "dbgRegisterFilename")
+ result = nil
+ for i in 0.. ")
- elif m.config.target.targetOS == osGenode:
- m.includeHeader("")
+ var nimMain, otherMain: FormatStr
+ if platform.targetOS == osWindows and
+ gGlobalOptions * {optGenGuiApp, optGenDynLib} != {}:
+ if optGenGuiApp in gGlobalOptions:
+ nimMain = WinNimMain
+ otherMain = WinCMain
+ else:
+ nimMain = WinNimDllMain
+ otherMain = WinCDllMain
+ discard lists.includeStr(m.headerFiles, "")
+ elif optGenDynLib in gGlobalOptions:
+ nimMain = PosixNimDllMain
+ otherMain = PosixCDllMain
+ elif platform.targetOS == osStandalone:
+ nimMain = PosixNimMain
+ otherMain = StandaloneCMain
+ else:
+ nimMain = PosixNimMain
+ otherMain = PosixCMain
+ if gBreakpoints != nil: discard cgsym(m, "dbgRegisterBreakpoint")
+ if optEndb in gOptions:
+ gBreakpoints.add(m.genFilenames)
let initStackBottomCall =
- if m.config.target.targetOS == osStandalone or m.config.selectedGC in {gcNone, gcArc, gcOrc}: "".rope
- else: ropecg(m, "\t#initStackBottomWith((void *)&inner);$N", [])
+ if platform.targetOS == osStandalone or gSelectedGC == gcNone: "".rope
+ else: ropecg(m, "\t#initStackBottomWith((void *)&inner);$N")
inc(m.labels)
+ appcg(m, m.s[cfsProcs], PreMainBody, [
+ mainDatInit, gBreakpoints, otherModsInit,
+ if emulatedThreadVars() and platform.targetOS != osStandalone:
+ ropecg(m, "\t#initThreadVarsEmulation();$N")
+ else:
+ "".rope,
+ initStackBottomCall])
- 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], nimMain, [mainModInit, initStackBottomCall, rope(m.labels)])
+ if optNoMain notin gGlobalOptions:
+ appcg(m, m.s[cfsProcs], otherMain, [])
- 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])
- else:
- const nimMain = WinNimDllMain
- appcg(m, m.s[cfsProcs], nimMain,
- [m.g.mainModInit, initStackBottomCall, m.labels, preMainCode, m.config.nimMainPrefix, isVolatile])
- 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])
- 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])
- else:
- const nimMain = NimMainBody
- appcg(m, m.s[cfsProcs], nimMain,
- [m.g.mainModInit, initStackBottomCall, m.labels, preMainCode, m.config.nimMainPrefix, isVolatile])
+proc getSomeInitName(m: PSym, suffix: string): 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
+ result.add suffix
- 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"
+proc getInitName(m: PSym): Rope = getSomeInitName(m, "Init000")
+proc getDatInitName(m: PSym): Rope = getSomeInitName(m, "DatInit000")
- 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])
- else:
- const otherMain = WinCDllMain
- appcg(m, m.s[cfsProcs], otherMain, [m.config.nimMainPrefix])
- elif m.config.target.targetOS == osGenode:
- const otherMain = ComponentConstruct
- appcg(m, m.s[cfsProcs], otherMain, [m.config.nimMainPrefix])
- elif optGenDynLib in m.config.globalOptions:
- const otherMain = PosixCDllMain
- appcg(m, m.s[cfsProcs], otherMain, [m.config.nimMainPrefix])
- elif m.config.target.targetOS == osStandalone:
- const otherMain = StandaloneCMain
- appcg(m, m.s[cfsProcs], otherMain, [m.config.nimMainPrefix])
- else:
- const otherMain = PosixCMain
- appcg(m, m.s[cfsProcs], otherMain, [if m.hcrOn: "*" else: "", m.config.nimMainPrefix])
-
- 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
-
-proc registerModuleToMain(g: BModuleList; m: BModule) =
- let
+proc registerModuleToMain(m: PSym) =
+ var
init = m.getInitName
datInit = m.getDatInitName
-
- if m.hcrOn:
- var hcrModuleMeta = "$nN_LIB_PRIVATE const char* hcr_module_list[] = {$n" % []
- let systemModulePath = getModuleDllPath(m, g.modules[g.graph.config.m.systemFileIdx.int].module)
- let mainModulePath = getModuleDllPath(m, m.module)
- if sfMainModule in m.module.flags:
- hcrModuleMeta.addf("\t$1,$n", [systemModulePath])
- g.graph.importDeps.withValue(FileIndex(m.module.position), deps):
- for curr in deps[]:
- hcrModuleMeta.addf("\t$1,$n", [getModuleDllPath(m, g.modules[curr.int].module)])
- 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])
- if sfMainModule in m.module.flags:
- g.mainModProcs.add(hcrModuleMeta)
- g.mainModProcs.addf("static void* hcr_handle;$N", [])
- g.mainModProcs.addf("N_LIB_EXPORT N_NIMCALL(void, $1)(void);$N", [init])
- g.mainModProcs.addf("N_LIB_EXPORT N_NIMCALL(void, $1)(void);$N", [datInit])
- g.mainModProcs.addf("N_LIB_EXPORT N_NIMCALL(void, $1)(void*, N_NIMCALL_PTR(void*, getProcAddr)(void*, char*));$N", [m.getHcrInitName])
- g.mainModProcs.addf("N_LIB_EXPORT N_NIMCALL(void, HcrCreateTypeInfos)(void);$N", [])
- g.mainModInit.addf("\t$1();$N", [init])
- g.otherModsInit.addf("\thcrInit((void**)hcr_module_list, $1, $2, $3, hcr_handle, nimGetProcAddr);$n",
- [mainModulePath, systemModulePath, datInit])
- g.mainDatInit.addf("\t$1(hcr_handle, nimGetProcAddr);$N", [m.getHcrInitName])
- g.mainDatInit.addf("\thcrAddModule($1);\n", [mainModulePath])
- g.mainDatInit.addf("\tHcrCreateTypeInfos();$N", [])
- # nasty nasty hack to get the command line functionality working with HCR
- # register the 2 variables on behalf of the os module which might not even
- # be loaded (in which case it will get collected but that is not a problem)
- # EDIT: indeed, this hack, in combination with another un-necessary one
- # (`makeCString` was doing line wrap of string litterals) was root cause for
- # bug #16265.
- let osModulePath = ($systemModulePath).replace("stdlib_system", "stdlib_os").rope
- g.mainDatInit.addf("\thcrAddModule($1);\n", [osModulePath])
- g.mainDatInit.add("\tint* cmd_count;\n")
- g.mainDatInit.add("\tchar*** cmd_line;\n")
- g.mainDatInit.addf("\thcrRegisterGlobal($1, \"cmdCount\", sizeof(cmd_count), NULL, (void**)&cmd_count);$N", [osModulePath])
- g.mainDatInit.addf("\thcrRegisterGlobal($1, \"cmdLine\", sizeof(cmd_line), NULL, (void**)&cmd_line);$N", [osModulePath])
- g.mainDatInit.add("\t*cmd_count = cmdCount;\n")
- g.mainDatInit.add("\t*cmd_line = cmdLine;\n")
- else:
- m.s[cfsInitProc].add(hcrModuleMeta)
- return
-
- if m.s[cfsDatInitProc].len > 0:
- g.mainModProcs.addf("N_LIB_PRIVATE N_NIMCALL(void, $1)(void);$N", [datInit])
- g.mainDatInit.addf("\t$1();$N", [datInit])
-
- # Initialization of TLS and GC should be done in between
- # systemDatInit and systemInit calls if any
- if sfSystemModule in m.module.flags:
- if emulatedThreadVars(m.config) and m.config.target.targetOS != osStandalone:
- g.mainDatInit.add(ropecg(m, "\t#initThreadVarsEmulation();$N", []))
- if m.config.target.targetOS != osStandalone and m.config.selectedGC notin {gcNone, gcArc, gcOrc}:
- g.mainDatInit.add(ropecg(m, "\t#initStackBottomWith((void *)&inner);$N", []))
-
- if m.s[cfsInitProc].len > 0:
- g.mainModProcs.addf("N_LIB_PRIVATE N_NIMCALL(void, $1)(void);$N", [init])
+ addf(mainModProcs, "NIM_EXTERNC N_NOINLINE(void, $1)(void);$N", [init])
+ addf(mainModProcs, "NIM_EXTERNC N_NOINLINE(void, $1)(void);$N", [datInit])
+ if sfSystemModule notin m.flags:
+ addf(mainDatInit, "\t$1();$N", [datInit])
let initCall = "\t$1();$N" % [init]
- if sfMainModule in m.module.flags:
- g.mainModInit.add(initCall)
- elif sfSystemModule in m.module.flags:
- g.mainDatInit.add(initCall) # systemInit must called right after systemDatInit if any
+ if sfMainModule in m.flags:
+ add(mainModInit, initCall)
else:
- g.otherModsInit.add(initCall)
-
-proc genDatInitCode(m: BModule) =
- ## this function is called in cgenWriteModules after all modules are closed,
- ## it means raising dependency on the symbols is too late as it will not propagate
- ## into other modules, only simple rope manipulations are allowed
-
- var moduleDatInitRequired = m.hcrOn
-
- var prc = "$1 N_NIMCALL(void, $2)(void) {$N" %
- [rope(if m.hcrOn: "N_LIB_EXPORT" else: "N_LIB_PRIVATE"), getDatInitName(m)]
-
- # we don't want to break into such init code - could happen if a line
- # directive from a function written by the user spills after itself
- genCLineDir(prc, "generated_not_to_break_here", 999999, m.config)
-
- for i in cfsTypeInit1..cfsDynLibInit:
- if m.s[i].len != 0:
- moduleDatInitRequired = true
- prc.add(m.s[i])
-
- 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
-proc hcrGetProcLoadCode(m: BModule, sym, prefix, handle, getProcFunc: string): Rope =
- let prc = magicsys.getCompilerProc(m.g.graph, sym)
- assert prc != nil
- fillProcLoc(m, prc.ast[namePos])
-
- var extname = prefix & sym
- var tmp = mangleDynLibProc(prc)
- prc.loc.r = tmp
- prc.typ.sym = nil
-
- if not containsOrIncl(m.declaredThings, prc.id):
- m.s[cfsVars].addf("static $2 $1;$n", [prc.loc.r, getTypeDesc(m, prc.loc.t, skVar)])
-
- result = "\t$1 = ($2) $3($4, $5);$n" %
- [tmp, getTypeDesc(m, prc.typ, skVar), getProcFunc.rope, handle.rope, makeCString(prefix & sym)]
+ add(otherModsInit, initCall)
proc genInitCode(m: BModule) =
- ## this function is called in cgenWriteModules after all modules are closed,
- ## it means raising dependency on the symbols is too late as it will not propagate
- ## into other modules, only simple rope manipulations are allowed
- var moduleInitRequired = m.hcrOn
- let initname = getInitName(m)
- var prc = "$1 N_NIMCALL(void, $2)(void) {$N" %
- [rope(if m.hcrOn: "N_LIB_EXPORT" else: "N_LIB_PRIVATE"), initname]
- # we don't want to break into such init code - could happen if a line
- # directive from a function written by the user spills after itself
- genCLineDir(prc, "generated_not_to_break_here", 999999, m.config)
+ var initname = getInitName(m.module)
+ var prc = "NIM_EXTERNC N_NOINLINE(void, $1)(void) {$N" % [initname]
if m.typeNodes > 0:
- if m.hcrOn:
- appcg(m, m.s[cfsTypeInit1], "\t#TNimNode* $1;$N", [m.typeNodesName])
- appcg(m, m.s[cfsTypeInit1], "\thcrRegisterGlobal($3, \"$1_$2\", sizeof(TNimNode) * $2, NULL, (void**)&$1);$N",
- [m.typeNodesName, m.typeNodes, getModuleDllPath(m, m.module)])
- else:
- appcg(m, m.s[cfsTypeInit1], "static #TNimNode $1[$2];$n",
- [m.typeNodesName, m.typeNodes])
+ appcg(m, m.s[cfsTypeInit1], "static #TNimNode $1[$2];$n",
+ [m.typeNodesName, rope(m.typeNodes)])
if m.nimTypes > 0:
appcg(m, m.s[cfsTypeInit1], "static #TNimType $1[$2];$n",
- [m.nimTypesName, m.nimTypes])
+ [m.nimTypesName, rope(m.nimTypes)])
- if m.hcrOn:
- prc.addf("\tint* nim_hcr_dummy_ = 0;$n" &
- "\tNIM_BOOL nim_hcr_do_init_ = " &
- "hcrRegisterGlobal($1, \"module_initialized_\", 1, NULL, (void**)&nim_hcr_dummy_);$n",
- [getModuleDllPath(m, m.module)])
+ add(prc, initGCFrame(m.initProc))
- template writeSection(thing: untyped, section: TCProcSection, addHcrGuards = false) =
- if m.thing.s(section).len > 0:
- moduleInitRequired = true
- if addHcrGuards: prc.add("\tif (nim_hcr_do_init_) {\n\n")
- prc.add(m.thing.s(section))
- if addHcrGuards: prc.add("\n\t} // nim_hcr_do_init_\n")
+ add(prc, genSectionStart(cpsLocals))
+ add(prc, m.preInitProc.s(cpsLocals))
+ add(prc, m.initProc.s(cpsLocals))
+ add(prc, m.postInitProc.s(cpsLocals))
+ add(prc, genSectionEnd(cpsLocals))
- 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")
+ if optStackTrace in m.initProc.options and frameDeclared notin m.flags:
+ # BUT: the generated init code might depend on a current frame, so
+ # declare it nevertheless:
+ incl m.flags, frameDeclared
+ if preventStackTrace notin m.flags:
+ var procname = makeCString(m.module.name.s)
+ add(prc, initFrame(m.initProc, procname, m.module.info.quotedFilename))
+ else:
+ add(prc, ~"\tTFrame F; FR.len = 0;$N")
- writeSection(preInitProc, cpsLocals)
- writeSection(preInitProc, cpsInit, m.hcrOn)
- writeSection(preInitProc, cpsStmts)
- prc.addf("}/* preInitProc end */$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)
- m.initProc.blocks[0].sections[cpsStmts].prepend m.preInitProc.s(cpsStmts)
+ add(prc, genSectionStart(cpsInit))
+ add(prc, m.preInitProc.s(cpsInit))
+ add(prc, m.initProc.s(cpsInit))
+ add(prc, m.postInitProc.s(cpsInit))
+ add(prc, genSectionEnd(cpsInit))
- # add new scope for following code, because old vcc compiler need variable
- # be defined at the top of the block
- prc.addf("{$N", [])
- writeSection(initProc, cpsLocals)
+ add(prc, genSectionStart(cpsStmts))
+ add(prc, m.preInitProc.s(cpsStmts))
+ add(prc, m.initProc.s(cpsStmts))
+ add(prc, m.postInitProc.s(cpsStmts))
+ add(prc, genSectionEnd(cpsStmts))
+ if optStackTrace in m.initProc.options and preventStackTrace notin m.flags:
+ add(prc, deinitFrame(m.initProc))
+ add(prc, deinitGCFrame(m.initProc))
+ addf(prc, "}$N$N", [])
- if m.initProc.s(cpsInit).len > 0 or m.initProc.s(cpsStmts).len > 0:
- moduleInitRequired = true
- if optStackTrace in m.initProc.options and frameDeclared notin m.flags:
- # BUT: the generated init code might depend on a current frame, so
- # declare it nevertheless:
- incl m.flags, frameDeclared
- if preventStackTrace notin m.flags:
- 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.addf("NIM_EXTERNC N_NOINLINE(void, $1)(void) {$N",
+ [getDatInitName(m.module)])
- writeSection(initProc, cpsInit, m.hcrOn)
- writeSection(initProc, cpsStmts)
-
- if beforeRetNeeded in m.initProc.flags:
- prc.add("\tBeforeRet_: ;\n")
-
- if sfMainModule in m.module.flags and m.config.exc == excGoto:
- if getCompilerProc(m.g.graph, "nimTestErrorFlag") != nil:
- m.appcg(prc, "\t#nimTestErrorFlag();$n", [])
-
- 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", [])
-
- prc.addf("}$N$N", [])
+ for i in cfsTypeInit1..cfsDynLibInit:
+ add(prc, genSectionStart(i))
+ add(prc, m.s[i])
+ add(prc, genSectionEnd(i))
+ addf(prc, "}$N$N", [])
# we cannot simply add the init proc to ``m.s[cfsProcs]`` anymore because
# that would lead to a *nesting* of merge sections which the merger does
# not support. So we add it to another special section: ``cfsInitProc``
-
- if m.hcrOn:
- var procsToLoad = @["hcrRegisterProc", "hcrGetProc", "hcrRegisterGlobal", "hcrGetGlobal"]
-
- m.s[cfsInitProc].addf("N_LIB_EXPORT N_NIMCALL(void, $1)(void* handle, N_NIMCALL_PTR(void*, getProcAddr)(void*, char*)) {$N", [getHcrInitName(m)])
- if sfMainModule in m.module.flags:
- # additional procs to load
- procsToLoad.add("hcrInit")
- procsToLoad.add("hcrAddModule")
- # load procs
- for curr in procsToLoad:
- m.s[cfsInitProc].add(hcrGetProcLoadCode(m, curr, "", "handle", "getProcAddr"))
- m.s[cfsInitProc].addf("}$N$N", [])
+ add(m.s[cfsInitProc], prc)
for i, el in pairs(m.extensionLoaders):
- if el != "":
- let ex = "NIM_EXTERNC N_NIMCALL(void, nimLoadProcs$1)(void) {$2}$N$N" %
+ if el != nil:
+ let ex = "N_NIMCALL(void, nimLoadProcs$1)(void) {$2}$N$N" %
[(i.ord - '0'.ord).rope, el]
- moduleInitRequired = true
- prc.add(ex)
+ add(m.s[cfsInitProc], ex)
- if moduleInitRequired or sfMainModule in m.module.flags:
- m.s[cfsInitProc].add(prc)
- #rememberFlag(m.g.graph, m.module, HasModuleInitProc)
-
- genDatInitCode(m)
-
- if m.hcrOn:
- m.s[cfsInitProc].addf("N_LIB_EXPORT N_NIMCALL(void, HcrCreateTypeInfos)(void) {$N", [])
- m.s[cfsInitProc].add(m.hcrCreateTypeInfosProc)
- m.s[cfsInitProc].addf("}$N$N", [])
-
- registerModuleToMain(m.g, m)
-
-proc genModule(m: BModule, cfile: Cfile): Rope =
- var moduleIsEmpty = true
-
- result = getFileHeader(m.config, cfile)
+proc genModule(m: BModule, cfile: string): Rope =
+ result = getFileHeader(cfile)
+ result.add(genMergeInfo(m))
generateThreadLocalStorage(m)
generateHeaders(m)
- result.add(m.s[cfsHeaders])
- if m.config.cppCustomNamespace.len > 0:
- openNamespaceNim(m.config.cppCustomNamespace, result)
- if m.s[cfsFrameDefines].len > 0:
- result.add(m.s[cfsFrameDefines])
- else:
- result.add("#define nimfr_(x, y)\n#define nimln_(x, y)\n")
+ for i in countup(cfsHeaders, cfsProcs):
+ add(result, genSectionStart(i))
+ add(result, m.s[i])
+ add(result, genSectionEnd(i))
+ add(result, m.s[cfsInitProc])
- for i in cfsForwardTypes..cfsProcs:
- if m.s[i].len > 0:
- moduleIsEmpty = false
- result.add(m.s[i])
+proc newPreInitProc(m: BModule): BProc =
+ result = newProc(nil, m)
+ # little hack so that unique temporaries are generated:
+ result.labels = 100_000
- if m.s[cfsInitProc].len > 0:
- moduleIsEmpty = false
- result.add(m.s[cfsInitProc])
- if m.s[cfsDatInitProc].len > 0 or m.hcrOn:
- moduleIsEmpty = false
- result.add(m.s[cfsDatInitProc])
-
- if m.config.cppCustomNamespace.len > 0:
- closeNamespaceNim(result)
-
- if moduleIsEmpty:
- result = ""
+proc newPostInitProc(m: BModule): BProc =
+ result = newProc(nil, m)
+ # little hack so that unique temporaries are generated:
+ result.labels = 200_000
proc initProcOptions(m: BModule): TOptions =
- let opts = m.config.options
- if sfSystemModule in m.module.flags: opts-{optStackTrace} else: opts
+ if sfSystemModule in m.module.flags: gOptions-{optStackTrace} else: gOptions
-proc rawNewModule(g: BModuleList; module: PSym, filename: AbsoluteFile): BModule =
+proc rawNewModule(module: PSym, filename: string): BModule =
new(result)
- result.g = g
- result.tmpBase = rope("TM" & $hashOwner(module) & "_")
- result.headerFiles = @[]
+ result.tmpBase = rope("T" & $hashOwner(module) & "_")
+ initLinkedList(result.headerFiles)
result.declaredThings = initIntSet()
result.declaredProtos = initIntSet()
result.cfilename = filename
result.filename = filename
- result.typeCache = initTable[SigHash, Rope]()
- result.forwTypeCache = initTable[SigHash, Rope]()
+ initIdTable(result.typeCache)
+ initIdTable(result.forwTypeCache)
result.module = module
- result.typeInfoMarker = initTable[SigHash, Rope]()
- result.sigConflicts = initCountTable[SigHash]()
+ result.typeInfoMarker = initIntSet()
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
- result.preInitProc.labels = 100_000 # little hack so that unique temporaries are generated
+ result.preInitProc = newPreInitProc(result)
+ result.postInitProc = newPostInitProc(result)
initNodeTable(result.dataCache)
result.typeStack = @[]
+ result.forwardedProcs = @[]
result.typeNodesName = getTempName(result)
result.nimTypesName = getTempName(result)
# no line tracing for the init sections of the system module so that we
- # don't generate a TFrame which can confuse the stack bottom initialization:
+ # don't generate a TFrame which can confuse the stack botton initialization:
if sfSystemModule in module.flags:
incl result.flags, preventStackTrace
excl(result.preInitProc.options, optStackTrace)
- let ndiName = if optCDebug in g.config.globalOptions: changeFileExt(completeCfilePath(g.config, filename), "ndi")
- else: AbsoluteFile""
- open(result.ndi, ndiName, g.config)
+ excl(result.postInitProc.options, optStackTrace)
-proc rawNewModule(g: BModuleList; module: PSym; conf: ConfigRef): BModule =
- result = rawNewModule(g, module, AbsoluteFile toFullPath(conf, module.position.FileIndex))
+proc nullify[T](arr: var T) =
+ for i in low(arr)..high(arr):
+ arr[i] = Rope(nil)
-proc newModule*(g: BModuleList; module: PSym; conf: ConfigRef): BModule =
+proc resetModule*(m: BModule) =
+ # between two compilations in CAAS mode, we can throw
+ # away all the data that was written to disk
+ initLinkedList(m.headerFiles)
+ m.declaredProtos = initIntSet()
+ initIdTable(m.forwTypeCache)
+ m.initProc = newProc(nil, m)
+ m.initProc.options = initProcOptions(m)
+ m.preInitProc = newPreInitProc(m)
+ m.postInitProc = newPostInitProc(m)
+ initNodeTable(m.dataCache)
+ m.typeStack = @[]
+ m.forwardedProcs = @[]
+ m.typeNodesName = getTempName(m)
+ m.nimTypesName = getTempName(m)
+ if sfSystemModule in m.module.flags:
+ incl m.flags, preventStackTrace
+ else:
+ excl m.flags, preventStackTrace
+ nullify m.s
+ m.typeNodes = 0
+ m.nimTypes = 0
+ nullify m.extensionLoaders
+
+ # indicate that this is now cached module
+ # the cache will be invalidated by nullifying gModules
+ m.fromCache = true
+
+ # we keep only the "merge info" information for the module
+ # and the properties that can't change:
+ # m.filename
+ # m.cfilename
+ # m.isHeaderFile
+ # m.module ?
+ # m.typeCache
+ # m.declaredThings
+ # m.typeInfoMarker
+ # m.labels
+ # m.FrameDeclared
+
+proc resetCgenModules* =
+ for m in cgenModules(): resetModule(m)
+
+proc rawNewModule(module: PSym): BModule =
+ result = rawNewModule(module, module.position.int32.toFullPath)
+
+proc newModule(module: PSym): BModule =
# we should create only one cgen module for each module sym
- result = rawNewModule(g, module, conf)
- if module.position >= g.modules.len:
- setLen(g.modules, module.position + 1)
- #growCache g.modules, module.position
- g.modules[module.position] = result
+ internalAssert getCgenModule(module) == nil
-template injectG() {.dirty.} =
- if graph.backend == nil:
- graph.backend = newModuleList(graph)
- let g = BModuleList(graph.backend)
+ result = rawNewModule(module)
+ growCache gModules, module.position
+ gModules[module.position] = result
-proc setupCgen*(graph: ModuleGraph; module: PSym; idgen: IdGenerator): PPassContext =
- injectG()
- result = newModule(g, module, graph.config)
- result.idgen = idgen
- if optGenIndex in graph.config.globalOptions and g.generatedHeader == nil:
- let f = if graph.config.headerFile.len > 0: AbsoluteFile graph.config.headerFile
- else: graph.config.projectFull
- g.generatedHeader = rawNewModule(g, module,
- changeFileExt(completeCfilePath(graph.config, f), hExt))
- incl g.generatedHeader.flags, isHeaderFile
+ if (optDeadCodeElim in gGlobalOptions):
+ if (sfDeadCodeElim in module.flags):
+ internalError("added pending module twice: " & module.filename)
+
+proc myOpen(module: PSym): PPassContext =
+ result = newModule(module)
+ if optGenIndex in gGlobalOptions and generatedHeader == nil:
+ let f = if headerFile.len > 0: headerFile else: gProjectFull
+ generatedHeader = rawNewModule(module,
+ changeFileExt(completeCFilePath(f), hExt))
+ incl generatedHeader.flags, isHeaderFile
proc writeHeader(m: BModule) =
- var result = headerTop()
+ var result = getCopyright(m.filename)
var guard = "__$1__" % [m.filename.splitFile.name.rope]
result.addf("#ifndef $1$n#define $1$n", [guard])
- addNimDefines(result, m.config)
+ addIntTypes(result)
generateHeaders(m)
generateThreadLocalStorage(m)
- for i in cfsHeaders..cfsProcs:
- result.add(m.s[i])
- if m.config.cppCustomNamespace.len > 0 and i == cfsHeaders:
- openNamespaceNim(m.config.cppCustomNamespace, result)
- result.add(m.s[cfsInitProc])
+ for i in countup(cfsHeaders, cfsProcs):
+ add(result, genSectionStart(i))
+ add(result, m.s[i])
+ add(result, genSectionEnd(i))
+ add(result, m.s[cfsInitProc])
- if optGenDynLib in m.config.globalOptions:
+ if optGenDynLib in gGlobalOptions:
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", [])
result.addf("#endif /* $1 */$n", [guard])
- if not writeRope(result, m.filename):
- rawMessage(m.config, errCannotOpenFile, m.filename.string)
+ writeRope(result, m.filename)
-proc getCFile(m: BModule): AbsoluteFile =
+proc getCFile(m: BModule): string =
let ext =
- 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)
+ if m.compileToCpp: ".cpp"
+ elif gCmd == cmdCompileToOC or sfCompileToObjC in m.module.flags: ".m"
+ else: ".c"
+ result = changeFileExt(completeCFilePath(m.cfilename.withPackageName), ext)
-when false:
- proc myOpenCached(graph: ModuleGraph; module: PSym, rd: PRodReader): PPassContext =
- injectG()
- var m = newModule(g, module, graph.config)
- readMergeInfo(getCFile(m), m)
- result = m
+proc myOpenCached(module: PSym, rd: PRodReader): PPassContext =
+ assert optSymbolFiles in gGlobalOptions
+ var m = newModule(module)
+ readMergeInfo(getCFile(m), m)
+ result = m
-proc addHcrInitGuards(p: BProc, n: PNode, inInitGuard: var bool) =
- if n.kind == nkStmtList:
- for child in n:
- addHcrInitGuards(p, child, inInitGuard)
- else:
- let stmtShouldExecute = n.kind in {nkVarSection, nkLetSection} or
- nfExecuteOnReload in n.flags
- if inInitGuard:
- if stmtShouldExecute:
- endBlock(p)
- inInitGuard = false
- else:
- if not stmtShouldExecute:
- line(p, cpsStmts, "if (nim_hcr_do_init_)\n")
- startBlock(p)
- inInitGuard = true
-
- 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 or passes.skipCodegen(n): return
+ var m = BModule(b)
m.initProc.options = initProcOptions(m)
- #softRnl = if optLineDir in m.config.options: noRnl else: rnl
- # XXX replicate this logic!
- var transformedN = transformStmt(m.g.graph, m.idgen, m.module, n)
- if sfInjectDestructors in m.module.flags:
- transformedN = injectDestructorCalls(m.g.graph, m.idgen, m.module, transformedN)
+ genStmts(m.initProc, n)
- if m.hcrOn:
- addHcrInitGuards(m.initProc, transformedN, m.inHcrInitGuard)
- else:
- genProcBody(m.initProc, transformedN)
+proc finishModule(m: BModule) =
+ var i = 0
+ while i <= high(m.forwardedProcs):
+ # Note: ``genProc`` may add to ``m.forwardedProcs``, so we cannot use
+ # a ``for`` loop here
+ var prc = m.forwardedProcs[i]
+ if sfForward in prc.flags:
+ internalError(prc.info, "still forwarded: " & prc.name.s)
+ genProcNoForward(m, prc)
+ inc(i)
+ assert(gForwardedProcsCounter >= i)
+ dec(gForwardedProcsCounter, i)
+ setLen(m.forwardedProcs, 0)
-proc shouldRecompile(m: BModule; code: Rope, cfile: Cfile): bool =
- if optForceFullMake notin m.config.globalOptions:
- if not moduleHasChanged(m.g.graph, m.module):
- result = false
- elif not equalsFile(code, cfile.cname):
- when false:
- #m.config.symbolFiles == readOnlySf: #isDefined(m.config, "nimdiff"):
- if fileExists(cfile.cname):
- copyFile(cfile.cname.string, cfile.cname.string & ".backup")
- echo "diff ", cfile.cname.string, ".backup ", cfile.cname.string
- else:
- echo "new file ", cfile.cname.string
- if not writeRope(code, cfile.cname):
- rawMessage(m.config, errCannotOpenFile, cfile.cname.string)
- result = true
- elif fileExists(cfile.obj) and os.fileNewer(cfile.obj.string, cfile.cname.string):
- result = false
- else:
- result = true
+proc shouldRecompile(code: Rope, cfile: string): bool =
+ result = true
+ if optForceFullMake notin gGlobalOptions:
+ var objFile = toObjFile(cfile)
+ if writeRopeIfNotEqual(code, cfile): return
+ if existsFile(objFile) and os.fileNewer(objFile, cfile): result = false
else:
- if not writeRope(code, cfile.cname):
- rawMessage(m.config, errCannotOpenFile, cfile.cname.string)
- result = true
+ writeRope(code, cfile)
# We need 2 different logics here: pending modules (including
# 'nim__dat') may require file merging for the combination of dead code
@@ -2056,115 +1250,86 @@ proc shouldRecompile(m: BModule; code: Rope, cfile: Cfile): bool =
# it would generate multiple 'main' procs, for instance.
proc writeModule(m: BModule, pending: bool) =
- template onExit() = close(m.ndi, m.config)
- let cfile = getCFile(m)
- if moduleHasChanged(m.g.graph, m.module):
+ # generate code for the init statements of the module:
+ var cfile = getCFile(m)
+ var cfilenoext = changeFileExt(cfile, "")
+
+ if not m.fromCache or optForceFullMake in gGlobalOptions:
genInitCode(m)
finishTypeDescriptions(m)
if sfMainModule in m.module.flags:
# generate main file:
- genMainProc(m)
- m.s[cfsProcHeaders].add(m.g.mainModProcs)
+ add(m.s[cfsProcHeaders], 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:
+ var code = genModule(m, cfile)
when hasTinyCBackend:
- if m.config.cmd == cmdTcc:
- tccgen.compileCCode($code, m.config)
- onExit()
+ if gCmd == cmdRun:
+ tccgen.compileCCode($code)
return
- if not shouldRecompile(m, code, cf): cf.flags = {CfileFlag.Cached}
- addFileToCompile(m.config, cf)
- onExit()
+ if shouldRecompile(code, cfile):
+ addFileToCompile(cfile)
+ elif pending and mergeRequired(m) and sfMainModule notin m.module.flags:
+ mergeFiles(cfile, m)
+ genInitCode(m)
+ finishTypeDescriptions(m)
+ var code = genModule(m, cfile)
+ writeRope(code, cfile)
+ addFileToCompile(cfile)
+ elif not existsFile(toObjFile(cfilenoext)):
+ # 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:
+ addFileToCompile(cfile)
+
+ addFileToLink(cfilenoext)
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)
+ let cfilenoext = changeFileExt(cfile, "")
+
+ if mergeRequired(m) and sfMainModule notin m.module.flags:
+ mergeFiles(cfile, m)
+ genInitCode(m)
+ finishTypeDescriptions(m)
+ var code = genModule(m, cfile)
+ writeRope(code, cfile)
+ addFileToCompile(cfile)
+
+ addFileToLink(cfilenoext)
+
+proc myClose(b: PPassContext, n: PNode): PNode =
+ result = n
+ if b == nil or passes.skipCodegen(n): return
+ var m = BModule(b)
+ if n != nil:
+ m.initProc.options = initProcOptions(m)
+ genStmts(m.initProc, n)
+ # cached modules need to registered too:
+ registerModuleToMain(m.module)
-proc finalCodegenActions*(graph: ModuleGraph; m: BModule; n: PNode) =
- ## Also called from IC.
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")
-
- 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 moduleHasChanged(graph, m.module):
- # if the module is cached, we don't regenerate the main proc
- # nor the dispatchers? But if the dispatchers changed?
- # XXX emit the dispatchers into its own .c file?
- if n != nil:
- m.initProc.options = initProcOptions(m)
- genProcBody(m.initProc, n)
-
- if m.hcrOn:
- # make sure this is pulled in (meaning hcrGetGlobal() is called for it during init)
- cgsym(m, "programResult")
- if m.inHcrInitGuard:
- endBlock(m.initProc)
-
- if sfMainModule in m.module.flags:
- if m.hcrOn:
- # pull ("define" since they are inline when HCR is on) these functions in the main file
- # 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")
-
- # raise dependencies on behalf of genMainProc
- if m.config.target.targetOS != osStandalone and m.config.selectedGC notin {gcNone, gcArc, gcOrc}:
- cgsym(m, "initStackBottomWith")
- if emulatedThreadVars(m.config) and m.config.target.targetOS != osStandalone:
- cgsym(m, "initThreadVarsEmulation")
-
- if m.g.forwardedProcs.len == 0:
- incl m.flags, objHasKidsValid
- let disp = generateMethodDispatchers(graph)
- for x in disp: genProcAux(m, x.sym)
-
- let mm = m
- m.g.modulesClosed.add mm
-
-proc genForwardedProcs(g: BModuleList) =
- # Forward declared proc:s lack bodies when first encountered, so they're given
- # a second pass here
- # Note: ``genProcNoForward`` may add to ``forwardedProcs``
- while g.forwardedProcs.len > 0:
- let
- prc = g.forwardedProcs.pop()
- m = g.modules[prc.itemId.module]
- if sfForward in prc.flags:
- internalError(m.config, prc.info, "still forwarded: " & prc.name.s)
-
- genProcNoForward(m, prc)
-
-proc cgenWriteModules*(backend: RootRef, config: ConfigRef) =
- let g = BModuleList(backend)
- g.config = config
+ incl m.flags, objHasKidsValid
+ var disp = generateMethodDispatchers()
+ for i in 0..sonsLen(disp)-1: genProcAux(m, disp.sons[i].sym)
+ genMainProc(m)
+proc cgenWriteModules* =
# we need to process the transitive closure because recursive module
# deps are allowed (and the system module is processed in the wrong
# order anyway)
- genForwardedProcs(g)
+ if generatedHeader != nil: finishModule(generatedHeader)
+ while gForwardedProcsCounter > 0:
+ for m in cgenModules():
+ if not m.fromCache:
+ finishModule(m)
+ for m in cgenModules():
+ if m.fromCache:
+ m.updateCachedModule
+ else:
+ m.writeModule(pending=true)
+ writeMapping(gMapping)
+ if generatedHeader != nil: writeHeader(generatedHeader)
- for m in cgenModules(g):
- m.writeModule(pending=true)
- writeMapping(config, g.mapping)
- if g.generatedHeader != nil: writeHeader(g.generatedHeader)
+const cgenPass* = makePass(myOpen, myOpenCached, myProcess, myClose)
diff --git a/compiler/cgendata.nim b/compiler/cgendata.nim
index e1309e0fd..c027bb451 100644
--- a/compiler/cgendata.nim
+++ b/compiler/cgendata.nim
@@ -10,30 +10,34 @@
## This module contains the data structures for the C code generation phase.
import
- ast, ropes, options, intsets,
- tables, ndi, lineinfos, pathutils, modulegraphs, sets
+ ast, astalgo, ropes, passes, options, intsets, lists, platform
+
+from msgs import TLineInfo
type
- TLabel* = Rope # for the C generator a label is just a rope
+ 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
cfsTypes, # section for C typedefs
cfsSeqTypes, # section for sequence types only
# this is needed for strange type generation
# reasons
- cfsTypeInfo, # section for type information (ag ABI checks)
+ cfsFieldInfo, # section for field information
+ cfsTypeInfo, # section for type information
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,
@@ -49,36 +53,28 @@ type
TCProcSections* = array[TCProcSection, Rope] # represents a generated C proc
BModule* = ref TCGen
BProc* = ref TCProc
- TBlock* = object
+ TBlock*{.final.} = object
id*: int # the ID of the label; positive means that it
- label*: Rope # generated text for the label
+ label*: Rope # generated text for the label
# nil if label is not used
- sections*: TCProcSections # the code belonging
+ sections*: TCProcSections # the code beloging
isLoop*: bool # whether block is a loop
nestedTryStmts*: int16 # how many try statements is it nested into
nestedExceptStmts*: int16 # how many except statements is it nested into
frameLen*: int16
- TCProcFlag* = enum
- beforeRetNeeded,
- threadVarAccessed,
- hasCurFramePointer,
- noSafePoints,
- nimErrorFlagAccessed,
- nimErrorFlagDeclared,
- nimErrorFlagDisabled
-
- TCProc = object # represents C proc that is currently generated
+ TCProc{.final.} = object # represents C proc that is currently generated
prc*: PSym # the Nim proc that this C proc belongs to
- flags*: set[TCProcFlag]
+ beforeRetNeeded*: bool # true iff 'BeforeRet' label for proc is needed
+ threadVarAccessed*: bool # true if the proc already accessed some threadvar
lastLineInfo*: TLineInfo # to avoid generating excessive 'nimln' statements
- currLineInfo*: TLineInfo # AST codegen will make this superfluous
- nestedTryStmts*: seq[tuple[fin: PNode, inExcept: bool, label: Natural]]
- # in how many nested try statements we are
- # (the vars must be volatile then)
- # bool is true when are in the except part of a try block
+ nestedTryStmts*: seq[PNode] # in how many nested try statements we are
+ # (the vars must be volatile then)
+ inExceptBlock*: int # are we currently inside an except block?
+ # leaving such scopes by raise or by return must
+ # execute any applicable finally blocks
finallySafePoints*: seq[Rope] # For correctly cleaning up exceptions when
- # using return in finally statements
+ # using return in finally statements
labels*: Natural # for generating unique labels in the C proc
blocks*: seq[TBlock] # nested blocks
breakIdx*: int # the block that will be exited
@@ -86,22 +82,18 @@ type
options*: TOptions # options that should be used for code
# generation; this is the same as prc.options
# unless prc == nil
- optionsStack*: seq[TOptions]
+ maxFrameLen*: int # max length of frame descriptor
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
# requires 'T x = T()' to become 'T x; x = T()'
# (yes, C++ is weird like that)
- withinTryWithExcept*: int # required for goto based exception handling
- withinBlockLeaveActions*: int # complex to explain
- sigConflicts*: CountTable[string]
- inUncheckedAssignSection*: int
+ gcFrameId*: Natural # for the GC stack marking
+ gcFrameType*: Rope # the struct {} we put the GC markers into
TTypeSeq* = seq[PType]
- TypeCache* = Table[SigHash, Rope]
- TypeCacheWithOwner* = Table[SigHash, tuple[str: Rope, owner: int32]]
- CodegenFlag* = enum
+ Codegenflag* = enum
preventStackTrace, # true if stack traces need to be prevented
usesThreadVars, # true if the module uses a thread var
frameDeclared, # hack for ROD support so that we don't declare
@@ -109,105 +101,66 @@ 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
- mapping*: Rope # the generated mapping file (if requested)
- modules*: seq[BModule] # list of all compiled modules
- modulesClosed*: seq[BModule] # list of the same compiled modules, but in the order they were closed
- forwardedProcs*: seq[PSym] # proc:s that did not yet have a body
- generatedHeader*: BModule
- typeInfoMarker*: TypeCacheWithOwner
- typeInfoMarkerV2*: TypeCacheWithOwner
- config*: ConfigRef
- graph*: ModuleGraph
- strVersion*, seqVersion*: int # version of the string/seq implementation to use
-
- nimtv*: Rope # Nim thread vars; the struct body
- nimtvDeps*: seq[PType] # type deps: every module needs whole struct
- nimtvDeclared*: IntSet # so that every var/field exists only once
- # in the struct
- # 'nimtv' is incredibly hard to modularize! Best
- # effort is to store all thread vars in a ROD
- # section and with their type deps and load them
- # unconditionally...
- # nimtvDeps is VERY hard to cache because it's
- # not a list of IDs nor can it be made to be one.
-
- TCGen = object of PPassContext # represents a C source file
+ TCGen = object of TPassContext # represents a C source file
s*: TCFileSections # sections of the C file
- flags*: set[CodegenFlag]
+ flags*: set[Codegenflag]
module*: PSym
- filename*: AbsoluteFile
- cfilename*: AbsoluteFile # filename of the module (including path,
+ filename*: string
+ cfilename*: string # filename of the module (including path,
# without extension)
tmpBase*: Rope # base for temp identifier generation
- typeCache*: TypeCache # cache the generated types
- typeABICache*: HashSet[SigHash] # cache for ABI checks; reusing typeCache
- # would be ideal but for some reason enums
- # don't seem to get cached so it'd generate
- # 1 ABI check per occurence in code
- 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
+ typeCache*: TIdTable # cache the generated types
+ forwTypeCache*: TIdTable # 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
+ headerFiles*: TLinkedList # needed headers to include
+ typeInfoMarker*: IntSet # needed for generating type information
initProc*: BProc # code for init procedure
+ postInitProc*: BProc # code to be executed after the init proc
preInitProc*: BProc # code executed before the init proc
- hcrCreateTypeInfosProc*: Rope # type info globals are in here when HCR=on
- inHcrInitGuard*: bool # We are currently within a HCR reloading guard.
typeStack*: TTypeSeq # used for type generation
dataCache*: TNodeTable
+ forwardedProcs*: TSymSeq # keep forwarded procs here
typeNodes*, nimTypes*: int # used for type info generation
typeNodesName*, nimTypesName*: Rope # used for type info generation
labels*: Natural # for generating unique module-scope names
extensionLoaders*: array['0'..'9', Rope] # special procs for the
- # OpenGL wrapper
- sigConflicts*: CountTable[SigHash]
- g*: BModuleList
- ndi*: NdiFile
+ # OpenGL wrapper
+ injectStmt*: Rope
-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:
- this.headerFiles.add header
+var
+ mainModProcs*, mainModInit*, otherModsInit*, mainDatInit*: Rope
+ # varuious parts of the main module
+ gMapping*: Rope # the generated mapping file (if requested)
+ gModules*: seq[BModule] = @[] # list of all compiled modules
+ gForwardedProcsCounter*: int = 0
proc s*(p: BProc, s: TCProcSection): var Rope {.inline.} =
# section in the current block
- result = p.blocks[^1].sections[s]
+ result = p.blocks[p.blocks.len - 1].sections[s]
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 bmod*(module: PSym): BModule =
+ # obtains the BModule for a given module PSym
+ result = gModules[module.position]
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()]
+ if prc != nil: result.options = prc.options
+ else: result.options = gOptions
+ 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]](),
- config: g.config, graph: g, nimtvDeclared: initIntSet())
+iterator cgenModules*: BModule =
+ for i in 0..high(gModules):
+ # ultimately, we are iterating over the file ids here.
+ # some "files" won't have an associated cgen module (like stdin)
+ # and we must skip over them.
+ if gModules[i] != nil: yield gModules[i]
-iterator cgenModules*(g: BModuleList): BModule =
- for m in g.modulesClosed:
- # iterate modules in the order they were closed
- yield m
diff --git a/compiler/cgmeth.nim b/compiler/cgmeth.nim
index 4940a9093..312afec1a 100644
--- a/compiler/cgmeth.nim
+++ b/compiler/cgmeth.nim
@@ -7,17 +7,13 @@
# distribution, for details about the copyright.
#
-## This module implements code generation for methods.
+## This module implements code generation for multi methods.
import
- intsets, options, ast, msgs, idents, renderer, types, magicsys,
- sempass2, modulegraphs, lineinfos
+ intsets, options, ast, astalgo, msgs, idents, renderer, types, magicsys,
+ sempass2, strutils
-when defined(nimPreviewSlimSystem):
- import std/assertions
-
-
-proc genConv(n: PNode, d: PType, downcast: bool; conf: ConfigRef): PNode =
+proc genConv(n: PNode, d: PType, downcast: bool): PNode =
var dest = skipTypes(d, abstractPtrs)
var source = skipTypes(n.typ, abstractPtrs)
if (source.kind == tyObject) and (dest.kind == tyObject):
@@ -27,183 +23,177 @@ proc genConv(n: PNode, d: PType, downcast: bool; conf: ConfigRef): PNode =
result = n
elif diff < 0:
result = newNodeIT(nkObjUpConv, n.info, d)
- result.add n
- if downcast: internalError(conf, n.info, "cgmeth.genConv: no upcast allowed")
+ addSon(result, n)
+ if downcast: internalError(n.info, "cgmeth.genConv: no upcast allowed")
elif diff > 0:
result = newNodeIT(nkObjDownConv, n.info, d)
- result.add n
+ addSon(result, n)
if not downcast:
- internalError(conf, n.info, "cgmeth.genConv: no downcast allowed")
+ internalError(n.info, "cgmeth.genConv: no downcast allowed")
else:
result = n
else:
result = n
-proc getDispatcher*(s: PSym): PSym =
- ## can return nil if is has no dispatcher.
- if dispatcherPos < s.ast.len:
- result = s.ast[dispatcherPos].sym
- doAssert sfDispatcher in result.flags
-
-proc methodCall*(n: PNode; conf: ConfigRef): PNode =
+proc methodCall*(n: PNode): PNode =
result = n
# 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))
+ disp.ast.sons[bodyPos] = ast.emptyNode
+ disp.loc.r = nil
+ if s.typ.sons[0] != nil:
+ if disp.ast.sonsLen > resultPos:
+ disp.ast.sons[resultPos].sym = copySym(s.ast.sons[resultPos].sym)
else:
# We've encountered a method prototype without a filled-in
# resultPos slot. We put a placeholder in there that will
# be updated in fixupDispatcher().
- disp.ast.add newNodeI(nkEmpty, s.info)
+ disp.ast.addSon(ast.emptyNode)
attachDispatcher(s, newSymNode(disp))
# attach to itself to prevent bugs:
attachDispatcher(disp, newSymNode(disp))
return disp
-proc fixupDispatcher(meth, disp: PSym; conf: ConfigRef) =
+proc fixupDispatcher(meth, disp: PSym) =
# We may have constructed the dispatcher from a method prototype
# and need to augment the incomplete dispatcher with information
# from later definitions, particularly the resultPos slot. Also,
# the lock level of the dispatcher needs to be updated/checked
# against that of the method.
- if disp.ast.len > resultPos and meth.ast.len > resultPos and
- disp.ast[resultPos].kind == nkEmpty:
- disp.ast[resultPos] = copyTree(meth.ast[resultPos])
+ if disp.ast.sonsLen > resultPos and meth.ast.sonsLen > resultPos and
+ disp.ast.sons[resultPos] == ast.emptyNode:
+ disp.ast.sons[resultPos] = copyTree(meth.ast.sons[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(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*(s: PSym, fromCache: bool) =
+ var L = len(gMethods)
var witness: PSym
- for i in 0.. n: break
while true:
h = h div 3
- for i in h..= 0:
@@ -213,73 +203,59 @@ proc sortBucket(a: var seq[PSym], relevantCols: IntSet) =
a[j] = v
if h == 1: break
-proc genDispatcher(g: ModuleGraph; methods: seq[PSym], relevantCols: IntSet): PSym =
- var base = methods[0].ast[dispatcherPos].sym
+proc genDispatcher(methods: TSymSeq, relevantCols: IntSet): PSym =
+ var base = lastSon(methods[0].ast).sym
result = base
- var paramLen = base.typ.len
- var nilchecks = newNodeI(nkStmtList, base.info)
+ var paramLen = sonsLen(base.typ)
var disp = newNodeI(nkIfStmt, base.info)
- var ands = getSysMagic(g, unknownLineInfo, "and", mAnd)
- var iss = getSysMagic(g, unknownLineInfo, "of", mOf)
- let boolType = getSysType(g, unknownLineInfo, tyBool)
- for col in 1.. 0:
-# echo "hi"
-# yield a
-# dec a
-#
-# Should be transformed to:
-# STATE0:
-# if a > 0:
-# echo "hi"
-# :state = 1 # Next state
-# return a # yield
-# else:
-# :state = 2 # Next state
-# break :stateLoop # Proceed to the next state
-# STATE1:
-# dec a
-# :state = 0 # Next state
-# break :stateLoop # Proceed to the next state
-# STATE2:
-# :state = -1 # End of execution
-
-# The transformation should play well with lambdalifting, however depending
-# on situation, it can be called either before or after lambdalifting
-# transformation. As such we behave slightly differently, when accessing
-# iterator state, or using temp variables. If lambdalifting did not happen,
-# we just create local variables, so that they will be lifted further on.
-# Otherwise, we utilize existing env, created by lambdalifting.
-
-# Lambdalifting treats :state variable specially, it should always end up
-# as the first field in env. Currently C codegen depends on this behavior.
-
-# One special subtransformation is nkStmtListExpr lowering.
-# Example:
-# template foo(): int =
-# yield 1
-# 2
-#
-# iterator it(): int {.closure.} =
-# if foo() == 2:
-# yield 3
-#
-# If a nkStmtListExpr has yield inside, it has first to be lowered to:
-# yield 1
-# :tmpSlLower = 2
-# if :tmpSlLower == 2:
-# yield 3
-
-# nkTryStmt Transformations:
-# If the iter has an nkTryStmt with a yield inside
-# - the closure iter is promoted to have exceptions (ctx.hasExceptions = true)
-# - exception table is created. This is a const array, where
-# `abs(exceptionTable[i])` is a state idx to which we should jump from state
-# `i` should exception be raised in state `i`. For all states in `try` block
-# the target state is `except` block. For all states in `except` block
-# the target state is `finally` block. For all other states there is no
-# target state (0, as the first block can never be neither except nor finally).
-# `exceptionTable[i]` is < 0 if `abs(exceptionTable[i])` is except block,
-# and > 0, for finally block.
-# - local variable :curExc is created
-# - the iter body is wrapped into a
-# try:
-# closureIterSetupExc(:curExc)
-# ...body...
-# catch:
-# :state = exceptionTable[:state]
-# if :state == 0: raise # No state that could handle exception
-# :unrollFinally = :state > 0 # Target state is finally
-# if :state < 0:
-# :state = -:state
-# :curExc = getCurrentException()
-#
-# nkReturnStmt within a try/except/finally now has to behave differently as we
-# want the nearest finally block to be executed before the return, thus it is
-# transformed to:
-# :tmpResult = returnValue (if return doesn't have a value, this is skipped)
-# :unrollFinally = true
-# goto nearestFinally (or -1 if not exists)
-#
-# Example:
-#
-# try:
-# yield 0
-# raise ...
-# except:
-# yield 1
-# return 3
-# finally:
-# yield 2
-#
-# Is transformed to (yields are left in place for example simplicity,
-# in reality the code is subdivided even more, as described above):
-#
-# STATE0: # Try
-# yield 0
-# raise ...
-# :state = 2 # What would happen should we not raise
-# break :stateLoop
-# STATE1: # Except
-# yield 1
-# :tmpResult = 3 # Return
-# :unrollFinally = true # Return
-# :state = 2 # Goto Finally
-# break :stateLoop
-# :state = 2 # What would happen should we not return
-# break :stateLoop
-# STATE2: # Finally
-# yield 2
-# if :unrollFinally: # This node is created by `newEndFinallyNode`
-# if :curExc.isNil:
-# if nearestFinally == 0:
-# return :tmpResult
-# else:
-# :state = nearestFinally # bubble up
-# else:
-# closureIterSetupExc(nil)
-# raise
-# state = -1 # Goto next state. In this case we just exit
-# break :stateLoop
-
-import
- ast, msgs, idents,
- renderer, magicsys, lowerings, lambdalifting, modulegraphs, lineinfos,
- tables, options
-
-when defined(nimPreviewSlimSystem):
- import std/assertions
-
-type
- Ctx = object
- g: ModuleGraph
- fn: PSym
- stateVarSym: PSym # :state variable. nil if env already introduced by lambdalifting
- tmpResultSym: PSym # Used when we return, but finally has to interfere
- unrollFinallySym: PSym # Indicates that we're unrolling finally states (either exception happened or premature return)
- curExcSym: PSym # Current exception
-
- states: seq[PNode] # The resulting states. Every state is an nkState node.
- blockLevel: int # Temp used to transform break and continue stmts
- stateLoopLabel: PSym # Label to break on, when jumping between states.
- exitStateIdx: int # index of the last state
- tempVarId: int # unique name counter
- tempVars: PNode # Temp var decls, nkVarSection
- exceptionTable: seq[int] # For state `i` jump to state `exceptionTable[i]` if exception is raised
- hasExceptions: bool # Does closure have yield in try?
- curExcHandlingState: int # Negative for except, positive for finally
- nearestFinally: int # Index of the nearest finally block. For try/except it
- # is their finally. For finally it is parent finally. Otherwise -1
- idgen: IdGenerator
-
-const
- nkSkip = {nkEmpty..nkNilLit, nkTemplateDef, nkTypeSection, nkStaticStmt,
- nkCommentStmt, nkMixinStmt, nkBindStmt} + procDefs
-
-proc newStateAccess(ctx: var Ctx): PNode =
- if ctx.stateVarSym.isNil:
- result = rawIndirectAccess(newSymNode(getEnvParam(ctx.fn)),
- getStateField(ctx.g, ctx.fn), ctx.fn.info)
- else:
- result = newSymNode(ctx.stateVarSym)
-
-proc newStateAssgn(ctx: var Ctx, toValue: PNode): PNode =
- # Creates state assignment:
- # :state = toValue
- newTree(nkAsgn, ctx.newStateAccess(), toValue)
-
-proc newStateAssgn(ctx: var Ctx, stateNo: int = -2): PNode =
- # Creates state assignment:
- # :state = stateNo
- ctx.newStateAssgn(newIntTypeNode(stateNo, ctx.g.getSysType(TLineInfo(), tyInt)))
-
-proc newEnvVar(ctx: var Ctx, name: string, typ: PType): PSym =
- result = newSym(skVar, getIdent(ctx.g.cache, name), nextSymId(ctx.idgen), ctx.fn, ctx.fn.info)
- result.typ = typ
- assert(not typ.isNil)
-
- if not ctx.stateVarSym.isNil:
- # We haven't gone through labmda lifting yet, so just create a local var,
- # it will be lifted later
- if ctx.tempVars.isNil:
- ctx.tempVars = newNodeI(nkVarSection, ctx.fn.info)
- addVar(ctx.tempVars, newSymNode(result))
- else:
- let envParam = getEnvParam(ctx.fn)
- # let obj = envParam.typ.lastSon
- result = addUniqueField(envParam.typ.lastSon, result, ctx.g.cache, ctx.idgen)
-
-proc newEnvVarAccess(ctx: Ctx, s: PSym): PNode =
- if ctx.stateVarSym.isNil:
- result = rawIndirectAccess(newSymNode(getEnvParam(ctx.fn)), s, ctx.fn.info)
- else:
- result = newSymNode(s)
-
-proc newTmpResultAccess(ctx: var Ctx): PNode =
- if ctx.tmpResultSym.isNil:
- ctx.tmpResultSym = ctx.newEnvVar(":tmpResult", ctx.fn.typ[0])
- ctx.newEnvVarAccess(ctx.tmpResultSym)
-
-proc newUnrollFinallyAccess(ctx: var Ctx, info: TLineInfo): PNode =
- if ctx.unrollFinallySym.isNil:
- ctx.unrollFinallySym = ctx.newEnvVar(":unrollFinally", ctx.g.getSysType(info, tyBool))
- ctx.newEnvVarAccess(ctx.unrollFinallySym)
-
-proc newCurExcAccess(ctx: var Ctx): PNode =
- if ctx.curExcSym.isNil:
- ctx.curExcSym = ctx.newEnvVar(":curExc", ctx.g.callCodegenProc("getCurrentException").typ)
- ctx.newEnvVarAccess(ctx.curExcSym)
-
-proc newState(ctx: var Ctx, n, gotoOut: PNode): int =
- # Creates a new state, adds it to the context fills out `gotoOut` so that it
- # will goto this state.
- # Returns index of the newly created state
-
- result = ctx.states.len
- let resLit = ctx.g.newIntLit(n.info, result)
- let s = newNodeI(nkState, n.info)
- s.add(resLit)
- s.add(n)
- ctx.states.add(s)
- ctx.exceptionTable.add(ctx.curExcHandlingState)
-
- if not gotoOut.isNil:
- assert(gotoOut.len == 0)
- gotoOut.add(ctx.g.newIntLit(gotoOut.info, result))
-
-proc toStmtList(n: PNode): PNode =
- result = n
- if result.kind notin {nkStmtList, nkStmtListExpr}:
- result = newNodeI(nkStmtList, n.info)
- result.add(n)
-
-proc addGotoOut(n: PNode, gotoOut: PNode): PNode =
- # Make sure `n` is a stmtlist, and ends with `gotoOut`
- result = toStmtList(n)
- if result.len == 0 or result[^1].kind != nkGotoState:
- result.add(gotoOut)
-
-proc newTempVar(ctx: var Ctx, typ: PType): PSym =
- result = ctx.newEnvVar(":tmpSlLower" & $ctx.tempVarId, typ)
- inc ctx.tempVarId
-
-proc hasYields(n: PNode): bool =
- # TODO: This is very inefficient. It traverses the node, looking for nkYieldStmt.
- case n.kind
- of nkYieldStmt:
- result = true
- of nkSkip:
- discard
- else:
- for c in n:
- if c.hasYields:
- result = true
- break
-
-proc transformBreaksAndContinuesInWhile(ctx: var Ctx, n: PNode, before, after: PNode): PNode =
- result = n
- case n.kind
- of nkSkip:
- discard
- of nkWhileStmt: discard # Do not recurse into nested whiles
- of nkContinueStmt:
- result = before
- of nkBlockStmt:
- inc ctx.blockLevel
- result[1] = ctx.transformBreaksAndContinuesInWhile(result[1], before, after)
- dec ctx.blockLevel
- of nkBreakStmt:
- if ctx.blockLevel == 0:
- result = after
- else:
- for i in 0.. 1:
- var cond: PNode
- for i in 0..
- # BEGIN_STATE:
- # if e:
- # s
- # goto BEGIN_STATE
- # else:
- # goto OUT
-
- result = newNodeI(nkGotoState, n.info)
-
- let s = newNodeI(nkStmtList, n.info)
- discard ctx.newState(s, result)
- let ifNode = newNodeI(nkIfStmt, n.info)
- let elifBranch = newNodeI(nkElifBranch, n.info)
- elifBranch.add(n[0])
-
- var body = addGotoOut(n[1], result)
-
- body = ctx.transformBreaksAndContinuesInWhile(body, result, gotoOut)
- body = ctx.transformClosureIteratorBody(body, result)
-
- elifBranch.add(body)
- ifNode.add(elifBranch)
-
- let elseBranch = newTree(nkElse, gotoOut)
- ifNode.add(elseBranch)
- s.add(ifNode)
-
- of nkBlockStmt:
- result[1] = addGotoOut(result[1], gotoOut)
- result[1] = ctx.transformBreaksInBlock(result[1], result[0], gotoOut)
- result[1] = ctx.transformClosureIteratorBody(result[1], gotoOut)
-
- of nkTryStmt, nkHiddenTryStmt:
- # See explanation above about how this works
- ctx.hasExceptions = true
-
- result = newNodeI(nkGotoState, n.info)
- var tryBody = toStmtList(n[0])
- var exceptBody = ctx.collectExceptState(n)
- var finallyBody = newTree(nkStmtList, getFinallyNode(ctx, n))
- finallyBody = ctx.transformReturnsInTry(finallyBody)
- finallyBody.add(ctx.newEndFinallyNode(finallyBody.info))
-
- # The following index calculation is based on the knowledge how state
- # indexes are assigned
- let tryIdx = ctx.states.len
- var exceptIdx, finallyIdx: int
- if exceptBody.kind != nkEmpty:
- exceptIdx = -(tryIdx + 1)
- finallyIdx = tryIdx + 2
- else:
- exceptIdx = tryIdx + 1
- finallyIdx = tryIdx + 1
-
- let outToFinally = newNodeI(nkGotoState, finallyBody.info)
-
- block: # Create initial states.
- let oldExcHandlingState = ctx.curExcHandlingState
- ctx.curExcHandlingState = exceptIdx
- let realTryIdx = ctx.newState(tryBody, result)
- assert(realTryIdx == tryIdx)
-
- if exceptBody.kind != nkEmpty:
- ctx.curExcHandlingState = finallyIdx
- let realExceptIdx = ctx.newState(exceptBody, nil)
- assert(realExceptIdx == -exceptIdx)
-
- ctx.curExcHandlingState = oldExcHandlingState
- let realFinallyIdx = ctx.newState(finallyBody, outToFinally)
- assert(realFinallyIdx == finallyIdx)
-
- block: # Subdivide the states
- let oldNearestFinally = ctx.nearestFinally
- ctx.nearestFinally = finallyIdx
-
- let oldExcHandlingState = ctx.curExcHandlingState
-
- ctx.curExcHandlingState = exceptIdx
-
- if ctx.transformReturnsInTry(tryBody) != tryBody:
- internalError(ctx.g.config, "transformReturnsInTry != tryBody")
- if ctx.transformClosureIteratorBody(tryBody, outToFinally) != tryBody:
- internalError(ctx.g.config, "transformClosureIteratorBody != tryBody")
-
- ctx.curExcHandlingState = finallyIdx
- ctx.addElseToExcept(exceptBody)
- if ctx.transformReturnsInTry(exceptBody) != exceptBody:
- internalError(ctx.g.config, "transformReturnsInTry != exceptBody")
- if ctx.transformClosureIteratorBody(exceptBody, outToFinally) != exceptBody:
- internalError(ctx.g.config, "transformClosureIteratorBody != exceptBody")
-
- ctx.curExcHandlingState = oldExcHandlingState
- ctx.nearestFinally = oldNearestFinally
- if ctx.transformClosureIteratorBody(finallyBody, gotoOut) != finallyBody:
- internalError(ctx.g.config, "transformClosureIteratorBody != finallyBody")
-
- of nkGotoState, nkForStmt:
- internalError(ctx.g.config, "closure iter " & $n.kind)
-
- else:
- for i in 0..
- # :state = STATE
- # return e
- ########################## 2
- # goto STATE
- # ->
- # :state = STATE
- # break :stateLoop
- ########################## 3
- # return e
- # ->
- # :state = -1
- # return e
- #
- result = n
- case n.kind
- of nkStmtList, nkStmtListExpr:
- if n.len != 0 and n[0].kind == nkYieldStmt:
- assert(n.len == 2)
- assert(n[1].kind == nkGotoState)
-
- result = newNodeI(nkStmtList, n.info)
- result.add(ctx.newStateAssgn(stateFromGotoState(n[1])))
-
- var retStmt = newNodeI(nkReturnStmt, n.info)
- if n[0][0].kind != nkEmpty:
- var a = newNodeI(nkAsgn, n[0][0].info)
- var retVal = n[0][0] #liftCapturedVars(n[0], owner, d, c)
- a.add newSymNode(getClosureIterResult(ctx.g, ctx.fn, ctx.idgen))
- a.add retVal
- retStmt.add(a)
- else:
- retStmt.add(ctx.g.emptyNode)
-
- result.add(retStmt)
- else:
- for i in 0.. 0
- # if :state < 0:
- # :state = -:state
- # :curExc = getCurrentException()
-
- result = newNodeI(nkStmtList, info)
-
- let intTyp = ctx.g.getSysType(info, tyInt)
- let boolTyp = ctx.g.getSysType(info, tyBool)
-
- # :state = exceptionTable[:state]
- block:
- # exceptionTable[:state]
- let getNextState = newTree(nkBracketExpr,
- ctx.createExceptionTable(),
- ctx.newStateAccess())
- getNextState.typ = intTyp
-
- # :state = exceptionTable[:state]
- result.add(ctx.newStateAssgn(getNextState))
-
- # if :state == 0: raise
- block:
- let cond = newTree(nkCall,
- ctx.g.getSysMagic(info, "==", mEqI).newSymNode(),
- ctx.newStateAccess(),
- newIntTypeNode(0, intTyp))
- cond.typ = boolTyp
-
- let raiseStmt = newTree(nkRaiseStmt, ctx.g.emptyNode)
- let ifBranch = newTree(nkElifBranch, cond, raiseStmt)
- let ifStmt = newTree(nkIfStmt, ifBranch)
- result.add(ifStmt)
-
- # :unrollFinally = :state > 0
- block:
- let cond = newTree(nkCall,
- ctx.g.getSysMagic(info, "<", mLtI).newSymNode,
- newIntTypeNode(0, intTyp),
- ctx.newStateAccess())
- cond.typ = boolTyp
-
- let asgn = newTree(nkAsgn, ctx.newUnrollFinallyAccess(info), cond)
- result.add(asgn)
-
- # if :state < 0: :state = -:state
- block:
- let cond = newTree(nkCall,
- ctx.g.getSysMagic(info, "<", mLtI).newSymNode,
- ctx.newStateAccess(),
- newIntTypeNode(0, intTyp))
- cond.typ = boolTyp
-
- let negateState = newTree(nkCall,
- ctx.g.getSysMagic(info, "-", mUnaryMinusI).newSymNode,
- ctx.newStateAccess())
- negateState.typ = intTyp
-
- let ifBranch = newTree(nkElifBranch, cond, ctx.newStateAssgn(negateState))
- let ifStmt = newTree(nkIfStmt, ifBranch)
- result.add(ifStmt)
-
- # :curExc = getCurrentException()
- block:
- result.add(newTree(nkAsgn,
- ctx.newCurExcAccess(),
- ctx.g.callCodegenProc("getCurrentException")))
-
-proc wrapIntoTryExcept(ctx: var Ctx, n: PNode): PNode {.inline.} =
- let setupExc = newTree(nkCall,
- newSymNode(ctx.g.getCompilerProc("closureIterSetupExc")),
- ctx.newCurExcAccess())
-
- let tryBody = newTree(nkStmtList, setupExc, n)
- let exceptBranch = newTree(nkExceptBranch, ctx.newCatchBody(ctx.fn.info))
-
- result = newTree(nkTryStmt, tryBody, exceptBranch)
-
-proc wrapIntoStateLoop(ctx: var Ctx, n: PNode): PNode =
- # while true:
- # block :stateLoop:
- # gotoState :state
- # local vars decl (if needed)
- # body # Might get wrapped in try-except
- let loopBody = newNodeI(nkStmtList, n.info)
- result = newTree(nkWhileStmt, newSymNode(ctx.g.getSysSym(n.info, "true")), loopBody)
- result.info = n.info
-
- let localVars = newNodeI(nkStmtList, n.info)
- if not ctx.stateVarSym.isNil:
- let varSect = newNodeI(nkVarSection, n.info)
- addVar(varSect, newSymNode(ctx.stateVarSym))
- localVars.add(varSect)
-
- if not ctx.tempVars.isNil:
- localVars.add(ctx.tempVars)
-
- let blockStmt = newNodeI(nkBlockStmt, n.info)
- blockStmt.add(newSymNode(ctx.stateLoopLabel))
-
- let gs = newNodeI(nkGotoState, n.info)
- gs.add(ctx.newStateAccess())
- gs.add(ctx.g.newIntLit(n.info, ctx.states.len - 1))
-
- var blockBody = newTree(nkStmtList, gs, localVars, n)
- if ctx.hasExceptions:
- blockBody = ctx.wrapIntoTryExcept(blockBody)
-
- blockStmt.add(blockBody)
- loopBody.add(blockStmt)
-
-proc deleteEmptyStates(ctx: var Ctx) =
- let goOut = newTree(nkGotoState, ctx.g.newIntLit(TLineInfo(), -1))
- ctx.exitStateIdx = ctx.newState(goOut, nil)
-
- # Apply new state indexes and mark unused states with -1
- var iValid = 0
- for i, s in ctx.states:
- let body = skipStmtList(ctx, s[1])
- if body.kind == nkGotoState and i != ctx.states.len - 1 and i != 0:
- # This is an empty state. Mark with -1.
- s[0].intVal = -1
- else:
- s[0].intVal = iValid
- inc iValid
-
- for i, s in ctx.states:
- let body = skipStmtList(ctx, s[1])
- if body.kind != nkGotoState or i == 0:
- discard ctx.skipThroughEmptyStates(s)
- let excHandlState = ctx.exceptionTable[i]
- if excHandlState < 0:
- ctx.exceptionTable[i] = -ctx.skipEmptyStates(-excHandlState)
- elif excHandlState != 0:
- ctx.exceptionTable[i] = ctx.skipEmptyStates(excHandlState)
-
- var i = 0
- while i < ctx.states.len - 1:
- let fs = skipStmtList(ctx, ctx.states[i][1])
- if fs.kind == nkGotoState and i != 0:
- ctx.states.delete(i)
- ctx.exceptionTable.delete(i)
- else:
- inc i
-
-type
- PreprocessContext = object
- finallys: seq[PNode]
- config: ConfigRef
- blocks: seq[(PNode, int)]
- idgen: IdGenerator
- FreshVarsContext = object
- tab: Table[int, PSym]
- config: ConfigRef
- info: TLineInfo
- idgen: IdGenerator
-
-proc freshVars(n: PNode; c: var FreshVarsContext): PNode =
- case n.kind
- of nkSym:
- let x = c.tab.getOrDefault(n.sym.id)
- if x == nil:
- result = n
- else:
- result = newSymNode(x, n.info)
- of nkSkip - {nkSym}:
- result = n
- of nkLetSection, nkVarSection:
- result = copyNode(n)
- for it in n:
- if it.kind in {nkIdentDefs, nkVarTuple}:
- let idefs = copyNode(it)
- for v in 0..it.len-3:
- if it[v].kind == nkSym:
- let x = copySym(it[v].sym, nextSymId(c.idgen))
- c.tab[it[v].sym.id] = x
- idefs.add newSymNode(x)
- else:
- idefs.add it[v]
-
- for rest in it.len-2 ..< it.len: idefs.add it[rest]
- result.add idefs
- else:
- result.add it
- of nkRaiseStmt:
- localError(c.config, c.info, "unsupported control flow: 'finally: ... raise' duplicated because of 'break'")
- else:
- result = n
- for i in 0..= 0:
- result = newNodeI(nkStmtList, n.info)
- for i in countdown(c.finallys.high, fin):
- var vars = FreshVarsContext(tab: initTable[int, PSym](), config: c.config, info: n.info, idgen: c.idgen)
- result.add freshVars(copyTree(c.finallys[i]), vars)
- c.idgen = vars.idgen
- result.add n
- of nkSkip: discard
- else:
- for i in 0 ..< n.len:
- result[i] = preprocess(c, n[i])
-
-proc transformClosureIterator*(g: ModuleGraph; idgen: IdGenerator; fn: PSym, n: PNode): PNode =
- var ctx: Ctx
- ctx.g = g
- ctx.fn = fn
- ctx.idgen = idgen
-
- if getEnvParam(fn).isNil:
- # Lambda lifting was not done yet. Use temporary :state sym, which will
- # be handled specially by lambda lifting. Local temp vars (if needed)
- # should follow the same logic.
- ctx.stateVarSym = newSym(skVar, getIdent(ctx.g.cache, ":state"), nextSymId(idgen), fn, fn.info)
- ctx.stateVarSym.typ = g.createClosureIterStateType(fn, idgen)
- ctx.stateLoopLabel = newSym(skLabel, getIdent(ctx.g.cache, ":stateLoop"), nextSymId(idgen), fn, fn.info)
- var pc = PreprocessContext(finallys: @[], config: g.config, idgen: idgen)
- var n = preprocess(pc, n.toStmtList)
- #echo "transformed into ", n
- #var n = n.toStmtList
-
- discard ctx.newState(n, nil)
- let gotoOut = newTree(nkGotoState, g.newIntLit(n.info, -1))
-
- var ns = false
- n = ctx.lowerStmtListExprs(n, ns)
-
- if n.hasYieldsInExpressions():
- internalError(ctx.g.config, "yield in expr not lowered")
-
- # Splitting transformation
- discard ctx.transformClosureIteratorBody(n, gotoOut)
-
- # Optimize empty states away
- ctx.deleteEmptyStates()
-
- # Make new body by concatenating the list of states
- result = newNodeI(nkStmtList, n.info)
- for s in ctx.states:
- assert(s.len == 2)
- let body = s[1]
- s.sons.del(1)
- result.add(s)
- result.add(body)
-
- result = ctx.transformStateAssignments(result)
- result = ctx.wrapIntoStateLoop(result)
-
- # echo "TRANSFORM TO STATES: "
- # echo renderTree(result)
-
- # echo "exception table:"
- # for i, e in ctx.exceptionTable:
- # echo i, " -> ", e
diff --git a/compiler/cmdlinehelper.nim b/compiler/cmdlinehelper.nim
deleted file mode 100644
index 02162755c..000000000
--- a/compiler/cmdlinehelper.nim
+++ /dev/null
@@ -1,78 +0,0 @@
-#
-#
-# The Nim Compiler
-# (c) Copyright 2018 Nim contributors
-#
-# See the file "copying.txt", included in this
-# distribution, for details about the copyright.
-#
-
-## Helpers for binaries that use compiler passes, e.g.: nim, nimsuggest, nimfix
-
-import
- options, idents, nimconf, extccomp, commands, msgs,
- lineinfos, modulegraphs, condsyms, os, pathutils, parseopt
-
-proc prependCurDir*(f: AbsoluteFile): AbsoluteFile =
- when defined(unix):
- if os.isAbsolute(f.string): result = f
- else: result = AbsoluteFile("./" & f.string)
- else:
- result = f
-
-proc addCmdPrefix*(result: var string, kind: CmdLineKind) =
- # consider moving this to std/parseopt
- case kind
- of cmdLongOption: result.add "--"
- of cmdShortOption: result.add "-"
- of cmdArgument, cmdEnd: discard
-
-type
- NimProg* = ref object
- suggestMode*: bool
- supportsStdinFile*: bool
- processCmdLine*: proc(pass: TCmdLinePass, cmd: string; config: ConfigRef)
-
-proc initDefinesProg*(self: NimProg, conf: ConfigRef, name: string) =
- condsyms.initDefines(conf.symbols)
- defineSymbol conf.symbols, name
-
-proc processCmdLineAndProjectPath*(self: NimProg, conf: ConfigRef) =
- self.processCmdLine(passCmd1, "", conf)
- if conf.projectIsCmd and conf.projectName in ["-", ""]:
- handleCmdInput(conf)
- elif self.supportsStdinFile and conf.projectName == "-":
- handleStdinInput(conf)
- elif conf.projectName != "":
- setFromProjectName(conf, conf.projectName)
- else:
- conf.projectPath = AbsoluteDir canonicalizePath(conf, AbsoluteFile getCurrentDir())
-
-proc loadConfigsAndProcessCmdLine*(self: NimProg, cache: IdentCache; conf: ConfigRef;
- graph: ModuleGraph): bool =
- if self.suggestMode:
- conf.setCmd cmdIdeTools
- if conf.cmd == cmdNimscript:
- incl(conf.globalOptions, optWasNimscript)
- loadConfigs(DefaultConfig, cache, conf, graph.idgen) # load all config files
-
- if not self.suggestMode:
- let scriptFile = conf.projectFull.changeFileExt("nims")
- # 'nim foo.nims' means to just run the NimScript file and do nothing more:
- if fileExists(scriptFile) and scriptFile == conf.projectFull:
- if conf.cmd == cmdNone: conf.setCmd cmdNimscript
- if conf.cmd == cmdNimscript: return false
- # now process command line arguments again, because some options in the
- # command line can overwrite the config file's settings
- if conf.backend != backendJs: # bug #19059
- extccomp.initVars(conf)
- self.processCmdLine(passCmd2, "", conf)
- if conf.cmd == cmdNone:
- rawMessage(conf, errGenerated, "command missing")
-
- graph.suggestMode = self.suggestMode
- return true
-
-proc loadConfigsAndRunMainCommand*(self: NimProg, cache: IdentCache; conf: ConfigRef; graph: ModuleGraph): bool =
- ## Alias for loadConfigsAndProcessCmdLine, here for backwards compatibility
- loadConfigsAndProcessCmdLine(self, cache, conf, graph)
diff --git a/compiler/commands.nim b/compiler/commands.nim
index 7c0f7ab0a..b3edb5e14 100644
--- a/compiler/commands.nim
+++ b/compiler/commands.nim
@@ -9,34 +9,36 @@
# This module handles the parsing of command line arguments.
+
# We do this here before the 'import' statement so 'defined' does not get
-# confused with 'TGCMode.gcMarkAndSweep' etc.
-template bootSwitch(name, expr, userString) =
+# confused with 'TGCMode.gcGenerational' etc.
+template bootSwitch(name, expr, userString: expr): expr =
# Helper to build boot constants, for debugging you can 'echo' the else part.
const name = if expr: " " & userString else: ""
bootSwitch(usedRelease, defined(release), "-d:release")
-bootSwitch(usedDanger, defined(danger), "-d:danger")
-# `useLinenoise` deprecated in favor of `nimUseLinenoise`, kept for backward compatibility
-bootSwitch(useLinenoise, defined(nimUseLinenoise) or defined(useLinenoise), "-d:nimUseLinenoise")
+bootSwitch(usedGnuReadline, defined(useLinenoise), "-d:useLinenoise")
+bootSwitch(usedNoCaas, defined(noCaas), "-d:noCaas")
bootSwitch(usedBoehm, defined(boehmgc), "--gc:boehm")
bootSwitch(usedMarkAndSweep, defined(gcmarkandsweep), "--gc:markAndSweep")
+bootSwitch(usedGenerational, defined(gcgenerational), "--gc:generational")
bootSwitch(usedGoGC, defined(gogc), "--gc:go")
bootSwitch(usedNoGC, defined(nogc), "--gc:none")
-import std/[setutils, os, strutils, parseutils, parseopt, sequtils, strtabs]
import
- msgs, options, nversion, condsyms, extccomp, platform,
- wordrecg, nimblecmd, lineinfos, pathutils, pathnorm
-
-from ast import setUseIc, eqTypeFlags, tfGcSafe, tfNoSideEffect
-
-when defined(nimPreviewSlimSystem):
- import std/assertions
+ os, msgs, options, nversion, condsyms, strutils, extccomp, platform, lists,
+ wordrecg, parseutils, nimblecmd, idents, parseopt
# but some have deps to imported modules. Yay.
bootSwitch(usedTinyC, hasTinyCBackend, "-d:tinyc")
-bootSwitch(usedFFI, hasFFI, "-d:nimHasLibFFI")
+bootSwitch(usedAvoidTimeMachine, noTimeMachine, "-d:avoidTimeMachine")
+bootSwitch(usedNativeStacktrace,
+ defined(nativeStackTrace) and nativeStackTraceSupported,
+ "-d:nativeStackTrace")
+bootSwitch(usedFFI, hasFFI, "-d:useFFI")
+
+
+proc writeCommandLineUsage*()
type
TCmdLinePass* = enum
@@ -44,1113 +46,633 @@ type
passCmd2, # second pass over the command line
passPP # preprocessor called processCommand()
-const
- HelpMessage = "Nim Compiler Version $1 [$2: $3]\n" &
- "Compiled at $4\n" &
- "Copyright (c) 2006-" & copyrightYear & " by Andreas Rumpf\n"
+proc processCommand*(switch: string, pass: TCmdLinePass)
+proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo)
-proc genFeatureDesc[T: enum](t: typedesc[T]): string {.compileTime.} =
- result = ""
- for f in T:
- if result.len > 0: result.add "|"
- result.add $f
+# implementation
const
- Usage = slurp"../doc/basicopt.txt".replace(" //", " ")
- AdvancedUsage = slurp"../doc/advopt.txt".replace(" //", " ") % [genFeatureDesc(Feature), genFeatureDesc(LegacyFeature)]
+ HelpMessage = "Nim Compiler Version $1 (" & CompileDate & ") [$2: $3]\n" &
+ "Copyright (c) 2006-" & CompileDate.substr(0, 3) & " by Andreas Rumpf\n"
-proc getCommandLineDesc(conf: ConfigRef): string =
- result = (HelpMessage % [VersionAsString, platform.OS[conf.target.hostOS].name,
- CPU[conf.target.hostCPU].name, CompileDate]) &
- Usage
+const
+ Usage = slurp"doc/basicopt.txt".replace("//", "")
+ AdvancedUsage = slurp"doc/advopt.txt".replace("//", "")
-proc helpOnError(conf: ConfigRef; pass: TCmdLinePass) =
+proc getCommandLineDesc(): string =
+ result = (HelpMessage % [VersionAsString, platform.OS[platform.hostOS].name,
+ CPU[platform.hostCPU].name]) & Usage
+
+proc helpOnError(pass: TCmdLinePass) =
if pass == passCmd1:
- msgWriteln(conf, getCommandLineDesc(conf), {msgStdout})
+ msgWriteln(getCommandLineDesc(), {msgStdout})
msgQuit(0)
-proc writeAdvancedUsage(conf: ConfigRef; pass: TCmdLinePass) =
+proc writeAdvancedUsage(pass: TCmdLinePass) =
if pass == passCmd1:
- msgWriteln(conf, (HelpMessage % [VersionAsString,
- platform.OS[conf.target.hostOS].name,
- CPU[conf.target.hostCPU].name, CompileDate]) &
- AdvancedUsage,
+ msgWriteln(`%`(HelpMessage, [VersionAsString,
+ platform.OS[platform.hostOS].name,
+ CPU[platform.hostCPU].name]) & AdvancedUsage,
{msgStdout})
msgQuit(0)
-proc writeFullhelp(conf: ConfigRef; pass: TCmdLinePass) =
+proc writeVersionInfo(pass: TCmdLinePass) =
if pass == passCmd1:
- msgWriteln(conf, `%`(HelpMessage, [VersionAsString,
- platform.OS[conf.target.hostOS].name,
- CPU[conf.target.hostCPU].name, CompileDate]) &
- Usage & AdvancedUsage,
- {msgStdout})
- msgQuit(0)
+ msgWriteln(`%`(HelpMessage, [VersionAsString,
+ platform.OS[platform.hostOS].name,
+ CPU[platform.hostCPU].name]))
-proc writeVersionInfo(conf: ConfigRef; pass: TCmdLinePass) =
- if pass == passCmd1:
- msgWriteln(conf, `%`(HelpMessage, [VersionAsString,
- platform.OS[conf.target.hostOS].name,
- CPU[conf.target.hostCPU].name, CompileDate]),
- {msgStdout})
-
- const gitHash {.strdefine.} = gorge("git log -n 1 --format=%H").strip
- # xxx move this logic to std/private/gitutils
+ const gitHash = gorge("git log -n 1 --format=%H").strip
when gitHash.len == 40:
- msgWriteln(conf, "git hash: " & gitHash, {msgStdout})
+ msgWriteln("git hash: " & gitHash)
- msgWriteln(conf, "active boot switches:" & usedRelease & usedDanger &
- usedTinyC & useLinenoise &
- usedFFI & usedBoehm & usedMarkAndSweep & usedGoGC & usedNoGC,
- {msgStdout})
+ msgWriteln("active boot switches:" & usedRelease & usedAvoidTimeMachine &
+ usedTinyC & usedGnuReadline & usedNativeStacktrace & usedNoCaas &
+ usedFFI & usedBoehm & usedMarkAndSweep & usedGenerational & usedGoGC & usedNoGC)
msgQuit(0)
-proc writeCommandLineUsage*(conf: ConfigRef) =
- msgWriteln(conf, getCommandLineDesc(conf), {msgStdout})
+var
+ helpWritten: bool
+
+proc writeCommandLineUsage() =
+ if not helpWritten:
+ msgWriteln(getCommandLineDesc(), {msgStdout})
+ helpWritten = true
proc addPrefix(switch: string): string =
- if switch.len <= 1: result = "-" & switch
+ if len(switch) == 1: result = "-" & switch
else: result = "--" & switch
-const
- errInvalidCmdLineOption = "invalid command line option: '$1'"
- errOnOrOffExpectedButXFound = "'on' or 'off' expected, but '$1' found"
- errOnOffOrListExpectedButXFound = "'on', 'off' or 'list' expected, but '$1' found"
- errOffHintsError = "'off', 'hint', 'error' or 'usages' expected, but '$1' found"
+proc invalidCmdLineOption(pass: TCmdLinePass, switch: string, info: TLineInfo) =
+ if switch == " ": localError(info, errInvalidCmdLineOption, "-")
+ else: localError(info, errInvalidCmdLineOption, addPrefix(switch))
-proc invalidCmdLineOption(conf: ConfigRef; pass: TCmdLinePass, switch: string, info: TLineInfo) =
- if switch == " ": localError(conf, info, errInvalidCmdLineOption % "-")
- else: localError(conf, info, errInvalidCmdLineOption % addPrefix(switch))
-
-proc splitSwitch(conf: ConfigRef; switch: string, cmd, arg: var string, pass: TCmdLinePass,
+proc splitSwitch(switch: string, cmd, arg: var string, pass: TCmdLinePass,
info: TLineInfo) =
cmd = ""
var i = 0
- if i < switch.len and switch[i] == '-': inc(i)
- if i < switch.len and switch[i] == '-': inc(i)
- while i < switch.len:
+ if i < len(switch) and switch[i] == '-': inc(i)
+ if i < len(switch) and switch[i] == '-': inc(i)
+ while i < len(switch):
case switch[i]
- of 'a'..'z', 'A'..'Z', '0'..'9', '_', '.': cmd.add(switch[i])
+ of 'a'..'z', 'A'..'Z', '0'..'9', '_', '.': add(cmd, switch[i])
else: break
inc(i)
- if i >= switch.len: arg = ""
- # cmd:arg => (cmd,arg)
- elif switch[i] in {':', '='}: arg = substr(switch, i + 1)
- # cmd[sub]:rest => (cmd,[sub]:rest)
- elif switch[i] == '[': arg = substr(switch, i)
- else: invalidCmdLineOption(conf, pass, switch, info)
+ if i >= len(switch): arg = ""
+ elif switch[i] in {':', '=', '['}: arg = substr(switch, i + 1)
+ else: invalidCmdLineOption(pass, switch, info)
-template switchOn(arg: string): bool =
- # xxx use `switchOn` wherever appropriate
- case arg.normalize
- of "", "on": true
- of "off": false
- else:
- localError(conf, info, errOnOrOffExpectedButXFound % arg)
- false
-
-proc processOnOffSwitch(conf: ConfigRef; op: TOptions, arg: string, pass: TCmdLinePass,
+proc processOnOffSwitch(op: TOptions, arg: string, pass: TCmdLinePass,
info: TLineInfo) =
- case arg.normalize
- of "", "on": conf.options.incl op
- of "off": conf.options.excl op
- else: localError(conf, info, errOnOrOffExpectedButXFound % arg)
+ case whichKeyword(arg)
+ of wOn: gOptions = gOptions + op
+ of wOff: gOptions = gOptions - op
+ else: localError(info, errOnOrOffExpectedButXFound, arg)
-proc processOnOffSwitchOrList(conf: ConfigRef; op: TOptions, arg: string, pass: TCmdLinePass,
+proc processOnOffSwitchOrList(op: TOptions, arg: string, pass: TCmdLinePass,
info: TLineInfo): bool =
result = false
- case arg.normalize
- of "on": conf.options.incl op
- of "off": conf.options.excl op
- of "list": result = true
- else: localError(conf, info, errOnOffOrListExpectedButXFound % arg)
-
-proc processOnOffSwitchG(conf: ConfigRef; op: TGlobalOptions, arg: string, pass: TCmdLinePass,
- info: TLineInfo) =
- case arg.normalize
- of "", "on": conf.globalOptions.incl op
- of "off": conf.globalOptions.excl op
- else: localError(conf, info, errOnOrOffExpectedButXFound % arg)
-
-proc expectArg(conf: ConfigRef; switch, arg: string, pass: TCmdLinePass, info: TLineInfo) =
- if arg == "":
- localError(conf, info, "argument for command line option expected: '$1'" % addPrefix(switch))
-
-proc expectNoArg(conf: ConfigRef; switch, arg: string, pass: TCmdLinePass, info: TLineInfo) =
- if arg != "":
- localError(conf, info, "invalid argument for command line option: '$1'" % addPrefix(switch))
-
-proc processSpecificNote*(arg: string, state: TSpecialWord, pass: TCmdLinePass,
- info: TLineInfo; orig: string; conf: ConfigRef) =
- var id = "" # arg = key or [key] or key:val or [key]:val; with val=on|off
- var i = 0
- var notes: set[TMsgKind]
- var isBracket = false
- if i < arg.len and arg[i] == '[':
- isBracket = true
- inc(i)
- while i < arg.len and (arg[i] notin {':', '=', ']'}):
- id.add(arg[i])
- inc(i)
- if isBracket:
- if i < arg.len and arg[i] == ']': inc(i)
- else: invalidCmdLineOption(conf, pass, orig, info)
-
- if i == arg.len: discard
- elif i < arg.len and (arg[i] in {':', '='}): inc(i)
- else: invalidCmdLineOption(conf, pass, orig, info)
-
- let isSomeHint = state in {wHint, wHintAsError}
- template findNote(noteMin, noteMax, name) =
- # unfortunately, hintUser and warningUser clash, otherwise implementation would simplify a bit
- let x = findStr(noteMin, noteMax, id, errUnknown)
- if x != errUnknown: notes = {TNoteKind(x)}
- else: localError(conf, info, "unknown $#: $#" % [name, id])
- case id.normalize
- of "all": # other note groups would be easy to support via additional cases
- notes = if isSomeHint: {hintMin..hintMax} else: {warnMin..warnMax}
- elif isSomeHint: findNote(hintMin, hintMax, "hint")
- else: findNote(warnMin, warnMax, "warning")
- var val = substr(arg, i).normalize
- if val == "": val = "on"
- if val notin ["on", "off"]:
- # xxx in future work we should also allow users to have control over `foreignPackageNotes`
- # so that they can enable `hints|warnings|warningAsErrors` for all the code they depend on.
- localError(conf, info, errOnOrOffExpectedButXFound % arg)
+ case whichKeyword(arg)
+ of wOn: gOptions = gOptions + op
+ of wOff: gOptions = gOptions - op
else:
- let isOn = val == "on"
- if isOn and id.normalize == "all":
- localError(conf, info, "only 'all:off' is supported")
- for n in notes:
- if n notin conf.cmdlineNotes or pass == passCmd1:
- if pass == passCmd1: incl(conf.cmdlineNotes, n)
- incl(conf.modifiedyNotes, n)
- if state in {wWarningAsError, wHintAsError}:
- conf.warningAsErrors[n] = isOn # xxx rename warningAsErrors to noteAsErrors
- else:
- conf.notes[n] = isOn
- conf.mainPackageNotes[n] = isOn
- if not isOn: excl(conf.foreignPackageNotes, n)
+ if arg == "list":
+ result = true
+ else:
+ localError(info, errOnOffOrListExpectedButXFound, arg)
-proc processCompile(conf: ConfigRef; filename: string) =
- var found = findFile(conf, filename)
- if found.isEmpty: found = AbsoluteFile filename
- extccomp.addExternalFileToCompile(conf, found)
+proc processOnOffSwitchG(op: TGlobalOptions, arg: string, pass: TCmdLinePass,
+ info: TLineInfo) =
+ case whichKeyword(arg)
+ of wOn: gGlobalOptions = gGlobalOptions + op
+ of wOff: gGlobalOptions = gGlobalOptions - op
+ else: localError(info, errOnOrOffExpectedButXFound, arg)
-const
- errNoneBoehmRefcExpectedButXFound = "'arc', 'orc', 'markAndSweep', 'boehm', 'go', 'none', 'regions', or 'refc' expected, but '$1' found"
- errNoneSpeedOrSizeExpectedButXFound = "'none', 'speed' or 'size' expected, but '$1' found"
- errGuiConsoleOrLibExpectedButXFound = "'gui', 'console' or 'lib' expected, but '$1' found"
- errInvalidExceptionSystem = "'goto', 'setjmp', 'cpp' or 'quirky' expected, but '$1' found"
+proc expectArg(switch, arg: string, pass: TCmdLinePass, info: TLineInfo) =
+ if arg == "": localError(info, errCmdLineArgExpected, addPrefix(switch))
-template warningOptionNoop(switch: string) =
- warningDeprecated(conf, info, "'$#' is deprecated, now a noop" % switch)
+proc expectNoArg(switch, arg: string, pass: TCmdLinePass, info: TLineInfo) =
+ if arg != "": localError(info, errCmdLineNoArgExpected, addPrefix(switch))
-template deprecatedAlias(oldName, newName: string) =
- warningDeprecated(conf, info, "'$#' is a deprecated alias for '$#'" % [oldName, newName])
+var
+ enableNotes: TNoteKinds
+ disableNotes: TNoteKinds
-proc testCompileOptionArg*(conf: ConfigRef; switch, arg: string, info: TLineInfo): bool =
+proc processSpecificNote(arg: string, state: TSpecialWord, pass: TCmdLinePass,
+ info: TLineInfo; orig: string) =
+ var id = "" # arg = "X]:on|off"
+ var i = 0
+ var n = hintMin
+ while i < len(arg) and (arg[i] != ']'):
+ add(id, arg[i])
+ inc(i)
+ if i < len(arg) and (arg[i] == ']'): inc(i)
+ else: invalidCmdLineOption(pass, orig, info)
+ if i < len(arg) and (arg[i] in {':', '='}): inc(i)
+ else: invalidCmdLineOption(pass, orig, info)
+ if state == wHint:
+ var x = findStr(msgs.HintsToStr, id)
+ if x >= 0: n = TNoteKind(x + ord(hintMin))
+ else: localError(info, "unknown hint: " & id)
+ else:
+ var x = findStr(msgs.WarningsToStr, id)
+ if x >= 0: n = TNoteKind(x + ord(warnMin))
+ else: localError(info, "unknown warning: " & id)
+ case whichKeyword(substr(arg, i))
+ of wOn:
+ incl(gNotes, n)
+ incl(enableNotes, n)
+ of wOff:
+ excl(gNotes, n)
+ incl(disableNotes, n)
+ excl(ForeignPackageNotes, n)
+ else: localError(info, errOnOrOffExpectedButXFound, arg)
+
+proc processCompile(filename: string) =
+ var found = findFile(filename)
+ if found == "": found = filename
+ var trunc = changeFileExt(found, "")
+ extccomp.addExternalFileToCompile(found)
+ extccomp.addFileToLink(completeCFilePath(trunc, false))
+
+proc testCompileOptionArg*(switch, arg: string, info: TLineInfo): bool =
case switch.normalize
- of "gc", "mm":
+ of "gc":
case arg.normalize
- of "boehm": result = conf.selectedGC == gcBoehm
- of "refc": result = conf.selectedGC == gcRefc
- of "markandsweep": result = conf.selectedGC == gcMarkAndSweep
- of "destructors", "arc": result = conf.selectedGC == gcArc
- of "orc": result = conf.selectedGC == gcOrc
- of "hooks": result = conf.selectedGC == gcHooks
- of "go": result = conf.selectedGC == gcGo
- of "none": result = conf.selectedGC == gcNone
- of "stack", "regions": result = conf.selectedGC == gcRegions
- else: localError(conf, info, errNoneBoehmRefcExpectedButXFound % arg)
+ of "boehm": result = gSelectedGC == gcBoehm
+ of "refc": result = gSelectedGC == gcRefc
+ of "v2": result = gSelectedGC == gcV2
+ of "markandsweep": result = gSelectedGC == gcMarkAndSweep
+ of "generational": result = gSelectedGC == gcGenerational
+ of "go": result = gSelectedGC == gcGo
+ of "none": result = gSelectedGC == gcNone
+ of "stack": result = gSelectedGC == gcStack
+ else: localError(info, errNoneBoehmRefcExpectedButXFound, arg)
of "opt":
case arg.normalize
- of "speed": result = contains(conf.options, optOptimizeSpeed)
- of "size": result = contains(conf.options, optOptimizeSize)
- of "none": result = conf.options * {optOptimizeSpeed, optOptimizeSize} == {}
- else: localError(conf, info, errNoneSpeedOrSizeExpectedButXFound % arg)
- of "verbosity": result = $conf.verbosity == arg
- of "app":
- case arg.normalize
- of "gui": result = contains(conf.globalOptions, optGenGuiApp)
- of "console": result = not contains(conf.globalOptions, optGenGuiApp)
- of "lib": result = contains(conf.globalOptions, optGenDynLib) and
- not contains(conf.globalOptions, optGenGuiApp)
- of "staticlib": result = contains(conf.globalOptions, optGenStaticLib) and
- not contains(conf.globalOptions, optGenGuiApp)
- else: localError(conf, info, errGuiConsoleOrLibExpectedButXFound % arg)
- of "dynliboverride":
- result = isDynlibOverride(conf, arg)
- of "exceptions":
- case arg.normalize
- of "cpp": result = conf.exc == excCpp
- of "setjmp": result = conf.exc == excSetjmp
- of "quirky": result = conf.exc == excQuirky
- of "goto": result = conf.exc == excGoto
- else: localError(conf, info, errInvalidExceptionSystem % arg)
- else: invalidCmdLineOption(conf, passCmd1, switch, info)
+ of "speed": result = contains(gOptions, optOptimizeSpeed)
+ of "size": result = contains(gOptions, optOptimizeSize)
+ of "none": result = gOptions * {optOptimizeSpeed, optOptimizeSize} == {}
+ else: localError(info, errNoneSpeedOrSizeExpectedButXFound, arg)
+ of "verbosity": result = $gVerbosity == arg
+ else: invalidCmdLineOption(passCmd1, switch, info)
-proc testCompileOption*(conf: ConfigRef; switch: string, info: TLineInfo): bool =
+proc testCompileOption*(switch: string, info: TLineInfo): bool =
case switch.normalize
- of "debuginfo": result = contains(conf.globalOptions, optCDebug)
- of "compileonly", "c": result = contains(conf.globalOptions, optCompileOnly)
- of "nolinking": result = contains(conf.globalOptions, optNoLinking)
- of "nomain": result = contains(conf.globalOptions, optNoMain)
- of "forcebuild", "f": result = contains(conf.globalOptions, optForceFullMake)
- of "warnings", "w": result = contains(conf.options, optWarns)
- of "hints": result = contains(conf.options, optHints)
- of "threadanalysis": result = contains(conf.globalOptions, optThreadAnalysis)
- of "stacktrace": result = contains(conf.options, optStackTrace)
- of "stacktracemsgs": result = contains(conf.options, optStackTraceMsgs)
- of "linetrace": result = contains(conf.options, optLineTrace)
- of "debugger": result = contains(conf.globalOptions, optCDebug)
- of "profiler": result = contains(conf.options, optProfiler)
- of "memtracker": result = contains(conf.options, optMemTracker)
- of "checks", "x": result = conf.options * ChecksOptions == ChecksOptions
+ of "debuginfo": result = contains(gGlobalOptions, optCDebug)
+ of "compileonly", "c": result = contains(gGlobalOptions, optCompileOnly)
+ of "nolinking": result = contains(gGlobalOptions, optNoLinking)
+ of "nomain": result = contains(gGlobalOptions, optNoMain)
+ of "forcebuild", "f": result = contains(gGlobalOptions, optForceFullMake)
+ of "warnings", "w": result = contains(gOptions, optWarns)
+ of "hints": result = contains(gOptions, optHints)
+ of "threadanalysis": result = contains(gGlobalOptions, optThreadAnalysis)
+ of "stacktrace": result = contains(gOptions, optStackTrace)
+ of "linetrace": result = contains(gOptions, optLineTrace)
+ of "debugger": result = contains(gOptions, optEndb)
+ of "profiler": result = contains(gOptions, optProfiler)
+ of "checks", "x": result = gOptions * ChecksOptions == ChecksOptions
of "floatchecks":
- result = conf.options * {optNaNCheck, optInfCheck} == {optNaNCheck, optInfCheck}
- of "infchecks": result = contains(conf.options, optInfCheck)
- of "nanchecks": result = contains(conf.options, optNaNCheck)
- of "objchecks": result = contains(conf.options, optObjCheck)
- of "fieldchecks": result = contains(conf.options, optFieldCheck)
- of "rangechecks": result = contains(conf.options, optRangeCheck)
- of "boundchecks": result = contains(conf.options, optBoundsCheck)
- of "refchecks":
- warningDeprecated(conf, info, "refchecks is deprecated!")
- result = contains(conf.options, optRefCheck)
- of "overflowchecks": result = contains(conf.options, optOverflowCheck)
- of "staticboundchecks": result = contains(conf.options, optStaticBoundsCheck)
- of "stylechecks": result = contains(conf.options, optStyleCheck)
- of "linedir": result = contains(conf.options, optLineDir)
- of "assertions", "a": result = contains(conf.options, optAssert)
- of "run", "r": result = contains(conf.globalOptions, optRun)
- of "symbolfiles": result = conf.symbolFiles != disabledSf
- of "genscript": result = contains(conf.globalOptions, optGenScript)
- of "gencdeps": result = contains(conf.globalOptions, optGenCDeps)
- of "threads": result = contains(conf.globalOptions, optThreads)
- of "tlsemulation": result = contains(conf.globalOptions, optTlsEmulation)
- of "implicitstatic": result = contains(conf.options, optImplicitStatic)
- of "patterns", "trmacros":
- if switch.normalize == "patterns": deprecatedAlias(switch, "trmacros")
- result = contains(conf.options, optTrMacros)
- of "excessivestacktrace": result = contains(conf.globalOptions, optExcessiveStackTrace)
- of "nilseqs", "nilchecks", "taintmode": warningOptionNoop(switch)
- of "panics": result = contains(conf.globalOptions, optPanics)
- else: invalidCmdLineOption(conf, passCmd1, switch, info)
+ result = gOptions * {optNaNCheck, optInfCheck} == {optNaNCheck, optInfCheck}
+ of "infchecks": result = contains(gOptions, optInfCheck)
+ of "nanchecks": result = contains(gOptions, optNaNCheck)
+ of "objchecks": result = contains(gOptions, optObjCheck)
+ of "fieldchecks": result = contains(gOptions, optFieldCheck)
+ of "rangechecks": result = contains(gOptions, optRangeCheck)
+ of "boundchecks": result = contains(gOptions, optBoundsCheck)
+ of "overflowchecks": result = contains(gOptions, optOverflowCheck)
+ of "linedir": result = contains(gOptions, optLineDir)
+ of "assertions", "a": result = contains(gOptions, optAssert)
+ of "deadcodeelim": result = contains(gGlobalOptions, optDeadCodeElim)
+ of "run", "r": result = contains(gGlobalOptions, optRun)
+ of "symbolfiles": result = contains(gGlobalOptions, optSymbolFiles)
+ of "genscript": result = contains(gGlobalOptions, optGenScript)
+ of "threads": result = contains(gGlobalOptions, optThreads)
+ of "taintmode": result = contains(gGlobalOptions, optTaintMode)
+ of "tlsemulation": result = contains(gGlobalOptions, optTlsEmulation)
+ of "implicitstatic": result = contains(gOptions, optImplicitStatic)
+ of "patterns": result = contains(gOptions, optPatterns)
+ of "experimental": result = gExperimentalMode
+ else: invalidCmdLineOption(passCmd1, switch, info)
-proc processPath(conf: ConfigRef; path: string, info: TLineInfo,
- notRelativeToProj = false): AbsoluteDir =
- let p = if os.isAbsolute(path) or '$' in path:
- path
- elif notRelativeToProj:
- getCurrentDir() / path
- else:
- conf.projectPath.string / path
- try:
- result = AbsoluteDir pathSubs(conf, p, toFullPath(conf, info).splitFile().dir)
- except ValueError:
- localError(conf, info, "invalid path: " & p)
- result = AbsoluteDir p
-
-proc processCfgPath(conf: ConfigRef; path: string, info: TLineInfo): AbsoluteDir =
- let path = if path.len > 0 and path[0] == '"': strutils.unescape(path)
- else: path
- let basedir = toFullPath(conf, info).splitFile().dir
- let p = if os.isAbsolute(path) or '$' in path:
+proc processPath(path: string, info: TLineInfo,
+ notRelativeToProj = false): string =
+ let p = if notRelativeToProj or os.isAbsolute(path) or
+ '$' in path or path[0] == '.':
path
else:
- basedir / path
+ options.gProjectPath / path
try:
- result = AbsoluteDir pathSubs(conf, p, basedir)
+ result = pathSubs(p, info.toFullPath().splitFile().dir)
except ValueError:
- localError(conf, info, "invalid path: " & p)
- result = AbsoluteDir p
+ localError(info, "invalid path: " & p)
+ result = p
-const
- errInvalidNumber = "$1 is not a valid number"
-
-proc makeAbsolute(s: string): AbsoluteFile =
- if isAbsolute(s):
- AbsoluteFile pathnorm.normalizePath(s)
- else:
- AbsoluteFile pathnorm.normalizePath(os.getCurrentDir() / s)
-
-proc setTrackingInfo(conf: ConfigRef; dirty, file, line, column: string,
- info: TLineInfo) =
- ## set tracking info, common code for track, trackDirty, & ideTrack
- var ln, col: int
- if parseUtils.parseInt(line, ln) <= 0:
- localError(conf, info, errInvalidNumber % line)
- if parseUtils.parseInt(column, col) <= 0:
- localError(conf, info, errInvalidNumber % column)
-
- let a = makeAbsolute(file)
- if dirty == "":
- conf.m.trackPos = newLineInfo(conf, a, ln, col)
- else:
- let dirtyOriginalIdx = fileInfoIdx(conf, a)
- if dirtyOriginalIdx.int32 >= 0:
- msgs.setDirtyFile(conf, dirtyOriginalIdx, makeAbsolute(dirty))
- conf.m.trackPos = newLineInfo(dirtyOriginalIdx, ln, col)
-
-proc trackDirty(conf: ConfigRef; arg: string, info: TLineInfo) =
+proc trackDirty(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)
+ if a.len != 4: localError(info, errTokenExpected,
+ "DIRTY_BUFFER,ORIGINAL_FILE,LINE,COLUMN")
+ var line, column: int
+ if parseUtils.parseInt(a[2], line) <= 0:
+ localError(info, errInvalidNumber, a[1])
+ if parseUtils.parseInt(a[3], column) <= 0:
+ localError(info, errInvalidNumber, a[2])
-proc track(conf: ConfigRef; arg: string, info: TLineInfo) =
+ let dirtyOriginalIdx = a[1].fileInfoIdx
+ if dirtyOriginalIdx >= 0:
+ msgs.setDirtyFile(dirtyOriginalIdx, a[0])
+
+ gTrackPos = newLineInfo(dirtyOriginalIdx, line, column)
+
+proc track(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)
+ if a.len != 3: localError(info, errTokenExpected, "FILE,LINE,COLUMN")
+ var line, column: int
+ if parseUtils.parseInt(a[1], line) <= 0:
+ localError(info, errInvalidNumber, a[1])
+ if parseUtils.parseInt(a[2], column) <= 0:
+ localError(info, errInvalidNumber, a[2])
+ gTrackPos = newLineInfo(a[0], line, column)
-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
-
-proc dynlibOverride(conf: ConfigRef; switch, arg: string, pass: TCmdLinePass, info: TLineInfo) =
+proc dynlibOverride(switch, arg: string, pass: TCmdLinePass, info: TLineInfo) =
if pass in {passCmd2, passPP}:
- expectArg(conf, switch, arg, pass, info)
- options.inclDynlibOverride(conf, arg)
+ expectArg(switch, arg, pass, info)
+ options.inclDynlibOverride(arg)
-template handleStdinOrCmdInput =
- conf.projectFull = conf.projectName.AbsoluteFile
- conf.projectPath = AbsoluteDir getCurrentDir()
- if conf.outDir.isEmpty:
- conf.outDir = getNimcacheDir(conf)
-
-proc handleStdinInput*(conf: ConfigRef) =
- conf.projectName = "stdinfile"
- conf.projectIsStdin = true
- handleStdinOrCmdInput()
-
-proc handleCmdInput*(conf: ConfigRef) =
- conf.projectName = "cmdfile"
- handleStdinOrCmdInput()
-
-proc parseCommand*(command: string): Command =
- case command.normalize
- of "c", "cc", "compile", "compiletoc": cmdCompileToC
- of "cpp", "compiletocpp": cmdCompileToCpp
- of "objc", "compiletooc": cmdCompileToOC
- of "js", "compiletojs": cmdCompileToJS
- of "r": cmdCrun
- of "run": cmdTcc
- of "check": cmdCheck
- of "e": cmdNimscript
- of "doc0": cmdDoc0
- of "doc2", "doc": cmdDoc
- of "doc2tex": cmdDoc2tex
- of "rst2html": cmdRst2html
- of "md2tex": cmdMd2tex
- of "md2html": cmdMd2html
- of "rst2tex": cmdRst2tex
- of "jsondoc0": cmdJsondoc0
- of "jsondoc2", "jsondoc": cmdJsondoc
- of "ctags": cmdCtags
- of "buildindex": cmdBuildindex
- of "gendepend": cmdGendepend
- of "dump": cmdDump
- of "parse": cmdParse
- of "rod": cmdRod
- of "secret": cmdInteractive
- of "nop", "help": cmdNop
- of "jsonscript": cmdJsonscript
- else: cmdUnknown
-
-proc setCmd*(conf: ConfigRef, cmd: Command) =
- ## sets cmd, backend so subsequent flags can query it (e.g. so --gc:arc can be ignored for backendJs)
- # Note that `--backend` can override the backend, so the logic here must remain reversible.
- conf.cmd = cmd
- case cmd
- of cmdCompileToC, cmdCrun, cmdTcc: conf.backend = backendC
- of cmdCompileToCpp: conf.backend = backendCpp
- of cmdCompileToOC: conf.backend = backendObjc
- of cmdCompileToJS: conf.backend = backendJs
- else: discard
-
-proc setCommandEarly*(conf: ConfigRef, command: string) =
- conf.command = command
- setCmd(conf, command.parseCommand)
- # 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.
- 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) =
+proc processSwitch(switch, arg: string, pass: TCmdLinePass, info: TLineInfo) =
var
+ theOS: TSystemOS
+ cpu: TSystemCPU
key, val: string
case switch.normalize
- of "eval":
- expectArg(conf, switch, arg, pass, info)
- conf.projectIsCmd = true
- conf.cmdInput = arg # can be empty (a nim file with empty content is valid too)
- if conf.cmd == cmdNone:
- conf.command = "e"
- conf.setCmd cmdNimscript # better than `cmdCrun` as a default
- conf.implicitCmd = true
of "path", "p":
- expectArg(conf, switch, arg, pass, info)
- for path in nimbleSubs(conf, arg):
- addPath(conf, if pass == passPP: processCfgPath(conf, path, info)
- else: processPath(conf, path, info), info)
+ expectArg(switch, arg, pass, info)
+ addPath(processPath(arg, info), info)
of "nimblepath", "babelpath":
- if switch.normalize == "babelpath": deprecatedAlias(switch, "nimblepath")
- if pass in {passCmd2, passPP} and optNoNimblePath notin conf.globalOptions:
- expectArg(conf, switch, arg, pass, info)
- var path = processPath(conf, arg, info, notRelativeToProj=true)
- let nimbleDir = AbsoluteDir getEnv("NIMBLE_DIR")
- if not nimbleDir.isEmpty and pass == passPP:
- path = nimbleDir / RelativeDir"pkgs"
- nimblePath(conf, path, info)
+ # keep the old name for compat
+ if pass in {passCmd2, passPP} and not options.gNoNimblePath:
+ expectArg(switch, arg, pass, info)
+ let path = processPath(arg, info, notRelativeToProj=true)
+ nimblePath(path, info)
of "nonimblepath", "nobabelpath":
- if switch.normalize == "nobabelpath": deprecatedAlias(switch, "nonimblepath")
- expectNoArg(conf, switch, arg, pass, info)
- disableNimblePath(conf)
- of "clearnimblepath":
- expectNoArg(conf, switch, arg, pass, info)
- clearNimblePath(conf)
+ expectNoArg(switch, arg, pass, info)
+ options.gNoNimblePath = true
+ options.lazyPaths.head = nil
+ options.lazyPaths.tail = nil
+ options.lazyPaths.counter = 0
of "excludepath":
- expectArg(conf, switch, arg, pass, info)
- let path = processPath(conf, arg, info)
- conf.searchPaths.keepItIf(it != path)
- conf.lazyPaths.keepItIf(it != path)
+ expectArg(switch, arg, pass, info)
+ let path = processPath(arg, info)
+ lists.excludePath(options.searchPaths, path)
+ lists.excludePath(options.lazyPaths, path)
+ if (len(path) > 0) and (path[len(path) - 1] == DirSep):
+ let strippedPath = path[0 .. (len(path) - 2)]
+ lists.excludePath(options.searchPaths, strippedPath)
+ lists.excludePath(options.lazyPaths, strippedPath)
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)
+ expectArg(switch, arg, pass, info)
+ options.nimcacheDir = processPath(arg, info, true)
of "out", "o":
- expectArg(conf, switch, arg, pass, info)
- let f = splitFile(processPath(conf, arg, info, notRelativeToProj=true).string)
- conf.outFile = RelativeFile f.name & f.ext
- conf.outDir = toAbsoluteDir f.dir
- of "outdir":
- expectArg(conf, switch, arg, pass, info)
- conf.outDir = processPath(conf, arg, info, notRelativeToProj=true)
- of "usenimcache":
- processOnOffSwitchG(conf, {optUseNimcache}, arg, pass, info)
+ expectArg(switch, arg, pass, info)
+ options.outFile = arg
of "docseesrcurl":
- expectArg(conf, switch, arg, pass, info)
- conf.docSeeSrcUrl = arg
- of "docroot":
- conf.docRoot = if arg.len == 0: docRootDefault else: arg
- 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
+ expectArg(switch, arg, pass, info)
+ options.docSeeSrcUrl = 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)
- defineSymbol(conf.symbols, key, val)
- else:
- specialDefine(conf, arg, pass)
- defineSymbol(conf.symbols, arg)
+ expectArg(switch, arg, pass, info)
+ defineSymbol(arg)
of "undef", "u":
- expectArg(conf, switch, arg, pass, info)
- undefSymbol(conf.symbols, arg)
+ expectArg(switch, arg, pass, info)
+ undefSymbol(arg)
+ of "symbol":
+ expectArg(switch, arg, pass, info)
+ # deprecated, do nothing
of "compile":
- expectArg(conf, switch, arg, pass, info)
- if pass in {passCmd2, passPP}: processCompile(conf, arg)
+ expectArg(switch, arg, pass, info)
+ if pass in {passCmd2, passPP}: processCompile(arg)
of "link":
- expectArg(conf, switch, arg, pass, info)
- if pass in {passCmd2, passPP}:
- addExternalFileToLink(conf, AbsoluteFile arg)
+ expectArg(switch, arg, pass, info)
+ if pass in {passCmd2, passPP}: addFileToLink(arg)
of "debuginfo":
- processOnOffSwitchG(conf, {optCDebug}, arg, pass, info)
+ expectNoArg(switch, arg, pass, info)
+ incl(gGlobalOptions, optCDebug)
of "embedsrc":
- processOnOffSwitchG(conf, {optEmbedOrigSrc}, arg, pass, info)
+ expectNoArg(switch, arg, pass, info)
+ incl(gGlobalOptions, optEmbedOrigSrc)
of "compileonly", "c":
- processOnOffSwitchG(conf, {optCompileOnly}, arg, pass, info)
+ expectNoArg(switch, arg, pass, info)
+ incl(gGlobalOptions, optCompileOnly)
of "nolinking":
- processOnOffSwitchG(conf, {optNoLinking}, arg, pass, info)
+ expectNoArg(switch, arg, pass, info)
+ incl(gGlobalOptions, optNoLinking)
of "nomain":
- processOnOffSwitchG(conf, {optNoMain}, arg, pass, info)
+ expectNoArg(switch, arg, pass, info)
+ incl(gGlobalOptions, optNoMain)
of "forcebuild", "f":
- processOnOffSwitchG(conf, {optForceFullMake}, arg, pass, info)
+ expectNoArg(switch, arg, pass, info)
+ incl(gGlobalOptions, optForceFullMake)
of "project":
- processOnOffSwitchG(conf, {optWholeProject, optGenIndex}, arg, pass, info)
+ expectNoArg(switch, arg, pass, info)
+ gWholeProject = true
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)
+ expectArg(switch, arg, pass, info)
+ case arg.normalize
+ of "boehm":
+ gSelectedGC = gcBoehm
+ defineSymbol("boehmgc")
+ of "refc":
+ gSelectedGC = gcRefc
+ of "v2":
+ gSelectedGC = gcV2
+ of "markandsweep":
+ gSelectedGC = gcMarkAndSweep
+ defineSymbol("gcmarkandsweep")
+ of "generational":
+ gSelectedGC = gcGenerational
+ defineSymbol("gcgenerational")
+ of "go":
+ gSelectedGC = gcGo
+ defineSymbol("gogc")
+ of "none":
+ gSelectedGC = gcNone
+ defineSymbol("nogc")
+ of "stack":
+ gSelectedGC= gcStack
+ defineSymbol("gcstack")
+ else: localError(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)
+ if processOnOffSwitchOrList({optWarns}, arg, pass, info): listWarnings()
+ of "warning": processSpecificNote(arg, wWarning, pass, info, switch)
+ of "hint": processSpecificNote(arg, wHint, pass, info, switch)
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 "stacktrace": processOnOffSwitch(conf, {optStackTrace}, arg, pass, info)
- of "stacktracemsgs": processOnOffSwitch(conf, {optStackTraceMsgs}, arg, pass, info)
- of "excessivestacktrace": processOnOffSwitchG(conf, {optExcessiveStackTrace}, arg, pass, info)
- of "linetrace": processOnOffSwitch(conf, {optLineTrace}, arg, pass, info)
+ if processOnOffSwitchOrList({optHints}, arg, pass, info): listHints()
+ of "threadanalysis": processOnOffSwitchG({optThreadAnalysis}, arg, pass, info)
+ of "stacktrace": processOnOffSwitch({optStackTrace}, arg, pass, info)
+ of "excessivestacktrace": processOnOffSwitchG({optExcessiveStackTrace}, arg, pass, info)
+ of "linetrace": processOnOffSwitch({optLineTrace}, arg, pass, info)
of "debugger":
case arg.normalize
- of "on", "native", "gdb":
- conf.globalOptions.incl optCDebug
- conf.options.incl optLineDir
- #defineSymbol(conf.symbols, "nimTypeNames") # type names are used in gdb pretty printing
+ of "on", "endb":
+ gOptions.incl optEndb
+ defineSymbol("endb")
of "off":
- conf.globalOptions.excl optCDebug
+ gOptions.excl optEndb
+ undefSymbol("endb")
+ of "native", "gdb":
+ incl(gGlobalOptions, optCDebug)
+ gOptions = gOptions + {optLineDir} - {optEndb}
+ undefSymbol("endb")
else:
- localError(conf, info, "expected native|gdb|on|off but found " & arg)
- of "g": # alias for --debugger:native
- conf.globalOptions.incl optCDebug
- conf.options.incl optLineDir
- #defineSymbol(conf.symbols, "nimTypeNames") # type names are used in gdb pretty printing
+ localError(info, "expected endb|gdb but found " & arg)
of "profiler":
- processOnOffSwitch(conf, {optProfiler}, arg, pass, info)
- if optProfiler in conf.options: defineSymbol(conf.symbols, "profiler")
- else: undefSymbol(conf.symbols, "profiler")
- of "memtracker":
- processOnOffSwitch(conf, {optMemTracker}, arg, pass, info)
- if optMemTracker in conf.options: defineSymbol(conf.symbols, "memtracker")
- else: undefSymbol(conf.symbols, "memtracker")
- of "hotcodereloading":
- processOnOffSwitchG(conf, {optHotCodeReloading}, arg, pass, info)
- if conf.hcrOn:
- defineSymbol(conf.symbols, "hotcodereloading")
- defineSymbol(conf.symbols, "useNimRtl")
- # hardcoded linking with dynamic runtime for MSVC for smaller binaries
- # should do the same for all compilers (wherever applicable)
- if isVSCompatible(conf):
- extccomp.addCompileOptionCmd(conf, "/MD")
- else:
- undefSymbol(conf.symbols, "hotcodereloading")
- undefSymbol(conf.symbols, "useNimRtl")
- of "checks", "x": processOnOffSwitch(conf, ChecksOptions, arg, pass, info)
+ processOnOffSwitch({optProfiler}, arg, pass, info)
+ if optProfiler in gOptions: defineSymbol("profiler")
+ else: undefSymbol("profiler")
+ of "checks", "x": processOnOffSwitch(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 "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 "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)
+ processOnOffSwitch({optNaNCheck, optInfCheck}, arg, pass, info)
+ of "infchecks": processOnOffSwitch({optInfCheck}, arg, pass, info)
+ of "nanchecks": processOnOffSwitch({optNaNCheck}, arg, pass, info)
+ of "objchecks": processOnOffSwitch({optObjCheck}, arg, pass, info)
+ of "fieldchecks": processOnOffSwitch({optFieldCheck}, arg, pass, info)
+ of "rangechecks": processOnOffSwitch({optRangeCheck}, arg, pass, info)
+ of "boundchecks": processOnOffSwitch({optBoundsCheck}, arg, pass, info)
+ of "overflowchecks": processOnOffSwitch({optOverflowCheck}, arg, pass, info)
+ of "linedir": processOnOffSwitch({optLineDir}, arg, pass, info)
+ of "assertions", "a": processOnOffSwitch({optAssert}, arg, pass, info)
+ of "deadcodeelim": processOnOffSwitchG({optDeadCodeElim}, arg, pass, info)
+ of "reportconceptfailures":
+ processOnOffSwitchG({optReportConceptFailures}, arg, pass, info)
of "threads":
- if conf.backend == backendJs or conf.cmd == cmdNimscript: discard
- else: processOnOffSwitchG(conf, {optThreads}, arg, pass, info)
- #if optThreads in conf.globalOptions: conf.setNote(warnGcUnsafe)
- of "tlsemulation": processOnOffSwitchG(conf, {optTlsEmulation}, arg, pass, info)
+ processOnOffSwitchG({optThreads}, arg, pass, info)
+ #if optThreads in gGlobalOptions: incl(gNotes, warnGcUnsafe)
+ of "tlsemulation": processOnOffSwitchG({optTlsEmulation}, arg, pass, info)
+ of "taintmode": processOnOffSwitchG({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)
+ processOnOffSwitch({optImplicitStatic}, arg, pass, info)
+ of "patterns":
+ processOnOffSwitch({optPatterns}, arg, pass, info)
of "opt":
- expectArg(conf, switch, arg, pass, info)
+ expectArg(switch, arg, pass, info)
case arg.normalize
of "speed":
- incl(conf.options, optOptimizeSpeed)
- excl(conf.options, optOptimizeSize)
+ incl(gOptions, optOptimizeSpeed)
+ excl(gOptions, optOptimizeSize)
of "size":
- excl(conf.options, optOptimizeSpeed)
- incl(conf.options, optOptimizeSize)
+ excl(gOptions, optOptimizeSpeed)
+ incl(gOptions, optOptimizeSize)
of "none":
- excl(conf.options, optOptimizeSpeed)
- excl(conf.options, optOptimizeSize)
- else: localError(conf, info, errNoneSpeedOrSizeExpectedButXFound % arg)
+ excl(gOptions, optOptimizeSpeed)
+ excl(gOptions, optOptimizeSize)
+ else: localError(info, errNoneSpeedOrSizeExpectedButXFound, arg)
of "app":
- expectArg(conf, switch, arg, pass, info)
+ expectArg(switch, arg, pass, info)
case arg.normalize
of "gui":
- incl(conf.globalOptions, optGenGuiApp)
- defineSymbol(conf.symbols, "executable")
- defineSymbol(conf.symbols, "guiapp")
+ incl(gGlobalOptions, optGenGuiApp)
+ defineSymbol("executable")
+ defineSymbol("guiapp")
of "console":
- excl(conf.globalOptions, optGenGuiApp)
- defineSymbol(conf.symbols, "executable")
- defineSymbol(conf.symbols, "consoleapp")
+ excl(gGlobalOptions, optGenGuiApp)
+ defineSymbol("executable")
+ defineSymbol("consoleapp")
of "lib":
- incl(conf.globalOptions, optGenDynLib)
- excl(conf.globalOptions, optGenGuiApp)
- defineSymbol(conf.symbols, "library")
- defineSymbol(conf.symbols, "dll")
+ incl(gGlobalOptions, optGenDynLib)
+ excl(gGlobalOptions, optGenGuiApp)
+ defineSymbol("library")
+ defineSymbol("dll")
of "staticlib":
- incl(conf.globalOptions, optGenStaticLib)
- excl(conf.globalOptions, optGenGuiApp)
- defineSymbol(conf.symbols, "library")
- defineSymbol(conf.symbols, "staticlib")
- else: localError(conf, info, errGuiConsoleOrLibExpectedButXFound % arg)
+ incl(gGlobalOptions, optGenStaticLib)
+ excl(gGlobalOptions, optGenGuiApp)
+ defineSymbol("library")
+ defineSymbol("staticlib")
+ else: localError(info, errGuiConsoleOrLibExpectedButXFound, arg)
of "passc", "t":
- expectArg(conf, switch, arg, pass, info)
- if pass in {passCmd2, passPP}: extccomp.addCompileOptionCmd(conf, arg)
+ expectArg(switch, arg, pass, info)
+ if pass in {passCmd2, passPP}: extccomp.addCompileOption(arg)
of "passl", "l":
- expectArg(conf, switch, arg, pass, info)
- if pass in {passCmd2, passPP}: extccomp.addLinkOptionCmd(conf, arg)
+ expectArg(switch, arg, pass, info)
+ if pass in {passCmd2, passPP}: extccomp.addLinkOption(arg)
of "cincludes":
- expectArg(conf, switch, arg, pass, info)
- if pass in {passCmd2, passPP}: conf.cIncludes.add processPath(conf, arg, info)
+ expectArg(switch, arg, pass, info)
+ if pass in {passCmd2, passPP}: cIncludes.add arg.processPath(info)
of "clibdir":
- expectArg(conf, switch, arg, pass, info)
- if pass in {passCmd2, passPP}: conf.cLibs.add processPath(conf, arg, info)
+ expectArg(switch, arg, pass, info)
+ if pass in {passCmd2, passPP}: cLibs.add arg.processPath(info)
of "clib":
- expectArg(conf, switch, arg, pass, info)
- if pass in {passCmd2, passPP}:
- conf.cLinkedLibs.add arg
+ expectArg(switch, arg, pass, info)
+ if pass in {passCmd2, passPP}: cLinkedLibs.add arg.processPath(info)
of "header":
- if conf != nil: conf.headerFile = arg
- incl(conf.globalOptions, optGenIndex)
+ headerFile = arg
+ incl(gGlobalOptions, 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({optGenIndex}, arg, pass, info)
of "import":
- expectArg(conf, switch, arg, pass, info)
- if pass in {passCmd2, passPP}:
- conf.implicitImports.add findModule(conf, arg, toFullPath(conf, info)).string
+ expectArg(switch, arg, pass, info)
+ if pass in {passCmd2, passPP}: implicitImports.add arg
of "include":
- expectArg(conf, switch, arg, pass, info)
- if pass in {passCmd2, passPP}:
- conf.implicitIncludes.add findModule(conf, arg, toFullPath(conf, info)).string
+ expectArg(switch, arg, pass, info)
+ if pass in {passCmd2, passPP}: implicitIncludes.add arg
of "listcmd":
- processOnOffSwitchG(conf, {optListCmd}, arg, pass, info)
- of "asm":
- processOnOffSwitchG(conf, {optProduceAsm}, arg, pass, info)
+ expectNoArg(switch, arg, pass, info)
+ incl(gGlobalOptions, optListCmd)
of "genmapping":
- processOnOffSwitchG(conf, {optGenMapping}, arg, pass, info)
+ expectNoArg(switch, arg, pass, info)
+ incl(gGlobalOptions, optGenMapping)
of "os":
- expectArg(conf, switch, arg, pass, info)
- let theOS = platform.nameToOS(arg)
- if theOS == osNone:
- let osList = platform.listOSnames().join(", ")
- localError(conf, info, "unknown OS: '$1'. Available options are: $2" % [arg, $osList])
- else:
- setTarget(conf.target, theOS, conf.target.targetCPU)
+ expectArg(switch, arg, pass, info)
+ if pass in {passCmd1, passPP}:
+ theOS = platform.nameToOS(arg)
+ if theOS == osNone: localError(info, errUnknownOS, arg)
+ elif theOS != platform.hostOS:
+ setTarget(theOS, targetCPU)
of "cpu":
- expectArg(conf, switch, arg, pass, info)
- let cpu = platform.nameToCPU(arg)
- if cpu == cpuNone:
- let cpuList = platform.listCPUnames().join(", ")
- localError(conf, info, "unknown CPU: '$1'. Available options are: $2" % [ arg, cpuList])
- else:
- setTarget(conf.target, conf.target.targetOS, cpu)
+ expectArg(switch, arg, pass, info)
+ if pass in {passCmd1, passPP}:
+ cpu = platform.nameToCPU(arg)
+ if cpu == cpuNone: localError(info, errUnknownCPU, arg)
+ elif cpu != platform.hostCPU:
+ setTarget(targetOS, cpu)
of "run", "r":
- processOnOffSwitchG(conf, {optRun}, arg, pass, info)
- of "maxloopiterationsvm":
- expectArg(conf, switch, arg, pass, info)
- conf.maxLoopIterationsVM = parseInt(arg)
- of "errormax":
- expectArg(conf, switch, arg, pass, info)
- # Note: `nim check` (etc) can overwrite this.
- # `0` is meaningless, give it a useful meaning as in clang's -ferror-limit
- # If user doesn't set this flag and the code doesn't either, it'd
- # have the same effect as errorMax = 1
- let ret = parseInt(arg)
- conf.errorMax = if ret == 0: high(int) else: ret
+ expectNoArg(switch, arg, pass, info)
+ incl(gGlobalOptions, optRun)
of "verbosity":
- expectArg(conf, switch, arg, pass, info)
- let verbosity = parseInt(arg)
- if verbosity notin {0..3}:
- localError(conf, info, "invalid verbosity level: '$1'" % arg)
- conf.verbosity = verbosity
- var verb = NotesVerbosity[conf.verbosity]
- ## We override the default `verb` by explicitly modified (set/unset) notes.
- conf.notes = (conf.modifiedyNotes * conf.notes + verb) -
- (conf.modifiedyNotes * verb - conf.notes)
- conf.mainPackageNotes = conf.notes
+ expectArg(switch, arg, pass, info)
+ gVerbosity = parseInt(arg)
+ gNotes = NotesVerbosity[gVerbosity]
+ incl(gNotes, enableNotes)
+ excl(gNotes, disableNotes)
of "parallelbuild":
- expectArg(conf, switch, arg, pass, info)
- conf.numberOfProcessors = parseInt(arg)
+ expectArg(switch, arg, pass, info)
+ gNumberOfProcessors = parseInt(arg)
of "version", "v":
- expectNoArg(conf, switch, arg, pass, info)
- writeVersionInfo(conf, pass)
+ expectNoArg(switch, arg, pass, info)
+ writeVersionInfo(pass)
of "advanced":
- expectNoArg(conf, switch, arg, pass, info)
- writeAdvancedUsage(conf, pass)
- of "fullhelp":
- expectNoArg(conf, switch, arg, pass, info)
- writeFullhelp(conf, pass)
+ expectNoArg(switch, arg, pass, info)
+ writeAdvancedUsage(pass)
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)
+ expectNoArg(switch, arg, pass, info)
+ helpOnError(pass)
+ of "symbolfiles":
+ processOnOffSwitchG({optSymbolFiles}, arg, pass, info)
of "skipcfg":
- processOnOffSwitchG(conf, {optSkipSystemConfigFile}, arg, pass, info)
+ expectNoArg(switch, arg, pass, info)
+ incl(gGlobalOptions, optSkipConfigFile)
of "skipprojcfg":
- processOnOffSwitchG(conf, {optSkipProjConfigFile}, arg, pass, info)
+ expectNoArg(switch, arg, pass, info)
+ incl(gGlobalOptions, optSkipProjConfigFile)
of "skipusercfg":
- processOnOffSwitchG(conf, {optSkipUserConfigFile}, arg, pass, info)
+ expectNoArg(switch, arg, pass, info)
+ incl(gGlobalOptions, optSkipUserConfigFile)
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)
+ expectNoArg(switch, arg, pass, info)
+ incl(gGlobalOptions, optSkipParentConfigFiles)
+ of "genscript":
+ expectNoArg(switch, arg, pass, info)
+ incl(gGlobalOptions, optGenScript)
+ of "colors": processOnOffSwitchG({optUseColors}, arg, pass, info)
of "lib":
- expectArg(conf, switch, arg, pass, info)
- conf.libpath = processPath(conf, arg, info, notRelativeToProj=true)
+ expectArg(switch, arg, pass, info)
+ libpath = processPath(arg, info, notRelativeToProj=true)
of "putenv":
- expectArg(conf, switch, arg, pass, info)
- splitSwitch(conf, arg, key, val, pass, info)
+ expectArg(switch, arg, pass, info)
+ splitSwitch(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(switch, arg, pass, info)
+ setCC(arg)
of "track":
- expectArg(conf, switch, arg, pass, info)
- track(conf, arg, info)
+ expectArg(switch, arg, pass, info)
+ track(arg, info)
of "trackdirty":
- expectArg(conf, switch, arg, pass, info)
- trackDirty(conf, arg, info)
+ expectArg(switch, arg, pass, info)
+ trackDirty(arg, info)
of "suggest":
- expectNoArg(conf, switch, arg, pass, info)
- conf.ideCmd = ideSug
+ expectNoArg(switch, arg, pass, info)
+ gIdeCmd = ideSug
of "def":
- expectArg(conf, switch, arg, pass, info)
- trackIde(conf, ideDef, arg, info)
+ expectNoArg(switch, arg, pass, info)
+ gIdeCmd = ideDef
+ of "eval":
+ expectArg(switch, arg, pass, info)
+ gEvalExpr = arg
of "context":
- expectNoArg(conf, switch, arg, pass, info)
- conf.ideCmd = ideCon
+ expectNoArg(switch, arg, pass, info)
+ gIdeCmd = 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(switch, arg, pass, info)
+ gIdeCmd = 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: ""
+ expectNoArg(switch, arg, pass, info)
+ incl(gGlobalOptions, optStdout)
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)
- of "declaredlocs":
- processOnOffSwitchG(conf, {optDeclaredLocs}, arg, pass, info)
+ expectNoArg(switch, arg, pass, info)
+ gListFullPaths = true
of "dynliboverride":
- dynlibOverride(conf, switch, arg, pass, info)
- of "dynliboverrideall":
- processOnOffSwitchG(conf, {optDynlibOverrideAll}, arg, pass, info)
- of "experimental":
- if arg.len == 0:
- conf.features.incl oldExperimentalFeatures
- else:
- try:
- conf.features.incl parseEnum[Feature](arg)
- except ValueError:
- localError(conf, info, "unknown experimental feature")
- of "legacy":
- try:
- conf.legacyFeatures.incl parseEnum[LegacyFeature](arg)
- except ValueError:
- localError(conf, info, "unknown obsolete feature")
- of "nocppexceptions":
- 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
- of "setjmp": conf.exc = excSetjmp
- of "quirky": conf.exc = excQuirky
- of "goto": conf.exc = excGoto
- else: localError(conf, info, errInvalidExceptionSystem % arg)
- of "cppdefine":
- expectArg(conf, switch, arg, pass, info)
- 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)
- incl(conf.features, destructor)
- incl(conf.globalOptions, optTinyRtti)
- incl(conf.globalOptions, optOwnedRefs)
- incl(conf.globalOptions, optSeqDestructors)
- defineSymbol(conf.symbols, "nimV2")
- conf.selectedGC = gcHooks
- defineSymbol(conf.symbols, "gchooks")
- defineSymbol(conf.symbols, "nimSeqsV2")
- defineSymbol(conf.symbols, "nimOwnedEnabled")
- of "seqsv2":
- processOnOffSwitchG(conf, {optSeqDestructors}, arg, pass, info)
- if pass in {passCmd2, passPP}:
- defineSymbol(conf.symbols, "nimSeqsV2")
- of "stylecheck":
- case arg.normalize
- 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)
- of "cppcompiletonamespace":
- if arg.len > 0:
- conf.cppCustomNamespace = arg
- else:
- conf.cppCustomNamespace = "Nim"
- defineSymbol(conf.symbols, "cppCompileToNamespace", conf.cppCustomNamespace)
- of "docinternal":
- processOnOffSwitchG(conf, {optDocInternal}, arg, pass, info)
- of "multimethods":
- processOnOffSwitchG(conf, {optMultiMethods}, arg, pass, info)
- of "expandmacro":
- expectArg(conf, switch, arg, pass, info)
- conf.macrosToExpand[arg] = "T"
- of "expandarc":
- expectArg(conf, switch, arg, pass, info)
- conf.arcToExpand[arg] = "T"
- of "useversion":
- expectArg(conf, switch, arg, pass, info)
+ dynlibOverride(switch, arg, pass, info)
+ of "cs":
+ expectArg(switch, arg, pass, info)
case arg
- of "1.0":
- defineSymbol(conf.symbols, "NimMajor", "1")
- defineSymbol(conf.symbols, "NimMinor", "0")
- # old behaviors go here:
- defineSymbol(conf.symbols, "nimOldRelativePathBehavior")
- undefSymbol(conf.symbols, "nimDoesntTrackDefects")
- ast.eqTypeFlags.excl {tfGcSafe, tfNoSideEffect}
- conf.globalOptions.incl optNimV1Emulation
- of "1.2":
- defineSymbol(conf.symbols, "NimMajor", "1")
- defineSymbol(conf.symbols, "NimMinor", "2")
- 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")
- of "benchmarkvm":
- processOnOffSwitchG(conf, {optBenchmarkVM}, arg, pass, info)
- of "profilevm":
- processOnOffSwitchG(conf, {optProfileVM}, arg, pass, info)
- of "sinkinference":
- processOnOffSwitch(conf, {optSinkInference}, arg, pass, info)
- of "cursorinference":
- # undocumented, for debugging purposes only:
- processOnOffSwitch(conf, {optCursorInference}, arg, pass, info)
- of "panics":
- processOnOffSwitchG(conf, {optPanics}, arg, pass, info)
- if optPanics in conf.globalOptions:
- defineSymbol(conf.symbols, "nimPanics")
- of "sourcemap": # xxx document in --fullhelp
- 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
+ of "partial": idents.firstCharIsCS = true
+ of "none": idents.firstCharIsCS = false
+ else: localError(info, errGenerated,
+ "'partial' or 'none' expected, but found " & arg)
+ of "experimental":
+ expectNoArg(switch, arg, pass, info)
+ gExperimentalMode = true
+ of "assembler":
+ cAssembler = nameToCC(arg)
+ if cAssembler notin cValidAssemblers:
+ localError(info, errGenerated, "'$1' is not a valid assembler." % [arg])
+ of "nocppexceptions":
+ expectNoArg(switch, arg, pass, info)
+ incl(gGlobalOptions, optNoCppExceptions)
+ defineSymbol("noCppExceptions")
else:
- if strutils.find(switch, '.') >= 0: options.setConfigVar(conf, switch, arg)
- else: invalidCmdLineOption(conf, pass, switch, info)
+ if strutils.find(switch, '.') >= 0: options.setConfigVar(switch, arg)
+ else: invalidCmdLineOption(pass, switch, info)
-proc processCommand*(switch: string, pass: TCmdLinePass; config: ConfigRef) =
+proc processCommand(switch: string, pass: TCmdLinePass) =
var cmd, arg: string
- splitSwitch(config, switch, cmd, arg, pass, gCmdLineInfo)
- processSwitch(cmd, arg, pass, gCmdLineInfo, config)
+ splitSwitch(switch, cmd, arg, pass, gCmdLineInfo)
+ processSwitch(cmd, arg, pass, gCmdLineInfo)
-proc processSwitch*(pass: TCmdLinePass; p: OptParser; config: ConfigRef) =
+
+var
+ arguments* = ""
+ # the arguments to be passed to the program that
+ # should be run
+
+proc processSwitch*(pass: TCmdLinePass; p: OptParser) =
# hint[X]:off is parsed as (p.key = "hint[X]", p.val = "off")
- # we transform it to (key = hint, val = [X]:off)
+ # we fix this here
var bracketLe = strutils.find(p.key, '[')
if bracketLe >= 0:
var key = substr(p.key, 0, bracketLe - 1)
- var val = substr(p.key, bracketLe) & ':' & p.val
- processSwitch(key, val, pass, gCmdLineInfo, config)
+ var val = substr(p.key, bracketLe + 1) & ':' & p.val
+ processSwitch(key, val, pass, gCmdLineInfo)
else:
- processSwitch(p.key, p.val, pass, gCmdLineInfo, config)
+ processSwitch(p.key, p.val, pass, gCmdLineInfo)
proc processArgument*(pass: TCmdLinePass; p: OptParser;
- argsCount: var int; config: ConfigRef): bool =
- if argsCount == 0 and config.implicitCmd:
- argsCount.inc
+ argsCount: var int): bool =
if argsCount == 0:
# nim filename.nims is the same as "nim e filename.nims":
- if p.key.endsWith(".nims"):
- config.setCmd cmdNimscript
- incl(config.globalOptions, optWasNimscript)
- config.projectName = unixToNativePath(p.key)
- config.arguments = cmdLineRest(p)
+ if p.key.endswith(".nims"):
+ options.command = "e"
+ options.gProjectName = unixToNativePath(p.key)
+ arguments = cmdLineRest(p)
result = true
- elif pass != passCmd2: setCommandEarly(config, p.key)
+ elif pass != passCmd2:
+ options.command = p.key
else:
- if pass == passCmd1: config.commandArgs.add p.key
+ if pass == passCmd1: options.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)
- config.arguments = cmdLineRest(p)
+ options.gProjectName = unixToNativePath(p.key)
+ arguments = cmdLineRest(p)
result = true
inc argsCount
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)])
+proc addDependencyAux(importing, imported: string) =
+ addf(gDotGraph, "$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)
+ var g = PGen(c)
case n.kind
of nkImportStmt:
- for i in 0.. 0: #Ok, we are in a try, lets see which (if any) try's we break out from:
- for b in countdown(c.blocks.high, i):
- if c.blocks[b].isTryBlock:
- trailingFinales.add c.blocks[b].finale
-
- c.blocks[i].breakFixups.add (lab1, trailingFinales)
-
-proc genBreak(c: var Con; n: PNode) =
- inc c.interestingInstructions
- if n[0].kind == nkSym:
- for i in countdown(c.blocks.high, 0):
- if not c.blocks[i].isTryBlock and c.blocks[i].label == n[0].sym:
- genBreakOrRaiseAux(c, i, n)
- return
- #globalError(n.info, "VM problem: cannot find 'break' target")
- else:
- for i in countdown(c.blocks.high, 0):
- if not c.blocks[i].isTryBlock:
- genBreakOrRaiseAux(c, i, n)
- return
-
-proc genTry(c: var Con; n: PNode) =
- var endings: seq[TPosition] = @[]
-
- let oldLen = c.blocks.len
- c.blocks.add TBlock(isTryBlock: true, finale: if n[^1].kind == nkFinally: n[^1] else: newNode(nkEmpty))
-
- inc c.inTryStmt
- c.gen(n[0])
- dec c.inTryStmt
-
- for f in c.blocks[oldLen].raiseFixups:
- c.patch(f)
-
- c.blocks.setLen oldLen
-
- for i in 1.. 0:
- for i in countdown(c.blocks.high, 0):
- if c.blocks[i].isTryBlock:
- genBreakOrRaiseAux(c, i, n)
- return
- assert false #Unreachable
- else:
- genNoReturn(c)
-
-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:
- genImplicitReturn(c)
- genBreakOrRaiseAux(c, 0, n)
-
-const
- InterestingSyms = {skVar, skResult, skLet, skParam, skForVar, skTemp}
-
-proc skipTrivials(c: var Con, n: PNode): PNode =
- result = n
- while true:
- case result.kind
- of PathKinds0 - {nkBracketExpr}:
- result = result[0]
- of nkBracketExpr:
- gen(c, result[1])
- result = result[0]
- of PathKinds1:
- result = result[1]
- else: break
-
-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:
- 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
-
-proc genCall(c: var Con; n: PNode) =
- gen(c, n[0])
- var t = n[0].typ
- if t != nil: t = t.skipTypes(abstractInst)
- for i in 1.. 0 and canRaiseConservative(n[0]):
- # we generate the instruction sequence:
- # fork lab1
- # goto exceptionHandler (except or finally)
- # lab1:
- # join F1
- forkT:
- for i in countdown(c.blocks.high, 0):
- if c.blocks[i].isTryBlock:
- genBreakOrRaiseAux(c, i, n)
- break
-
-proc genMagic(c: var Con; n: PNode; m: TMagic) =
- case m
- of mAnd, mOr: c.genAndOr(n)
- of mNew, mNewFinalize:
- genDef(c, n[1])
- for i in 2.. pc:
- pc = newPc
- else:
- break
- c[i].dest = pc - i
- of loop, def, use: discard
-
-proc constructCfg*(s: PSym; body: PNode; root: PSym): ControlFlowGraph =
- ## constructs a control flow graph for ``body``.
- var c = Con(code: @[], blocks: @[], owner: s, root: root)
- withBlock(s):
- gen(c, body)
- if root.kind == skResult:
- 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..d954b897b 100644
--- a/compiler/docgen.nim
+++ b/compiler/docgen.nim
@@ -7,332 +7,63 @@
# 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. It is currently pretty simple: No
+# semantic checking is done for the code. 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],
- json, xmltree, trees, types,
- typesrenderer, astalgo, lineinfos, intsets,
- pathutils, tables, nimpaths, renderverbatim, osproc, packages
-import packages/docutils/rstast except FileIndex, TLineInfo
-
-from uri import encodeUrl
-from nodejs import findNodeJs
-
-when defined(nimPreviewSlimSystem):
- import std/[assertions, syncio]
-
-
-const
- exportSection = skField
- docCmdSkip = "skip"
- DocColOffset = "## ".len # assuming that a space was added after ##
+ ast, strutils, strtabs, options, msgs, os, ropes, idents,
+ wordrecg, syntaxes, renderer, lexer, rstast, rst, rstgen, times, highlite,
+ importer, sempass2, json, xmltree, cgi, typesrenderer
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]
- 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
+ TSections = array[TSymKind, Rope]
TDocumentor = object of rstgen.RstGenerator
- modDescPre: ItemPre # module description, not finalized
- modDescFinal: string # module description, after RST pass 2 and rendering
- 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`)
+ modDesc: Rope # module description
+ id: int # for generating IDs
+ toc, section: TSections
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
- types: TStrTable
- sharedState: PRstSharedState
- standaloneDoc: bool # is markup (.rst/.md) document?
- conf*: ConfigRef
- cache*: IdentCache
- exampleCounter: int
- emitted: IntSet # we need to track which symbols have been emitted
- # already. See bug #3655
- 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 compilerMsgHandler(filename: string, line, col: int,
+ msgKind: rst.MsgKind, arg: string) {.procvar.} =
+ # translate msg kind:
+ var k: msgs.TMsgKind
+ case msgKind
+ of meCannotOpenFile: k = errCannotOpenFile
+ of meExpected: k = errXExpected
+ of meGridTableNotImplemented: k = errGridTableNotImplemented
+ of meNewSectionExpected: k = errNewSectionExpected
+ of meGeneralParseError: k = errGeneralParseError
+ of meInvalidDirective: k = errInvalidDirectiveX
+ of mwRedefinitionOfLabel: k = warnRedefinitionOfLabel
+ of mwUnknownSubstitution: k = warnUnknownSubstitutionX
+ of mwUnsupportedLanguage: k = warnLanguageXNotSupported
+ of mwUnsupportedField: k = warnFieldXNotSupported
+ globalError(newLineInfo(filename, line, col), k, arg)
-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 docgenFindFile(s: string): string {.procvar.} =
+ result = options.findFile(s)
+ if result.len == 0:
+ result = getCurrentDir() / s
+ if not existsFile(result): 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 parseRst(text, filename: string,
+ line, column: int, hasToc: var bool,
+ rstOptions: RstParseOptions): PRstNode =
+ result = rstParse(text, filename, line, column, hasToc, rstOptions,
+ docgenFindFile, compilerMsgHandler)
-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 =
- ## returns a relative file that will be appended to outDir
- let file2 = $file
- template bail() =
- result = relativeTo(file, conf.projectPath)
- proc nimbleDir(): AbsoluteDir =
- getNimbleFile(conf, file2).parentDir.AbsoluteDir
- case conf.docRoot:
- of docRootDefault:
- result = getRelativePathFromConfigPath(conf, file)
- let dir = nimbleDir()
- if not dir.isEmpty:
- let result2 = relativeTo(file, dir)
- if not result2.isEmpty and (result.isEmpty or result2.string.len < result.string.len):
- result = result2
- if result.isEmpty: bail()
- of "@pkg":
- let dir = nimbleDir()
- if dir.isEmpty: bail()
- else: result = relativeTo(file, dir)
- of "@path":
- result = getRelativePathFromConfigPath(conf, file)
- 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)
- conf.globalAssert conf.docRoot.isAbsolute, arg=conf.docRoot
- conf.globalAssert conf.docRoot.dirExists, arg=conf.docRoot
- # needed because `canonicalizePath` called on `file`
- result = file.relativeTo conf.docRoot.expandFilename.AbsoluteDir
- else:
- bail()
- if isAbsolute(result.string):
- result = file.string.splitPath()[1].RelativeFile
- result = result.string.replace("..", dotdotMangle).RelativeFile
- doAssert not result.isEmpty
- doAssert not isAbsolute(result.string)
-
-proc whichType(d: PDoc; n: PNode): PSym =
- if n.kind == nkSym:
- if d.types.strTableContains(n.sym):
- result = n.sym
- else:
- for i in 0.. 1: check(1)
- if params.len > 0: check(0)
- for i in 2..
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
@@ -344,104 +75,95 @@ proc newDocumentor*(filename: AbsoluteFile; cache: IdentCache; conf: ConfigRef,
ga('send', 'pageview');
- """ % [conf.configVars.getOrDefault"doc.googleAnalytics"]
- elif conf.configVars.hasKey("doc.plausibleAnalytics"):
- result.analytics = """
-
- """ % [conf.configVars.getOrDefault"doc.plausibleAnalytics"]
+ """ % [config.getOrDefault"doc.googleAnalytics"]
else:
result.analytics = ""
result.seenSymbols = newStringTable(modeCaseInsensitive)
result.id = 100
- result.jEntriesFinal = newJArray()
- initStrTable result.types
- result.onTestSnippet =
- proc (gen: var RstGenerator; filename, cmd: string; status: int; content: string) {.gcsafe.} =
- if conf.docCmd == docCmdSkip: return
- inc(gen.id)
- var d = (ptr TDocumentor)(addr 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) /
- RelativeFile(nameOnly & "_snippet_" & $d.id & ".nim")
- elif isAbsolute(filename):
- outp = AbsoluteFile(filename)
- else:
- # Nim's convention: every path is relative to the file it was written in:
- let nameOnly = splitFile(d.filename).name
- outp = AbsoluteDir(nameOnly) / RelativeFile(filename)
- # 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("\\", "/")]
- 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)
- let (output, gotten) = execCmdEx(cmd)
- 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.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)
- else: dest.addf(tex, args)
+proc dispA(dest: var Rope, xml, tex: string, args: openArray[Rope]) =
+ if gCmd != cmdRst2tex: addf(dest, xml, args)
+ else: addf(dest, tex, args)
proc getVarIdx(varnames: openArray[string], id: string): int =
- for i in 0..high(varnames):
+ for i in countup(0, high(varnames)):
if cmpIgnoreStyle(varnames[i], id) == 0:
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(frmt: FormatStr, varnames: openArray[string],
+ varvalues: openArray[Rope]): Rope =
+ var i = 0
+ var L = len(frmt)
+ result = nil
+ var num = 0
+ while i < L:
+ if frmt[i] == '$':
+ inc(i) # skip '$'
+ case frmt[i]
+ of '#':
+ add(result, varvalues[num])
+ inc(num)
+ inc(i)
+ of '$':
+ add(result, "$")
+ inc(i)
+ of '0'..'9':
+ var j = 0
+ while true:
+ j = (j * 10) + ord(frmt[i]) - ord('0')
+ inc(i)
+ if (i > L + 0 - 1) or not (frmt[i] in {'0'..'9'}): break
+ if j > high(varvalues) + 1: internalError("ropeFormatNamedVars")
+ num = j
+ add(result, varvalues[j - 1])
+ of 'A'..'Z', 'a'..'z', '\x80'..'\xFF':
+ var id = ""
+ while true:
+ add(id, frmt[i])
+ inc(i)
+ if not (frmt[i] in {'A'..'Z', '_', 'a'..'z', '\x80'..'\xFF'}): break
+ var idx = getVarIdx(varnames, id)
+ if idx >= 0: add(result, varvalues[idx])
+ else: rawMessage(errUnknownSubstitionVar, id)
+ of '{':
+ var id = ""
+ inc(i)
+ while frmt[i] != '}':
+ if frmt[i] == '\0': rawMessage(errTokenExpected, "}")
+ add(id, frmt[i])
+ inc(i)
+ inc(i) # skip }
+ # search for the variable:
+ var idx = getVarIdx(varnames, id)
+ if idx >= 0: add(result, varvalues[idx])
+ else: rawMessage(errUnknownSubstitionVar, id)
+ else: internalError("ropeFormatNamedVars")
+ var start = i
+ while i < L:
+ if frmt[i] != '$': inc(i)
+ else: break
+ if i - 1 >= start: add(result, substr(frmt, start, i - 1))
-proc genRecCommentAux(d: PDoc, n: PNode): PRstNode =
+proc genComment(d: PDoc, n: PNode): string =
+ result = ""
+ var dummyHasToc: bool
+ if n.comment != nil:
+ renderRstToOut(d[], parseRst(n.comment, toFilename(n.info),
+ toLinenumber(n.info), toColumn(n.info),
+ dummyHasToc, d.options), result)
+
+proc genRecComment(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}:
- # notin {nkEmpty..nkNilLit, nkEnumTy, nkTupleTy}:
- for i in 0.. 0: return
+ if result.len < 1:
+ if n.kind notin {nkEmpty..nkNilLit}:
+ for i in countup(0, len(n)-1):
+ result = getPlainDocstring(n.sons[i])
+ if result.len > 0: return
-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)
- let tmp = getOutFile2(d.conf, presentationPath(d.conf, full), HtmlExt, sfMainModule notin module.flags)
- result = relativeTo(tmp, d.thisDir, '/').string
- else:
- result = extractFilename toFullPath(d.conf, FileIndex module.position)
-proc nodeToHighlightedHtml(d: PDoc; n: PNode; result: var string;
- renderFlags: TRenderFlags = {};
- procLink: string) =
- 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)
- while true:
- getNextTok(r, kind, literal)
- inc tokenPos
- case kind
- of tkEof:
- break
- of tkComment:
- dispA(d.conf, result, "", "\\spanComment{$1}",
- [escLit])
- of tokKeywordLow..tokKeywordHigh:
- if kind in {tkProc, tkMethod, tkIterator, tkMacro, tkTemplate, tkFunc, tkConverter}:
- procTokenPos = tokenPos
- dispA(d.conf, result, "$1", "\\spanKeyword{$1}",
- [literal])
- of tkOpr:
- dispA(d.conf, result, "$1", "\\spanOperator{$1}",
- [escLit])
- of tkStrLit..tkTripleStrLit, tkCustomLit:
- dispA(d.conf, result, "$1",
- "\\spanStringLit{$1}", [escLit])
- of tkCharLit:
- dispA(d.conf, result, "$1", "\\spanCharLit{$1}",
- [escLit])
- of tkIntLit..tkUInt64Lit:
- dispA(d.conf, result, "$1",
- "\\spanDecNumber{$1}", [escLit])
- of tkFloatLit..tkFloat128Lit:
- dispA(d.conf, result, "$1",
- "\\spanFloatNumber{$1}", [escLit])
- of tkSymbol:
- let s = getTokSym(r)
- # -2 because of the whitespace in between:
- if procTokenPos == tokenPos-2 and procLink != "":
- 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:
- let external = externalDep(d, s.owner)
- result.addf "$3",
- [changeFileExt(external, "html"), literal,
- escLit]
- else:
- dispA(d.conf, result, "$1",
- "\\spanIdentifier{$1}", [escLit])
- of tkSpaces, tkInvalid:
- result.add(literal)
- of tkHideableStart:
- 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 """
-
-...
-
-""".replace("\n", "") # Must remove newlines because wrapped in a
- of tkHideableEnd:
- 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])
- of tkParLe, tkParRi, tkBracketLe, tkBracketRi, tkCurlyLe, tkCurlyRi,
- tkBracketDotLe, tkBracketDotRi, tkParDotLe,
- tkParDotRi, tkComma, tkSemiColon, tkColon, tkEquals, tkDot, tkDotDot,
- tkAccent, tkColonColon,
- tkGStrLit, tkGTripleStrLit, tkInfixOpr, tkPrefixOpr, tkPostfixOpr,
- tkBracketLeColon:
- dispA(d.conf, result, "$1", "\\spanOther{$1}",
- [escLit])
+proc findDocComment(n: PNode): PNode =
+ if n == nil: return nil
+ if not isNil(n.comment) and startsWith(n.comment, "##"): return n
+ for i in countup(0, safeLen(n)-1):
+ result = findDocComment(n.sons[i])
+ if result != nil: return
-proc exampleOutputDir(d: PDoc): AbsoluteDir = d.conf.getNimcacheDir / RelativeDir"runnableExamples"
-
-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`
- # since semantics are affected by `config.backend`, not by isDefined(d.conf, "js")
- let outputDir = d.exampleOutputDir
- for _, group in d.exampleGroups:
- if group.docCmd == docCmdSkip: continue
- let outp = outputDir / RelativeFile("$1_group$2_examples.nim" % [d.filename.splitFile.name, $group.index])
- group.code = "# autogenerated by docgen\n# source: $1\n# rdoccmd: $2\n$3" % [d.filename, group.rdoccmd, group.code]
- 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()),
- "backend", $d.conf.backend,
- "path", quoteShell(d.conf.projectPath),
- "libpath", quoteShell(d.conf.libpath),
- "nimcache", quoteShell(outputDir),
- "file", quoteShell(outp),
- "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]
+proc extractDocComment*(s: PSym, d: PDoc = nil): string =
+ let n = findDocComment(s.ast)
+ result = ""
+ if not n.isNil:
+ if not d.isNil:
+ var dummyHasToc: bool
+ renderRstToOut(d[], parseRst(n.comment, toFilename(n.info),
+ toLinenumber(n.info), toColumn(n.info),
+ dummyHasToc, d.options + {roSkipPounds}),
+ result)
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
+ result = n.comment.substr(2).replace("\n##", "\n").strip
-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] =
- ## 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")
- if n.len == 3:
- let n1 = n[1]
- # xxx this should be evaluated during sempass
- if n1.kind notin nkStrKinds: globalError(d.conf, n1.info, "string litteral expected")
- rdoccmd = n1.strVal
-
- let useRenderModule = false
- 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)
- when false:
- proc extractImports(n: PNode; result: PNode) =
- if n.kind in {nkImportStmt, nkImportExceptStmt, nkFromStmt}:
- result.add copyTree(n)
- n.kind = nkEmpty
- return
- for i in 0..= 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")
- else: discard
- return rsDone
- # change this to `rsStart` if you want to keep generating doc comments
- # and runnableExamples that occur after some code in routine
-
-proc getRoutineBody(n: PNode): PNode =
- ##[
- nim transforms these quite differently:
-
- proc someType*(): int =
- ## foo
- result = 3
-=>
- result =
- ## foo
- 3;
-
- proc someType*(): int =
- ## foo
- 3
-=>
- ## foo
- result = 3;
-
- so we normalize the results to get to the statement list containing the
- (0 or more) doc comments and runnableExamples.
- ]##
- result = n[bodyPos]
-
- # This won't be transformed: result.id = 10. Namely result[0].kind != nkSym.
- if result.kind == nkAsgn and result[0].kind == nkSym and
- n.len > bodyPos+1 and n[bodyPos+1].kind == nkSym:
- doAssert result.len == 2
- result = result[1]
-
-proc getAllRunnableExamples(d: PDoc, n: PNode, dest: var ItemPre) =
- var n = n
- var state = rsStart
- template fn(n2, topLevel) =
- state = getAllRunnableExamplesImpl(d, n2, dest, state, topLevel)
- dest.add genComment(d, n)
- case n.kind
- of routineDefs:
- n = n.getRoutineBody
- case n.kind
- of nkCommentStmt, nkCallKinds: fn(n, topLevel = false)
- else:
- for i in 0.. paramsPos and n[paramsPos].kind == nkFormalParams:
- let params = renderParamTypes(n[paramsPos])
- if params.len > 0:
- result.add(defaultParamSeparator)
- result.add(params)
+
+ if len(n) > paramsPos and n[paramsPos].kind == nkFormalParams:
+ result.add(renderParamTypes(n[paramsPos]))
+
+
+proc isCallable(n: PNode): bool =
+ ## Returns true if `n` contains a callable node.
+ case n.kind
+ of nkProcDef, nkMethodDef, nkIteratorDef, nkMacroDef, nkTemplateDef,
+ nkConverterDef: result = true
+ else:
+ result = false
+
proc docstringSummary(rstText: string): string =
## Returns just the first line or a brief chunk of text from a rst string.
##
## Most docstrings will contain a one liner summary, so stripping at the
## first newline is usually fine. If after that the content is still too big,
- ## it is stripped at the first comma, colon or dot, usual English sentence
+ ## it is stripped at the first comma, colon or dot, usual english sentence
## separators.
##
## No guarantees are made on the size of the output, but it should be small.
## Also, we hope to not break the rst, but maybe we do. If there is any
## trimming done, an ellipsis unicode char is added.
const maxDocstringChars = 100
- assert(rstText.len < 2 or (rstText[0] == '#' and rstText[1] == '#'))
+ assert (rstText.len < 2 or (rstText[0] == '#' and rstText[1] == '#'))
result = rstText.substr(2).strip
var pos = result.find('\L')
if pos > 0:
- result.setLen(pos - 1)
+ result.delete(pos, result.len - 1)
result.add("…")
if pos < maxDocstringChars:
return
@@ -906,996 +323,332 @@ 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 =
- ## 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", ""]
- 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)]
- else:
- doAssert false
-
-type DocFlags = enum
- kDefault
- kForceExport
-
-proc genSeeSrc(d: PDoc, path: string, line: int): string =
- let docItemSeeSrc = getConfigVar(d.conf, "doc.item.seesrc")
- if docItemSeeSrc.len > 0:
- let path = relativeTo(AbsoluteFile path, AbsoluteDir getCurrentDir(), '/')
- when false:
- let cwd = canonicalizePath(d.conf, getCurrentDir())
- var path = path
- if path.startsWith(cwd):
- path = path[cwd.len+1..^1].replace('\\', '/')
- let gitUrl = getConfigVar(d.conf, "git.url")
- if gitUrl.len > 0:
- let defaultBranch =
- if NimPatch mod 2 == 1: "devel"
- 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]])
-
-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) =
- if (docFlags != kForceExport) and not isVisible(d, nameNode): return
+proc genItem(d: PDoc, n, nameNode: PNode, k: TSymKind) =
+ if not isVisible(nameNode): return
let
- name = getName(nameNode)
- nameEsc = esc(d.target, name)
- var plainDocstring = getPlainDocstring(n) # call here before genRecComment!
- var result = ""
+ name = getName(d, nameNode)
+ nameRope = name.rope
+ plainDocstring = getPlainDocstring(n) # call here before genRecComment!
+ var result: Rope = nil
var literal, plainName = ""
var kind = tkEof
- var comm: ItemPre
- if n.kind in routineDefs:
- getAllRunnableExamples(d, n, comm)
- else:
- comm.add genRecComment(d, n)
-
+ var comm = genRecComment(d, n) # call this here for the side-effect!
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)
-
+ # Render the HTML hyperlink.
+ initTokRender(r, n, {renderNoBody, renderNoComments, renderDocComments})
+ while true:
+ getNextTok(r, kind, literal)
+ case kind
+ of tkEof:
+ break
+ of tkComment:
+ dispA(result, "", "\\spanComment{$1}",
+ [rope(esc(d.target, literal))])
+ of tokKeywordLow..tokKeywordHigh:
+ dispA(result, "$1", "\\spanKeyword{$1}",
+ [rope(literal)])
+ of tkOpr:
+ dispA(result, "$1", "\\spanOperator{$1}",
+ [rope(esc(d.target, literal))])
+ of tkStrLit..tkTripleStrLit:
+ dispA(result, "$1",
+ "\\spanStringLit{$1}", [rope(esc(d.target, literal))])
+ of tkCharLit:
+ dispA(result, "$1", "\\spanCharLit{$1}",
+ [rope(esc(d.target, literal))])
+ of tkIntLit..tkUInt64Lit:
+ dispA(result, "$1",
+ "\\spanDecNumber{$1}", [rope(esc(d.target, literal))])
+ of tkFloatLit..tkFloat128Lit:
+ dispA(result, "$1",
+ "\\spanFloatNumber{$1}", [rope(esc(d.target, literal))])
+ of tkSymbol:
+ dispA(result, "$1",
+ "\\spanIdentifier{$1}", [rope(esc(d.target, literal))])
+ of tkSpaces, tkInvalid:
+ add(result, literal)
+ of tkParLe, tkParRi, tkBracketLe, tkBracketRi, tkCurlyLe, tkCurlyRi,
+ tkBracketDotLe, tkBracketDotRi, tkCurlyDotLe, tkCurlyDotRi, tkParDotLe,
+ tkParDotRi, tkComma, tkSemiColon, tkColon, tkEquals, tkDot, tkDotDot,
+ tkAccent, tkColonColon,
+ tkGStrLit, tkGTripleStrLit, tkInfixOpr, tkPrefixOpr, tkPostfixOpr:
+ dispA(result, "$1", "\\spanOther{$1}",
+ [rope(esc(d.target, literal))])
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
- # 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)
+ var seeSrcRope: Rope = nil
+ let docItemSeeSrc = getConfigVar("doc.item.seesrc")
+ if docItemSeeSrc.len > 0 and options.docSeeSrcUrl.len > 0:
+ let path = n.info.toFilename.extractFilename.rope
+ let urlRope = ropeFormatNamedVars(options.docSeeSrcUrl,
+ ["path", "line"], [path, rope($n.info.line)])
+ dispA(seeSrcRope, "$1", "", [ropeFormatNamedVars(docItemSeeSrc,
+ ["path", "line", "url"], [path,
+ rope($n.info.line), urlRope])])
- let renderFlags =
- if nonExports: {renderNoBody, renderNoComments, renderDocComments, renderSyms,
- renderExpandUsing, renderNonExportedFields}
- else: {renderNoBody, renderNoComments, renderDocComments, renderSyms, renderExpandUsing}
- nodeToHighlightedHtml(d, n, result, renderFlags, symbolOrIdEnc)
+ add(d.section[k], ropeFormatNamedVars(getConfigVar("doc.item"),
+ ["name", "header", "desc", "itemID", "header_plain", "itemSym",
+ "itemSymOrID", "itemSymEnc", "itemSymOrIDEnc", "seeSrc"],
+ [nameRope, result, comm, itemIDRope, plainNameRope, plainSymbolRope,
+ symbolOrIdRope, plainSymbolEncRope, symbolOrIdEncRope, seeSrcRope]))
+ add(d.toc[k], ropeFormatNamedVars(getConfigVar("doc.item.toc"),
+ ["name", "header", "desc", "itemID", "header_plain", "itemSym",
+ "itemSymOrID", "itemSymEnc", "itemSymOrIDEnc"],
+ [rope(getName(d, nameNode, d.splitAfter)), result, comm,
+ itemIDRope, plainNameRope, plainSymbolRope, symbolOrIdRope,
+ plainSymbolEncRope, symbolOrIdEncRope]))
- let seeSrc = genSeeSrc(d, toFullPath(d.conf, n.info), 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))
- 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])
+ setIndexTerm(d[], symbolOrId, name, linkTitle,
+ xmltree.escape(plainDocstring.docstringSummary))
- let external = d.destFile.AbsoluteFile.relativeTo(d.conf.outDir, '/').changeFileExt(HtmlExt).string
-
- var attype = ""
- if k in routineKinds and nameNode.kind == nkSym:
- let att = attachToType(d, nameNode.sym)
- if att != nil:
- attype = 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)
-
- d.tocSimple[k].add TocItem(
- sortName: sortName,
- content: getConfigVar(d.conf, "doc.item.toc") % [
- "name", name, "header_plain", plainNameEsc,
- "itemSymOrIDEnc", symbolOrIdEnc])
-
- 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])
-
- setIndexTerm(d[], ieNim, htmlFile = external, id = symbolOrId, term = name,
- linkTitle = detailedName,
- linkDesc = xmltree.escape(plainDocstring.docstringSummary),
- line = n.info.line.int)
- if k == skType and nameNode.kind == nkSym:
- d.types.strTableAdd nameNode.sym
-
-proc genJsonItem(d: PDoc, n, nameNode: PNode, k: TSymKind): JsonItem =
- if not isVisible(d, nameNode): return
+proc genJSONItem(d: PDoc, n, nameNode: PNode, k: TSymKind): JsonNode =
+ if not isVisible(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"
- if r.buf.len > 0:
- result.json["code"] = %r.buf
- if k in routineKinds:
- result.json["signature"] = newJObject()
- if n[paramsPos][0].kind != nkEmpty:
- result.json["signature"]["return"] = %($n[paramsPos][0])
- if n[paramsPos].len > 1:
- result.json["signature"]["arguments"] = newJArray()
- for paramIdx in 1 ..< n[paramsPos].len:
- for identIdx in 0 ..< n[paramsPos][paramIdx].len - 2:
- let
- paramName = $n[paramsPos][paramIdx][identIdx]
- 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}
- else:
- result.json["signature"]["arguments"].add %{"name": %paramName, "type": %paramType}
- if n[pragmasPos].kind != nkEmpty:
- result.json["signature"]["pragmas"] = newJArray()
- for pragma in n[pragmasPos]:
- result.json["signature"]["pragmas"].add %($pragma)
- if n[genericParamsPos].kind != nkEmpty:
- result.json["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]
- )
+ initTokRender(r, n, {renderNoBody, renderNoComments, renderDocComments})
+
+ result = %{ "name": %name, "type": %($k) }
+
+ if comm != nil and comm != "":
+ result["description"] = %comm
+ if r.buf != nil:
+ result["code"] = %r.buf
proc checkForFalse(n: PNode): bool =
- result = n.kind == nkIdent and cmpIgnoreStyle(n.ident.s, "false") == 0
+ result = n.kind == nkIdent and identEq(n.ident, "false")
-proc traceDeps(d: PDoc, it: PNode) =
+proc traceDeps(d: PDoc, n: PNode) =
const k = skModule
- if it.kind == nkInfix and it.len == 3 and it[2].kind == nkBracket:
- let sep = it[0]
- let dir = it[1]
- let a = newNodeI(nkInfix, it.info)
- a.add sep
- a.add dir
- a.add sep # dummy entry, replaced in the loop
- for x in it[2]:
- a[2] = x
- traceDeps(d, a)
- elif it.kind == nkSym and belongsToProjectPackage(d.conf, it.sym):
- let external = externalDep(d, it.sym)
- if d.section[k].finalMarkup != "": d.section[k].finalMarkup.add(", ")
- dispA(d.conf, d.section[k].finalMarkup,
- "$1",
- "$1", [esc(d.target, external.prettyLink),
- changeFileExt(external, "html")])
+ if d.section[k] != nil: add(d.section[k], ", ")
+ dispA(d.section[k],
+ "$1",
+ "$1", [rope(getModuleName(n))])
-proc exportSym(d: PDoc; s: PSym) =
- const k = exportSection
- if s.kind == skModule and belongsToProjectPackage(d.conf, s):
- let external = externalDep(d, s)
- if d.section[k].finalMarkup != "": d.section[k].finalMarkup.add(", ")
- dispA(d.conf, d.section[k].finalMarkup,
- "$1",
- "$1", [esc(d.target, external.prettyLink),
- changeFileExt(external, "html")])
- elif s.kind != skModule and s.owner != nil:
- let module = originatingModule(s)
- if belongsToProjectPackage(d.conf, module):
- let
- complexSymbol = complexName(s.kind, s.ast, s.name.s)
- symbolOrId = d.newUniquePlainSymbol(complexSymbol)
- external = externalDep(d, module)
- if d.section[k].finalMarkup != "": d.section[k].finalMarkup.add(", ")
- # XXX proper anchor generation here
- dispA(d.conf, d.section[k].finalMarkup,
- "$1",
- "$1", [esc(d.target, s.name.s),
- changeFileExt(external, "html"),
- symbolOrId])
-
-proc documentNewEffect(cache: IdentCache; n: PNode): PNode =
- let s = n[namePos].sym
- if tfReturnsNew in s.typ.flags:
- result = newIdentNode(getIdent(cache, "new"), n.info)
-
-proc documentEffect(cache: IdentCache; n, x: PNode, effectType: TSpecialWord, idx: int): PNode =
- let spec = effectSpec(x, effectType)
- if isNil(spec):
- let s = n[namePos].sym
-
- 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.. 0:
- result = newTreeI(nkExprColonExpr, n.info,
- newIdentNode(getIdent(cache, pragmaName), n.info), effects)
-
-proc documentRaises*(cache: IdentCache; n: PNode) =
- if n[namePos].kind != nkSym: return
- let pragmas = n[pragmasPos]
- let p1 = documentEffect(cache, n, pragmas, wRaises, exceptionEffects)
- let p2 = documentEffect(cache, n, pragmas, wTags, tagEffects)
- let p3 = documentWriteEffect(cache, n, sfWrittenTo, "writes")
- let p4 = documentNewEffect(cache, n)
- let p5 = documentWriteEffect(cache, n, sfEscapes, "escapes")
- let p6 = documentEffect(cache, n, pragmas, wForbids, forbiddenEffects)
-
- if p1 != nil or p2 != nil or p3 != nil or p4 != nil or p5 != nil or p6 != nil:
- if pragmas.kind == nkEmpty:
- n[pragmasPos] = newNodeI(nkPragma, n.info)
- if p1 != nil: n[pragmasPos].add p1
- if p2 != nil: n[pragmasPos].add p2
- if p3 != nil: n[pragmasPos].add p3
- if p4 != nil: n[pragmasPos].add p4
- if p5 != nil: n[pragmasPos].add p5
- if p6 != nil: n[pragmasPos].add p6
-
-proc generateDoc*(d: PDoc, n, orig: PNode, config: ConfigRef, docFlags: DocFlags = kDefault) =
- ## Goes through nim nodes recursively and collects doc comments.
- ## Main function for `doc`:option: command,
- ## which is implemented in ``docgen2.nim``.
- template genItemAux(skind) =
- genItem(d, n, n[namePos], skind, docFlags)
- let showNonExports = optShowNonExportedFields in config.globalOptions
+proc generateDoc*(d: PDoc, n: PNode) =
case n.kind
- of nkPragma:
- let pragmaNode = findPragma(n, wDeprecated)
- d.modDeprecationMsg.add(genDeprecationMsg(d, pragmaNode))
- let doctypeNode = findPragma(n, wDoctype)
- setDoctype(d, doctypeNode)
- of nkCommentStmt: d.modDescPre.add(genComment(d, n))
- of nkProcDef, nkFuncDef:
- when useEffectSystem: documentRaises(d.cache, n)
- genItemAux(skProc)
- of nkMethodDef:
- when useEffectSystem: documentRaises(d.cache, n)
- genItemAux(skMethod)
- of nkIteratorDef:
- when useEffectSystem: documentRaises(d.cache, n)
- genItemAux(skIterator)
- of nkMacroDef: genItemAux(skMacro)
- of nkTemplateDef: genItemAux(skTemplate)
- of nkConverterDef:
- when useEffectSystem: documentRaises(d.cache, n)
- genItemAux(skConverter)
- of nkTypeSection, nkVarSection, nkLetSection, nkConstSection:
- 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 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]())
- else:
- d.modDescPre.add(genComment(d, n))
- of nkProcDef, nkFuncDef:
- when useEffectSystem: documentRaises(d.cache, n)
- d.add genJsonItem(d, n, n[namePos], skProc)
- of nkMethodDef:
- when useEffectSystem: documentRaises(d.cache, n)
- d.add genJsonItem(d, n, n[namePos], skMethod)
- of nkIteratorDef:
- when useEffectSystem: documentRaises(d.cache, n)
- d.add genJsonItem(d, n, n[namePos], skIterator)
- of nkMacroDef:
- d.add genJsonItem(d, n, n[namePos], skMacro)
- of nkTemplateDef:
- d.add genJsonItem(d, n, n[namePos], skTemplate)
- of nkConverterDef:
- when useEffectSystem: documentRaises(d.cache, n)
- d.add genJsonItem(d, n, n[namePos], skConverter)
- of nkTypeSection, nkVarSection, nkLetSection, nkConstSection:
- for i in 0..$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.
+ title = "Module " & extractFilename(changeFileExt(d.filename, ""))
- var groupsection = getConfigVar(d.conf, "doc.body_toc_groupsection")
- let bodyname = if d.hasToc and not d.standaloneDoc and not d.conf.isLatexCmd:
- groupsection.setLen 0
- "doc.body_toc_group"
- elif d.hasToc: "doc.body_toc"
- 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]
- if optCompileOnly notin d.conf.globalOptions:
+ let bodyname = if d.hasToc: "doc.body_toc" else: "doc.body_no_toc"
+ content = ropeFormatNamedVars(getConfigVar(bodyname), ["title",
+ "tableofcontents", "moduledesc", "date", "time", "content"],
+ [title.rope, toc, d.modDesc, rope(getDateStr()),
+ rope(getClockStr()), code])
+ if optCompileOnly notin gGlobalOptions:
# 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(getConfigVar("doc.file"), ["title",
+ "tableofcontents", "moduledesc", "date", "time",
+ "content", "author", "version", "analytics"],
+ [title.rope, toc, d.modDesc, rope(getDateStr()),
+ rope(getClockStr()), content, d.meta[metaAuthor].rope,
+ d.meta[metaVersion].rope, d.analytics.rope])
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)
- writeIndexFile(d[], dest.string)
+ if optGenIndex in gGlobalOptions:
+ writeIndexFile(d[], splitFile(options.outFile).dir /
+ splitFile(d.filename).name & IndexExt)
-proc updateOutfile(d: PDoc, outfile: AbsoluteFile) =
- if d.module == nil or sfMainModule in d.module.flags: # nil for e.g. for commandRst2Html
- if d.conf.outFile.isEmpty:
- d.conf.outFile = outfile.relativeTo(d.conf.outDir)
- if isAbsolute(d.conf.outFile.string):
- 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)
+proc writeOutput*(d: PDoc, filename, outExt: string, useWarning = false) =
+ var content = genOutFile(d)
+ if optStdout in gGlobalOptions:
+ 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:
- rawMessage(d.conf, if useWarning: warnCannotOpenFile else: errCannotOpenFile,
- outfile.string)
- if 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)
- if optGenIndex in d.conf.globalOptions:
- let docHackJs2 = getDocHacksJs(nimr, nim = getAppFilename())
- copyFile(docHackJs2, $d.conf.outDir / docHackJs2.lastPathPart)
- d.wroteSupportFiles = true
+ writeRope(content, getOutFile(filename, outExt), useWarning)
-proc writeOutputJson*(d: PDoc, useWarning = false) =
- runAllExamples(d)
- var modDesc: string
- for desc in d.modDescFinal:
- modDesc &= desc
- let content = %*{"orig": d.filename,
- "nimble": getPackageName(d.conf, d.filename),
- "moduleDescription": modDesc,
- "entries": d.jEntriesFinal}
- 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)
- close(f)
- updateOutfile(d, d.destFile.AbsoluteFile)
- else:
- localError(d.conf, newLineInfo(d.conf, AbsoluteFile d.filename, -1, -1),
- warnUser, "unable to open file \"" & d.destFile &
- "\" for writing")
-
-proc handleDocOutputOptions*(conf: ConfigRef) =
- if optWholeProject in conf.globalOptions:
- # Backward compatibility with previous versions
- # xxx this is buggy when user provides `nim doc --project -o:sub/bar.html main`,
- # it'd write to `sub/bar.html/main.html`
- 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)
+proc commandDoc*() =
+ var ast = parseFile(gProjectMainIdx)
if ast == nil: return
- var d = newDocumentor(conf.projectFull, cache, conf, hasToc = true)
- generateDoc(d, ast, ast, conf)
- finishGenerateDoc(d)
- writeOutput(d)
+ var d = newDocumentor(gProjectFull, options.gConfigVars)
+ d.hasToc = true
+ generateDoc(d, ast)
+ writeOutput(d, gProjectFull, HtmlExt)
generateIndex(d)
-proc commandRstAux(cache: IdentCache, conf: ConfigRef;
- filename: AbsoluteFile, outExt: string,
- preferMarkdown: bool) =
+proc commandRstAux(filename, 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)
- writeOutput(d)
+ var d = newDocumentor(filen, options.gConfigVars)
+ var rst = parseRst(readFile(filen), filen, 0, 1, d.hasToc,
+ {roSupportRawDirective})
+ var modDesc = newStringOfCap(30_000)
+ #d.modDesc = newMutableRope(30_000)
+ renderRstToOut(d[], rst, modDesc)
+ #freezeMutableRope(d.modDesc)
+ d.modDesc = rope(modDesc)
+ writeOutput(d, filename, outExt)
generateIndex(d)
-proc commandRst2Html*(cache: IdentCache, conf: ConfigRef,
- preferMarkdown=false) =
- commandRstAux(cache, conf, conf.projectFull, HtmlExt, preferMarkdown)
+proc commandRst2Html*() =
+ commandRstAux(gProjectFull, HtmlExt)
-proc commandRst2TeX*(cache: IdentCache, conf: ConfigRef,
- preferMarkdown=false) =
- commandRstAux(cache, conf, conf.projectFull, TexExt, preferMarkdown)
+proc commandRst2TeX*() =
+ splitter = "\\-"
+ commandRstAux(gProjectFull, TexExt)
-proc commandJson*(cache: IdentCache, conf: ConfigRef) =
- ## implementation of a deprecated jsondoc0 command
- var ast = parseFile(conf.projectMainIdx, cache, conf)
+proc commandJSON*() =
+ var ast = parseFile(gProjectMainIdx)
if ast == nil: return
- var d = newDocumentor(conf.projectFull, cache, conf, hasToc = true)
- d.onTestSnippet = proc (d: var RstGenerator; filename, cmd: string;
- status: int; content: string) {.gcsafe.} =
- localError(conf, newLineInfo(conf, AbsoluteFile d.filename, -1, -1),
- warnUser, "the ':test:' attribute is not supported by this backend")
- generateJson(d, ast)
- finishGenerateDoc(d)
- let json = d.jEntriesFinal
- let content = pretty(json)
+ var d = newDocumentor(gProjectFull, options.gConfigVars)
+ d.hasToc = true
+ var json = generateJson(d, ast)
+ var content = rope(pretty(json))
- if optStdout in d.conf.globalOptions:
- write(stdout, content)
+ if optStdout in gGlobalOptions:
+ writeRope(stdout, content)
else:
- #echo getOutFile(gProjectFull, JsonExt)
- let filename = getOutFile(conf, RelativeFile conf.projectName, JsonExt)
- try:
- writeFile(filename, content)
- except IOError:
- rawMessage(conf, errCannotOpenFile, filename.string)
+ echo getOutFile(gProjectFull, JsonExt)
+ writeRope(content, getOutFile(gProjectFull, JsonExt), useWarning = false)
-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)
- d.onTestSnippet = proc (d: var RstGenerator; filename, cmd: string;
- status: int; content: string) {.gcsafe.} =
- localError(conf, newLineInfo(conf, AbsoluteFile d.filename, -1, -1),
- warnUser, "the ':test:' attribute is not supported by this backend")
- var
- content = ""
- generateTags(d, ast, content)
+proc commandBuildIndex*() =
+ var content = mergeIndexes(gProjectFull).rope
- if optStdout in d.conf.globalOptions:
- write(stdout, content)
- else:
- #echo getOutFile(gProjectFull, TagsExt)
- let filename = getOutFile(conf, RelativeFile conf.projectName, TagsExt)
- try:
- writeFile(filename, content)
- except IOError:
- rawMessage(conf, errCannotOpenFile, filename.string)
-
-proc commandBuildIndex*(conf: ConfigRef, dir: string, outFile = RelativeFile"") =
- if optGenIndexOnly in conf.globalOptions:
- return
- var content = mergeIndexes(dir)
-
- 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(getConfigVar("doc.file"), ["title",
+ "tableofcontents", "moduledesc", "date", "time",
+ "content", "author", "version", "analytics"],
+ ["Index".rope, 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:
- rawMessage(conf, errCannotOpenFile, filename.string)
+ writeRope(code, getOutFile("theindex", HtmlExt))
diff --git a/compiler/docgen2.nim b/compiler/docgen2.nim
index 0941be78a..27de06811 100644
--- a/compiler/docgen2.nim
+++ b/compiler/docgen2.nim
@@ -11,68 +11,39 @@
# semantic checking.
import
- options, ast, msgs, docgen, lineinfos, pathutils, packages
-
-from modulegraphs import ModuleGraph, PPassContext
+ os, options, ast, astalgo, msgs, ropes, idents, passes, docgen
type
- TGen = object of PPassContext
+ TGen = object of TPassContext
doc: PDoc
module: PSym
- config: ConfigRef
PGen = ref TGen
-proc shouldProcess(g: PGen): bool =
- (optWholeProject in g.doc.conf.globalOptions and g.doc.conf.belongsToProjectPackage(g.module)) or
- sfMainModule in g.module.flags or g.config.projectMainIdx == g.module.info.fileIndex
-
-template closeImpl(body: untyped) {.dirty.} =
+proc close(p: PPassContext, n: PNode): PNode =
var g = PGen(p)
let useWarning = sfMainModule notin g.module.flags
- let groupedToc = true
- if shouldProcess(g):
- finishGenerateDoc(g.doc)
- body
+ if gWholeProject or sfMainModule in g.module.flags:
+ writeOutput(g.doc, g.module.filename, HtmlExt, useWarning)
try:
generateIndex(g.doc)
except IOError:
discard
-proc closeDoc*(graph: ModuleGraph; p: PPassContext, n: PNode): PNode =
- closeImpl:
- writeOutput(g.doc, useWarning, groupedToc)
-
-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)
-proc processNodeJson*(c: PPassContext, n: PNode): PNode =
- result = n
- var g = PGen(c)
- if shouldProcess(g):
- generateJson(g.doc, n, false)
-
-template myOpenImpl(ext: untyped) {.dirty.} =
+proc myOpen(module: PSym): PPassContext =
var g: PGen
new(g)
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)
+ var d = newDocumentor(module.filename, options.gConfigVars)
+ d.hasToc = true
g.doc = d
result = g
-proc openHtml*(graph: ModuleGraph; module: PSym; idgen: IdGenerator): PPassContext =
- myOpenImpl(HtmlExt)
+const docgen2Pass* = makePass(open = myOpen, process = processNode, close = close)
-proc openTex*(graph: ModuleGraph; module: PSym; idgen: IdGenerator): PPassContext =
- myOpenImpl(TexExt)
-
-proc openJson*(graph: ModuleGraph; module: PSym; idgen: IdGenerator): PPassContext =
- myOpenImpl(JsonExt)
+proc finishDoc2Pass*(project: string) =
+ discard
diff --git a/compiler/enumtostr.nim b/compiler/enumtostr.nim
deleted file mode 100644
index f8b3744d5..000000000
--- a/compiler/enumtostr.nim
+++ /dev/null
@@ -1,109 +0,0 @@
-
-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)
-
- let dest = newSym(skParam, getIdent(g.cache, "e"), nextSymId idgen, result, info)
- dest.typ = t
-
- let res = newSym(skResult, getIdent(g.cache, "result"), nextSymId idgen, result, info)
- res.typ = getSysType(g, info, tyString)
-
- result.typ = newType(tyProc, nextTypeId idgen, t.owner)
- result.typ.n = newNodeI(nkFormalParams, info)
- rawAddSon(result.typ, res.typ)
- result.typ.n.add newNodeI(nkEffectList, info)
-
- result.typ.addParam dest
-
- var body = newNodeI(nkStmtList, info)
- var caseStmt = newNodeI(nkCaseStmt, info)
- caseStmt.add(newSymNode dest)
-
- # copy the branches over, but replace the fields with the for loop body:
- for i in 0.. 0:
- result = searchObjCase(t[0].skipTypes({tyAlias, tyGenericInst, tyRef, tyPtr}), field)
- doAssert result != nil
-
-proc genCaseObjDiscMapping*(t: PType; field: PSym; info: TLineInfo; g: ModuleGraph; idgen: IdGenerator): PSym =
- result = newSym(skProc, getIdent(g.cache, "objDiscMapping"), nextSymId idgen, t.owner, info)
-
- let dest = newSym(skParam, getIdent(g.cache, "e"), nextSymId idgen, result, info)
- dest.typ = field.typ
-
- let res = newSym(skResult, getIdent(g.cache, "result"), nextSymId idgen, result, info)
- res.typ = getSysType(g, info, tyUInt8)
-
- result.typ = newType(tyProc, nextTypeId idgen, t.owner)
- result.typ.n = newNodeI(nkFormalParams, info)
- rawAddSon(result.typ, res.typ)
- result.typ.n.add newNodeI(nkEffectList, info)
-
- result.typ.addParam dest
-
- var body = newNodeI(nkStmtList, info)
- var caseStmt = newNodeI(nkCaseStmt, info)
- caseStmt.add(newSymNode dest)
-
- let subObj = searchObjCase(t, field)
- for i in 1.. 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..75394c2f3 100644
--- a/compiler/evalffi.nim
+++ b/compiler/evalffi.nim
@@ -9,82 +9,76 @@
## This file implements the FFI part of the evaluator for Nim code.
-import ast, types, options, tables, dynlib, msgs, lineinfos
-from os import getAppFilename
-import pkg/libffi
+import ast, astalgo, ropes, types, options, tables, dynlib, libffi, msgs, os
when defined(windows):
const libcDll = "msvcrt.dll"
-elif defined(linux):
- const libcDll = "libc.so(.6|.5|)"
-elif defined(openbsd):
- const libcDll = "/usr/lib/libc.so(.95.1|)"
-elif defined(bsd):
- const libcDll = "/lib/libc.so.7"
-elif defined(osx):
- const libcDll = "/usr/lib/libSystem.dylib"
else:
- {.error: "`libcDll` not implemented on this platform".}
+ const libcDll = "libc.so(.6|.5|)"
type
- TDllCache = tables.Table[string, LibHandle]
+ TDllCache = tables.TTable[string, TLibHandle]
var
- gDllCache = initTable[string, LibHandle]()
+ gDllCache = initTable[string, TLibHandle]()
when defined(windows):
- var gExeHandle = loadLib(getAppFilename())
+ var gExeHandle = loadLib(os.getAppFilename())
else:
var gExeHandle = loadLib()
-proc getDll(conf: ConfigRef, cache: var TDllCache; dll: string; info: TLineInfo): pointer =
- if dll in cache:
- return cache[dll]
- var libs: seq[string]
- libCandidates(dll, libs)
- for c in libs:
- result = loadLib(c)
- if not result.isNil: break
+proc getDll(cache: var TDllCache; dll: string; info: TLineInfo): pointer =
+ result = cache[dll]
if result.isNil:
- globalError(conf, info, "cannot load: " & dll)
- cache[dll] = result
+ var libs: seq[string] = @[]
+ libCandidates(dll, libs)
+ for c in libs:
+ result = loadLib(c)
+ if not result.isNil: break
+ if result.isNil:
+ globalError(info, "cannot load: " & dll)
+ cache[dll] = result
const
nkPtrLit = nkIntLit # hopefully we can get rid of this hack soon
-proc importcSymbol*(conf: ConfigRef, sym: PSym): PNode =
- let name = sym.cname # $sym.loc.r would point to internal name
+var myerrno {.importc: "errno", header: "".}: cint ## error variable
+
+proc importcSymbol*(sym: PSym): PNode =
+ let name = ropeToStr(sym.loc.r)
+
# the AST does not support untyped pointers directly, so we use an nkIntLit
# that contains the address instead:
result = newNodeIT(nkPtrLit, sym.info, sym.typ)
- when true:
- var libPathMsg = ""
+ case name
+ of "stdin": result.intVal = cast[ByteAddress](system.stdin)
+ of "stdout": result.intVal = cast[ByteAddress](system.stdout)
+ of "stderr": result.intVal = cast[ByteAddress](system.stderr)
+ of "vmErrnoWrapper": result.intVal = cast[ByteAddress](myerrno)
+ else:
let lib = sym.annex
if lib != nil and lib.path.kind notin {nkStrLit..nkTripleStrLit}:
- globalError(conf, sym.info, "dynlib needs to be a string lit")
+ globalError(sym.info, "dynlib needs to be a string lit for the REPL")
var theAddr: pointer
- if (lib.isNil or lib.kind == libHeader) and not gExeHandle.isNil:
- libPathMsg = "current exe: " & getAppFilename() & " nor libc: " & libcDll
+ if lib.isNil and not gExehandle.isNil:
# 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)
+ let dllhandle = gDllCache.getDll(libcDll, sym.info)
+ 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)
- if theAddr.isNil: globalError(conf, sym.info,
- "cannot import symbol: " & name & " from " & libPathMsg)
- result.intVal = cast[int](theAddr)
+ let dllhandle = gDllCache.getDll(if lib.kind == libHeader: libcDll
+ else: lib.path.strVal, sym.info)
+ theAddr = dllhandle.symAddr(name)
+ if theAddr.isNil: globalError(sym.info, "cannot import: " & sym.name.s)
+ result.intVal = cast[ByteAddress](theAddr)
-proc mapType(conf: ConfigRef, t: ast.PType): ptr libffi.Type =
+proc mapType(t: ast.PType): ptr libffi.TType =
if t == nil: return addr libffi.type_void
-
+
case t.kind
of tyBool, tyEnum, tyChar, tyInt..tyInt64, tyUInt..tyUInt64, tySet:
- case getSize(conf, t)
+ case t.getSize
of 1: result = addr libffi.type_uint8
of 2: result = addr libffi.type_sint16
of 4: result = addr libffi.type_sint32
@@ -92,93 +86,93 @@ 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,
- tyTyped, tyTypeDesc, tyProc, tyArray, tyStatic, tyNil:
+ of tyVar, tyPointer, tyPtr, tyRef, tyCString, tySequence, tyString, tyExpr,
+ tyStmt, tyTypeDesc, tyProc, tyArray, tyArrayConstr, tyStatic, tyNil:
result = addr libffi.type_pointer
- of tyDistinct, tyAlias, tySink:
- result = mapType(conf, t[0])
+ of tyDistinct:
+ result = mapType(t.sons[0])
else:
result = nil
# too risky:
#of tyFloat128: result = addr libffi.type_longdouble
-proc mapCallConv(conf: ConfigRef, cc: TCallingConvention, info: TLineInfo): TABI =
+proc mapCallConv(cc: TCallingConvention, info: TLineInfo): TABI =
case cc
- of ccNimCall: result = DEFAULT_ABI
- of ccStdCall: result = when defined(windows) and defined(x86): STDCALL else: DEFAULT_ABI
+ of ccDefault: result = DEFAULT_ABI
+ of ccStdCall: result = when defined(windows): STDCALL else: DEFAULT_ABI
of ccCDecl: result = DEFAULT_ABI
else:
- globalError(conf, info, "cannot map calling convention to FFI")
+ globalError(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 `+!`(x, y: untyped): untyped =
- cast[pointer](cast[int](x) + y)
+template rd(T, p: expr): expr {.immediate.} = (cast[ptr T](p))[]
+template wr(T, p, v: expr) {.immediate.} = (cast[ptr T](p))[] = v
+template `+!`(x, y: expr): expr {.immediate.} =
+ cast[pointer](cast[ByteAddress](x) + y)
-proc packSize(conf: ConfigRef, v: PNode, typ: PType): int =
+proc packSize(v: PNode, typ: PType): int =
## computes the size of the blob
case typ.kind
- of tyPtr, tyRef, tyVar, tyLent:
+ of tyPtr, tyRef, tyVar:
if v.kind in {nkNilLit, nkPtrLit}:
result = sizeof(pointer)
else:
- result = sizeof(pointer) + packSize(conf, v[0], typ.lastSon)
- of tyDistinct, tyGenericInst, tyAlias, tySink:
- result = packSize(conf, v, typ[0])
- of tyArray:
+ result = sizeof(pointer) + packSize(v.sons[0], typ.lastSon)
+ of tyDistinct, tyGenericInst:
+ result = packSize(v, typ.sons[0])
+ of tyArray, tyArrayConstr:
# consider: ptr array[0..1000_000, int] which is common for interfacing;
# we use the real length here instead
if v.kind in {nkNilLit, nkPtrLit}:
result = sizeof(pointer)
elif v.len != 0:
- result = v.len * packSize(conf, v[0], typ[1])
+ result = v.len * packSize(v.sons[0], typ.sons[1])
else:
- result = getSize(conf, typ).int
+ result = typ.getSize.int
-proc pack(conf: ConfigRef, v: PNode, typ: PType, res: pointer)
+proc pack(v: PNode, typ: PType, res: pointer)
-proc getField(conf: ConfigRef, n: PNode; position: int): PSym =
+proc getField(n: PNode; position: int): PSym =
case n.kind
of nkRecList:
- for i in 0.. maxPackDepth:
packRecCheck = 0
- globalError(conf, v.info, "cannot map value to FFI " & typeToString(v.typ))
+ globalError(v.info, "cannot map value to FFI " & typeToString(v.typ))
inc packRecCheck
- pack(conf, v[0], typ.lastSon, res +! sizeof(pointer))
+ pack(v.sons[0], typ.lastSon, res +! sizeof(pointer))
dec packRecCheck
awr(pointer, res +! sizeof(pointer))
- of tyArray:
- let baseSize = getSize(conf, typ[1])
- for i in 0.. expectedRegularParams + genericParams:
- globalError(conf, n.info, errWrongNumberOfArguments)
-
- if totalParams < genericParams:
- globalError(conf, n.info, errMissingGenericParamsForTemplate %
- n.renderTree)
+ globalError(n.info, errWrongNumberOfArguments)
result = newNodeI(nkArgList, n.info)
- for i in 1..givenRegularParams:
- result.add n[i]
+ for i in 1 .. givenRegularParams:
+ result.addSon n.sons[i]
# handle parameters with default values, which were
# not supplied by the user
- for i in givenRegularParams+1..expectedRegularParams:
- let default = s.typ.n[i].sym.ast
+ for i in givenRegularParams+1 .. expectedRegularParams:
+ let default = s.typ.n.sons[i].sym.ast
if default.isNil or default.kind == nkEmpty:
- localError(conf, n.info, errWrongNumberOfArguments)
- result.add newNodeI(nkEmpty, n.info)
+ localError(n.info, errWrongNumberOfArguments)
+ addSon(result, ast.emptyNode)
else:
- result.add default.copyTree
+ addSon(result, default.copyTree)
- # add any generic parameters
- for i in 1..genericParams:
- result.add n[givenRegularParams + i]
+ # add any generic paramaters
+ for i in 1 .. genericParams:
+ result.addSon n.sons[givenRegularParams + i]
-# to prevent endless recursion in template instantiation
-const evalTemplateLimit* = 1000
+var evalTemplateCounter* = 0
+ # to prevent endless recursion in templates instantiation
-proc wrapInComesFrom*(info: TLineInfo; sym: PSym; res: PNode): PNode =
- when true:
- result = res
- result.info = info
- if result.kind in {nkStmtList, nkStmtListExpr} and result.len > 0:
- result.lastSon.info = info
- when false:
- # this hack is required to
- var x = result
- while x.kind == nkStmtListExpr: x = x.lastSon
- if x.kind in nkCallKinds:
- for i in 1.. evalTemplateLimit:
- globalError(conf, n.info, errTemplateInstantiationTooNested)
+proc evalTemplate*(n: PNode, tmpl, genSymOwner: PSym; fromHlo=false): PNode =
+ inc(evalTemplateCounter)
+ if evalTemplateCounter > 100:
+ globalError(n.info, errTemplateInstantiationTooNested)
result = n
# replace each param by the corresponding node:
- var args = evalTemplateArgs(n, tmpl, conf, fromHlo)
+ var args = evalTemplateArgs(n, tmpl, fromHlo)
var ctx: TemplCtx
ctx.owner = tmpl
ctx.genSymOwner = genSymOwner
- ctx.config = conf
- ctx.ic = ic
initIdTable(ctx.mapping)
- ctx.instID = instID[]
- ctx.idgen = idgen
- let body = tmpl.ast[bodyPos]
- #echo "instantion of ", renderTree(body, {renderIds})
+ let body = tmpl.getBody
if isAtom(body):
result = newNodeI(nkPar, body.info)
evalTemplateAux(body, args, ctx, result)
- if result.len == 1: result = result[0]
+ if result.len == 1: result = result.sons[0]
else:
- localError(conf, result.info, "illformed AST: " &
+ localError(result.info, errIllFormedAstX,
renderTree(result, {renderNoComments}))
else:
result = copyNode(body)
- ctx.instLines = sfCallsite in tmpl.flags
- if ctx.instLines:
- setInfoRecursive(result, n.info)
- for i in 0.. 0:
- flushDot(conf)
- stderr.writeLine(cmd)
+var
+ cCompiler* = ccGcc # the used compiler
+ cAssembler* = ccNone
+ gMixedMode*: bool # true if some module triggered C++ codegen
+ cIncludes*: seq[string] = @[] # directories to search for included files
+ cLibs*: seq[string] = @[] # directories to search for lib files
+ cLinkedLibs*: seq[string] = @[] # libraries to link
+
+const
+ cValidAssemblers* = {asmFasm}
+
+# implementation
+
+proc libNameTmpl(): string {.inline.} =
+ result = if targetOS == osWindows: "$1.lib" else: "lib$1.a"
+
+var
+ toLink, toCompile, externalToCompile: TLinkedList
+ linkOptions: string = ""
+ compileOptions: string = ""
+ ccompilerpath: string = ""
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.
- for i in succ(ccNone)..high(TSystemCC):
+ for i in countup(succ(ccNone), high(TSystemCC)):
if cmpIgnoreStyle(name, CC[i].name) == 0:
return i
result = ccNone
-proc listCCnames(): string =
- result = ""
- for i in succ(ccNone)..high(TSystemCC):
- if i > succ(ccNone): result.add ", "
- result.add CC[i].name
-
-proc isVSCompatible*(conf: ConfigRef): bool =
- return conf.cCompiler == ccVcc or
- conf.cCompiler == ccClangCl or
- (conf.cCompiler == ccIcl and conf.target.hostOS in osDos..osWindows)
-
-proc getConfigVar(conf: ConfigRef; c: TSystemCC, suffix: string): string =
+proc getConfigVar(c: TSystemCC, suffix: string): string =
# use ``cpu.os.cc`` for cross compilation, unless ``--compileOnly`` is given
# for niminst support
- var fullSuffix = suffix
- case conf.backend
- of backendCpp, backendJs, backendObjc: fullSuffix = "." & $conf.backend & suffix
- of backendC: discard
- of backendInvalid:
- # during parsing of cfg files; we don't know the backend yet, no point in
- # guessing wrong thing
- return ""
-
- if (conf.target.hostOS != conf.target.targetOS or conf.target.hostCPU != conf.target.targetCPU) and
- optCompileOnly notin conf.globalOptions:
- let fullCCname = platform.CPU[conf.target.targetCPU].name & '.' &
- platform.OS[conf.target.targetOS].name & '.' &
- CC[c].name & fullSuffix
- result = getConfigVar(conf, fullCCname)
- if existsConfigVar(conf, fullCCname):
- result = getConfigVar(conf, fullCCname)
+ let fullSuffix =
+ if gCmd == cmdCompileToCpp:
+ ".cpp" & suffix
+ elif gCmd == cmdCompileToOC:
+ ".objc" & suffix
+ elif gCmd == cmdCompileToJS:
+ ".js" & suffix
else:
- # not overridden for this cross compilation setting?
- result = getConfigVar(conf, CC[c].name & fullSuffix)
- else:
- result = getConfigVar(conf, CC[c].name & fullSuffix)
+ suffix
-proc setCC*(conf: ConfigRef; ccname: string; info: TLineInfo) =
- conf.cCompiler = nameToCC(ccname)
- if conf.cCompiler == ccNone:
- localError(conf, info, "unknown C compiler: '$1'. Available options are: $2" % [ccname, listCCnames()])
- conf.compileOptions = getConfigVar(conf, conf.cCompiler, ".options.always")
- conf.linkOptions = ""
- conf.cCompilerPath = getConfigVar(conf, conf.cCompiler, ".path")
- for c in CC: undefSymbol(conf.symbols, c.name)
- defineSymbol(conf.symbols, CC[conf.cCompiler].name)
+ if (platform.hostOS != targetOS or platform.hostCPU != targetCPU) and
+ optCompileOnly notin gGlobalOptions:
+ let fullCCname = platform.CPU[targetCPU].name & '.' &
+ platform.OS[targetOS].name & '.' &
+ CC[c].name & fullSuffix
+ result = getConfigVar(fullCCname)
+ if result.len == 0:
+ # not overriden for this cross compilation setting?
+ result = getConfigVar(CC[c].name & fullSuffix)
+ else:
+ result = getConfigVar(CC[c].name & fullSuffix)
+
+proc setCC*(ccname: string) =
+ cCompiler = nameToCC(ccname)
+ if cCompiler == ccNone: rawMessage(errUnknownCcompiler, ccname)
+ compileOptions = getConfigVar(cCompiler, ".options.always")
+ linkOptions = ""
+ ccompilerpath = getConfigVar(cCompiler, ".path")
+ for i in countup(low(CC), high(CC)): undefSymbol(CC[i].name)
+ defineSymbol(CC[cCompiler].name)
proc addOpt(dest: var string, src: string) =
- if dest.len == 0 or dest[^1] != ' ': dest.add(" ")
- dest.add(src)
+ if len(dest) == 0 or dest[len(dest)-1] != ' ': add(dest, " ")
+ add(dest, src)
-proc addLinkOption*(conf: ConfigRef; option: string) =
- addOpt(conf.linkOptions, option)
+proc addLinkOption*(option: string) =
+ addOpt(linkOptions, option)
-proc addCompileOption*(conf: ConfigRef; option: string) =
- if strutils.find(conf.compileOptions, option, 0) < 0:
- addOpt(conf.compileOptions, option)
+proc addCompileOption*(option: string) =
+ if strutils.find(compileOptions, option, 0) < 0:
+ addOpt(compileOptions, option)
-proc addLinkOptionCmd*(conf: ConfigRef; option: string) =
- addOpt(conf.linkOptionsCmd, option)
-
-proc addCompileOptionCmd*(conf: ConfigRef; option: string) =
- conf.compileOptionsCmd.add(option)
-
-proc initVars*(conf: ConfigRef) =
+proc initVars*() =
# we need to define the symbol here, because ``CC`` may have never been set!
- for c in CC: undefSymbol(conf.symbols, c.name)
- defineSymbol(conf.symbols, CC[conf.cCompiler].name)
- addCompileOption(conf, getConfigVar(conf, conf.cCompiler, ".options.always"))
+ for i in countup(low(CC), high(CC)): undefSymbol(CC[i].name)
+ defineSymbol(CC[cCompiler].name)
+ addCompileOption(getConfigVar(cCompiler, ".options.always"))
#addLinkOption(getConfigVar(cCompiler, ".options.linker"))
- if conf.cCompilerPath.len == 0:
- conf.cCompilerPath = getConfigVar(conf, conf.cCompiler, ".path")
+ if len(ccompilerpath) == 0:
+ ccompilerpath = getConfigVar(cCompiler, ".path")
-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 completeCFilePath*(cfile: string, createSubDir: bool = true): string =
+ result = completeGeneratedFilePath(cfile, createSubDir)
-proc toObjFile*(conf: ConfigRef; filename: AbsoluteFile): AbsoluteFile =
+proc toObjFile*(filename: string): string =
# Object file for compilation
- result = AbsoluteFile(filename.string & "." & CC[conf.cCompiler].objExt)
+ result = changeFileExt(filename, CC[cCompiler].objExt)
-proc addFileToCompile*(conf: ConfigRef; cf: Cfile) =
- conf.toCompile.add(cf)
+proc addFileToCompile*(filename: string) =
+ appendStr(toCompile, filename)
-proc addLocalCompileOption*(conf: ConfigRef; option: string; nimfile: AbsoluteFile) =
- let key = completeCfilePath(conf, mangleModuleName(conf, nimfile).AbsoluteFile).string
- var value = conf.cfileSpecificOptions.getOrDefault(key)
- if strutils.find(value, option, 0) < 0:
- addOpt(value, option)
- conf.cfileSpecificOptions[key] = value
-
-proc resetCompilationLists*(conf: ConfigRef) =
- conf.toCompile.setLen 0
+proc resetCompilationLists* =
+ initLinkedList(toCompile)
## XXX: we must associate these with their originating module
# when the module is loaded/unloaded it adds/removes its items
# That's because we still need to hash check the external files
# Maybe we can do that in checkDep on the other hand?
- conf.externalToLink.setLen 0
+ initLinkedList(externalToCompile)
+ initLinkedList(toLink)
-proc addExternalFileToLink*(conf: ConfigRef; filename: AbsoluteFile) =
- conf.externalToLink.insert(filename.string, 0)
+proc addFileToLink*(filename: string) =
+ prependStr(toLink, filename)
+ # BUGFIX: was ``appendStr``
-proc execWithEcho(conf: ConfigRef; cmd: string, msg = hintExecuting): int =
- rawMessage(conf, msg, if msg == hintLinking and not(optListCmd in conf.globalOptions or conf.verbosity > 1): "" else: cmd)
+proc execWithEcho(cmd: string, msg = hintExecuting): int =
+ rawMessage(msg, cmd)
result = execCmd(cmd)
-proc execExternalProgram*(conf: ConfigRef; cmd: string, msg = hintExecuting) =
- if execWithEcho(conf, cmd, msg) != 0:
- rawMessage(conf, errGenerated, "execution of an external program failed: '$1'" %
- cmd)
+proc execExternalProgram*(cmd: string, msg = hintExecuting) =
+ if execWithEcho(cmd, msg) != 0:
+ rawMessage(errExecutionOfProgramFailed, cmd)
-proc generateScript(conf: ConfigRef; script: Rope) =
- let (_, name, _) = splitFile(conf.outFile.string)
- let filename = getNimcacheDir(conf) / RelativeFile(addFileExt("compile_" & name,
- platform.OS[conf.target.targetOS].scriptExt))
- if not writeRope(script, filename):
- rawMessage(conf, errGenerated, "could not write to file: " & filename.string)
+proc generateScript(projectFile: string, script: Rope) =
+ let (dir, name, ext) = splitFile(projectFile)
+ writeRope(script, dir / addFileExt("compile_" & name,
+ platform.OS[targetOS].scriptExt))
-proc getOptSpeed(conf: ConfigRef; c: TSystemCC): string =
- result = getConfigVar(conf, c, ".options.speed")
+proc getOptSpeed(c: TSystemCC): string =
+ result = getConfigVar(c, ".options.speed")
if result == "":
result = CC[c].optSpeed # use default settings from this file
-proc getDebug(conf: ConfigRef; c: TSystemCC): string =
- result = getConfigVar(conf, c, ".options.debug")
+proc getDebug(c: TSystemCC): string =
+ result = getConfigVar(c, ".options.debug")
if result == "":
result = CC[c].debug # use default settings from this file
-proc getOptSize(conf: ConfigRef; c: TSystemCC): string =
- result = getConfigVar(conf, c, ".options.size")
+proc getOptSize(c: TSystemCC): string =
+ result = getConfigVar(c, ".options.size")
if result == "":
result = CC[c].optSize # use default settings from this file
-proc noAbsolutePaths(conf: ConfigRef): bool {.inline.} =
+proc noAbsolutePaths: bool {.inline.} =
# We used to check current OS != specified OS, but this makes no sense
# 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 = gGlobalOptions * {optGenScript, optGenMapping} != {}
-proc cFileSpecificOptions(conf: ConfigRef; nimname, fullNimFile: string): string =
- result = conf.compileOptions
+const
+ specialFileA = 42
+ specialFileB = 42
- for option in conf.compileOptionsCmd:
- if strutils.find(result, option, 0) < 0:
- addOpt(result, option)
+var fileCounter: int
- if optCDebug in conf.globalOptions:
- let key = nimname & ".debug"
- if existsConfigVar(conf, key): addOpt(result, getConfigVar(conf, key))
- else: addOpt(result, getDebug(conf, conf.cCompiler))
- if optOptimizeSpeed in conf.options:
- let key = nimname & ".speed"
- if existsConfigVar(conf, key): addOpt(result, getConfigVar(conf, key))
- else: addOpt(result, getOptSpeed(conf, conf.cCompiler))
- elif optOptimizeSize in conf.options:
- let key = nimname & ".size"
- if existsConfigVar(conf, key): addOpt(result, getConfigVar(conf, key))
- else: addOpt(result, getOptSize(conf, conf.cCompiler))
- let key = nimname & ".always"
- if existsConfigVar(conf, key): addOpt(result, getConfigVar(conf, key))
+proc add(s: var string, many: openArray[string]) =
+ s.add many.join
- addOpt(result, conf.cfileSpecificOptions.getOrDefault(fullNimFile))
+proc cFileSpecificOptions(cfilename: string): string =
+ result = compileOptions
+ var trunk = splitFile(cfilename).name
+ if optCDebug in gGlobalOptions:
+ var key = trunk & ".debug"
+ if existsConfigVar(key): addOpt(result, getConfigVar(key))
+ else: addOpt(result, getDebug(cCompiler))
+ if optOptimizeSpeed in gOptions:
+ var key = trunk & ".speed"
+ if existsConfigVar(key): addOpt(result, getConfigVar(key))
+ else: addOpt(result, getOptSpeed(cCompiler))
+ elif optOptimizeSize in gOptions:
+ var key = trunk & ".size"
+ if existsConfigVar(key): addOpt(result, getConfigVar(key))
+ else: addOpt(result, getOptSize(cCompiler))
+ var key = trunk & ".always"
+ if existsConfigVar(key): addOpt(result, getConfigVar(key))
-proc getCompileOptions(conf: ConfigRef): string =
- result = cFileSpecificOptions(conf, "__dummy__", "__dummy__")
+proc getCompileOptions: string =
+ result = cFileSpecificOptions("__dummy__")
-proc vccplatform(conf: ConfigRef): string =
- # VCC specific but preferable over the config hacks people
- # had to do before, see #11306
- if conf.cCompiler == ccVcc:
- let exe = getConfigVar(conf, conf.cCompiler, ".exe")
- if "vccexe.exe" == extractFilename(exe):
- result = case conf.target.targetCPU
- of cpuI386: " --platform:x86"
- of cpuArm: " --platform:arm"
- of cpuAmd64: " --platform:amd64"
- else: ""
+proc getLinkOptions: string =
+ result = linkOptions
+ for linkedLib in items(cLinkedLibs):
+ result.add(CC[cCompiler].linkLibCmd % linkedLib.quoteShell)
+ for libDir in items(cLibs):
+ result.add([CC[cCompiler].linkDirCmd, libDir.quoteShell])
-proc getLinkOptions(conf: ConfigRef): string =
- result = conf.linkOptions & " " & conf.linkOptionsCmd & " "
- for linkedLib in items(conf.cLinkedLibs):
- result.add(CC[conf.cCompiler].linkLibCmd % linkedLib.quoteShell)
- for libDir in items(conf.cLibs):
- result.add(join([CC[conf.cCompiler].linkDirCmd, libDir.quoteShell]))
+proc needsExeExt(): bool {.inline.} =
+ result = (optGenScript in gGlobalOptions and targetOS == osWindows) or
+ (platform.hostOS == osWindows)
-proc needsExeExt(conf: ConfigRef): bool {.inline.} =
- result = (optGenScript in conf.globalOptions and conf.target.targetOS == osWindows) or
- (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
-
-proc envFlags(conf: ConfigRef): string =
- result = if conf.backend == backendCpp:
- getEnv("CXXFLAGS")
- else:
- getEnv("CFLAGS")
-
-proc getCompilerExe(conf: ConfigRef; compiler: TSystemCC; isCpp: bool): string =
- if compiler == ccEnv:
- result = if isCpp:
- getEnv("CXX")
- else:
- getEnv("CC")
- else:
- result = if isCpp:
- CC[compiler].cppCompiler
- else:
- CC[compiler].compilerExe
+proc getCompilerExe(compiler: TSystemCC): string =
+ result = if gCmd == cmdCompileToCpp: CC[compiler].cppCompiler
+ else: CC[compiler].compilerExe
if result.len == 0:
- rawMessage(conf, errGenerated,
- "Compiler '$1' doesn't support the requested target" %
- CC[compiler].name)
+ rawMessage(errCompilerDoesntSupportTarget, CC[compiler].name)
-proc ccHasSaneOverflow*(conf: ConfigRef): bool =
- if conf.cCompiler == ccGcc:
- 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)
- if exitCode == 0:
- var major: int
- discard parseInt(s, major)
- result = major >= 5
- else:
- result = conf.cCompiler == ccCLang
-
-proc getLinkerExe(conf: ConfigRef; compiler: TSystemCC): string =
+proc getLinkerExe(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 gMixedMode and gCmd != cmdCompileToCpp: CC[compiler].cppCompiler
+ else: compiler.getCompilerExe
-proc getCompileCFileCmd*(conf: ConfigRef; cfile: Cfile,
- isMainFile = false; produceOutput = false): string =
- let
- c = conf.cCompiler
- isCpp = useCpp(conf, cfile.cname)
- # We produce files like module.nim.cpp, so the absolute Nim filename is not
- # cfile.name but `cfile.cname.changeFileExt("")`:
- var options = cFileSpecificOptions(conf, cfile.nimname, cfile.cname.changeFileExt("").string)
- if isCpp:
- # needs to be prepended so that --passc:-std=c++17 can override default.
- # we could avoid allocation by making cFileSpecificOptions inplace
- options = CC[c].cppXsupport & ' ' & options
- # If any C++ file was compiled, we need to use C++ driver for linking as well
- incl conf.globalOptions, optMixedMode
-
- var exe = getConfigVar(conf, c, ".exe")
- if exe.len == 0: exe = getCompilerExe(conf, c, isCpp)
-
- if needsExeExt(conf): exe = addFileExt(exe, "exe")
- if (optGenDynLib in conf.globalOptions or (conf.hcrOn and not isMainFile)) and
- ospNeedsPIC in platform.OS[conf.target.targetOS].props:
- options.add(' ' & CC[c].pic)
-
- if cfile.customArgs != "":
- options.add ' '
- options.add cfile.customArgs
-
- var compilePattern: string
- # compute include paths:
- var includeCmd = CC[c].includeCmd & quoteShell(conf.libpath)
- if not noAbsolutePaths(conf):
- for includeDir in items(conf.cIncludes):
- includeCmd.add(join([CC[c].includeCmd, includeDir.quoteShell]))
-
- compilePattern = joinPath(conf.cCompilerPath, exe)
- else:
- compilePattern = getCompilerExe(conf, c, isCpp)
-
- includeCmd.add(join([CC[c].includeCmd, quoteShell(conf.projectPath.string)]))
-
- let cf = if noAbsolutePaths(conf): AbsoluteFile extractFilename(cfile.cname.string)
- else: cfile.cname
-
- let objfile =
- if cfile.obj.isEmpty:
- if CfileFlag.External notin cfile.flags or noAbsolutePaths(conf):
- toObjFile(conf, cf).string
+proc getCompileCFileCmd*(cfilename: string, isExternal = false): string =
+ var c = cCompiler
+ if cfilename.endswith(".asm"):
+ var customAssembler = getConfigVar("assembler")
+ if customAssembler.len > 0:
+ c = nameToCC(customAssembler)
+ else:
+ if targetCPU == cpuI386 or targetCPU == cpuAmd64:
+ c = asmFasm
else:
- completeCfilePath(conf, toObjFile(conf, cf)).string
- elif noAbsolutePaths(conf):
- extractFilename(cfile.obj.string)
- else:
- cfile.obj.string
+ c = ccNone
- # D files are required by nintendo switch libs for
- # compilation. They are basically a list of all includes.
- let dfile = objfile.changeFileExt(".d").quoteShell
+ if c == ccNone:
+ rawMessage(errExternalAssemblerNotFound, "")
+ elif c notin cValidAssemblers:
+ rawMessage(errExternalAssemblerNotValid, customAssembler)
- let cfsh = quoteShell(cf)
+ var options = cFileSpecificOptions(cfilename)
+ var exe = getConfigVar(c, ".exe")
+ if exe.len == 0: exe = c.getCompilerExe
+
+ if needsExeExt(): exe = addFileExt(exe, "exe")
+ if optGenDynLib in gGlobalOptions and
+ ospNeedsPIC in platform.OS[targetOS].props:
+ add(options, ' ' & CC[c].pic)
+
+ var includeCmd, compilePattern: string
+ if not noAbsolutePaths():
+ # compute include paths:
+ includeCmd = CC[c].includeCmd & quoteShell(libpath)
+
+ for includeDir in items(cIncludes):
+ includeCmd.add([CC[c].includeCmd, includeDir.quoteShell])
+
+ compilePattern = joinPath(ccompilerpath, exe)
+ else:
+ includeCmd = ""
+ compilePattern = c.getCompilerExe
+
+ var cfile = if noAbsolutePaths(): extractFilename(cfilename)
+ else: cfilename
+ var objfile = if not isExternal or noAbsolutePaths():
+ toObjFile(cfile)
+ else:
+ completeCFilePath(toObjFile(cfile))
+ objfile = quoteShell(objfile)
+ cfile = quoteShell(cfile)
result = quoteShell(compilePattern % [
- "dfile", dfile,
- "file", cfsh, "objfile", quoteShell(objfile), "options", options,
- "include", includeCmd, "nim", getPrefixDir(conf).string,
- "lib", conf.libpath.string,
- "ccenvflags", envFlags(conf)])
-
- if optProduceAsm in conf.globalOptions:
- if CC[conf.cCompiler].produceAsm.len > 0:
- let asmfile = objfile.changeFileExt(".asm").quoteShell
- addOpt(result, CC[conf.cCompiler].produceAsm % ["asmfile", asmfile])
- if produceOutput:
- rawMessage(conf, hintUserRaw, "Produced assembler here: " & asmfile)
- else:
- if produceOutput:
- rawMessage(conf, hintUserRaw, "Couldn't produce assembler listing " &
- "for the selected C compiler: " & CC[conf.cCompiler].name)
-
- result.add(' ')
- strutils.addf(result, CC[c].compileTmpl, [
- "dfile", dfile,
- "file", cfsh, "objfile", quoteShell(objfile),
+ "file", cfile, "objfile", objfile, "options", options,
+ "include", includeCmd, "nim", getPrefixDir(),
+ "nim", getPrefixDir(), "lib", libpath])
+ add(result, ' ')
+ addf(result, CC[c].compileTmpl, [
+ "file", cfile, "objfile", objfile,
"options", options, "include", includeCmd,
- "nim", quoteShell(getPrefixDir(conf)),
- "lib", quoteShell(conf.libpath),
- "vccplatform", vccplatform(conf),
- "ccenvflags", envFlags(conf)])
+ "nim", quoteShell(getPrefixDir()),
+ "nim", quoteShell(getPrefixDir()),
+ "lib", quoteShell(libpath)])
-proc footprint(conf: ConfigRef; cfile: Cfile): SecureHash =
+proc footprint(filename: string): SecureHash =
result = secureHash(
- $secureHashFile(cfile.cname.string) &
- platform.OS[conf.target.targetOS].name &
- platform.CPU[conf.target.targetCPU].name &
- extccomp.CC[conf.cCompiler].name &
- getCompileCFileCmd(conf, cfile))
+ $secureHashFile(filename) &
+ platform.OS[targetOS].name &
+ platform.CPU[targetCPU].name &
+ extccomp.CC[extccomp.cCompiler].name &
+ getCompileCFileCmd(filename, true))
-proc externalFileChanged(conf: ConfigRef; cfile: Cfile): bool =
- if conf.backend == backendJs: return false # pre-existing behavior, but not sure it's good
+proc externalFileChanged(filename: string): bool =
+ if gCmd notin {cmdCompileToC, cmdCompileToCpp, cmdCompileToOC, cmdCompileToLLVM}:
+ return false
- let hashFile = toGeneratedFile(conf, conf.mangleModuleName(cfile.cname).AbsoluteFile, "sha1")
- let currentHash = footprint(conf, cfile)
+ var hashFile = toGeneratedFile(filename.withPackageName, "sha1")
+ var currentHash = footprint(filename)
var f: File
- if open(f, hashFile.string, fmRead):
+ if open(f, hashFile, fmRead):
let oldHash = parseSecureHash(f.readLine())
close(f)
result = oldHash != currentHash
else:
result = true
if result:
- if open(f, hashFile.string, fmWrite):
+ if open(f, hashFile, fmWrite):
f.writeLine($currentHash)
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:
- c.flags.incl CfileFlag.Cached
- else:
- # make sure Nim keeps recompiling the external file on reruns
- # if compilation is not successful
- discard tryRemoveFile(c.obj.string)
- conf.toCompile.add(c)
+proc addExternalFileToCompile*(filename: string) =
+ if optForceFullMake in gGlobalOptions or externalFileChanged(filename):
+ appendStr(externalToCompile, filename)
-proc addExternalFileToCompile*(conf: ConfigRef; filename: AbsoluteFile) =
- var c = Cfile(nimname: splitFile(filename).name, cname: filename,
- obj: toObjFile(conf, completeCfilePath(conf, filename, false)),
- flags: {CfileFlag.External})
- addExternalFileToCompile(conf, c)
+proc compileCFile(list: TLinkedList, script: var Rope, cmds: var TStringSeq,
+ prettyCmds: var TStringSeq, isExternal: bool) =
+ var it = PStrEntry(list.head)
+ while it != nil:
+ inc(fileCounter) # call the C compiler for the .c file:
+ var compileCmd = getCompileCFileCmd(it.data, isExternal)
+ if optCompileOnly notin gGlobalOptions:
+ add(cmds, compileCmd)
+ let (dir, name, ext) = splitFile(it.data)
+ add(prettyCmds, "CC: " & name)
+ if optGenScript in gGlobalOptions:
+ add(script, compileCmd)
+ add(script, tnl)
+ it = PStrEntry(it.next)
-proc getLinkCmd(conf: ConfigRef; output: AbsoluteFile,
- objfiles: string, isDllBuild: bool, removeStaticFile: 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)]
- else:
- var linkerExe = getConfigVar(conf, conf.cCompiler, ".linkerexe")
- if linkerExe.len == 0: linkerExe = getLinkerExe(conf, conf.cCompiler)
- # bug #6452: We must not use ``quoteShell`` here for ``linkerExe``
- if needsExeExt(conf): linkerExe = addFileExt(linkerExe, "exe")
- if noAbsolutePaths(conf): result = linkerExe
- else: result = joinPath(conf.cCompilerPath, linkerExe)
- let buildgui = if optGenGuiApp in conf.globalOptions and conf.target.targetOS == osWindows:
- CC[conf.cCompiler].buildGui
- else:
- ""
- let builddll = if isDllBuild: CC[conf.cCompiler].buildDll else: ""
- let exefile = quoteShell(output)
-
- when false:
- if optCDebug in conf.globalOptions:
- writeDebugInfo(exefile.changeFileExt("ndb"))
-
- # Map files are required by Nintendo Switch compilation. They are a list
- # of all function calls in the library and where they come from.
- let mapfile = quoteShell(getNimcacheDir(conf) / RelativeFile(splitFile(output).name & ".map"))
-
- let linkOptions = getLinkOptions(conf) & " " &
- getConfigVar(conf, conf.cCompiler, ".options.linker")
- var linkTmpl = getConfigVar(conf, conf.cCompiler, ".linkTmpl")
- if linkTmpl.len == 0:
- linkTmpl = CC[conf.cCompiler].linkTmpl
- result = quoteShell(result % ["builddll", builddll,
- "mapfile", mapfile,
- "buildgui", buildgui, "options", linkOptions, "objfiles", objfiles,
- "exefile", exefile, "nim", getPrefixDir(conf).string, "lib", conf.libpath.string])
- result.add ' '
- strutils.addf(result, linkTmpl, ["builddll", builddll,
- "mapfile", mapfile,
- "buildgui", buildgui, "options", linkOptions,
- "objfiles", objfiles, "exefile", exefile,
- "nim", quoteShell(getPrefixDir(conf)),
- "lib", quoteShell(conf.libpath),
- "vccplatform", vccplatform(conf)])
- # On windows the debug information for binaries is emitted in a separate .pdb
- # file and the binaries (.dll and .exe) contain a full path to that .pdb file.
- # This is a problem for hot code reloading because even when we copy the .dll
- # and load the copy so the build process may overwrite the original .dll on
- # the disk (windows locks the files of running binaries) the copy still points
- # to the original .pdb (and a simple copy of the .pdb won't help). This is a
- # problem when a debugger is attached to the program we are hot-reloading.
- # This problem is nonexistent on Unix since there by default debug symbols
- # are embedded in the binaries so loading a copy of a .so will be fine. There
- # is the '/Z7' flag for the MSVC compiler to embed the debug info of source
- # files into their respective .obj files but the linker still produces a .pdb
- # when a final .dll or .exe is linked so the debug info isn't embedded.
- # There is also the issue that even when a .dll is unloaded the debugger
- # still keeps the .pdb for that .dll locked. This is a major problem and
- # because of this we cannot just alternate between 2 names for a .pdb file
- # when rebuilding a .dll - instead we need to accumulate differently named
- # .pdb files in the nimcache folder - this is the easiest and most reliable
- # way of being able to debug and rebuild the program at the same time. This
- # is accomplished using the /PDB: flag (there also exists the
- # /PDBALTPATH: flag). The only downside is that the .pdb files are
- # at least 300kb big (when linking statically to the runtime - or else 5mb+)
- # and will quickly accumulate. There is a hacky solution: we could try to
- # delete all .pdb files with a pattern and swallow exceptions.
- #
- # links about .pdb files and hot code reloading:
- # https://ourmachinery.com/post/dll-hot-reloading-in-theory-and-practice/
- # https://ourmachinery.com/post/little-machines-working-together-part-2/
- # https://github.com/fungos/cr
- # https://fungos.github.io/blog/2017/11/20/cr.h-a-simple-c-hot-reload-header-only-library/
- # on forcing the debugger to unlock a locked .pdb of an unloaded library:
- # https://blog.molecular-matters.com/2017/05/09/deleting-pdb-files-locked-by-visual-studio/
- # and a bit about the .pdb format in case that is ever needed:
- # https://github.com/crosire/blink
- # http://www.debuginfo.com/articles/debuginfomatch.html#pdbfiles
- if conf.hcrOn and isVSCompatible(conf):
- let t = now()
- let pdb = output.string & "." & format(t, "MMMM-yyyy-HH-mm-") & $t.nanosecond & ".pdb"
- result.add " /link /PDB:" & pdb
- 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 tryExceptOSErrorMessage(conf: ConfigRef; errorPrefix: string = "", body: untyped) =
- try:
- body
- except OSError:
- let ose = (ref OSError)(getCurrentException())
- if errorPrefix.len > 0:
- rawMessage(conf, errGenerated, errorPrefix & " " & ose.msg & " " & $ose.errorCode)
- else:
- rawMessage(conf, errGenerated, "execution of an external program failed: '$1'" %
- (ose.msg & " " & $ose.errorCode))
- raise
-
-proc getExtraCmds(conf: ConfigRef; output: AbsoluteFile): seq[string] =
- when defined(macosx):
- if optCDebug in conf.globalOptions and optGenStaticLib notin conf.globalOptions:
- # if needed, add an option to skip or override location
- result.add "dsymutil " & $(output).quoteShell
-
-proc execLinkCmd(conf: ConfigRef; linkCmd: string) =
- tryExceptOSErrorMessage(conf, "invocation of external linker program failed."):
- execExternalProgram(conf, linkCmd, hintLinking)
-
-proc execCmdsInParallel(conf: ConfigRef; cmds: seq[string]; prettyCb: proc (idx: int)) =
+proc callCCompiler*(projectfile: string) =
+ var
+ linkCmd, buildgui, builddll: string
+ if gGlobalOptions * {optCompileOnly, optGenScript} == {optCompileOnly}:
+ return # speed up that call if only compiling and no script shall be
+ # generated
+ fileCounter = 0
+ var c = cCompiler
+ var script: Rope = nil
+ var cmds: TStringSeq = @[]
+ var prettyCmds: TStringSeq = @[]
+ let prettyCb = proc (idx: int) =
+ echo prettyCmds[idx]
let runCb = proc (idx: int, p: Process) =
let exitCode = p.peekExitCode
if exitCode != 0:
- rawMessage(conf, errGenerated, "execution of an external compiler program '" &
- cmds[idx] & "' failed with exit code: " & $exitCode & "\n\n")
- if conf.numberOfProcessors == 0: conf.numberOfProcessors = countProcessors()
- var res = 0
- if conf.numberOfProcessors <= 1:
- for i in 0..high(cmds):
- tryExceptOSErrorMessage(conf, "invocation of external compiler program failed."):
- res = execWithEcho(conf, cmds[i])
- if res != 0:
- rawMessage(conf, errGenerated, "execution of an external program failed: '$1'" %
- cmds[i])
- else:
- tryExceptOSErrorMessage(conf, "invocation of external compiler program failed."):
+ rawMessage(errGenerated, "execution of an external compiler program '" &
+ cmds[idx] & "' failed with exit code: " & $exitCode & "\n\n" &
+ p.outputStream.readAll.strip)
+ compileCFile(toCompile, script, cmds, prettyCmds, false)
+ compileCFile(externalToCompile, script, cmds, prettyCmds, true)
+ if optCompileOnly notin gGlobalOptions:
+ if gNumberOfProcessors == 0: gNumberOfProcessors = countProcessors()
+ var res = 0
+ if gNumberOfProcessors <= 1:
+ for i in countup(0, high(cmds)):
+ res = execWithEcho(cmds[i])
+ if res != 0: rawMessage(errExecutionOfProgramFailed, cmds[i])
+ elif optListCmd in gGlobalOptions or gVerbosity > 1:
+ res = execProcesses(cmds, {poEchoCmd, poStdErrToStdOut, poUsePath, poParentStreams},
+ gNumberOfProcessors, afterRunEvent=runCb)
+ elif gVerbosity == 1:
res = execProcesses(cmds, {poStdErrToStdOut, poUsePath, poParentStreams},
- conf.numberOfProcessors, prettyCb, afterRunEvent=runCb)
- if res != 0:
- if conf.numberOfProcessors <= 1:
- rawMessage(conf, errGenerated, "execution of an external program failed: '$1'" %
- cmds.join())
-
-proc linkViaResponseFile(conf: ConfigRef; cmd: string) =
- # Extracting the linker.exe here is a bit hacky but the best solution
- # given ``buildLib``'s design.
- var i = 0
- var last = 0
- if cmd.len > 0 and cmd[0] == '"':
- inc i
- while i < cmd.len and cmd[i] != '"': inc i
- last = i
- inc i
- else:
- while i < cmd.len and cmd[i] != ' ': inc i
- last = i
- while i < cmd.len and cmd[i] == ' ': inc i
- let linkerArgs = conf.projectName & "_" & "linkerArgs.txt"
- let args = cmd.substr(i)
- # GCC's response files don't support backslashes. Junk.
- if conf.cCompiler == ccGcc or conf.cCompiler == ccCLang:
- writeFile(linkerArgs, args.replace('\\', '/'))
- else:
- writeFile(linkerArgs, args)
- try:
- execLinkCmd(conf, cmd.substr(0, last) & " @" & linkerArgs)
- 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
-
-proc hcrLinkTargetName(conf: ConfigRef, objFile: string, isMain = false): AbsoluteFile =
- let basename = splitFile(objFile).name
- let targetName = if isMain: basename & ".exe"
- else: platform.OS[conf.target.targetOS].dllFrmt % basename
- result = conf.getNimcacheDir / RelativeFile(targetName)
-
-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)
+ gNumberOfProcessors, prettyCb, afterRunEvent=runCb)
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)
-
-proc callCCompiler*(conf: ConfigRef) =
- var
- linkCmd: string
- extraCmds: seq[string]
- if conf.globalOptions * {optCompileOnly, optGenScript} == {optCompileOnly}:
- return # speed up that call if only compiling and no script shall be
- # generated
- #var c = cCompiler
- var script: Rope = ""
- var cmds: TStringSeq
- var prettyCmds: TStringSeq
- let prettyCb = proc (idx: int) = writePrettyCmdsStderr(prettyCmds[idx])
-
- for idx, it in conf.toCompile:
- # call the C compiler for the .c file:
- if CfileFlag.Cached in it.flags: continue
- let compileCmd = getCompileCFileCmd(conf, it, idx == conf.toCompile.len - 1, produceOutput=true)
- if optCompileOnly notin conf.globalOptions:
- cmds.add(compileCmd)
- prettyCmds.add displayProgressCC(conf, $it.cname, compileCmd)
- if optGenScript in conf.globalOptions:
- script.add(compileCmd)
- script.add("\n")
-
- if optCompileOnly notin conf.globalOptions:
- execCmdsInParallel(conf, cmds, prettyCb)
- if optNoLinking notin conf.globalOptions:
+ res = execProcesses(cmds, {poStdErrToStdOut, poUsePath, poParentStreams},
+ gNumberOfProcessors, afterRunEvent=runCb)
+ if res != 0:
+ if gNumberOfProcessors <= 1:
+ rawMessage(errExecutionOfProgramFailed, cmds.join())
+ if optNoLinking notin gGlobalOptions:
# call the linker:
+ var it = PStrEntry(toLink.head)
var objfiles = ""
- for it in conf.externalToLink:
- let objFile = if noAbsolutePaths(conf): it.extractFilename else: it
- objfiles.add(' ')
- objfiles.add(quoteShell(
- addFileExt(objFile, CC[conf.cCompiler].objExt)))
+ while it != nil:
+ let objFile = if noAbsolutePaths(): it.data.extractFilename else: it.data
+ add(objfiles, ' ')
+ add(objfiles, quoteShell(
+ addFileExt(objFile, CC[cCompiler].objExt)))
+ it = PStrEntry(it.next)
- if conf.hcrOn: # lets assume that optCompileOnly isn't on
- cmds = @[]
- let mainFileIdx = conf.toCompile.len - 1
- for idx, x in conf.toCompile:
- # don't relink each of the many binaries (one for each source file) if the nim code is
- # cached because that would take too much time for small changes - the only downside to
- # this is that if an external-to-link file changes the final target wouldn't be relinked
- if CfileFlag.Cached in x.flags: continue
- # we pass each object file as if it is the project file - a .dll will be created for each such
- # object file in the nimcache directory, and only in the case of the main project file will
- # there be probably an executable (if the project is such) which will be copied out of the nimcache
- 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))
- # 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):
- for pdb in walkFiles(objFile & ".*.pdb"):
- discard tryRemoveFile(pdb)
- # execute link commands in parallel - output will be a bit different
- # if it fails than that from execLinkCmd() but that doesn't matter
- prettyCmds = map(prettyCmds, proc (curr: string): string = return curr.replace("CC", "Link"))
- execCmdsInParallel(conf, cmds, prettyCb)
- # 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
- copyFileWithPermissions(src.string, dst.string)
+ if optGenStaticLib in gGlobalOptions:
+ let name = splitFile(gProjectName).name
+ linkCmd = CC[c].buildLib % ["libfile", (libNameTmpl() % name),
+ "objfiles", objfiles]
else:
- for x in conf.toCompile:
- let objFile = if noAbsolutePaths(conf): x.obj.extractFilename else: x.obj.string
- 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)
- extraCmds = getExtraCmds(conf, mainOutput)
- if optCompileOnly notin conf.globalOptions:
- preventLinkCmdMaxCmdLen(conf, linkCmd)
- for cmd in extraCmds:
- execExternalProgram(conf, cmd, hintExecuting)
+ var linkerExe = getConfigVar(c, ".linkerexe")
+ if len(linkerExe) == 0: linkerExe = c.getLinkerExe
+ if needsExeExt(): linkerExe = addFileExt(linkerExe, "exe")
+ if noAbsolutePaths(): linkCmd = quoteShell(linkerExe)
+ else: linkCmd = quoteShell(joinPath(ccompilerpath, linkerExe))
+ if optGenGuiApp in gGlobalOptions: buildgui = CC[c].buildGui
+ else: buildgui = ""
+ var exefile: string
+ if optGenDynLib in gGlobalOptions:
+ exefile = platform.OS[targetOS].dllFrmt % splitFile(projectfile).name
+ builddll = CC[c].buildDll
+ else:
+ exefile = splitFile(projectfile).name & platform.OS[targetOS].exeExt
+ builddll = ""
+ if options.outFile.len > 0:
+ exefile = options.outFile.expandTilde
+ if not exefile.isAbsolute():
+ exefile = getCurrentDir() / exefile
+ if not noAbsolutePaths():
+ if not exefile.isAbsolute():
+ exefile = joinPath(splitFile(projectfile).dir, exefile)
+ if optCDebug in gGlobalOptions:
+ writeDebugInfo(exefile.changeFileExt("ndb"))
+ exefile = quoteShell(exefile)
+ let linkOptions = getLinkOptions() & " " &
+ getConfigVar(cCompiler, ".options.linker")
+ linkCmd = quoteShell(linkCmd % ["builddll", builddll,
+ "buildgui", buildgui, "options", linkOptions, "objfiles", objfiles,
+ "exefile", exefile, "nim", getPrefixDir(), "lib", libpath])
+ linkCmd.add ' '
+ addf(linkCmd, CC[c].linkTmpl, ["builddll", builddll,
+ "buildgui", buildgui, "options", linkOptions,
+ "objfiles", objfiles, "exefile", exefile,
+ "nim", quoteShell(getPrefixDir()),
+ "lib", quoteShell(libpath)])
+ if optCompileOnly notin gGlobalOptions:
+ execExternalProgram(linkCmd,
+ if gVerbosity > 1: hintExecuting else: hintLinking)
else:
linkCmd = ""
- if optGenScript in conf.globalOptions:
- script.add(linkCmd)
- script.add("\n")
- generateScript(conf, script)
+ if optGenScript in gGlobalOptions:
+ add(script, linkCmd)
+ add(script, tnl)
+ generateScript(projectfile, script)
-template hashNimExe(): string = $secureHashFile(os.getAppFilename())
+proc genMappingFiles(list: TLinkedList): Rope =
+ var it = PStrEntry(list.head)
+ while it != nil:
+ addf(result, "--file:r\"$1\"$N", [rope(it.data)])
+ it = PStrEntry(it.next)
-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:
- 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)
-
-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)
- 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
-
-proc runJsonBuildInstructions*(conf: ConfigRef; jsonFile: AbsoluteFile) =
- var bcache: BuildCache
- try: bcache.fromJson(jsonFile.string.parseFile)
- except ValueError, KeyError, JsonKindError:
- 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)
-
-proc genMappingFiles(conf: ConfigRef; list: CfileList): Rope =
- for it in list:
- result.addf("--file:r\"$1\"$N", [rope(it.cname.string)])
-
-proc writeMapping*(conf: ConfigRef; symbolMapping: Rope) =
- if optGenMapping notin conf.globalOptions: return
+proc writeMapping*(gSymbolMapping: Rope) =
+ if optGenMapping notin gGlobalOptions: return
var code = rope("[C_Files]\n")
- code.add(genMappingFiles(conf, conf.toCompile))
- code.add("\n[C_Compiler]\nFlags=")
- code.add(strutils.escape(getCompileOptions(conf)))
+ add(code, genMappingFiles(toCompile))
+ add(code, genMappingFiles(externalToCompile))
+ add(code, "\n[C_Compiler]\nFlags=")
+ add(code, strutils.escape(getCompileOptions()))
- code.add("\n[Linker]\nFlags=")
- code.add(strutils.escape(getLinkOptions(conf) & " " &
- getConfigVar(conf, conf.cCompiler, ".options.linker")))
+ add(code, "\n[Linker]\nFlags=")
+ add(code, strutils.escape(getLinkOptions() & " " &
+ getConfigVar(cCompiler, ".options.linker")))
- code.add("\n[Environment]\nlibpath=")
- code.add(strutils.escape(conf.libpath.string))
+ add(code, "\n[Environment]\nlibpath=")
+ add(code, strutils.escape(libpath))
- code.addf("\n[Symbols]$n$1", [symbolMapping])
- let filename = conf.projectPath / RelativeFile"mapping.txt"
- if not writeRope(code, filename):
- rawMessage(conf, errGenerated, "could not write to file: " & filename.string)
+ addf(code, "\n[Symbols]$n$1", [gSymbolMapping])
+ writeRope(code, joinPath(gProjectPath, "mapping.txt"))
diff --git a/compiler/filter_tmpl.nim b/compiler/filter_tmpl.nim
index 84ed99164..9e123e3a1 100644
--- a/compiler/filter_tmpl.nim
+++ b/compiler/filter_tmpl.nim
@@ -10,24 +10,28 @@
# This module implements Nim's standard template filter.
import
- llstream, strutils, ast, msgs, options,
- filters, lineinfos, pathutils
+ llstream, os, wordrecg, idents, strutils, ast, astalgo, msgs, options,
+ renderer, filters
+
+proc filterTmpl*(stdin: PLLStream, filename: string, call: PNode): PLLStream
+ # #! template(subsChar='$', metaChar='#') | standard(version="0.7.2")
+# implementation
type
TParseState = enum
psDirective, psTempl
- TTmplParser = object
+ TTmplParser{.final.} = object
inp: PLLStream
state: TParseState
info: TLineInfo
indent, emitPar: int
x: string # the current input line
- outp: PLLStream # the output will be parsed by parser
+ outp: PLLStream # the ouput will be parsed by pnimsyn
subsChar, nimDirective: char
emit, conc, toStr: string
curly, bracket, par: int
pendingExprLine: bool
- config: ConfigRef
+
const
PatternChars = {'a'..'z', 'A'..'Z', '0'..'9', '\x80'..'\xFF', '.', '_'}
@@ -35,15 +39,16 @@ const
proc newLine(p: var TTmplParser) =
llStreamWrite(p.outp, repeat(')', p.emitPar))
p.emitPar = 0
- if p.info.line > uint16(1): llStreamWrite(p.outp, "\n")
+ if p.info.line > int16(1): llStreamWrite(p.outp, "\n")
if p.pendingExprLine:
llStreamWrite(p.outp, spaces(2))
p.pendingExprLine = false
proc scanPar(p: var TTmplParser, d: int) =
var i = d
- while i < p.x.len:
+ while true:
case p.x[i]
+ of '\0': break
of '(': inc(p.par)
of ')': dec(p.par)
of '[': inc(p.bracket)
@@ -56,48 +61,44 @@ proc scanPar(p: var TTmplParser, d: int) =
proc withInExpr(p: TTmplParser): bool {.inline.} =
result = p.par > 0 or p.bracket > 0 or p.curly > 0
-const
- LineContinuationOprs = {'+', '-', '*', '/', '\\', '<', '>', '^',
- '|', '%', '&', '$', '@', '~', ','}
-
proc parseLine(p: var TTmplParser) =
- var j = 0
- let len = p.x.len
-
- while j < len and p.x[j] == ' ': inc(j)
-
- if len >= 2 and p.x[0] == p.nimDirective and p.x[1] == '?':
+ var
+ d, j, curly: int
+ keyw: string
+ j = 0
+ while p.x[j] == ' ': inc(j)
+ if p.x[0] == p.nimDirective and p.x[1] == '?':
newLine(p)
- elif j < len and p.x[j] == p.nimDirective:
+ elif p.x[j] == p.nimDirective:
newLine(p)
inc(j)
- while j < len and p.x[j] == ' ': inc(j)
- let d = j
- var keyw = ""
- while j < len and p.x[j] in PatternChars:
- keyw.add(p.x[j])
+ while p.x[j] == ' ': inc(j)
+ d = j
+ keyw = ""
+ while p.x[j] in PatternChars:
+ add(keyw, p.x[j])
inc(j)
scanPar(p, j)
- p.pendingExprLine = withInExpr(p) or p.x.endsWith(LineContinuationOprs)
- case keyw
- of "end":
+ p.pendingExprLine = withInExpr(p) or llstream.endsWithOpr(p.x)
+ case whichKeyword(keyw)
+ of wEnd:
if p.indent >= 2:
dec(p.indent, 2)
else:
p.info.col = int16(j)
- localError(p.config, p.info, "'end' does not close a control flow construct")
+ localError(p.info, errXNotAllowedHere, "end")
llStreamWrite(p.outp, spaces(p.indent))
llStreamWrite(p.outp, "#end")
- of "if", "when", "try", "while", "for", "block", "case", "proc", "iterator",
- "converter", "macro", "template", "method", "func":
+ of wIf, wWhen, wTry, wWhile, wFor, wBlock, wCase, wProc, wIterator,
+ wConverter, wMacro, wTemplate, wMethod:
llStreamWrite(p.outp, spaces(p.indent))
llStreamWrite(p.outp, substr(p.x, d))
inc(p.indent, 2)
- of "elif", "of", "else", "except", "finally":
+ of wElif, wOf, wElse, wExcept, wFinally:
llStreamWrite(p.outp, spaces(p.indent - 2))
llStreamWrite(p.outp, substr(p.x, d))
- of "let", "var", "const", "type":
+ of wLet, wVar, wConst, wType:
llStreamWrite(p.outp, spaces(p.indent))
llStreamWrite(p.outp, substr(p.x, d))
if not p.x.contains({':', '='}):
@@ -128,8 +129,10 @@ proc parseLine(p: var TTmplParser) =
llStreamWrite(p.outp, "(\"")
inc(p.emitPar)
p.state = psTempl
- while j < len:
+ while true:
case p.x[j]
+ of '\0':
+ break
of '\x01'..'\x1F', '\x80'..'\xFF':
llStreamWrite(p.outp, "\\x")
llStreamWrite(p.outp, toHex(ord(p.x[j]), 2))
@@ -155,9 +158,12 @@ proc parseLine(p: var TTmplParser) =
llStreamWrite(p.outp, p.toStr)
llStreamWrite(p.outp, '(')
inc(j)
- var curly = 0
- while j < len:
+ curly = 0
+ while true:
case p.x[j]
+ of '\0':
+ localError(p.info, errXExpected, "}")
+ break
of '{':
inc(j)
inc(curly)
@@ -170,9 +176,6 @@ proc parseLine(p: var TTmplParser) =
else:
llStreamWrite(p.outp, p.x[j])
inc(j)
- if curly > 0:
- localError(p.config, p.info, "expected closing '}'")
- break
llStreamWrite(p.outp, ')')
llStreamWrite(p.outp, p.conc)
llStreamWrite(p.outp, '\"')
@@ -181,7 +184,7 @@ proc parseLine(p: var TTmplParser) =
llStreamWrite(p.outp, p.conc)
llStreamWrite(p.outp, p.toStr)
llStreamWrite(p.outp, '(')
- while j < len and p.x[j] in PatternChars:
+ while p.x[j] in PatternChars:
llStreamWrite(p.outp, p.x[j])
inc(j)
llStreamWrite(p.outp, ')')
@@ -193,30 +196,28 @@ proc parseLine(p: var TTmplParser) =
inc(j)
else:
p.info.col = int16(j)
- localError(p.config, p.info, "invalid expression")
+ localError(p.info, errInvalidExpression, "$")
else:
llStreamWrite(p.outp, p.x[j])
inc(j)
llStreamWrite(p.outp, "\\n\"")
-proc filterTmpl*(conf: ConfigRef, stdin: PLLStream, filename: AbsoluteFile,
- call: PNode): PLLStream =
+proc filterTmpl(stdin: PLLStream, filename: string, call: PNode): PLLStream =
var p: TTmplParser
- p.config = conf
- p.info = newLineInfo(conf, filename, 0, 0)
+ p.info = newLineInfo(filename, 0, 0)
p.outp = llStreamOpen("")
p.inp = stdin
- p.subsChar = charArg(conf, call, "subschar", 1, '$')
- p.nimDirective = charArg(conf, call, "metachar", 2, '#')
- p.emit = strArg(conf, call, "emit", 3, "result.add")
- p.conc = strArg(conf, call, "conc", 4, " & ")
- p.toStr = strArg(conf, call, "tostring", 5, "$")
+ p.subsChar = charArg(call, "subschar", 1, '$')
+ p.nimDirective = charArg(call, "metachar", 2, '#')
+ p.emit = strArg(call, "emit", 3, "result.add")
+ p.conc = strArg(call, "conc", 4, " & ")
+ p.toStr = strArg(call, "tostring", 5, "$")
p.x = newStringOfCap(120)
# do not process the first line which contains the directive:
if llStreamReadLine(p.inp, p.x):
- inc p.info.line
+ p.info.line = p.info.line + int16(1)
while llStreamReadLine(p.inp, p.x):
- inc p.info.line
+ p.info.line = p.info.line + int16(1)
parseLine(p)
newLine(p)
result = p.outp
diff --git a/compiler/filters.nim b/compiler/filters.nim
index 8151c0b93..adafe464e 100644
--- a/compiler/filters.nim
+++ b/compiler/filters.nim
@@ -10,61 +10,68 @@
# This module implements Nim's simple filters and helpers for filters.
import
- llstream, idents, strutils, ast, msgs, options,
- renderer, pathutils
+ llstream, os, wordrecg, idents, strutils, ast, astalgo, msgs, options,
+ renderer
-proc invalidPragma(conf: ConfigRef; n: PNode) =
- localError(conf, n.info,
- "'$1' not allowed here" % renderTree(n, {renderNoComments}))
+proc filterReplace*(stdin: PLLStream, filename: string, call: PNode): PLLStream
+proc filterStrip*(stdin: PLLStream, filename: string, call: PNode): PLLStream
+ # helpers to retrieve arguments:
+proc charArg*(n: PNode, name: string, pos: int, default: char): char
+proc strArg*(n: PNode, name: string, pos: int, default: string): string
+proc boolArg*(n: PNode, name: string, pos: int, default: bool): bool
+# implementation
-proc getArg(conf: ConfigRef; n: PNode, name: string, pos: int): PNode =
+proc invalidPragma(n: PNode) =
+ localError(n.info, errXNotAllowedHere, renderTree(n, {renderNoComments}))
+
+proc getArg(n: PNode, name: string, pos: int): PNode =
result = nil
if n.kind in {nkEmpty..nkNilLit}: return
- for i in 1.. 0:
- result[0].setLen(result[0].len - "\n".len)
- result[1] = p.waitForExit
-
-proc opGorge*(cmd, input, cache: string, info: TLineInfo; conf: ConfigRef): (string, int) =
- let workingDir = parentDir(toFullPath(conf, info))
- if cache.len > 0:
- let h = secureHash(cmd & "\t" & input & "\t" & cache)
- let filename = toGeneratedFile(conf, AbsoluteFile("gorge_" & $h), "txt").string
- var f: File
- if optForceFullMake notin conf.globalOptions and open(f, filename):
- result = (f.readAll, 0)
- f.close
- return
- var readSuccessful = false
- try:
- var p = startProcess(cmd, workingDir,
- options={poEvalCommand, poStdErrToStdOut})
- if input.len != 0:
- p.inputStream.write(input)
- p.inputStream.close()
- result = p.readOutput
- p.close()
- readSuccessful = true
- # only cache successful runs:
- 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)
- else:
- try:
- var p = startProcess(cmd, workingDir,
- options={poEvalCommand, poStdErrToStdOut})
- if input.len != 0:
- p.inputStream.write(input)
- p.inputStream.close()
- result = p.readOutput
- p.close()
- except IOError, OSError:
- when defined(nimLegacyGorgeErrors):
- result = ("", -1)
- else:
- result = ("Error running startProcess: " & getCurrentExceptionMsg(), -1)
diff --git a/compiler/guards.nim b/compiler/guards.nim
index 15c6a64e3..5ad932e48 100644
--- a/compiler/guards.nim
+++ b/compiler/guards.nim
@@ -10,24 +10,22 @@
## This module implements the 'implies' relation for guards.
import ast, astalgo, msgs, magicsys, nimsets, trees, types, renderer, idents,
- saturate, modulegraphs, options, lineinfos, int128
-
-when defined(nimPreviewSlimSystem):
- import std/assertions
+ saturate
const
someEq = {mEqI, mEqF64, mEqEnum, mEqCh, mEqB, mEqRef, mEqProc,
- mEqStr, mEqSet, mEqCString}
+ mEqUntracedRef, mEqStr, mEqSet, mEqCString}
# set excluded here as the semantics are vastly different:
- someLe = {mLeI, mLeF64, mLeU, mLeEnum,
+ someLe = {mLeI, mLeF64, mLeU, mLeU64, mLeEnum,
mLeCh, mLeB, mLePtr, mLeStr}
- someLt = {mLtI, mLtF64, mLtU, mLtEnum,
+ someLt = {mLtI, mLtF64, mLtU, mLtU64, mLtEnum,
mLtCh, mLtB, mLtPtr, mLtStr}
- someLen = {mLengthOpenArray, mLengthStr, mLengthArray, mLengthSeq}
+ someLen = {mLengthOpenArray, mLengthStr, mLengthArray, mLengthSeq,
+ mXLenStr, mXLenSeq}
- someIn = {mInSet}
+ someIn = {mInRange, mInSet}
someHigh = {mHigh}
# we don't list unsigned here because wrap around semantics suck for
@@ -37,8 +35,8 @@ const
someMul = {mMulI, mMulF64}
someDiv = {mDivI, mDivF64}
someMod = {mModI}
- someMax = {mMaxI}
- someMin = {mMinI}
+ someMax = {mMaxI, mMaxF64}
+ someMin = {mMinI, mMinF64}
someBinaryOp = someAdd+someSub+someMul+someMax+someMin
proc isValue(n: PNode): bool = n.kind in {nkCharLit..nkNilLit}
@@ -49,12 +47,12 @@ proc isLet(n: PNode): bool =
if n.sym.kind in {skLet, skTemp, skForVar}:
result = true
elif n.sym.kind == skParam and skipTypes(n.sym.typ,
- abstractInst).kind notin {tyVar}:
+ abstractInst).kind != tyVar:
result = true
proc isVar(n: PNode): bool =
n.kind == nkSym and n.sym.kind in {skResult, skVar} and
- {sfAddrTaken} * n.sym.flags == {}
+ {sfGlobal, sfAddrTaken} * n.sym.flags == {}
proc isLetLocation(m: PNode, isApprox: bool): bool =
# consider: 'n[].kind' --> we really need to support 1 deref op even if this
@@ -67,19 +65,16 @@ proc isLetLocation(m: PNode, isApprox: bool): bool =
while true:
case n.kind
of nkDotExpr, nkCheckedFieldExpr, nkObjUpConv, nkObjDownConv:
- n = n[0]
- of nkDerefExpr:
- n = n[0]
+ n = n.sons[0]
+ of nkDerefExpr, nkHiddenDeref:
+ n = n.sons[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]
+ if isConstExpr(n.sons[1]) or isLet(n.sons[1]):
+ n = n.sons[0]
else: return
of nkHiddenStdConv, nkHiddenSubConv, nkConv:
- n = n[1]
+ n = n.sons[1]
else:
break
result = n.isLet and derefs <= ord(isApprox)
@@ -88,36 +83,49 @@ proc isLetLocation(m: PNode, isApprox: bool): bool =
proc interestingCaseExpr*(m: PNode): bool = isLetLocation(m, true)
+let
+ opLe = createMagic("<=", mLeI)
+ opLt = createMagic("<", mLtI)
+ opAnd = createMagic("and", mAnd)
+ opOr = createMagic("or", mOr)
+ opIsNil = createMagic("isnil", mIsNil)
+ opEq = createMagic("==", mEqI)
+ opAdd = createMagic("+", mAddI)
+ opSub = createMagic("-", mSubI)
+ opMul = createMagic("*", mMulI)
+ opDiv = createMagic("div", mDivI)
+ opLen = createMagic("len", mLengthSeq)
+
proc swapArgs(fact: PNode, newOp: PSym): PNode =
result = newNodeI(nkCall, fact.info, 3)
- result[0] = newSymNode(newOp)
- result[1] = fact[2]
- result[2] = fact[1]
+ result.sons[0] = newSymNode(newOp)
+ result.sons[1] = fact.sons[2]
+ result.sons[2] = fact.sons[1]
-proc neg(n: PNode; o: Operators): PNode =
+proc neg(n: PNode): PNode =
if n == nil: return nil
case n.getMagic
of mNot:
- result = n[1]
+ result = n.sons[1]
of someLt:
# not (a < b) == a >= b == b <= a
- result = swapArgs(n, o.opLe)
+ result = swapArgs(n, opLe)
of someLe:
- result = swapArgs(n, o.opLt)
+ result = swapArgs(n, opLt)
of mInSet:
- if n[1].kind != nkCurly: return nil
- let t = n[2].typ.skipTypes(abstractInst)
+ if n.sons[1].kind != nkCurly: return nil
+ let t = n.sons[2].typ.skipTypes(abstractInst)
result = newNodeI(nkCall, n.info, 3)
- result[0] = n[0]
- result[2] = n[2]
+ result.sons[0] = n.sons[0]
+ result.sons[2] = n.sons[2]
if t.kind == tyEnum:
- var s = newNodeIT(nkCurly, n.info, n[1].typ)
+ var s = newNodeIT(nkCurly, n.info, n.sons[1].typ)
for e in t.n:
let eAsNode = newIntNode(nkIntLit, e.sym.position)
- if not inSet(n[1], eAsNode): s.add eAsNode
- result[1] = s
- #elif t.kind notin {tyString, tySequence} and lengthOrd(t) < 1000:
- # result[1] = complement(n[1])
+ if not inSet(n.sons[1], eAsNode): s.add eAsNode
+ result.sons[1] = s
+ elif t.kind notin {tyString, tySequence} and lengthOrd(t) < 1000:
+ result.sons[1] = complement(n.sons[1])
else:
# not ({2, 3, 4}.contains(x)) x != 2 and x != 3 and x != 4
# XXX todo
@@ -125,13 +133,13 @@ proc neg(n: PNode; o: Operators): PNode =
of mOr:
# not (a or b) --> not a and not b
let
- a = n[1].neg(o)
- b = n[2].neg(o)
+ a = n.sons[1].neg
+ b = n.sons[2].neg
if a != nil and b != nil:
result = newNodeI(nkCall, n.info, 3)
- result[0] = newSymNode(o.opAnd)
- result[1] = a
- result[2] = b
+ result.sons[0] = newSymNode(opAnd)
+ result.sons[1] = a
+ result.sons[2] = b
elif a != nil:
result = a
elif b != nil:
@@ -139,19 +147,19 @@ proc neg(n: PNode; o: Operators): PNode =
else:
# leave not (a == 4) as it is
result = newNodeI(nkCall, n.info, 2)
- result[0] = newSymNode(o.opNot)
- result[1] = n
+ result.sons[0] = newSymNode(opNot)
+ result.sons[1] = n
-proc buildCall*(op: PSym; a: PNode): PNode =
+proc buildCall(op: PSym; a: PNode): PNode =
result = newNodeI(nkCall, a.info, 2)
- result[0] = newSymNode(op)
- result[1] = a
+ result.sons[0] = newSymNode(op)
+ result.sons[1] = a
-proc buildCall*(op: PSym; a, b: PNode): PNode =
+proc buildCall(op: PSym; a, b: PNode): PNode =
result = newNodeI(nkInfix, a.info, 3)
- result[0] = newSymNode(op)
- result[1] = a
- result[2] = b
+ result.sons[0] = newSymNode(op)
+ result.sons[1] = a
+ result.sons[2] = b
proc `|+|`(a, b: PNode): PNode =
result = copyNode(a)
@@ -173,7 +181,7 @@ proc `|div|`(a, b: PNode): PNode =
if a.kind in {nkCharLit..nkUInt64Lit}: result.intVal = a.intVal div b.intVal
else: result.floatVal = a.floatVal / b.floatVal
-proc negate(a, b, res: PNode; o: Operators): PNode =
+proc negate(a, b, res: PNode): PNode =
if b.kind in {nkCharLit..nkUInt64Lit} and b.intVal != low(BiggestInt):
var b = copyNode(b)
b.intVal = -b.intVal
@@ -181,11 +189,11 @@ proc negate(a, b, res: PNode; o: Operators): PNode =
b.intVal = b.intVal |+| a.intVal
result = b
else:
- result = buildCall(o.opAdd, a, b)
+ result = buildCall(opAdd, a, b)
elif b.kind in {nkFloatLit..nkFloat64Lit}:
var b = copyNode(b)
b.floatVal = -b.floatVal
- result = buildCall(o.opAdd, a, b)
+ result = buildCall(opAdd, a, b)
else:
result = res
@@ -193,35 +201,35 @@ proc zero(): PNode = nkIntLit.newIntNode(0)
proc one(): PNode = nkIntLit.newIntNode(1)
proc minusOne(): PNode = nkIntLit.newIntNode(-1)
-proc lowBound*(conf: ConfigRef; x: PNode): PNode =
- result = nkIntLit.newIntNode(firstOrd(conf, x.typ))
+proc lowBound*(x: PNode): PNode =
+ result = nkIntLit.newIntNode(firstOrd(x.typ))
result.info = x.info
-proc highBound*(conf: ConfigRef; x: PNode; o: Operators): PNode =
+proc highBound*(x: PNode): PNode =
let typ = x.typ.skipTypes(abstractInst)
- result = if typ.kind == tyArray:
- nkIntLit.newIntNode(lastOrd(conf, typ))
+ result = if typ.kind in {tyArrayConstr, tyArray}:
+ nkIntLit.newIntNode(lastOrd(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())
+ opAdd.buildCall(opLen.buildCall(x), minusOne())
result.info = x.info
-proc reassociation(n: PNode; o: Operators): PNode =
+proc reassociation(n: PNode): PNode =
result = n
# (foo+5)+5 --> foo+10; same for '*'
case result.getMagic
of someAdd:
if result[2].isValue and
result[1].getMagic in someAdd and result[1][2].isValue:
- result = o.opAdd.buildCall(result[1][1], result[1][2] |+| result[2])
+ result = opAdd.buildCall(result[1][1], result[1][2] |+| result[2])
if result[2].intVal == 0:
result = result[1]
of someMul:
if result[2].isValue and
result[1].getMagic in someMul and result[1][2].isValue:
- result = o.opMul.buildCall(result[1][1], result[1][2] |*| result[2])
+ result = opMul.buildCall(result[1][1], result[1][2] |*| result[2])
if result[2].intVal == 1:
result = result[1]
elif result[2].intVal == 0:
@@ -235,44 +243,44 @@ proc pred(n: PNode): PNode =
else:
result = n
-proc buildLe*(o: Operators; a, b: PNode): PNode =
- result = o.opLe.buildCall(a, b)
-
-proc canon*(n: PNode; o: Operators): PNode =
+proc canon*(n: PNode): PNode =
+ # XXX for now only the new code in 'semparallel' uses this
if n.safeLen >= 1:
result = shallowCopy(n)
- for i in 0.. (foo + 4) + 2
of someHigh:
# high == len+(-1)
- result = o.opAdd.buildCall(o.opLen.buildCall(result[1]), minusOne())
+ result = opAdd.buildCall(opLen.buildCall(result[1]), minusOne())
+ of mUnaryLt:
+ result = buildCall(opAdd, result[1], minusOne())
of someSub:
# x - 4 --> x + (-4)
- result = negate(result[1], result[2], result, o)
+ result = negate(result[1], result[2], result)
of someLen:
- result[0] = o.opLen.newSymNode
- of someLt - {mLtF64}:
+ result.sons[0] = opLen.newSymNode
+ of someLt:
# x < y same as x <= y-1:
- let y = n[2].canon(o)
+ let y = n[2].canon
let p = pred(y)
- let minus = if p != y: p else: o.opAdd.buildCall(y, minusOne()).canon(o)
- result = o.opLe.buildCall(n[1].canon(o), minus)
+ let minus = if p != y: p else: opAdd.buildCall(y, minusOne()).canon
+ result = opLe.buildCall(n[1].canon, minus)
else: discard
result = skipConv(result)
- result = reassociation(result, o)
+ result = reassociation(result)
# most important rule: (x-4) <= a.len --> x <= a.len+4
case result.getMagic
of someLe:
@@ -283,10 +291,10 @@ proc canon*(n: PNode; o: Operators): PNode =
case x.getMagic
of someSub:
result = buildCall(result[0].sym, x[1],
- reassociation(o.opAdd.buildCall(y, x[2]), o))
+ reassociation(opAdd.buildCall(y, x[2])))
of someAdd:
# Rule A:
- let plus = negate(y, x[2], nil, o).reassociation(o)
+ let plus = negate(y, x[2], nil).reassociation
if plus != nil: result = buildCall(result[0].sym, x[1], plus)
else: discard
elif y.kind in nkCallKinds and y.len == 3 and y[2].isValue and
@@ -295,71 +303,69 @@ proc canon*(n: PNode; o: Operators): PNode =
case y.getMagic
of someSub:
result = buildCall(result[0].sym, y[1],
- reassociation(o.opAdd.buildCall(x, y[2]), o))
+ reassociation(opAdd.buildCall(x, y[2])))
of someAdd:
- let plus = negate(x, y[2], nil, o).reassociation(o)
+ let plus = negate(x, y[2], nil).reassociation
# ensure that Rule A will not trigger afterwards with the
# additional 'not isLetLocation' constraint:
if plus != nil and not isLetLocation(x, true):
result = buildCall(result[0].sym, plus, y[1])
else: discard
- elif x.isValue and y.getMagic in someAdd and y[2].kind == x.kind:
+ elif x.isValue and y.getMagic in someAdd and y[2].isValue:
# 0 <= a.len + 3
# -3 <= a.len
- result[1] = x |-| y[2]
- result[2] = y[1]
- elif x.isValue and y.getMagic in someSub and y[2].kind == x.kind:
+ result.sons[1] = x |-| y[2]
+ result.sons[2] = y[1]
+ elif x.isValue and y.getMagic in someSub and y[2].isValue:
# 0 <= a.len - 3
# 3 <= a.len
- result[1] = x |+| y[2]
- result[2] = y[1]
+ result.sons[1] = x |+| y[2]
+ result.sons[2] = y[1]
else: discard
-proc buildAdd*(a: PNode; b: BiggestInt; o: Operators): PNode =
- canon(if b != 0: o.opAdd.buildCall(a, nkIntLit.newIntNode(b)) else: a, o)
+proc `+@`*(a: PNode; b: BiggestInt): PNode =
+ canon(if b != 0: opAdd.buildCall(a, nkIntLit.newIntNode(b)) else: a)
-proc usefulFact(n: PNode; o: Operators): PNode =
+proc usefulFact(n: PNode): PNode =
case n.getMagic
of someEq:
- if skipConv(n[2]).kind == nkNilLit and (
- isLetLocation(n[1], false) or isVar(n[1])):
- result = o.opIsNil.buildCall(n[1])
+ if skipConv(n.sons[2]).kind == nkNilLit and (
+ isLetLocation(n.sons[1], false) or isVar(n.sons[1])):
+ result = opIsNil.buildCall(n.sons[1])
else:
- if isLetLocation(n[1], true) or isLetLocation(n[2], true):
+ if isLetLocation(n.sons[1], true) or isLetLocation(n.sons[2], true):
# XXX algebraic simplifications! 'i-1 < a.len' --> 'i < a.len+1'
result = n
- elif n[1].getMagic in someLen or n[2].getMagic in someLen:
- result = n
of someLe+someLt:
- if isLetLocation(n[1], true) or isLetLocation(n[2], true):
+ if isLetLocation(n.sons[1], true) or isLetLocation(n.sons[2], true):
# XXX algebraic simplifications! 'i-1 < a.len' --> 'i < a.len+1'
result = n
elif n[1].getMagic in someLen or n[2].getMagic in someLen:
# XXX Rethink this whole idea of 'usefulFact' for semparallel
result = n
of mIsNil:
- if isLetLocation(n[1], false) or isVar(n[1]):
+ if isLetLocation(n.sons[1], false) or isVar(n.sons[1]):
result = n
of someIn:
- if isLetLocation(n[1], true):
+ if isLetLocation(n.sons[1], true):
result = n
of mAnd:
let
- a = usefulFact(n[1], o)
- b = usefulFact(n[2], o)
+ a = usefulFact(n.sons[1])
+ b = usefulFact(n.sons[2])
if a != nil and b != nil:
result = newNodeI(nkCall, n.info, 3)
- result[0] = newSymNode(o.opAnd)
- result[1] = a
- result[2] = b
+ result.sons[0] = newSymNode(opAnd)
+ result.sons[1] = a
+ result.sons[2] = b
elif a != nil:
result = a
elif b != nil:
result = b
of mNot:
- let a = usefulFact(n[1], o)
+ let a = usefulFact(n.sons[1])
if a != nil:
- result = a.neg(o)
+ result = a.neg
of mOr:
# 'or' sucks! (p.isNil or q.isNil) --> hard to do anything
# with that knowledge...
@@ -368,59 +374,47 @@ proc usefulFact(n: PNode; o: Operators): PNode =
# (x == 3) or (y == 2) ---> not ( not (x==3) and not (y == 2))
# not (x != 3 and y != 2)
let
- a = usefulFact(n[1], o).neg(o)
- b = usefulFact(n[2], o).neg(o)
+ a = usefulFact(n.sons[1]).neg
+ b = usefulFact(n.sons[2]).neg
if a != nil and b != nil:
result = newNodeI(nkCall, n.info, 3)
- result[0] = newSymNode(o.opAnd)
- result[1] = a
- result[2] = b
- result = result.neg(o)
+ result.sons[0] = newSymNode(opAnd)
+ result.sons[1] = a
+ result.sons[2] = b
+ result = result.neg
elif n.kind == nkSym and n.sym.kind == skLet:
# consider:
# let a = 2 < x
# if a:
# ...
# We make can easily replace 'a' by '2 < x' here:
- if n.sym.astdef != nil:
- result = usefulFact(n.sym.astdef, o)
+ if n.sym.ast != nil:
+ result = usefulFact(n.sym.ast)
elif n.kind == nkStmtListExpr:
- result = usefulFact(n.lastSon, o)
+ result = usefulFact(n.lastSon)
type
- TModel* = object
- s*: seq[PNode] # the "knowledge base"
- g*: ModuleGraph
- beSmart*: bool
+ TModel* = seq[PNode] # the "knowledge base"
proc addFact*(m: var TModel, nn: PNode) =
- let n = usefulFact(nn, m.g.operators)
- if n != nil:
- if not m.beSmart:
- m.s.add n
- else:
- let c = canon(n, m.g.operators)
- if c.getMagic == mAnd:
- addFact(m, c[1])
- addFact(m, c[2])
- else:
- m.s.add c
+ let n = usefulFact(nn)
+ if n != nil: m.add n
proc addFactNeg*(m: var TModel, n: PNode) =
- let n = n.neg(m.g.operators)
+ let n = n.neg
if n != nil: addFact(m, n)
-proc sameOpr(a, b: PSym): bool =
- case a.magic
- of someEq: result = b.magic in someEq
- of someLe: result = b.magic in someLe
- of someLt: result = b.magic in someLt
- of someLen: result = b.magic in someLen
- of someAdd: result = b.magic in someAdd
- of someSub: result = b.magic in someSub
- of someMul: result = b.magic in someMul
- of someDiv: result = b.magic in someDiv
- else: result = a == b
+proc canonOpr(opr: PSym): PSym =
+ case opr.magic
+ of someEq: result = opEq
+ of someLe: result = opLe
+ of someLt: result = opLt
+ of someLen: result = opLen
+ of someAdd: result = opAdd
+ of someSub: result = opSub
+ of someMul: result = opMul
+ of someDiv: result = opDiv
+ else: result = opr
proc sameTree*(a, b: PNode): bool =
result = false
@@ -431,32 +425,26 @@ proc sameTree*(a, b: PNode): bool =
of nkSym:
result = a.sym == b.sym
if not result and a.sym.magic != mNone:
- result = a.sym.magic == b.sym.magic or sameOpr(a.sym, b.sym)
+ result = a.sym.magic == b.sym.magic or canonOpr(a.sym) == canonOpr(b.sym)
of nkIdent: result = a.ident.id == b.ident.id
- of nkCharLit..nkUInt64Lit: result = a.intVal == b.intVal
+ of nkCharLit..nkInt64Lit: result = a.intVal == b.intVal
of nkFloatLit..nkFloat64Lit: result = a.floatVal == b.floatVal
of nkStrLit..nkTripleStrLit: result = a.strVal == b.strVal
of nkType: result = a.typ == b.typ
of nkEmpty, nkNilLit: result = true
else:
- if a.len == b.len:
- for i in 0.. unknown!
- if sameTree(fact[2], eq[val]): result = impYes
- elif valuesUnequal(fact[2], eq[val]): result = impNo
- elif sameTree(fact[2], eq[loc]):
- if sameTree(fact[1], eq[val]): result = impYes
- elif valuesUnequal(fact[1], eq[val]): result = impNo
+ if sameTree(fact.sons[2], eq.sons[val]): result = impYes
+ elif valuesUnequal(fact.sons[2], eq.sons[val]): result = impNo
+ elif sameTree(fact.sons[2], eq.sons[loc]):
+ if sameTree(fact.sons[1], eq.sons[val]): result = impYes
+ elif valuesUnequal(fact.sons[1], eq.sons[val]): result = impNo
of mInSet:
# remember: mInSet is 'contains' so the set comes first!
- if sameTree(fact[2], eq[loc]) and isValue(eq[val]):
- if inSet(fact[1], eq[val]): result = impYes
+ if sameTree(fact.sons[2], eq.sons[loc]) and isValue(eq.sons[val]):
+ if inSet(fact.sons[1], eq.sons[val]): result = impYes
else: result = impNo
- of mNot, mOr, mAnd: assert(false, "impliesEq")
+ of mNot, mOr, mAnd: internalError(eq.info, "impliesEq")
else: discard
proc leImpliesIn(x, c, aSet: PNode): TImplication =
@@ -509,7 +494,7 @@ proc leImpliesIn(x, c, aSet: PNode): TImplication =
# fact: x <= 4; question x in {56}?
# --> true if every value <= 4 is in the set {56}
#
- var value = newIntNode(c.kind, firstOrd(nil, x.typ))
+ var value = newIntNode(c.kind, firstOrd(x.typ))
# don't iterate too often:
if c.intVal - value.intVal < 1000:
var i, pos, neg: int
@@ -526,9 +511,9 @@ proc geImpliesIn(x, c, aSet: PNode): TImplication =
# --> true iff every value >= 4 is in the set {56}
#
var value = newIntNode(c.kind, c.intVal)
- let max = lastOrd(nil, x.typ)
+ let max = lastOrd(x.typ)
# don't iterate too often:
- if max - getInt(value) < toInt128(1000):
+ if max - value.intVal < 1000:
var i, pos, neg: int
while value.intVal <= max:
if inSet(aSet, value): inc pos
@@ -538,33 +523,33 @@ proc geImpliesIn(x, c, aSet: PNode): TImplication =
elif neg == i: result = impNo
proc compareSets(a, b: PNode): TImplication =
- if equalSets(nil, a, b): result = impYes
- elif intersectSets(nil, a, b).len == 0: result = impNo
+ if equalSets(a, b): result = impYes
+ elif intersectSets(a, b).len == 0: result = impNo
proc impliesIn(fact, loc, aSet: PNode): TImplication =
- case fact[0].sym.magic
+ case fact.sons[0].sym.magic
of someEq:
- if sameTree(fact[1], loc):
- if inSet(aSet, fact[2]): result = impYes
+ if sameTree(fact.sons[1], loc):
+ if inSet(aSet, fact.sons[2]): result = impYes
else: result = impNo
- elif sameTree(fact[2], loc):
- if inSet(aSet, fact[1]): result = impYes
+ elif sameTree(fact.sons[2], loc):
+ if inSet(aSet, fact.sons[1]): result = impYes
else: result = impNo
of mInSet:
- if sameTree(fact[2], loc):
- result = compareSets(fact[1], aSet)
+ if sameTree(fact.sons[2], loc):
+ result = compareSets(fact.sons[1], aSet)
of someLe:
- if sameTree(fact[1], loc):
- result = leImpliesIn(fact[1], fact[2], aSet)
- elif sameTree(fact[2], loc):
- result = geImpliesIn(fact[2], fact[1], aSet)
+ if sameTree(fact.sons[1], loc):
+ result = leImpliesIn(fact.sons[1], fact.sons[2], aSet)
+ elif sameTree(fact.sons[2], loc):
+ result = geImpliesIn(fact.sons[2], fact.sons[1], aSet)
of someLt:
- if sameTree(fact[1], loc):
- result = leImpliesIn(fact[1], fact[2].pred, aSet)
- elif sameTree(fact[2], loc):
+ if sameTree(fact.sons[1], loc):
+ result = leImpliesIn(fact.sons[1], fact.sons[2].pred, aSet)
+ elif sameTree(fact.sons[2], loc):
# 4 < x --> 3 <= x
- result = geImpliesIn(fact[2], fact[1].pred, aSet)
- of mNot, mOr, mAnd: assert(false, "impliesIn")
+ result = geImpliesIn(fact.sons[2], fact.sons[1].pred, aSet)
+ of mNot, mOr, mAnd: internalError(loc.info, "impliesIn")
else: discard
proc valueIsNil(n: PNode): TImplication =
@@ -573,95 +558,92 @@ proc valueIsNil(n: PNode): TImplication =
else: impUnknown
proc impliesIsNil(fact, eq: PNode): TImplication =
- case fact[0].sym.magic
+ case fact.sons[0].sym.magic
of mIsNil:
- if sameTree(fact[1], eq[1]):
+ if sameTree(fact.sons[1], eq.sons[1]):
result = impYes
of someEq:
- if sameTree(fact[1], eq[1]):
- result = valueIsNil(fact[2].skipConv)
- elif sameTree(fact[2], eq[1]):
- result = valueIsNil(fact[1].skipConv)
- of mNot, mOr, mAnd: assert(false, "impliesIsNil")
+ if sameTree(fact.sons[1], eq.sons[1]):
+ result = valueIsNil(fact.sons[2].skipConv)
+ elif sameTree(fact.sons[2], eq.sons[1]):
+ result = valueIsNil(fact.sons[1].skipConv)
+ of mNot, mOr, mAnd: internalError(eq.info, "impliesIsNil")
else: discard
proc impliesGe(fact, x, c: PNode): TImplication =
- assert isLocation(x)
- case fact[0].sym.magic
+ internalAssert isLocation(x)
+ case fact.sons[0].sym.magic
of someEq:
- if sameTree(fact[1], x):
- if isValue(fact[2]) and isValue(c):
+ if sameTree(fact.sons[1], x):
+ if isValue(fact.sons[2]) and isValue(c):
# fact: x = 4; question x >= 56? --> true iff 4 >= 56
- if leValue(c, fact[2]): result = impYes
+ if leValue(c, fact.sons[2]): result = impYes
else: result = impNo
- elif sameTree(fact[2], x):
- if isValue(fact[1]) and isValue(c):
- if leValue(c, fact[1]): result = impYes
+ elif sameTree(fact.sons[2], x):
+ if isValue(fact.sons[1]) and isValue(c):
+ if leValue(c, fact.sons[1]): result = impYes
else: result = impNo
of someLt:
- if sameTree(fact[1], x):
- if isValue(fact[2]) and isValue(c):
+ if sameTree(fact.sons[1], x):
+ if isValue(fact.sons[2]) and isValue(c):
# fact: x < 4; question N <= x? --> false iff N <= 4
- if leValue(fact[2], c): result = impNo
+ if leValue(fact.sons[2], c): result = impNo
# fact: x < 4; question 2 <= x? --> we don't know
- elif sameTree(fact[2], x):
+ elif sameTree(fact.sons[2], x):
# fact: 3 < x; question: N-1 < x ? --> true iff N-1 <= 3
- if isValue(fact[1]) and isValue(c):
- if leValue(c.pred, fact[1]): result = impYes
+ if isValue(fact.sons[1]) and isValue(c):
+ if leValue(c.pred, fact.sons[1]): result = impYes
of someLe:
- if sameTree(fact[1], x):
- if isValue(fact[2]) and isValue(c):
+ if sameTree(fact.sons[1], x):
+ if isValue(fact.sons[2]) and isValue(c):
# fact: x <= 4; question x >= 56? --> false iff 4 <= 56
- if leValue(fact[2], c): result = impNo
+ if leValue(fact.sons[2], c): result = impNo
# fact: x <= 4; question x >= 2? --> we don't know
- elif sameTree(fact[2], x):
+ elif sameTree(fact.sons[2], x):
# fact: 3 <= x; question: x >= 2 ? --> true iff 2 <= 3
- if isValue(fact[1]) and isValue(c):
- if leValue(c, fact[1]): result = impYes
- of mNot, mOr, mAnd: assert(false, "impliesGe")
+ if isValue(fact.sons[1]) and isValue(c):
+ if leValue(c, fact.sons[1]): result = impYes
+ of mNot, mOr, mAnd: internalError(x.info, "impliesGe")
else: discard
proc impliesLe(fact, x, c: PNode): TImplication =
if not isLocation(x):
- if c.isValue:
- if leValue(x, x): return impYes
- else: return impNo
return impliesGe(fact, c, x)
- case fact[0].sym.magic
+ case fact.sons[0].sym.magic
of someEq:
- if sameTree(fact[1], x):
- if isValue(fact[2]) and isValue(c):
+ if sameTree(fact.sons[1], x):
+ if isValue(fact.sons[2]) and isValue(c):
# fact: x = 4; question x <= 56? --> true iff 4 <= 56
- if leValue(fact[2], c): result = impYes
+ if leValue(fact.sons[2], c): result = impYes
else: result = impNo
- elif sameTree(fact[2], x):
- if isValue(fact[1]) and isValue(c):
- if leValue(fact[1], c): result = impYes
+ elif sameTree(fact.sons[2], x):
+ if isValue(fact.sons[1]) and isValue(c):
+ if leValue(fact.sons[1], c): result = impYes
else: result = impNo
of someLt:
- if sameTree(fact[1], x):
- if isValue(fact[2]) and isValue(c):
+ if sameTree(fact.sons[1], x):
+ if isValue(fact.sons[2]) and isValue(c):
# fact: x < 4; question x <= N? --> true iff N-1 <= 4
- if leValue(fact[2], c.pred): result = impYes
+ if leValue(fact.sons[2], c.pred): result = impYes
# fact: x < 4; question x <= 2? --> we don't know
- elif sameTree(fact[2], x):
+ elif sameTree(fact.sons[2], x):
# fact: 3 < x; question: x <= 1 ? --> false iff 1 <= 3
- if isValue(fact[1]) and isValue(c):
- if leValue(c, fact[1]): result = impNo
+ if isValue(fact.sons[1]) and isValue(c):
+ if leValue(c, fact.sons[1]): result = impNo
of someLe:
- if sameTree(fact[1], x):
- if isValue(fact[2]) and isValue(c):
+ if sameTree(fact.sons[1], x):
+ if isValue(fact.sons[2]) and isValue(c):
# fact: x <= 4; question x <= 56? --> true iff 4 <= 56
- if leValue(fact[2], c): result = impYes
+ if leValue(fact.sons[2], c): result = impYes
# fact: x <= 4; question x <= 2? --> we don't know
- elif sameTree(fact[2], x):
+ elif sameTree(fact.sons[2], x):
# fact: 3 <= x; question: x <= 2 ? --> false iff 2 < 3
- if isValue(fact[1]) and isValue(c):
- if leValue(c, fact[1].pred): result = impNo
+ if isValue(fact.sons[1]) and isValue(c):
+ if leValue(c, fact.sons[1].pred): result = impNo
- of mNot, mOr, mAnd: assert(false, "impliesLe")
+ of mNot, mOr, mAnd: internalError(x.info, "impliesLe")
else: discard
proc impliesLt(fact, x, c: PNode): TImplication =
@@ -695,44 +677,44 @@ proc factImplies(fact, prop: PNode): TImplication =
# (not a) -> b compute as not (a -> b) ???
# == not a or not b == not (a and b)
- let arg = fact[1]
+ let arg = fact.sons[1]
case arg.getMagic
of mIsNil, mEqRef:
return ~factImplies(arg, prop)
of mAnd:
# not (a and b) means not a or not b:
# a or b --> both need to imply 'prop'
- let a = factImplies(arg[1], prop)
- let b = factImplies(arg[2], prop)
+ let a = factImplies(arg.sons[1], prop)
+ let b = factImplies(arg.sons[2], prop)
if a == b: return ~a
return impUnknown
else:
return impUnknown
of mAnd:
- result = factImplies(fact[1], prop)
+ result = factImplies(fact.sons[1], prop)
if result != impUnknown: return result
- return factImplies(fact[2], prop)
+ return factImplies(fact.sons[2], prop)
else: discard
- case prop[0].sym.magic
- of mNot: result = ~fact.factImplies(prop[1])
+ case prop.sons[0].sym.magic
+ of mNot: result = ~fact.factImplies(prop.sons[1])
of mIsNil: result = impliesIsNil(fact, prop)
of someEq: result = impliesEq(fact, prop)
- of someLe: result = impliesLe(fact, prop[1], prop[2])
- of someLt: result = impliesLt(fact, prop[1], prop[2])
- of mInSet: result = impliesIn(fact, prop[2], prop[1])
+ of someLe: result = impliesLe(fact, prop.sons[1], prop.sons[2])
+ of someLt: result = impliesLt(fact, prop.sons[1], prop.sons[2])
+ of mInSet: result = impliesIn(fact, prop.sons[2], prop.sons[1])
else: result = impUnknown
proc doesImply*(facts: TModel, prop: PNode): TImplication =
assert prop.kind in nkCallKinds
- for f in facts.s:
+ for f in facts:
# facts can be invalidated, in which case they are 'nil':
if not f.isNil:
result = f.factImplies(prop)
if result != impUnknown: return
-proc impliesNotNil*(m: TModel, arg: PNode): TImplication =
- result = doesImply(m, m.g.operators.opIsNil.buildCall(arg).neg(m.g.operators))
+proc impliesNotNil*(facts: TModel, arg: PNode): TImplication =
+ result = doesImply(facts, opIsNil.buildCall(arg).neg)
proc simpleSlice*(a, b: PNode): BiggestInt =
# returns 'c' if a..b matches (i+c)..(i+c), -1 otherwise. (i)..(i) is matched
@@ -746,12 +728,12 @@ proc simpleSlice*(a, b: PNode): BiggestInt =
result = -1
-template isMul(x): untyped = x.getMagic in someMul
-template isDiv(x): untyped = x.getMagic in someDiv
-template isAdd(x): untyped = x.getMagic in someAdd
-template isSub(x): untyped = x.getMagic in someSub
-template isVal(x): untyped = x.kind in {nkCharLit..nkUInt64Lit}
-template isIntVal(x, y): untyped = x.intVal == y
+template isMul(x): expr = x.getMagic in someMul
+template isDiv(x): expr = x.getMagic in someDiv
+template isAdd(x): expr = x.getMagic in someAdd
+template isSub(x): expr = x.getMagic in someSub
+template isVal(x): expr = x.kind in {nkCharLit..nkUInt64Lit}
+template isIntVal(x, y): expr = x.intVal == y
import macros
@@ -776,17 +758,17 @@ macro `=~`(x: PNode, pat: untyped): bool =
of nnkIdent:
let c = newTree(nnkStmtListExpr, newLetStmt(pat, x))
conds.add c
- # XXX why is this 'isVal(pat)' and not 'isVal(x)'?
- if ($pat)[^1] == 'c': c.add(getAst(isVal(x)))
+ if ($pat)[^1] == 'c': c.add(getAst(isVal(pat)))
else: c.add bindSym"true"
of nnkIntLit:
- conds.add(getAst(isIntVal(x, pat.intVal)))
+ conds.add(getAst(isIntVal(pat.intVal)))
else:
error("invalid pattern")
var conds = newTree(nnkBracket)
m(x, pat, conds)
- result = nestList(ident"and", conds)
+ result = nestList(!"and", conds)
+
proc isMinusOne(n: PNode): bool =
n.kind in {nkCharLit..nkUInt64Lit} and n.intVal == -1
@@ -794,8 +776,8 @@ proc isMinusOne(n: PNode): bool =
proc pleViaModel(model: TModel; aa, bb: PNode): TImplication
proc ple(m: TModel; a, b: PNode): TImplication =
- template `<=?`(a,b): untyped = ple(m,a,b) == impYes
- template `>=?`(a,b): untyped = ple(m, nkIntLit.newIntNode(b), a) == impYes
+ template `<=?`(a,b): expr = ple(m,a,b) == impYes
+ template `>=?`(a,b): expr = ple(m, nkIntLit.newIntNode(b), a) == impYes
# 0 <= 3
if a.isValue and b.isValue:
@@ -803,10 +785,10 @@ proc ple(m: TModel; a, b: PNode): TImplication =
# use type information too: x <= 4 iff high(x) <= 4
if b.isValue and a.typ != nil and a.typ.isOrdinalType:
- if lastOrd(nil, a.typ) <= b.intVal: return impYes
+ if lastOrd(a.typ) <= b.intVal: return impYes
# 3 <= x iff low(x) <= 3
if a.isValue and b.typ != nil and b.typ.isOrdinalType:
- if a.intVal <= firstOrd(nil, b.typ): return impYes
+ if firstOrd(b.typ) <= a.intVal: return impYes
# x <= x
if sameTree(a, b): return impYes
@@ -817,11 +799,7 @@ proc ple(m: TModel; a, b: PNode): TImplication =
# x <= y+c if 0 <= c and x <= y
# x <= y+(-c) if c <= 0 and y >= x
- if b.getMagic in someAdd:
- if zero() <=? b[2] and a <=? b[1]: return impYes
- # x <= y-c if x+c <= y
- if b[2] <=? zero() and (canon(m.g.operators.opSub.buildCall(a, b[2]), m.g.operators) <=? b[1]):
- return impYes
+ if b.getMagic in someAdd and zero() <=? b[2] and a <=? b[1]: return impYes
# x+c <= y if c <= 0 and x <= y
if a.getMagic in someAdd and a[2] <=? zero() and a[1] <=? b: return impYes
@@ -834,20 +812,20 @@ proc ple(m: TModel; a, b: PNode): TImplication =
if a.getMagic in someMul and a[2].isValue and a[1].getMagic in someDiv and
a[1][2].isValue:
# simplify (x div 4) * 2 <= y to x div (c div d) <= y
- if ple(m, buildCall(m.g.operators.opDiv, a[1][1], `|div|`(a[1][2], a[2])), b) == impYes:
+ if ple(m, buildCall(opDiv, a[1][1], `|div|`(a[1][2], a[2])), b) == impYes:
return impYes
# x*3 + x == x*4. It follows that:
# x*3 + y <= x*4 if y <= x and 3 <= 4
if a =~ x*dc + y and b =~ x2*ec:
if sameTree(x, x2):
- let ec1 = m.g.operators.opAdd.buildCall(ec, minusOne())
+ let ec1 = opAdd.buildCall(ec, minusOne())
if x >=? 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 = opAdd.buildCall(ec, minusOne())
if x >=? 1 and ec >=? 1 and dc >=? 1 and dc <=? ec1 and y <=? zero():
return impYes
@@ -880,17 +858,17 @@ proc ple(m: TModel; a, b: PNode): TImplication =
# use the knowledge base:
return pleViaModel(m, a, b)
- #return doesImply(m, o.opLe.buildCall(a, b))
+ #return doesImply(m, opLe.buildCall(a, b))
-type TReplacements = seq[tuple[a, b: PNode]]
+type TReplacements = seq[tuple[a,b: PNode]]
proc replaceSubTree(n, x, by: PNode): PNode =
if sameTree(n, x):
result = by
elif hasSubTree(n, x):
result = shallowCopy(n)
- for i in 0..n.safeLen-1:
- result[i] = replaceSubTree(n[i], x, by)
+ for i in 0 .. safeLen(n)-1:
+ result.sons[i] = replaceSubTree(n.sons[i], x, by)
else:
result = n
@@ -900,23 +878,20 @@ proc applyReplacements(n: PNode; rep: TReplacements): PNode =
proc pleViaModelRec(m: var TModel; a, b: PNode): TImplication =
# now check for inferrable facts: a <= b and b <= c implies a <= c
- for i in 0..m.s.high:
- let fact = m.s[i]
+ for i in 0..m.high:
+ let fact = m[i]
if fact != nil and fact.getMagic in someLe:
# mark as used:
- m.s[i] = nil
+ m[i] = nil
# i <= len-100
# i <=? len-1
# --> true if (len-100) <= (len-1)
let x = fact[1]
let y = fact[2]
- # x <= y.
- # Question: x <= b? True iff y <= b.
- if sameTree(x, a):
- if ple(m, y, b) == impYes: return impYes
- if y.getMagic in someAdd and b.getMagic in someAdd and sameTree(y[1], b[1]):
- if ple(m, b[2], y[2]) == impYes:
- return impYes
+ if sameTree(x, a) and y.getMagic in someAdd and b.getMagic in someAdd and
+ sameTree(y[1], b[1]):
+ if ple(m, b[2], y[2]) == impYes:
+ return impYes
# x <= y implies a <= b if a <= x and y <= b
if ple(m, a, x) == impYes:
@@ -939,7 +914,7 @@ proc pleViaModelRec(m: var TModel; a, b: PNode): TImplication =
proc pleViaModel(model: TModel; aa, bb: PNode): TImplication =
# compute replacements:
var replacements: TReplacements = @[]
- for fact in model.s:
+ for fact in model:
if fact != nil and fact.getMagic in someEq:
let a = fact[1]
let b = fact[2]
@@ -949,13 +924,12 @@ proc pleViaModel(model: TModel; aa, bb: PNode): TImplication =
var a = aa
var b = bb
if replacements.len > 0:
- m.s = @[]
- m.g = model.g
+ m = @[]
# make the other facts consistent:
- for fact in model.s:
+ for fact in model:
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.add applyReplacements(fact, replacements).canon
a = applyReplacements(aa, replacements)
b = applyReplacements(bb, replacements)
else:
@@ -964,61 +938,55 @@ 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(opLe.buildCall(a, b))
#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(opLe.buildCall(opAdd.buildCall(b, one()), a))
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)
-
-proc addFactLt*(m: var TModel; a, b: PNode) =
- let bb = m.g.operators.opAdd.buildCall(b, minusOne())
- addFactLe(m, a, bb)
+ m.add canon(opLe.buildCall(a, b))
proc settype(n: PNode): PType =
- result = newType(tySet, ItemId(module: -1, item: -1), n.typ.owner)
- var idgen: IdGenerator
- addSonSkipIntLit(result, n.typ, idgen)
+ result = newType(tySet, n.typ.owner)
+ addSonSkipIntLit(result, n.typ)
-proc buildOf(it, loc: PNode; o: Operators): PNode =
+proc buildOf(it, loc: PNode): PNode =
var s = newNodeI(nkCurly, it.info, it.len-1)
s.typ = settype(loc)
- for i in 0.. '" &
+ if optHints in gOptions and hintPattern in gNotes:
+ message(orig.info, hintPattern, rule & " --> '" &
renderTree(result, {renderNoComments}) & "'")
proc applyPatterns(c: PContext, n: PNode): PNode =
@@ -40,25 +36,25 @@ proc applyPatterns(c: PContext, n: PNode): PNode =
# we apply the last pattern first, so that pattern overriding is possible;
# however the resulting AST would better not trigger the old rule then
# anymore ;-)
- for i in countdown(c.patterns.len-1, 0):
+ for i in countdown( evalTemplateLimit:
- globalError(c.config, n.info, "template instantiation too nested")
+ inc(evalTemplateCounter)
+ if evalTemplateCounter > 100:
+ globalError(n.info, errTemplateInstantiationTooNested)
# deactivate this pattern:
c.patterns[i] = nil
if x.kind == nkStmtList:
assert x.len == 3
- x[1] = evalPattern(c, x[1], result)
+ x.sons[1] = evalPattern(c, x.sons[1], result)
result = flattenStmts(x)
else:
result = evalPattern(c, x, result)
- dec(c.config.evalTemplateCounter)
+ dec(evalTemplateCounter)
# activate this pattern again:
c.patterns[i] = pattern
@@ -71,37 +67,37 @@ proc hlo(c: PContext, n: PNode): PNode =
# already processed (special cases in semstmts.nim)
result = n
else:
- if n.kind in {nkFastAsgn, nkAsgn, nkSinkAsgn, nkIdentDefs, nkVarTuple} and
- n[0].kind == nkSym and
- {sfGlobal, sfPure} * n[0].sym.flags == {sfGlobal, sfPure}:
+ if n.kind in {nkFastAsgn, nkAsgn, nkIdentDefs, nkVarTuple} and
+ n.sons[0].kind == nkSym and
+ {sfGlobal, sfPure} * n.sons[0].sym.flags == {sfGlobal, sfPure}:
# do not optimize 'var g {.global} = re(...)' again!
return n
result = applyPatterns(c, n)
if result == n:
# no optimization applied, try subtrees:
- for i in 0.. 0'u32
-
-proc `$`*(x: LitId): string {.borrow.}
-proc `<`*(x, y: LitId): bool {.borrow.}
-proc `<=`*(x, y: LitId): bool {.borrow.}
-proc `==`*(x, y: LitId): bool {.borrow.}
-proc hash*(x: LitId): Hash {.borrow.}
-
-
-proc len*[T](t: BiTable[T]): int = t.vals.len
-
-proc mustRehash(length, counter: int): bool {.inline.} =
- assert(length > counter)
- result = (length * 2 < counter * 3) or (length - counter < 4)
-
-const
- idStart = 256 ##
- ## Ids do not start with 0 but with this value. The IR needs it.
- ## TODO: explain why
-
-template idToIdx(x: LitId): int = x.int - idStart
-
-proc hasLitId*[T](t: BiTable[T]; x: LitId): bool =
- let idx = idToIdx(x)
- result = idx >= 0 and idx < t.vals.len
-
-proc enlarge[T](t: var BiTable[T]) =
- var n: seq[LitId]
- newSeq(n, len(t.keys) * 2)
- swap(t.keys, n)
- for i in 0..high(n):
- let eh = n[i]
- if isFilled(eh):
- var j = hash(t.vals[idToIdx eh]) and maxHash(t)
- while isFilled(t.keys[j]):
- j = nextTry(j, maxHash(t))
- t.keys[j] = move n[i]
-
-proc getKeyId*[T](t: BiTable[T]; v: T): LitId =
- let origH = hash(v)
- var h = origH and maxHash(t)
- if t.keys.len != 0:
- while true:
- let litId = t.keys[h]
- if not isFilled(litId): break
- if t.vals[idToIdx t.keys[h]] == v: return litId
- h = nextTry(h, maxHash(t))
- return LitId(0)
-
-proc getOrIncl*[T](t: var BiTable[T]; v: T): LitId =
- let origH = hash(v)
- var h = origH and maxHash(t)
- if t.keys.len != 0:
- while true:
- let litId = t.keys[h]
- if not isFilled(litId): break
- if t.vals[idToIdx t.keys[h]] == v: return litId
- h = nextTry(h, maxHash(t))
- # not found, we need to insert it:
- if mustRehash(t.keys.len, t.vals.len):
- enlarge(t)
- # recompute where to insert:
- h = origH and maxHash(t)
- while true:
- let litId = t.keys[h]
- if not isFilled(litId): break
- h = nextTry(h, maxHash(t))
- else:
- setLen(t.keys, 16)
- h = origH and maxHash(t)
-
- result = LitId(t.vals.len + idStart)
- t.keys[h] = result
- t.vals.add v
-
-
-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
- assert idx < t.vals.len
- result = t.vals[idx]
-
-proc hash*[T](t: BiTable[T]): Hash =
- ## as the keys are hashes of the values, we simply use them instead
- var h: Hash = 0
- for i, n in pairs t.keys:
- h = h !& hash((i, n))
- result = !$h
-
-proc store*[T](f: var RodFile; t: BiTable[T]) =
- storeSeq(f, t.vals)
- storeSeq(f, t.keys)
-
-proc load*[T](f: var RodFile; t: var BiTable[T]) =
- loadSeq(f, t.vals)
- loadSeq(f, t.keys)
-
-when isMainModule:
-
- var t: BiTable[string]
-
- echo getOrIncl(t, "hello")
-
- echo getOrIncl(t, "hello")
- echo getOrIncl(t, "hello3")
- echo getOrIncl(t, "hello4")
- echo getOrIncl(t, "helloasfasdfdsa")
- echo getOrIncl(t, "hello")
- echo getKeyId(t, "hello")
- echo getKeyId(t, "none")
-
- for i in 0 ..< 100_000:
- discard t.getOrIncl($i & "___" & $i)
-
- for i in 0 ..< 100_000:
- assert t.getOrIncl($i & "___" & $i).idToIdx == i + 4
- echo "begin"
- echo t.vals.len
-
- echo t.vals[0]
- echo t.vals[1004]
-
- echo "middle"
-
- var tf: BiTable[float]
-
- discard tf.getOrIncl(0.4)
- discard tf.getOrIncl(16.4)
- discard tf.getOrIncl(32.4)
- echo getKeyId(tf, 32.4)
-
- var f2 = open("testblah.bin", fmWrite)
- echo store(f2, tf)
- f2.close
-
- var f1 = open("testblah.bin", fmRead)
-
- var t2: BiTable[float]
-
- echo f1.load(t2)
- echo t2.vals.len
-
- echo getKeyId(t2, 32.4)
-
- echo "end"
-
-
- f1.close
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
deleted file mode 100644
index b096e3103..000000000
--- a/compiler/ic/design.rst
+++ /dev/null
@@ -1,56 +0,0 @@
-====================================
- Incremental Recompilations
-====================================
-
-We split the Nim compiler into a frontend and a backend.
-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.
-
-Advantage of the "set of files" vs the previous global database:
-- By construction, we either read from the `.rod` file or from the
- `.nim` file, there can be no inconsistency. There can also be no
- partial updates.
-- No dependency to external packages (SQLite). SQLite simply is too
- slow and the old way of serialization was too slow too. We use a
- format designed for Nim and expect to base further tools on this
- file format.
-
-References to external modules must be (moduleId, symId) pairs.
-The symbol IDs are module specific. This way no global ID increment
-mechanism needs to be implemented that we could get wrong. ModuleIds
-are rod-file specific too.
-
-
-
-Global state
-------------
-
-There is no global state.
-
-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
deleted file mode 100644
index 87b1516fa..000000000
--- a/compiler/ic/packed_ast.nim
+++ /dev/null
@@ -1,419 +0,0 @@
-#
-#
-# The Nim Compiler
-# (c) Copyright 2020 Andreas Rumpf
-#
-# See the file "copying.txt", included in this
-# distribution, for details about the copyright.
-#
-
-## Packed AST representation, mostly based on a seq of nodes.
-## For IC support. Far future: Rewrite the compiler passes to
-## use this representation directly in all the transformations,
-## it is superior.
-
-import hashes, tables, strtabs
-import bitabs
-import ".." / [ast, options]
-
-when defined(nimPreviewSlimSystem):
- import std/assertions
-
-type
- SymId* = distinct int32
- ModuleId* = distinct int32
- NodePos* = distinct int
-
- NodeId* = distinct int32
-
- PackedItemId* = object
- module*: LitId # 0 if it's this module
- item*: int32 # same as the in-memory representation
-
-const
- nilItemId* = PackedItemId(module: LitId(0), item: -1.int32)
-
-const
- emptyNodeId* = NodeId(-1)
-
-type
- PackedLineInfo* = object
- line*: uint16
- col*: int16
- file*: LitId
-
- PackedLib* = object
- kind*: TLibKind
- generated*: bool
- isOverriden*: bool
- name*: LitId
- path*: NodeId
-
- PackedSym* = object
- kind*: TSymKind
- name*: LitId
- typ*: PackedItemId
- flags*: TSymFlags
- magic*: TMagic
- info*: PackedLineInfo
- ast*: NodeId
- owner*: PackedItemId
- guard*: PackedItemId
- bitsize*: int
- alignment*: int # for alignment
- options*: TOptions
- position*: int
- offset*: int
- externalName*: LitId # instead of TLoc
- locFlags*: TLocFlags
- annex*: PackedLib
- when hasFFI:
- cname*: LitId
- constraint*: NodeId
-
- PackedType* = object
- kind*: TTypeKind
- callConv*: TCallingConvention
- #nodekind*: TNodeKind
- flags*: TTypeFlags
- types*: seq[PackedItemId]
- n*: NodeId
- #nodeflags*: TNodeFlags
- sym*: PackedItemId
- owner*: PackedItemId
- size*: BiggestInt
- align*: int16
- paddingAtEnd*: int16
- # not serialized: loc*: TLoc because it is backend-specific
- typeInst*: PackedItemId
- nonUniqueId*: int32
-
- PackedNode* = object # 28 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
- info*: PackedLineInfo
-
- PackedTree* = object ## usually represents a full Nim module
- nodes*: seq[PackedNode]
-
- PackedInstantiation* = object
- key*, sym*: PackedItemId
- concreteTypes*: seq[PackedItemId]
-
-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: NodeId): bool {.borrow.}
-
-proc newTreeFrom*(old: PackedTree): PackedTree =
- result.nodes = @[]
- when false: result.sh = old.sh
-
-when false:
- proc declareSym*(tree: var PackedTree; kind: TSymKind;
- name: LitId; info: PackedLineInfo): SymId =
- result = SymId(tree.sh.syms.len)
- tree.sh.syms.add PackedSym(kind: kind, name: name, flags: {}, magic: mNone, info: info)
-
- proc litIdFromName*(tree: PackedTree; name: string): LitId =
- result = tree.sh.strings.getOrIncl(name)
-
- proc add*(tree: var PackedTree; kind: TNodeKind; token: string; info: PackedLineInfo) =
- tree.nodes.add PackedNode(kind: kind, info: info,
- operand: int32 getOrIncl(tree.sh.strings, token))
-
- proc add*(tree: var PackedTree; kind: TNodeKind; info: PackedLineInfo) =
- tree.nodes.add PackedNode(kind: kind, operand: 0, info: info)
-
-proc throwAwayLastNode*(tree: var PackedTree) =
- tree.nodes.setLen(tree.nodes.len-1)
-
-proc addIdent*(tree: var PackedTree; s: LitId; info: PackedLineInfo) =
- tree.nodes.add PackedNode(kind: nkIdent, operand: int32(s), info: info)
-
-proc addSym*(tree: var PackedTree; s: int32; info: PackedLineInfo) =
- tree.nodes.add PackedNode(kind: nkSym, operand: s, info: info)
-
-proc addModuleId*(tree: var PackedTree; s: ModuleId; info: PackedLineInfo) =
- tree.nodes.add PackedNode(kind: nkInt32Lit, operand: int32(s), info: info)
-
-proc addSymDef*(tree: var PackedTree; s: SymId; info: PackedLineInfo) =
- tree.nodes.add PackedNode(kind: nkSym, operand: int32(s), info: info)
-
-proc isAtom*(tree: PackedTree; pos: int): bool {.inline.} = tree.nodes[pos].kind <= nkNilLit
-
-proc copyTree*(dest: var PackedTree; tree: PackedTree; n: NodePos) =
- # and this is why the IR is superior. We can copy subtrees
- # via a linear scan.
- let pos = n.int
- let L = if isAtom(tree, pos): 1 else: tree.nodes[pos].operand
- let d = dest.nodes.len
- dest.nodes.setLen(d + L)
- for i in 0.. nkNilLit
- let distance = int32(tree.nodes.len - pos)
- tree.nodes[pos].operand = distance
-
-proc len*(tree: PackedTree): int {.inline.} = tree.nodes.len
-
-proc `[]`*(tree: PackedTree; i: int): lent PackedNode {.inline.} =
- tree.nodes[i]
-
-proc nextChild(tree: PackedTree; pos: var int) {.inline.} =
- if tree.nodes[pos].kind > nkNilLit:
- assert tree.nodes[pos].operand > 0
- inc pos, tree.nodes[pos].operand
- else:
- inc pos
-
-iterator sonsReadonly*(tree: PackedTree; n: NodePos): NodePos =
- var pos = n.int
- assert tree.nodes[pos].kind > nkNilLit
- let last = pos + tree.nodes[pos].operand
- inc pos
- while pos < last:
- yield NodePos pos
- nextChild tree, pos
-
-iterator sons*(dest: var PackedTree; tree: PackedTree; n: NodePos): NodePos =
- let patchPos = prepare(dest, tree, n)
- for x in sonsReadonly(tree, n): yield x
- patch dest, patchPos
-
-iterator isons*(dest: var PackedTree; tree: PackedTree;
- n: NodePos): (int, NodePos) =
- var i = 0
- for ch0 in sons(dest, tree, n):
- yield (i, ch0)
- inc i
-
-iterator sonsFrom1*(tree: PackedTree; n: NodePos): NodePos =
- var pos = n.int
- assert tree.nodes[pos].kind > nkNilLit
- let last = pos + tree.nodes[pos].operand
- inc pos
- if pos < last:
- nextChild tree, pos
- while pos < last:
- yield NodePos pos
- nextChild tree, pos
-
-iterator sonsWithoutLast2*(tree: PackedTree; n: NodePos): NodePos =
- var count = 0
- for child in sonsReadonly(tree, n):
- inc count
- var pos = n.int
- assert tree.nodes[pos].kind > nkNilLit
- let last = pos + tree.nodes[pos].operand
- inc pos
- while pos < last and count > 2:
- yield NodePos pos
- dec count
- nextChild tree, pos
-
-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)):
- dec pos
- #assert pos >= 0, "node has no parent"
- result = NodePos(pos)
-
-template parent*(n: NodePos): NodePos = parentImpl(tree, n)
-
-proc hasXsons*(tree: PackedTree; n: NodePos; x: int): bool =
- var count = 0
- if tree.nodes[n.int].kind > nkNilLit:
- for child in sonsReadonly(tree, n): inc count
- result = count == x
-
-proc hasAtLeastXsons*(tree: PackedTree; n: NodePos; x: int): bool =
- if tree.nodes[n.int].kind > nkNilLit:
- var count = 0
- for child in sonsReadonly(tree, n):
- inc count
- if count >= x: return true
- return false
-
-proc firstSon*(tree: PackedTree; n: NodePos): NodePos {.inline.} =
- NodePos(n.int+1)
-proc kind*(tree: PackedTree; n: NodePos): TNodeKind {.inline.} =
- tree.nodes[n.int].kind
-proc litId*(tree: PackedTree; n: NodePos): LitId {.inline.} =
- LitId tree.nodes[n.int].operand
-proc info*(tree: PackedTree; n: NodePos): PackedLineInfo {.inline.} =
- tree.nodes[n.int].info
-
-template typ*(n: NodePos): PackedItemId =
- tree.nodes[n.int].typeId
-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.} =
- if isAtom(tree, pos): 1 else: tree.nodes[pos].operand
-
-proc sons2*(tree: PackedTree; n: NodePos): (NodePos, NodePos) =
- assert(not isAtom(tree, n.int))
- let a = n.int+1
- let b = a + span(tree, a)
- result = (NodePos a, NodePos b)
-
-proc sons3*(tree: PackedTree; n: NodePos): (NodePos, NodePos, NodePos) =
- assert(not isAtom(tree, n.int))
- let a = n.int+1
- let b = a + span(tree, a)
- let c = b + span(tree, b)
- result = (NodePos a, NodePos b, NodePos c)
-
-proc ithSon*(tree: PackedTree; n: NodePos; i: int): NodePos =
- if tree.nodes[n.int].kind > nkNilLit:
- var count = 0
- for child in sonsReadonly(tree, n):
- if count == i: return child
- inc count
- assert false, "node has no i-th child"
-
-when false:
- proc `@`*(tree: PackedTree; lit: LitId): lent string {.inline.} =
- tree.sh.strings[lit]
-
-template kind*(n: NodePos): TNodeKind = tree.nodes[n.int].kind
-template info*(n: NodePos): PackedLineInfo = tree.nodes[n.int].info
-template litId*(n: NodePos): LitId = LitId tree.nodes[n.int].operand
-
-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]
-
- proc strVal*(tree: PackedTree; n: NodePos): string =
- assert n.kind == nkStrLit
- result = tree.sh.strings[LitId tree.nodes[n.int].operand]
- #result = cookedStrLit(raw)
-
- proc filenameVal*(tree: PackedTree; n: NodePos): string =
- case n.kind
- of nkStrLit:
- result = strVal(tree, n)
- of nkIdent:
- result = tree.sh.strings[n.litId]
- of nkSym:
- result = tree.sh.strings[tree.sh.syms[int n.symId].name]
- else:
- result = ""
-
- proc identAsStr*(tree: PackedTree; n: NodePos): lent string =
- assert n.kind == nkIdent
- result = tree.sh.strings[LitId tree.nodes[n.int].operand]
-
-const
- externIntLit* = {nkCharLit,
- nkIntLit,
- nkInt8Lit,
- nkInt16Lit,
- nkInt64Lit,
- nkUIntLit,
- nkUInt8Lit,
- nkUInt16Lit,
- nkUInt32Lit,
- nkUInt64Lit} # nkInt32Lit is missing by design!
-
- externSIntLit* = {nkIntLit, nkInt8Lit, nkInt16Lit, nkInt64Lit}
- externUIntLit* = {nkUIntLit, nkUInt8Lit, nkUInt16Lit, nkUInt32Lit, nkUInt64Lit}
- directIntLit* = nkInt32Lit
-
-when false:
- proc identIdImpl(tree: PackedTree; n: NodePos): LitId =
- if n.kind == nkIdent:
- result = n.litId
- elif n.kind == nkSym:
- result = tree.sh.syms[int n.symId].name
- else:
- result = LitId(0)
-
- template identId*(n: NodePos): LitId = identIdImpl(tree, n)
-
-template copyInto*(dest, n, body) =
- let patchPos = prepare(dest, tree, n)
- body
- patch dest, patchPos
-
-template copyIntoKind*(dest, kind, info, body) =
- let patchPos = prepare(dest, kind, info)
- body
- patch dest, patchPos
-
-when false:
- proc hasPragma*(tree: PackedTree; n: NodePos; pragma: string): bool =
- let litId = tree.sh.strings.getKeyId(pragma)
- if litId == LitId(0):
- return false
- assert n.kind == nkPragma
- for ch0 in sonsReadonly(tree, n):
- if ch0.kind == nkExprColonExpr:
- if ch0.firstSon.identId == litId:
- return true
- elif ch0.identId == litId:
- return true
-
-proc getNodeId*(tree: PackedTree): NodeId {.inline.} = NodeId tree.nodes.len
-
-when false:
- proc produceError*(dest: var PackedTree; tree: PackedTree; n: NodePos; msg: string) =
- let patchPos = prepare(dest, nkError, n.info)
- dest.add nkStrLit, msg, n.info
- 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 toPackedItemId*(item: int32): PackedItemId {.inline.} =
- PackedItemId(module: LitId(0), item: item)
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
deleted file mode 100644
index e492624d0..000000000
--- a/compiler/ic/rodfiles.nim
+++ /dev/null
@@ -1,249 +0,0 @@
-#
-#
-# The Nim Compiler
-# (c) Copyright 2020 Andreas Rumpf
-#
-# See the file "copying.txt", included in this
-# 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
- configSection
- stringsSection
- checkSumsSection
- depsSection
- numbersSection
- exportsSection
- hiddenSection
- reexportsSection
- compilerProcsSection
- trmacrosSection
- convertersSection
- methodsSection
- pureEnumsSection
- toReplaySection
- 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,
- includeFileChanged
-
- RodFile* = object
- f*: File
- currentSection*: RodSection # for error checking
- err*: RodFileError # little experiment to see if this works
- # better than exceptions.
-
-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")
-
-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
- return
- var lenPrefix = int32(s.len)
- if writeBuffer(f.f, addr lenPrefix, sizeof(lenPrefix)) != sizeof(lenPrefix):
- setError f, ioFailure
- else:
- if s.len != 0:
- if writeBuffer(f.f, unsafeAddr(s[0]), s.len) != s.len:
- setError f, 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
- 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
- return
- var lenPrefix = int32(s.len)
- if writeBuffer(f.f, addr lenPrefix, sizeof(lenPrefix)) != sizeof(lenPrefix):
- setError f, ioFailure
- else:
- for i in 0.. 0:
- if readBuffer(f.f, unsafeAddr(s[0]), s.len) != s.len:
- setError f, 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
- 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
- else:
- s = newSeq[T](lenPrefix)
- for i in 0.. 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)
+proc importSymbol(c: PContext, n: PNode, fromMod: PSym) =
+ let ident = lookups.considerQuotedIdent(n)
+ let s = strTableGet(fromMod.tab, ident)
if s == nil:
- errorUndeclaredIdentifier(c, n.info, ident.s)
+ localError(n.info, errUndeclaredIdentifier, ident.s)
else:
- when false:
- if s.kind == skStub: loadStub(s)
- let multiImport = s.kind notin ExportableSymKinds or s.kind in skProcKinds
+ if s.kind == skStub: loadStub(s)
+ if s.kind notin ExportableSymKinds:
+ internalError(n.info, "importSymbol: 2")
# for an enumeration we have to add all identifiers
- if multiImport:
+ case s.kind
+ of skProcKinds:
# 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)
- else:
- rawImportSymbol(c, s, fromMod, importSet)
- suggestSym(c.graph, n.info, s, c.graph.usageSym, false)
-
-proc addImport(c: PContext; im: sink ImportedModule) =
- for i in 0..high(c.imports):
- if c.imports[i].m == im.m:
- # we have already imported the module: Check which import
- # is more "powerful":
- case c.imports[i].mode
- of importAll: discard "already imported all symbols"
- of importSet:
- case im.mode
- of importAll, importExcept:
- # XXX: slightly wrong semantics for 'importExcept'...
- # But we should probably change the spec and disallow this case.
- c.imports[i] = im
- of importSet:
- # merge the import sets:
- c.imports[i].imported.incl im.imported
- of importExcept:
- case im.mode
- of importAll:
- c.imports[i] = im
- of importSet:
- discard
- of importExcept:
- var cut = initIntSet()
- # only exclude what is consistent between the two sets:
- for j in im.exceptSet:
- if j in c.imports[i].exceptSet:
- cut.incl j
- c.imports[i].exceptSet = cut
- return
- c.imports.add im
-
-template addUnnamedIt(c: PContext, fromMod: PSym; filter: untyped) {.dirty.} =
- for it in mitems 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:
- 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:
- if filter:
- loadPackedSym(c.graph, it)
- importPureEnumFields(c, it.sym, it.sym.typ)
+ if e.name.id != s.name.id: internalError(n.info, "importSymbol: 3")
+ rawImportSymbol(c, e)
+ e = nextIdentIter(it, fromMod.tab)
+ else: rawImportSymbol(c, s)
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)
+ 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(s.info, "importAllSymbols: " & $s.kind)
+ if exceptSet.isNil or s.name.id notin exceptSet:
+ rawImportSymbol(c, s)
+ s = nextIter(i, fromMod.tab)
proc importAllSymbols*(c: PContext, fromMod: PSym) =
- c.addImport ImportedModule(m: fromMod, mode: importAll)
- addUnnamedIt(c, fromMod, true)
- when false:
- var exceptSet: IntSet
- importAllSymbolsExcept(c, fromMod, exceptSet)
+ var exceptSet: IntSet
+ importAllSymbolsExcept(c, fromMod, exceptSet)
-proc importForwarded(c: PContext, n: PNode, exceptSet: IntSet; fromMod: PSym; importSet: var IntSet) =
+proc importForwarded(c: PContext, n: PNode, exceptSet: IntSet) =
if n.isNil: return
case n.kind
of nkExportStmt:
@@ -221,150 +143,64 @@ proc importForwarded(c: PContext, n: PNode, exceptSet: IntSet; fromMod: PSym; im
if s.kind == skModule:
importAllSymbolsExcept(c, s, exceptSet)
elif exceptSet.isNil or s.name.id notin exceptSet:
- rawImportSymbol(c, s, fromMod, importSet)
+ rawImportSymbol(c, s)
of nkExportExceptStmt:
- localError(c.config, n.info, "'export except' not implemented")
+ localError(n.info, errGenerated, "'export except' not implemented")
else:
- for i in 0..n.safeLen-1:
- importForwarded(c, n[i], exceptSet, fromMod, importSet)
+ for i in 0..safeLen(n)-1:
+ importForwarded(c, n.sons[i], exceptSet)
-proc importModuleAs(c: PContext; n: PNode, realModule: PSym, importHidden: bool): PSym =
+proc importModuleAs(n: PNode, realModule: PSym): PSym =
result = realModule
- template createModuleAliasImpl(ident): untyped =
- createModuleAlias(realModule, nextSymId c.idgen, ident, n.info, c.config.options)
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:
+ elif n.len != 2 or n.sons[1].kind != nkIdent:
+ localError(n.info, errGenerated, "module alias must be an identifier")
+ elif n.sons[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, n.sons[1].ident, realModule.info)
-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})
+proc myImportModule(c: PContext, n: PNode): PSym =
+ var f = checkModuleName(n)
+ if f != InvalidFileIDX:
+ result = importModuleAs(n, gImportModule(c.module, f))
+ if result.info.fileIndex == n.info.fileIndex:
+ localError(n.info, errGenerated, "A module cannot import itself")
+ if sfDeprecated in result.flags:
+ message(n.info, warnDeprecated, result.name.s)
+ #suggestSym(n.info, result, false)
- 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 evalImport(c: PContext, n: PNode): PNode =
+ result = n
+ var emptySet: IntSet
+ for i in countup(0, sonsLen(n) - 1):
+ var m = myImportModule(c, n.sons[i])
+ if m != nil:
+ # ``addDecl`` needs to be done before ``importAllSymbols``!
+ addDecl(c, m) # add symbol to symbol table of module
+ importAllSymbolsExcept(c, m, emptySet)
+ #importForwarded(c, m.ast, emptySet)
-proc myImportModule(c: PContext, n: var PNode, importStmtResult: PNode): PSym =
- let transf = transformImportAs(c, n)
- n = transf.node
- let f = checkModuleName(c.config, n)
- if f != InvalidFileIdx:
- addImportFileDep(c, f)
- let L = c.graph.importStack.len
- let recursion = c.graph.importStack.find(f)
- c.graph.importStack.add f
- #echo "adding ", toFullPath(f), " at ", L+1
- if recursion >= 0:
- var err = ""
- for i in recursion.. recursion: err.add "\n"
- err.add toFullPath(c.config, c.graph.importStack[i]) & " imports " &
- 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)
- 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)
- 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 impMod(c: PContext; it: PNode; importStmtResult: PNode) =
- var it = it
- let m = myImportModule(c, it, importStmtResult)
+proc evalFrom(c: PContext, n: PNode): PNode =
+ result = n
+ checkMinSonsLen(n, 2)
+ var m = myImportModule(c, n.sons[0])
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)
- for i in 0.. 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)
- when toDebug.len > 0:
- # for more effective debugging
- if not result and c.graph.config.selectedGC in {gcArc, gcOrc}:
- assert(not containsGarbageCollectedRef(t))
-
-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 p(n: PNode; c: var Con; s: var Scope; mode: ProcessMode; tmpFlags = {sfSingleUsedTemp}): PNode
-
-type
- MoveOrCopyFlag = enum
- IsDecl, IsExplicitSink
-
-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 =
- case n.kind
- of nkSym:
- sfCursor in n.sym.flags
- of nkDotExpr:
- isCursor(n[1])
- of nkCheckedFieldExpr:
- isCursor(n[0])
- else:
- false
-
-template isUnpackedTuple(n: PNode): bool =
- ## we move out all elements of unpacked tuples,
- ## hence unpacked tuples themselves don't need to be destroyed
- (n.kind == nkSym and n.sym.kind == skTemp and n.sym.typ.kind == tyTuple)
-
-proc checkForErrorPragma(c: Con; t: PType; ri: PNode; opname: string) =
- var m = "'" & opname & "' is not available for type <" & typeToString(t) & ">"
- if (opname == "=" or opname == "=copy") and ri != nil:
- 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'):
- 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
- elif ri.kind == nkSym and ri.sym.kind == skParam and not isSinkType(ri.sym.typ):
- m.add "; try to make "
- m.add renderTree(ri)
- m.add " a 'sink' parameter"
- m.add "; routine: "
- m.add c.owner.name.s
- localError(c.graph.config, ri.info, errGenerated, m)
-
-proc makePtrType(c: var Con, baseType: PType): PType =
- result = newType(tyPtr, nextTypeId c.idgen, c.owner)
- addSonSkipIntLit(result, baseType, c.idgen)
-
-proc genOp(c: var Con; op: PSym; dest: PNode): PNode =
- let addrExp = newNodeIT(nkHiddenAddr, dest.info, makePtrType(c, dest.typ))
- addrExp.add(dest)
- 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:
- # give up and find the canonical type instead:
- let h = sighashes.hashType(t, c.graph.config, {CoType, CoConsiderOwned, CoDistinct})
- let canon = c.graph.canonTypes.getOrDefault(h)
- if canon != nil:
- op = getAttachedOp(c.graph, canon, 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:
- globalError(c.graph.config, dest.info, "internal error: '" & AttachedOpToStr[kind] &
- "' operator is generic")
- dbg:
- if kind == attachedDestructor:
- echo "destructor is ", op.id, " ", op.ast
- if sfError in op.flags: checkForErrorPragma(c, t, ri, AttachedOpToStr[kind])
- c.genOp(op, dest)
-
-proc genDestroy(c: var Con; dest: PNode): PNode =
- let t = dest.typ.skipTypes({tyGenericInst, tyAlias, tySink})
- result = c.genOp(t, attachedDestructor, dest, nil)
-
-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
- else:
- result = getAttachedOp(c.graph, t, 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
- (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:
- result = c.genOp(t, attachedSink, dest, ri)
- result.add ri
- else:
- # the default is to use combination of `=destroy(dest)` and
- # and copyMem(dest, source). This is efficient.
- result = newTree(nkStmtList, c.genDestroy(dest), newTree(nkFastAsgn, dest, ri))
-
-proc isCriticalLink(dest: PNode): bool {.inline.} =
- #[
- Lins's idea that only "critical" links can introduce a cycle. This is
- critical for the performance gurantees that we strive for: If you
- traverse a data structure, no tracing will be performed at all.
- ORC is about this promise: The GC only touches the memory that the
- mutator touches too.
-
- These constructs cannot possibly create cycles::
-
- local = ...
-
- new(x)
- dest = ObjectConstructor(field: noalias(dest))
-
- But since 'ObjectConstructor' is already moved into 'dest' all we really have
- to look for is assignments to local variables.
- ]#
- result = dest.kind != nkSym
-
-proc finishCopy(c: var Con; result, dest: PNode; isFromSink: bool) =
- if c.graph.config.selectedGC == gcOrc:
- let t = dest.typ.skipTypes({tyGenericInst, tyAlias, tySink, tyDistinct})
- if cyclicType(t):
- result.add boolLit(c.graph, result.info, isFromSink or isCriticalLink(dest))
-
-proc genMarkCyclic(c: var Con; result, dest: PNode) =
- if c.graph.config.selectedGC == gcOrc:
- let t = dest.typ.skipTypes({tyGenericInst, tyAlias, tySink, tyDistinct})
- if cyclicType(t):
- if t.kind == tyRef:
- result.add callCodegenProc(c.graph, "nimMarkCyclic", dest.info, dest)
- else:
- let xenv = genBuiltin(c.graph, c.idgen, 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 =
- let t = dest.typ.skipTypes({tyGenericInst, tyAlias, tySink})
- result = c.genOp(t, a, dest, ri)
- assert ri.typ != nil
-
-proc genCopy(c: var Con; dest, ri: PNode; flags: set[MoveOrCopyFlag]): 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
-
-proc genDiscriminantAsgn(c: var Con; s: var Scope; n: PNode): PNode =
- # discriminator is ordinal value that doesn't need sink destroy
- # but fields within active case branch might need destruction
-
- # tmp to support self assignments
- let tmp = c.getTemp(s, n[1].typ, n.info)
-
- result = newTree(nkStmtList)
- result.add newTree(nkFastAsgn, tmp, p(n[1], c, s, consumed))
- result.add p(n[0], c, s, normal)
-
- let le = p(n[0], c, s, normal)
- let leDotExpr = if le.kind == nkCheckedFieldExpr: le[0] else: le
- 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:
- 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)
- return
-
- # generate: if le != tmp: `=destroy`(le)
- let branchDestructor = produceDestructorForDiscriminator(c.graph, objType, leDotExpr[1].sym, n.info, c.idgen)
- let cond = newNodeIT(nkInfix, n.info, getSysType(c.graph, unknownLineInfo, tyBool))
- cond.add newSymNode(getMagicEqSymForType(c.graph, le.typ, n.info))
- 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 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 & ")")
-
-proc genDefaultCall(t: PType; c: Con; info: TLineInfo): PNode =
- result = newNodeI(nkCall, info)
- result.add(newSymNode(createMagic(c.graph, c.idgen, "default", mDefault)))
- result.typ = t
-
-proc destructiveMoveVar(n: PNode; c: var Con; s: var Scope): PNode =
- # generate: (let tmp = v; reset(v); tmp)
- if not hasDestructor(c, n.typ):
- assert n.kind != nkSym or not hasDestructor(c, n.sym.typ)
- result = copyTree(n)
- else:
- result = newNodeIT(nkStmtListExpr, n.info, n.typ)
-
- var temp = newSym(skLet, getIdent(c.graph.cache, "blitTmp"), nextSymId c.idgen, c.owner, n.info)
- temp.typ = n.typ
- var v = newNodeI(nkLetSection, n.info)
- let tempAsNode = newSymNode(temp)
-
- var vpart = newNodeI(nkIdentDefs, tempAsNode.info, 3)
- vpart[0] = tempAsNode
- vpart[1] = newNodeI(nkEmpty, tempAsNode.info)
- vpart[2] = n
- v.add(vpart)
-
- result.add v
- let nn = skipConv(n)
- c.genMarkCyclic(result, nn)
- let wasMovedCall = c.genWasMoved(nn)
- result.add wasMovedCall
- result.add tempAsNode
-
-proc isCapturedVar(n: PNode): bool =
- let root = getRoot(n)
- if root != nil: result = root.name.s[0] == ':'
-
-proc passCopyToSink(n: PNode; c: var Con; s: var Scope): PNode =
- result = newNodeIT(nkStmtListExpr, n.info, n.typ)
- 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, {})
- m.add p(n, c, s, normal)
- c.finishCopy(m, n, isFromSink = true)
- result.add m
- if isLValue(n) and not isCapturedVar(n) and n.typ.skipTypes(abstractInst).kind != tyRef and c.inSpawn == 0:
- message(c.graph.config, n.info, hintPerformance,
- ("passing '$1' to a sink parameter introduces an implicit copy; " &
- "if possible, rearrange your program's control flow to prevent it") % $n)
- else:
- if c.graph.config.selectedGC in {gcArc, gcOrc}:
- assert(not containsManagedMemory(n.typ))
- if n.typ.skipTypes(abstractInst).kind in {tyOpenArray, tyVarargs}:
- localError(c.graph.config, n.info, "cannot create an implicit openArray copy to be passed to a sink parameter")
- result.add newTree(nkAsgn, tmp, p(n, c, s, normal))
- # Since we know somebody will take over the produced copy, there is
- # no need to destroy it.
- result.add tmp
-
-proc isDangerousSeq(t: PType): bool {.inline.} =
- let t = t.skipTypes(abstractInst)
- result = t.kind == tySequence and tfHasOwned notin t[0].flags
-
-proc containsConstSeq(n: PNode): bool =
- if n.kind == nkBracket and n.len > 0 and n.typ != nil and isDangerousSeq(n.typ):
- return true
- result = false
- case n.kind
- of nkExprEqExpr, nkExprColonExpr, nkHiddenStdConv, nkHiddenSubConv:
- result = containsConstSeq(n[1])
- of nkObjConstr, nkClosure:
- for i in 1.. 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:
- # 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:
- c.graph.globalDestructors.add c.genDestroy(v)
- else:
- s.final.add c.genDestroy(v)
-
-proc processScope(c: var Con; s: var Scope; ret: PNode): PNode =
- result = newNodeI(nkStmtList, ret.info)
- if s.vars.len > 0:
- let varSection = newNodeI(nkVarSection, ret.info)
- for tmp in s.vars:
- varSection.add newTree(nkIdentDefs, newSymNode(tmp), newNodeI(nkEmpty, ret.info),
- newNodeI(nkEmpty, ret.info))
- result.add varSection
- if s.wasMoved.len > 0 or s.final.len > 0:
- let finSection = newNodeI(nkStmtList, ret.info)
- for m in s.wasMoved: finSection.add m
- for i in countdown(s.final.high, 0): finSection.add s.final[i]
- if s.needsTry:
- result.add newTryFinally(ret, finSection)
- else:
- result.add ret
- result.add finSection
- else:
- result.add ret
-
- 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 =
- assert not ret.typ.isEmptyType
- var result = newNodeIT(nkStmtListExpr, ret.info, ret.typ)
- # There is a possibility to do this check: s.wasMoved.len > 0 or s.final.len > 0
- # later and use it to eliminate the temporary when theres no need for it, but its
- # tricky because you would have to intercept moveOrCopy at a certain point
- let tmp = c.getTemp(s.parent[], ret.typ, ret.info)
- tmp.sym.flags = tmpFlags
- let cpy = if hasDestructor(c, ret.typ):
- s.parent[].final.add c.genDestroy(tmp)
- moveOrCopy(tmp, ret, c, s, {IsDecl})
- else:
- newTree(nkFastAsgn, tmp, p(ret, c, s, normal))
-
- if s.vars.len > 0:
- let varSection = newNodeI(nkVarSection, ret.info)
- for tmp in s.vars:
- varSection.add newTree(nkIdentDefs, newSymNode(tmp), newNodeI(nkEmpty, ret.info),
- newNodeI(nkEmpty, ret.info))
- result.add varSection
- let finSection = newNodeI(nkStmtList, ret.info)
- for m in s.wasMoved: finSection.add m
- for i in countdown(s.final.high, 0): finSection.add s.final[i]
- if s.needsTry:
- result.add newTryFinally(newTree(nkStmtListExpr, cpy, processCall(tmp, s.parent[])), finSection)
- else:
- result.add cpy
- result.add finSection
- result.add processCall(tmp, s.parent[])
-
- if s.parent != nil: s.parent[].needsTry = s.parent[].needsTry or s.needsTry
-
- result
-
-template handleNestedTempl(n, processCall: untyped, willProduceStmt = false,
- tmpFlags = {sfSingleUsedTemp}) =
- template maybeVoid(child, s): untyped =
- if isEmptyType(child.typ): p(child, c, s, normal)
- else: processCall(child, s)
-
- case n.kind
- of nkStmtList, nkStmtListExpr:
- # a statement list does not open a new scope
- if n.len == 0: return n
- result = copyNode(n)
- for i in 0.. 0:
- c.inSpawn.dec
-
- let parameters = n[0].typ
- let L = if parameters != nil: parameters.len else: 0
-
- when false:
- var isDangerous = false
- if n[0].kind == nkSym and n[0].sym.magic in {mOr, mAnd}:
- inc c.inDangerousBranch
- isDangerous = true
-
- result = shallowCopy(n)
- for i in 1.. 0):
- result[i] = p(n[i], c, s, sinkArg)
- else:
- result[i] = p(n[i], c, s, normal)
-
- when false:
- if isDangerous:
- dec c.inDangerousBranch
-
- if n[0].kind == nkSym and n[0].sym.magic in {mNew, mNewFinalize}:
- result[0] = copyTree(n[0])
- if c.graph.config.selectedGC in {gcHooks, gcArc, gcOrc}:
- let destroyOld = c.genDestroy(result[1])
- result = newTree(nkStmtList, destroyOld, result)
- else:
- result[0] = p(n[0], c, s, normal)
- if canRaise(n[0]): s.needsTry = true
- if mode == normal:
- result = ensureDestruction(result, n, c, s)
- of nkDiscardStmt: # Small optimization
- result = shallowCopy(n)
- if n[0].kind != nkEmpty:
- result[0] = p(n[0], c, s, normal)
- else:
- result[0] = copyNode(n[0])
- of nkVarSection, nkLetSection:
- # transform; var x = y to var x; x op y where op is a move or copy
- result = newNodeI(nkStmtList, n.info)
- for it in n:
- var ri = it[^1]
- if it.kind == nkVarTuple and hasDestructor(c, ri.typ):
- 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: {})
- 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, 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)
-
- if optCursorInference in g.config.options:
- computeCursors(owner, n, g)
-
- var scope = Scope(body: n)
- let body = p(n, c, scope, normal)
-
- if owner.kind in {skProc, skFunc, skMethod, skIterator, skConverter}:
- let params = owner.typ.n
- for i in 1..---------transformed-to--------->"
- echo renderTree(result, {renderIds})
-
- if g.config.arcToExpand.hasKey(owner.name.s):
- echo "--expandArc: ", owner.name.s
- echo renderTree(result, {renderIr, renderNoComments})
- echo "-- end of expandArc ------------------------"
diff --git a/compiler/installer.ini b/compiler/installer.ini
index 226682715..07fdb13b5 100644
--- a/compiler/installer.ini
+++ b/compiler/installer.ini
@@ -6,17 +6,13 @@ Name: "Nim"
Version: "$version"
Platforms: """
windows: i386;amd64
- linux: i386;hppa;ia64;alpha;amd64;powerpc64;arm;sparc;sparc64;m68k;mips;mipsel;mips64;mips64el;powerpc;powerpc64el;arm64;riscv32;riscv64
- macosx: i386;amd64;powerpc64;arm64
- solaris: i386;amd64;sparc;sparc64
- freebsd: i386;amd64;powerpc64;arm;arm64;riscv64;sparc64;mips;mipsel;mips64;mips64el;powerpc;powerpc64el
+ linux: i386;amd64;powerpc64;arm;sparc;mips;mipsel;powerpc;powerpc64el;arm64
+ macosx: i386;amd64;powerpc64
+ solaris: i386;amd64;sparc
+ freebsd: i386;amd64
netbsd: i386;amd64
- openbsd: i386;amd64;arm;arm64
- dragonfly: i386;amd64
- crossos: amd64
+ openbsd: i386;amd64
haiku: i386;amd64
- android: i386;arm;arm64
- nintendoswitch: arm64
"""
Authors: "Andreas Rumpf"
@@ -36,21 +32,24 @@ App: Console
License: "copying.txt"
[Config]
-Files: "config/*.cfg"
-Files: "config/config.nims"
+Files: "config/nim.cfg"
+Files: "config/nimdoc.cfg"
+Files: "config/nimdoc.tex.cfg"
[Documentation]
; Files: "doc/*.html"
; Files: "doc/*.cfg"
; Files: "doc/*.pdf"
; Files: "doc/*.ini"
-Files: "doc/html/overview.html"
-Start: "doc/html/overview.html"
+Files: "doc/overview.html"
+Start: "doc/overview.html"
[Other]
-Files: "copying.txt"
+Files: "readme.txt;install.txt;contributors.txt;copying.txt"
+Files: "makefile"
Files: "koch.nim"
+Files: "install_nimble.nims"
Files: "icons/nim.ico"
Files: "icons/nim.rc"
@@ -63,56 +62,46 @@ Files: "icons/koch_icon.o"
Files: "compiler"
Files: "doc"
-Files: "doc/html"
Files: "tools"
-Files: "tools/debug/nim-gdb.py"
-Files: "nimpretty"
-Files: "testament"
-Files: "nimsuggest"
-Files: "nimsuggest/tests/*.nim"
-Files: "changelogs/*.md"
+Files: "web/website.ini"
+Files: "web/ticker.html"
+Files: "web/*.nim"
+Files: "web/*.rst"
+Files: "web/*.csv"
+Files: "web/news/*.rst"
+Files: "bin/nimblepkg/*.nim"
+Files: "bin/nimblepkg/*.cfg"
[Lib]
Files: "lib"
[Other]
Files: "examples"
-Files: "dist/nimble"
+#Files: "dist/nimble"
Files: "tests"
[Windows]
Files: "bin/nim.exe"
+Files: "bin/c2nim.exe"
Files: "bin/nimgrep.exe"
Files: "bin/nimsuggest.exe"
Files: "bin/nimble.exe"
-Files: "bin/vccexe.exe"
-Files: "bin/nimgrab.exe"
-Files: "bin/nimpretty.exe"
-Files: "bin/testament.exe"
-Files: "bin/nim-gdb.bat"
+Files: "bin/*.dll"
+Files: "dist/*.dll"
Files: "koch.exe"
-Files: "finish.exe"
-; Files: "bin/downloader.exe"
-
; Files: "dist/mingw"
-Files: r"tools\start.bat"
+Files: "start.bat"
BinPath: r"bin;dist\mingw\bin;dist"
; Section | dir | zipFile | size hint (in KB) | url | exe start menu entry
-Download: r"Documentation|doc|docs.zip|13824|https://nim-lang.org/download/docs-${version}.zip|overview.html"
-Download: r"C Compiler (MingW)|dist|mingw.zip|82944|https://nim-lang.org/download/${mingw}.zip"
-Download: r"Support DLLs|bin|nim_dlls.zip|479|https://nim-lang.org/download/dlls.zip"
-Download: r"Aporia Text Editor|dist|aporia.zip|97997|https://nim-lang.org/download/aporia-0.4.0.zip|aporia-0.4.0\bin\aporia.exe"
+Download: r"Documentation|doc|docs.zip|13824|http://nim-lang.org/download/docs-${version}.zip|overview.html"
+Download: r"C Compiler (MingW)|dist|mingw.zip|82944|http://nim-lang.org/download/${mingw}.zip"
+Download: r"Support DLL's|bin|nim_dlls.zip|479|http://nim-lang.org/download/dlls.zip"
+Download: r"Aporia IDE|dist|aporia.zip|97997|http://nim-lang.org/download/aporia-0.4.0.zip|aporia-0.4.0\bin\aporia.exe"
; for now only NSIS supports optional downloads
-[WinBin]
-Files: "bin/makelink.exe"
-Files: "bin/7zG.exe"
-Files: "bin/*.dll"
-Files: "bin/cacert.pem"
-
[UnixBin]
Files: "bin/nim"
@@ -120,8 +109,6 @@ Files: "bin/nim"
[Unix]
InstallScript: "yes"
UninstallScript: "yes"
-Files: "bin/nim-gdb"
-Files: "build_all.sh"
[InnoSetup]
@@ -129,6 +116,7 @@ path = r"c:\Program Files (x86)\Inno Setup 5\iscc.exe"
flags = "/Q"
[NSIS]
+path = r"c:\Program Files (x86)\NSIS\makensis.exe"
flags = "/V0"
[C_Compiler]
@@ -143,5 +131,5 @@ shortDesc: "The Nim Compiler"
licenses: "bin/nim,MIT;lib/*,MIT;"
[nimble]
-pkgName: "nim"
-pkgFiles: "compiler/*;doc/basicopt.txt;doc/advopt.txt;doc/nimdoc.css"
+pkgName: "compiler"
+pkgFiles: "compiler/*;doc/basicopt.txt;doc/advopt.txt"
diff --git a/compiler/int128.nim b/compiler/int128.nim
deleted file mode 100644
index b0341eb37..000000000
--- a/compiler/int128.nim
+++ /dev/null
@@ -1,574 +0,0 @@
-## 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
-## type is for that purpose.
-
-from math import trunc
-
-when defined(nimPreviewSlimSystem):
- import std/assertions
-
-type
- Int128* = object
- udata: array[4, uint32]
-
-template sdata(arg: Int128, idx: int): int32 =
- # udata and sdata was supposed to be in a union, but unions are
- # handled incorrectly in the VM.
- cast[ptr int32](arg.udata[idx].unsafeAddr)[]
-
-# 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])
-
-template low*(t: typedesc[Int128]): Int128 = Min
-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
- 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 =
- (uint64(a) shl 32) or uint64(b)
-
-proc toInt64*(arg: Int128): int64 =
- if isNegative(arg):
- assert(arg.sdata(3) == -1, "out of range")
- assert(arg.sdata(2) == -1, "out of range")
- else:
- assert(arg.sdata(3) == 0, "out of range")
- assert(arg.sdata(2) == 0, "out of range")
-
- cast[int64](bitconcat(arg.udata[1], arg.udata[0]))
-
-proc toInt64Checked*(arg: Int128; onError: int64): int64 =
- if isNegative(arg):
- if arg.sdata(3) != -1 or arg.sdata(2) != -1:
- return onError
- else:
- if arg.sdata(3) != 0 or arg.sdata(2) != 0:
- return onError
- return cast[int64](bitconcat(arg.udata[1], arg.udata[0]))
-
-proc toInt32*(arg: Int128): int32 =
- if isNegative(arg):
- assert(arg.sdata(3) == -1, "out of range")
- assert(arg.sdata(2) == -1, "out of range")
- assert(arg.sdata(1) == -1, "out of range")
- else:
- assert(arg.sdata(3) == 0, "out of range")
- assert(arg.sdata(2) == 0, "out of range")
- assert(arg.sdata(1) == 0, "out of range")
-
- arg.sdata(0)
-
-proc toInt16*(arg: Int128): int16 =
- if isNegative(arg):
- assert(arg.sdata(3) == -1, "out of range")
- assert(arg.sdata(2) == -1, "out of range")
- assert(arg.sdata(1) == -1, "out of range")
- else:
- assert(arg.sdata(3) == 0, "out of range")
- assert(arg.sdata(2) == 0, "out of range")
- assert(arg.sdata(1) == 0, "out of range")
-
- int16(arg.sdata(0))
-
-proc toInt8*(arg: Int128): int8 =
- if isNegative(arg):
- assert(arg.sdata(3) == -1, "out of range")
- assert(arg.sdata(2) == -1, "out of range")
- assert(arg.sdata(1) == -1, "out of range")
- else:
- assert(arg.sdata(3) == 0, "out of range")
- assert(arg.sdata(2) == 0, "out of range")
- assert(arg.sdata(1) == 0, "out of range")
-
- int8(arg.sdata(0))
-
-proc toInt*(arg: Int128): int =
- when sizeof(int) == 4:
- cast[int](toInt32(arg))
- else:
- cast[int](toInt64(arg))
-
-proc toUInt64*(arg: Int128): uint64 =
- assert(arg.udata[3] == 0)
- assert(arg.udata[2] == 0)
- bitconcat(arg.udata[1], arg.udata[0])
-
-proc toUInt32*(arg: Int128): uint32 =
- assert(arg.udata[3] == 0)
- assert(arg.udata[2] == 0)
- assert(arg.udata[1] == 0)
- arg.udata[0]
-
-proc toUInt16*(arg: Int128): uint16 =
- assert(arg.udata[3] == 0)
- assert(arg.udata[2] == 0)
- assert(arg.udata[1] == 0)
- uint16(arg.udata[0])
-
-proc toUInt8*(arg: Int128): uint8 =
- assert(arg.udata[3] == 0)
- assert(arg.udata[2] == 0)
- assert(arg.udata[1] == 0)
- uint8(arg.udata[0])
-
-proc toUInt*(arg: Int128): uint =
- when sizeof(int) == 4:
- cast[uint](toInt32(arg))
- else:
- cast[uint](toInt64(arg))
-
-proc castToInt64*(arg: Int128): int64 =
- ## Conversion to int64 without range check.
- cast[int64](bitconcat(arg.udata[1], arg.udata[0]))
-
-proc castToUInt64*(arg: Int128): uint64 =
- ## Conversion to uint64 without range check.
- cast[uint64](bitconcat(arg.udata[1], arg.udata[0]))
-
-proc addToHex(result: var string; arg: uint32) =
- for i in 0..<8:
- let idx = (arg shr ((7-i) * 4)) and 0xf
- result.add "0123456789abcdef"[idx]
-
-proc addToHex*(result: var string; arg: Int128) =
- var i = 3
- while i >= 0:
- result.addToHex(arg.udata[i])
- i -= 1
-
-proc toHex*(arg: Int128): string =
- result.addToHex(arg)
-
-proc inc*(a: var Int128, y: uint32 = 1) =
- a.udata[0] += y
- if unlikely(a.udata[0] < y):
- a.udata[1].inc
- if unlikely(a.udata[1] == 0):
- a.udata[2].inc
- if unlikely(a.udata[2] == 0):
- a.udata[3].inc
- doAssert(a.sdata(3) != low(int32), "overflow")
-
-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])
- if tmp2 != 0: return tmp2
- let tmp3 = cmp(a.udata[1], b.udata[1])
- if tmp3 != 0: return tmp3
- 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 =
- 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 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 =
- 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 =
- 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 =
- 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]
- result.udata[3] = a.udata[3] xor b.udata[3]
-
-proc `shr`*(a: Int128, b: int): Int128 =
- let b = b and 127
- if b < 32:
- result.sdata(3) = a.sdata(3) shr b
- result.udata[2] = cast[uint32](bitconcat(a.udata[3], a.udata[2]) shr b)
- result.udata[1] = cast[uint32](bitconcat(a.udata[2], a.udata[1]) shr b)
- result.udata[0] = cast[uint32](bitconcat(a.udata[1], a.udata[0]) shr b)
- elif b < 64:
- if isNegative(a):
- result.sdata(3) = -1
- result.sdata(2) = a.sdata(3) shr (b and 31)
- result.udata[1] = cast[uint32](bitconcat(a.udata[3], a.udata[2]) shr (b and 31))
- result.udata[0] = cast[uint32](bitconcat(a.udata[2], a.udata[1]) shr (b and 31))
- elif b < 96:
- if isNegative(a):
- result.sdata(3) = -1
- result.sdata(2) = -1
- result.sdata(1) = a.sdata(3) shr (b and 31)
- result.udata[0] = cast[uint32](bitconcat(a.udata[3], a.udata[2]) shr (b and 31))
- else: # b < 128
- if isNegative(a):
- result.sdata(3) = -1
- result.sdata(2) = -1
- result.sdata(1) = -1
- result.sdata(0) = a.sdata(3) shr (b and 31)
-
-proc `shl`*(a: Int128, b: int): Int128 =
- let b = b and 127
- if b < 32:
- result.udata[0] = a.udata[0] shl b
- result.udata[1] = cast[uint32]((bitconcat(a.udata[1], a.udata[0]) shl b) shr 32)
- result.udata[2] = cast[uint32]((bitconcat(a.udata[2], a.udata[1]) shl b) shr 32)
- result.udata[3] = cast[uint32]((bitconcat(a.udata[3], a.udata[2]) shl b) shr 32)
- elif b < 64:
- result.udata[0] = 0
- result.udata[1] = a.udata[0] shl (b and 31)
- result.udata[2] = cast[uint32]((bitconcat(a.udata[1], a.udata[0]) shl (b and 31)) shr 32)
- result.udata[3] = cast[uint32]((bitconcat(a.udata[2], a.udata[1]) shl (b and 31)) shr 32)
- elif b < 96:
- result.udata[0] = 0
- result.udata[1] = 0
- result.udata[2] = a.udata[0] shl (b and 31)
- result.udata[3] = cast[uint32]((bitconcat(a.udata[1], a.udata[0]) shl (b and 31)) shr 32)
- else:
- result.udata[0] = 0
- result.udata[1] = 0
- result.udata[2] = 0
- result.udata[3] = a.udata[0] shl (b and 31)
-
-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)
- result.udata[1] = cast[uint32](tmp1)
- let tmp2 = uint64(a.udata[2]) + uint64(b.udata[2]) + (tmp1 shr 32)
- result.udata[2] = cast[uint32](tmp2)
- let tmp3 = uint64(a.udata[3]) + uint64(b.udata[3]) + (tmp2 shr 32)
- result.udata[3] = cast[uint32](tmp3)
-
-proc `+=`*(a: var Int128, b: Int128) =
- a = a + b
-
-proc `-`*(a: Int128): Int128 =
- result = bitnot(a)
- result.inc
-
-proc `-`*(a, b: Int128): Int128 =
- a + (-b)
-
-proc `-=`*(a: var Int128, b: Int128) =
- a = a - b
-
-proc abs*(a: Int128): Int128 =
- if isNegative(a):
- -a
- else:
- a
-
-proc abs(a: int32): int =
- if a < 0: -a else: a
-
-proc `*`(a: Int128, b: uint32): Int128 =
- let tmp0 = uint64(a.udata[0]) * uint64(b)
- let tmp1 = uint64(a.udata[1]) * uint64(b)
- let tmp2 = uint64(a.udata[2]) * uint64(b)
- let tmp3 = uint64(a.udata[3]) * uint64(b)
-
- if unlikely(tmp3 > uint64(high(int32))):
- assert(false, "overflow")
-
- result.udata[0] = cast[uint32](tmp0)
- result.udata[1] = cast[uint32](tmp1) + cast[uint32](tmp0 shr 32)
- result.udata[2] = cast[uint32](tmp2) + cast[uint32](tmp1 shr 32)
- result.udata[3] = cast[uint32](tmp3) + cast[uint32](tmp2 shr 32)
-
-proc `*`*(a: Int128, b: int32): Int128 =
- result = a * cast[uint32](abs(b))
- if b < 0:
- result = -result
-
-proc `*=`*(a: var Int128, b: int32): Int128 =
- result = result * b
-
-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)
- result.udata[3] = cast[uint32](high shr 32)
-
-proc high64(a: Int128): uint64 =
- bitconcat(a.udata[3], a.udata[2])
-
-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])
- 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
-
-proc `*=`*(a: var Int128, b: Int128) =
- a = a * b
-
-import bitops
-
-proc fastLog2*(a: Int128): int =
- if a.udata[3] != 0:
- return 96 + fastLog2(a.udata[3])
- if a.udata[2] != 0:
- return 64 + fastLog2(a.udata[2])
- if a.udata[1] != 0:
- return 32 + fastLog2(a.udata[1])
- if a.udata[0] != 0:
- return fastLog2(a.udata[0])
-
-proc divMod*(dividend, divisor: Int128): tuple[quotient, remainder: Int128] =
- assert(divisor != Zero)
- let isNegativeA = isNegative(dividend)
- let isNegativeB = isNegative(divisor)
-
- var dividend = abs(dividend)
- let divisor = abs(divisor)
-
- if divisor > dividend:
- result.quotient = Zero
- if isNegativeA:
- result.remainder = -dividend
- else:
- result.remainder = dividend
- return
-
- if divisor == dividend:
- if isNegativeA xor isNegativeB:
- result.quotient = NegOne
- else:
- result.quotient = One
- result.remainder = Zero
- return
-
- var denominator = divisor
- var quotient = Zero
-
- # Left aligns the MSB of the denominator and the dividend.
- let shift = fastLog2(dividend) - fastLog2(denominator)
- denominator = denominator shl shift
-
- # Uses shift-subtract algorithm to divide dividend by denominator. The
- # remainder will be left in dividend.
- for i in 0..shift:
- quotient = quotient shl 1
- if dividend >= denominator:
- dividend -= denominator
- quotient = bitor(quotient, One)
-
- denominator = denominator shr 1
-
- if isNegativeA xor isNegativeB:
- result.quotient = -quotient
- else:
- result.quotient = quotient
- if isNegativeA:
- result.remainder = -dividend
- else:
- result.remainder = dividend
-
-proc `div`*(a, b: Int128): Int128 =
- let (a, _) = divMod(a, b)
- return a
-
-proc `mod`*(a, b: Int128): Int128 =
- let (_, b) = divMod(a, b)
- return b
-
-proc addInt128*(result: var string; value: Int128) =
- let initialSize = result.len
- if value == Zero:
- result.add '0'
- elif value == low(Int128):
- result.add "-170141183460469231731687303715884105728"
- else:
- let isNegative = isNegative(value)
- var value = abs(value)
- while value > Zero:
- let (quot, rem) = divMod(value, Ten)
- result.add "0123456789"[rem.toInt64]
- value = quot
- if isNegative:
- result.add '-'
-
- var i = initialSize
- var j = high(result)
- while i < j:
- swap(result[i], result[j])
- i += 1
- 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'})
-
- var isNegative = false
- var pos = pos
- if arg[pos] == '-':
- isNegative = true
- pos += 1
-
- result = Zero
- while pos < arg.len and arg[pos] in '0'..'9':
- result = result * Ten
- result.inc(uint32(arg[pos]) - uint32('0'))
- pos += 1
-
- if isNegative:
- result = -result
-
-# fluff
-
-proc `<`*(a: Int128, b: BiggestInt): bool =
- 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
-
-proc `<=`*(a: BiggestInt, b: Int128): bool =
- cmp(toInt128(a), b) <= 0
-
-proc `==`*(a: Int128, b: BiggestInt): bool =
- a == toInt128(b)
-
-proc `==`*(a: BiggestInt, b: Int128): bool =
- toInt128(a) == b
-
-proc `-`*(a: BiggestInt, b: Int128): Int128 =
- toInt128(a) - b
-
-proc `-`*(a: Int128, b: BiggestInt): Int128 =
- a - toInt128(b)
-
-proc `+`*(a: BiggestInt, b: Int128): Int128 =
- toInt128(a) + b
-
-proc `+`*(a: Int128, b: BiggestInt): Int128 =
- a + toInt128(b)
-
-proc toFloat64*(arg: Int128): float64 =
- let isNegative = isNegative(arg)
- let arg = abs(arg)
-
- let a = float64(bitconcat(arg.udata[1], arg.udata[0]))
- let b = float64(bitconcat(arg.udata[3], arg.udata[2]))
-
- result = a + 18446744073709551616'f64 * b # a + 2^64 * b
- if isNegative:
- result = -result
-
-proc ldexp(x: float64, exp: cint): float64 {.importc: "ldexp", header: "".}
-
-template bitor(a, b, c: Int128): Int128 = bitor(bitor(a, b), c)
-
-proc toInt128*(arg: float64): Int128 =
- let isNegative = arg < 0
- let v0 = ldexp(abs(arg), -100)
- let w0 = uint64(trunc(v0))
- let v1 = ldexp(v0 - float64(w0), 50)
- let w1 = uint64(trunc(v1))
- let v2 = ldexp(v1 - float64(w1), 50)
- let w2 = uint64(trunc(v2))
-
- let res = bitor(toInt128(w0) shl 100, toInt128(w1) shl 50, toInt128(w2))
- if isNegative:
- return -res
- else:
- return res
-
-proc maskUInt64*(arg: Int128): Int128 {.noinit, inline.} =
- result.udata[0] = arg.udata[0]
- result.udata[1] = arg.udata[1]
- result.udata[2] = 0
- result.udata[3] = 0
-
-proc maskUInt32*(arg: Int128): Int128 {.noinit, inline.} =
- result.udata[0] = arg.udata[0]
- result.udata[1] = 0
- result.udata[2] = 0
- result.udata[3] = 0
-
-proc maskUInt16*(arg: Int128): Int128 {.noinit, inline.} =
- result.udata[0] = arg.udata[0] and 0xffff
- result.udata[1] = 0
- result.udata[2] = 0
- result.udata[3] = 0
-
-proc maskUInt8*(arg: Int128): Int128 {.noinit, inline.} =
- result.udata[0] = arg.udata[0] and 0xff
- result.udata[1] = 0
- result.udata[2] = 0
- result.udata[3] = 0
-
-proc maskBytes*(arg: Int128, numbytes: int): Int128 {.noinit.} =
- case numbytes
- of 1:
- return maskUInt8(arg)
- of 2:
- return maskUInt16(arg)
- of 4:
- return maskUInt32(arg)
- of 8:
- return maskUInt64(arg)
- else:
- assert(false, "masking only implemented for 1, 2, 4 and 8 bytes")
diff --git a/compiler/isolation_check.nim b/compiler/isolation_check.nim
deleted file mode 100644
index 2674605dc..000000000
--- a/compiler/isolation_check.nim
+++ /dev/null
@@ -1,151 +0,0 @@
-#
-#
-# The Nim Compiler
-# (c) Copyright 2020 Andreas Rumpf
-#
-# See the file "copying.txt", included in this
-# distribution, for details about the copyright.
-#
-
-## Implementation of the check that `recover` needs, see
-## https://github.com/nim-lang/RFCs/issues/244 for more details.
-
-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 =
- case n.kind
- of nkRecList:
- for i in 0.. 0 and ret[0] != nil:
- result = canAlias(arg, ret[0], marker)
- else:
- result = true
- of tyTuple:
- for i in 0.. 0:
- result = checkIsolate(n[^1])
- else:
- result = false
- else:
- # unanalysable expression:
- result = false
- else:
- # no ref, no cry:
- result = true
diff --git a/compiler/jsgen.nim b/compiler/jsgen.nim
index d0a143793..8a631bb66 100644
--- a/compiler/jsgen.nim
+++ b/compiler/jsgen.nim
@@ -8,48 +8,39 @@
#
# This is the JavaScript code generator.
+# Soon also a PHP code generator. ;-)
discard """
The JS code generator contains only 2 tricks:
Trick 1
-------
-Some locations (for example 'var int') require "fat pointers" (`etyBaseIndex`)
+Some locations (for example 'var int') require "fat pointers" (``etyBaseIndex``)
which are pairs (array, index). The derefence operation is then 'array[index]'.
-Check `mapType` for the details.
+Check ``mapType`` for the details.
Trick 2
-------
It is preferable to generate '||' and '&&' if possible since that is more
idiomatic and hence should be friendlier for the JS JIT implementation. However
-code like `foo and (let bar = baz())` cannot be translated this way. Instead
-the expressions need to be transformed into statements. `isSimpleExpr`
+code like ``foo and (let bar = baz())`` cannot be translated this way. Instead
+the expressions need to be transformed into statements. ``isSimpleExpr``
implements the required case distinction.
"""
import
- ast, trees, magicsys, options,
- nversion, msgs, idents, types,
- ropes, ccgutils, wordrecg, renderer,
- cgmeth, lowerings, sighashes, modulegraphs, lineinfos, rodutils,
- transf, injectdestructors, sourcemap, astmsgs, backendpragmas
-
-import pipelineutils
-
-import json, sets, math, tables, intsets
-import strutils except addf
-
-when defined(nimPreviewSlimSystem):
- import std/[assertions, syncio]
-
+ ast, astalgo, strutils, hashes, trees, platform, magicsys, extccomp, options,
+ nversion, nimsets, msgs, securehash, bitsets, idents, lists, types, os,
+ times, ropes, math, passes, ccgutils, wordrecg, renderer, rodread, rodutils,
+ intsets, cgmeth, lowerings
type
- TJSGen = object of PPassContext
+ TTarget = enum
+ targetJS, targetPHP
+ TJSGen = object of TPassContext
module: PSym
- graph: ModuleGraph
- config: ConfigRef
- sigConflicts: CountTable[SigHash]
+ target: TTarget
BModule = ref TJSGen
TJSTypeKind = enum # necessary JS "types"
@@ -57,7 +48,6 @@ type
etyNull, # null type
etyProc, # proc type
etyBool, # bool type
- etySeq, # Nim seq or string type
etyInt, # JavaScript's int
etyFloat, # JavaScript's float
etyString, # JavaScript's string
@@ -74,84 +64,59 @@ type
res: Rope # result part; index if this is an
# (address, index)-tuple
address: Rope # address of an (address, index)-tuple
- tmpLoc: Rope # tmp var which stores the (address, index)
- # pair to prevent multiple evals.
- # the tmp is initialized upon evaling the
- # address.
- # might be nil.
- # (see `maybeMakeTemp`)
TBlock = object
id: int # the ID of the label; positive means that it
# has been used (i.e. the label should be emitted)
isLoop: bool # whether it's a 'block' or 'while'
- PGlobals = ref object of RootObj
+ TGlobals = object
typeInfo, constants, code: Rope
forwarded: seq[PSym]
generatedSyms: IntSet
typeInfoGenerated: IntSet
+ classes: seq[(PType, Rope)]
unique: int # for temp identifier generation
- inSystem: bool
+ PGlobals = ref TGlobals
PProc = ref TProc
TProc = object
procDef: PNode
prc: PSym
globals, locals, body: Rope
options: TOptions
- optionsStack: seq[TOptions]
module: BModule
g: PGlobals
- generatedParamCopies: IntSet
beforeRetNeeded: bool
+ target: TTarget # duplicated here for faster dispatching
unique: int # for temp identifier generation
blocks: seq[TBlock]
- extraIndent: int
up: PProc # up the call chain; required for closure support
declaredGlobals: IntSet
-template config*(p: PProc): ConfigRef = p.module.config
-
-proc indentLine(p: PProc, r: Rope): Rope =
- var p = p
- var ind = 0
- while true:
- inc ind, p.blocks.len + p.extraIndent
- if p.up == nil or p.up.prc != p.prc.owner:
- break
- p = p.up
- result = repeat(' ', ind*2) & r
-
-template line(p: PProc, added: string) =
- p.body.add(indentLine(p, rope(added)))
-
-template lineF(p: PProc, frmt: FormatStr, args: varargs[Rope]) =
- p.body.add(indentLine(p, ropes.`%`(frmt, args)))
-
-template nested(p, body) =
- inc p.extraIndent
- body
- dec p.extraIndent
+template `|`(a, b: expr): expr {.immediate, dirty.} =
+ (if p.target == targetJS: a else: b)
proc newGlobals(): PGlobals =
new(result)
result.forwarded = @[]
result.generatedSyms = initIntSet()
result.typeInfoGenerated = initIntSet()
+ result.classes = @[]
proc initCompRes(r: var TCompRes) =
- r.address = ""
- r.res = ""
- r.tmpLoc = ""
+ r.address = nil
+ r.res = nil
r.typ = etyNone
r.kind = resNone
proc rdLoc(a: TCompRes): Rope {.inline.} =
- if a.typ != etyBaseIndex:
- result = a.res
- else:
- result = "$1[$2]" % [a.address, a.res]
+ result = a.res
+ when false:
+ if a.typ != etyBaseIndex:
+ result = a.res
+ else:
+ result = "$1[$2]" % [a.address, a.res]
proc newProc(globals: PGlobals, module: BModule, procDef: PNode,
options: TOptions): PProc =
@@ -161,24 +126,18 @@ proc newProc(globals: PGlobals, module: BModule, procDef: PNode,
module: module,
procDef: procDef,
g: globals,
- extraIndent: int(procDef != nil))
- if procDef != nil: result.prc = procDef[namePos].sym
-
-proc initProcOptions(module: BModule): TOptions =
- result = module.config.options
- if PGlobals(module.graph.backend).inSystem:
- result.excl(optStackTrace)
-
-proc newInitProc(globals: PGlobals, module: BModule): PProc =
- result = newProc(globals, module, nil, initProcOptions(module))
+ target: module.target)
+ if procDef != nil: result.prc = procDef.sons[namePos].sym
+ if result.target == targetPHP:
+ result.declaredGlobals = initIntSet()
proc declareGlobal(p: PProc; id: int; r: Rope) =
if p.prc != nil and not p.declaredGlobals.containsOrIncl(id):
p.locals.addf("global $1;$n", [r])
const
- MappedToObject = {tyObject, tyArray, tyTuple, tyOpenArray,
- tySet, tyVarargs}
+ MappedToObject = {tyObject, tyArray, tyArrayConstr, tyTuple, tyOpenArray,
+ tySet, tyBigNum, tyVarargs}
proc mapType(typ: PType): TJSTypeKind =
let t = skipTypes(typ, abstractInst)
@@ -191,61 +150,32 @@ proc mapType(typ: PType): TJSTypeKind =
of tyPointer:
# treat a tyPointer like a typed pointer to an array of bytes
result = etyBaseIndex
- of tyRange, tyDistinct, tyOrdinal, tyProxy, tyLent:
- # tyLent is no-op as JS has pass-by-reference semantics
- result = mapType(t[0])
+ of tyRange, tyDistinct, tyOrdinal, tyConst, tyMutable, tyIter, tyProxy:
+ result = mapType(t.sons[0])
of tyInt..tyInt64, tyUInt..tyUInt64, tyEnum, tyChar: result = etyInt
of tyBool: result = etyBool
of tyFloat..tyFloat128: result = etyFloat
of tySet: result = etyObject # map a set to a table
- of tyString, tySequence: result = etySeq
- of tyObject, tyArray, tyTuple, tyOpenArray, tyVarargs, tyUncheckedArray:
+ of tyString, tySequence: result = etyInt # little hack to get right semantics
+ of tyObject, tyArray, tyArrayConstr, tyTuple, tyOpenArray, tyBigNum,
+ tyVarargs:
result = etyObject
of tyNil: result = etyNull
- of tyGenericParam, tyGenericBody, tyGenericInvocation,
- tyNone, tyFromExpr, tyForward, tyEmpty,
- tyUntyped, tyTyped, tyTypeDesc, tyBuiltInTypeClass, tyCompositeTypeClass,
- tyAnd, tyOr, tyNot, tyAnything, tyVoid:
+ of tyGenericInst, tyGenericParam, tyGenericBody, tyGenericInvocation,
+ tyNone, tyFromExpr, tyForward, tyEmpty, tyFieldAccessor,
+ tyExpr, tyStmt, tyStatic, tyTypeDesc, tyTypeClasses:
result = etyNone
- of tyGenericInst, tyInferred, tyAlias, tyUserTypeClass, tyUserTypeClassInst,
- tySink, tyOwned:
- result = mapType(typ.lastSon)
- of tyStatic:
- if t.n != nil: result = mapType(lastSon t)
- else: result = etyNone
of tyProc: result = etyProc
- of tyCstring: result = etyString
- of tyConcept, tyIterable: doAssert false
+ of tyCString: result = etyString
proc mapType(p: PProc; typ: PType): TJSTypeKind =
- result = mapType(typ)
+ if p.target == targetPHP: result = etyObject
+ else: result = mapType(typ)
-proc mangleName(m: BModule, s: PSym): Rope =
- proc validJsName(name: string): bool =
- result = true
- const reservedWords = ["abstract", "await", "boolean", "break", "byte",
- "case", "catch", "char", "class", "const", "continue", "debugger",
- "default", "delete", "do", "double", "else", "enum", "export", "extends",
- "false", "final", "finally", "float", "for", "function", "goto", "if",
- "implements", "import", "in", "instanceof", "int", "interface", "let",
- "long", "native", "new", "null", "package", "private", "protected",
- "public", "return", "short", "static", "super", "switch", "synchronized",
- "this", "throw", "throws", "transient", "true", "try", "typeof", "var",
- "void", "volatile", "while", "with", "yield"]
- case name
- of reservedWords:
- return false
- else:
- discard
- if name[0] in {'0'..'9'}: return false
- for chr in name:
- if chr notin {'A'..'Z','a'..'z','_','$','0'..'9'}:
- return false
+proc mangleName(s: PSym; target: TTarget): Rope =
result = s.loc.r
- if result == "":
- if s.kind == skField and s.name.s.validJsName:
- result = rope(s.name.s)
- elif s.kind == skTemp:
+ if result == nil:
+ if target == targetJS or s.kind == skTemp:
result = rope(mangle(s.name.s))
else:
var x = newStringOfCap(s.name.s.len)
@@ -253,21 +183,19 @@ proc mangleName(m: BModule, s: PSym): Rope =
while i < s.name.s.len:
let c = s.name.s[i]
case c
- of 'A'..'Z', 'a'..'z', '_', '0'..'9':
+ of 'A'..'Z':
+ if i > 0 and s.name.s[i-1] in {'a'..'z'}:
+ x.add '_'
+ x.add(chr(c.ord - 'A'.ord + 'a'.ord))
+ of 'a'..'z', '_', '0'..'9':
x.add c
else:
x.add("HEX" & toHex(ord(c), 2))
inc i
result = rope(x)
- # From ES5 on reserved words can be used as object field names
- if s.kind != skField:
- if m.config.hcrOn:
- # When hot reloading is enabled, we must ensure that the names
- # of functions and types will be preserved across rebuilds:
- result.add(idOrSig(s, m.module.name.s, m.sigConflicts, m.config))
- else:
- result.add("_")
- result.add(rope(s.id))
+ if s.name.s != "this" and s.kind != skField:
+ add(result, "_")
+ add(result, rope(s.id))
s.loc.r = result
proc escapeJSString(s: string): string =
@@ -284,30 +212,17 @@ proc escapeJSString(s: string): string =
of '\v': result.add("\\v")
of '\\': result.add("\\\\")
of '\"': result.add("\\\"")
- else: result.add(c)
+ else: add(result, c)
result.add("\"")
proc makeJSString(s: string, escapeNonAscii = true): Rope =
- if escapeNonAscii:
+ if s.isNil:
+ result = "null".rope
+ elif escapeNonAscii:
result = strutils.escape(s).rope
else:
result = escapeJSString(s).rope
-proc makeJsNimStrLit(s: string): Rope =
- var x = newStringOfCap(4*s.len+1)
- x.add "["
- var i = 0
- if i < s.len:
- x.addInt int64(s[i])
- inc i
- while i < s.len:
- x.add ","
- x.addInt int64(s[i])
- inc i
- x.add "]"
- result = rope(x)
-
-
include jstypes
proc gen(p: PProc, n: PNode, r: var TCompRes)
@@ -317,41 +232,37 @@ proc genConstant(p: PProc, c: PSym)
proc useMagic(p: PProc, name: string) =
if name.len == 0: return
- var s = magicsys.getCompilerProc(p.module.graph, name)
+ var s = magicsys.getCompilerProc(name)
if s != nil:
- internalAssert p.config, s.kind in {skProc, skFunc, skMethod, skConverter}
+ internalAssert s.kind in {skProc, skMethod, skConverter}
if not p.g.generatedSyms.containsOrIncl(s.id):
let code = genProc(p, s)
- p.g.constants.add(code)
+ add(p.g.constants, code)
else:
- if p.prc != nil:
- globalError(p.config, p.prc.info, "system module needs: " & name)
- else:
- rawMessage(p.config, errGenerated, "system module needs: " & name)
+ # 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:
+ if p.prc != nil: globalError(p.prc.info, errSystemNeeds, name)
+ else: rawMessage(errSystemNeeds, name)
proc isSimpleExpr(p: PProc; n: PNode): bool =
# calls all the way down --> 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} or
+ (p.target == targetJS and 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.. genFieldAccess, nkCheckedFieldExpr -> genCheckedFieldOp
- # but the uses of maybeMakeTempAssignable don't need them
- else:
- result = (a: a, tmp: b)
- else:
- result = (a: a, tmp: b)
-
-template binaryExpr(p: PProc, n: PNode, r: var TCompRes, magic, frmt: string,
- reassign = false) =
- # $1 and $2 in the `frmt` string bind to lhs and rhs of the expr,
- # if $3 or $4 are present they will be substituted with temps for
- # lhs and rhs respectively
+proc binaryExpr(p: PProc, n: PNode, r: var TCompRes, magic, frmt: string) =
var x, y: TCompRes
useMagic(p, magic)
- gen(p, n[1], x)
- gen(p, n[2], y)
-
- var
- a, tmp = x.rdLoc
- b, tmp2 = y.rdLoc
- when reassign:
- (a, tmp) = maybeMakeTempAssignable(p, n[1], x)
- else:
- when "$3" in frmt: (a, tmp) = maybeMakeTemp(p, n[1], x)
- when "$4" in frmt: (b, tmp2) = maybeMakeTemp(p, n[2], y)
-
- r.res = frmt % [a, b, tmp, tmp2]
+ gen(p, n.sons[1], x)
+ gen(p, n.sons[2], y)
+ r.res = frmt % [x.rdLoc, y.rdLoc]
r.kind = resExpr
proc unsignedTrimmerJS(size: BiggestInt): Rope =
case size
- of 1: rope"& 0xff"
- of 2: rope"& 0xffff"
- of 4: rope">>> 0"
- else: rope""
+ of 1: rope"& 0xff"
+ of 2: rope"& 0xffff"
+ of 4: rope">>> 0"
+ else: rope""
+proc unsignedTrimmerPHP(size: BiggestInt): Rope =
+ case size
+ of 1: rope"& 0xff"
+ of 2: rope"& 0xffff"
+ of 4: rope"& 0xffffffff"
+ else: rope""
template unsignedTrimmer(size: BiggestInt): Rope =
- size.unsignedTrimmerJS
+ size.unsignedTrimmerJS | size.unsignedTrimmerPHP
-proc binaryUintExpr(p: PProc, n: PNode, r: var TCompRes, op: string,
- reassign: static[bool] = false) =
+proc binaryUintExpr(p: PProc, n: PNode, r: var TCompRes, op: string, reassign: bool = false) =
var x, y: TCompRes
- gen(p, n[1], x)
- gen(p, n[2], y)
+ gen(p, n.sons[1], x)
+ gen(p, n.sons[2], y)
let trimmer = unsignedTrimmer(n[1].typ.skipTypes(abstractRange).size)
- when reassign:
- let (a, tmp) = maybeMakeTempAssignable(p, n[1], x)
- r.res = "$1 = (($5 $2 $3) $4)" % [a, rope op, y.rdLoc, trimmer, tmp]
+ if reassign:
+ r.res = "$1 = (($1 $2 $3) $4)" % [x.rdLoc, rope op, y.rdLoc, trimmer]
else:
r.res = "(($1 $2 $3) $4)" % [x.rdLoc, rope op, y.rdLoc, trimmer]
- r.kind = resExpr
-template ternaryExpr(p: PProc, n: PNode, r: var TCompRes, magic, frmt: string) =
+proc ternaryExpr(p: PProc, n: PNode, r: var TCompRes, magic, frmt: string) =
var x, y, z: TCompRes
useMagic(p, magic)
- gen(p, n[1], x)
- gen(p, n[2], y)
- gen(p, n[3], z)
+ gen(p, n.sons[1], x)
+ gen(p, n.sons[2], y)
+ gen(p, n.sons[3], z)
r.res = frmt % [x.rdLoc, y.rdLoc, z.rdLoc]
r.kind = resExpr
-template unaryExpr(p: PProc, n: PNode, r: var TCompRes, magic, frmt: string) =
- # $1 binds to n[1], if $2 is present it will be substituted to a tmp of $1
+proc unaryExpr(p: PProc, n: PNode, r: var TCompRes, magic, frmt: string) =
useMagic(p, magic)
- gen(p, n[1], r)
- var a, tmp = r.rdLoc
- if "$2" in frmt: (a, tmp) = maybeMakeTemp(p, n[1], r)
- r.res = frmt % [a, tmp]
+ gen(p, n.sons[1], r)
+ r.res = frmt % [r.rdLoc]
r.kind = resExpr
-proc arithAux(p: PProc, n: PNode, r: var TCompRes, op: TMagic) =
+proc arithAux(p: PProc, n: PNode, r: var TCompRes, op: TMagic, ops: TMagicOps) =
var
x, y: TCompRes
- xLoc, yLoc: Rope
let i = ord(optOverflowCheck notin p.options)
- useMagic(p, jsMagics[op][i])
- if n.len > 2:
- gen(p, n[1], x)
- gen(p, n[2], y)
- xLoc = x.rdLoc
- yLoc = y.rdLoc
+ useMagic(p, ops[op][i])
+ if sonsLen(n) > 2:
+ gen(p, n.sons[1], x)
+ gen(p, n.sons[2], y)
+ r.res = ops[op][i + 2] % [x.rdLoc, y.rdLoc]
else:
- gen(p, n[1], r)
- xLoc = r.rdLoc
-
- template applyFormat(frmt) =
- r.res = frmt % [xLoc, yLoc]
- template applyFormat(frmtA, frmtB) =
- if i == 0: applyFormat(frmtA) else: applyFormat(frmtB)
-
- 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)")
- of mDivI: applyFormat("divInt($1, $2)", "Math.trunc($1 / $2)")
- of mModI: applyFormat("modInt($1, $2)", "Math.trunc($1 % $2)")
- of mSucc: applyFormat("addInt($1, $2)", "($1 + $2)")
- of mPred: applyFormat("subInt($1, $2)", "($1 - $2)")
- of mAddF64: applyFormat("($1 + $2)", "($1 + $2)")
- of mSubF64: applyFormat("($1 - $2)", "($1 - $2)")
- of mMulF64: applyFormat("($1 * $2)", "($1 * $2)")
- of mDivF64: applyFormat("($1 / $2)", "($1 / $2)")
- of mShrI: applyFormat("", "")
- of mShlI:
- if n[1].typ.size <= 4:
- applyFormat("($1 << $2)", "($1 << $2)")
- else:
- 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))")
- of mBitandI: applyFormat("($1 & $2)", "($1 & $2)")
- of mBitorI: applyFormat("($1 | $2)", "($1 | $2)")
- of mBitxorI: applyFormat("($1 ^ $2)", "($1 ^ $2)")
- of mMinI: applyFormat("nimMin($1, $2)", "nimMin($1, $2)")
- of mMaxI: applyFormat("nimMax($1, $2)", "nimMax($1, $2)")
- of mAddU: applyFormat("", "")
- of mSubU: applyFormat("", "")
- of mMulU: applyFormat("", "")
- of mDivU: applyFormat("", "")
- of mModU: applyFormat("($1 % $2)", "($1 % $2)")
- of mEqI: applyFormat("($1 == $2)", "($1 == $2)")
- of mLeI: applyFormat("($1 <= $2)", "($1 <= $2)")
- of mLtI: applyFormat("($1 < $2)", "($1 < $2)")
- of mEqF64: applyFormat("($1 == $2)", "($1 == $2)")
- of mLeF64: applyFormat("($1 <= $2)", "($1 <= $2)")
- of mLtF64: applyFormat("($1 < $2)", "($1 < $2)")
- of mLeU: applyFormat("($1 <= $2)", "($1 <= $2)")
- of mLtU: applyFormat("($1 < $2)", "($1 < $2)")
- of mEqEnum: applyFormat("($1 == $2)", "($1 == $2)")
- of mLeEnum: applyFormat("($1 <= $2)", "($1 <= $2)")
- of mLtEnum: applyFormat("($1 < $2)", "($1 < $2)")
- of mEqCh: applyFormat("($1 == $2)", "($1 == $2)")
- of mLeCh: applyFormat("($1 <= $2)", "($1 <= $2)")
- of mLtCh: applyFormat("($1 < $2)", "($1 < $2)")
- of mEqB: applyFormat("($1 == $2)", "($1 == $2)")
- of mLeB: applyFormat("($1 <= $2)", "($1 <= $2)")
- of mLtB: applyFormat("($1 < $2)", "($1 < $2)")
- of mEqRef: applyFormat("($1 == $2)", "($1 == $2)")
- of mLePtr: applyFormat("($1 <= $2)", "($1 <= $2)")
- of mLtPtr: applyFormat("($1 < $2)", "($1 < $2)")
- of mXor: applyFormat("($1 != $2)", "($1 != $2)")
- of mEqCString: applyFormat("($1 == $2)", "($1 == $2)")
- of mEqProc: applyFormat("($1 == $2)", "($1 == $2)")
- of mUnaryMinusI: applyFormat("negInt($1)", "-($1)")
- of mUnaryMinusI64: applyFormat("negInt64($1)", "-($1)")
- of mAbsI: applyFormat("absInt($1)", "Math.abs($1)")
- of mNot: applyFormat("!($1)", "!($1)")
- of mUnaryPlusI: applyFormat("+($1)", "+($1)")
- of mBitnotI: applyFormat("~($1)", "~($1)")
- of mUnaryPlusF64: applyFormat("+($1)", "+($1)")
- 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 mCStrToStr: applyFormat("cstrToNimstr($1)", "cstrToNimstr($1)")
- of mStrToStr, mUnown, mIsolate, mFinished: applyFormat("$1", "$1")
- else:
- assert false, $op
+ gen(p, n.sons[1], r)
+ r.res = ops[op][i + 2] % [r.rdLoc]
proc arith(p: PProc, n: PNode, r: var TCompRes, op: TMagic) =
case op
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)
+ if p.target == targetPHP:
+ var x, y: TCompRes
+ gen(p, n.sons[1], x)
+ gen(p, n.sons[2], y)
+ r.res = "intval($1 / $2)" % [x.rdLoc, y.rdLoc]
+ else:
+ arithAux(p, n, r, op, jsOps)
of mModI:
- arithAux(p, n, r, op)
+ if p.target == targetPHP:
+ var x, y: TCompRes
+ gen(p, n.sons[1], x)
+ gen(p, n.sons[2], y)
+ r.res = "($1 % $2)" % [x.rdLoc, y.rdLoc]
+ else:
+ arithAux(p, n, r, op, jsOps)
of mShrI:
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:
- arithAux(p, n, r, op)
- of mEqRef:
- if mapType(n[1].typ) != etyBaseIndex:
- arithAux(p, n, r, op)
+ gen(p, n.sons[1], x)
+ gen(p, n.sons[2], y)
+ let trimmer = unsignedTrimmer(n[1].typ.skipTypes(abstractRange).size)
+ if p.target == targetPHP:
+ # XXX prevent multi evaluations
+ r.res = "(($1 $2) >= 0) ? (($1 $2) >> $3) : ((($1 $2) & 0x7fffffff) >> $3) | (0x40000000 >> ($3 - 1))" % [x.rdLoc, trimmer, y.rdLoc]
else:
- var x, y: TCompRes
- gen(p, n[1], x)
- gen(p, n[2], y)
- r.res = "($# == $# && $# == $#)" % [x.address, y.address, x.res, y.res]
+ r.res = "(($1 $2) >>> $3)" % [x.rdLoc, trimmer, y.rdLoc]
+ of mCharToStr, mBoolToStr, mIntToStr, mInt64ToStr, mFloatToStr,
+ mCStrToStr, mStrToStr, mEnumToStr:
+ if p.target == targetPHP:
+ if op == mEnumToStr:
+ var x: TCompRes
+ gen(p, n.sons[1], x)
+ r.res = "$#[$#]" % [genEnumInfoPHP(p, n.sons[1].typ), x.rdLoc]
+ elif op == mCharToStr:
+ var x: TCompRes
+ gen(p, n.sons[1], x)
+ r.res = "chr($#)" % [x.rdLoc]
+ else:
+ gen(p, n.sons[1], r)
+ else:
+ arithAux(p, n, r, op, jsOps)
else:
- arithAux(p, n, r, op)
+ arithAux(p, n, r, op, jsOps)
r.kind = resExpr
-proc hasFrameInfo(p: PProc): bool =
- ({optLineTrace, optStackTrace} * p.options == {optLineTrace, optStackTrace}) and
- ((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)
- ]
-
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):
- lineF(p, "F.line = $1;$n", [rope(line)])
+ if optLineDir in p.options:
+ addf(p.body, "// line $2 \"$1\"$n",
+ [rope(toFilename(n.info)), rope(line)])
+ if {optStackTrace, optEndb} * p.options == {optStackTrace, optEndb} and
+ ((p.prc == nil) or sfPure notin p.prc.flags):
+ useMagic(p, "endb")
+ addf(p.body, "endb($1);$n", [rope(line)])
+ elif ({optLineTrace, optStackTrace} * p.options ==
+ {optLineTrace, optStackTrace}) and
+ ((p.prc == nil) or not (sfPure in p.prc.flags)):
+ addf(p.body, "F.line = $1;$n" | "$$F['line'] = $1;$n", [rope(line)])
proc genWhileStmt(p: PProc, n: PNode) =
- var cond: TCompRes
- internalAssert p.config, isEmptyType(n.typ)
+ var
+ cond: TCompRes
+ internalAssert isEmptyType(n.typ)
genLineDir(p, n)
inc(p.unique)
- setLen(p.blocks, p.blocks.len + 1)
- p.blocks[^1].id = -p.unique
- p.blocks[^1].isLoop = true
+ var length = len(p.blocks)
+ setLen(p.blocks, length + 1)
+ p.blocks[length].id = -p.unique
+ p.blocks[length].isLoop = true
let labl = p.unique.rope
- lineF(p, "Label$1: while (true) {$n", [labl])
- p.nested: gen(p, n[0], cond)
- lineF(p, "if (!$1) break Label$2;$n",
+ addf(p.body, "L$1: while (true) {$n" | "while (true) {$n", [labl])
+ gen(p, n.sons[0], cond)
+ addf(p.body, "if (!$1) break L$2;$n" | "if (!$1) goto L$2;$n",
[cond.res, labl])
- p.nested: genStmt(p, n[1])
- lineF(p, "}$n", [labl])
- setLen(p.blocks, p.blocks.len - 1)
+ genStmt(p, n.sons[1])
+ addf(p.body, "}$n" | "}L$#:;$n", [labl])
+ setLen(p.blocks, length)
proc moveInto(p: PProc, src: var TCompRes, dest: TCompRes) =
if src.kind != resNone:
if dest.kind != resNone:
- lineF(p, "$1 = $2;$n", [dest.rdLoc, src.rdLoc])
+ p.body.addf("$1 = $2;$n", [dest.rdLoc, src.rdLoc])
else:
- lineF(p, "$1;$n", [src.rdLoc])
+ p.body.addf("$1;$n", [src.rdLoc])
src.kind = resNone
- src.res = ""
+ src.res = nil
proc genTry(p: PProc, n: PNode, r: var TCompRes) =
# code to generate:
#
# ++excHandler;
- # var tmpFramePtr = framePtr;
# try {
# stmts;
- # --excHandler;
- # } catch (EXCEPTION) {
- # var prevJSError = lastJSError; lastJSError = EXCEPTION;
- # framePtr = tmpFramePtr;
+ # } catch (EXC) {
+ # var prevJSError = lastJSError; lastJSError = EXC;
# --excHandler;
# if (e.typ && e.typ == NTI433 || e.typ == NTI2321) {
# stmts;
@@ -789,7 +568,6 @@ proc genTry(p: PProc, n: PNode, r: var TCompRes) =
# }
# lastJSError = prevJSError;
# } finally {
- # framePtr = tmpFramePtr;
# stmts;
# }
genLineDir(p, n)
@@ -798,254 +576,173 @@ proc genTry(p: PProc, n: PNode, r: var TCompRes) =
r.res = getTemp(p)
inc(p.unique)
var i = 1
- var catchBranchesExist = n.len > 1 and n[i].kind == nkExceptBranch
- if catchBranchesExist:
- p.body.add("++excHandler;\L")
- var tmpFramePtr = rope"F"
- lineF(p, "try {$n", [])
+ var length = sonsLen(n)
+ var catchBranchesExist = length > 1 and n.sons[i].kind == nkExceptBranch
+ if catchBranchesExist and p.target == targetJS:
+ add(p.body, "++excHandler;" & tnl)
+ var safePoint = "Tmp$1" % [rope(p.unique)]
+ if optStackTrace in p.options: add(p.body, "framePtr = F;" & tnl)
+ addf(p.body, "try {$n", [])
+ if p.target == targetPHP and p.globals == nil:
+ p.globals = "global $lastJSError; global $prevJSError;".rope
var a: TCompRes
- gen(p, n[0], a)
+ gen(p, n.sons[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])
- while i < n.len and n[i].kind == nkExceptBranch:
- if n[i].len == 1:
+ let dollar = rope(if p.target == targetJS: "" else: "$")
+ if p.target == targetJS and catchBranchesExist:
+ addf(p.body, "} catch (EXC) {$n var prevJSError = lastJSError;$n" &
+ " lastJSError = EXC;$n --excHandler;$n", [])
+ elif p.target == targetPHP:
+ addf(p.body, "} catch (Exception $$EXC) {$n $$prevJSError = $$lastJSError;$n $$lastJSError = $$EXC;$n", [])
+ while i < length and n.sons[i].kind == nkExceptBranch:
+ let blen = sonsLen(n.sons[i])
+ if blen == 1:
# general except section:
generalCatchBranchExists = true
- if i > 1: lineF(p, "else {$n", [])
- gen(p, n[i][0], a)
+ if i > 1: addf(p.body, "else {$n", [])
+ gen(p, n.sons[i].sons[0], a)
moveInto(p, a, r)
- if i > 1: lineF(p, "}$n", [])
+ if i > 1: addf(p.body, "}$n", [])
else:
- var orExpr: Rope = ""
- var excAlias: PNode = nil
-
+ var orExpr: Rope = nil
useMagic(p, "isObj")
- for j in 0.. 1: line(p, "else ")
- lineF(p, "if (lastJSError && ($1)) {$n", [orExpr])
- # If some branch requires a local alias introduce it here. This is needed
- # since JS cannot do ``catch x as y``.
- if excAlias != nil:
- excAlias.sym.loc.r = mangleName(p.module, excAlias.sym)
- lineF(p, "var $1 = lastJSError;$n", excAlias.sym.loc.r)
- gen(p, n[i][^1], a)
+ for j in countup(0, blen - 2):
+ if n.sons[i].sons[j].kind != nkType:
+ internalError(n.info, "genTryStmt")
+ if orExpr != nil: add(orExpr, "||")
+ addf(orExpr, "isObj($2lastJSError.m_type, $1)",
+ [genTypeInfo(p, n.sons[i].sons[j].typ), dollar])
+ if i > 1: add(p.body, "else ")
+ addf(p.body, "if ($3lastJSError && ($2)) {$n",
+ [safePoint, orExpr, dollar])
+ gen(p, n.sons[i].sons[blen - 1], a)
moveInto(p, a, r)
- lineF(p, "}$n", [])
+ addf(p.body, "}$n", [])
inc(i)
if catchBranchesExist:
if not generalCatchBranchExists:
useMagic(p, "reraiseException")
- line(p, "else {\L")
- line(p, "\treraiseException();\L")
- line(p, "}\L")
- lineF(p, "lastJSError = prevJSError;$n")
- line(p, "} finally {\L")
- if hasFrameInfo(p):
- line(p, "framePtr = $1;$n" % [tmpFramePtr])
- if i < n.len and n[i].kind == nkFinally:
- genStmt(p, n[i][0])
- line(p, "}\L")
+ add(p.body, "else {" & tnl & "reraiseException();" & tnl & "}" & tnl)
+ addf(p.body, "$1lastJSError = $1prevJSError;$n", [dollar])
+ if p.target == targetJS:
+ add(p.body, "} finally {" & tnl)
+ if p.target == targetPHP:
+ # XXX ugly hack for PHP codegen
+ add(p.body, "}" & tnl)
+ if i < length and n.sons[i].kind == nkFinally:
+ genStmt(p, n.sons[i].sons[0])
+ if p.target == targetPHP:
+ # XXX ugly hack for PHP codegen
+ add(p.body, "if($lastJSError) throw($lastJSError);" & tnl)
+ if p.target == targetJS:
+ add(p.body, "}" & tnl)
proc genRaiseStmt(p: PProc, n: PNode) =
- if n[0].kind != nkEmpty:
+ genLineDir(p, n)
+ if n.sons[0].kind != nkEmpty:
var a: TCompRes
- gen(p, n[0], a)
- let typ = skipTypes(n[0].typ, abstractPtrs)
- genLineDir(p, n)
+ gen(p, n.sons[0], a)
+ let typ = skipTypes(n.sons[0].typ, abstractPtrs)
useMagic(p, "raiseException")
- lineF(p, "raiseException($1, $2);$n",
- [a.rdLoc, makeJSString(typ.sym.name.s)])
+ addf(p.body, "raiseException($1, $2);$n",
+ [a.rdLoc, makeJSString(typ.sym.name.s)])
else:
- genLineDir(p, n)
useMagic(p, "reraiseException")
- line(p, "reraiseException();\L")
+ add(p.body, "reraiseException();" & tnl)
proc genCaseJS(p: PProc, n: PNode, r: var TCompRes) =
var
- a, b, cond, stmt: TCompRes
+ cond, stmt: TCompRes
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:
+ gen(p, n.sons[0], cond)
+ let stringSwitch = skipTypes(n.sons[0].typ, abstractVar).kind == tyString
+ if stringSwitch and p.target == targetJS:
useMagic(p, "toJSStr")
- lineF(p, "switch (toJSStr($1)) {$n", [cond.rdLoc])
- of tyFloat..tyFloat128, tyInt..tyInt64, tyUInt..tyUInt64:
- transferRange = true
+ addf(p.body, "switch (toJSStr($1)) {$n", [cond.rdLoc])
else:
- lineF(p, "switch ($1) {$n", [cond.rdLoc])
+ addf(p.body, "switch ($1) {$n", [cond.rdLoc])
if not isEmptyType(n.typ):
r.kind = resVal
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.sons[0])
+ while v.intVal <= e.sons[1].intVal:
+ gen(p, v, cond)
+ addf(p.body, "case $1: ", [cond.rdLoc])
+ inc(v.intVal)
else:
- if anyString:
+ if stringSwitch:
case e.kind
- of nkStrLit..nkTripleStrLit: lineF(p, "case $1:$n",
+ of nkStrLit..nkTripleStrLit: addf(p.body, "case $1: ",
[makeJSString(e.strVal, false)])
- of nkNilLit: lineF(p, "case null:$n", [])
- else: internalError(p.config, e.info, "jsgen.genCaseStmt: 2")
+ else: internalError(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, "){", [])
- p.nested:
- gen(p, lastSon(it), stmt)
- moveInto(p, stmt, r)
- if transferRange:
- lineF(p, "}$n", [])
- else:
- lineF(p, "break;$n", [])
+ gen(p, e, cond)
+ addf(p.body, "case $1: ", [cond.rdLoc])
+ gen(p, lastSon(it), stmt)
+ moveInto(p, stmt, r)
+ addf(p.body, "$nbreak;$n", [])
of nkElse:
- if transferRange:
- lineF(p, "else{$n", [])
- else:
- 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", [])
- else: internalError(p.config, it.info, "jsgen.genCaseStmt")
- if not transferRange:
- lineF(p, "}$n", [])
+ addf(p.body, "default: $n", [])
+ gen(p, it.sons[0], stmt)
+ moveInto(p, stmt, r)
+ addf(p.body, "break;$n", [])
+ else: internalError(it.info, "jsgen.genCaseStmt")
+ addf(p.body, "}$n", [])
proc genBlock(p: PProc, n: PNode, r: var TCompRes) =
inc(p.unique)
- let idx = p.blocks.len
- if n[0].kind != nkEmpty:
+ let idx = len(p.blocks)
+ if n.sons[0].kind != nkEmpty:
# named block?
- if (n[0].kind != nkSym): internalError(p.config, n.info, "genBlock")
- var sym = n[0].sym
+ if (n.sons[0].kind != nkSym): internalError(n.info, "genBlock")
+ var sym = n.sons[0].sym
sym.loc.k = locOther
sym.position = idx+1
- let labl = p.unique
- lineF(p, "Label$1: {$n", [labl.rope])
setLen(p.blocks, idx + 1)
p.blocks[idx].id = - p.unique # negative because it isn't used yet
- gen(p, n[1], r)
+ let labl = p.unique
+ addf(p.body, "L$1: do {$n" | "", [labl.rope])
+ gen(p, n.sons[1], r)
+ addf(p.body, "} while(false);$n" | "$nL$#:;$n", [labl.rope])
setLen(p.blocks, idx)
- lineF(p, "};$n", [labl.rope])
proc genBreakStmt(p: PProc, n: PNode) =
var idx: int
genLineDir(p, n)
- if n[0].kind != nkEmpty:
+ if n.sons[0].kind != nkEmpty:
# named break?
- assert(n[0].kind == nkSym)
- let sym = n[0].sym
+ assert(n.sons[0].kind == nkSym)
+ let sym = n.sons[0].sym
assert(sym.loc.k == locOther)
idx = sym.position-1
else:
# an unnamed 'break' can only break a loop after 'transf' pass:
- idx = p.blocks.len - 1
+ idx = len(p.blocks) - 1
while idx >= 0 and not p.blocks[idx].isLoop: dec idx
if idx < 0 or not p.blocks[idx].isLoop:
- internalError(p.config, n.info, "no loop to break")
+ internalError(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)])
+ addf(p.body, "break L$1;$n" | "goto L$1;$n", [rope(p.blocks[idx].id)])
proc genAsmOrEmitStmt(p: PProc, n: PNode) =
genLineDir(p, n)
- p.body.add p.indentLine("")
- for i in 0.. 0:
- lineF(p, "else {$n", [])
+ addf(p.body, "else {$n", [])
inc(toClose)
- p.nested: gen(p, it[0], cond)
- lineF(p, "if ($1) {$n", [cond.rdLoc])
- gen(p, it[1], stmt)
+ gen(p, it.sons[0], cond)
+ addf(p.body, "if ($1) {$n", [cond.rdLoc])
+ gen(p, it.sons[1], stmt)
else:
# else part:
- lineF(p, "else {$n", [])
- p.nested: gen(p, it[0], stmt)
+ addf(p.body, "else {$n", [])
+ gen(p, it.sons[0], stmt)
moveInto(p, stmt, r)
- lineF(p, "}$n", [])
- line(p, repeat('}', toClose) & "\L")
+ addf(p.body, "}$n", [])
+ add(p.body, repeat('}', toClose) & tnl)
proc generateHeader(p: PProc, typ: PType): Rope =
- result = ""
- for i in 1..= 2 and x[0].typ.skipTypes(abstractInst).kind == tyCstring:
- localError(p.config, x.info, "cstring doesn't support `[]=` operator")
+ if p.target == targetPHP and x.kind == nkBracketExpr and
+ x[0].typ.skipTypes(abstractVar).kind in {tyString, tyCString}:
+ var c: TCompRes
+ gen(p, x[0], a)
+ gen(p, x[1], b)
+ gen(p, y, c)
+ addf(p.body, "$#[$#] = chr($#);$n", [a.rdLoc, b.rdLoc, c.rdLoc])
+ return
+
+ let xtyp = mapType(p, x.typ)
+
+ if x.kind == nkHiddenDeref and x.sons[0].kind == nkCall and xtyp != etyObject:
+ gen(p, x.sons[0], a)
+ let tmp = p.getTemp(false)
+ addf(p.body, "var $1 = $2;$n", [tmp, a.rdLoc])
+ a.res = "$1[0][$1[1]]" % [tmp]
+ else:
+ gen(p, x, a)
- gen(p, x, a)
- genLineDir(p, y)
gen(p, y, b)
- # we don't care if it's an etyBaseIndex (global) of a string, it's
- # still a string that needs to be copied properly:
- if x.typ.skipTypes(abstractInst).kind in {tySequence, tyString}:
- xtyp = etySeq
case xtyp
- of etySeq:
- if x.typ.kind in {tyVar, tyLent} or (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:
- lineF(p, "$1 = $2;$n", [a.rdLoc, b.rdLoc])
+ if (needsNoCopy(p, y) and needsNoCopy(p, x)) or noCopyNeeded:
+ addf(p.body, "$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)])
- else:
- lineF(p, "$1 = nimCopy($1, $2, $3);$n",
- [a.res, b.res, genTypeInfo(p, x.typ)])
+ addf(p.body, "nimCopy($1, $2, $3);$n",
+ [a.res, b.res, genTypeInfo(p, y.typ)])
of etyBaseIndex:
if a.typ != etyBaseIndex or b.typ != etyBaseIndex:
if y.kind == nkCall:
let tmp = p.getTemp(false)
- lineF(p, "var $1 = $4; $2 = $1[0]; $3 = $1[1];$n", [tmp, a.address, a.res, b.rdLoc])
- elif b.typ == etyBaseIndex:
- lineF(p, "$# = [$#, $#];$n", [a.res, b.address, b.res])
- elif b.typ == etyNone:
- internalAssert p.config, b.address == ""
- 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])
+ addf(p.body, "var $1 = $4; $2 = $1[0]; $3 = $1[1];$n", [tmp, a.address, a.res, b.rdLoc])
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])
+ internalError(x.info, "genAsgn")
else:
- lineF(p, "$1 = $2;$n", [a.address, b.res])
+ addf(p.body, "$1 = $2; $3 = $4;$n", [a.address, b.address, a.res, b.res])
else:
- lineF(p, "$1 = $2;$n", [a.rdLoc, b.rdLoc])
+ addf(p.body, "$1 = $2;$n", [a.res, b.res])
proc genAsgn(p: PProc, n: PNode) =
- genAsgnAux(p, n[0], n[1], noCopyNeeded=false)
+ genLineDir(p, n)
+ genAsgnAux(p, n.sons[0], n.sons[1], noCopyNeeded=p.target == targetPHP)
proc genFastAsgn(p: PProc, n: PNode) =
- # 'shallowCopy' always produced 'noCopyNeeded = true' here but this is wrong
- # for code like
- # while j >= pos:
- # dest[i].shallowCopy(dest[j])
- # See bug #5933. So we try to be more compatible with the C backend semantics
- # here for 'shallowCopy'. This is an educated guess and might require further
- # changes later:
- let noCopy = n[0].typ.skipTypes(abstractInst).kind in {tySequence, tyString}
- genAsgnAux(p, n[0], n[1], noCopyNeeded=noCopy)
+ genLineDir(p, n)
+ genAsgnAux(p, n.sons[0], n.sons[1], noCopyNeeded=true)
proc genSwap(p: PProc, n: PNode) =
var a, b: TCompRes
- gen(p, n[1], a)
- gen(p, n[2], b)
+ gen(p, n.sons[1], a)
+ gen(p, n.sons[2], b)
var tmp = p.getTemp(false)
- if mapType(p, skipTypes(n[1].typ, abstractVar)) == etyBaseIndex:
+ if mapType(p, skipTypes(n.sons[1].typ, abstractVar)) == etyBaseIndex:
let tmp2 = p.getTemp(false)
if a.typ != etyBaseIndex or b.typ != etyBaseIndex:
- internalError(p.config, n.info, "genSwap")
- lineF(p, "var $1 = $2; $2 = $3; $3 = $1;$n",
- [tmp, a.address, b.address])
+ internalError(n.info, "genSwap")
+ addf(p.body, "var $1 = $2; $2 = $3; $3 = $1;$n" |
+ "$1 = $2; $2 = $3; $3 = $1;$n", [
+ tmp, a.address, b.address])
tmp = tmp2
- lineF(p, "var $1 = $2; $2 = $3; $3 = $1;",
- [tmp, a.res, b.res])
+ addf(p.body, "var $1 = $2; $2 = $3; $3 = $1;" |
+ "$1 = $2; $2 = $3; $3 = $1;", [tmp, a.res, b.res])
-proc getFieldPosition(p: PProc; f: PNode): int =
+proc getFieldPosition(f: PNode): int =
case f.kind
of nkIntLit..nkUInt64Lit: result = int(f.intVal)
of nkSym: result = f.sym.position
- else: internalError(p.config, f.info, "genFieldPosition")
+ else: internalError(f.info, "genFieldPosition")
proc genFieldAddr(p: PProc, n: PNode, r: var TCompRes) =
var a: TCompRes
r.typ = etyBaseIndex
- let b = if n.kind == nkHiddenAddr: n[0] else: n
- gen(p, b[0], a)
- if skipTypes(b[0].typ, abstractVarRange).kind == tyTuple:
- r.res = makeJSString("Field" & $getFieldPosition(p, b[1]))
+ let b = if n.kind == nkHiddenAddr: n.sons[0] else: n
+ gen(p, b.sons[0], a)
+ if skipTypes(b.sons[0].typ, abstractVarRange).kind == tyTuple:
+ if p.target == targetJS:
+ r.res = makeJSString( "Field" & $getFieldPosition(b.sons[1]) )
+ else:
+ r.res = getFieldPosition(b.sons[1]).rope
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 b.sons[1].kind != nkSym: internalError(b.sons[1].info, "genFieldAddr")
+ var f = b.sons[1].sym
+ if f.loc.r == nil: f.loc.r = mangleName(f, p.target)
r.res = makeJSString($f.loc.r)
- internalAssert p.config, a.typ != etyBaseIndex
+ internalAssert a.typ != etyBaseIndex
r.address = a.res
r.kind = resExpr
proc genFieldAccess(p: PProc, n: PNode, r: var TCompRes) =
- gen(p, n[0], r)
- r.typ = mapType(n.typ)
- let otyp = skipTypes(n[0].typ, abstractVarRange)
-
- template mkTemp(i: int) =
- if r.typ == etyBaseIndex:
- if needsTemp(p, n[i]):
- let tmp = p.getTemp
- r.address = "($1 = $2, $1)[0]" % [tmp, r.res]
- r.res = "$1[1]" % [tmp]
- r.tmpLoc = tmp
- else:
- r.address = "$1[0]" % [r.res]
- r.res = "$1[1]" % [r.res]
+ r.typ = etyNone
+ gen(p, n.sons[0], r)
+ let otyp = skipTypes(n.sons[0].typ, abstractVarRange)
if otyp.kind == tyTuple:
- r.res = ("$1.Field$2") %
- [r.res, getFieldPosition(p, n[1]).rope]
- mkTemp(0)
+ r.res = ("$1.Field$2" | "$1[$2]") %
+ [r.res, getFieldPosition(n.sons[1]).rope]
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)
- r.res = "$1.$2" % [r.res, f.loc.r]
- mkTemp(1)
+ if n.sons[1].kind != nkSym: internalError(n.sons[1].info, "genFieldAccess")
+ var f = n.sons[1].sym
+ if f.loc.r == nil: f.loc.r = mangleName(f, p.target)
+ if p.target == targetJS:
+ r.res = "$1.$2" % [r.res, f.loc.r]
+ else:
+ if {sfImportc, sfExportc} * f.flags != {}:
+ r.res = "$1->$2" % [r.res, f.loc.r]
+ else:
+ r.res = "$1['$2']" % [r.res, f.loc.r]
r.kind = resExpr
proc genAddr(p: PProc, n: PNode, r: var TCompRes)
-proc genCheckedFieldOp(p: PProc, n: PNode, addrTyp: PType, r: var TCompRes) =
- internalAssert p.config, n.kind == nkCheckedFieldExpr
- # nkDotExpr to access the requested field
- let accessExpr = n[0]
- # nkCall to check if the discriminant is valid
- var checkExpr = n[1]
+proc genCheckedFieldAddr(p: PProc, n: PNode, r: var TCompRes) =
+ let m = if n.kind == nkHiddenAddr: n.sons[0] else: n
+ internalAssert m.kind == nkCheckedFieldExpr
+ genAddr(p, m, r) # XXX
- let negCheck = checkExpr[0].sym.magic == mNot
- if negCheck:
- checkExpr = checkExpr[^1]
-
- # Field symbol
- var field = accessExpr[1].sym
- internalAssert p.config, field.kind == skField
- if field.loc.r == "": 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)
-
- var setx: TCompRes
- gen(p, checkExpr[1], setx)
-
- var obj: TCompRes
- gen(p, accessExpr[0], obj)
- # Avoid evaluating the LHS twice (one to read the discriminant and one to read
- # the field)
- let tmp = p.getTemp()
- lineF(p, "var $1 = $2;$n", tmp, obj.res)
-
- useMagic(p, "raiseFieldError2")
- 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))
-
- if addrTyp != nil and mapType(p, addrTyp) == etyBaseIndex:
- r.typ = etyBaseIndex
- r.res = makeJSString($field.loc.r)
- r.address = tmp
- else:
- r.typ = etyNone
- r.res = "$1.$2" % [tmp, field.loc.r]
- r.kind = resExpr
+proc genCheckedFieldAccess(p: PProc, n: PNode, r: var TCompRes) =
+ genFieldAccess(p, n.sons[0], r) # XXX
proc genArrayAddr(p: PProc, n: PNode, r: var TCompRes) =
var
a, b: TCompRes
- first: Int128
+ first: BiggestInt
r.typ = etyBaseIndex
- let m = if n.kind == nkHiddenAddr: n[0] else: n
- gen(p, m[0], a)
- gen(p, m[1], b)
- #internalAssert p.config, a.typ != etyBaseIndex and b.typ != etyBaseIndex
- let (x, tmp) = maybeMakeTemp(p, m[0], a)
- r.address = x
- var typ = skipTypes(m[0].typ, abstractPtrs)
- if typ.kind == tyArray:
- first = firstOrd(p.config, typ[0])
- if optBoundsCheck in p.options:
+ let m = if n.kind == nkHiddenAddr: n.sons[0] else: n
+ gen(p, m.sons[0], a)
+ gen(p, m.sons[1], b)
+ internalAssert a.typ != etyBaseIndex and b.typ != etyBaseIndex
+ r.address = a.res
+ var typ = skipTypes(m.sons[0].typ, abstractPtrs)
+ if typ.kind in {tyArray, tyArrayConstr}: first = firstOrd(typ.sons[0])
+ else: first = 0
+ if optBoundsCheck in p.options and not isConstExpr(m.sons[1]):
useMagic(p, "chckIndx")
- if first == 0: # save a couple chars
- r.res = "chckIndx($1, 0, ($2).length - 1)" % [b.res, tmp]
+ if p.target == targetPHP:
+ if typ.kind != tyString:
+ r.res = "chckIndx($1, $2, count($3))-$2" % [b.res, rope(first), a.res]
+ else:
+ r.res = "chckIndx($1, $2, strlen($3))-$2" % [b.res, rope(first), a.res]
else:
- r.res = "chckIndx($1, $2, ($3).length + ($2) - 1) - ($2)" % [
- b.res, rope(first), tmp]
+ r.res = "chckIndx($1, $2, $3.length)-$2" % [b.res, rope(first), a.res]
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
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)
+ var ty = skipTypes(n.sons[0].typ, abstractVarRange)
+ if ty.kind in {tyRef, tyPtr}: ty = skipTypes(ty.lastSon, abstractVarRange)
case ty.kind
- of tyArray, tyOpenArray, tySequence, tyString, tyCstring, tyVarargs:
+ of tyArray, tyArrayConstr, tyOpenArray, tySequence, tyString, tyCString,
+ tyVarargs:
genArrayAddr(p, n, r)
of tyTuple:
+ if p.target == targetPHP:
+ genFieldAccess(p, n, r)
+ return
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:
- r.res = "$1.charCodeAt($2)" % [r.address, r.res]
- elif r.typ == etyBaseIndex:
- if needsTemp(p, n[0]):
- let tmp = p.getTemp
- r.address = "($1 = $2, $1)[0]" % [tmp, r.rdLoc]
- r.res = "$1[1]" % [tmp]
- r.tmpLoc = tmp
+ else: internalError(n.info, "expr(nkBracketExpr, " & $ty.kind & ')')
+ r.typ = etyNone
+ if r.res == nil: internalError(n.info, "genArrayAccess")
+ if p.target == targetPHP:
+ if n.sons[0].kind in nkCallKinds+{nkStrLit..nkTripleStrLit}:
+ useMagic(p, "nimAt")
+ if ty.kind in {tyString, tyCString}:
+ # XXX this needs to be more like substr($1,$2)
+ r.res = "ord(nimAt($1, $2))" % [r.address, r.res]
+ else:
+ r.res = "nimAt($1, $2)" % [r.address, r.res]
+ elif ty.kind in {tyString, tyCString}:
+ # XXX this needs to be more like substr($1,$2)
+ r.res = "ord(@$1[$2])" % [r.address, r.res]
else:
- let x = r.rdLoc
- r.address = "$1[0]" % [x]
- r.res = "$1[1]" % [x]
+ r.res = "$1[$2]" % [r.address, r.res]
else:
r.res = "$1[$2]" % [r.address, r.res]
+ r.address = nil
r.kind = resExpr
template isIndirect(x: PSym): bool =
let v = x
({sfAddrTaken, sfGlobal} * v.flags != {} and
#(mapType(v.typ) != etyObject) and
- {sfImportc, sfExportc} * v.flags == {} and
- 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))
+ {sfImportc, sfVolatile, sfExportc} * v.flags == {} and
+ v.kind notin {skProc, skConverter, skMethod, skIterator,
+ skConst, skTemp, skLet} and p.target == targetJS)
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.sons[0].kind
+ of nkSym:
+ let s = n.sons[0].sym
+ if s.loc.r == nil: internalError(n.info, "genAddr: 3")
+ case s.kind
+ 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]"
+ elif p.target == targetPHP:
+ r.res = "&" & s.loc.r
+ 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)
+ gen(p, n.sons[0], r)
+ #internalError(n.info, "genAddr: 4 " & renderTree(n))
+ else: internalError(n.info, "genAddr: 2")
+ of nkCheckedFieldExpr:
+ genCheckedFieldAddr(p, n, r)
+ of nkDotExpr:
+ if mapType(p, n.typ) == etyBaseIndex:
+ genFieldAddr(p, n.sons[0], r)
else:
- internalError(p.config, n[0].info, "genAddr: " & $n[0].kind)
+ genFieldAccess(p, n.sons[0], r)
+ of nkBracketExpr:
+ var ty = skipTypes(n.sons[0].typ, abstractVarRange)
+ if ty.kind in MappedToObject:
+ gen(p, n.sons[0], r)
+ else:
+ let kindOfIndexedExpr = skipTypes(n.sons[0].sons[0].typ, abstractVarRange).kind
+ case kindOfIndexedExpr
+ of tyArray, tyArrayConstr, tyOpenArray, tySequence, tyString, tyCString,
+ tyVarargs:
+ genArrayAddr(p, n.sons[0], r)
+ of tyTuple:
+ genFieldAddr(p, n.sons[0], r)
+ else: internalError(n.sons[0].info, "expr(nkBracketExpr, " & $kindOfIndexedExpr & ')')
+ of nkObjDownConv:
+ gen(p, n.sons[0], r)
+ else: internalError(n.sons[0].info, "genAddr: " & $n.sons[0].kind)
+
+proc thisParam(p: PProc; typ: PType): PType =
+ if p.target == targetPHP:
+ # XXX Might be very useful for the JS backend too?
+ let typ = skipTypes(typ, abstractInst)
+ assert(typ.kind == tyProc)
+ if 1 < sonsLen(typ.n):
+ assert(typ.n.sons[1].kind == nkSym)
+ let param = typ.n.sons[1].sym
+ if param.name.s == "this":
+ result = param.typ.skipTypes(abstractVar)
proc attachProc(p: PProc; content: Rope; s: PSym) =
- p.g.code.add(content)
+ let otyp = thisParam(p, s.typ)
+ if otyp != nil:
+ for i, cls in p.g.classes:
+ if sameType(cls[0], otyp):
+ add(p.g.classes[i][1], content)
+ return
+ p.g.classes.add((otyp, content))
+ else:
+ add(p.g.code, content)
proc attachProc(p: PProc; s: PSym) =
let newp = genProc(p, s)
@@ -1465,68 +1083,52 @@ proc genProcForSymIfNeeded(p: PProc, s: PSym) =
var owner = p
while owner != nil and owner.prc != s.owner:
owner = owner.up
- if owner != nil: owner.locals.add(newp)
+ if owner != nil: add(owner.locals, newp)
else: attachProc(p, newp, s)
-proc genCopyForParamIfNeeded(p: PProc, n: PNode) =
- let s = n.sym
- if p.prc == s.owner or needsNoCopy(p, n):
- return
- var owner = p.up
- while true:
- if owner == nil:
- internalError(p.config, n.info, "couldn't find the owner proc of the closed over param: " & s.name.s)
- if owner.prc == s.owner:
- if not owner.generatedParamCopies.containsOrIncl(s.id):
- let copy = "$1 = nimCopy(null, $1, $2);$n" % [s.loc.r, genTypeInfo(p, s.typ)]
- owner.locals.add(owner.indentLine(copy))
- return
- owner = owner.up
-
-proc genVarInit(p: PProc, v: PSym, n: PNode)
-
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 == "":
- 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))
- if s.kind == skParam:
- genCopyForParamIfNeeded(p, n)
- let k = mapType(p, s.typ)
- if k == etyBaseIndex:
- r.typ = etyBaseIndex
- if {sfAddrTaken, sfGlobal} * s.flags != {}:
- if isIndirect(s):
- r.address = "$1[0][0]" % [s.loc.r]
- r.res = "$1[0][1]" % [s.loc.r]
- else:
+ if s.loc.r == nil:
+ internalError(n.info, "symbol has no generated name: " & s.name.s)
+ if p.target == targetJS:
+ let k = mapType(p, s.typ)
+ if k == etyBaseIndex:
+ r.typ = etyBaseIndex
+ if {sfAddrTaken, sfGlobal} * s.flags != {}:
r.address = "$1[0]" % [s.loc.r]
r.res = "$1[1]" % [s.loc.r]
+ else:
+ r.address = s.loc.r
+ r.res = s.loc.r & "_Idx"
+ elif isIndirect(s):
+ r.res = "$1[0]" % [s.loc.r]
else:
- r.address = s.loc.r
- r.res = s.loc.r & "_Idx"
- elif isIndirect(s):
- r.res = "$1[0]" % [s.loc.r]
+ r.res = s.loc.r
else:
- r.res = s.loc.r
+ r.res = "$" & s.loc.r
+ if sfGlobal in s.flags:
+ p.declareGlobal(s.id, r.res)
of skConst:
genConstant(p, s)
- if s.loc.r == "":
- internalError(p.config, n.info, "symbol has no generated name: " & s.name.s)
- r.res = s.loc.r
- of skProc, skFunc, skConverter, skMethod:
- if sfCompileTime in s.flags:
- localError(p.config, n.info, "request to generate code for .compileTime proc: " &
- 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 s.loc.r == nil:
+ internalError(n.info, "symbol has no generated name: " & s.name.s)
+ if p.target == targetJS:
+ r.res = s.loc.r
+ else:
+ r.res = "$" & s.loc.r
+ p.declareGlobal(s.id, r.res)
+ of skProc, skConverter, skMethod:
+ discard mangleName(s, p.target)
+ if p.target == targetPHP and r.kind != resCallee:
+ r.res = makeJsString($s.loc.r)
+ else:
+ r.res = s.loc.r
+ 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,128 +1136,96 @@ proc genSym(p: PProc, n: PNode, r: var TCompRes) =
else:
genProcForSymIfNeeded(p, s)
else:
- if s.loc.r == "":
- 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
+ if s.loc.r == nil:
+ internalError(n.info, "symbol has no generated name: " & s.name.s)
+ 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:
- gen(p, it, r)
+ if mapType(p, n.sons[0].typ) == etyObject:
+ gen(p, n.sons[0], r)
else:
var a: TCompRes
- gen(p, it, a)
- r.kind = a.kind
- r.typ = mapType(p, n.typ)
- if r.typ == etyBaseIndex:
+ gen(p, n.sons[0], a)
+ if a.typ == etyBaseIndex:
+ r.res = "$1[$2]" % [a.address, a.res]
+ elif n.sons[0].kind == nkCall:
let tmp = p.getTemp
- r.address = "($1 = $2, $1)[0]" % [tmp, a.rdLoc]
- r.res = "$1[1]" % [tmp]
- r.tmpLoc = tmp
- elif a.typ == etyBaseIndex:
- if a.tmpLoc != "":
- r.tmpLoc = a.tmpLoc
- r.res = a.rdLoc
+ r.res = "($1 = $2, $1[0][$1[1]])" % [tmp, a.res]
else:
- internalError(p.config, n.info, "genDeref")
+ internalError(n.info, "genDeref")
proc genArgNoParam(p: PProc, n: PNode, r: var TCompRes) =
var a: TCompRes
gen(p, n, a)
if a.typ == etyBaseIndex:
- r.res.add(a.address)
- r.res.add(", ")
- r.res.add(a.res)
+ add(r.res, a.address)
+ add(r.res, ", ")
+ add(r.res, a.res)
else:
- r.res.add(a.res)
+ add(r.res, a.res)
-proc genArg(p: PProc, n: PNode, param: PSym, r: var TCompRes; emitted: ptr int = nil) =
+proc genArg(p: PProc, n: PNode, param: PSym, r: var TCompRes) =
var a: TCompRes
gen(p, n, a)
if skipTypes(param.typ, abstractVar).kind in {tyOpenArray, tyVarargs} and
a.typ == etyBaseIndex:
- r.res.add("$1[$2]" % [a.address, a.res])
+ add(r.res, "$1[$2]" % [a.address, a.res])
elif a.typ == etyBaseIndex:
- r.res.add(a.address)
- r.res.add(", ")
- r.res.add(a.res)
- if emitted != nil: inc emitted[]
- elif n.typ.kind in {tyVar, tyPtr, tyRef, tyLent, tyOwned} and
- n.kind in nkCallKinds and mapType(param.typ) == etyBaseIndex:
- # this fixes bug #5608:
- let tmp = getTemp(p)
- r.res.add("($1 = $2, $1[0]), $1[1]" % [tmp, a.rdLoc])
- if emitted != nil: inc emitted[]
+ add(r.res, a.address)
+ add(r.res, ", ")
+ add(r.res, a.res)
else:
- r.res.add(a.res)
+ add(r.res, a.res)
proc genArgs(p: PProc, n: PNode, r: var TCompRes; start=1) =
- r.res.add("(")
+ add(r.res, "(")
var hasArgs = false
- var typ = skipTypes(n[0].typ, abstractInst)
+ var typ = skipTypes(n.sons[0].typ, abstractInst)
assert(typ.kind == tyProc)
- assert(typ.len == typ.n.len)
- var emitted = start-1
+ assert(sonsLen(typ) == sonsLen(typ.n))
- for i in start..= n.len:
- globalError(p.config, n.info, "wrong importcpp pattern; expected parameter at position " & $i &
- " but got only: " & $(n.len-1))
let it = n[i]
var paramType: PNode = nil
- if i < typ.len:
- assert(typ.n[i].kind == nkSym)
- paramType = typ.n[i]
+ if i < sonsLen(typ):
+ assert(typ.n.sons[i].kind == nkSym)
+ paramType = typ.n.sons[i]
if paramType.typ.isCompileTimeOnly: return
if paramType.isNil:
genArgNoParam(p, it, r)
else:
genArg(p, it, paramType.sym, r)
- inc generated
proc genPatternCall(p: PProc; n: PNode; pat: string; typ: PType;
r: var TCompRes) =
var i = 0
var j = 1
- r.kind = resExpr
while i < pat.len:
case pat[i]
of '@':
var generated = 0
- for k in j.. 0: r.res.add(", ")
+ for k in j .. < n.len:
+ if generated > 0: add(r.res, ", ")
genOtherArg(p, n, k, typ, generated, r)
inc i
of '#':
@@ -1663,72 +1233,67 @@ proc genPatternCall(p: PProc; n: PNode; pat: string; typ: PType;
genOtherArg(p, n, j, typ, generated, r)
inc j
inc i
- of '\31':
- # unit separator
- r.res.add("#")
- inc i
- of '\29':
- # group separator
- r.res.add("@")
- inc i
else:
let start = i
while i < pat.len:
- if pat[i] notin {'@', '#', '\31', '\29'}: inc(i)
+ if pat[i] notin {'@', '#'}: inc(i)
else: break
if i - 1 >= start:
- r.res.add(substr(pat, start, i - 1))
+ add(r.res, substr(pat, start, i - 1))
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(f, p.target)
if sfInfixCall in f.flags:
- let pat = $n[0].sym.loc.r
- internalAssert p.config, pat.len > 0
+ let pat = n.sons[0].sym.loc.r.data
+ internalAssert pat != nil
if pat.contains({'#', '(', '@'}):
- var typ = skipTypes(n[0].typ, abstractInst)
+ var typ = skipTypes(n.sons[0].typ, abstractInst)
assert(typ.kind == tyProc)
genPatternCall(p, n, pat, typ, r)
return
- if n.len != 1:
- gen(p, n[1], r)
- if r.typ == etyBaseIndex:
- if r.address == "":
- globalError(p.config, n.info, "cannot invoke with infix syntax")
- r.res = "$1[$2]" % [r.address, r.res]
- r.address = ""
- r.typ = etyNone
- r.res.add(".")
+ gen(p, n.sons[1], r)
+ if r.typ == etyBaseIndex:
+ if r.address == nil:
+ globalError(n.info, "cannot invoke with infix syntax")
+ r.res = "$1[$2]" % [r.address, r.res]
+ r.address = nil
+ r.typ = etyNone
+ add(r.res, "." | "->")
var op: TCompRes
- gen(p, n[0], op)
- r.res.add(op.res)
+ if p.target == targetPHP:
+ op.kind = resCallee
+ gen(p, n.sons[0], op)
+ add(r.res, op.res)
genArgs(p, n, r, 2)
proc genCall(p: PProc, n: PNode, r: var TCompRes) =
- gen(p, n[0], r)
+ if n.sons[0].kind == nkSym and thisParam(p, n.sons[0].typ) != nil:
+ genInfixCall(p, n, r)
+ return
+ if p.target == targetPHP:
+ r.kind = resCallee
+ gen(p, n.sons[0], r)
genArgs(p, n, r)
- if n.typ != nil:
- let t = mapType(n.typ)
- if t == etyBaseIndex:
- let tmp = p.getTemp
- r.address = "($1 = $2, $1)[0]" % [tmp, r.rdLoc]
- r.res = "$1[1]" % [tmp]
- r.tmpLoc = tmp
- r.typ = t
proc genEcho(p: PProc, n: PNode, r: var TCompRes) =
let n = n[1].skipConv
- internalAssert p.config, n.kind == nkBracket
- useMagic(p, "toJSStr") # Used in rawEcho
- useMagic(p, "rawEcho")
- r.res.add("rawEcho(")
- for i in 0.. 0: r.res.add(", ")
+ if i > 0: add(r.res, ", " | ".")
genArgNoParam(p, it, r)
- r.res.add(")")
+ add(r.res, ")" | """."\n")""")
r.kind = resExpr
proc putToSeq(s: string, indirect: bool): Rope =
@@ -1739,327 +1304,269 @@ proc createVar(p: PProc, typ: PType, indirect: bool): Rope
proc createRecordVarAux(p: PProc, rec: PNode, excludedFieldIDs: IntSet, output: var Rope) =
case rec.kind
of nkRecList:
- for i in 0.. 0: output.add(", ")
- output.addf("$#: ", [mangleName(p.module, rec.sym)])
+ if p.target == targetJS:
+ output.add(mangleName(rec.sym, p.target))
+ output.add(": ")
+ else:
+ output.addf("'$#' => ", [mangleName(rec.sym, p.target)])
output.add(createVar(p, rec.sym.typ, false))
- else: internalError(p.config, rec.info, "createRecordVarAux")
+ else: internalError(rec.info, "createRecordVarAux")
proc createObjInitList(p: PProc, typ: PType, excludedFieldIDs: IntSet, output: var Rope) =
var t = typ
- if objHasTypeField(t):
+ if tfFinal notin t.flags or t.sons[0] != nil:
if output.len > 0: output.add(", ")
- output.addf("m_type: $1", [genTypeInfo(p, t)])
+ addf(output, "m_type: $1" | "'m_type' => $#", [genTypeInfo(p, t)])
while t != nil:
- t = t.skipTypes(skipPtrs)
createRecordVarAux(p, t.n, excludedFieldIDs, output)
- t = t[0]
+ t = t.sons[0]
proc arrayTypeForElemType(typ: PType): string =
- let typ = typ.skipTypes(abstractRange)
case typ.kind
of tyInt, tyInt32: "Int32Array"
of tyInt16: "Int16Array"
of tyInt8: "Int8Array"
- of tyUInt, tyUInt32: "Uint32Array"
- of tyUInt16: "Uint16Array"
- of tyUInt8, tyChar, tyBool: "Uint8Array"
+ of tyUint, tyUint32: "Uint32Array"
+ of tyUint16: "Uint16Array"
+ 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: ""
+ else: nil
proc createVar(p: PProc, typ: PType, indirect: bool): Rope =
var t = skipTypes(typ, abstractInst)
case t.kind
of tyInt..tyInt64, tyUInt..tyUInt64, tyEnum, tyChar:
- if $t.sym.loc.r == "bigint":
- result = putToSeq("0n", indirect)
- else:
- result = putToSeq("0", indirect)
+ result = putToSeq("0", indirect)
of tyFloat..tyFloat128:
result = putToSeq("0.0", indirect)
- of tyRange, tyGenericInst, tyAlias, tySink, tyOwned, tyLent:
+ of tyRange, tyGenericInst:
result = createVar(p, lastSon(typ), indirect)
of tySet:
- result = putToSeq("{}", indirect)
+ result = putToSeq("{}" | "array()", indirect)
of tyBool:
result = putToSeq("false", indirect)
- of tyNil:
- result = putToSeq("null", indirect)
- of tyArray:
- let length = toInt(lengthOrd(p.config, t))
+ of tyArray, tyArrayConstr:
+ let length = int(lengthOrd(t))
let e = elemType(t)
let jsTyp = arrayTypeForElemType(e)
- if jsTyp.len > 0:
+ if not jsTyp.isNil and p.target == targetJS:
result = "new $1($2)" % [rope(jsTyp), rope(length)]
elif length > 32:
useMagic(p, "arrayConstr")
# XXX: arrayConstr depends on nimCopy. This line shouldn't be necessary.
- useMagic(p, "nimCopy")
+ if p.target == targetJS: useMagic(p, "nimCopy")
result = "arrayConstr($1, $2, $3)" % [rope(length),
createVar(p, e, false), genTypeInfo(p, e)]
else:
- result = rope("[")
+ result = rope("[" | "array(")
var i = 0
while i < length:
- if i > 0: result.add(", ")
- result.add(createVar(p, e, false))
+ if i > 0: add(result, ", ")
+ add(result, createVar(p, e, false))
inc(i)
- result.add("]")
+ add(result, "]" | ")")
if indirect: result = "[$1]" % [result]
of tyTuple:
- result = rope("{")
- for i in 0.. 0: result.add(", ")
- result.addf("Field$1: $2", [i.rope,
- createVar(p, t[i], false)])
- result.add("}")
- if indirect: result = "[$1]" % [result]
+ if p.target == targetJS:
+ result = rope("{")
+ for i in 0.. 0: add(result, ", ")
+ addf(result, "Field$1: $2", [i.rope,
+ createVar(p, t.sons[i], false)])
+ add(result, "}")
+ if indirect: result = "[$1]" % [result]
+ else:
+ result = rope("array(")
+ for i in 0.. 0: add(result, ", ")
+ add(result, createVar(p, t.sons[i], false))
+ add(result, ")")
of tyObject:
var initList: Rope
createObjInitList(p, t, initIntSet(), initList)
- result = ("({$1})") % [initList]
+ result = ("{$1}" | "array($#)") % [initList]
if indirect: result = "[$1]" % [result]
- of tyVar, tyPtr, tyRef, tyPointer:
+ of tyVar, tyPtr, tyRef:
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 tySequence, tyString, tyCString, tyPointer, 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 = ""
else:
- internalError(p.config, "createVar: " & $t.kind)
- result = ""
-
-template returnType: untyped = ""
+ internalError("createVar: " & $t.kind)
+ result = nil
proc genVarInit(p: PProc, v: PSym, n: PNode) =
var
a: TCompRes
s: Rope
- varCode: string
- varName = mangleName(p.module, v)
- useReloadingGuard = sfGlobal in v.flags and p.config.hcrOn
- useGlobalPragmas = sfGlobal in v.flags and ({sfPure, sfThread} * v.flags != {})
-
- if v.constraint.isNil:
- if useReloadingGuard:
- lineF(p, "var $1;$n", varName)
- lineF(p, "if ($1 === undefined) {$n", varName)
- varCode = $varName
- inc p.extraIndent
- elif useGlobalPragmas:
- lineF(p, "if (globalThis.$1 === undefined) {$n", varName)
- varCode = "globalThis." & $varName
- inc p.extraIndent
- else:
- varCode = "var $2"
- else:
- # Is this really a thought through feature? this basically unused
- # feature makes it impossible for almost all format strings in
- # this function to be checked at compile time.
- varCode = v.constraint.strVal
-
if n.kind == nkEmpty:
- if not isIndirect(v) and
- v.typ.kind in {tyVar, tyPtr, tyLent, tyRef, tyOwned} and mapType(p, v.typ) == etyBaseIndex:
- lineF(p, "var $1 = null;$n", [varName])
- lineF(p, "var $1_Idx = 0;$n", [varName])
- else:
- line(p, runtimeFormat(varCode & " = $3;$n", [returnType, varName, createVar(p, v.typ, isIndirect(v))]))
+ addf(p.body, "var $1 = $2;$n" | "$$$1 = $2;$n",
+ [mangleName(v, p.target), createVar(p, v.typ, isIndirect(v))])
else:
+ discard mangleName(v, p.target)
gen(p, n, a)
case mapType(p, v.typ)
- of etyObject, etySeq:
+ of etyObject:
if needsNoCopy(p, n):
s = a.res
else:
useMagic(p, "nimCopy")
s = "nimCopy(null, $1, $2)" % [a.res, genTypeInfo(p, n.typ)]
of etyBaseIndex:
- let targetBaseIndex = {sfAddrTaken, sfGlobal} * v.flags == {}
- if a.typ == etyBaseIndex:
- if targetBaseIndex:
- line(p, runtimeFormat(varCode & " = $3, $2_Idx = $4;$n",
- [returnType, v.loc.r, a.address, a.res]))
- else:
- if isIndirect(v):
- line(p, runtimeFormat(varCode & " = [[$3, $4]];$n",
- [returnType, v.loc.r, a.address, a.res]))
- else:
- line(p, runtimeFormat(varCode & " = [$3, $4];$n",
- [returnType, v.loc.r, a.address, a.res]))
+ if (a.typ != etyBaseIndex): internalError(n.info, "genVarInit")
+ if {sfAddrTaken, sfGlobal} * v.flags != {}:
+ addf(p.body, "var $1 = [$2, $3];$n",
+ [v.loc.r, a.address, a.res])
else:
- if targetBaseIndex:
- let tmp = p.getTemp
- lineF(p, "var $1 = $2, $3 = $1[0], $3_Idx = $1[1];$n",
- [tmp, a.res, v.loc.r])
- else:
- line(p, runtimeFormat(varCode & " = $3;$n", [returnType, v.loc.r, a.res]))
+ addf(p.body, "var $1 = $2; var $1_Idx = $3;$n", [
+ v.loc.r, a.address, a.res])
return
else:
s = a.res
if isIndirect(v):
- line(p, runtimeFormat(varCode & " = [$3];$n", [returnType, v.loc.r, s]))
+ addf(p.body, "var $1 = /**/[$2];$n", [v.loc.r, s])
else:
- line(p, runtimeFormat(varCode & " = $3;$n", [returnType, v.loc.r, s]))
-
- if useReloadingGuard or useGlobalPragmas:
- dec p.extraIndent
- lineF(p, "}$n")
+ addf(p.body, "var $1 = $2;$n" | "$$$1 = $2;$n", [v.loc.r, s])
proc genVarStmt(p: PProc, n: PNode) =
- for i in 0.. 0: r.res.add(", ")
+ gen(p, it[0], a)
+ gen(p, it[1], b)
+ r.res.add("$# => $#" % [a.rdLoc, b.rdLoc])
+ else:
+ localError(it.info, "'toArray' needs tuple constructors")
+ else:
+ localError(x.info, "'toArray' needs an array literal")
r.res.add(")")
proc genRepr(p: PProc, n: PNode, r: var TCompRes) =
- let t = skipTypes(n[1].typ, abstractVarRange)
+ if p.target == targetPHP:
+ localError(n.info, "'repr' not available for PHP backend")
+ return
+ let t = skipTypes(n.sons[1].typ, abstractVarRange)
case t.kind
- of tyInt..tyInt64, tyUInt..tyUInt64:
- genReprAux(p, n, r, "reprInt")
- of tyChar:
- genReprAux(p, n, r, "reprChar")
- of tyBool:
- genReprAux(p, n, r, "reprBool")
- of tyFloat..tyFloat128:
- genReprAux(p, n, r, "reprFloat")
- of tyString:
- genReprAux(p, n, r, "reprStr")
+ of tyInt..tyUInt64:
+ unaryExpr(p, n, r, "", "(\"\"+ ($1))")
of tyEnum, tyOrdinal:
- genReprAux(p, n, r, "reprEnum", genTypeInfo(p, t))
- of tySet:
- genReprAux(p, n, r, "reprSet", genTypeInfo(p, t))
- of tyEmpty, tyVoid:
- localError(p.config, n.info, "'repr' doesn't support 'void' type")
- of tyPointer:
- genReprAux(p, n, r, "reprPointer")
- of tyOpenArray, tyVarargs:
- genReprAux(p, n, r, "reprJSONStringify")
+ gen(p, n.sons[1], r)
+ useMagic(p, "cstrToNimstr")
+ r.kind = resExpr
+ r.res = "cstrToNimstr($1.node.sons[$2].name)" % [genTypeInfo(p, t), r.res]
else:
- genReprAux(p, n, r, "reprAny", genTypeInfo(p, t))
- r.kind = resExpr
+ # XXX:
+ internalError(n.info, "genRepr: Not implemented")
proc genOf(p: PProc, n: PNode, r: var TCompRes) =
var x: TCompRes
- let t = skipTypes(n[2].typ,
- abstractVarRange+{tyRef, tyPtr, tyLent, tyTypeDesc, tyOwned})
- gen(p, n[1], x)
+ let t = skipTypes(n.sons[2].typ, abstractVarRange+{tyRef, tyPtr, tyTypeDesc})
+ gen(p, n.sons[1], x)
if tfFinal in t.flags:
r.res = "($1.m_type == $2)" % [x.res, genTypeInfo(p, t)]
else:
@@ -2067,166 +1574,113 @@ proc genOf(p: PProc, n: PNode, r: var TCompRes) =
r.res = "isObj($1.m_type, $2)" % [x.res, genTypeInfo(p, t)]
r.kind = resExpr
-proc genDefault(p: PProc, n: PNode; r: var TCompRes) =
- r.res = createVar(p, n.typ, indirect = false)
- r.kind = resExpr
-
proc genReset(p: PProc, n: PNode) =
var x: TCompRes
useMagic(p, "genericReset")
- gen(p, n[1], x)
- if x.typ == etyBaseIndex:
- lineF(p, "$1 = null, $2 = 0;$n", [x.address, x.res])
- else:
- let (a, tmp) = maybeMakeTempAssignable(p, n[1], x)
- lineF(p, "$1 = genericReset($3, $2);$n", [a,
- genTypeInfo(p, n[1].typ), tmp])
-
-proc genMove(p: PProc; n: PNode; r: var TCompRes) =
- var a: TCompRes
- r.kind = resVal
- r.res = p.getTemp()
- gen(p, n[1], a)
- lineF(p, "$1 = $2;$n", [r.rdLoc, a.rdLoc])
- 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("]")
+ gen(p, n.sons[1], x)
+ addf(p.body, "$1 = genericReset($1, $2);$n", [x.res,
+ genTypeInfo(p, n.sons[1].typ)])
proc genMagic(p: PProc, n: PNode, r: var TCompRes) =
var
a: TCompRes
line, filen: Rope
- var op = n[0].sym.magic
+ var op = n.sons[0].sym.magic
case op
- of mOr: genOr(p, n[1], n[2], r)
- of mAnd: genAnd(p, n[1], n[2], r)
+ of mOr: genOr(p, n.sons[1], n.sons[2], r)
+ of mAnd: genAnd(p, n.sons[1], n.sons[2], r)
of mAddI..mStrToStr: arith(p, n, r, op)
of mRepr: genRepr(p, n, r)
of mSwap: genSwap(p, n)
+ of mUnaryLt:
+ # XXX: range checking?
+ if not (optOverflowCheck in p.options): unaryExpr(p, n, r, "", "$1 - 1")
+ else: unaryExpr(p, n, r, "subInt", "subInt($1, 1)")
of mAppendStrCh:
- binaryExpr(p, n, r, "addChar",
- "addChar($1, $2);")
+ if p.target == targetJS:
+ binaryExpr(p, n, r, "addChar",
+ "if ($1 != null) { addChar($1, $2); } else { $1 = [$2, 0]; }")
+ else:
+ binaryExpr(p, n, r, "",
+ "$1 .= chr($2)")
of mAppendStrStr:
- var lhs, rhs: 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 p.target == targetJS:
+ if skipTypes(n.sons[1].typ, abstractVarRange).kind == tyCString:
+ binaryExpr(p, n, r, "", "if ($1 != null) { $1 += $2; } else { $1 = $2; }")
+ else:
+ binaryExpr(p, n, r, "",
+ "if ($1 != null) { $1 = ($1.slice(0, -1)).concat($2); } else { $1 = $2;}")
+ # XXX: make a copy of $2, because of Javascript's sucking semantics
else:
- let (a, tmp) = maybeMakeTemp(p, n[1], lhs)
- r.res = "$1.push.apply($3, $2);" % [a, rhs.rdLoc, tmp]
- r.kind = resExpr
+ binaryExpr(p, n, r, "",
+ "$1 .= $2;")
of mAppendSeqElem:
- var x, y: TCompRes
- gen(p, n[1], x)
- gen(p, n[2], y)
- if mapType(n[2].typ) == etyBaseIndex:
- let c = "[$1, $2]" % [y.address, y.res]
- r.res = "$1.push($2);" % [x.rdLoc, c]
- elif needsNoCopy(p, n[2]):
- r.res = "$1.push($2);" % [x.rdLoc, y.rdLoc]
+ if p.target == targetJS:
+ binaryExpr(p, n, r, "",
+ "if ($1 != null) { $1.push($2); } else { $1 = [$2]; }")
else:
- useMagic(p, "nimCopy")
- let c = getTemp(p, defineInLocals=false)
- lineF(p, "var $1 = nimCopy(null, $2, $3);$n",
- [c, y.rdLoc, genTypeInfo(p, n[2].typ)])
- r.res = "$1.push($2);" % [x.rdLoc, c]
- r.kind = resExpr
+ binaryExpr(p, n, r, "",
+ "$1[] = $2")
of mConStrStr:
- genConStrStr(p, n, r)
- of mEqStr:
- binaryExpr(p, n, r, "eqStrings", "eqStrings($1, $2)")
- of mLeStr:
- binaryExpr(p, n, r, "cmpStrings", "(cmpStrings($1, $2) <= 0)")
- of mLtStr:
- binaryExpr(p, n, r, "cmpStrings", "(cmpStrings($1, $2) < 0)")
- of mIsNil:
- # we want to accept undefined, so we ==
- if mapType(n[1].typ) != etyBaseIndex:
- unaryExpr(p, n, r, "", "($1 == null)")
+ if p.target == targetJS:
+ genConStrStr(p, n, r)
else:
- var x: TCompRes
- gen(p, n[1], x)
- r.res = "($# == null && $# === 0)" % [x.address, x.res]
+ genConStrStrPHP(p, n, r)
+ of mEqStr:
+ if p.target == targetJS:
+ binaryExpr(p, n, r, "eqStrings", "eqStrings($1, $2)")
+ else:
+ binaryExpr(p, n, r, "", "($1 == $2)")
+ of mLeStr:
+ if p.target == targetJS:
+ binaryExpr(p, n, r, "cmpStrings", "(cmpStrings($1, $2) <= 0)")
+ else:
+ binaryExpr(p, n, r, "", "($1 <= $2)")
+ of mLtStr:
+ if p.target == targetJS:
+ binaryExpr(p, n, r, "cmpStrings", "(cmpStrings($1, $2) < 0)")
+ else:
+ binaryExpr(p, n, r, "", "($1 < $2)")
+ of mIsNil: unaryExpr(p, n, r, "", "($1 === null)")
of mEnumToStr: genRepr(p, n, r)
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)
- 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 mSizeOf: r.res = rope(getSize(n.sons[1].typ))
+ of mChr, mArrToSeq: gen(p, n.sons[1], r) # nothing to do
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]
+ of mLengthStr:
+ if p.target == targetJS and n.sons[1].typ.skipTypes(abstractInst).kind == tyCString:
+ unaryExpr(p, n, r, "", "($1 != null ? $1.length : 0)")
else:
- r.res = "($1).length" % [x.rdLoc]
- r.kind = resExpr
+ unaryExpr(p, n, r, "", "($1 != null ? $1.length-1 : 0)" |
+ "strlen($1)")
+ of mXLenStr: unaryExpr(p, n, r, "", "$1.length-1" | "strlen($1)")
+ of mLengthSeq, mLengthOpenArray, mLengthArray:
+ unaryExpr(p, n, r, "", "($1 != null ? $1.length : 0)" |
+ "count($1)")
+ of mXLenSeq:
+ unaryExpr(p, n, r, "", "$1.length" | "count($1)")
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]
+ if skipTypes(n.sons[1].typ, abstractVar).kind == tyString:
+ unaryExpr(p, n, r, "", "($1 != null ? ($1.length-2) : -1)" |
+ "(strlen($1)-1)")
else:
- r.res = "($1).length - 1" % [x.rdLoc]
- r.kind = resExpr
+ unaryExpr(p, n, r, "", "($1 != null ? ($1.length-1) : -1)" |
+ "(count($1)-1)")
of mInc:
- if n[1].typ.skipTypes(abstractRange).kind in {tyUInt..tyUInt64}:
+ if n[1].typ.skipTypes(abstractRange).kind in tyUInt .. tyUInt64:
binaryUintExpr(p, n, r, "+", true)
else:
if optOverflowCheck notin p.options: binaryExpr(p, n, r, "", "$1 += $2")
- else: binaryExpr(p, n, r, "addInt", "$1 = addInt($3, $2)", true)
+ else: binaryExpr(p, n, r, "addInt", "$1 = addInt($1, $2)")
of ast.mDec:
- if n[1].typ.skipTypes(abstractRange).kind in {tyUInt..tyUInt64}:
+ if n[1].typ.skipTypes(abstractRange).kind in tyUInt .. tyUInt64:
binaryUintExpr(p, n, r, "-", true)
else:
if optOverflowCheck notin p.options: binaryExpr(p, n, r, "", "$1 -= $2")
- else: binaryExpr(p, n, r, "subInt", "$1 = subInt($3, $2)", true)
+ else: binaryExpr(p, n, r, "subInt", "$1 = subInt($1, $2)")
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; }""")
- of mSetLengthSeq:
- var x, y: TCompRes
- gen(p, n[1], x)
- gen(p, n[2], y)
- 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); }
- else { $4.length = $5; }""" % [a, b, createVar(p, t, false), tmp, tmp2]
- r.kind = resExpr
+ binaryExpr(p, n, r, "", "$1.length = $2+1; $1[$1.length-1] = 0" | "$1 = substr($1, 0, $2)")
+ of mSetLengthSeq: binaryExpr(p, n, r, "", "$1.length = $2" | "$1 = array_slice($1, 0, $2)")
of mCard: unaryExpr(p, n, r, "SetCard", "SetCard($1)")
of mLtSet: binaryExpr(p, n, r, "SetLt", "SetLt($1, $2)")
of mLeSet: binaryExpr(p, n, r, "SetLe", "SetLe($1, $2)")
@@ -2235,172 +1689,146 @@ proc genMagic(p: PProc, n: PNode, r: var TCompRes) =
of mPlusSet: binaryExpr(p, n, r, "SetPlus", "SetPlus($1, $2)")
of mMinusSet: binaryExpr(p, n, r, "SetMinus", "SetMinus($1, $2)")
of mIncl: binaryExpr(p, n, r, "", "$1[$2] = true")
- of mExcl: binaryExpr(p, n, r, "", "delete $1[$2]")
+ of mExcl: binaryExpr(p, n, r, "", "delete $1[$2]" | "unset $1[$2]")
of mInSet:
- binaryExpr(p, n, r, "", "($1[$2] != undefined)")
+ if p.target == targetJS:
+ binaryExpr(p, n, r, "", "($1[$2] != undefined)")
+ else:
+ let s = n.sons[1]
+ if s.kind == nkCurly:
+ var a, b, x: TCompRes
+ gen(p, n.sons[2], x)
+ r.res = rope("(")
+ r.kind = resExpr
+ for i in countup(0, sonsLen(s) - 1):
+ if i > 0: add(r.res, " || ")
+ var it = s.sons[i]
+ if it.kind == nkRange:
+ gen(p, it.sons[0], a)
+ gen(p, it.sons[1], b)
+ addf(r.res, "($1 >= $2 && $1 <= $3)", [x.res, a.res, b.res,])
+ else:
+ gen(p, it, a)
+ addf(r.res, "($1 == $2)", [x.res, a.res])
+ add(r.res, ")")
+ else:
+ binaryExpr(p, n, r, "", "isset($1[$2])")
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 mReset, mWasMoved: genReset(p, n)
+ of mReset: genReset(p, n)
of mEcho: genEcho(p, n, r)
of mNLen..mNError, mSlurp, mStaticExec:
- localError(p.config, n.info, errXMustBeCompileTime % n[0].sym.name.s)
+ localError(n.info, errXMustBeCompileTime, n.sons[0].sym.name.s)
+ of mCopyStr:
+ binaryExpr(p, n, r, "", "($1.slice($2))" | "substr($1, $2)")
+ of mCopyStrLast:
+ if p.target == targetJS:
+ ternaryExpr(p, n, r, "", "($1.slice($2, ($3)+1).concat(0))")
+ else:
+ ternaryExpr(p, n, r, "nimSubstr", "nimSubstr($#, $#, $#)")
of mNewString: unaryExpr(p, n, r, "mnewString", "mnewString($1)")
of mNewStringOfCap:
- unaryExpr(p, n, r, "mnewString", "mnewString(0)")
+ if p.target == targetJS:
+ unaryExpr(p, n, r, "mnewString", "mnewString(0)")
+ else:
+ unaryExpr(p, n, r, "", "''")
of mDotDot:
- genProcForSymIfNeeded(p, n[0].sym)
+ genProcForSymIfNeeded(p, n.sons[0].sym)
genCall(p, n, r)
of mParseBiggestFloat:
useMagic(p, "nimParseBiggestFloat")
genCall(p, n, r)
- of mSlice:
- # arr.slice([begin[, end]]): 'end' is exclusive
- var x, y, z: 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.kind = resExpr
- of mMove:
- genMove(p, n, r)
+ of mArray:
+ if p.target == targetPHP: genToArray(p, n, r)
+ else: genCall(p, n, r)
else:
genCall(p, n, r)
- #else internalError(p.config, e.info, 'genMagic: ' + magicToStr[op]);
+ #else internalError(e.info, 'genMagic: ' + magicToStr[op]);
proc genSetConstr(p: PProc, n: PNode, r: var TCompRes) =
var
a, b: TCompRes
- useMagic(p, "setConstr")
- r.res = rope("setConstr(")
+ useMagic(p, "SetConstr")
+ r.res = rope("SetConstr(")
r.kind = resExpr
- for i in 0.. 0: r.res.add(", ")
- var it = n[i]
+ for i in countup(0, sonsLen(n) - 1):
+ if i > 0: add(r.res, ", ")
+ var it = n.sons[i]
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])
+ gen(p, it.sons[0], a)
+ gen(p, it.sons[1], b)
+ addf(r.res, "[$1, $2]" | "array($#,$#)", [a.res, b.res])
else:
gen(p, it, a)
- r.res.add(a.res)
- r.res.add(")")
- # emit better code for constant sets:
- if isDeepConstExpr(n):
- inc(p.g.unique)
- let tmp = rope("ConstSet") & rope(p.g.unique)
- p.g.constants.addf("var $1 = $2;$n", [tmp, r.res])
- r.res = tmp
+ add(r.res, a.res)
+ add(r.res, ")")
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)
- r.res.add(a.res)
- r.res.add("])")
- else:
- genJSArrayConstr(p, n, r)
+ var a: TCompRes
+ r.res = rope("[" | "array(")
+ r.kind = resExpr
+ for i in countup(0, sonsLen(n) - 1):
+ if i > 0: add(r.res, ", ")
+ gen(p, n.sons[i], a)
+ add(r.res, a.res)
+ add(r.res, "]" | ")")
proc genTupleConstr(p: PProc, n: PNode, r: var TCompRes) =
var a: TCompRes
- r.res = rope("{")
+ r.res = rope("{" | "array(")
r.kind = resExpr
- for i in 0.. 0: r.res.add(", ")
- var it = n[i]
- if it.kind == nkExprColonExpr: it = it[1]
+ for i in countup(0, sonsLen(n) - 1):
+ if i > 0: add(r.res, ", ")
+ var it = n.sons[i]
+ if it.kind == nkExprColonExpr: it = it.sons[1]
gen(p, it, a)
- let typ = it.typ.skipTypes(abstractInst)
- if a.typ == etyBaseIndex:
- r.res.addf("Field$#: [$#, $#]", [i.rope, a.address, a.res])
- else:
- if not needsNoCopy(p, it):
- useMagic(p, "nimCopy")
- a.res = "nimCopy(null, $1, $2)" % [a.rdLoc, genTypeInfo(p, typ)]
- r.res.addf("Field$#: $#", [i.rope, a.res])
- r.res.add("}")
+ addf(r.res, "Field$#: $#" | "$2", [i.rope, a.res])
+ r.res.add("}" | ")")
proc genObjConstr(p: PProc, n: PNode, r: var TCompRes) =
var a: 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]
- internalAssert p.config, it.kind == nkExprColonExpr
- 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)
-
- let typ = val.typ.skipTypes(abstractInst)
- if a.typ == etyBaseIndex:
- initList.addf("$#: [$#, $#]", [f.loc.r, a.address, a.res])
- else:
- if not needsNoCopy(p, val):
- useMagic(p, "nimCopy")
- a.res = "nimCopy(null, $1, $2)" % [a.rdLoc, genTypeInfo(p, typ)]
- initList.addf("$#: $#", [f.loc.r, a.res])
+ for i in countup(1, sonsLen(n) - 1):
+ if i > 1: add(initList, ", ")
+ var it = n.sons[i]
+ internalAssert it.kind == nkExprColonExpr
+ gen(p, it.sons[1], a)
+ var f = it.sons[0].sym
+ if f.loc.r == nil: f.loc.r = mangleName(f, p.target)
+ fieldIDs.incl(f.id)
+ addf(initList, "$#: $#" | "'$#' => $#" , [f.loc.r, a.res])
let t = skipTypes(n.typ, abstractInst + skipPtrs)
createObjInitList(p, t, fieldIDs, initList)
- r.res = ("{$1}") % [initList]
+ r.res = ("{$1}" | "array($#)") % [initList]
proc genConv(p: PProc, n: PNode, r: var TCompRes) =
var dest = skipTypes(n.typ, abstractVarRange)
- var src = skipTypes(n[1].typ, abstractVarRange)
- gen(p, n[1], r)
+ var src = skipTypes(n.sons[1].typ, abstractVarRange)
+ gen(p, n.sons[1], r)
if dest.kind == src.kind:
# no-op conversion
return
- let toInt = (dest.kind in tyInt..tyInt32)
- let fromInt = (src.kind in tyInt..tyInt32)
- let toUint = (dest.kind in tyUInt..tyUInt32)
- let fromUint = (src.kind in tyUInt..tyUInt32)
- if toUint and (fromInt or fromUint):
- let trimmer = unsignedTrimmer(dest.size)
- r.res = "($1 $2)" % [r.res, trimmer]
- elif dest.kind == tyBool:
- r.res = "(!!($1))" % [r.res]
+ case dest.kind:
+ of tyBool:
+ r.res = "(($1)? 1:0)" % [r.res]
r.kind = resExpr
- elif toInt:
- r.res = "(($1) | 0)" % [r.res]
+ of tyInt:
+ r.res = "($1|0)" % [r.res]
else:
# TODO: What types must we handle here?
discard
proc upConv(p: PProc, n: PNode, r: var TCompRes) =
- gen(p, n[0], r) # XXX
+ gen(p, n.sons[0], r) # XXX
proc genRangeChck(p: PProc, n: PNode, r: var TCompRes, magic: string) =
var a, b: TCompRes
- gen(p, n[0], r)
- if optRangeCheck notin p.options or (skipTypes(n.typ, abstractVar).kind in {tyUInt..tyUInt64} and
- checkUnsignedConversions notin p.config.legacyFeatures):
- discard "XXX maybe emit masking instructions here"
- else:
- gen(p, n[1], a)
- gen(p, n[2], b)
+ gen(p, n.sons[0], r)
+ if optRangeCheck in p.options:
+ gen(p, n.sons[1], a)
+ gen(p, n.sons[2], b)
useMagic(p, "chckRange")
r.res = "chckRange($1, $2, $3)" % [r.res, a.res, b.res]
r.kind = resExpr
@@ -2408,11 +1836,11 @@ proc genRangeChck(p: PProc, n: PNode, r: var TCompRes, magic: string) =
proc convStrToCStr(p: PProc, n: PNode, r: var TCompRes) =
# we do an optimization here as this is likely to slow down
# much of the code otherwise:
- if n[0].kind == nkCStringToString:
- gen(p, n[0][0], r)
+ if n.sons[0].kind == nkCStringToString:
+ gen(p, n.sons[0].sons[0], r)
else:
- gen(p, n[0], r)
- if r.res == "": internalError(p.config, n.info, "convStrToCStr")
+ gen(p, n.sons[0], r)
+ if r.res == nil: internalError(n.info, "convStrToCStr")
useMagic(p, "toJSStr")
r.res = "toJSStr($1)" % [r.res]
r.kind = resExpr
@@ -2420,61 +1848,51 @@ proc convStrToCStr(p: PProc, n: PNode, r: var TCompRes) =
proc convCStrToStr(p: PProc, n: PNode, r: var TCompRes) =
# we do an optimization here as this is likely to slow down
# much of the code otherwise:
- if n[0].kind == nkStringToCString:
- gen(p, n[0][0], r)
+ if n.sons[0].kind == nkStringToCString:
+ gen(p, n.sons[0].sons[0], r)
else:
- gen(p, n[0], r)
- if r.res == "": internalError(p.config, n.info, "convCStrToStr")
+ gen(p, n.sons[0], r)
+ if r.res == nil: internalError(n.info, "convCStrToStr")
useMagic(p, "cstrToNimstr")
r.res = "cstrToNimstr($1)" % [r.res]
r.kind = resExpr
proc genReturnStmt(p: PProc, n: PNode) =
- if p.procDef == nil: internalError(p.config, n.info, "genReturnStmt")
+ if p.procDef == nil: internalError(n.info, "genReturnStmt")
p.beforeRetNeeded = true
- if n[0].kind != nkEmpty:
- genStmt(p, n[0])
+ if n.sons[0].kind != nkEmpty:
+ genStmt(p, n.sons[0])
else:
genLineDir(p, n)
- lineF(p, "break BeforeRet;$n", [])
+ addf(p.body, "break BeforeRet;$n" | "goto BeforeRet;$n", [])
proc frameCreate(p: PProc; procname, filename: Rope): Rope =
- const frameFmt =
- "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", []))
+ result = (("var F={procname:$1,prev:framePtr,filename:$2,line:0};$nframePtr = F;$n" |
+ "global $$framePtr; $$F=array('procname'=>$#,'prev'=>$$framePtr,'filename'=>$#,'line'=>0);$n$$framePtr = &$$F;$n")) % [
+ procname, filename]
proc frameDestroy(p: PProc): Rope =
- result = p.indentLine rope(("framePtr = F.prev;") & "\L")
+ result = rope(("framePtr = framePtr.prev;" | "$framePtr = $framePtr['prev'];") & tnl)
proc genProcBody(p: PProc, prc: PSym): Rope =
- if hasFrameInfo(p):
+ if optStackTrace in prc.options:
result = frameCreate(p,
makeJSString(prc.owner.name.s & '.' & prc.name.s),
- makeJSString(toFilenameOption(p.config, prc.info.fileIndex, foStacktrace)))
+ makeJSString(toFilename(prc.info)))
else:
- result = ""
+ result = nil
if p.beforeRetNeeded:
- result.add p.indentLine("BeforeRet: {\n")
- result.add p.body
- result.add p.indentLine("};\n")
+ addf(result, "BeforeRet: do {$n$1} while (false); $n" |
+ "$# BeforeRet:;$n", [p.body])
else:
- result.add(p.body)
- if prc.typ.callConv == ccSysCall:
+ add(result, p.body)
+ if prc.typ.callConv == ccSysCall and p.target == targetJS:
result = ("try {$n$1} catch (e) {$n" &
" alert(\"Unhandled exception:\\n\" + e.message + \"\\n\"$n}") % [result]
- if hasFrameInfo(p):
- result.add(frameDestroy(p))
-
-proc optionalLine(p: Rope): Rope =
- if p == "":
- return ""
- else:
- return p & "\L"
+ if optStackTrace in prc.options:
+ add(result, frameDestroy(p))
proc genProc(oldProc: PProc, prc: PSym): Rope =
- ## Generate a JS procedure ('function').
var
resultSym: PSym
a: TCompRes
@@ -2482,118 +1900,63 @@ 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 name = mangleName(p.module, prc)
+ var returnStmt: Rope = nil
+ var resultAsgn: Rope = nil
+ let name = mangleName(prc, p.target)
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
- resultSym.typ.kind in {tyVar, tyPtr, tyLent, tyRef, tyOwned} and
- mapType(p, resultSym.typ) == etyBaseIndex
- if useRawPointer:
- resultAsgn = p.indentLine(("var $# = null;$n") % [mname])
- resultAsgn.add p.indentLine("var $#_Idx = 0;$n" % [mname])
- else:
- let resVar = createVar(p, resultSym.typ, isIndirect(resultSym))
- resultAsgn = p.indentLine(("var $# = $#;$n") % [mname, resVar])
- gen(p, prc.ast[resultPos], a)
+ if prc.typ.sons[0] != nil and sfPure notin prc.flags:
+ resultSym = prc.ast.sons[resultPos].sym
+ resultAsgn = ("var $# = $#;$n" | "$$$# = $#;$n") % [
+ mangleName(resultSym, p.target),
+ createVar(p, resultSym.typ, isIndirect(resultSym))]
+ gen(p, prc.ast.sons[resultPos], a)
if mapType(p, resultSym.typ) == etyBaseIndex:
returnStmt = "return [$#, $#];$n" % [a.address, a.res]
else:
returnStmt = "return $#;$n" % [a.res]
+ genStmt(p, prc.getBody)
- var transformedBody = transformBody(p.module.graph, p.module.idgen, prc, dontUseCache)
- if sfInjectDestructors in prc.flags:
- transformedBody = injectDestructorCalls(p.module.graph, p.module.idgen, prc, transformedBody)
-
- p.nested: genStmt(p, transformedBody)
-
-
- if optLineDir in p.config.options:
- result = lineDir(p.config, prc.info, toLinenumber(prc.info))
-
- var def: Rope
- if not prc.constraint.isNil:
- def = runtimeFormat(prc.constraint.strVal & " {$n$#$#$#$#$#",
- [ returnType,
- name,
- header,
- optionalLine(p.globals),
- optionalLine(p.locals),
- optionalLine(resultAsgn),
- optionalLine(genProcBody(p, prc)),
- optionalLine(p.indentLine(returnStmt))])
- else:
- # if optLineDir in p.config.options:
- # result.add("\L")
-
- if p.config.hcrOn:
- # Here, we introduce thunks that create the equivalent of a jump table
- # for all global functions, because references to them may be stored
- # in JavaScript variables. The added indirection ensures that such
- # references will end up calling the reloaded code.
- var thunkName = name
- name = name & "IMLP"
- result.add("\Lfunction $#() { return $#.apply(this, arguments); }$n" %
- [thunkName, name])
-
- def = "\Lfunction $#($#) {$n$#$#$#$#$#" %
- [ name,
- header,
- optionalLine(p.globals),
- optionalLine(p.locals),
- optionalLine(resultAsgn),
- optionalLine(genProcBody(p, prc)),
- optionalLine(p.indentLine(returnStmt))]
-
- dec p.extraIndent
- result.add p.indentLine(def)
- result.add p.indentLine("}\n")
-
+ result = "function $#($#) {$n$#$n$#$#$#$#}$n" %
+ [name, header, p.globals, p.locals, resultAsgn,
+ genProcBody(p, prc), returnStmt]
#if gVerbosity >= 3:
# echo "END generated code for: " & prc.name.s
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: addf(p.body, "$#;$n", [r.res])
proc genPragma(p: PProc, n: PNode) =
- for i in 0..(float)2147483647?(int)$1-4294967296:$1)" % [r.res]
else:
let trimmer = unsignedTrimmer(dest.size)
let minuend = case dest.size
@@ -2602,24 +1965,16 @@ proc genCast(p: PProc, n: PNode, r: var TCompRes) =
of 4: "0xfffffffe"
else: ""
r.res = "($1 - ($2 $3))" % [rope minuend, r.res, trimmer]
- elif (src.kind == tyPtr and mapType(p, src) == etyObject) and dest.kind == tyPointer:
- r.address = r.res
- r.res = "null"
- r.typ = etyBaseIndex
- elif (dest.kind == tyPtr and mapType(p, dest) == etyObject) and src.kind == tyPointer:
- r.res = r.address
- r.typ = etyObject
proc gen(p: PProc, n: PNode, r: var TCompRes) =
r.typ = etyNone
if r.kind != resCallee: r.kind = resNone
- #r.address = ""
- r.res = ""
-
+ #r.address = nil
+ r.res = nil
case n.kind
of nkSym:
genSym(p, n, r)
- of nkCharLit..nkUInt64Lit:
+ of nkCharLit..nkUInt32Lit:
if n.typ.kind == tyBool:
r.res = if n.intVal == 0: rope"false" else: rope"true"
else:
@@ -2637,63 +1992,46 @@ proc gen(p: PProc, n: PNode, r: var TCompRes) =
r.res = rope"null"
r.kind = resExpr
of nkStrLit..nkTripleStrLit:
- if skipTypes(n.typ, abstractVarRange).kind == tyString:
- if n.strVal.len <= 64:
- r.res = makeJsNimStrLit(n.strVal)
- else:
- useMagic(p, "makeNimstrLit")
- r.res = "makeNimstrLit($1)" % [makeJSString(n.strVal)]
+ if skipTypes(n.typ, abstractVarRange).kind == tyString and
+ p.target == targetJS:
+ useMagic(p, "makeNimstrLit")
+ r.res = "makeNimstrLit($1)" % [makeJSString(n.strVal)]
else:
r.res = makeJSString(n.strVal, false)
r.kind = resExpr
of nkFloatLit..nkFloat64Lit:
let f = n.floatVal
- case classify(f)
- of fcNan:
- if signbit(f):
- r.res = rope"-NaN"
- else:
- r.res = rope"NaN"
- of fcNegZero:
- r.res = rope"-0.0"
- of fcZero:
- r.res = rope"0.0"
- of fcInf:
- r.res = rope"Infinity"
- of fcNegInf:
- r.res = rope"-Infinity"
+ if f != f: r.res = rope"NaN"
+ elif f == 0.0: r.res = rope"0.0"
+ elif f == 0.5 * f:
+ if f > 0.0: r.res = rope"Infinity"
+ else: r.res = rope"-Infinity"
else: r.res = rope(f.toStrMaxPrecision)
r.kind = resExpr
of nkCallKinds:
- if isEmptyType(n.typ):
- genLineDir(p, n)
- if (n[0].kind == nkSym) and (n[0].sym.magic != mNone):
+ if (n.sons[0].kind == nkSym) and (n.sons[0].sym.magic != mNone):
genMagic(p, n, r)
- elif n[0].kind == nkSym and sfInfixCall in n[0].sym.flags and
- n.len >= 1:
+ elif n.sons[0].kind == nkSym and sfInfixCall in n.sons[0].sym.flags and
+ n.len >= 2:
genInfixCall(p, n, r)
else:
genCall(p, n, r)
of nkClosure: gen(p, n[0], r)
of nkCurly: genSetConstr(p, n, r)
of nkBracket: genArrayConstr(p, n, r)
- of nkPar, nkTupleConstr: genTupleConstr(p, n, r)
+ of nkPar: genTupleConstr(p, n, r)
of nkObjConstr: genObjConstr(p, n, r)
of nkHiddenStdConv, nkHiddenSubConv, nkConv: genConv(p, n, r)
of nkAddr, nkHiddenAddr:
- if n.typ.kind in {tyLent}:
- gen(p, n[0], r)
- else:
+ if p.target == targetJS:
genAddr(p, n, r)
- of nkDerefExpr, nkHiddenDeref:
- if n.typ.kind in {tyLent}:
- gen(p, n[0], r)
else:
- genDeref(p, n, r)
+ gen(p, n.sons[0], r)
+ of nkDerefExpr, nkHiddenDeref: genDeref(p, n, r)
of nkBracketExpr: genArrayAccess(p, n, r)
of nkDotExpr: genFieldAccess(p, n, r)
- of nkCheckedFieldExpr: genCheckedFieldOp(p, n, nil, r)
- of nkObjDownConv: gen(p, n[0], r)
+ of nkCheckedFieldExpr: genCheckedFieldAccess(p, n, r)
+ of nkObjDownConv: gen(p, n.sons[0], r)
of nkObjUpConv: upConv(p, n, r)
of nkCast: genCast(p, n, r)
of nkChckRangeF: genRangeChck(p, n, r, "chckRangeF")
@@ -2703,163 +2041,123 @@ proc gen(p: PProc, n: PNode, r: var TCompRes) =
of nkCStringToString: convCStrToStr(p, n, r)
of nkEmpty: discard
of nkLambdaKinds:
- let s = n[namePos].sym
- discard mangleName(p.module, s)
+ let s = n.sons[namePos].sym
+ discard mangleName(s, p.target)
r.res = s.loc.r
- if lfNoDecl in s.loc.flags or s.magic notin generatedMagics: discard
+ if lfNoDecl in s.loc.flags or s.magic != mNone: discard
elif not p.g.generatedSyms.containsOrIncl(s.id):
- p.locals.add(genProc(p, s))
+ add(p.locals, genProc(p, s))
of nkType: r.res = genTypeInfo(p, n.typ)
of nkStmtList, nkStmtListExpr:
# this shows the distinction is nice for backends and should be kept
# in the frontend
let isExpr = not isEmptyType(n.typ)
- for i in 0.. 0:
+ if options.outFile.isAbsolute: options.outFile
+ else: getCurrentDir() / options.outFile
+ else:
+ changeFileExt(completeCFilePath(f), ext)
+ discard writeRopeIfNotEqual(genHeader(m.target) & code, outfile)
+ for obj, content in items(globals.classes):
+ genClass(obj, content, ext)
-proc setupJSgen*(graph: ModuleGraph; s: PSym; idgen: IdGenerator): PPassContext =
- result = newModule(graph, s)
- result.idgen = idgen
+proc myOpenCached(s: PSym, rd: PRodReader): PPassContext =
+ internalError("symbol files are not possible with the JS code generator")
+ result = nil
+
+proc myOpen(s: PSym): PPassContext =
+ var r = newModule(s)
+ r.target = if gCmd == cmdCompileToPHP: targetPHP else: targetJS
+ result = r
+
+const JSgenPass* = makePass(myOpen, myOpenCached, myProcess, myClose)
diff --git a/compiler/jstypes.nim b/compiler/jstypes.nim
index 4b4ca9fe7..8d109e48a 100644
--- a/compiler/jstypes.nim
+++ b/compiler/jstypes.nim
@@ -7,89 +7,84 @@
# distribution, for details about the copyright.
#
-# included from jsgen.nim
-
## Type info generation for the JS backend.
-proc rope(arg: Int128): Rope = rope($arg)
-
proc genTypeInfo(p: PProc, typ: PType): Rope
proc genObjectFields(p: PProc, typ: PType, n: PNode): Rope =
var
s, u: Rope
+ length: int
field: PSym
b: PNode
- result = ""
+ result = nil
case n.kind
of nkRecList:
- if n.len == 1:
- result = genObjectFields(p, typ, n[0])
+ length = sonsLen(n)
+ if length == 1:
+ result = genObjectFields(p, typ, n.sons[0])
else:
- s = ""
- for i in 0.. 0: s.add(", \L")
- s.add(genObjectFields(p, typ, n[i]))
+ s = nil
+ for i in countup(0, length - 1):
+ if i > 0: add(s, ", " & tnl)
+ add(s, genObjectFields(p, typ, n.sons[i]))
result = ("{kind: 2, len: $1, offset: 0, " &
- "typ: null, name: null, sons: [$2]}") % [rope(n.len), s]
+ "typ: null, name: null, sons: [$2]}") % [rope(length), s]
of nkSym:
field = n.sym
s = genTypeInfo(p, field.typ)
result = ("{kind: 1, offset: \"$1\", len: 0, " &
"typ: $2, name: $3, sons: null}") %
- [mangleName(p.module, field), s,
+ [mangleName(field, p.target), s,
makeJSString(field.name.s)]
of nkRecCase:
- if (n[0].kind != nkSym): internalError(p.config, n.info, "genObjectFields")
- field = n[0].sym
+ length = sonsLen(n)
+ if (n.sons[0].kind != nkSym): internalError(n.info, "genObjectFields")
+ field = n.sons[0].sym
s = genTypeInfo(p, field.typ)
- for i in 1.. 0: s.add(", \L")
+ var s: Rope = nil
+ for i in 0 .. 0: add(s, ", " & tnl)
s.addf("{kind: 1, offset: \"Field$1\", len: 0, " &
"typ: $2, name: \"Field$1\", sons: null}",
- [i.rope, genTypeInfo(p, typ[i])])
+ [i.rope, genTypeInfo(p, typ.sons[i])])
result = ("{kind: 2, len: $1, offset: 0, " &
"typ: null, name: null, sons: [$2]}") % [rope(typ.len), s]
@@ -97,60 +92,77 @@ proc genTupleInfo(p: PProc, typ: PType, name: Rope) =
var s = ("var $1 = {size: 0, kind: $2, base: null, node: null, " &
"finalizer: null};$n") % [name, rope(ord(typ.kind))]
prepend(p.g.typeInfo, s)
- p.g.typeInfo.addf("var NNI$1 = $2;$n",
+ addf(p.g.typeInfo, "var NNI$1 = $2;$n",
[rope(typ.id), genTupleFields(p, typ)])
- p.g.typeInfo.addf("$1.node = NNI$2;$n", [name, rope(typ.id)])
+ addf(p.g.typeInfo, "$1.node = NNI$2;$n", [name, rope(typ.id)])
proc genEnumInfo(p: PProc, typ: PType, name: Rope) =
- var s: Rope = ""
- for i in 0.. 0: s.add(", \L")
+ let length = sonsLen(typ.n)
+ var s: Rope = nil
+ for i in countup(0, length - 1):
+ if (typ.n.sons[i].kind != nkSym): internalError(typ.n.info, "genEnumInfo")
+ let field = typ.n.sons[i].sym
+ if i > 0: add(s, ", " & tnl)
let extName = if field.ast == nil: field.name.s else: field.ast.strVal
- s.addf("\"$1\": {kind: 1, offset: $1, typ: $2, name: $3, len: 0, sons: null}",
+ addf(s, "{kind: 1, offset: $1, typ: $2, name: $3, len: 0, sons: null}",
[rope(field.position), name, makeJSString(extName)])
var n = ("var NNI$1 = {kind: 2, offset: 0, typ: null, " &
- "name: null, len: $2, sons: {$3}};$n") % [rope(typ.id), rope(typ.n.len), s]
+ "name: null, len: $2, sons: [$3]};$n") % [rope(typ.id), rope(length), s]
s = ("var $1 = {size: 0, kind: $2, base: null, node: null, " &
"finalizer: null};$n") % [name, rope(ord(typ.kind))]
prepend(p.g.typeInfo, s)
- p.g.typeInfo.add(n)
- p.g.typeInfo.addf("$1.node = NNI$2;$n", [name, rope(typ.id)])
- if typ[0] != nil:
- p.g.typeInfo.addf("$1.base = $2;$n",
- [name, genTypeInfo(p, typ[0])])
+ add(p.g.typeInfo, n)
+ addf(p.g.typeInfo, "$1.node = NNI$2;$n", [name, rope(typ.id)])
+ if typ.sons[0] != nil:
+ addf(p.g.typeInfo, "$1.base = $2;$n",
+ [name, genTypeInfo(p, typ.sons[0])])
+
+proc genEnumInfoPHP(p: PProc; t: PType): Rope =
+ let t = t.skipTypes({tyGenericInst, tyDistinct})
+ result = "$$NTI$1" % [rope(t.id)]
+ p.declareGlobal(t.id, result)
+ if containsOrIncl(p.g.typeInfoGenerated, t.id): return
+
+ let length = sonsLen(t.n)
+ var s: Rope = nil
+ for i in countup(0, length - 1):
+ if (t.n.sons[i].kind != nkSym): internalError(t.n.info, "genEnumInfo")
+ let field = t.n.sons[i].sym
+ if i > 0: add(s, ", " & tnl)
+ let extName = if field.ast == nil: field.name.s else: field.ast.strVal
+ addf(s, "$# => $#$n",
+ [rope(field.position), makeJSString(extName)])
+ prepend(p.g.typeInfo, "$$$# = $#;$n" % [result, s])
proc genTypeInfo(p: PProc, typ: PType): Rope =
- let t = typ.skipTypes({tyGenericInst, tyDistinct, tyAlias, tySink, tyOwned})
+ if p.target == targetPHP:
+ return makeJSString(typeToString(typ, preferModuleInfo))
+ let t = typ.skipTypes({tyGenericInst, tyDistinct})
result = "NTI$1" % [rope(t.id)]
if containsOrIncl(p.g.typeInfoGenerated, t.id): return
case t.kind
of tyDistinct:
- result = genTypeInfo(p, t[0])
- of tyPointer, tyProc, tyBool, tyChar, tyCstring, tyString, tyInt..tyUInt64:
+ result = genTypeInfo(p, t.sons[0])
+ of tyPointer, tyProc, tyBool, tyChar, tyCString, tyString, tyInt..tyUInt64:
var s =
"var $1 = {size: 0,kind: $2,base: null,node: null,finalizer: null};$n" %
[result, rope(ord(t.kind))]
prepend(p.g.typeInfo, s)
- of tyVar, tyLent, tyRef, tyPtr, tySequence, tyRange, tySet, tyOpenArray:
+ of tyVar, tyRef, tyPtr, tySequence, tyRange, tySet:
var s =
- "var $1 = {size: 0, kind: $2, base: null, node: null, finalizer: null};$n" %
+ "var $1 = {size: 0,kind: $2,base: null,node: null,finalizer: null};$n" %
[result, rope(ord(t.kind))]
prepend(p.g.typeInfo, s)
- p.g.typeInfo.addf("$1.base = $2;$n",
+ addf(p.g.typeInfo, "$1.base = $2;$n",
[result, genTypeInfo(p, t.lastSon)])
- of tyArray:
+ of tyArrayConstr, tyArray:
var s =
- "var $1 = {size: 0, kind: $2, base: null, node: null, finalizer: null};$n" %
+ "var $1 = {size: 0,kind: $2,base: null,node: null,finalizer: null};$n" %
[result, rope(ord(t.kind))]
prepend(p.g.typeInfo, s)
- p.g.typeInfo.addf("$1.base = $2;$n",
- [result, genTypeInfo(p, t[1])])
+ addf(p.g.typeInfo, "$1.base = $2;$n",
+ [result, genTypeInfo(p, t.sons[1])])
of tyEnum: genEnumInfo(p, t, result)
of tyObject: genObjectInfo(p, t, result)
of tyTuple: genTupleInfo(p, t, result)
- of tyStatic:
- if t.n != nil: result = genTypeInfo(p, lastSon t)
- else: internalError(p.config, "genTypeInfo(" & $t.kind & ')')
- else: internalError(p.config, "genTypeInfo(" & $t.kind & ')')
+ else: internalError("genTypeInfo(" & $t.kind & ')')
diff --git a/compiler/lambdalifting.nim b/compiler/lambdalifting.nim
index f6bc21c01..753602c80 100644
--- a/compiler/lambdalifting.nim
+++ b/compiler/lambdalifting.nim
@@ -7,15 +7,11 @@
# distribution, for details about the copyright.
#
-# This file implements lambda lifting for the transformator.
+# This include file implements lambda lifting for the transformator.
import
- intsets, strutils, options, ast, astalgo, msgs,
- idents, renderer, types, magicsys, lowerings, tables, modulegraphs, lineinfos,
- transf, liftdestructors, typeallowed
-
-when defined(nimPreviewSlimSystem):
- import std/assertions
+ intsets, strutils, lists, options, ast, astalgo, trees, treetab, msgs, os,
+ idents, renderer, types, magicsys, rodread, lowerings, tables
discard """
The basic approach is that captured vars need to be put on the heap and
@@ -129,60 +125,60 @@ proc newCall(a: PSym, b: PNode): PNode =
result.add newSymNode(a)
result.add b
-proc createClosureIterStateType*(g: ModuleGraph; iter: PSym; idgen: IdGenerator): PType =
+proc createStateType(iter: PSym): PType =
var n = newNodeI(nkRange, iter.info)
- n.add newIntNode(nkIntLit, -1)
- n.add newIntNode(nkIntLit, 0)
- result = newType(tyRange, nextTypeId(idgen), iter)
+ addSon(n, newIntNode(nkIntLit, -1))
+ addSon(n, newIntNode(nkIntLit, 0))
+ result = newType(tyRange, iter)
result.n = n
- var intType = nilOrSysInt(g)
- if intType.isNil: intType = newType(tyInt, nextTypeId(idgen), iter)
+ var intType = nilOrSysInt()
+ if intType.isNil: intType = newType(tyInt, iter)
rawAddSon(result, intType)
-proc createStateField(g: ModuleGraph; iter: PSym; idgen: IdGenerator): PSym =
- result = newSym(skField, getIdent(g.cache, ":state"), nextSymId(idgen), iter, iter.info)
- result.typ = createClosureIterStateType(g, iter, idgen)
+proc createStateField(iter: PSym): PSym =
+ result = newSym(skField, getIdent(":state"), iter, iter.info)
+ result.typ = createStateType(iter)
-proc createEnvObj(g: ModuleGraph; idgen: IdGenerator; owner: PSym; info: TLineInfo): PType =
+proc createEnvObj(owner: PSym): PType =
# YYY meh, just add the state field for every closure for now, it's too
# hard to figure out if it comes from a closure iterator:
- result = createObj(g, idgen, owner, info, final=false)
- rawAddField(result, createStateField(g, owner, idgen))
+ result = createObj(owner, owner.info)
+ rawAddField(result, createStateField(owner))
-proc getClosureIterResult*(g: ModuleGraph; iter: PSym; idgen: IdGenerator): PSym =
+proc getIterResult(iter: PSym): PSym =
if resultPos < iter.ast.len:
- result = iter.ast[resultPos].sym
+ result = iter.ast.sons[resultPos].sym
else:
# XXX a bit hacky:
- result = newSym(skResult, getIdent(g.cache, ":result"), nextSymId(idgen), iter, iter.info, {})
- result.typ = iter.typ[0]
+ result = newSym(skResult, getIdent":result", iter, iter.info)
+ result.typ = iter.typ.sons[0]
incl(result.flags, sfUsed)
iter.ast.add newSymNode(result)
proc addHiddenParam(routine: PSym, param: PSym) =
assert param.kind == skParam
- var params = routine.ast[paramsPos]
+ var params = routine.ast.sons[paramsPos]
# -1 is correct here as param.position is 0 based but we have at position 0
# some nkEffect node:
param.position = routine.typ.n.len-1
- params.add newSymNode(param)
+ addSon(params, newSymNode(param))
#incl(routine.typ.flags, tfCapturesEnv)
assert sfFromGeneric in param.flags
#echo "produced environment: ", param.id, " for ", routine.id
-proc getHiddenParam(g: ModuleGraph; routine: PSym): PSym =
- let params = routine.ast[paramsPos]
+proc getHiddenParam(routine: PSym): PSym =
+ let params = routine.ast.sons[paramsPos]
let hidden = lastSon(params)
if hidden.kind == nkSym and hidden.sym.kind == skParam and hidden.sym.name.s == paramName:
result = hidden.sym
assert sfFromGeneric in result.flags
else:
# writeStackTrace()
- localError(g.config, routine.info, "internal error: could not find env param for " & routine.name.s)
+ localError(routine.info, "internal error: could not find env param for " & routine.name.s)
result = routine
proc getEnvParam*(routine: PSym): PSym =
- let params = routine.ast[paramsPos]
+ let params = routine.ast.sons[paramsPos]
let hidden = lastSon(params)
if hidden.kind == nkSym and hidden.sym.name.s == paramName:
result = hidden.sym
@@ -190,14 +186,15 @@ proc getEnvParam*(routine: PSym): PSym =
proc interestingVar(s: PSym): bool {.inline.} =
result = s.kind in {skVar, skLet, skTemp, skForVar, skParam, skResult} and
- sfGlobal notin s.flags and
- s.typ.kind notin {tyStatic, tyTypeDesc}
+ sfGlobal notin s.flags
proc illegalCapture(s: PSym): bool {.inline.} =
- result = classifyViewType(s.typ) != noView or s.kind == skResult
+ result = skipTypes(s.typ, abstractInst).kind in
+ {tyVar, tyOpenArray, tyVarargs} or
+ s.kind == skResult
proc isInnerProc(s: PSym): bool =
- if s.kind in {skProc, skFunc, skMethod, skConverter, skIterator} and s.magic == mNone:
+ if s.kind in {skProc, skMethod, skConverter, skIterator} and s.magic == mNone:
result = s.skipGenericOwner.kind in routineKinds
proc newAsgnStmt(le, ri: PNode, info: TLineInfo): PNode =
@@ -207,101 +204,67 @@ proc newAsgnStmt(le, ri: PNode, info: TLineInfo): PNode =
# which is however the only case when we generate an assignment in the first
# place.
result = newNodeI(nkAsgn, info, 2)
- result[0] = le
- result[1] = ri
+ result.sons[0] = le
+ result.sons[1] = ri
-proc makeClosure*(g: ModuleGraph; idgen: IdGenerator; prc: PSym; env: PNode; info: TLineInfo): PNode =
+proc makeClosure*(prc: PSym; env: PNode; info: TLineInfo): PNode =
result = newNodeIT(nkClosure, info, prc.typ)
result.add(newSymNode(prc))
if env == nil:
- result.add(newNodeIT(nkNilLit, info, getSysType(g, info, tyNil)))
+ result.add(newNodeIT(nkNilLit, info, getSysType(tyNil)))
else:
if env.skipConv.kind == nkClosure:
- localError(g.config, info, "internal error: taking closure of closure")
+ localError(info, "internal error: taking closure of closure")
result.add(env)
- #if isClosureIterator(result.typ):
- createTypeBoundOps(g, nil, result.typ, info, idgen)
- if tfHasAsgn in result.typ.flags or optSeqDestructors in g.config.globalOptions:
- prc.flags.incl sfInjectDestructors
proc interestingIterVar(s: PSym): bool {.inline.} =
# XXX optimization: Only lift the variable if it lives across
# yield/return boundaries! This can potentially speed up
# closure iterators quite a bit.
- result = s.kind in {skResult, skVar, skLet, skTemp, skForVar} and sfGlobal notin s.flags
+ result = s.kind in {skVar, skLet, skTemp, skForVar} and sfGlobal notin s.flags
template isIterator*(owner: PSym): bool =
owner.kind == skIterator and owner.typ.callConv == ccClosure
-proc liftingHarmful(conf: ConfigRef; owner: PSym): bool {.inline.} =
- ## lambda lifting can be harmful for JS-like code generators.
- let isCompileTime = sfCompileTime in owner.flags or owner.kind == skMacro
- result = conf.backend == backendJs and not isCompileTime
-
-proc createTypeBoundOpsLL(g: ModuleGraph; refType: PType; info: TLineInfo; idgen: IdGenerator; owner: PSym) =
- if owner.kind != skMacro:
- createTypeBoundOps(g, nil, refType.lastSon, info, idgen)
- createTypeBoundOps(g, nil, refType, info, idgen)
- if tfHasAsgn in refType.flags or optSeqDestructors in g.config.globalOptions:
- owner.flags.incl sfInjectDestructors
-
-proc genCreateEnv(env: PNode): PNode =
- var c = newNodeIT(nkObjConstr, env.info, env.typ)
- c.add newNodeIT(nkType, env.info, env.typ)
- let e = copyTree(env)
- e.flags.incl nfFirstWrite
- result = newAsgnStmt(e, c)
-
-proc liftIterSym*(g: ModuleGraph; n: PNode; idgen: IdGenerator; owner: PSym): PNode =
+proc liftIterSym*(n: PNode; owner: PSym): PNode =
# transforms (iter) to (let env = newClosure[iter](); (iter, env))
- if liftingHarmful(g.config, owner): return n
let iter = n.sym
assert iter.isIterator
result = newNodeIT(nkStmtListExpr, n.info, n.typ)
- let hp = getHiddenParam(g, iter)
- var env: PNode
- if owner.isIterator:
- let it = getHiddenParam(g, owner)
- addUniqueField(it.typ.skipTypes({tyOwned})[0], hp, g.cache, idgen)
- env = indirectAccess(newSymNode(it), hp, hp.info)
- else:
- let e = newSym(skLet, iter.name, nextSymId(idgen), owner, n.info)
- e.typ = hp.typ
- e.flags = hp.flags
- env = newSymNode(e)
- var v = newNodeI(nkVarSection, n.info)
- addVar(v, env)
- result.add(v)
+ let hp = getHiddenParam(iter)
+ let env = newSym(skLet, iter.name, owner, n.info)
+ env.typ = hp.typ
+ env.flags = hp.flags
+ var v = newNodeI(nkVarSection, n.info)
+ addVar(v, newSymNode(env))
+ result.add(v)
# add 'new' statement:
- #result.add newCall(getSysSym(g, n.info, "internalNew"), env)
- result.add genCreateEnv(env)
- createTypeBoundOpsLL(g, env.typ, n.info, idgen, owner)
- result.add makeClosure(g, idgen, iter, env, n.info)
+ let envAsNode = env.newSymNode
+ result.add newCall(getSysSym"internalNew", envAsNode)
+ result.add makeClosure(iter, envAsNode, n.info)
-proc freshVarForClosureIter*(g: ModuleGraph; s: PSym; idgen: IdGenerator; owner: PSym): PNode =
- let envParam = getHiddenParam(g, owner)
- let obj = envParam.typ.skipTypes({tyOwned, tyRef, tyPtr})
- let field = addField(obj, s, g.cache, idgen)
+proc freshVarForClosureIter*(s, owner: PSym): PNode =
+ let envParam = getHiddenParam(owner)
+ let obj = envParam.typ.lastSon
+ addField(obj, s)
var access = newSymNode(envParam)
assert obj.kind == tyObject
- result = rawIndirectAccess(access, field, s.info)
+ let field = getFieldFromObj(obj, s)
+ if field != nil:
+ result = rawIndirectAccess(access, field, s.info)
+ else:
+ localError(s.info, "internal error: cannot generate fresh variable")
+ result = access
# ------------------ new stuff -------------------------------------------
-proc markAsClosure(g: ModuleGraph; owner: PSym; n: PNode) =
+proc markAsClosure(owner: PSym; n: PNode) =
let s = n.sym
- if illegalCapture(s):
- localError(g.config, n.info,
- ("'$1' is of type <$2> 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.") %
- [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>" %
- [s.name.s, owner.name.s, $owner.typ.callConv])
+ if illegalCapture(s) or owner.typ.callConv notin {ccClosure, ccDefault}:
+ localError(n.info, errIllegalCaptureX, s.name.s)
incl(owner.typ.flags, tfCapturesEnv)
owner.typ.callConv = ccClosure
@@ -310,16 +273,12 @@ type
processed, capturedVars: IntSet
ownerToType: Table[int, PType]
somethingToDo: bool
- graph: ModuleGraph
- idgen: IdGenerator
-proc initDetectionPass(g: ModuleGraph; fn: PSym; idgen: IdGenerator): DetectionPass =
+proc initDetectionPass(fn: PSym): DetectionPass =
result.processed = initIntSet()
result.capturedVars = initIntSet()
result.ownerToType = initTable[int, PType]()
result.processed.incl(fn.id)
- result.graph = g
- result.idgen = idgen
discard """
proc outer =
@@ -331,60 +290,29 @@ This is why need to store the 'ownerToType' table and use it
during .closure'fication.
"""
-proc getEnvTypeForOwner(c: var DetectionPass; owner: PSym;
- info: TLineInfo): PType =
+proc getEnvTypeForOwner(c: var DetectionPass; owner: PSym): PType =
result = c.ownerToType.getOrDefault(owner.id)
if result.isNil:
- result = newType(tyRef, nextTypeId(c.idgen), owner)
- let obj = createEnvObj(c.graph, c.idgen, owner, info)
+ result = newType(tyRef, owner)
+ let obj = createEnvObj(owner)
rawAddSon(result, obj)
c.ownerToType[owner.id] = result
-proc asOwnedRef(c: var DetectionPass; t: PType): PType =
- if optOwnedRefs in c.graph.config.globalOptions:
- assert t.kind == tyRef
- result = newType(tyOwned, nextTypeId(c.idgen), t.owner)
- result.flags.incl tfHasOwned
- result.rawAddSon t
- else:
- result = t
-
-proc getEnvTypeForOwnerUp(c: var DetectionPass; owner: PSym;
- info: TLineInfo): PType =
- var r = c.getEnvTypeForOwner(owner, info)
- result = newType(tyPtr, nextTypeId(c.idgen), owner)
- rawAddSon(result, r.skipTypes({tyOwned, tyRef, tyPtr}))
-
-proc createUpField(c: var DetectionPass; dest, dep: PSym; info: TLineInfo) =
- let refObj = c.getEnvTypeForOwner(dest, info) # getHiddenParam(dest).typ
- let obj = refObj.skipTypes({tyOwned, tyRef, tyPtr})
- # The assumption here is that gcDestructors means we cannot deal
- # with cycles properly, so it's better to produce a weak ref (=ptr) here.
- # This seems to be generally correct but since it's a bit risky it's disabled
- # for now.
- # XXX This is wrong for the 'hamming' test, so remove this logic again.
- let fieldType = if isDefined(c.graph.config, "nimCycleBreaker"):
- c.getEnvTypeForOwnerUp(dep, info) #getHiddenParam(dep).typ
- else:
- c.getEnvTypeForOwner(dep, info)
+proc createUpField(c: var DetectionPass; dest, dep: PSym) =
+ let refObj = c.getEnvTypeForOwner(dest) # getHiddenParam(dest).typ
+ let obj = refObj.lastSon
+ let fieldType = c.getEnvTypeForOwner(dep) #getHiddenParam(dep).typ
if refObj == fieldType:
- localError(c.graph.config, dep.info, "internal error: invalid up reference computed")
+ localError(dep.info, "internal error: invalid up reference computed")
- let upIdent = getIdent(c.graph.cache, upName)
+ let upIdent = getIdent(upName)
let upField = lookupInRecord(obj.n, upIdent)
if upField != nil:
- if upField.typ.skipTypes({tyOwned, tyRef, tyPtr}) != fieldType.skipTypes({tyOwned, tyRef, tyPtr}):
- localError(c.graph.config, dep.info, "internal error: up references do not agree")
-
- when false:
- if c.graph.config.selectedGC == gcDestructors and sfCursor notin upField.flags:
- localError(c.graph.config, dep.info, "internal error: up reference is not a .cursor")
+ if upField.typ != fieldType:
+ localError(dep.info, "internal error: up references do not agree")
else:
- let result = newSym(skField, upIdent, nextSymId(c.idgen), obj.owner, obj.owner.info)
+ let result = newSym(skField, upIdent, obj.owner, obj.owner.info)
result.typ = fieldType
- when false:
- if c.graph.config.selectedGC == gcDestructors:
- result.flags.incl sfCursor
rawAddField(obj, result)
discard """
@@ -413,52 +341,49 @@ Consider:
"""
-proc addClosureParam(c: var DetectionPass; fn: PSym; info: TLineInfo) =
+proc addClosureParam(c: var DetectionPass; fn: PSym) =
var cp = getEnvParam(fn)
let owner = if fn.kind == skIterator: fn else: fn.skipGenericOwner
- let t = c.getEnvTypeForOwner(owner, info)
+ let t = c.getEnvTypeForOwner(owner)
if cp == nil:
- cp = newSym(skParam, getIdent(c.graph.cache, paramName), nextSymId(c.idgen), fn, fn.info)
+ cp = newSym(skParam, getIdent(paramName), fn, fn.info)
incl(cp.flags, sfFromGeneric)
cp.typ = t
addHiddenParam(fn, cp)
elif cp.typ != t and fn.kind != skIterator:
- localError(c.graph.config, fn.info, "internal error: inconsistent environment type")
+ localError(fn.info, "internal error: inconsistent environment type")
#echo "adding closure to ", fn.name.s
proc detectCapturedVars(n: PNode; owner: PSym; c: var DetectionPass) =
case n.kind
of nkSym:
let s = n.sym
- if s.kind in {skProc, skFunc, skMethod, skConverter, skIterator} and
- s.typ != nil and s.typ.callConv == ccClosure:
+ if s.kind in {skProc, skMethod, skConverter, skIterator} and s.typ != nil and s.typ.callConv == ccClosure:
# this handles the case that the inner proc was declared as
# .closure but does not actually capture anything:
- addClosureParam(c, s, n.info)
+ addClosureParam(c, s)
c.somethingToDo = true
let innerProc = isInnerProc(s)
if innerProc:
if s.isIterator: c.somethingToDo = true
if not c.processed.containsOrIncl(s.id):
- let body = transformBody(c.graph, c.idgen, s, useCache)
- detectCapturedVars(body, s, c)
+ detectCapturedVars(s.getBody, s, c)
let ow = s.skipGenericOwner
if ow == owner:
if owner.isIterator:
c.somethingToDo = true
- addClosureParam(c, owner, n.info)
+ addClosureParam(c, owner)
if interestingIterVar(s):
if not c.capturedVars.containsOrIncl(s.id):
- let obj = getHiddenParam(c.graph, owner).typ.skipTypes({tyOwned, tyRef, tyPtr})
- #let obj = c.getEnvTypeForOwner(s.owner).skipTypes({tyOwned, tyRef, tyPtr})
-
- 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)
+ let obj = getHiddenParam(owner).typ.lastSon
+ #let obj = c.getEnvTypeForOwner(s.owner).lastSon
+ addField(obj, s)
+ # but always return because the rest of the proc is only relevant when
+ # ow != owner:
+ return
# direct or indirect dependency:
- elif (innerProc and not s.isIterator and s.typ.callConv == ccClosure) or interestingVar(s):
+ if (innerProc and s.typ.callConv == ccClosure) or interestingVar(s):
discard """
proc outer() =
var x: int
@@ -471,14 +396,14 @@ proc detectCapturedVars(n: PNode; owner: PSym; c: var DetectionPass) =
"""
# mark 'owner' as taking a closure:
c.somethingToDo = true
- markAsClosure(c.graph, owner, n)
- addClosureParam(c, owner, n.info)
+ markAsClosure(owner, n)
+ addClosureParam(c, owner)
#echo "capturing ", n.info
# variable 's' is actually captured:
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)
+ let obj = c.getEnvTypeForOwner(ow).lastSon
+ #getHiddenParam(owner).typ.lastSon
+ addField(obj, s)
# create required upFields:
var w = owner.skipGenericOwner
if isInnerProc(w) or owner.isIterator:
@@ -496,20 +421,18 @@ proc detectCapturedVars(n: PNode; owner: PSym; c: var DetectionPass) =
"""
let up = w.skipGenericOwner
#echo "up for ", w.name.s, " up ", up.name.s
- markAsClosure(c.graph, w, n)
- addClosureParam(c, w, n.info) # , ow
- createUpField(c, w, up, n.info)
+ markAsClosure(w, n)
+ addClosureParam(c, w) # , ow
+ createUpField(c, w, up)
w = up
of nkEmpty..pred(nkSym), succ(nkSym)..nkNilLit,
- nkTemplateDef, nkTypeSection, nkProcDef, nkMethodDef,
- nkConverterDef, nkMacroDef, nkFuncDef, nkCommentStmt,
- nkTypeOfExpr, nkMixinStmt, nkBindStmt:
+ nkTemplateDef, nkTypeSection:
+ discard
+ of nkProcDef, nkMethodDef, nkConverterDef, nkMacroDef:
discard
of nkLambdaKinds, nkIteratorDef:
if n.typ != nil:
detectCapturedVars(n[namePos], owner, c)
- of nkReturnStmt:
- detectCapturedVars(n[0], owner, c)
else:
for i in 0.. 0:
+ if n.sons[0].kind == nkGotoState: return n
+ if n.len > 1 and n[1].kind == nkStmtList and n[1].len > 0 and
+ n[1][0].kind == nkGotoState:
+ return n
+ let info = n.info
+ result = newNodeI(nkStmtList, info)
+ var gs = newNodeI(nkGotoState, info)
+ gs.add(rawIndirectAccess(newSymNode(owner.getHiddenParam), getStateField(owner), info))
+ result.add(gs)
+ var state0 = newNodeI(nkState, info)
+ state0.add(newIntNode(nkIntLit, 0))
+ result.add(state0)
+
+ result.add(n)
+
+ var stateAsgnStmt = newNodeI(nkAsgn, info)
+ stateAsgnStmt.add(rawIndirectAccess(newSymNode(owner.getHiddenParam),
+ getStateField(owner), info))
+ stateAsgnStmt.add(newIntTypeNode(nkIntLit, -1, getSysType(tyInt)))
+ result.add(stateAsgnStmt)
+
+proc symToClosure(n: PNode; owner: PSym; d: DetectionPass;
c: var LiftingPass): PNode =
let s = n.sym
if s == owner:
# recursive calls go through (lambda, hiddenParam):
- let available = getHiddenParam(d.graph, owner)
- result = makeClosure(d.graph, d.idgen, s, available.newSymNode, n.info)
+ let available = getHiddenParam(owner)
+ result = makeClosure(s, available.newSymNode, n.info)
elif s.isIterator:
result = closureCreationForIter(n, d, c)
elif s.skipGenericOwner == owner:
# direct dependency, so use the outer's env variable:
- result = makeClosure(d.graph, d.idgen, s, setupEnvVar(owner, d, c, n.info), n.info)
+ result = makeClosure(s, setupEnvVar(owner, d, c), n.info)
else:
- let available = getHiddenParam(d.graph, owner)
- let wanted = getHiddenParam(d.graph, s).typ
+ let available = getHiddenParam(owner)
+ let wanted = getHiddenParam(s).typ
# ugh: call through some other inner proc;
var access = newSymNode(available)
while true:
if access.typ == wanted:
- return makeClosure(d.graph, d.idgen, s, access, n.info)
- let obj = access.typ.skipTypes({tyOwned, tyRef, tyPtr})
- let upField = lookupInRecord(obj.n, getIdent(d.graph.cache, upName))
+ return makeClosure(s, access, n.info)
+ let obj = access.typ.sons[0]
+ let upField = lookupInRecord(obj.n, getIdent(upName))
if upField == nil:
- localError(d.graph.config, n.info, "internal error: no environment found")
+ localError(n.info, "internal error: no environment found")
return n
access = rawIndirectAccess(access, upField, n.info)
-proc liftCapturedVars(n: PNode; owner: PSym; d: var DetectionPass;
+proc liftCapturedVars(n: PNode; owner: PSym; d: DetectionPass;
c: var LiftingPass): PNode =
result = n
case n.kind
@@ -735,157 +689,112 @@ proc liftCapturedVars(n: PNode; owner: PSym; d: var DetectionPass;
if not c.processed.containsOrIncl(s.id):
#if s.name.s == "temp":
# echo renderTree(s.getBody, {renderIds})
- let oldInContainer = c.inContainer
- c.inContainer = 0
- var body = transformBody(d.graph, d.idgen, s, dontUseCache)
- body = liftCapturedVars(body, s, d, c)
- if c.envVars.getOrDefault(s.id).isNil:
- s.transformedBody = body
+ let body = wrapIterBody(liftCapturedVars(s.getBody, s, d, c), s)
+ if c.envvars.getOrDefault(s.id).isNil:
+ s.ast.sons[bodyPos] = body
else:
- s.transformedBody = newTree(nkStmtList, rawClosureCreation(s, d, c, n.info), body)
- finishClosureCreation(s, d, c, n.info, s.transformedBody)
- c.inContainer = oldInContainer
-
+ s.ast.sons[bodyPos] = newTree(nkStmtList, rawClosureCreation(s, d, c), body)
if s.typ.callConv == ccClosure:
result = symToClosure(n, owner, d, c)
-
elif s.id in d.capturedVars:
if s.owner != owner:
- result = accessViaEnvParam(d.graph, n, owner)
+ result = accessViaEnvParam(n, owner)
elif owner.isIterator and interestingIterVar(s):
- result = accessViaEnvParam(d.graph, n, owner)
+ result = accessViaEnvParam(n, owner)
else:
result = accessViaEnvVar(n, owner, d, c)
- of nkEmpty..pred(nkSym), succ(nkSym)..nkNilLit, nkComesFrom,
- nkTemplateDef, nkTypeSection, nkProcDef, nkMethodDef, nkConverterDef,
- nkMacroDef, nkFuncDef, nkMixinStmt, nkBindStmt:
+ of nkEmpty..pred(nkSym), succ(nkSym)..nkNilLit,
+ nkTemplateDef, nkTypeSection:
+ discard
+ of nkProcDef, nkMethodDef, nkConverterDef, nkMacroDef:
discard
of nkClosure:
if n[1].kind == nkNilLit:
- n[0] = liftCapturedVars(n[0], owner, d, c)
- let x = n[0].skipConv
+ n.sons[0] = liftCapturedVars(n[0], owner, d, c)
+ let x = n.sons[0].skipConv
if x.kind == nkClosure:
#localError(n.info, "internal error: closure to closure created")
# now we know better, so patch it:
- n[0] = x[0]
- n[1] = x[1]
+ n.sons[0] = x.sons[0]
+ n.sons[1] = x.sons[1]
of nkLambdaKinds, nkIteratorDef:
if n.typ != nil and n[namePos].kind == nkSym:
- let oldInContainer = c.inContainer
- c.inContainer = 0
let m = newSymNode(n[namePos].sym)
m.typ = n.typ
result = liftCapturedVars(m, owner, d, c)
- c.inContainer = oldInContainer
of nkHiddenStdConv:
if n.len == 2:
- n[1] = liftCapturedVars(n[1], owner, d, c)
+ n.sons[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}:
- # 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
- n[0][1] = liftCapturedVars(n[0][1], owner, d, c)
- else:
- n[0] = liftCapturedVars(n[0], owner, d, c)
- of nkTypeOfExpr:
- result = n
else:
if owner.isIterator:
- if nfLL in n.flags:
+ if n.kind == nkYieldStmt:
+ return transformYield(n, owner, d, c)
+ elif n.kind == nkReturnStmt:
+ return transformReturn(n, owner, d, c)
+ elif nfLL in n.flags:
# special case 'when nimVm' due to bug #3636:
- n[1] = liftCapturedVars(n[1], owner, d, c)
+ n.sons[1] = liftCapturedVars(n[1], owner, d, c)
return
-
- let inContainer = n.kind in {nkObjConstr, nkBracket}
- if inContainer: inc c.inContainer
for i in 0.. MinLineLen and endPos > startPos + 4
-
-proc findNewline(em: Emitter; p, lineLen: var int) =
- while p < em.tokens.len and em.kinds[p] notin {ltCrucialNewline, ltSplittingNewline}:
- inc lineLen, em.tokens[p].len
- inc p
-
-proc countNewlines(s: string): int =
- result = 0
- for i in 0..= 0 and s[i] != '\L':
- dec i
- inc em.col
-
-proc optionalIsGood(em: var Emitter; pos, currentLen: int): bool =
- let ourIndent = em.tokens[pos].len
- var p = pos+1
- var lineLen = 0
- em.findNewline(p, lineLen)
- if p == pos+1: # optionalNewline followed by another newline
- result = false
- elif em.kinds[p-1] == ltComment and currentLen+lineLen < em.maxLineLen+MinLineLen:
- result = false
- elif p+1 < em.tokens.len and em.kinds[p+1] == ltSpaces and
- em.kinds[p-1] == ltOptionalNewline:
- if em.tokens[p+1].len == ourIndent:
- # concatenate lines with the same indententation
- var nlPos = p
- var lineLenTotal = lineLen
- inc p
- em.findNewline(p, lineLenTotal)
- if isLongEnough(lineLenTotal, nlPos, p):
- em.kinds[nlPos] = ltOptionalNewline
- if em.kinds[nlPos+1] == ltSpaces:
- # inhibit extra spaces when concatenating two lines
- em.tokens[nlPos+1] = if em.tokens[nlPos-2] == ",": " " else: ""
- result = true
- elif em.tokens[p+1].len < ourIndent:
- result = isLongEnough(lineLen, pos, p)
- elif em.kinds[pos+1] in {ltOther, ltSomeParLe, ltSomeParRi}: # note: pos+1, not p+1
- result = false
- else:
- result = isLongEnough(lineLen, pos, p)
-
-proc lenOfNextTokens(em: Emitter; pos: int): int =
- result = 0
- for i in 1..= 0 and em.kinds[i] != ltSomeParLe:
- dec i
- while i+1 <= em.kinds.high and em.kinds[i] != ltSomeParRi:
- if em.kinds[i] == ltSplittingNewline and em.kinds[i+1] == ltSpaces:
- return em.tokens[i+1].len
- inc i
- result = -1
-
-proc renderTokens*(em: var Emitter): string =
- ## Render Emitter tokens to a string of code
- template defaultCase() =
- content.add em.tokens[i]
- inc lineLen, em.tokens[i].len
- var content = newStringOfCap(16_000)
- var maxLhs = 0
- var lineLen = 0
- var lineBegin = 0
- var openPars = 0
- var i = 0
- while i <= em.tokens.high:
- when defined(debug):
- echo (token: em.tokens[i], kind: em.kinds[i])
- case em.kinds[i]
- of ltBeginSection:
- maxLhs = computeMax(em, lineBegin)
- of ltEndSection:
- maxLhs = 0
- lineBegin = i+1
- of ltTab:
- if i >= 2 and em.kinds[i-2] in {ltCrucialNewline, ltSplittingNewline} and
- em.kinds[i-1] in {ltCrucialNewline, ltSplittingNewline, ltSpaces}:
- # a previous section has ended
- maxLhs = 0
-
- if maxLhs == 0:
- if em.kinds[i-1] != ltSpaces:
- content.add em.tokens[i]
- inc lineLen, em.tokens[i].len
- else:
- # pick the shorter indentation token:
- var spaces = maxLhs - lineLen
- if spaces < em.tokens[i].len or computeRhs(em, i+1)+maxLhs <= em.maxLineLen+MinLineLen:
- if spaces <= 0 and content[^1] notin {' ', '\L'}: spaces = 1
- for j in 1..spaces: content.add ' '
- inc lineLen, spaces
- else:
- content.add em.tokens[i]
- inc lineLen, em.tokens[i].len
- of ltCrucialNewline, ltSplittingNewline:
- content.add em.tokens[i]
- lineLen = 0
- lineBegin = i+1
- of ltOptionalNewline:
- let totalLineLen = lineLen + lenOfNextTokens(em, i)
- if totalLineLen > em.maxLineLen and optionalIsGood(em, i, lineLen):
- if i-1 >= 0 and em.kinds[i-1] == ltSpaces:
- let spaces = em.tokens[i-1].len
- content.setLen(content.len - spaces)
- content.add "\L"
- let guide = if openPars > 0: guidingInd(em, i) else: -1
- if guide >= 0:
- content.add repeat(' ', guide)
- lineLen = guide
- else:
- content.add em.tokens[i]
- lineLen = em.tokens[i].len
- lineBegin = i+1
- if i+1 < em.kinds.len and em.kinds[i+1] == ltSpaces:
- # inhibit extra spaces at the start of a new line
- inc i
- of ltLit:
- let lineSpan = countNewlines(em.tokens[i])
- if lineSpan > 0:
- em.calcCol(em.tokens[i])
- lineLen = em.col
- else:
- inc lineLen, em.tokens[i].len
- content.add em.tokens[i]
- of ltSomeParLe:
- inc openPars
- defaultCase()
- of ltSomeParRi:
- doAssert openPars > 0
- dec openPars
- defaultCase()
- else:
- defaultCase()
- inc i
-
- return content
-
-proc writeOut*(em: Emitter, content: string) =
- ## Write to disk
- let outFile = em.config.absOutFile
- if fileExists(outFile) and readFile(outFile.string) == content:
- discard "do nothing, see #9499"
- return
- var f = llStreamOpen(outFile, fmWrite)
- if f == nil:
- rawMessage(em.config, errGenerated, "cannot open file: " & outFile.string)
- return
- f.llStreamWrite content
- llStreamClose(f)
-
-proc closeEmitter*(em: var Emitter) =
- ## Renders emitter tokens and write to a file
- let content = renderTokens(em)
- em.writeOut(content)
-
-proc wr(em: var Emitter; x: string; lt: LayoutToken) =
- em.tokens.add x
- em.kinds.add lt
- inc em.col, x.len
- assert em.tokens.len == em.kinds.len
-
-proc wrNewline(em: var Emitter; kind = ltCrucialNewline) =
- em.tokens.add "\L"
- em.kinds.add kind
- em.col = 0
-
-proc newlineWasSplitting*(em: var Emitter) =
- if em.kinds.len >= 3 and em.kinds[^3] == ltCrucialNewline:
- em.kinds[^3] = ltSplittingNewline
-
-#[
-Splitting newlines can occur:
-- after commas, semicolon, '[', '('.
-- after binary operators, '='.
-- after ':' type
-
-We only need parser support for the "after type" case.
-]#
-
-proc wrSpaces(em: var Emitter; spaces: int) =
- if spaces > 0:
- wr(em, strutils.repeat(' ', spaces), ltSpaces)
-
-proc wrSpace(em: var Emitter) =
- wr(em, " ", ltSpaces)
-
-proc wrTab(em: var Emitter) =
- wr(em, " ", ltTab)
-
-proc beginSection*(em: var Emitter) =
- let pos = max(0, em.tokens.len-2)
- em.tokens.insert "", pos
- em.kinds.insert ltBeginSection, pos
- inc em.inSection
-
-#wr(em, "", ltBeginSection)
-proc endSection*(em: var Emitter) =
- em.tokens.insert "", em.tokens.len-2
- em.kinds.insert ltEndSection, em.kinds.len-2
- dec em.inSection
-
-#wr(em, "", ltEndSection)
-
-proc removeSpaces(em: var Emitter) =
- while em.kinds.len > 0 and em.kinds[^1] == ltSpaces:
- let tokenLen = em.tokens[^1].len
- setLen(em.tokens, em.tokens.len-1)
- setLen(em.kinds, em.kinds.len-1)
- dec em.col, tokenLen
-
-
-const
- openPars = {tkParLe, tkParDotLe,
- tkBracketLe, tkBracketDotLe, tkBracketLeColon,
- tkCurlyDotLe, tkCurlyLe}
- closedPars = {tkParRi, tkParDotRi,
- tkBracketRi, tkBracketDotRi,
- tkCurlyDotRi, tkCurlyRi}
-
- splitters = openPars + {tkComma, tkSemiColon} # do not add 'tkColon' here!
- oprSet = {tkOpr, tkDiv, tkMod, tkShl, tkShr, tkIn, tkNotin, tkIs,
- tkIsnot, tkNot, tkOf, tkAs, tkFrom, tkDotDot, tkAnd, tkOr, tkXor}
-
-template goodCol(col): bool = col >= em.maxLineLen div 2
-
-template moreIndent(em): int =
- if em.doIndentMore > 0: em.indWidth*2 else: em.indWidth
-
-template rememberSplit(kind) =
- if goodCol(em.col) and not em.inquote:
- let spaces = em.indentLevel+moreIndent(em)
- if spaces < em.col and spaces > 0:
- wr(em, strutils.repeat(' ', spaces), ltOptionalNewline)
- #em.altSplitPos[kind] = em.tokens.len
-
-proc emitMultilineComment(em: var Emitter, lit: string, col: int; dontIndent: bool) =
- # re-align every line in the multi-line comment:
- var i = 0
- var lastIndent = if em.keepIndents > 0: em.indentLevel else: em.indentStack[^1]
- var b = 0
- var dontIndent = dontIndent
- var hasEmptyLine = false
- for commentLine in splitLines(lit):
- if i == 0 and (commentLine.endsWith("\\") or commentLine.endsWith("[")):
- dontIndent = true
- wr em, commentLine, ltComment
- elif dontIndent:
- if i > 0: wrNewline em
- wr em, commentLine, ltComment
- else:
- let stripped = commentLine.strip()
- if i == 0:
- if em.kinds.len > 0 and em.kinds[^1] != ltTab:
- wr(em, "", ltTab)
- elif stripped.len == 0:
- wrNewline em
- hasEmptyLine = true
- else:
- var a = 0
- while a < commentLine.len and commentLine[a] == ' ': inc a
-
- if a > lastIndent:
- b += em.indWidth
- lastIndent = a
- elif a < lastIndent:
- b -= em.indWidth
- lastIndent = a
- wrNewline em
- if not hasEmptyLine or col + b < 15:
- if col + b > 0:
- wr(em, repeat(' ', col+b), ltTab)
- else:
- wr(em, "", ltTab)
- else:
- wr(em, repeat(' ', a), ltSpaces)
- wr em, stripped, ltComment
- inc i
-
-proc lastChar(s: string): char =
- result = if s.len > 0: s[s.high] else: '\0'
-
-proc endsInWhite(em: Emitter): bool =
- var i = em.tokens.len-1
- while i >= 0 and em.kinds[i] in {ltBeginSection, ltEndSection}: dec(i)
- result = if i >= 0: em.kinds[i] in {ltSpaces, ltCrucialNewline, ltSplittingNewline, ltTab} else: true
-
-proc endsInNewline(em: Emitter): bool =
- var i = em.tokens.len-1
- while i >= 0 and em.kinds[i] in {ltBeginSection, ltEndSection, ltSpaces}: dec(i)
- result = if i >= 0: em.kinds[i] in {ltCrucialNewline, ltSplittingNewline, ltTab} else: true
-
-proc endsInAlpha(em: Emitter): bool =
- var i = em.tokens.len-1
- while i >= 0 and em.kinds[i] in {ltBeginSection, ltEndSection}: dec(i)
- result = if i >= 0: em.tokens[i].lastChar in SymChars+{'_'} else: false
-
-proc emitComment(em: var Emitter; tok: Token; dontIndent: bool) =
- var col = em.col
- let lit = strip fileSection(em.config, em.fid, tok.commentOffsetA, tok.commentOffsetB)
- em.lineSpan = countNewlines(lit)
- if em.lineSpan > 0: calcCol(em, lit)
- if em.lineSpan == 0:
- if not endsInNewline(em):
- wrTab em
- wr em, lit, ltComment
- else:
- if not endsInWhite(em):
- wrTab em
- inc col
- emitMultilineComment(em, lit, col, dontIndent)
-
-proc emitTok*(em: var Emitter; L: Lexer; tok: Token) =
- template wasExportMarker(em): bool =
- em.kinds.len > 0 and em.kinds[^1] == ltExportMarker
-
- if tok.tokType == tkComment and tok.literal.startsWith("#!nimpretty"):
- case tok.literal
- of "#!nimpretty off":
- inc em.keepIndents
- wrNewline em
- em.lastLineNumber = tok.line + 1
- of "#!nimpretty on":
- dec em.keepIndents
- em.lastLineNumber = tok.line
- wrNewline em
- wr em, tok.literal, ltComment
- em.col = 0
- em.lineSpan = 0
- return
-
- var preventComment = false
- if tok.tokType == tkComment and tok.line == em.lastLineNumber:
- # we have an inline comment so handle it before the indentation token:
- emitComment(em, tok, dontIndent = (em.inSection == 0))
- preventComment = true
- em.fixedUntil = em.tokens.high
-
- elif tok.indent >= 0:
- var newlineKind = ltCrucialNewline
- if em.keepIndents > 0:
- em.indentLevel = tok.indent
- elif (em.lastTok in (splitters + oprSet) and
- tok.tokType notin (closedPars - {tkBracketDotRi})):
- if tok.tokType in openPars and tok.indent > em.indentStack[^1]:
- while em.indentStack[^1] < tok.indent:
- em.indentStack.add(em.indentStack[^1] + em.indWidth)
- while em.indentStack[^1] > tok.indent:
- discard em.indentStack.pop()
-
- # aka: we are in an expression context:
- let alignment = max(tok.indent - em.indentStack[^1], 0)
- em.indentLevel = alignment + em.indentStack.high * em.indWidth
- newlineKind = ltSplittingNewline
- else:
- if tok.indent > em.indentStack[^1]:
- em.indentStack.add tok.indent
- else:
- # dedent?
- while em.indentStack.len > 1 and em.indentStack[^1] > tok.indent:
- discard em.indentStack.pop()
- em.indentLevel = em.indentStack.high * em.indWidth
- #[ we only correct the indentation if it is not in an expression context,
- so that code like
-
- const splitters = {tkComma, tkSemicolon, tkParLe, tkParDotLe,
- tkBracketLe, tkBracketLeColon, tkCurlyDotLe,
- tkCurlyLe}
-
- is not touched.
- ]#
- # remove trailing whitespace:
- removeSpaces em
- wrNewline em, newlineKind
- for i in 2..tok.line - em.lastLineNumber: wrNewline(em)
- wrSpaces em, em.indentLevel
- em.fixedUntil = em.tokens.high
-
- var lastTokWasTerse = false
- case tok.tokType
- of tokKeywordLow..tokKeywordHigh:
- if endsInAlpha(em):
- wrSpace em
- elif not em.inquote and not endsInWhite(em) and
- em.lastTok notin (openPars+{tkOpr, tkDotDot}) and not em.lastTokWasTerse:
- #and tok.tokType in oprSet
- wrSpace em
-
- if not em.inquote:
- wr(em, $tok.tokType, ltKeyword)
- if tok.tokType in {tkAnd, tkOr, tkIn, tkNotin}:
- rememberSplit(splitIn)
- wrSpace em
- else:
- # keywords in backticks are not normalized:
- wr(em, tok.ident.s, ltIdent)
-
- of tkColon:
- wr(em, $tok.tokType, ltOther)
- wrSpace em
- of tkSemiColon, tkComma:
- wr(em, $tok.tokType, ltOther)
- rememberSplit(splitComma)
- wrSpace em
- of openPars:
- if tok.strongSpaceA 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)
- of closedPars:
- wr(em, $tok.tokType, ltSomeParRi)
- of tkColonColon:
- wr(em, $tok.tokType, ltOther)
- of tkDot:
- lastTokWasTerse = true
- wr(em, $tok.tokType, ltOther)
- of tkEquals:
- if not em.inquote and not em.endsInWhite: wrSpace(em)
- 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
- tok.ident.s notin ["<", ">", "<=", ">=", "==", "!="]):
- # bug #9504: remember to not spacify a keyword:
- lastTokWasTerse = true
- # if not surrounded by whitespace, don't produce any whitespace either:
- wr(em, tok.ident.s, ltOpr)
- else:
- if not em.endsInWhite: wrSpace(em)
- wr(em, tok.ident.s, ltOpr)
- template isUnary(tok): bool =
- tok.strongSpaceB == tsNone and tok.strongSpaceA
-
- if not isUnary(tok):
- rememberSplit(splitBinary)
- wrSpace(em)
- of tkAccent:
- if not em.inquote and endsInAlpha(em): wrSpace(em)
- wr(em, $tok.tokType, ltOther)
- em.inquote = not em.inquote
- of tkComment:
- 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
- of tkEof: discard
- else:
- let lit = if tok.ident != nil: tok.ident.s else: tok.literal
- if endsInAlpha(em): wrSpace(em)
- wr em, lit, ltIdent
-
- em.lastTok = tok.tokType
- em.lastTokWasTerse = lastTokWasTerse
- em.lastLineNumber = tok.line + em.lineSpan
- em.lineSpan = 0
-
-proc endsWith(em: Emitter; k: varargs[string]): bool =
- if em.tokens.len < k.len: return false
- for i in 0..high(k):
- if em.tokens[em.tokens.len - k.len + i] != k[i]: return false
- return true
-
-proc rfind(em: Emitter, t: string): int =
- for i in 1..5:
- if em.tokens[^i] == t:
- return i
-
-proc starWasExportMarker*(em: var Emitter) =
- if em.endsWith(" ", "*", " "):
- setLen(em.tokens, em.tokens.len-3)
- setLen(em.kinds, em.kinds.len-3)
- em.tokens.add("*")
- em.kinds.add ltExportMarker
- dec em.col, 2
-
-proc commaWasSemicolon*(em: var Emitter) =
- if em.semicolons == detectSemicolonKind:
- em.semicolons = if em.rfind(";") > 0: useSemicolon else: dontTouch
- if em.semicolons == useSemicolon:
- let commaPos = em.rfind(",")
- if commaPos > 0:
- em.tokens[^commaPos] = ";"
-
-proc curlyRiWasPragma*(em: var Emitter) =
- if em.endsWith("}"):
- em.tokens[^1] = ".}"
- inc em.col
diff --git a/compiler/lexer.nim b/compiler/lexer.nim
index a62d40e54..8b201431e 100644
--- a/compiler/lexer.nim
+++ b/compiler/lexer.nim
@@ -7,20 +7,17 @@
# 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
+# the \n or \r character we have to check wether 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]
+ wordrecg, etcpriv
const
MaxLineLength* = 80 # lines longer than this lead to a warning
@@ -28,569 +25,518 @@ const
SymChars*: set[char] = {'a'..'z', 'A'..'Z', '0'..'9', '\x80'..'\xFF'}
SymStartChars*: set[char] = {'a'..'z', 'A'..'Z', '\x80'..'\xFF'}
OpChars*: set[char] = {'+', '-', '*', '/', '\\', '<', '>', '!', '?', '^', '.',
- '|', '=', '%', '&', '$', '@', '~', ':'}
- UnaryMinusWhitelist = {' ', '\t', '\n', '\r', ',', ';', '(', '[', '{'}
+ '|', '=', '%', '&', '$', '@', '~', ':', '\x80'..'\xFF'}
# don't forget to update the 'highlite' module if these charsets should change
type
- TokType* = enum
- tkInvalid = "tkInvalid", tkEof = "[EOF]", # order is important here!
- tkSymbol = "tkSymbol", # keywords:
- tkAddr = "addr", tkAnd = "and", tkAs = "as", tkAsm = "asm",
- tkBind = "bind", tkBlock = "block", tkBreak = "break", tkCase = "case", tkCast = "cast",
- tkConcept = "concept", tkConst = "const", tkContinue = "continue", tkConverter = "converter",
- tkDefer = "defer", tkDiscard = "discard", tkDistinct = "distinct", tkDiv = "div", tkDo = "do",
- tkElif = "elif", tkElse = "else", tkEnd = "end", tkEnum = "enum", tkExcept = "except", tkExport = "export",
- tkFinally = "finally", tkFor = "for", tkFrom = "from", tkFunc = "func",
- tkIf = "if", tkImport = "import", tkIn = "in", tkInclude = "include", tkInterface = "interface",
- tkIs = "is", tkIsnot = "isnot", tkIterator = "iterator",
- tkLet = "let",
- tkMacro = "macro", tkMethod = "method", tkMixin = "mixin", tkMod = "mod", tkNil = "nil", tkNot = "not", tkNotin = "notin",
- tkObject = "object", tkOf = "of", tkOr = "or", tkOut = "out",
- tkProc = "proc", tkPtr = "ptr", tkRaise = "raise", tkRef = "ref", tkReturn = "return",
- tkShl = "shl", tkShr = "shr", tkStatic = "static",
- tkTemplate = "template",
- tkTry = "try", tkTuple = "tuple", tkType = "type", tkUsing = "using",
- tkVar = "var", tkWhen = "when", tkWhile = "while", tkXor = "xor",
- tkYield = "yield", # end of keywords
+ TTokType* = enum
+ tkInvalid, tkEof, # order is important here!
+ tkSymbol, # keywords:
+ tkAddr, tkAnd, tkAs, tkAsm, tkAtomic,
+ tkBind, tkBlock, tkBreak, tkCase, tkCast,
+ tkConcept, tkConst, tkContinue, tkConverter,
+ tkDefer, tkDiscard, tkDistinct, tkDiv, tkDo,
+ tkElif, tkElse, tkEnd, tkEnum, tkExcept, tkExport,
+ tkFinally, tkFor, tkFrom, tkFunc,
+ tkGeneric, tkIf, tkImport, tkIn, tkInclude, tkInterface,
+ tkIs, tkIsnot, tkIterator,
+ tkLet,
+ tkMacro, tkMethod, tkMixin, tkMod, tkNil, tkNot, tkNotin,
+ tkObject, tkOf, tkOr, tkOut,
+ tkProc, tkPtr, tkRaise, tkRef, tkReturn,
+ tkShl, tkShr, tkStatic,
+ tkTemplate,
+ tkTry, tkTuple, tkType, tkUsing,
+ tkVar, tkWhen, tkWhile, tkWith, tkWithout, tkXor,
+ tkYield, # end of keywords
+ tkIntLit, tkInt8Lit, tkInt16Lit, tkInt32Lit, tkInt64Lit,
+ tkUIntLit, tkUInt8Lit, tkUInt16Lit, tkUInt32Lit, tkUInt64Lit,
+ tkFloatLit, tkFloat32Lit, tkFloat64Lit, tkFloat128Lit,
+ tkStrLit, tkRStrLit, tkTripleStrLit,
+ tkGStrLit, tkGTripleStrLit, tkCharLit, tkParLe, tkParRi, tkBracketLe,
+ tkBracketRi, tkCurlyLe, tkCurlyRi,
+ tkBracketDotLe, tkBracketDotRi, # [. and .]
+ tkCurlyDotLe, tkCurlyDotRi, # {. and .}
+ tkParDotLe, tkParDotRi, # (. and .)
+ tkComma, tkSemiColon,
+ tkColon, tkColonColon, tkEquals, tkDot, tkDotDot,
+ tkOpr, tkComment, tkAccent,
+ tkSpaces, tkInfixOpr, tkPrefixOpr, tkPostfixOpr
- tkIntLit = "tkIntLit", tkInt8Lit = "tkInt8Lit", tkInt16Lit = "tkInt16Lit",
- tkInt32Lit = "tkInt32Lit", tkInt64Lit = "tkInt64Lit",
- 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",
-
- tkParLe = "(", tkParRi = ")", tkBracketLe = "[",
- tkBracketRi = "]", tkCurlyLe = "{", tkCurlyRi = "}",
- tkBracketDotLe = "[.", tkBracketDotRi = ".]",
- tkCurlyDotLe = "{.", tkCurlyDotRi = ".}",
- tkParDotLe = "(.", tkParDotRi = ".)",
- tkComma = ",", tkSemiColon = ";",
- tkColon = ":", tkColonColon = "::", tkEquals = "=",
- tkDot = ".", tkDotDot = "..", tkBracketLeColon = "[:",
- tkOpr, tkComment, tkAccent = "`",
- # these are fake tokens used by renderer.nim
- tkSpaces, tkInfixOpr, tkPrefixOpr, tkPostfixOpr, tkHideableStart, tkHideableEnd
-
- TokTypes* = set[TokType]
+ TTokTypes* = set[TTokType]
const
- weakTokens = {tkComma, tkSemiColon, tkColon,
- tkParRi, tkParDotRi, tkBracketRi, tkBracketDotRi,
- tkCurlyRi} # \
- # tokens that should not be considered for previousToken
tokKeywordLow* = succ(tkSymbol)
tokKeywordHigh* = pred(tkIntLit)
+ TokTypeToStr*: array[TTokType, string] = ["tkInvalid", "[EOF]",
+ "tkSymbol",
+ "addr", "and", "as", "asm", "atomic",
+ "bind", "block", "break", "case", "cast",
+ "concept", "const", "continue", "converter",
+ "defer", "discard", "distinct", "div", "do",
+ "elif", "else", "end", "enum", "except", "export",
+ "finally", "for", "from", "func", "generic", "if",
+ "import", "in", "include", "interface", "is", "isnot", "iterator",
+ "let",
+ "macro", "method", "mixin", "mod",
+ "nil", "not", "notin", "object", "of", "or",
+ "out", "proc", "ptr", "raise", "ref", "return",
+ "shl", "shr", "static",
+ "template",
+ "try", "tuple", "type", "using",
+ "var", "when", "while", "with", "without", "xor",
+ "yield",
+ "tkIntLit", "tkInt8Lit", "tkInt16Lit", "tkInt32Lit", "tkInt64Lit",
+ "tkUIntLit", "tkUInt8Lit", "tkUInt16Lit", "tkUInt32Lit", "tkUInt64Lit",
+ "tkFloatLit", "tkFloat32Lit", "tkFloat64Lit", "tkFloat128Lit",
+ "tkStrLit", "tkRStrLit",
+ "tkTripleStrLit", "tkGStrLit", "tkGTripleStrLit", "tkCharLit", "(",
+ ")", "[", "]", "{", "}", "[.", ".]", "{.", ".}", "(.", ".)",
+ ",", ";",
+ ":", "::", "=", ".", "..",
+ "tkOpr", "tkComment", "`",
+ "tkSpaces", "tkInfixOpr",
+ "tkPrefixOpr", "tkPostfixOpr"]
type
- NumericalBase* = enum
+ TNumericalBase* = enum
base10, # base10 is listed as the first element,
# 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
+ TToken* = object # a Nim token
+ tokType*: TTokType # 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*: TNumericalBase # 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
- # like 0b01 or r"\L" are unaffected
- commentOffsetA*, commentOffsetB*: int
- ErrorHandler* = proc (conf: ConfigRef; info: TLineInfo; msg: TMsgKind; arg: string)
- Lexer* = object of TBaseLexer
- fileIdx*: FileIndex
- indentAhead*: int # if > 0 an indentation has already been read
+ TErrorHandler* = proc (info: TLineInfo; msg: TMsgKind; arg: string)
+ TLexer* = object of TBaseLexer
+ fileIdx*: int32
+ indentAhead*: int # if > 0 an indendation has already been read
# this is needed because scanning comments
# needs so much look-ahead
currLineIndent*: int
- strongSpaces*, allowTabs*: bool
- errorHandler*: ErrorHandler
- cache*: IdentCache
- when defined(nimsuggest):
- previousToken: TLineInfo
- tokenEnd*: TLineInfo
- previousTokenEnd*: TLineInfo
- config*: ConfigRef
+ strongSpaces*: bool
+ errorHandler*: TErrorHandler
-proc getLineInfo*(L: Lexer, tok: Token): TLineInfo {.inline.} =
- result = newLineInfo(L.fileIdx, tok.line, tok.col)
- when defined(nimpretty):
- result.offsetA = tok.offsetA
- result.offsetB = tok.offsetB
- result.commentOffsetA = tok.commentOffsetA
- result.commentOffsetB = tok.commentOffsetB
+var gLinesCompiled*: int # all lines that have been compiled
-proc isKeyword*(kind: TokType): bool =
- (kind >= tokKeywordLow) and (kind <= tokKeywordHigh)
+proc getLineInfo*(L: TLexer, tok: TToken): TLineInfo {.inline.} =
+ newLineInfo(L.fileIdx, tok.line, tok.col)
-template ones(n): untyped = ((1 shl n)-1) # for utf-8 conversion
+proc isKeyword*(kind: TTokType): bool =
+ result = (kind >= tokKeywordLow) and (kind <= tokKeywordHigh)
+
+template ones(n: expr): expr = ((1 shl n)-1) # for utf-8 conversion
proc isNimIdentifier*(s: string): bool =
- let sLen = s.len
- if sLen > 0 and s[0] in SymStartChars:
+ if s[0] in SymStartChars:
var i = 1
+ var sLen = s.len
while i < sLen:
- if s[i] == '_': inc(i)
- if i < sLen and s[i] notin SymChars: return
+ if s[i] == '_':
+ inc(i)
+ elif isMagicIdentSeparatorRune(cstring s, i):
+ inc(i, magicIdentSeparatorRuneByteWidth)
+ if s[i] notin SymChars: return
inc(i)
result = true
-proc `$`*(tok: Token): string =
+proc tokToStr*(tok: TToken): string =
case tok.tokType
- of tkIntLit..tkInt64Lit: $tok.iNumber
- of tkFloatLit..tkFloat64Lit: $tok.fNumber
- of tkInvalid, tkStrLit..tkCharLit, tkComment: tok.literal
- of tkParLe..tkColon, tkEof, tkAccent: $tok.tokType
+ of tkIntLit..tkInt64Lit: result = $tok.iNumber
+ of tkFloatLit..tkFloat64Lit: result = $tok.fNumber
+ of tkInvalid, tkStrLit..tkCharLit, tkComment: result = tok.literal
+ of tkParLe..tkColon, tkEof, tkAccent:
+ result = TokTypeToStr[tok.tokType]
else:
if tok.ident != nil:
- tok.ident.s
+ result = tok.ident.s
else:
- ""
+ internalError("tokToStr")
+ result = ""
-proc prettyTok*(tok: Token): string =
- if isKeyword(tok.tokType): "keyword " & tok.ident.s
- else: $tok
+proc prettyTok*(tok: TToken): string =
+ if isKeyword(tok.tokType): result = "keyword " & tok.ident.s
+ else: result = tokToStr(tok)
-proc printTok*(conf: ConfigRef; tok: Token) =
- # xxx factor with toLocation
- msgWriteln(conf, $tok.line & ":" & $tok.col & "\t" & $tok.tokType & " " & $tok)
+proc printTok*(tok: TToken) =
+ msgWriteln($tok.line & ":" & $tok.col & "\t" &
+ TokTypeToStr[tok.tokType] & " " & tokToStr(tok))
-proc initToken*(L: var Token) =
+var dummyIdent: PIdent
+
+proc initToken*(L: var TToken) =
L.tokType = tkInvalid
L.iNumber = 0
L.indent = 0
- L.strongSpaceA = false
+ L.strongSpaceA = 0
L.literal = ""
L.fNumber = 0.0
L.base = base10
- L.ident = nil
- when defined(nimpretty):
- L.commentOffsetA = 0
- L.commentOffsetB = 0
+ L.ident = dummyIdent
-proc fillToken(L: var Token) =
+proc fillToken(L: var TToken) =
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
- L.ident = nil
- when defined(nimpretty):
- L.commentOffsetA = 0
- L.commentOffsetB = 0
+ L.ident = dummyIdent
-proc openLexer*(lex: var Lexer, fileIdx: FileIndex, inputstream: PLLStream;
- cache: IdentCache; config: ConfigRef) =
+proc openLexer*(lex: var TLexer, fileIdx: int32, inputstream: PLLStream) =
openBaseLexer(lex, inputstream)
- lex.fileIdx = fileIdx
- lex.indentAhead = -1
+ lex.fileIdx = fileidx
+ lex.indentAhead = - 1
lex.currLineIndent = 0
inc(lex.lineNumber, inputstream.lineOffset)
- lex.cache = cache
- when defined(nimsuggest):
- lex.previousToken.fileIndex = fileIdx
- lex.config = config
-proc openLexer*(lex: var Lexer, filename: AbsoluteFile, inputstream: PLLStream;
- cache: IdentCache; config: ConfigRef) =
- openLexer(lex, fileInfoIdx(config, filename), inputstream, cache, config)
+proc openLexer*(lex: var TLexer, filename: string, inputstream: PLLStream) =
+ openLexer(lex, filename.fileInfoIdx, inputstream)
-proc closeLexer*(lex: var Lexer) =
- if lex.config != nil:
- inc(lex.config.linesCompiled, lex.lineNumber)
+proc closeLexer*(lex: var TLexer) =
+ inc(gLinesCompiled, lex.lineNumber)
closeBaseLexer(lex)
-proc getLineInfo(L: Lexer): TLineInfo =
+proc getColumn(L: TLexer): int =
+ result = getColNumber(L, L.bufpos)
+
+proc getLineInfo(L: TLexer): TLineInfo =
result = newLineInfo(L.fileIdx, L.lineNumber, getColNumber(L, L.bufpos))
-proc dispMessage(L: Lexer; info: TLineInfo; msg: TMsgKind; arg: string) =
+proc dispMessage(L: TLexer; info: TLineInfo; msg: TMsgKind; arg: string) =
if L.errorHandler.isNil:
- msgs.message(L.config, info, msg, arg)
+ msgs.message(info, msg, arg)
else:
- L.errorHandler(L.config, info, msg, arg)
+ L.errorHandler(info, msg, arg)
-proc lexMessage*(L: Lexer, msg: TMsgKind, arg = "") =
+proc lexMessage*(L: TLexer, msg: TMsgKind, arg = "") =
L.dispMessage(getLineInfo(L), msg, arg)
-proc lexMessageTok*(L: Lexer, msg: TMsgKind, tok: Token, arg = "") =
+proc lexMessageTok*(L: TLexer, msg: TMsgKind, tok: TToken, arg = "") =
var info = newLineInfo(L.fileIdx, tok.line, tok.col)
L.dispMessage(info, msg, arg)
-proc lexMessagePos(L: var Lexer, msg: TMsgKind, pos: int, arg = "") =
+proc lexMessagePos(L: var TLexer, msg: TMsgKind, pos: int, arg = "") =
var info = newLineInfo(L.fileIdx, L.lineNumber, pos - L.lineStart)
L.dispMessage(info, msg, arg)
-proc matchTwoChars(L: Lexer, first: char, second: set[char]): bool =
+proc matchTwoChars(L: TLexer, first: char, second: set[char]): bool =
result = (L.buf[L.bufpos] == first) and (L.buf[L.bufpos + 1] in second)
-template tokenBegin(tok, pos) {.dirty.} =
- when defined(nimsuggest):
- var colA = getColNumber(L, pos)
- when defined(nimpretty):
- tok.offsetA = L.offsetBase + pos
+proc isFloatLiteral(s: string): bool =
+ for i in countup(0, len(s) - 1):
+ if s[i] in {'.', 'e', 'E'}:
+ return true
+ result = false
-template tokenEnd(tok, pos) {.dirty.} =
- when defined(nimsuggest):
- let colB = getColNumber(L, pos)+1
- if L.fileIdx == L.config.m.trackPos.fileIndex and L.config.m.trackPos.col in colA..colB and
- L.lineNumber == L.config.m.trackPos.line.int and L.config.ideCmd in {ideSug, ideCon}:
- L.config.m.trackPos.col = colA.int16
- colA = 0
- when defined(nimpretty):
- tok.offsetB = L.offsetBase + pos
+{.push overflowChecks: off.}
+# We need to parse the largest uint literal without overflow checks
+proc unsafeParseUInt(s: string, b: var BiggestInt, start = 0): int =
+ var i = start
+ if s[i] in {'0'..'9'}:
+ b = 0
+ while s[i] in {'0'..'9'}:
+ b = b * 10 + (ord(s[i]) - ord('0'))
+ inc(i)
+ while s[i] == '_': inc(i) # underscores are allowed and ignored
+ result = i - start
+{.pop.} # overflowChecks
-template tokenEndIgnore(tok, pos) =
- when defined(nimsuggest):
- let colB = getColNumber(L, pos)
- if L.fileIdx == L.config.m.trackPos.fileIndex and L.config.m.trackPos.col in colA..colB and
- L.lineNumber == L.config.m.trackPos.line.int and L.config.ideCmd in {ideSug, ideCon}:
- L.config.m.trackPos.fileIndex = trackPosInvalidFileIdx
- L.config.m.trackPos.line = 0'u16
- colA = 0
- when defined(nimpretty):
- tok.offsetB = L.offsetBase + pos
-template tokenEndPrevious(tok, pos) =
- when defined(nimsuggest):
- # when we detect the cursor in whitespace, we attach the track position
- # to the token that came before that, but only if we haven't detected
- # the cursor in a string literal or comment:
- let colB = getColNumber(L, pos)
- if L.fileIdx == L.config.m.trackPos.fileIndex and L.config.m.trackPos.col in colA..colB and
- L.lineNumber == L.config.m.trackPos.line.int and L.config.ideCmd in {ideSug, ideCon}:
- L.config.m.trackPos = L.previousToken
- L.config.m.trackPosAttached = true
- colA = 0
- when defined(nimpretty):
- tok.offsetB = L.offsetBase + pos
-
-template eatChar(L: var Lexer, t: var Token, replacementChar: char) =
- t.literal.add(replacementChar)
+template eatChar(L: var TLexer, t: var TToken, replacementChar: char) =
+ add(t.literal, replacementChar)
inc(L.bufpos)
-template eatChar(L: var Lexer, t: var Token) =
- t.literal.add(L.buf[L.bufpos])
+template eatChar(L: var TLexer, t: var TToken) =
+ add(t.literal, L.buf[L.bufpos])
inc(L.bufpos)
-proc getNumber(L: var Lexer, result: var Token) =
- proc matchUnderscoreChars(L: var Lexer, tok: var Token, chars: set[char]): Natural =
+proc getNumber(L: var TLexer, result: var TToken) =
+ proc matchUnderscoreChars(L: var TLexer, tok: var TToken, chars: set[char]) =
var pos = L.bufpos # use registers for pos, buf
- result = 0
+ var buf = L.buf
while true:
- if L.buf[pos] in chars:
- tok.literal.add(L.buf[pos])
+ if buf[pos] in chars:
+ add(tok.literal, buf[pos])
inc(pos)
- inc(result)
else:
break
- if L.buf[pos] == '_':
- if L.buf[pos+1] notin chars:
- lexMessage(L, errGenerated,
- "only single underscores may occur in a token and token may not " &
- "end with an underscore: e.g. '1__1' and '1_' are invalid")
+ if buf[pos] == '_':
+ if buf[pos+1] notin chars:
+ lexMessage(L, errInvalidToken, "_")
break
- tok.literal.add('_')
+ add(tok.literal, '_')
inc(pos)
L.bufpos = pos
- proc matchChars(L: var Lexer, tok: var Token, chars: set[char]) =
+ proc matchChars(L: var TLexer, tok: var TToken, chars: set[char]) =
var pos = L.bufpos # use registers for pos, buf
- while L.buf[pos] in chars:
- tok.literal.add(L.buf[pos])
+ var buf = L.buf
+ while buf[pos] in chars:
+ add(tok.literal, buf[pos])
inc(pos)
L.bufpos = pos
- proc lexMessageLitNum(L: var Lexer, msg: string, startpos: int, msgKind = errGenerated) =
+ proc lexMessageLitNum(L: var TLexer, msg: TMsgKind, startpos: int) =
# Used to get slightly human friendlier err messages.
- const literalishChars = {'A'..'Z', 'a'..'z', '0'..'9', '_', '.', '\''}
+ # Note: the erroneous 'O' char in the character set is intentional
+ 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
+ var t: TToken
t.literal = ""
L.bufpos = startpos # Use L.bufpos as pos because of matchChars
matchChars(L, t, literalishChars)
# We must verify +/- specifically so that we're not past the literal
if L.buf[L.bufpos] in {'+', '-'} and
L.buf[L.bufpos - 1] in {'e', 'E'}:
- t.literal.add(L.buf[L.bufpos])
+ add(t.literal, 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)
+ add(t.literal, L.buf[L.bufpos])
matchChars(L, t, {'0'..'9'})
L.bufpos = msgPos
- lexMessage(L, msgKind, msg % t.literal)
+ lexMessage(L, msg, t.literal)
var
+ startpos, endpos: int
xi: BiggestInt
isBase10 = true
- numDigits = 0
const
- # 'c', 'C' is deprecated
- baseCodeChars = {'X', 'x', 'o', 'b', 'B', 'c', 'C'}
+ baseCodeChars = {'X', 'x', 'o', 'c', 'C', 'b', 'B'}
literalishChars = baseCodeChars + {'A'..'F', 'a'..'f', '0'..'9', '_', '\''}
floatTypes = {tkFloatLit, tkFloat32Lit, tkFloat64Lit, tkFloat128Lit}
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
# First stage: find out base, make verifications, build token literal string
- # {'c', 'C'} is added for deprecation reasons to provide a clear error message
- if L.buf[L.bufpos] == '0' and L.buf[L.bufpos + 1] in baseCodeChars + {'c', 'C', 'O'}:
+ if L.buf[L.bufpos] == '0' and L.buf[L.bufpos + 1] in baseCodeChars + {'O'}:
isBase10 = false
eatChar(L, result, '0')
case L.buf[L.bufpos]
- of 'c', 'C':
- lexMessageLitNum(L,
- "$1 will soon be invalid for oct literals; Use '0o' " &
- "for octals. 'c', 'C' prefix",
- startpos,
- warnDeprecated)
- eatChar(L, result, 'c')
- numDigits = matchUnderscoreChars(L, result, {'0'..'7'})
of 'O':
- lexMessageLitNum(L, "$1 is an invalid int literal; For octal literals " &
- "use the '0o' prefix.", startpos)
+ lexMessageLitNum(L, errInvalidNumberOctalCode, startpos)
of 'x', 'X':
eatChar(L, result, 'x')
- numDigits = matchUnderscoreChars(L, result, {'0'..'9', 'a'..'f', 'A'..'F'})
- of 'o':
- eatChar(L, result, 'o')
- numDigits = matchUnderscoreChars(L, result, {'0'..'7'})
+ matchUnderscoreChars(L, result, {'0'..'9', 'a'..'f', 'A'..'F'})
+ of 'o', 'c', 'C':
+ eatChar(L, result, 'c')
+ matchUnderscoreChars(L, result, {'0'..'7'})
of 'b', 'B':
eatChar(L, result, 'b')
- numDigits = matchUnderscoreChars(L, result, {'0'..'1'})
+ matchUnderscoreChars(L, result, {'0'..'1'})
else:
- internalError(L.config, getLineInfo(L), "getNumber")
- if numDigits == 0:
- lexMessageLitNum(L, "invalid number: '$1'", startpos)
+ internalError(getLineInfo(L), "getNumber")
else:
- discard matchUnderscoreChars(L, result, {'0'..'9'})
+ matchUnderscoreChars(L, result, {'0'..'9'})
if (L.buf[L.bufpos] == '.') and (L.buf[L.bufpos + 1] in {'0'..'9'}):
- result.tokType = tkFloatLit
+ result.tokType = tkFloat64Lit
eatChar(L, result, '.')
- discard matchUnderscoreChars(L, result, {'0'..'9'})
+ matchUnderscoreChars(L, result, {'0'..'9'})
if L.buf[L.bufpos] in {'e', 'E'}:
- result.tokType = tkFloatLit
- eatChar(L, result)
+ result.tokType = tkFloat64Lit
+ eatChar(L, result, 'e')
if L.buf[L.bufpos] in {'+', '-'}:
eatChar(L, result)
- discard matchUnderscoreChars(L, result, {'0'..'9'})
- let endpos = L.bufpos
+ matchUnderscoreChars(L, result, {'0'..'9'})
+ 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, errInvalidNumber, 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, errInvalidNumber, 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)
+ lexMessageLitNum(L, errInvalidNumber, 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)
+ 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(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 = BiggestInt(int8(toU8(int(xi))))
+ of tkInt16Lit: result.iNumber = BiggestInt(int16(toU16(int(xi))))
+ of tkInt32Lit: result.iNumber = BiggestInt(int32(toU32(int64(xi))))
+ of tkUIntLit, tkUInt64Lit: result.iNumber = xi
+ of tkUInt8Lit: result.iNumber = BiggestInt(uint8(toU8(int(xi))))
+ of tkUInt16Lit: result.iNumber = BiggestInt(uint16(toU16(int(xi))))
+ of tkUInt32Lit: result.iNumber = BiggestInt(uint32(toU32(int64(xi))))
+ of tkFloat32Lit:
+ result.fNumber = (cast[PFloat32](addr(xi)))[]
+ # note: this code is endian neutral!
+ # XXX: Test this on big endian machine!
+ of tkFloat64Lit: result.fNumber = (cast[PFloat64](addr(xi)))[]
+ else: internalError(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:
- lexMessageLitNum(L, "number out of range: '$1'", startpos)
+ #echo "out of range num: ", result.iNumber, " vs ", xi
+ lexMessageLitNum(L, errNumberOutOfRange, 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:
+ xi = 0
+ let len = unsafeParseUInt(result.literal, xi)
+ if len != result.literal.len or len == 0:
+ raise newException(ValueError, "invalid integer: " & $xi)
+ result.iNumber = xi
+ else:
+ result.iNumber = parseBiggestInt(result.literal)
- except ValueError:
- lexMessageLitNum(L, "invalid number: '$1'", startpos)
- except OverflowDefect, RangeDefect:
- lexMessageLitNum(L, "number out of range: '$1'", startpos)
- tokenEnd(result, postPos-1)
+ # Explicit bounds checks
+ let outOfRange = case result.tokType:
+ of tkInt8Lit: (result.iNumber < int8.low or result.iNumber > int8.high)
+ of tkUInt8Lit: (result.iNumber < BiggestInt(uint8.low) or
+ result.iNumber > BiggestInt(uint8.high))
+ of tkInt16Lit: (result.iNumber < int16.low or result.iNumber > int16.high)
+ of tkUInt16Lit: (result.iNumber < BiggestInt(uint16.low) or
+ result.iNumber > BiggestInt(uint16.high))
+ of tkInt32Lit: (result.iNumber < int32.low or result.iNumber > int32.high)
+ of tkUInt32Lit: (result.iNumber < BiggestInt(uint32.low) or
+ result.iNumber > BiggestInt(uint32.high))
+ else: false
+
+ if outOfRange: lexMessageLitNum(L, errNumberOutOfRange, startpos)
+
+ # Promote int literal to int64? Not always necessary, but more consistent
+ if result.tokType == tkIntLit:
+ if (result.iNumber < low(int32)) or (result.iNumber > high(int32)):
+ result.tokType = tkInt64Lit
+
+ except ValueError:
+ lexMessageLitNum(L, errInvalidNumber, startpos)
+ except OverflowError, RangeError:
+ lexMessageLitNum(L, errNumberOutOfRange, startpos)
L.bufpos = postPos
-proc handleHexChar(L: var Lexer, xi: var int; position: range[0..4]) =
- template invalid() =
- lexMessage(L, errGenerated,
- "expected a hex digit, but found: " & L.buf[L.bufpos] &
- "; maybe prepend with 0")
-
+proc handleHexChar(L: var TLexer, xi: var int) =
case L.buf[L.bufpos]
of '0'..'9':
xi = (xi shl 4) or (ord(L.buf[L.bufpos]) - ord('0'))
@@ -601,145 +547,99 @@ proc handleHexChar(L: var Lexer, xi: var int; position: range[0..4]) =
of 'A'..'F':
xi = (xi shl 4) or (ord(L.buf[L.bufpos]) - ord('A') + 10)
inc(L.bufpos)
- of '"', '\'':
- if position <= 1: invalid()
- # do not progress the bufpos here.
- if position == 0: inc(L.bufpos)
- else:
- invalid()
- # Need to progress for `nim check`
- inc(L.bufpos)
+ else: discard
-proc handleDecChars(L: var Lexer, xi: var int) =
+proc handleDecChars(L: var TLexer, xi: var int) =
while L.buf[L.bufpos] in {'0'..'9'}:
xi = (xi * 10) + (ord(L.buf[L.bufpos]) - ord('0'))
inc(L.bufpos)
-proc addUnicodeCodePoint(s: var string, i: int) =
- let i = cast[uint](i)
- # inlined toUTF-8 to avoid unicode and strutils dependencies.
- let pos = s.len
- if i <= 127:
- s.setLen(pos+1)
- s[pos+0] = chr(i)
- elif i <= 0x07FF:
- s.setLen(pos+2)
- s[pos+0] = chr((i shr 6) or 0b110_00000)
- s[pos+1] = chr((i and ones(6)) or 0b10_0000_00)
- elif i <= 0xFFFF:
- s.setLen(pos+3)
- s[pos+0] = chr(i shr 12 or 0b1110_0000)
- s[pos+1] = chr(i shr 6 and ones(6) or 0b10_0000_00)
- s[pos+2] = chr(i and ones(6) or 0b10_0000_00)
- elif i <= 0x001FFFFF:
- s.setLen(pos+4)
- s[pos+0] = chr(i shr 18 or 0b1111_0000)
- s[pos+1] = chr(i shr 12 and ones(6) or 0b10_0000_00)
- s[pos+2] = chr(i shr 6 and ones(6) or 0b10_0000_00)
- s[pos+3] = chr(i and ones(6) or 0b10_0000_00)
- elif i <= 0x03FFFFFF:
- s.setLen(pos+5)
- s[pos+0] = chr(i shr 24 or 0b111110_00)
- s[pos+1] = chr(i shr 18 and ones(6) or 0b10_0000_00)
- s[pos+2] = chr(i shr 12 and ones(6) or 0b10_0000_00)
- s[pos+3] = chr(i shr 6 and ones(6) or 0b10_0000_00)
- s[pos+4] = chr(i and ones(6) or 0b10_0000_00)
- elif i <= 0x7FFFFFFF:
- s.setLen(pos+6)
- s[pos+0] = chr(i shr 30 or 0b1111110_0)
- s[pos+1] = chr(i shr 24 and ones(6) or 0b10_0000_00)
- s[pos+2] = chr(i shr 18 and ones(6) or 0b10_0000_00)
- s[pos+3] = chr(i shr 12 and ones(6) or 0b10_0000_00)
- s[pos+4] = chr(i shr 6 and ones(6) or 0b10_0000_00)
- s[pos+5] = chr(i and ones(6) or 0b10_0000_00)
-
-proc getEscapedChar(L: var Lexer, tok: var Token) =
+proc getEscapedChar(L: var TLexer, tok: var TToken) =
inc(L.bufpos) # skip '\'
case L.buf[L.bufpos]
of 'n', 'N':
- tok.literal.add('\L')
- inc(L.bufpos)
- of 'p', 'P':
- if tok.tokType == tkCharLit:
- lexMessage(L, errGenerated, "\\p not allowed in character literal")
- tok.literal.add(L.config.target.tnl)
+ if tok.tokType == tkCharLit: lexMessage(L, errNnotAllowedInCharacter)
+ add(tok.literal, tnl)
inc(L.bufpos)
of 'r', 'R', 'c', 'C':
- tok.literal.add(CR)
+ add(tok.literal, CR)
inc(L.bufpos)
of 'l', 'L':
- tok.literal.add(LF)
+ add(tok.literal, LF)
inc(L.bufpos)
of 'f', 'F':
- tok.literal.add(FF)
+ add(tok.literal, FF)
inc(L.bufpos)
of 'e', 'E':
- tok.literal.add(ESC)
+ add(tok.literal, ESC)
inc(L.bufpos)
of 'a', 'A':
- tok.literal.add(BEL)
+ add(tok.literal, BEL)
inc(L.bufpos)
of 'b', 'B':
- tok.literal.add(BACKSPACE)
+ add(tok.literal, BACKSPACE)
inc(L.bufpos)
of 'v', 'V':
- tok.literal.add(VT)
+ add(tok.literal, VT)
inc(L.bufpos)
of 't', 'T':
- tok.literal.add('\t')
+ add(tok.literal, '\t')
inc(L.bufpos)
of '\'', '\"':
- tok.literal.add(L.buf[L.bufpos])
+ add(tok.literal, L.buf[L.bufpos])
inc(L.bufpos)
of '\\':
- tok.literal.add('\\')
+ add(tok.literal, '\\')
inc(L.bufpos)
- of 'x', 'X':
+ of 'x', 'X', 'u', 'U':
+ var tp = L.buf[L.bufpos]
inc(L.bufpos)
var xi = 0
- handleHexChar(L, xi, 1)
- handleHexChar(L, xi, 2)
- tok.literal.add(chr(xi))
- of 'u', 'U':
- if tok.tokType == tkCharLit:
- lexMessage(L, errGenerated, "\\u not allowed in character literal")
- inc(L.bufpos)
- var xi = 0
- if L.buf[L.bufpos] == '{':
- inc(L.bufpos)
- var start = L.bufpos
- while L.buf[L.bufpos] != '}':
- handleHexChar(L, xi, 0)
- if start == L.bufpos:
- lexMessage(L, errGenerated,
- "Unicode codepoint cannot be empty")
- inc(L.bufpos)
- if xi > 0x10FFFF:
- let hex = ($L.buf)[start..L.bufpos-2]
- lexMessage(L, errGenerated,
- "Unicode codepoint must be lower than 0x10FFFF, but was: " & hex)
+ handleHexChar(L, xi)
+ handleHexChar(L, xi)
+ if tp in {'u', 'U'}:
+ handleHexChar(L, xi)
+ handleHexChar(L, xi)
+ # inlined toUTF-8 to avoid unicode and strutils dependencies.
+ if xi <=% 127:
+ add(tok.literal, xi.char )
+ elif xi <=% 0x07FF:
+ add(tok.literal, ((xi shr 6) or 0b110_00000).char )
+ add(tok.literal, ((xi and ones(6)) or 0b10_0000_00).char )
+ elif xi <=% 0xFFFF:
+ add(tok.literal, (xi shr 12 or 0b1110_0000).char )
+ add(tok.literal, (xi shr 6 and ones(6) or 0b10_0000_00).char )
+ add(tok.literal, (xi and ones(6) or 0b10_0000_00).char )
+ else: # value is 0xFFFF
+ add(tok.literal, "\xef\xbf\xbf" )
else:
- handleHexChar(L, xi, 1)
- handleHexChar(L, xi, 2)
- handleHexChar(L, xi, 3)
- handleHexChar(L, xi, 4)
- addUnicodeCodePoint(tok.literal, xi)
+ add(tok.literal, chr(xi))
of '0'..'9':
if matchTwoChars(L, '0', {'0'..'9'}):
lexMessage(L, warnOctalEscape)
var xi = 0
handleDecChars(L, xi)
- if (xi <= 255): tok.literal.add(chr(xi))
- else: lexMessage(L, errGenerated, "invalid character constant")
- else: lexMessage(L, errGenerated, "invalid character constant")
+ if (xi <= 255): add(tok.literal, chr(xi))
+ else: lexMessage(L, errInvalidCharacterConstant)
+ else: lexMessage(L, errInvalidCharacterConstant)
-proc handleCRLF(L: var Lexer, pos: int): int =
+proc newString(s: cstring, len: int): string =
+ ## XXX, how come there is no support for this?
+ result = newString(len)
+ for i in 0 .. MaxLineLength:
- lexMessagePos(L, hintLineTooLong, pos)
+ if col > MaxLineLength:
+ lexMessagePos(L, hintLineTooLong, pos)
+
+ if optEmbedOrigSrc in gGlobalOptions:
+ let lineStart = cast[ByteAddress](L.buf) + L.lineStart
+ let line = newString(cast[cstring](lineStart), col)
+ addSourceLine(L.fileIdx, line)
case L.buf[pos]
of CR:
@@ -750,491 +650,322 @@ proc handleCRLF(L: var Lexer, pos: int): int =
result = nimlexbase.handleLF(L, pos)
else: result = pos
-type
- StringMode = enum
- normal,
- raw,
- generalized
-
-proc getString(L: var Lexer, tok: var Token, mode: StringMode) =
- var pos = L.bufpos
+proc getString(L: var TLexer, tok: var TToken, rawMode: bool) =
+ var pos = L.bufpos + 1 # skip "
+ var buf = L.buf # put `buf` in a register
var line = L.lineNumber # save linenumber for better error message
- tokenBegin(tok, pos - ord(mode == raw))
- inc pos # skip "
- if L.buf[pos] == '\"' and L.buf[pos+1] == '\"':
+ if buf[pos] == '\"' and buf[pos+1] == '\"':
tok.tokType = tkTripleStrLit # long string literal:
inc(pos, 2) # skip ""
# skip leading newline:
- if L.buf[pos] in {' ', '\t'}:
+ if buf[pos] in {' ', '\t'}:
var newpos = pos+1
- while L.buf[newpos] in {' ', '\t'}: inc newpos
- if L.buf[newpos] in {CR, LF}: pos = newpos
+ while buf[newpos] in {' ', '\t'}: inc newpos
+ if buf[newpos] in {CR, LF}: pos = newpos
pos = handleCRLF(L, pos)
+ buf = L.buf
while true:
- case L.buf[pos]
+ case buf[pos]
of '\"':
- if L.buf[pos+1] == '\"' and L.buf[pos+2] == '\"' and
- L.buf[pos+3] != '\"':
- tokenEndIgnore(tok, pos+2)
+ if buf[pos+1] == '\"' and buf[pos+2] == '\"' and
+ buf[pos+3] != '\"':
L.bufpos = pos + 3 # skip the three """
break
- tok.literal.add('\"')
+ add(tok.literal, '\"')
inc(pos)
of CR, LF:
- tokenEndIgnore(tok, pos)
pos = handleCRLF(L, pos)
- tok.literal.add("\n")
+ buf = L.buf
+ add(tok.literal, tnl)
of nimlexbase.EndOfFile:
- tokenEndIgnore(tok, pos)
var line2 = L.lineNumber
L.lineNumber = line
- lexMessagePos(L, errGenerated, L.lineStart, "closing \"\"\" expected, but end of file reached")
+ lexMessagePos(L, errClosingTripleQuoteExpected, L.lineStart)
L.lineNumber = line2
L.bufpos = pos
break
else:
- tok.literal.add(L.buf[pos])
+ add(tok.literal, buf[pos])
inc(pos)
else:
# ordinary string literal
- if mode != normal: tok.tokType = tkRStrLit
+ if rawMode: tok.tokType = tkRStrLit
else: tok.tokType = tkStrLit
while true:
- var c = L.buf[pos]
+ var c = buf[pos]
if c == '\"':
- if mode != normal and L.buf[pos+1] == '\"':
+ if rawMode and buf[pos+1] == '\"':
inc(pos, 2)
- tok.literal.add('"')
+ add(tok.literal, '"')
else:
- tokenEndIgnore(tok, pos)
inc(pos) # skip '"'
break
elif c in {CR, LF, nimlexbase.EndOfFile}:
- tokenEndIgnore(tok, pos)
- lexMessage(L, errGenerated, "closing \" expected")
+ lexMessage(L, errClosingQuoteExpected)
break
- elif (c == '\\') and mode == normal:
+ elif (c == '\\') and not rawMode:
L.bufpos = pos
getEscapedChar(L, tok)
pos = L.bufpos
else:
- tok.literal.add(c)
+ add(tok.literal, c)
inc(pos)
L.bufpos = pos
-proc getCharacter(L: var Lexer; tok: var Token) =
- tokenBegin(tok, L.bufpos)
- let startPos = L.bufpos
+proc getCharacter(L: var TLexer, tok: var TToken) =
inc(L.bufpos) # skip '
var c = L.buf[L.bufpos]
case c
- of '\0'..pred(' '), '\'':
- lexMessage(L, errGenerated, "invalid character literal")
- tok.literal = $c
+ of '\0'..pred(' '), '\'': lexMessage(L, errInvalidCharacterConstant)
of '\\': getEscapedChar(L, tok)
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)
+ if L.buf[L.bufpos] != '\'': lexMessage(L, errMissingFinalQuote)
+ inc(L.bufpos) # skip '
-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
-
-proc getSymbol(L: var Lexer, tok: var Token) =
+proc getSymbol(L: var TLexer, tok: var TToken) =
var h: Hash = 0
var pos = L.bufpos
- tokenBegin(tok, pos)
- var suspicious = false
+ var buf = L.buf
while true:
- var c = L.buf[pos]
+ var c = buf[pos]
case c
- of 'a'..'z', '0'..'9':
- h = h !& ord(c)
- inc(pos)
+ of 'a'..'z', '0'..'9', '\x80'..'\xFF':
+ if c == '\226' and
+ buf[pos+1] == '\128' and
+ buf[pos+2] == '\147': # It's a 'magic separator' en-dash Unicode
+ if buf[pos + magicIdentSeparatorRuneByteWidth] notin SymChars:
+ lexMessage(L, errInvalidToken, "–")
+ break
+ inc(pos, magicIdentSeparatorRuneByteWidth)
+ else:
+ h = h !& ord(c)
+ inc(pos)
of 'A'..'Z':
c = chr(ord(c) + (ord('a') - ord('A'))) # toLower()
h = h !& ord(c)
inc(pos)
- suspicious = true
of '_':
- if L.buf[pos+1] notin SymChars:
- lexMessage(L, errGenerated, "invalid token: trailing underscore")
+ if buf[pos+1] notin SymChars:
+ lexMessage(L, errInvalidToken, "_")
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 = getIdent(addr(L.buf[L.bufpos]), pos - L.bufpos, h)
+ L.bufpos = pos
if (tok.ident.id < ord(tokKeywordLow) - ord(tkSymbol)) or
(tok.ident.id > ord(tokKeywordHigh) - ord(tkSymbol)):
tok.tokType = tkSymbol
else:
- tok.tokType = TokType(tok.ident.id + ord(tkSymbol))
- if suspicious and {optStyleHint, optStyleError} * L.config.globalOptions != {}:
- lintReport(L.config, getLineInfo(L), tok.ident.s.normalize, tok.ident.s)
- L.bufpos = pos
+ tok.tokType = TTokType(tok.ident.id + ord(tkSymbol))
-
-proc endOperator(L: var Lexer, tok: var Token, pos: int,
+proc endOperator(L: var TLexer, tok: var TToken, 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 = 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))
+ else: tok.tokType = TTokType(tok.ident.id - oprLow + ord(tkColon))
L.bufpos = pos
-proc getOperator(L: var Lexer, tok: var Token) =
+proc getOperator(L: var TLexer, tok: var TToken) =
var pos = L.bufpos
- tokenBegin(tok, pos)
+ var buf = L.buf
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
-
- template considerAsgn(value: untyped) =
- result = if tok.ident.s[^1] == '=': 1 else: value
-
- case relevantChar
- of '$', '^': considerAsgn(10)
- of '*', '%', '/', '\\': considerAsgn(MulPred)
- of '~': result = 8
- of '+', '-', '|': considerAsgn(PlusPred)
- 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
- of tkIn, tkNotin, tkIs, tkIsnot, tkOf, tkAs, tkFrom: result = 5
- of tkAnd: result = 4
- of tkOr, tkXor, tkPtr, tkRef: result = 3
- else: return -10
-
-proc newlineFollows*(L: Lexer): bool =
- var pos = L.bufpos
- while true:
- case L.buf[pos]
- of ' ', '\t':
- inc(pos)
- of CR, LF:
- result = true
- break
- of '#':
- inc(pos)
- if L.buf[pos] == '#': inc(pos)
- if L.buf[pos] != '[': return true
- else:
- break
-
-proc skipMultiLineComment(L: var Lexer; tok: var Token; start: int;
+proc skipMultiLineComment(L: var TLexer; tok: var TToken; start: int;
isDoc: bool) =
var pos = start
+ var buf = L.buf
var toStrip = 0
- tokenBegin(tok, pos)
# 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 buf[pos] == ' ': inc pos
+ if buf[pos] in {CR, LF}:
pos = handleCRLF(L, pos)
+ buf = L.buf
toStrip = 0
- while L.buf[pos] == ' ':
+ while buf[pos] == ' ':
inc pos
inc toStrip
var nesting = 0
while true:
- case L.buf[pos]
+ case buf[pos]
of '#':
if isDoc:
- if L.buf[pos+1] == '#' and L.buf[pos+2] == '[':
+ if buf[pos+1] == '#' and buf[pos+2] == '[':
inc nesting
tok.literal.add '#'
- elif L.buf[pos+1] == '[':
+ elif buf[pos+1] == '[':
inc nesting
inc pos
of ']':
if isDoc:
- if L.buf[pos+1] == '#' and L.buf[pos+2] == '#':
+ if buf[pos+1] == '#' and buf[pos+2] == '#':
if nesting == 0:
- tokenEndIgnore(tok, pos+2)
inc(pos, 3)
break
dec nesting
tok.literal.add ']'
- elif L.buf[pos+1] == '#':
+ elif buf[pos+1] == '#':
if nesting == 0:
- tokenEndIgnore(tok, pos+1)
inc(pos, 2)
break
dec nesting
inc pos
of CR, LF:
- tokenEndIgnore(tok, pos)
pos = handleCRLF(L, pos)
+ buf = L.buf
# strip leading whitespace:
- when defined(nimpretty): tok.literal.add "\L"
if isDoc:
- when not defined(nimpretty): tok.literal.add "\n"
+ tok.literal.add "\n"
inc tok.iNumber
var c = toStrip
- while L.buf[pos] == ' ' and c > 0:
+ while buf[pos] == ' ' and c > 0:
inc pos
dec c
of nimlexbase.EndOfFile:
- tokenEndIgnore(tok, pos)
lexMessagePos(L, errGenerated, pos, "end of multiline comment expected")
break
else:
- if isDoc or defined(nimpretty): tok.literal.add L.buf[pos]
+ if isDoc: tok.literal.add buf[pos]
inc(pos)
L.bufpos = pos
- when defined(nimpretty):
- tok.commentOffsetB = L.offsetBase + pos - 1
-proc scanComment(L: var Lexer, tok: var Token) =
+proc scanComment(L: var TLexer, tok: var TToken) =
var pos = L.bufpos
+ var buf = L.buf
tok.tokType = tkComment
# iNumber contains the number of '\n' in the token
tok.iNumber = 0
- assert L.buf[pos+1] == '#'
- when defined(nimpretty):
- tok.commentOffsetA = L.offsetBase + pos
-
- if L.buf[pos+2] == '[':
- skipMultiLineComment(L, tok, pos+3, true)
- return
- tokenBegin(tok, pos)
- inc(pos, 2)
+ when not defined(nimfix):
+ assert buf[pos+1] == '#'
+ if buf[pos+2] == '[':
+ skipMultiLineComment(L, tok, pos+3, true)
+ return
+ inc(pos, 2)
var toStrip = 0
- var stripInit = false
+ while buf[pos] == ' ':
+ inc pos
+ inc toStrip
+ when defined(nimfix):
+ var col = getColNumber(L, pos)
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
- tok.literal.add(L.buf[pos])
+ while buf[pos] notin {CR, LF, nimlexbase.EndOfFile}:
+ if buf[pos] == '\\': lastBackslash = pos+1
+ add(tok.literal, buf[pos])
inc(pos)
- tokenEndIgnore(tok, pos)
+ when defined(nimfix):
+ if lastBackslash > 0:
+ # a backslash is a continuation character if only followed by spaces
+ # plus a newline:
+ while buf[lastBackslash] == ' ': inc(lastBackslash)
+ if buf[lastBackslash] notin {CR, LF, nimlexbase.EndOfFile}:
+ # false positive:
+ lastBackslash = -1
+
pos = handleCRLF(L, pos)
+ buf = L.buf
var indent = 0
- while L.buf[pos] == ' ':
+ while buf[pos] == ' ':
inc(pos)
inc(indent)
- if L.buf[pos] == '#' and L.buf[pos+1] == '#':
+ when defined(nimfix):
+ template doContinue(): expr =
+ buf[pos] == '#' and (col == indent or lastBackslash > 0)
+ else:
+ template doContinue(): expr =
+ buf[pos] == '#' and buf[pos+1] == '#'
+ if doContinue():
tok.literal.add "\n"
- inc(pos, 2)
- if stripInit:
+ when defined(nimfix): col = indent
+ else:
+ inc(pos, 2)
var c = toStrip
- while L.buf[pos] == ' ' and c > 0:
+ while buf[pos] == ' ' and c > 0:
inc pos
dec c
- inc tok.iNumber
+ inc tok.iNumber
else:
- if L.buf[pos] > ' ':
+ if buf[pos] > ' ':
L.indentAhead = indent
- tokenEndIgnore(tok, pos)
break
L.bufpos = pos
- when defined(nimpretty):
- tok.commentOffsetB = L.offsetBase + pos - 1
-proc skip(L: var Lexer, tok: var Token) =
+proc skip(L: var TLexer, tok: var TToken) =
var pos = L.bufpos
- tokenBegin(tok, pos)
- tok.strongSpaceA = false
- when defined(nimpretty):
- var hasComment = false
- var commentIndent = L.currLineIndent
- tok.commentOffsetA = L.offsetBase + pos
- tok.commentOffsetB = tok.commentOffsetA
- tok.line = -1
+ var buf = L.buf
+ tok.strongSpaceA = 0
while true:
- case L.buf[pos]
+ case 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")
+ lexMessagePos(L, errTabulatorsAreNotAllowed, pos)
inc(pos)
of CR, LF:
- tokenEndPrevious(tok, pos)
pos = handleCRLF(L, pos)
+ buf = L.buf
var indent = 0
while true:
- if L.buf[pos] == ' ':
+ if buf[pos] == ' ':
inc(pos)
inc(indent)
- elif L.buf[pos] == '#' and L.buf[pos+1] == '[':
- when defined(nimpretty):
- hasComment = true
- if tok.line < 0:
- tok.line = L.lineNumber
- commentIndent = indent
+ elif buf[pos] == '#' and buf[pos+1] == '[':
skipMultiLineComment(L, tok, pos+2, false)
pos = L.bufpos
+ buf = L.buf
else:
break
- tok.strongSpaceA = false
- 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] == '#'):
+ tok.strongSpaceA = 0
+ when defined(nimfix):
+ template doBreak(): expr = buf[pos] > ' '
+ else:
+ template doBreak(): expr =
+ buf[pos] > ' ' and (buf[pos] != '#' or buf[pos+1] == '#')
+ if doBreak():
tok.indent = indent
L.currLineIndent = indent
break
of '#':
- # do not skip documentation comment:
- if L.buf[pos+1] == '#': break
- when defined(nimpretty):
- hasComment = true
- if tok.line < 0:
- tok.line = L.lineNumber
-
- if L.buf[pos+1] == '[':
- skipMultiLineComment(L, tok, pos+2, false)
- pos = L.bufpos
+ when defined(nimfix):
+ break
else:
- tokenBegin(tok, pos)
- while L.buf[pos] notin {CR, LF, nimlexbase.EndOfFile}:
- when defined(nimpretty): tok.literal.add L.buf[pos]
- inc(pos)
- tokenEndIgnore(tok, pos+1)
- when defined(nimpretty):
- tok.commentOffsetB = L.offsetBase + pos + 1
+ # do not skip documentation comment:
+ if buf[pos+1] == '#': break
+ if buf[pos+1] == '[':
+ skipMultiLineComment(L, tok, pos+2, false)
+ pos = L.bufpos
+ buf = L.buf
+ else:
+ while buf[pos] notin {CR, LF, nimlexbase.EndOfFile}: inc(pos)
else:
break # EndOfFile also leaves the loop
- tokenEndPrevious(tok, pos-1)
L.bufpos = pos
- when defined(nimpretty):
- if hasComment:
- tok.commentOffsetB = L.offsetBase + pos - 1
- tok.tokType = tkComment
- tok.indent = commentIndent
-
-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
- L.previousToken.col = tok.col.int16
+proc rawGetTok*(L: var TLexer, tok: var TToken) =
fillToken(tok)
if L.indentAhead >= 0:
tok.indent = L.indentAhead
@@ -1243,22 +974,13 @@ proc rawGetTok*(L: var Lexer, tok: var Token) =
else:
tok.indent = -1
skip(L, tok)
- when defined(nimpretty):
- if tok.tokType == tkComment:
- L.indentAhead = L.currLineIndent
- return
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', 'l'}:
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 '*':
@@ -1272,10 +994,16 @@ proc rawGetTok*(L: var Lexer, tok: var Token) =
of ',':
tok.tokType = tkComma
inc(L.bufpos)
+ of 'l':
+ # if we parsed exactly one character and its a small L (l), this
+ # is treated as a warning because it may be confused with the number 1
+ if L.buf[L.bufpos+1] notin (SymChars + {'_'}):
+ lexMessage(L, warnSmallLshouldNotBeUsed)
+ getSymbol(L, tok)
of 'r', 'R':
if L.buf[L.bufpos + 1] == '\"':
inc(L.bufpos)
- getString(L, tok, raw)
+ getString(L, tok, true)
else:
getSymbol(L, tok)
of '(':
@@ -1285,10 +1013,6 @@ proc rawGetTok*(L: var Lexer, tok: var Token) =
inc(L.bufpos)
else:
tok.tokType = tkParLe
- when defined(nimsuggest):
- if L.fileIdx == L.config.m.trackPos.fileIndex and tok.col < L.config.m.trackPos.col and
- tok.line == L.config.m.trackPos.line.int and L.config.ideCmd == ideCon:
- L.config.m.trackPos.col = tok.col.int16
of ')':
tok.tokType = tkParRi
inc(L.bufpos)
@@ -1297,23 +1021,12 @@ proc rawGetTok*(L: var Lexer, tok: var Token) =
if L.buf[L.bufpos] == '.' and L.buf[L.bufpos+1] != '.':
tok.tokType = tkBracketDotLe
inc(L.bufpos)
- elif L.buf[L.bufpos] == ':':
- tok.tokType = tkBracketLeColon
- inc(L.bufpos)
else:
tok.tokType = tkBracketLe
of ']':
tok.tokType = tkBracketRi
inc(L.bufpos)
of '.':
- when defined(nimsuggest):
- if L.fileIdx == L.config.m.trackPos.fileIndex and tok.col+1 == L.config.m.trackPos.col and
- tok.line == L.config.m.trackPos.line.int and L.config.ideCmd == ideSug:
- tok.tokType = tkDot
- L.config.m.trackPos.col = tok.col.int16
- inc(L.bufpos)
- atTokenEnd()
- return
if L.buf[L.bufpos+1] == ']':
tok.tokType = tkBracketDotRi
inc(L.bufpos, 2)
@@ -1343,18 +1056,18 @@ proc rawGetTok*(L: var Lexer, tok: var Token) =
inc(L.bufpos)
of '_':
inc(L.bufpos)
- if L.buf[L.bufpos] notin SymChars+{'_'}:
+ if L.buf[L.bufpos] notin SymChars:
tok.tokType = tkSymbol
- tok.ident = L.cache.getIdent("_")
+ tok.ident = getIdent("_")
else:
tok.literal = $c
tok.tokType = tkInvalid
- lexMessage(L, errGenerated, "invalid token: " & c & " (\\" & $(ord(c)) & ')')
+ lexMessage(L, errInvalidToken, c & " (\\" & $(ord(c)) & ')')
of '\"':
- # check for generalized raw string literal:
- let mode = if L.bufpos > 0 and L.buf[L.bufpos-1] in SymChars: generalized else: normal
- getString(L, tok, mode)
- if mode == generalized:
+ # check for extended raw string literal:
+ var rawMode = L.bufpos > 0 and L.buf[L.bufpos-1] in SymChars
+ getString(L, tok, rawMode)
+ if rawMode:
# tkRStrLit -> tkGStrLit
# tkTripleStrLit -> tkGTripleStrLit
inc(tok.tokType, 2)
@@ -1364,30 +1077,6 @@ proc rawGetTok*(L: var Lexer, tok: var Token) =
tok.tokType = tkCharLit
of '0'..'9':
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)
else:
if c in OpChars:
getOperator(L, tok)
@@ -1397,32 +1086,7 @@ proc rawGetTok*(L: var Lexer, tok: var Token) =
else:
tok.literal = $c
tok.tokType = tkInvalid
- lexMessage(L, errGenerated, "invalid token: " & c & " (\\" & $(ord(c)) & ')')
+ lexMessage(L, errInvalidToken, c & " (\\" & $(ord(c)) & ')')
inc(L.bufpos)
- atTokenEnd()
-proc getIndentWidth*(fileIdx: FileIndex, inputstream: PLLStream;
- cache: IdentCache; config: ConfigRef): int =
- var lex: Lexer
- var tok: Token
- initToken(tok)
- openLexer(lex, fileIdx, inputstream, cache, config)
- var prevToken = tkEof
- while tok.tokType != tkEof:
- rawGetTok(lex, tok)
- if tok.indent > 0 and prevToken in {tkColon, tkEquals, tkType, tkConst, tkLet, tkVar, tkUsing}:
- result = tok.indent
- if result > 0: break
- prevToken = tok.tokType
- closeLexer(lex)
-
-proc getPrecedence*(ident: PIdent): int =
- ## assumes ident is binary operator already
- var tok: Token
- initToken(tok)
- tok.ident = ident
- tok.tokType =
- if tok.ident.id in ord(tokKeywordLow) - ord(tkSymbol)..ord(tokKeywordHigh) - ord(tkSymbol):
- TokType(tok.ident.id + ord(tkSymbol))
- else: tkOpr
- getPrecedence(tok)
+dummyIdent = getIdent("")
diff --git a/compiler/liftdestructors.nim b/compiler/liftdestructors.nim
deleted file mode 100644
index 467f015d8..000000000
--- a/compiler/liftdestructors.nim
+++ /dev/null
@@ -1,1151 +0,0 @@
-#
-#
-# The Nim Compiler
-# (c) Copyright 2015 Andreas Rumpf
-#
-# See the file "copying.txt", included in this
-# distribution, for details about the copyright.
-#
-
-## This module implements lifting for type-bound operations
-## (``=sink``, ``=copy``, ``=destroy``, ``=deepCopy``).
-
-import modulegraphs, lineinfos, idents, ast, renderer, semdata,
- sighashes, lowerings, options, types, msgs, magicsys, tables, ccgutils
-
-from trees import isCaseObj
-
-when defined(nimPreviewSlimSystem):
- import std/assertions
-
-type
- TLiftCtx = object
- g: ModuleGraph
- info: TLineInfo # for construction
- kind: TTypeAttachedOp
- fn: PSym
- asgnForType: PType
- recurse: bool
- addMemReset: bool # add wasMoved() call after destructor call
- canRaise: bool
- filterDiscriminator: PSym # we generating destructor for case branch
- 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
-
-proc createTypeBoundOps*(g: ModuleGraph; c: PContext; orig: PType; info: TLineInfo;
- idgen: IdGenerator)
-
-proc at(a, i: PNode, elemType: PType): PNode =
- result = newNodeI(nkBracketExpr, a.info, 2)
- result[0] = a
- 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 fillBodyTup(c: var TLiftCtx; t: PType; body, x, y: PNode) =
- for i in 0.. 0 and t[0] != nil:
- fillBody(c, skipTypes(t[0], abstractPtrs), body, x, y)
- fillBodyObj(c, t.n, body, x, y, enforceDefaultOp = false)
-
-proc fillBodyObjT(c: var TLiftCtx; t: PType, body, x, y: PNode) =
- var hasCase = isCaseObj(t.n)
- var obj = t
- while obj.len > 0 and obj[0] != nil:
- obj = skipTypes(obj[0], abstractPtrs)
- hasCase = hasCase or isCaseObj(obj.n)
-
- if hasCase and c.kind in {attachedAsgn, attachedDeepCopy}:
- # assignment for case objects is complex, we do:
- # =destroy(dest)
- # wasMoved(dest)
- # for every field:
- # `=` dest.field, src.field
- # ^ this is what we used to do, but for 'result = result.sons[0]' it
- # destroys 'result' too early.
- # So this is what we really need to do:
- # let blob {.cursor.} = dest # remembers the old dest.kind
- # wasMoved(dest)
- # dest.kind = src.kind
- # for every field (dependent on dest.kind):
- # `=` dest.field, src.field
- # =destroy(blob)
- var dummy = newSym(skTemp, getIdent(c.g.cache, lowerings.genPrefix), nextSymId c.idgen, c.fn, c.info)
- dummy.typ = y.typ
- if ccgIntroducedPtr(c.g.config, dummy, y.typ):
- # Because of potential aliasing when the src param is passed by ref, we need to check for equality here,
- # because the wasMoved(dest) call would zero out src, if dest aliases src.
- var cond = newTree(nkCall, newSymNode(c.g.getSysMagic(c.info, "==", mEqRef)),
- newTreeIT(nkAddr, c.info, makePtrType(c.fn, x.typ, c.idgen), x), newTreeIT(nkAddr, c.info, makePtrType(c.fn, y.typ, c.idgen), y))
- cond.typ = getSysType(c.g, x.info, tyBool)
- body.add genIf(c, cond, newTreeI(nkReturnStmt, c.info, newNodeI(nkEmpty, c.info)))
- var temp = newSym(skTemp, getIdent(c.g.cache, lowerings.genPrefix), nextSymId c.idgen, c.fn, c.info)
- temp.typ = x.typ
- incl(temp.flags, sfFromGeneric)
- var v = newNodeI(nkVarSection, c.info)
- let blob = newSymNode(temp)
- v.addVar(blob, x)
- body.add v
- #body.add newAsgnStmt(blob, x)
-
- var wasMovedCall = newNodeI(nkCall, c.info)
- wasMovedCall.add(newSymNode(createMagic(c.g, c.idgen, "wasMoved", mWasMoved)))
- wasMovedCall.add x # mWasMoved does not take the address
- body.add wasMovedCall
-
- fillBodyObjTImpl(c, t, body, x, y)
- when false:
- # does not work yet due to phase-ordering problems:
- assert t.destructor != nil
- body.add destructorCall(c.g, t.destructor, blob)
- let prevKind = c.kind
- c.kind = attachedDestructor
- fillBodyObjTImpl(c, t, body, blob, y)
- c.kind = prevKind
- else:
- fillBodyObjTImpl(c, t, body, x, y)
-
-proc boolLit*(g: ModuleGraph; info: TLineInfo; value: bool): PNode =
- result = newIntLit(g, info, ord value)
- result.typ = getSysType(g, info, tyBool)
-
-proc getCycleParam(c: TLiftCtx): PNode =
- assert c.kind == attachedAsgn
- if c.fn.typ.len == 4:
- result = c.fn.typ.n.lastSon
- assert result.kind == nkSym
- assert result.sym.name.s == "cyclic"
- else:
- result = boolLit(c.g, c.info, true)
-
-proc newHookCall(c: var TLiftCtx; op: PSym; x, y: PNode): PNode =
- #if sfError in op.flags:
- # localError(c.config, x.info, "usage of '$1' is a user-defined error" % op.name.s)
- result = newNodeI(nkCall, x.info)
- result.add newSymNode(op)
- if sfNeverRaises notin op.flags:
- c.canRaise = true
- if op.typ.sons[1].kind == tyVar:
- result.add genAddr(c, x)
- else:
- result.add x
- if y != nil:
- result.add y
- if op.typ.len == 4:
- assert y != nil
- if c.fn.typ.len == 4:
- result.add getCycleParam(c)
- else:
- # assume the worst: A cycle is created:
- result.add boolLit(c.g, y.info, true)
-
-proc newOpCall(c: var TLiftCtx; op: PSym; x: PNode): PNode =
- result = newNodeIT(nkCall, x.info, op.typ[0])
- result.add(newSymNode(op))
- result.add x
- if sfNeverRaises notin op.flags:
- c.canRaise = true
-
-proc newDeepCopyCall(c: var TLiftCtx; op: PSym; x, y: PNode): PNode =
- result = newAsgnStmt(x, newOpCall(c, op, y))
-
-proc usesBuiltinArc(t: PType): bool =
- proc wrap(t: PType): bool {.nimcall.} = ast.isGCedMem(t)
- result = types.searchTypeFor(t, wrap)
-
-proc useNoGc(c: TLiftCtx; t: PType): bool {.inline.} =
- result = optSeqDestructors in c.g.config.globalOptions and
- ({tfHasGCedMem, tfHasOwned} * t.flags != {} or usesBuiltinArc(t))
-
-proc requiresDestructor(c: TLiftCtx; t: PType): bool {.inline.} =
- result = optSeqDestructors in c.g.config.globalOptions and
- containsGarbageCollectedRef(t)
-
-proc instantiateGeneric(c: var TLiftCtx; op: PSym; t, typeInst: PType): PSym =
- if c.c != nil and typeInst != nil:
- result = c.c.instTypeBoundOp(c.c, op, typeInst, c.info, attachedAsgn, 1)
- else:
- localError(c.g.config, c.info,
- "cannot generate destructor for generic type: " & typeToString(t))
- result = nil
-
-proc considerAsgnOrSink(c: var TLiftCtx; t: PType; body, x, y: PNode;
- field: var PSym): bool =
- if optSeqDestructors in c.g.config.globalOptions:
- var op = field
- let destructorOverriden = destructorOverriden(c.g, t)
- if op != nil and op != c.fn and
- (sfOverriden in op.flags or destructorOverriden):
- if sfError in op.flags:
- incl c.fn.flags, sfError
- #else:
- # markUsed(c.g.config, c.info, op, c.g.usageSym)
- onUse(c.info, op)
- body.add newHookCall(c, op, x, y)
- result = true
- elif op == nil and destructorOverriden:
- op = produceSym(c.g, c.c, t, c.kind, c.info, c.idgen)
- body.add newHookCall(c, op, x, y)
- result = true
- elif tfHasAsgn in t.flags:
- var op: PSym
- if sameType(t, c.asgnForType):
- # generate recursive call:
- if c.recurse:
- op = c.fn
- else:
- c.recurse = true
- return false
- else:
- op = field
- if op == nil:
- op = produceSym(c.g, c.c, t, c.kind, c.info, c.idgen)
- if sfError in op.flags:
- incl c.fn.flags, sfError
- #else:
- # markUsed(c.g.config, c.info, op, c.g.usageSym)
- onUse(c.info, op)
- # We also now do generic instantiations in the destructor lifting pass:
- if op.ast.isGenericRoutine:
- op = instantiateGeneric(c, op, t, t.typeInst)
- field = op
- #echo "trying to use ", op.ast
- #echo "for ", op.name.s, " "
- #debug(t)
- #return false
- assert op.ast[genericParamsPos].kind == nkEmpty
- body.add newHookCall(c, op, x, y)
- result = true
-
-proc addDestructorCall(c: var TLiftCtx; orig: PType; body, x: PNode) =
- let t = orig.skipTypes(abstractInst - {tyDistinct})
- var op = t.destructor
-
- if op != nil and sfOverriden in op.flags:
- if op.ast.isGenericRoutine:
- # patch generic destructor:
- op = instantiateGeneric(c, op, t, t.typeInst)
- setAttachedOp(c.g, c.idgen.module, t, attachedDestructor, op)
-
- if op == nil and (useNoGc(c, t) or requiresDestructor(c, t)):
- op = produceSym(c.g, c.c, t, attachedDestructor, c.info, c.idgen)
- doAssert op != nil
- doAssert op == t.destructor
-
- if op != nil:
- #markUsed(c.g.config, c.info, op, c.g.usageSym)
- onUse(c.info, op)
- body.add destructorCall(c, op, x)
- elif useNoGc(c, t):
- internalError(c.g.config, c.info,
- "type-bound operator could not be resolved")
-
-proc considerUserDefinedOp(c: var TLiftCtx; t: PType; body, x, y: PNode): bool =
- case c.kind
- of attachedDestructor:
- var op = t.destructor
- if op != nil and sfOverriden in op.flags:
-
- if op.ast.isGenericRoutine:
- # patch generic destructor:
- op = instantiateGeneric(c, op, t, t.typeInst)
- setAttachedOp(c.g, c.idgen.module, t, attachedDestructor, op)
-
- #markUsed(c.g.config, c.info, op, c.g.usageSym)
- onUse(c.info, op)
- body.add destructorCall(c, op, x)
- result = true
- #result = addDestructorCall(c, t, body, x)
- of attachedAsgn, attachedSink, attachedTrace:
- var op = getAttachedOp(c.g, t, c.kind)
- if op != nil and sfOverriden in op.flags:
- if op.ast.isGenericRoutine:
- # patch generic =trace:
- op = instantiateGeneric(c, op, t, t.typeInst)
- setAttachedOp(c.g, c.idgen.module, t, c.kind, op)
-
- result = considerAsgnOrSink(c, t, body, x, y, op)
- if op != nil:
- setAttachedOp(c.g, c.idgen.module, t, c.kind, op)
-
- of attachedDeepCopy:
- let op = getAttachedOp(c.g, t, attachedDeepCopy)
- if op != nil:
- #markUsed(c.g.config, c.info, op, c.g.usageSym)
- onUse(c.info, op)
- body.add newDeepCopyCall(c, op, x, y)
- result = true
-
- of attachedWasMoved:
- var op = getAttachedOp(c.g, t, attachedWasMoved)
- if op != nil and sfOverriden in op.flags:
-
- if op.ast.isGenericRoutine:
- # patch generic destructor:
- op = instantiateGeneric(c, op, t, t.typeInst)
- setAttachedOp(c.g, c.idgen.module, t, attachedWasMoved, op)
-
- #markUsed(c.g.config, c.info, op, c.g.usageSym)
- onUse(c.info, op)
- body.add genWasMovedCall(c, op, x)
- result = true
-
-proc declareCounter(c: var TLiftCtx; body: PNode; first: BiggestInt): PNode =
- var temp = newSym(skTemp, getIdent(c.g.cache, lowerings.genPrefix), nextSymId(c.idgen), c.fn, c.info)
- temp.typ = getSysType(c.g, body.info, tyInt)
- incl(temp.flags, sfFromGeneric)
-
- var v = newNodeI(nkVarSection, c.info)
- result = newSymNode(temp)
- v.addVar(result, lowerings.newIntLit(c.g, body.info, first))
- body.add v
-
-proc declareTempOf(c: var TLiftCtx; body: PNode; value: PNode): PNode =
- var temp = newSym(skTemp, getIdent(c.g.cache, lowerings.genPrefix), nextSymId(c.idgen), c.fn, c.info)
- temp.typ = value.typ
- incl(temp.flags, sfFromGeneric)
-
- var v = newNodeI(nkVarSection, c.info)
- result = newSymNode(temp)
- v.addVar(result, value)
- body.add v
-
-proc addIncStmt(c: var TLiftCtx; body, i: PNode) =
- let incCall = genBuiltin(c, mInc, "inc", i)
- incCall.add lowerings.newIntLit(c.g, c.info, 1)
- body.add incCall
-
-proc newSeqCall(c: var TLiftCtx; x, y: PNode): PNode =
- # don't call genAddr(c, x) here:
- result = genBuiltin(c, mNewSeq, "newSeq", x)
- let lenCall = genBuiltin(c, mLengthSeq, "len", y)
- lenCall.typ = getSysType(c.g, x.info, tyInt)
- result.add lenCall
-
-proc setLenStrCall(c: var TLiftCtx; x, y: PNode): PNode =
- let lenCall = genBuiltin(c, mLengthStr, "len", y)
- lenCall.typ = getSysType(c.g, x.info, tyInt)
- result = genBuiltin(c, mSetLengthStr, "setLen", x) # genAddr(g, x))
- result.add lenCall
-
-proc setLenSeqCall(c: var TLiftCtx; t: PType; x, y: PNode): PNode =
- let lenCall = genBuiltin(c, mLengthSeq, "len", y)
- lenCall.typ = getSysType(c.g, x.info, tyInt)
- var op = getSysMagic(c.g, x.info, "setLen", mSetLengthSeq)
- op = instantiateGeneric(c, op, t, t)
- result = newTree(nkCall, newSymNode(op, x.info), x, lenCall)
-
-proc forallElements(c: var TLiftCtx; t: PType; body, x, y: PNode) =
- let counterIdx = body.len
- let i = declareCounter(c, body, toInt64(firstOrd(c.g.config, t)))
- let whileLoop = genWhileLoop(c, i, x)
- let elemType = t.lastSon
- let b = if c.kind == attachedTrace: y else: y.at(i, elemType)
- fillBody(c, elemType, whileLoop[1], x.at(i, elemType), b)
- if whileLoop[1].len > 0:
- addIncStmt(c, whileLoop[1], i)
- body.add whileLoop
- else:
- body.sons.setLen counterIdx
-
-proc fillSeqOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
- case c.kind
- of attachedAsgn, attachedDeepCopy:
- # we generate:
- # setLen(dest, y.len)
- # var i = 0
- # while i < y.len: dest[i] = y[i]; inc(i)
- # This is usually more efficient than a destroy/create pair.
- body.add setLenSeqCall(c, t, x, y)
- forallElements(c, t, body, x, y)
- of attachedSink:
- let moveCall = genBuiltin(c, mMove, "move", x)
- moveCall.add y
- doAssert t.destructor != nil
- moveCall.add destructorCall(c, t.destructor, x)
- body.add moveCall
- of attachedDestructor:
- # destroy all elements:
- forallElements(c, t, body, x, y)
- body.add genBuiltin(c, 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)
-
-proc useSeqOrStrOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
- createTypeBoundOps(c.g, c.c, t, body.info, c.idgen)
- # recursions are tricky, so we might need to forward the generated
- # 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 canon = c.g.canonTypes.getOrDefault(h)
- if canon != nil: t = canon
-
- case c.kind
- of attachedAsgn, attachedDeepCopy:
- # XXX: replace these with assertions.
- if t.assignment == nil:
- return # protect from recursion
- 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)
- moveCall.add y
- doAssert t.destructor != nil
- moveCall.add destructorCall(c, t.destructor, x)
- body.add moveCall
- # alternatively we could do this:
- when false:
- doAssert t.asink != nil
- body.add newHookCall(c, t.asink, x, y)
- of attachedDestructor:
- 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)
-
-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)
- 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 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
- of tyRef: result = types.canFormAcycle(t.lastSon)
- of tyProc: result = t.callConv == ccClosure
- else: result = false
-
-proc atomicRefOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
- #[ bug #15753 is really subtle. Usually the classical write barrier for reference
- counting looks like this::
-
- incRef source # increment first; this takes care of self-assignments1
- decRef dest
- dest[] = source
-
- However, 'decRef dest' might trigger a cycle collection and then the collector
- traverses the graph. It is crucial that when it follows the pointers the assignment
- 'dest[] = source' already happened so that we don't do trial deletion on a wrong
- graph -- this causes premature freeing of objects! The correct barrier looks like
- this::
-
- let tmp = dest
- incRef source
- dest[] = source
- decRef tmp
-
- ]#
- var actions = newNodeI(nkStmtList, c.info)
- let elemType = t.lastSon
-
- 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)
- else:
- x
-
- if isFinal(elemType):
- addDestructorCall(c, elemType, actions, genDeref(tmp, nkDerefExpr))
- var alignOf = genBuiltin(c, 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:
- addDestructorCall(c, elemType, newNodeI(nkStmtList, c.info), genDeref(tmp, nkDerefExpr))
- actions.add callCodegenProc(c.g, "nimDestroyAndDispose", c.info, tmp)
-
- var cond: PNode
- if isCyclic:
- if isFinal(elemType):
- let typInfo = genBuiltin(c, 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)
-
- case c.kind
- of attachedSink:
- if isCyclic:
- body.add newAsgnStmt(x, y)
- body.add genIf(c, cond, actions)
- else:
- body.add genIf(c, cond, actions)
- body.add newAsgnStmt(x, y)
- of attachedAsgn:
- if isCyclic:
- body.add genIf(c, y, callCodegenProc(c.g,
- "nimIncRefCyclic", c.info, y, getCycleParam(c)))
- body.add newAsgnStmt(x, y)
- body.add genIf(c, cond, actions)
- else:
- body.add genIf(c, y, callCodegenProc(c.g, "nimIncRef", c.info, y))
- body.add genIf(c, cond, actions)
- body.add newAsgnStmt(x, y)
- of attachedDestructor:
- 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)
-
-
-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)
- xenv.typ = getSysType(c.g, c.info, tyPointer)
-
- let isCyclic = c.g.config.selectedGC == gcOrc
- let tmp =
- if isCyclic and c.kind in {attachedAsgn, attachedSink}:
- declareTempOf(c, body, xenv)
- else:
- xenv
-
- var actions = newNodeI(nkStmtList, c.info)
- actions.add callCodegenProc(c.g, "nimDestroyAndDispose", c.info, tmp)
-
- let decRefProc =
- if isCyclic: "nimDecRefIsLastCyclicDyn"
- else: "nimDecRefIsLast"
- let cond = callCodegenProc(c.g, decRefProc, c.info, tmp)
- cond.typ = getSysType(c.g, x.info, tyBool)
-
- case c.kind
- of attachedSink:
- if isCyclic:
- body.add newAsgnStmt(x, y)
- body.add genIf(c, cond, actions)
- else:
- body.add genIf(c, cond, actions)
- body.add newAsgnStmt(x, y)
- of attachedAsgn:
- let yenv = genBuiltin(c, 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)))
- body.add newAsgnStmt(x, y)
- body.add genIf(c, cond, actions)
- else:
- body.add genIf(c, yenv, callCodegenProc(c.g, "nimIncRef", c.info, yenv))
-
- body.add genIf(c, cond, actions)
- body.add newAsgnStmt(x, y)
- of attachedDestructor:
- body.add genIf(c, cond, actions)
- 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)
-
-proc weakrefOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
- case c.kind
- of attachedSink:
- # we 'nil' y out afterwards so we *need* to take over its reference
- # count value:
- body.add genIf(c, x, callCodegenProc(c.g, "nimDecWeakRef", c.info, x))
- body.add newAsgnStmt(x, y)
- of attachedAsgn:
- body.add genIf(c, y, callCodegenProc(c.g, "nimIncRef", c.info, y))
- body.add genIf(c, x, callCodegenProc(c.g, "nimDecWeakRef", c.info, x))
- body.add newAsgnStmt(x, y)
- of attachedDestructor:
- # it's better to prepend the destruction of weak refs in order to
- # prevent wrong "dangling refs exist" problems:
- var actions = newNodeI(nkStmtList, c.info)
- actions.add callCodegenProc(c.g, "nimDecWeakRef", c.info, x)
- let des = genIf(c, x, actions)
- if body.len == 0:
- body.add des
- else:
- body.sons.insert(des, 0)
- of attachedDeepCopy: assert(false, "cannot happen")
- of attachedTrace: discard
- of attachedWasMoved: body.add genBuiltin(c, mWasMoved, "wasMoved", x)
-
-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)
-
- if isFinal(elemType):
- addDestructorCall(c, elemType, actions, genDeref(x, nkDerefExpr))
- var alignOf = genBuiltin(c, 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:
- addDestructorCall(c, elemType, newNodeI(nkStmtList, c.info), genDeref(x, nkDerefExpr))
- actions.add callCodegenProc(c.g, "nimDestroyAndDispose", c.info, x)
-
- case c.kind
- of attachedSink, attachedAsgn:
- body.add genIf(c, x, actions)
- body.add newAsgnStmt(x, y)
- 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)
-
-proc closureOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
- if c.kind == attachedDeepCopy:
- # a big problem is that we don't know the environment's type here, so we
- # 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[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)
- xx.typ = getSysType(c.g, c.info, tyPointer)
- case c.kind
- of attachedSink:
- # we 'nil' y out afterwards so we *need* to take over its reference
- # count value:
- 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)
- 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))
- body.add newAsgnStmt(x, y)
- of attachedDestructor:
- let des = genIf(c, xx, callCodegenProc(c.g, "nimDecWeakRef", c.info, xx))
- if body.len == 0:
- body.add des
- else:
- body.sons.insert(des, 0)
- of attachedDeepCopy: assert(false, "cannot happen")
- of attachedTrace: discard
- of attachedWasMoved: body.add genBuiltin(c, mWasMoved, "wasMoved", x)
-
-proc ownedClosureOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
- let xx = genBuiltin(c, 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))
- actions.add callCodegenProc(c.g, "nimDestroyAndDispose", c.info, xx)
- case c.kind
- of attachedSink, attachedAsgn:
- body.add genIf(c, xx, actions)
- body.add newAsgnStmt(x, y)
- 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)
-
-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,
- tyPtr, tyUncheckedArray, tyVar, tyLent:
- defaultOp(c, t, body, x, y)
- of tyRef:
- if c.g.config.selectedGC in {gcArc, gcOrc}:
- atomicRefOp(c, t, body, x, y)
- elif (optOwnedRefs in c.g.config.globalOptions and
- optRefCheck in c.g.config.options):
- weakrefOp(c, t, body, x, y)
- else:
- defaultOp(c, t, body, x, y)
- of tyProc:
- if t.callConv == ccClosure:
- if c.g.config.selectedGC in {gcArc, gcOrc}:
- atomicClosureOp(c, t, body, x, y)
- else:
- closureOp(c, t, body, x, y)
- else:
- defaultOp(c, t, body, x, y)
- of tyOwned:
- let base = t.skipTypes(abstractInstOwned)
- if optOwnedRefs in c.g.config.globalOptions:
- case base.kind
- of tyRef:
- ownedRefOp(c, base, body, x, y)
- return
- of tyProc:
- if base.callConv == ccClosure:
- ownedClosureOp(c, base, body, x, y)
- return
- else: discard
- defaultOp(c, base, body, x, y)
- of tyArray:
- if tfHasAsgn in t.flags or useNoGc(c, t):
- forallElements(c, t, body, x, y)
- else:
- defaultOp(c, t, body, x, y)
- of tySequence:
- if useNoGc(c, t):
- useSeqOrStrOp(c, t, body, x, y)
- elif optSeqDestructors in c.g.config.globalOptions:
- # note that tfHasAsgn is propagated so we need the check on
- # 'selectedGC' here to determine if we have the new runtime.
- 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)
- forallElements(c, t, body, x, y)
- else:
- defaultOp(c, t, body, x, y)
- of tyString:
- if useNoGc(c, t):
- useSeqOrStrOp(c, t, body, x, y)
- elif tfHasAsgn in t.flags:
- discard considerUserDefinedOp(c, t, body, x, y)
- else:
- defaultOp(c, t, body, x, y)
- of tyObject:
- if not considerUserDefinedOp(c, t, body, x, y):
- if c.kind in {attachedAsgn, attachedSink} and t.sym != nil and sfImportc in t.sym.flags:
- body.add newAsgnStmt(x, y)
- else:
- fillBodyObjT(c, t, body, x, y)
- of tyDistinct:
- if not considerUserDefinedOp(c, t, body, x, y):
- fillBody(c, t[0], body, x, y)
- of tyTuple:
- fillBodyTup(c, t, body, x, y)
- of tyVarargs, tyOpenArray:
- if c.kind == attachedDestructor and (tfHasAsgn in t.flags or useNoGc(c, t)):
- forallElements(c, t, body, x, y)
- else:
- discard "cannot copy openArray"
-
- of tyFromExpr, tyProxy, tyBuiltInTypeClass, tyUserTypeClass,
- tyUserTypeClassInst, tyCompositeTypeClass, tyAnd, tyOr, tyNot, tyAnything,
- tyGenericParam, tyGenericBody, tyNil, tyUntyped, tyTyped,
- tyTypeDesc, tyGenericInvocation, tyForward, tyStatic:
- #internalError(c.g.config, c.info, "assignment requested for type: " & typeToString(t))
- discard
- of tyOrdinal, tyRange, tyInferred,
- tyGenericInst, tyAlias, tySink:
- fillBody(c, lastSon(t), body, x, y)
- of tyConcept, tyIterable: 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:
- discard produceSym(g, c, baseType, kind, info, idgen)
- result = getAttachedOp(g, baseType, kind)
- setAttachedOp(g, idgen.module, typ, kind, result)
-
-proc symPrototype(g: ModuleGraph; typ: PType; owner: PSym; kind: TTypeAttachedOp;
- info: TLineInfo; idgen: IdGenerator): PSym =
-
- let procname = getIdent(g.cache, AttachedOpToStr[kind])
- result = newSym(skProc, procname, nextSymId(idgen), owner, info)
- let dest = newSym(skParam, getIdent(g.cache, "dest"), nextSymId(idgen), result, info)
- let src = newSym(skParam, getIdent(g.cache, if kind == attachedTrace: "env" else: "src"),
- nextSymId(idgen), result, info)
- dest.typ = makeVarType(typ.owner, typ, idgen)
- if kind == attachedTrace:
- src.typ = getSysType(g, info, tyPointer)
- else:
- src.typ = typ
-
- result.typ = newProcType(info, nextTypeId(idgen), owner)
- result.typ.addParam dest
- if kind notin {attachedDestructor, attachedWasMoved}:
- result.typ.addParam src
-
- if kind == attachedAsgn and g.config.selectedGC == gcOrc and
- cyclicType(typ.skipTypes(abstractInst)):
- let cycleParam = newSym(skParam, getIdent(g.cache, "cyclic"),
- nextSymId(idgen), result, info)
- cycleParam.typ = getSysType(g, info, tyBool)
- result.typ.addParam cycleParam
-
- var n = newNodeI(nkProcDef, info, bodyPos+1)
- for i in 0.. mDestroy
- # 3. we have a lifted destructor.
- # 4. We have a custom destructor.
- # 5. We have a (custom) generic destructor.
-
- # we do not generate '=trace' procs if we
- # have the cycle detection disabled, saves code size.
- let lastAttached = if g.config.selectedGC == gcOrc: attachedTrace
- else: attachedSink
-
- # bug #15122: We need to produce all prototypes before entering the
- # mind boggling recursion. Hacks like these imply we should rewrite
- # this module.
- var generics: array[attachedWasMoved..attachedTrace, bool]
- for k in attachedWasMoved..lastAttached:
- generics[k] = getAttachedOp(g, canon, k) != nil
- if not generics[k]:
- setAttachedOp(g, idgen.module, canon, 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:
- if not generics[k]:
- discard produceSym(g, c, canon, k, info, idgen)
- else:
- inst(g, c, canon, k, idgen, info)
- if canon != orig:
- setAttachedOp(g, idgen.module, orig, k, getAttachedOp(g, canon, k))
-
- if not isTrival(getAttachedOp(g, orig, attachedDestructor)):
- #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
deleted file mode 100644
index 7ca46ab1b..000000000
--- a/compiler/liftlocals.nim
+++ /dev/null
@@ -1,73 +0,0 @@
-#
-#
-# The Nim Compiler
-# (c) Copyright 2015 Andreas Rumpf
-#
-# See the file "copying.txt", included in this
-# distribution, for details about the copyright.
-#
-
-## This module implements the '.liftLocals' pragma.
-
-import
- strutils, options, ast, msgs,
- idents, renderer, types, lowerings, lineinfos
-
-from pragmas import getPragmaVal
-from wordrecg import wLiftLocals
-
-type
- Ctx = object
- partialParam: PSym
- objType: PType
- cache: IdentCache
- idgen: IdGenerator
-
-proc interestingVar(s: PSym): bool {.inline.} =
- result = s.kind in {skVar, skLet, skTemp, skForVar, skResult} and
- sfGlobal notin s.flags
-
-proc lookupOrAdd(c: var Ctx; s: PSym; info: TLineInfo): PNode =
- let field = addUniqueField(c.objType, s, c.cache, c.idgen)
- var deref = newNodeI(nkHiddenDeref, info)
- deref.typ = c.objType
- deref.add(newSymNode(c.partialParam, info))
- result = newNodeI(nkDotExpr, info)
- result.add(deref)
- result.add(newSymNode(field))
- result.typ = field.typ
-
-proc liftLocals(n: PNode; i: int; c: var Ctx) =
- let it = n[i]
- case it.kind
- of nkSym:
- if interestingVar(it.sym):
- n[i] = lookupOrAdd(c, it.sym, it.info)
- of procDefs, nkTypeSection, nkMixinStmt, nkBindStmt: discard
- else:
- for i in 0.. 0 and urlSuffix[0] == '/':
- result.add urlSuffix
- else:
- result.add "/" & urlSuffix
-
-type
- TMsgKind* = enum
- # fatal errors
- errUnknown, errFatal, errInternal,
- # non-fatal errors
- errIllFormedAstX, errCannotOpenFile,
- errXExpected,
- errRstMissingClosing,
- errRstGridTableNotImplemented,
- errRstMarkdownIllformedTable,
- errRstIllformedTable,
- errRstNewSectionExpected,
- errRstGeneralParseError,
- errRstInvalidDirectiveX,
- errRstInvalidField,
- errRstFootnoteMismatch,
- errRstSandboxedDirective,
- errProveInit, # deadcode
- errGenerated,
- errUser,
- # warnings
- warnCannotOpenFile = "CannotOpenFile", warnOctalEscape = "OctalEscape",
- warnXIsNeverRead = "XIsNeverRead", warnXmightNotBeenInit = "XmightNotBeenInit",
- warnDeprecated = "Deprecated", warnConfigDeprecated = "ConfigDeprecated",
- warnDotLikeOps = "DotLikeOps",
- warnSmallLshouldNotBeUsed = "SmallLshouldNotBeUsed", warnUnknownMagic = "UnknownMagic",
- warnRstRedefinitionOfLabel = "RedefinitionOfLabel",
- warnRstUnknownSubstitutionX = "UnknownSubstitutionX",
- warnRstAmbiguousLink = "AmbiguousLink",
- warnRstBrokenLink = "BrokenLink",
- warnRstLanguageXNotSupported = "LanguageXNotSupported",
- warnRstFieldXNotSupported = "FieldXNotSupported",
- warnRstUnusedImportdoc = "UnusedImportdoc",
- warnRstStyle = "warnRstStyle",
- warnCommentXIgnored = "CommentXIgnored",
- warnTypelessParam = "TypelessParam",
- warnUseBase = "UseBase", warnWriteToForeignHeap = "WriteToForeignHeap",
- warnUnsafeCode = "UnsafeCode", warnUnusedImportX = "UnusedImport",
- warnInheritFromException = "InheritFromException", warnEachIdentIsTuple = "EachIdentIsTuple",
- warnUnsafeSetLen = "UnsafeSetLen", warnUnsafeDefault = "UnsafeDefault",
- warnProveInit = "ProveInit", warnProveField = "ProveField", warnProveIndex = "ProveIndex",
- warnUnreachableElse = "UnreachableElse", warnUnreachableCode = "UnreachableCode",
- warnStaticIndexCheck = "IndexCheck", warnGcUnsafe = "GcUnsafe", warnGcUnsafe2 = "GcUnsafe2",
- warnUninit = "Uninit", warnGcMem = "GcMem", warnDestructor = "Destructor",
- warnLockLevel = "LockLevel", # deadcode
- warnResultShadowed = "ResultShadowed",
- warnInconsistentSpacing = "Spacing", warnCaseTransition = "CaseTransition",
- warnCycleCreated = "CycleCreated", warnObservableStores = "ObservableStores",
- warnStrictNotNil = "StrictNotNil",
- warnResultUsed = "ResultUsed",
- warnCannotOpen = "CannotOpen",
- warnFileChanged = "FileChanged",
- warnSuspiciousEnumConv = "EnumConv",
- warnAnyEnumConv = "AnyEnumConv",
- warnHoleEnumConv = "HoleEnumConv",
- warnCstringConv = "CStringConv",
- warnPtrToCstringConv = "PtrToCstringConv",
- warnEffect = "Effect",
- warnCastSizes = "CastSizes"
- warnImplicitTemplateRedefinition = "ImplicitTemplateRedefinition",
- warnUnnamedBreak = "UnnamedBreak",
- warnStmtListLambda = "StmtListLambda",
- warnBareExcept = "BareExcept",
- warnUser = "User",
- # hints
- hintSuccess = "Success", hintSuccessX = "SuccessX",
- hintCC = "CC",
- hintLineTooLong = "LineTooLong",
- hintXDeclaredButNotUsed = "XDeclaredButNotUsed", hintDuplicateModuleImport = "DuplicateModuleImport",
- hintXCannotRaiseY = "XCannotRaiseY", hintConvToBaseNotNeeded = "ConvToBaseNotNeeded",
- hintConvFromXtoItselfNotNeeded = "ConvFromXtoItselfNotNeeded", hintExprAlwaysX = "ExprAlwaysX",
- hintQuitCalled = "QuitCalled", hintProcessing = "Processing", hintProcessingStmt = "ProcessingStmt", hintCodeBegin = "CodeBegin",
- hintCodeEnd = "CodeEnd", hintConf = "Conf", hintPath = "Path",
- hintConditionAlwaysTrue = "CondTrue", hintConditionAlwaysFalse = "CondFalse", hintName = "Name",
- hintPattern = "Pattern", hintExecuting = "Exec", hintLinking = "Link", hintDependency = "Dependency",
- hintSource = "Source", hintPerformance = "Performance", hintStackTrace = "StackTrace",
- hintGCStats = "GCStats", hintGlobalVar = "GlobalVar", hintExpandMacro = "ExpandMacro",
- hintAmbiguousEnum = "AmbiguousEnum",
- hintUser = "User", hintUserRaw = "UserRaw", hintExtendedContext = "ExtendedContext",
- hintMsgOrigin = "MsgOrigin", # since 1.3.5
- hintDeclaredLoc = "DeclaredLoc", # since 1.5.1
-
-const
- MsgKindToStr*: array[TMsgKind, string] = [
- errUnknown: "unknown error",
- errFatal: "fatal error: $1",
- errInternal: "internal error: $1",
- errIllFormedAstX: "illformed AST: $1",
- errCannotOpenFile: "cannot open '$1'",
- errXExpected: "'$1' expected",
- errRstMissingClosing: "$1",
- errRstGridTableNotImplemented: "grid table is not implemented",
- errRstMarkdownIllformedTable: "illformed delimiter row of a markdown table",
- errRstIllformedTable: "Illformed table: $1",
- errRstNewSectionExpected: "new section expected $1",
- errRstGeneralParseError: "general parse error",
- errRstInvalidDirectiveX: "invalid directive: '$1'",
- errRstInvalidField: "invalid field: $1",
- errRstFootnoteMismatch: "number of footnotes and their references don't match: $1",
- errRstSandboxedDirective: "disabled directive: '$1'",
- errProveInit: "Cannot prove that '$1' is initialized.", # deadcode
- errGenerated: "$1",
- errUser: "$1",
- warnCannotOpenFile: "cannot open '$1'",
- warnOctalEscape: "octal escape sequences do not exist; leading zero is ignored",
- warnXIsNeverRead: "'$1' is never read",
- warnXmightNotBeenInit: "'$1' might not have been initialized",
- warnDeprecated: "$1",
- warnConfigDeprecated: "config file '$1' is deprecated",
- warnDotLikeOps: "$1",
- warnSmallLshouldNotBeUsed: "'l' should not be used as an identifier; may look like '1' (one)",
- warnUnknownMagic: "unknown magic '$1' might crash the compiler",
- warnRstRedefinitionOfLabel: "redefinition of label '$1'",
- warnRstUnknownSubstitutionX: "unknown substitution '$1'",
- warnRstAmbiguousLink: "ambiguous doc link $1",
- warnRstBrokenLink: "broken link '$1'",
- warnRstLanguageXNotSupported: "language '$1' not supported",
- warnRstFieldXNotSupported: "field '$1' not supported",
- warnRstUnusedImportdoc: "importdoc for '$1' is not used",
- warnRstStyle: "RST style: $1",
- warnCommentXIgnored: "comment '$1' ignored",
- warnTypelessParam: "", # deadcode
- warnUseBase: "use {.base.} for base methods; baseless methods are deprecated",
- warnWriteToForeignHeap: "write to foreign heap",
- warnUnsafeCode: "unsafe code: '$1'",
- warnUnusedImportX: "imported and not used: '$1'",
- warnInheritFromException: "inherit from a more precise exception type like ValueError, " &
- "IOError or OSError. If these don't suit, inherit from CatchableError or Defect.",
- warnEachIdentIsTuple: "each identifier is a tuple",
- warnUnsafeSetLen: "setLen can potentially expand the sequence, " &
- "but the element type '$1' doesn't have a valid default value",
- warnUnsafeDefault: "The '$1' type doesn't have a valid default value",
- warnProveInit: "Cannot prove that '$1' is initialized. This will become a compile time error in the future.",
- warnProveField: "cannot prove that field '$1' is accessible",
- warnProveIndex: "cannot prove index '$1' is valid",
- warnUnreachableElse: "unreachable else, all cases are already covered",
- warnUnreachableCode: "unreachable code after 'return' statement or '{.noReturn.}' proc",
- warnStaticIndexCheck: "$1",
- warnGcUnsafe: "not GC-safe: '$1'",
- warnGcUnsafe2: "$1",
- warnUninit: "use explicit initialization of '$1' for clarity",
- warnGcMem: "'$1' uses GC'ed memory",
- warnDestructor: "usage of a type with a destructor in a non destructible context. This will become a compile time error in the future.",
- warnLockLevel: "$1", # deadcode
- warnResultShadowed: "Special variable 'result' is shadowed.",
- warnInconsistentSpacing: "Number of spaces around '$#' is not consistent",
- warnCaseTransition: "Potential object case transition, instantiate new object instead",
- warnCycleCreated: "$1",
- warnObservableStores: "observable stores to '$1'",
- warnStrictNotNil: "$1",
- warnResultUsed: "used 'result' variable",
- warnCannotOpen: "cannot open: $1",
- warnFileChanged: "file changed: $1",
- warnSuspiciousEnumConv: "$1",
- warnAnyEnumConv: "$1",
- warnHoleEnumConv: "$1",
- warnCstringConv: "$1",
- warnPtrToCstringConv: "unsafe conversion to 'cstring' from '$1'; this will become a compile time error in the future",
- warnEffect: "$1",
- warnCastSizes: "$1",
- warnImplicitTemplateRedefinition: "template '$1' is implicitly redefined; this is deprecated, add an explicit .redefine pragma",
- warnUnnamedBreak: "Using an unnamed break in a block is deprecated; Use a named block with a named break instead",
- warnStmtListLambda: "statement list expression assumed to be anonymous proc; this is deprecated, use `do (): ...` or `proc () = ...` instead",
- warnBareExcept: "$1",
- warnUser: "$1",
- hintSuccess: "operation successful: $#",
- # keep in sync with `testament.isSuccess`
- hintSuccessX: "$build\n$loc lines; ${sec}s; $mem; proj: $project; out: $output",
- hintCC: "CC: $1",
- hintLineTooLong: "line too long",
- hintXDeclaredButNotUsed: "'$1' is declared but not used",
- hintDuplicateModuleImport: "$1",
- hintXCannotRaiseY: "$1",
- hintConvToBaseNotNeeded: "conversion to base object is not needed",
- hintConvFromXtoItselfNotNeeded: "conversion from $1 to itself is pointless",
- hintExprAlwaysX: "expression evaluates always to '$1'",
- hintQuitCalled: "quit() called",
- hintProcessing: "$1",
- hintProcessingStmt: "$1",
- hintCodeBegin: "generated code listing:",
- hintCodeEnd: "end of listing",
- hintConf: "used config file '$1'",
- hintPath: "added path: '$1'",
- hintConditionAlwaysTrue: "condition is always true: '$1'",
- hintConditionAlwaysFalse: "condition is always false: '$1'",
- hintName: "$1",
- hintPattern: "$1",
- hintExecuting: "$1",
- hintLinking: "$1",
- hintDependency: "$1",
- hintSource: "$1",
- hintPerformance: "$1",
- hintStackTrace: "$1",
- hintGCStats: "$1",
- hintGlobalVar: "global variable declared here",
- hintExpandMacro: "expanded macro: $1",
- hintAmbiguousEnum: "$1",
- hintUser: "$1",
- hintUserRaw: "$1",
- hintExtendedContext: "$1",
- hintMsgOrigin: "$1",
- hintDeclaredLoc: "$1",
- ]
-
-const
- fatalMsgs* = {errUnknown..errInternal}
- errMin* = errUnknown
- errMax* = errUser
- warnMin* = warnCannotOpenFile
- warnMax* = pred(hintSuccess)
- hintMin* = hintSuccess
- hintMax* = high(TMsgKind)
- rstWarnings* = {warnRstRedefinitionOfLabel..warnRstStyle}
-
-type
- TNoteKind* = range[warnMin..hintMax] # "notes" are warnings or hints
- TNoteKinds* = set[TNoteKind]
-
-proc computeNotesVerbosity(): array[0..3, TNoteKinds] =
- result[3] = {low(TNoteKind)..high(TNoteKind)} - {warnObservableStores, warnResultUsed, warnAnyEnumConv}
- result[2] = result[3] - {hintStackTrace, hintExtendedContext, hintDeclaredLoc, hintProcessingStmt}
- result[1] = result[2] - {warnProveField, warnProveIndex,
- warnGcUnsafe, hintPath, hintDependency, hintCodeBegin, hintCodeEnd,
- hintSource, hintGlobalVar, hintGCStats, hintMsgOrigin, hintPerformance}
- result[0] = result[1] - {hintSuccessX, hintSuccess, hintConf,
- hintProcessing, hintPattern, hintExecuting, hintLinking, hintCC}
-
-const
- NotesVerbosity* = computeNotesVerbosity()
- errXMustBeCompileTime* = "'$1' can only be used in compile-time context"
- errArgsNeedRunOption* = "arguments can only be given if the '--run' option is selected"
-
-type
- TFileInfo* = object
- fullPath*: AbsoluteFile # This is a canonical full filesystem path
- projPath*: RelativeFile # This is relative to the project's root
- shortName*: string # short name of the module
- quotedName*: Rope # cached quoted short name for codegen
- # purposes
- quotedFullName*: Rope # cached quoted full name for codegen
- # purposes
-
- lines*: seq[string] # the source code of the module
- # used for better error messages and
- # embedding the original source in the
- # generated code
- dirtyFile*: AbsoluteFile # the file that is actually read into memory
- # and parsed; usually "" but is used
- # for 'nimsuggest'
- hash*: string # the checksum of the file
- dirty*: bool # for 'nimfix' / 'nimpretty' like tooling
- when defined(nimpretty):
- fullContent*: string
- FileIndex* = distinct int32
- TLineInfo* = object # This is designed to be as small as possible,
- # because it is used
- # in syntax nodes. We save space here by using
- # two int16 and an int32.
- # On 64 bit and on 32 bit systems this is
- # only 8 bytes.
- line*: uint16
- col*: int16
- fileIndex*: FileIndex
- when defined(nimpretty):
- offsetA*, offsetB*: int
- commentOffsetA*, commentOffsetB*: int
-
- TErrorOutput* = enum
- eStdOut
- eStdErr
-
- TErrorOutputs* = set[TErrorOutput]
-
- ERecoverableError* = object of ValueError
- ESuggestDone* = object of ValueError
-
-proc `==`*(a, b: FileIndex): bool {.borrow.}
-
-proc hash*(i: TLineInfo): Hash =
- hash (i.line.int, i.col.int, i.fileIndex.int)
-
-proc raiseRecoverableError*(msg: string) {.noinline.} =
- raise newException(ERecoverableError, msg)
-
-const
- InvalidFileIdx* = FileIndex(-1)
- unknownLineInfo* = TLineInfo(line: 0, col: -1, fileIndex: InvalidFileIdx)
-
-type
- Severity* {.pure.} = enum ## VS Code only supports these three
- Hint, Warning, Error
-
-const
- trackPosInvalidFileIdx* = FileIndex(-2) # special marker so that no suggestions
- # are produced within comments and string literals
- commandLineIdx* = FileIndex(-3)
-
-type
- MsgConfig* = object ## does not need to be stored in the incremental cache
- trackPos*: TLineInfo
- trackPosAttached*: bool ## whether the tracking position was attached to
- ## some close token.
-
- errorOutputs*: TErrorOutputs
- msgContext*: seq[tuple[info: TLineInfo, detail: string]]
- lastError*: TLineInfo
- filenameToIndexTbl*: Table[string, FileIndex]
- fileInfos*: seq[TFileInfo]
- systemFileIdx*: FileIndex
-
-
-proc initMsgConfig*(): MsgConfig =
- result.msgContext = @[]
- result.lastError = unknownLineInfo
- result.filenameToIndexTbl = initTable[string, FileIndex]()
- result.fileInfos = @[]
- result.errorOutputs = {eStdOut, eStdErr}
- result.filenameToIndexTbl["???"] = FileIndex(-1)
diff --git a/compiler/linter.nim b/compiler/linter.nim
deleted file mode 100644
index 0c2aaef79..000000000
--- a/compiler/linter.nim
+++ /dev/null
@@ -1,152 +0,0 @@
-#
-#
-# The Nim Compiler
-# (c) Copyright 2015 Andreas Rumpf
-#
-# See the file "copying.txt", included in this
-# distribution, for details about the copyright.
-#
-
-## This module implements the style checker.
-
-import std/strutils
-from std/sugar import dup
-
-import options, ast, msgs, idents, lineinfos, wordrecg, astmsgs, semdata, packages
-export packages
-
-const
- Letters* = {'a'..'z', 'A'..'Z', '0'..'9', '\x80'..'\xFF', '_'}
-
-proc identLen*(line: string, start: int): int =
- while start+result < line.len and line[start+result] in Letters:
- inc result
-
-proc `=~`(s: string, a: openArray[string]): bool =
- for x in a:
- if s.startsWith(x): return true
-
-proc beautifyName(s: string, k: TSymKind): string =
- # minimal set of rules here for transition:
- # GC_ is allowed
-
- let allUpper = allCharsInSet(s, {'A'..'Z', '0'..'9', '_'})
- if allUpper and k in {skConst, skEnumField, skType}: return s
- result = newStringOfCap(s.len)
- var i = 0
- case k
- of skType, skGenericParam:
- # Types should start with a capital unless builtins like 'int' etc.:
- if s =~ ["int", "uint", "cint", "cuint", "clong", "cstring", "string",
- "char", "byte", "bool", "openArray", "seq", "array", "void",
- "pointer", "float", "csize", "csize_t", "cdouble", "cchar", "cschar",
- "cshort", "cu", "nil", "typedesc", "auto", "any",
- "range", "openarray", "varargs", "set", "cfloat", "ref", "ptr",
- "untyped", "typed", "static", "sink", "lent", "type", "owned", "iterable"]:
- result.add s[i]
- else:
- result.add toUpperAscii(s[i])
- of skConst, skEnumField:
- # for 'const' we keep how it's spelt; either upper case or lower case:
- result.add s[0]
- else:
- # as a special rule, don't transform 'L' to 'l'
- if s.len == 1 and s[0] == 'L': result.add 'L'
- elif '_' in s: result.add(s[i])
- else: result.add toLowerAscii(s[0])
- inc i
- while i < s.len:
- if s[i] == '_':
- if i+1 >= s.len:
- discard "trailing underscores should be stripped off"
- elif 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
- result.add s[i]
- else:
- inc i
- result.add toUpperAscii(s[i])
- elif allUpper:
- result.add toLowerAscii(s[i])
- else:
- result.add s[i]
- inc i
-
-proc differ*(line: string, a, b: int, x: string): string =
- proc substrEq(s: string, pos, last: int, substr: string): bool =
- result = true
- for i in 0.. last or s[pos+i] != substr[i]: return false
-
- result = ""
- if not substrEq(line, a, b, x):
- let y = line[a..b]
- if cmpIgnoreStyle(y, x) == 0:
- result = y
-
-proc nep1CheckDefImpl(conf: ConfigRef; info: TLineInfo; s: PSym; k: TSymKind) =
- 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*(ctx: PContext; info: TLineInfo; s: PSym) =
- ## Check symbol definitions adhere to NEP1 style rules.
- styleCheckDef(ctx, info, s, s.kind)
-
-template styleCheckDef*(ctx: PContext; s: PSym) =
- ## Check symbol definitions adhere to NEP1 style rules.
- styleCheckDef(ctx, s.info, s, s.kind)
-
-proc differs(conf: ConfigRef; info: TLineInfo; newName: string): string =
- let line = sourceLine(conf, info)
- var first = min(info.col.int, line.len)
- if first < 0: return
- #inc first, skipIgnoreCase(line, "proc ", first)
- while first > 0 and line[first-1] in Letters: dec first
- if first < 0: return
- if first+1 < line.len and line[first] == '`': inc first
-
- let last = first+identLen(line, first)-1
- result = differ(line, first, last, newName)
-
-proc styleCheckUseImpl(conf: ConfigRef; info: TLineInfo; s: PSym) =
- let newName = s.name.s
- let badName = differs(conf, info, newName)
- if badName.len > 0:
- lintReport(conf, info, newName, badName, "".dup(addDeclaredLoc(conf, s)))
-
-template styleCheckUse*(ctx: PContext; info: TLineInfo; sym: PSym) =
- ## Check symbol uses match their definition's style.
- if {optStyleHint, optStyleError} * ctx.config.globalOptions != {} and # ignore if styleChecks are off
- hintName in ctx.config.notes and # ignore if name checks are not requested
- ctx.config.belongsToProjectPackage(ctx.module) and # ignore foreign packages
- sym.kind != skTemp and # ignore temporary variables created by the compiler
- sym.name.s[0] in Letters and # ignore operators TODO: what about unicode symbols???
- sfAnon notin sym.flags: # ignore temporary variables created by the compiler
- styleCheckUseImpl(ctx.config, info, sym)
-
-proc checkPragmaUseImpl(conf: ConfigRef; info: TLineInfo; w: TSpecialWord; pragmaName: string) =
- let wanted = $w
- if pragmaName != wanted:
- lintReport(conf, info, wanted, pragmaName)
-
-template checkPragmaUse*(conf: ConfigRef; info: TLineInfo; w: TSpecialWord; pragmaName: string) =
- if {optStyleHint, optStyleError} * conf.globalOptions != {}:
- checkPragmaUseImpl(conf, info, w, pragmaName)
diff --git a/compiler/lists.nim b/compiler/lists.nim
new file mode 100644
index 000000000..27e373342
--- /dev/null
+++ b/compiler/lists.nim
@@ -0,0 +1,119 @@
+#
+#
+# The Nim Compiler
+# (c) Copyright 2012 Andreas Rumpf
+#
+# See the file "copying.txt", included in this
+# distribution, for details about the copyright.
+#
+
+# This module implements a generic doubled linked list.
+# TODO Remove this and replace it with something sensible
+import os
+type
+ PListEntry* = ref TListEntry
+ TListEntry* = object of RootObj
+ prev*, next*: PListEntry
+
+ TStrEntry* = object of TListEntry
+ data*: string
+
+ PStrEntry* = ref TStrEntry
+ TLinkedList* = object # for the "find" operation:
+ head*, tail*: PListEntry
+ counter*: int
+
+ TCompareProc* = proc (entry: PListEntry, closure: pointer): bool {.nimcall.}
+
+proc initLinkedList*(list: var TLinkedList) =
+ list.counter = 0
+ list.head = nil
+ list.tail = nil
+
+proc append*(list: var TLinkedList, entry: PListEntry) =
+ inc(list.counter)
+ entry.next = nil
+ entry.prev = list.tail
+ if list.tail != nil:
+ assert(list.tail.next == nil)
+ list.tail.next = entry
+ list.tail = entry
+ if list.head == nil: list.head = entry
+
+proc contains*(list: TLinkedList, data: string): bool =
+ var it = list.head
+ while it != nil:
+ if PStrEntry(it).data == data:
+ return true
+ it = it.next
+
+proc newStrEntry(data: string): PStrEntry =
+ new(result)
+ result.data = data
+
+proc appendStr*(list: var TLinkedList, data: string) =
+ append(list, newStrEntry(data))
+
+proc includeStr*(list: var TLinkedList, data: string): bool =
+ if contains(list, data): return true
+ appendStr(list, data) # else: add to list
+
+proc prepend*(list: var TLinkedList, entry: PListEntry) =
+ inc(list.counter)
+ entry.prev = nil
+ entry.next = list.head
+ if list.head != nil:
+ assert(list.head.prev == nil)
+ list.head.prev = entry
+ list.head = entry
+ if list.tail == nil: list.tail = entry
+
+proc prependStr*(list: var TLinkedList, data: string) =
+ prepend(list, newStrEntry(data))
+
+proc insertBefore*(list: var TLinkedList, pos, entry: PListEntry) =
+ assert(pos != nil)
+ if pos == list.head:
+ prepend(list, entry)
+ else:
+ inc(list.counter)
+ entry.next = pos
+ entry.prev = pos.prev
+ if pos.prev != nil: pos.prev.next = entry
+ pos.prev = entry
+
+proc remove*(list: var TLinkedList, entry: PListEntry) =
+ dec(list.counter)
+ if entry == list.tail:
+ list.tail = entry.prev
+ if entry == list.head:
+ list.head = entry.next
+ if entry.next != nil: entry.next.prev = entry.prev
+ if entry.prev != nil: entry.prev.next = entry.next
+
+proc bringToFront*(list: var TLinkedList, entry: PListEntry) =
+ when true:
+ list.remove entry
+ list.prepend entry
+ else:
+ if entry == list.head: return
+ if entry == list.tail: list.tail = entry.prev
+ if entry.next != nil: entry.next.prev = entry.prev
+ if entry.prev != nil: entry.prev.next = entry.next
+ entry.prev = nil
+ entry.next = list.head
+ list.head = entry
+
+proc excludePath*(list: var TLinkedList, data: string) =
+ var it = list.head
+ while it != nil:
+ let nxt = it.next
+ if cmpPaths(PStrEntry(it).data, data) == 0:
+ remove(list, it)
+ it = nxt
+
+proc find*(list: TLinkedList, fn: TCompareProc, closure: pointer): PListEntry =
+ result = list.head
+ while result != nil:
+ if fn(result, closure): return
+ result = result.next
diff --git a/compiler/llstream.nim b/compiler/llstream.nim
index 004d990fa..0a1e09fc8 100644
--- a/compiler/llstream.nim
+++ b/compiler/llstream.nim
@@ -10,20 +10,13 @@
## Low-level streams for high performance.
import
- pathutils
+ strutils
-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)
-
-when hasRstdin: import rdstdin
+# support '-d:useGnuReadline' for backwards compatibility:
+when not defined(windows) and (defined(useGnuReadline) or defined(useLinenoise)):
+ import rdstdin
type
- TLLRepl* = proc (s: PLLStream, buf: pointer, bufLen: int): int
- OnPrompt* = proc() {.closure.}
TLLStreamKind* = enum # enum of different stream implementations
llsNone, # null stream: reading and writing has no effect
llsString, # stream encapsulates a string
@@ -35,8 +28,6 @@ type
s*: string
rd*, wr*: int # for string streams
lineOffset*: int # for fake stdin line numbers
- repl*: TLLRepl # gives stdin control to clients
- onPrompt*: OnPrompt
PLLStream* = ref TLLStream
@@ -50,23 +41,20 @@ proc llStreamOpen*(f: File): PLLStream =
result.f = f
result.kind = llsFile
-proc llStreamOpen*(filename: AbsoluteFile, mode: FileMode): PLLStream =
+proc llStreamOpen*(filename: string, mode: FileMode): PLLStream =
new(result)
result.kind = llsFile
- if not open(result.f, filename.string, mode): result = nil
+ if not open(result.f, filename, mode): result = nil
proc llStreamOpen*(): PLLStream =
new(result)
result.kind = llsNone
-proc llReadFromStdin(s: PLLStream, buf: pointer, bufLen: int): int
-proc llStreamOpenStdIn*(r: TLLRepl = llReadFromStdin, onPrompt: OnPrompt = nil): PLLStream =
+proc llStreamOpenStdIn*(): PLLStream =
new(result)
result.kind = llsStdIn
result.s = ""
result.lineOffset = -1
- result.repl = r
- result.onPrompt = onPrompt
proc llStreamClose*(s: PLLStream) =
case s.kind
@@ -96,16 +84,17 @@ const
AdditionalLineContinuationOprs = {'#', ':', '='}
proc endsWithOpr*(x: string): bool =
+ # also used by the standard template filter:
result = x.endsWith(LineContinuationOprs)
proc continueLine(line: string, inTripleString: bool): bool {.inline.} =
- result = inTripleString or line.len > 0 and (
- line[0] == ' ' or
- line.endsWith(LineContinuationOprs+AdditionalLineContinuationOprs))
+ result = inTripleString or
+ line[0] == ' ' or
+ line.endsWith(LineContinuationOprs+AdditionalLineContinuationOprs)
proc countTriples(s: string): int =
var i = 0
- while i+2 < s.len:
+ while i < s.len:
if s[i] == '"' and s[i+1] == '"' and s[i+2] == '"':
inc result
inc i, 2
@@ -117,12 +106,12 @@ proc llReadFromStdin(s: PLLStream, buf: pointer, bufLen: int): int =
var line = newStringOfCap(120)
var triples = 0
while readLineFromStdin(if s.s.len == 0: ">>> " else: "... ", line):
- s.s.add(line)
- s.s.add("\n")
+ add(s.s, line)
+ add(s.s, "\n")
inc triples, countTriples(line)
if not continueLine(line, (triples and 1) == 1): break
inc(s.lineOffset)
- result = min(bufLen, s.s.len - s.rd)
+ result = min(bufLen, len(s.s) - s.rd)
if result > 0:
copyMem(buf, addr(s.s[s.rd]), result)
inc(s.rd, result)
@@ -132,15 +121,14 @@ proc llStreamRead*(s: PLLStream, buf: pointer, bufLen: int): int =
of llsNone:
result = 0
of llsString:
- result = min(bufLen, s.s.len - s.rd)
+ result = min(bufLen, len(s.s) - s.rd)
if result > 0:
copyMem(buf, addr(s.s[0 + s.rd]), result)
inc(s.rd, result)
of llsFile:
result = readBuffer(s.f, buf, bufLen)
of llsStdIn:
- if s.onPrompt!=nil: s.onPrompt()
- result = s.repl(s, buf, bufLen)
+ result = llReadFromStdin(s, buf, bufLen)
proc llStreamReadLine*(s: PLLStream, line: var string): bool =
setLen(line, 0)
@@ -148,19 +136,19 @@ proc llStreamReadLine*(s: PLLStream, line: var string): bool =
of llsNone:
result = true
of llsString:
- while s.rd < s.s.len:
+ while s.rd < len(s.s):
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:
- line.add(s.s[s.rd])
+ add(line, s.s[s.rd])
inc(s.rd)
- result = line.len > 0 or s.rd < s.s.len
+ result = line.len > 0 or s.rd < len(s.s)
of llsFile:
result = readLine(s.f, line)
of llsStdIn:
@@ -171,8 +159,8 @@ proc llStreamWrite*(s: PLLStream, data: string) =
of llsNone, llsStdIn:
discard
of llsString:
- s.s.add(data)
- inc(s.wr, data.len)
+ add(s.s, data)
+ inc(s.wr, len(data))
of llsFile:
write(s.f, data)
@@ -186,7 +174,7 @@ proc llStreamWrite*(s: PLLStream, data: char) =
of llsNone, llsStdIn:
discard
of llsString:
- s.s.add(data)
+ add(s.s, data)
inc(s.wr)
of llsFile:
c = data
@@ -198,7 +186,7 @@ proc llStreamWrite*(s: PLLStream, buf: pointer, buflen: int) =
discard
of llsString:
if buflen > 0:
- setLen(s.s, s.s.len + buflen)
+ setLen(s.s, len(s.s) + buflen)
copyMem(addr(s.s[0 + s.wr]), buf, buflen)
inc(s.wr, buflen)
of llsFile:
@@ -213,7 +201,7 @@ proc llStreamReadAll*(s: PLLStream): string =
of llsString:
if s.rd == 0: result = s.s
else: result = substr(s.s, s.rd)
- s.rd = s.s.len
+ s.rd = len(s.s)
of llsFile:
result = newString(bufSize)
var bytes = readBuffer(s.f, addr(result[0]), bufSize)
diff --git a/compiler/lookups.nim b/compiler/lookups.nim
index 3f028a52f..962c28613 100644
--- a/compiler/lookups.nim
+++ b/compiler/lookups.nim
@@ -8,67 +8,45 @@
#
# 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
+ intsets, ast, astalgo, idents, semdata, types, msgs, options, rodread,
+ renderer, wordrecg, idgen, nimfix.prettybase
-proc ensureNoMissingOrUnusedSymbols(c: PContext; scope: PScope)
+proc ensureNoMissingOrUnusedSymbols(scope: PScope)
-proc noidentError(conf: ConfigRef; n, origin: PNode) =
- var m = ""
- if origin != nil:
- m.add "in expression '" & origin.renderTree & "': "
- m.add "identifier expected, but found '" & n.renderTree & "'"
- localError(conf, n.info, m)
-
-proc considerQuotedIdent*(c: PContext; n: PNode, origin: PNode = nil): PIdent =
+proc considerQuotedIdent*(n: PNode): PIdent =
## Retrieve a PIdent from a PNode, taking into account accent nodes.
- ## ``origin`` can be nil. If it is not nil, it is used for a better
- ## error message.
- template handleError(n, origin: PNode) =
- noidentError(c.config, n, origin)
- result = getIdent(c.cache, "")
-
case n.kind
of nkIdent: result = n.ident
of nkSym: result = n.sym.name
of nkAccQuoted:
case n.len
- of 0: handleError(n, origin)
- of 1: result = considerQuotedIdent(c, n[0], origin)
+ of 0:
+ localError(n.info, errIdentifierExpected, renderTree(n))
+ result = getIdent""
+ of 1: result = considerQuotedIdent(n.sons[0])
else:
var id = ""
- for i in 0.."
+ result = getIdent(id)
+ of nkOpenSymChoice, nkClosedSymChoice: result = n.sons[0].sym.name
else:
- handleError(n, origin)
+ localError(n.info, errIdentifierExpected, renderTree(n))
+ result = getIdent""
template addSym*(scope: PScope, s: PSym) =
strTableAdd(scope.symbols, s)
-proc addUniqueSym*(scope: PScope, s: PSym): PSym =
- result = strTableInclReportConflict(scope.symbols, s)
+proc addUniqueSym*(scope: PScope, s: PSym): bool =
+ result = not strTableIncl(scope.symbols, s)
proc openScope*(c: PContext): PScope {.discardable.} =
result = PScope(parent: c.currentScope,
@@ -80,750 +58,358 @@ proc rawCloseScope*(c: PContext) =
c.currentScope = c.currentScope.parent
proc closeScope*(c: PContext) =
- ensureNoMissingOrUnusedSymbols(c, c.currentScope)
+ ensureNoMissingOrUnusedSymbols(c.currentScope)
rawCloseScope(c)
-iterator allScopes*(scope: PScope): PScope =
+iterator walkScopes*(scope: PScope): PScope =
var current = scope
while current != nil:
yield current
current = current.parent
-iterator localScopesFrom*(c: PContext; scope: PScope): PScope =
- for s in allScopes(scope):
- if s == c.topLevelScope: break
- yield s
-
-proc skipAlias*(s: PSym; n: PNode; conf: ConfigRef): PSym =
+proc skipAlias*(s: PSym; n: PNode): PSym =
if s == nil or s.kind != skAlias:
result = s
else:
result = s.owner
- if conf.cmd == cmdNimfix:
- prettybase.replaceDeprecated(conf, n.info, s, result)
+ if gCmd == cmdPretty:
+ prettybase.replaceDeprecated(n.info, s, result)
else:
- message(conf, n.info, warnDeprecated, "use " & result.name.s & " instead; " &
- s.name.s & " is deprecated")
-
-proc isShadowScope*(s: PScope): bool {.inline.} =
- s.parent != nil and s.parent.depthLevel == s.depthLevel
+ message(n.info, warnDeprecated, "use " & result.name.s & " instead; " &
+ s.name.s)
proc localSearchInScope*(c: PContext, s: PIdent): PSym =
- var scope = c.currentScope
- result = strTableGet(scope.symbols, s)
- while result == nil and scope.isShadowScope:
- # We are in a shadow scope, check in the parent too
- scope = scope.parent
+ result = strTableGet(c.currentScope.symbols, s)
+
+proc searchInScopes*(c: PContext, s: PIdent): PSym =
+ for scope in walkScopes(c.currentScope):
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)
- while result != nil:
- let b =
- case im.mode
- of importAll: true
- of importSet: result.id in im.imported
- of importExcept: name.id notin im.exceptSet
- if b and not containsOrIncl(marked, result.id):
- return result
- result = nextModuleIter(ti, g)
-
-proc nextIdentIter(ti: var ModuleIter; marked: var IntSet; im: ImportedModule;
- g: ModuleGraph): PSym =
- while true:
- result = nextModuleIter(ti, g)
- if result == nil: return nil
- case im.mode
- of importAll:
- if not containsOrIncl(marked, result.id):
- return result
- of importSet:
- if result.id in im.imported and not containsOrIncl(marked, result.id):
- return result
- of importExcept:
- 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)
- while candidate != nil:
- yield candidate
- candidate = nextIdentIter(ti, marked, im, g)
-
-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):
- yield s
-
-proc allPureEnumFields(c: PContext; name: PIdent): seq[PSym] =
- var ti: TIdentIter
- result = @[]
- var res = initIdentIter(ti, c.pureEnumFields, name)
- while res != nil:
- result.add res
- res = nextIdentIter(ti, c.pureEnumFields)
-
-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
- dec scopeN
- for item in scope.symbols:
- yield (item, scopeN, isLocal)
-
- 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
-
-
-proc someSymFromImportTable*(c: PContext; name: PIdent; ambiguous: var bool): PSym =
- var marked = initIntSet()
- var symSet = OverloadableSyms
+ if result != nil: return
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:
- ambiguous = true
- break outer
-proc searchInScopes*(c: PContext, s: PIdent; ambiguous: var bool): PSym =
- for scope in allScopes(c.currentScope):
- result = strTableGet(scope.symbols, s)
- 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):
+ for scope in walkScopes(c.currentScope):
echo "scope ", i
- for h in 0..high(scope.symbols.data):
+ 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] =
- 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
- 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):
- if s.kind in filter:
- result.add s
+proc searchInScopes*(c: PContext, s: PIdent, filter: TSymKinds): PSym =
+ for scope in walkScopes(c.currentScope):
+ result = strTableGet(scope.symbols, s)
+ if result != nil and result.kind in filter: return
+ result = nil
proc errorSym*(c: PContext, n: PNode): PSym =
## creates an error symbol to avoid cascading errors (for IDE support)
var m = n
# ensure that 'considerQuotedIdent' can't fail:
- if m.kind == nkDotExpr: m = m[1]
+ if m.kind == nkDotExpr: m = m.sons[1]
let ident = if m.kind in {nkIdent, nkSym, nkAccQuoted}:
- considerQuotedIdent(c, m)
+ considerQuotedIdent(m)
else:
- getIdent(c.cache, "err:" & renderTree(m))
- result = newSym(skError, ident, nextSymId(c.idgen), getCurrOwner(c), n.info, {})
+ getIdent("err:" & renderTree(m))
+ result = newSym(skError, ident, getCurrOwner(), n.info)
result.typ = errorType(c)
incl(result.flags, sfDiscardable)
- # pretend it's from the top level scope to prevent cascading errors:
- if c.config.cmd != cmdInteractive and c.compilesContextId == 0:
- c.moduleScope.addSym(result)
+ # pretend it's imported from some unknown module to prevent cascading errors:
+ if gCmd != cmdInteractive and c.compilesContextId == 0:
+ c.importTable.addSym(result)
type
TOverloadIterMode* = enum
oimDone, oimNoQualifier, oimSelfModule, oimOtherModule, oimSymChoice,
oimSymChoiceLocalLookup
- TOverloadIter* = object
+ TOverloadIter*{.final.} = object
it*: TIdentIter
- mit*: ModuleIter
m*: PSym
mode*: TOverloadIterMode
symChoiceIndex*: int
- currentScope: PScope
- importIdx: int
- marked: IntSet
+ scope*: PScope
+ inSymChoice: IntSet
-proc getSymRepr*(conf: ConfigRef; s: PSym, getDeclarationPath = true): string =
+proc getSymRepr*(s: PSym): string =
case s.kind
- of routineKinds, skType:
- result = getProcHeader(conf, s, getDeclarationPath = getDeclarationPath)
- else:
- result = "'$1'" % s.name.s
- if getDeclarationPath:
- result.addDeclaredLoc(conf, s)
+ of skProc, skMethod, skConverter, skIterator: result = getProcHeader(s)
+ else: result = s.name.s
-proc ensureNoMissingOrUnusedSymbols(c: PContext; scope: PScope) =
+proc ensureNoMissingOrUnusedSymbols(scope: PScope) =
# check if all symbols have been used and defined:
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}:
+ if sfForward in s.flags:
# too many 'implementation of X' errors are annoying
# and slow 'suggest' down:
if missingImpls == 0:
- localError(c.config, s.info, "implementation of '$1' expected" %
- getSymRepr(c.config, s, getDeclarationPath=false))
+ localError(s.info, errImplOfXexpected, getSymRepr(s))
inc missingImpls
- elif {sfUsed, sfExported} * s.flags == {}:
- if s.kind notin {skForVar, skParam, skMethod, skUnknown, skGenericParam, skEnumField}:
+ elif {sfUsed, sfExported} * s.flags == {} and optHints in s.options:
+ # BUGFIX: check options in s!
+ if s.kind notin {skForVar, skParam, skMethod, skUnknown, skGenericParam}:
# XXX: implicit type params are currently skTypes
# 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))
+ if s.typ != nil and tfImplicitTypeParam notin s.typ.flags:
+ message(s.info, hintXDeclaredButNotUsed, getSymRepr(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
- if c.config.cmd != cmdInteractive:
- localError(c.config, info, note,
- "redefinition of '$1'; previous declaration here: $2" %
- [s, c.config $ conflictsWith])
+proc wrongRedefinition*(info: TLineInfo, s: string) =
+ if gCmd != cmdInteractive:
+ localError(info, errAttemptToRedefine, s)
-# 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)
- 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)
-
-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) =
+ if not c.currentScope.addUniqueSym(sym):
+ wrongRedefinition(sym.info, sym.name.s)
proc addPrelimDecl*(c: PContext, sym: PSym) =
discard c.currentScope.addUniqueSym(sym)
-from ic / ic import addHidden
+proc addDeclAt*(scope: PScope, sym: PSym) =
+ if not scope.addUniqueSym(sym):
+ wrongRedefinition(sym.info, sym.name.s)
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)
- 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)
+ if c.module != nil: strTableAdd(c.module.tab, sym)
+ else: internalError(sym.info, "addInterfaceDeclAux")
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)
+ addDeclAt(scope, sym)
+ addInterfaceDeclAux(c, sym)
-proc addInterfaceDecl*(c: PContext, sym: PSym) {.inline.} =
- ## adds a decl and the interface if appropriate
- addInterfaceDeclAt(c, c.currentScope, 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.
+proc addOverloadableSymAt*(scope: PScope, fn: PSym) =
if fn.kind notin OverloadableSyms:
- internalError(c.config, fn.info, "addOverloadableSymAt")
+ internalError(fn.info, "addOverloadableSymAt")
return
let check = strTableGet(scope.symbols, fn.name)
if check != nil and check.kind notin OverloadableSyms:
- wrongRedefinition(c, fn.info, fn.name.s, check.info)
+ wrongRedefinition(fn.info, fn.name.s)
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
- addOverloadableSymAt(c, scope, sym)
- if not scope.isShadowScope:
- # adding into a non-shadow scope, we need to handle exports, etc
- addInterfaceDeclAux(c, sym)
+ # it adds the symbol to the interface if appropriate
+ addOverloadableSymAt(scope, 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)
+when defined(nimfix):
+ import strutils
-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)
-
-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:
- if sym.kind in OverloadableSyms:
- c.addInterfaceOverloadableSymAt(c.currentScope, sym)
- 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 we cannot find the identifier, retry with a changed identifer:
+ 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(toLower(x.s[0]) & x.s.substr(1))
+ of 'a'..'z': result = getIdent(toLower(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: expr) =
+ 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: expr) = 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 = "Error: ambiguous identifier: '" & s.name.s & "'"
+ var ti: TIdentIter
+ var candidate = initIdentIter(ti, c.importTable.symbols, s.name)
var i = 0
- for candidate in candidates:
- if i == 0: err.add " -- $1 the following:\n" % prefix
- else: err.add "\n"
- err.add " " & candidate.owner.name.s & "." & candidate.name.s
- err.add ": " & typeToString(candidate.typ)
+ while candidate != nil:
+ if i == 0: err.add " --use "
+ else: err.add " or "
+ err.add candidate.owner.name.s & "." & candidate.name.s
+ candidate = nextIdentIter(ti, c.importTable.symbols)
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 errorUndeclaredIdentifier*(c: PContext; info: TLineInfo; name: string, extra = "") =
- var err = "undeclared identifier: '" & name & "'" & extra
- if c.recursiveDep.len > 0:
- err.add "\nThis might be caused by a recursive module dependency:\n"
- err.add c.recursiveDep
- # prevent excessive errors for 'nim check'
- 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)
+ localError(info, errGenerated, err)
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)
+ result = searchInScopes(c, n.ident).skipAlias(n)
+ if result == nil:
+ fixSpelling(n, n.ident, searchInScopes)
+ localError(n.info, errUndeclaredIdentifier, 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)
+ var ident = considerQuotedIdent(n)
+ result = searchInScopes(c, ident).skipAlias(n)
+ if result == nil:
+ fixSpelling(n, ident, searchInScopes)
+ localError(n.info, errUndeclaredIdentifier, ident.s)
+ result = errorSym(c, n)
else:
- internalError(c.config, n.info, "lookUp")
+ internalError(n.info, "lookUp")
return
- if amb:
- #contains(c.ambiguousSymbols, result.id):
- result = errorUseQualifier(c, n.info, result, amb)
- when false:
- if result.kind == skStub: loadStub(result)
+ if contains(c.ambiguousSymbols, result.id):
+ errorUseQualifier(c, n.info, result)
+ if result.kind == skStub: loadStub(result)
type
TLookupFlag* = enum
- checkAmbiguity, checkUndeclared, checkModule, checkPureEnumFields
+ checkAmbiguity, checkUndeclared
-proc qualifiedLookUp*(c: PContext, n: PNode, flags: set[TLookupFlag]): PSym =
- const allExceptModule = {low(TSymKind)..high(TSymKind)} - {skModule, skPackage}
+proc qualifiedLookUp*(c: PContext, n: PNode, flags = {checkUndeclared}): PSym =
case n.kind
of nkIdent, nkAccQuoted:
- var amb = false
- var ident = considerQuotedIdent(c, n)
- if checkModule in flags:
- result = searchInScopes(c, ident, amb).skipAlias(n, c.config)
- else:
- let candidates = searchInScopesFilterBy(c, ident, allExceptModule) #.skipAlias(n, c.config)
- if candidates.len > 0:
- result = candidates[0]
- amb = candidates.len > 1
- if amb and checkAmbiguity in flags:
- errorUseQualifier(c, n.info, candidates)
- if result == nil:
- let candidates = allPureEnumFields(c, ident)
- if candidates.len > 0:
- result = candidates[0]
- amb = candidates.len > 1
- if amb and checkAmbiguity in flags:
- errorUseQualifier(c, n.info, candidates)
-
+ var ident = considerQuotedIdent(n)
+ result = searchInScopes(c, ident).skipAlias(n)
if result == nil and checkUndeclared in flags:
- result = errorUndeclaredIdentifierHint(c, n, ident)
- elif checkAmbiguity in flags and result != nil and amb:
- result = errorUseQualifier(c, n.info, result, amb)
- c.isAmbiguous = amb
+ fixSpelling(n, ident, searchInScopes)
+ localError(n.info, errUndeclaredIdentifier, ident.s)
+ result = errorSym(c, n)
+ elif checkAmbiguity in flags and result != nil and
+ contains(c.ambiguousSymbols, result.id):
+ errorUseQualifier(c, n.info, result)
of nkSym:
result = n.sym
+ if checkAmbiguity in flags and contains(c.ambiguousSymbols, result.id):
+ errorUseQualifier(c, n.info, n.sym)
of nkDotExpr:
result = nil
- var m = qualifiedLookUp(c, n[0], (flags * {checkUndeclared}) + {checkModule})
+ var m = qualifiedLookUp(c, n.sons[0], flags*{checkUndeclared})
if m != nil and m.kind == skModule:
var ident: PIdent = nil
- if n[1].kind == nkIdent:
- ident = n[1].ident
- elif n[1].kind == nkAccQuoted:
- ident = considerQuotedIdent(c, n[1])
+ if n.sons[1].kind == nkIdent:
+ ident = n.sons[1].ident
+ elif n.sons[1].kind == nkAccQuoted:
+ ident = considerQuotedIdent(n.sons[1])
if ident != nil:
if m == c.module:
- result = strTableGet(c.topLevelScope.symbols, ident).skipAlias(n, c.config)
+ result = strTableGet(c.topLevelScope.symbols, ident).skipAlias(n)
else:
- result = someSym(c.graph, m, ident).skipAlias(n, c.config)
+ result = strTableGet(m.tab, ident).skipAlias(n)
if result == nil and checkUndeclared in flags:
- result = errorUndeclaredIdentifierHint(c, n[1], ident)
- elif n[1].kind == nkSym:
- result = n[1].sym
+ fixSpelling(n.sons[1], ident, searchInScopes)
+ localError(n.sons[1].info, errUndeclaredIdentifier, ident.s)
+ result = errorSym(c, n.sons[1])
+ elif n.sons[1].kind == nkSym:
+ result = n.sons[1].sym
elif checkUndeclared in flags and
- n[1].kind notin {nkOpenSymChoice, nkClosedSymChoice}:
- localError(c.config, n[1].info, "identifier expected, but got: " &
- renderTree(n[1]))
- result = errorSym(c, n[1])
+ n.sons[1].kind notin {nkOpenSymChoice, nkClosedSymChoice}:
+ localError(n.sons[1].info, errIdentifierExpected,
+ renderTree(n.sons[1]))
+ result = errorSym(c, n.sons[1])
else:
result = nil
- when false:
- if result != nil and result.kind == skStub: loadStub(result)
+ if result != nil and result.kind == skStub: loadStub(result)
proc initOverloadIter*(o: var TOverloadIter, c: PContext, n: PNode): PSym =
- o.importIdx = -1
- o.marked = initIntSet()
case n.kind
of nkIdent, nkAccQuoted:
- var ident = considerQuotedIdent(c, n)
- var scope = c.currentScope
+ var ident = considerQuotedIdent(n)
+ o.scope = c.currentScope
o.mode = oimNoQualifier
while true:
- result = initIdentIter(o.it, scope.symbols, ident).skipAlias(n, c.config)
+ result = initIdentIter(o.it, o.scope.symbols, ident).skipAlias(n)
if result != nil:
- o.currentScope = scope
break
else:
- 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)
- if result != nil:
- o.currentScope = nil
- o.importIdx = i
- return result
- return nil
-
+ o.scope = o.scope.parent
+ if o.scope == nil: break
of nkSym:
result = n.sym
o.mode = oimDone
of nkDotExpr:
o.mode = oimOtherModule
- o.m = qualifiedLookUp(c, n[0], {checkUndeclared, checkModule})
+ o.m = qualifiedLookUp(c, n.sons[0])
if o.m != nil and o.m.kind == skModule:
var ident: PIdent = nil
- if n[1].kind == nkIdent:
- ident = n[1].ident
- elif n[1].kind == nkAccQuoted:
- ident = considerQuotedIdent(c, n[1], n)
+ if n.sons[1].kind == nkIdent:
+ ident = n.sons[1].ident
+ elif n.sons[1].kind == nkAccQuoted:
+ ident = considerQuotedIdent(n.sons[1])
if ident != nil:
if o.m == c.module:
# a module may access its private members:
result = initIdentIter(o.it, c.topLevelScope.symbols,
- ident).skipAlias(n, c.config)
+ ident).skipAlias(n)
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)
else:
- noidentError(c.config, n[1], n)
- result = errorSym(c, n[1])
+ localError(n.sons[1].info, errIdentifierExpected,
+ renderTree(n.sons[1]))
+ result = errorSym(c, n.sons[1])
of nkClosedSymChoice, nkOpenSymChoice:
o.mode = oimSymChoice
- if n[0].kind == nkSym:
- result = n[0].sym
- else:
- o.mode = oimDone
- return nil
+ result = n.sons[0].sym
o.symChoiceIndex = 1
- o.marked = initIntSet()
- incl(o.marked, result.id)
+ o.inSymChoice = initIntSet()
+ incl(o.inSymChoice, result.id)
else: discard
- when false:
- if result != nil and result.kind == skStub: loadStub(result)
+ if result != nil and result.kind == skStub: loadStub(result)
proc lastOverloadScope*(o: TOverloadIter): int =
case o.mode
- of oimNoQualifier:
- result = if o.importIdx >= 0: 0
- elif o.currentScope.isNil: -1
- else: o.currentScope.depthLevel
+ of oimNoQualifier: result = if o.scope.isNil: -1 else: o.scope.depthLevel
of oimSelfModule: result = 1
of oimOtherModule: result = 0
else: result = -1
-proc nextOverloadIterImports(o: var TOverloadIter, c: PContext, n: PNode): PSym =
- assert o.currentScope == nil
- 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)
- if result != nil:
- # oh, we were wrong, some other module had the symbol, so remember that:
- o.importIdx = idx
- break
- inc idx
-
-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)
- #while result != nil and result.id in o.marked:
- # result = nextIdentIter(o.it, o.marked, c.imports[o.importIdx])
- if result != nil:
- #assert result.id notin o.marked
- return result
- inc o.importIdx
-
proc nextOverloadIter*(o: var TOverloadIter, c: PContext, n: PNode): PSym =
case o.mode
of oimDone:
result = nil
of oimNoQualifier:
- if o.currentScope != nil:
- assert o.importIdx < 0
- result = nextIdentIter(o.it, o.currentScope.symbols).skipAlias(n, c.config)
+ if o.scope != nil:
+ result = nextIdentIter(o.it, o.scope.symbols).skipAlias(n)
while result == nil:
- o.currentScope = o.currentScope.parent
- if o.currentScope != nil:
- result = initIdentIter(o.it, o.currentScope.symbols, o.it.name).skipAlias(n, c.config)
- # BUGFIX: o.it.name <-> n.ident
- 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)
- 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)
- if result == nil:
- result = nextOverloadIterImports(o, c, n)
+ o.scope = o.scope.parent
+ if o.scope == nil: break
+ result = initIdentIter(o.it, o.scope.symbols, o.it.name).skipAlias(n)
+ # BUGFIX: o.it.name <-> n.ident
else:
result = nil
of oimSelfModule:
- result = nextIdentIter(o.it, c.topLevelScope.symbols).skipAlias(n, c.config)
+ result = nextIdentIter(o.it, c.topLevelScope.symbols).skipAlias(n)
of oimOtherModule:
- result = nextModuleIter(o.mit, c.graph).skipAlias(n, c.config)
+ result = nextIdentIter(o.it, o.m.tab).skipAlias(n)
of oimSymChoice:
- if o.symChoiceIndex < n.len:
- result = n[o.symChoiceIndex].sym
- incl(o.marked, result.id)
+ if o.symChoiceIndex < sonsLen(n):
+ result = n.sons[o.symChoiceIndex].sym
+ incl(o.inSymChoice, result.id)
inc o.symChoiceIndex
elif n.kind == nkOpenSymChoice:
# try 'local' symbols too for Koenig's lookup:
o.mode = oimSymChoiceLocalLookup
- o.currentScope = c.currentScope
- result = firstIdentExcluding(o.it, o.currentScope.symbols,
- n[0].sym.name, o.marked).skipAlias(n, c.config)
+ o.scope = c.currentScope
+ result = firstIdentExcluding(o.it, o.scope.symbols,
+ n.sons[0].sym.name, o.inSymChoice).skipAlias(n)
while result == nil:
- o.currentScope = o.currentScope.parent
- if o.currentScope != nil:
- result = firstIdentExcluding(o.it, o.currentScope.symbols,
- n[0].sym.name, o.marked).skipAlias(n, c.config)
- else:
- o.importIdx = 0
- result = symChoiceExtension(o, c, n)
- break
- if result != nil:
- incl o.marked, result.id
+ o.scope = o.scope.parent
+ if o.scope == nil: break
+ result = firstIdentExcluding(o.it, o.scope.symbols,
+ n.sons[0].sym.name, o.inSymChoice).skipAlias(n)
of oimSymChoiceLocalLookup:
- if o.currentScope != nil:
- result = nextIdentExcluding(o.it, o.currentScope.symbols, o.marked).skipAlias(n, c.config)
- while result == nil:
- o.currentScope = o.currentScope.parent
- if o.currentScope != nil:
- result = firstIdentExcluding(o.it, o.currentScope.symbols,
- n[0].sym.name, o.marked).skipAlias(n, c.config)
- else:
- o.importIdx = 0
- result = symChoiceExtension(o, c, n)
- break
- if result != nil:
- incl o.marked, result.id
+ result = nextIdentExcluding(o.it, o.scope.symbols, o.inSymChoice).skipAlias(n)
+ while result == nil:
+ o.scope = o.scope.parent
+ if o.scope == nil: break
+ result = firstIdentExcluding(o.it, o.scope.symbols,
+ n.sons[0].sym.name, o.inSymChoice).skipAlias(n)
- elif o.importIdx < c.imports.len:
- result = nextIdentIter(o.mit, o.marked, c.imports[o.importIdx], c.graph).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)
- if result == nil:
- inc o.importIdx
- result = symChoiceExtension(o, c, n)
+ if result != nil and result.kind == skStub: loadStub(result)
- when false:
- if result != nil and result.kind == skStub: loadStub(result)
-
-proc pickSym*(c: PContext, n: PNode; kinds: set[TSymKind];
+proc pickSym*(c: PContext, n: PNode; kind: TSymKind;
flags: TSymFlags = {}): PSym =
var o: TOverloadIter
var a = initOverloadIter(o, c, n)
while a != nil:
- if a.kind in kinds and flags <= a.flags:
- if result == nil: result = a
- else: return nil # ambiguous
+ if a.kind == kind and flags <= a.flags:
+ return a
a = nextOverloadIter(o, c, n)
-
diff --git a/compiler/lowerings.nim b/compiler/lowerings.nim
index fc66fc9fa..7a5c7f44b 100644
--- a/compiler/lowerings.nim
+++ b/compiler/lowerings.nim
@@ -12,375 +12,629 @@
const
genPrefix* = ":tmp" # prefix for generated names
-import ast, astalgo, types, idents, magicsys, msgs, options, modulegraphs,
- lineinfos
-
-when defined(nimPreviewSlimSystem):
- import std/assertions
+import ast, astalgo, types, idents, magicsys, msgs, options
+from trees import getMagic
proc newDeref*(n: PNode): PNode {.inline.} =
- result = newNodeIT(nkHiddenDeref, n.info, n.typ[0])
- result.add n
+ result = newNodeIT(nkHiddenDeref, n.info, n.typ.sons[0])
+ addSon(result, n)
-proc newTupleAccess*(g: ModuleGraph; tup: PNode, i: int): PNode =
- if tup.kind == nkHiddenAddr:
- result = newNodeIT(nkHiddenAddr, tup.info, tup.typ.skipTypes(abstractInst+{tyPtr, tyVar, tyLent}))
- result.add newNodeIT(nkBracketExpr, tup.info, tup.typ.skipTypes(abstractInst+{tyPtr, tyVar, tyLent})[i])
- result[0].add tup[0]
- var lit = newNodeIT(nkIntLit, tup.info, getSysType(g, tup.info, tyInt))
- lit.intVal = i
- result[0].add lit
- else:
- result = newNodeIT(nkBracketExpr, tup.info, tup.typ.skipTypes(
- abstractInst)[i])
- result.add copyTree(tup)
- var lit = newNodeIT(nkIntLit, tup.info, getSysType(g, tup.info, tyInt))
- lit.intVal = i
- result.add lit
+proc newTupleAccess*(tup: PNode, i: int): PNode =
+ result = newNodeIT(nkBracketExpr, tup.info, tup.typ.skipTypes(
+ abstractInst).sons[i])
+ addSon(result, copyTree(tup))
+ var lit = newNodeIT(nkIntLit, tup.info, getSysType(tyInt))
+ lit.intVal = i
+ addSon(result, lit)
proc addVar*(father, v: PNode) =
var vpart = newNodeI(nkIdentDefs, v.info, 3)
- vpart[0] = v
- vpart[1] = newNodeI(nkEmpty, v.info)
- vpart[2] = vpart[1]
- father.add vpart
+ vpart.sons[0] = v
+ vpart.sons[1] = ast.emptyNode
+ vpart.sons[2] = ast.emptyNode
+ addSon(father, vpart)
-proc addVar*(father, v, value: PNode) =
- var vpart = newNodeI(nkIdentDefs, v.info, 3)
- vpart[0] = v
- vpart[1] = newNodeI(nkEmpty, v.info)
- vpart[2] = value
- father.add vpart
-
-proc newAsgnStmt*(le, ri: PNode): PNode =
+proc newAsgnStmt(le, ri: PNode): PNode =
result = newNodeI(nkAsgn, le.info, 2)
- result[0] = le
- result[1] = ri
+ result.sons[0] = le
+ result.sons[1] = ri
-proc newFastAsgnStmt*(le, ri: PNode): PNode =
+proc newFastAsgnStmt(le, ri: PNode): PNode =
result = newNodeI(nkFastAsgn, le.info, 2)
- result[0] = le
- result[1] = ri
+ result.sons[0] = le
+ result.sons[1] = ri
-proc newFastMoveStmt*(g: ModuleGraph, le, ri: PNode): PNode =
- result = newNodeI(nkFastAsgn, le.info, 2)
- result[0] = le
- result[1] = newNodeIT(nkCall, ri.info, ri.typ)
- result[1].add newSymNode(getSysMagic(g, ri.info, "move", mMove))
- result[1].add ri
-
-proc lowerTupleUnpacking*(g: ModuleGraph; n: PNode; idgen: IdGenerator; owner: PSym): PNode =
+proc lowerTupleUnpacking*(n: PNode; owner: PSym): PNode =
assert n.kind == nkVarTuple
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 v = newNodeI(nkVarSection, value.info)
- if not avoidTemp:
- v.addVar(tempAsNode, value)
- result.add(v)
-
- for i in 0.. nothing to do
+# - a proc returning GC'ed memory --> requires a flowVar
+# - a proc returning non GC'ed memory --> pass as hidden 'var' parameter
+# - not in a parallel environment --> requires a flowVar for memory safety
+type
+ TSpawnResult* = enum
+ srVoid, srFlowVar, srByVar
+ TFlowVarKind = enum
+ fvInvalid # invalid type T for 'FlowVar[T]'
+ fvGC # FlowVar of a GC'ed type
+ fvBlob # FlowVar of a blob type
+
+proc spawnResult*(t: PType; inParallel: bool): TSpawnResult =
+ if t.isEmptyType: srVoid
+ elif inParallel and not containsGarbageCollectedRef(t): srByVar
+ else: srFlowVar
+
+proc flowVarKind(t: PType): TFlowVarKind =
+ if t.skipTypes(abstractInst).kind in {tyRef, tyString, tySequence}: fvGC
+ elif containsGarbageCollectedRef(t): fvInvalid
+ else: fvBlob
+
+proc typeNeedsNoDeepCopy(t: PType): bool =
+ var t = t.skipTypes(abstractInst)
+ # for the tconvexhull example (and others) we're a bit lax here and pretend
+ # seqs and strings are *by value* only and 'shallow' doesn't exist!
+ if t.kind == tyString: return true
+ # note that seq[T] is fine, but 'var seq[T]' is not, so we need to skip 'var'
+ # for the stricter check and likewise we can skip 'seq' for a less
+ # strict check:
+ if t.kind in {tyVar, tySequence}: t = t.sons[0]
+ result = not containsGarbageCollectedRef(t)
+
+proc addLocalVar(varSection, varInit: PNode; owner: PSym; typ: PType;
+ v: PNode; useShallowCopy=false): PSym =
+ result = newSym(skTemp, getIdent(genPrefix), owner, varSection.info)
+ result.typ = typ
+ incl(result.flags, sfFromGeneric)
+
+ var vpart = newNodeI(nkIdentDefs, varSection.info, 3)
+ vpart.sons[0] = newSymNode(result)
+ vpart.sons[1] = ast.emptyNode
+ vpart.sons[2] = if varInit.isNil: v else: ast.emptyNode
+ varSection.add vpart
+ if varInit != nil:
+ if useShallowCopy and typeNeedsNoDeepCopy(typ):
+ varInit.add newFastAsgnStmt(newSymNode(result), v)
+ else:
+ let deepCopyCall = newNodeI(nkCall, varInit.info, 3)
+ deepCopyCall.sons[0] = newSymNode(createMagic("deepCopy", mDeepCopy))
+ deepCopyCall.sons[1] = newSymNode(result)
+ deepCopyCall.sons[2] = v
+ varInit.add deepCopyCall
+
+discard """
+We generate roughly this:
+
+proc f_wrapper(thread, args) =
+ barrierEnter(args.barrier) # for parallel statement
+ var a = args.a # thread transfer; deepCopy or shallowCopy or no copy
+ # depending on whether we're in a 'parallel' statement
+ var b = args.b
+ var fv = args.fv
+
+ fv.owner = thread # optional
+ nimArgsPassingDone() # signal parent that the work is done
+ #
+ args.fv.blob = f(a, b, ...)
+ nimFlowVarSignal(args.fv)
+
+ # - or -
+ f(a, b, ...)
+ barrierLeave(args.barrier) # for parallel statement
+
+stmtList:
+ var scratchObj
+ scratchObj.a = a
+ scratchObj.b = b
+
+ nimSpawn(f_wrapper, addr scratchObj)
+ scratchObj.fv # optional
+
+"""
+
+proc createWrapperProc(f: PNode; threadParam, argsParam: PSym;
+ varSection, varInit, call, barrier, fv: PNode;
+ spawnKind: TSpawnResult): PSym =
+ var body = newNodeI(nkStmtList, f.info)
+ var threadLocalBarrier: PSym
+ if barrier != nil:
+ var varSection2 = newNodeI(nkVarSection, barrier.info)
+ threadLocalBarrier = addLocalVar(varSection2, nil, argsParam.owner,
+ barrier.typ, barrier)
+ body.add varSection2
+ body.add callCodegenProc("barrierEnter", threadLocalBarrier.newSymNode)
+ var threadLocalProm: PSym
+ if spawnKind == srByVar:
+ threadLocalProm = addLocalVar(varSection, nil, argsParam.owner, fv.typ, fv)
+ elif fv != nil:
+ internalAssert fv.typ.kind == tyGenericInst
+ threadLocalProm = addLocalVar(varSection, nil, argsParam.owner, fv.typ, fv)
+ body.add varSection
+ body.add varInit
+ if fv != nil and spawnKind != srByVar:
+ # generate:
+ # fv.owner = threadParam
+ body.add newAsgnStmt(indirectAccess(threadLocalProm.newSymNode,
+ "owner", fv.info), threadParam.newSymNode)
+
+ body.add callCodegenProc("nimArgsPassingDone", threadParam.newSymNode)
+ if spawnKind == srByVar:
+ body.add newAsgnStmt(genDeref(threadLocalProm.newSymNode), call)
+ elif fv != nil:
+ let fk = fv.typ.sons[1].flowVarKind
+ if fk == fvInvalid:
+ localError(f.info, "cannot create a flowVar of type: " &
+ typeToString(fv.typ.sons[1]))
+ body.add newAsgnStmt(indirectAccess(threadLocalProm.newSymNode,
+ if fk == fvGC: "data" else: "blob", fv.info), call)
+ if fk == fvGC:
+ let incRefCall = newNodeI(nkCall, fv.info, 2)
+ incRefCall.sons[0] = newSymNode(createMagic("GCref", mGCref))
+ incRefCall.sons[1] = indirectAccess(threadLocalProm.newSymNode,
+ "data", fv.info)
+ body.add incRefCall
+ if barrier == nil:
+ # by now 'fv' is shared and thus might have beeen overwritten! we need
+ # to use the thread-local view instead:
+ body.add callCodegenProc("nimFlowVarSignal", threadLocalProm.newSymNode)
+ else:
+ body.add call
+ if barrier != nil:
+ body.add callCodegenProc("barrierLeave", threadLocalBarrier.newSymNode)
+
+ var params = newNodeI(nkFormalParams, f.info)
+ params.add emptyNode
+ params.add threadParam.newSymNode
+ params.add argsParam.newSymNode
+
+ var t = newType(tyProc, threadParam.owner)
+ t.rawAddSon nil
+ t.rawAddSon threadParam.typ
+ t.rawAddSon argsParam.typ
+ t.n = newNodeI(nkFormalParams, f.info)
+ t.n.add newNodeI(nkEffectList, f.info)
+ t.n.add threadParam.newSymNode
+ t.n.add argsParam.newSymNode
+
+ let name = (if f.kind == nkSym: f.sym.name.s else: genPrefix) & "Wrapper"
+ result = newSym(skProc, getIdent(name), argsParam.owner, f.info)
+ result.ast = newProcNode(nkProcDef, f.info, body, params, newSymNode(result))
+ result.typ = t
+
+proc createCastExpr(argsParam: PSym; objType: PType): PNode =
+ result = newNodeI(nkCast, argsParam.info)
+ result.add emptyNode
+ result.add newSymNode(argsParam)
+ result.typ = newType(tyPtr, objType.owner)
+ result.typ.rawAddSon(objType)
+
+proc setupArgsForConcurrency(n: PNode; objType: PType; scratchObj: PSym,
+ castExpr, call,
+ varSection, varInit, result: PNode) =
+ let formals = n[0].typ.n
+ let tmpName = getIdent(genPrefix)
+ for i in 1 .. 16) and
+ n.getRoot != nil:
+ # it is more efficient to pass a pointer instead:
+ let a = genAddrOf(n)
+ field.typ = a.typ
+ objType.addField(field)
+ result.add newFastAsgnStmt(newDotExpr(scratchObj, field), a)
+ let threadLocal = addLocalVar(varSection,nil, objType.owner, field.typ,
+ indirectAccess(castExpr, field, n.info),
+ useShallowCopy=true)
+ call.add(genDeref(threadLocal.newSymNode))
+ else:
+ # boring case
+ field.typ = argType
+ objType.addField(field)
+ result.add newFastAsgnStmt(newDotExpr(scratchObj, field), n)
+ let threadLocal = addLocalVar(varSection, varInit,
+ objType.owner, field.typ,
+ indirectAccess(castExpr, field, n.info),
+ useShallowCopy=true)
+ call.add(threadLocal.newSymNode)
+
+proc wrapProcForSpawn*(owner: PSym; spawnExpr: PNode; retType: PType;
+ barrier, dest: PNode = nil): PNode =
+ # if 'barrier' != nil, then it is in a 'parallel' section and we
+ # generate quite different code
+ let n = spawnExpr[^2]
+ let spawnKind = spawnResult(retType, barrier!=nil)
+ case spawnKind
+ of srVoid:
+ internalAssert dest == nil
+ result = newNodeI(nkStmtList, n.info)
+ of srFlowVar:
+ internalAssert dest == nil
+ result = newNodeIT(nkStmtListExpr, n.info, retType)
+ of srByVar:
+ if dest == nil: localError(n.info, "'spawn' must not be discarded")
+ result = newNodeI(nkStmtList, n.info)
+
+ if n.kind notin nkCallKinds:
+ localError(n.info, "'spawn' takes a call expression")
+ return
+ if optThreadAnalysis in gGlobalOptions:
+ if {tfThread, tfNoSideEffect} * n[0].typ.flags == {}:
+ localError(n.info, "'spawn' takes a GC safe call expression")
+ var
+ threadParam = newSym(skParam, getIdent"thread", owner, n.info)
+ argsParam = newSym(skParam, getIdent"args", owner, n.info)
+ block:
+ let ptrType = getSysType(tyPointer)
+ threadParam.typ = ptrType
+ argsParam.typ = ptrType
+ argsParam.position = 1
+
+ var objType = createObj(owner, n.info)
+ incl(objType.flags, tfFinal)
+ let castExpr = createCastExpr(argsParam, objType)
+
+ var scratchObj = newSym(skVar, getIdent"scratch", owner, n.info)
+ block:
+ scratchObj.typ = objType
+ incl(scratchObj.flags, sfFromGeneric)
+ var varSectionB = newNodeI(nkVarSection, n.info)
+ varSectionB.addVar(scratchObj.newSymNode)
+ result.add varSectionB
+
+ var call = newNodeIT(nkCall, n.info, n.typ)
+ var fn = n.sons[0]
+ # templates and macros are in fact valid here due to the nature of
+ # the transformation:
+ if not (fn.kind == nkSym and fn.sym.kind in {skProc, skTemplate, skMacro,
+ skMethod, skConverter}):
+ # for indirect calls we pass the function pointer in the scratchObj
+ var argType = n[0].typ.skipTypes(abstractInst)
+ var field = newSym(skField, getIdent"fn", owner, n.info)
+ field.typ = argType
+ objType.addField(field)
+ result.add newFastAsgnStmt(newDotExpr(scratchObj, field), n[0])
+ fn = indirectAccess(castExpr, field, n.info)
+ elif fn.kind == nkSym and fn.sym.kind == skIterator:
+ localError(n.info, "iterator in spawn environment is not allowed")
+ elif fn.typ.callConv == ccClosure:
+ localError(n.info, "closure in spawn environment is not allowed")
+
+ call.add(fn)
+ var varSection = newNodeI(nkVarSection, n.info)
+ var varInit = newNodeI(nkStmtList, n.info)
+ if barrier.isNil:
+ setupArgsForConcurrency(n, objType, scratchObj, castExpr, call,
+ varSection, varInit, result)
else:
- result = newNodeI(nkCall, n.info, 2)
- result.typ = getSysType(g, n.info, tyInt)
- result[0] = newSymNode(getSysMagic(g, n.info, "len", mLengthSeq))
- result[1] = n
+ setupArgsForParallelism(n, objType, scratchObj, castExpr, call,
+ varSection, varInit, result)
+ var barrierAsExpr: PNode = nil
+ if barrier != nil:
+ let typ = newType(tyPtr, owner)
+ typ.rawAddSon(magicsys.getCompilerProc("Barrier").typ)
+ var field = newSym(skField, getIdent"barrier", owner, n.info)
+ field.typ = typ
+ objType.addField(field)
+ result.add newFastAsgnStmt(newDotExpr(scratchObj, field), barrier)
+ barrierAsExpr = indirectAccess(castExpr, field, n.info)
+
+ var fvField, fvAsExpr: PNode = nil
+ if spawnKind == srFlowVar:
+ var field = newSym(skField, getIdent"fv", owner, n.info)
+ field.typ = retType
+ objType.addField(field)
+ fvField = newDotExpr(scratchObj, field)
+ fvAsExpr = indirectAccess(castExpr, field, n.info)
+ # create flowVar:
+ result.add newFastAsgnStmt(fvField, callProc(spawnExpr[^1]))
+ if barrier == nil:
+ result.add callCodegenProc("nimFlowVarCreateSemaphore", fvField)
+
+ elif spawnKind == srByVar:
+ var field = newSym(skField, getIdent"fv", owner, n.info)
+ field.typ = newType(tyPtr, objType.owner)
+ field.typ.rawAddSon(retType)
+ objType.addField(field)
+ fvAsExpr = indirectAccess(castExpr, field, n.info)
+ result.add newFastAsgnStmt(newDotExpr(scratchObj, field), genAddrOf(dest))
+
+ let wrapper = createWrapperProc(fn, threadParam, argsParam,
+ varSection, varInit, call,
+ barrierAsExpr, fvAsExpr, spawnKind)
+ result.add callCodegenProc("nimSpawn" & $spawnExpr.len, wrapper.newSymNode,
+ genAddrOf(scratchObj.newSymNode), nil, spawnExpr)
+
+ if spawnKind == srFlowVar: result.add fvField
diff --git a/compiler/macrocacheimpl.nim b/compiler/macrocacheimpl.nim
deleted file mode 100644
index c869c2289..000000000
--- a/compiler/macrocacheimpl.nim
+++ /dev/null
@@ -1,44 +0,0 @@
-#
-#
-# The Nim Compiler
-# (c) Copyright 2018 Andreas Rumpf
-#
-# See the file "copying.txt", included in this
-# distribution, for details about the copyright.
-#
-
-## This module implements helpers for the macro cache.
-
-import lineinfos, ast, vmdef
-
-proc append(c: PCtx; n: PNode) =
- c.vmstateDiff.add((c.module, n))
-
-proc recordInc*(c: PCtx; info: TLineInfo; key: string; by: BiggestInt) =
- var recorded = newNodeI(nkReplayAction, info)
- recorded.add newStrNode("inc", info)
- recorded.add newStrNode(key, info)
- recorded.add newIntNode(nkIntLit, by)
- c.append(recorded)
-
-proc recordPut*(c: PCtx; info: TLineInfo; key: string; k: string; val: PNode) =
- var recorded = newNodeI(nkReplayAction, info)
- recorded.add newStrNode("put", info)
- recorded.add newStrNode(key, info)
- recorded.add newStrNode(k, info)
- recorded.add copyTree(val)
- c.append(recorded)
-
-proc recordAdd*(c: PCtx; info: TLineInfo; key: string; val: PNode) =
- var recorded = newNodeI(nkReplayAction, info)
- recorded.add newStrNode("add", info)
- recorded.add newStrNode(key, info)
- recorded.add copyTree(val)
- c.append(recorded)
-
-proc recordIncl*(c: PCtx; info: TLineInfo; key: string; val: PNode) =
- var recorded = newNodeI(nkReplayAction, info)
- recorded.add newStrNode("incl", info)
- recorded.add newStrNode(key, info)
- recorded.add copyTree(val)
- c.append(recorded)
diff --git a/compiler/magicsys.nim b/compiler/magicsys.nim
index ab234a2a8..13b365d04 100644
--- a/compiler/magicsys.nim
+++ b/compiler/magicsys.nim
@@ -10,47 +10,57 @@
# Built-in types and compilerprocs are registered here.
import
- ast, astalgo, msgs, platform, idents,
- modulegraphs, lineinfos
+ ast, astalgo, hashes, msgs, platform, nversion, times, idents, rodread
-export createMagic
+var systemModule*: PSym
-proc nilOrSysInt*(g: ModuleGraph): PType = g.sysTypes[tyInt]
+var
+ gSysTypes: array[TTypeKind, PType]
+ compilerprocs: TStrTable
+ exposed: TStrTable
-proc newSysType(g: ModuleGraph; kind: TTypeKind, size: int): PType =
- result = newType(kind, nextTypeId(g.idgen), g.systemModule)
+proc nilOrSysInt*: PType = gSysTypes[tyInt]
+
+proc registerSysType*(t: PType) =
+ if gSysTypes[t.kind] == nil: gSysTypes[t.kind] = t
+
+proc newSysType(kind: TTypeKind, size: int): PType =
+ result = newType(kind, systemModule)
result.size = size
result.align = size.int16
-proc getSysSym*(g: ModuleGraph; info: TLineInfo; name: string): PSym =
- result = systemModuleSym(g, getIdent(g.cache, name))
+proc getSysSym*(name: string): PSym =
+ result = strTableGet(systemModule.tab, getIdent(name))
if result == nil:
- localError(g.config, info, "system module needs: " & name)
- result = newSym(skError, getIdent(g.cache, name), nextSymId(g.idgen), g.systemModule, g.systemModule.info, {})
- result.typ = newType(tyError, nextTypeId(g.idgen), g.systemModule)
+ rawMessage(errSystemNeeds, name)
+ result = newSym(skError, getIdent(name), systemModule, systemModule.info)
+ result.typ = newType(tyError, systemModule)
+ if result.kind == skStub: loadStub(result)
if result.kind == skAlias: result = result.owner
-proc getSysMagic*(g: ModuleGraph; info: TLineInfo; name: string, m: TMagic): PSym =
- let id = getIdent(g.cache, name)
- for r in systemModuleSyms(g, id):
+proc getSysMagic*(name: string, m: TMagic): PSym =
+ var ti: TIdentIter
+ let id = getIdent(name)
+ var r = initIdentIter(ti, systemModule.tab, id)
+ while r != nil:
+ if r.kind == skStub: loadStub(r)
if r.magic == m:
# prefer the tyInt variant:
- if r.typ[0] != nil and r.typ[0].kind == tyInt: return r
+ if r.typ.sons[0].kind == tyInt: return r
result = r
+ r = nextIdentIter(ti, systemModule.tab)
if result != nil: return result
- localError(g.config, info, "system module needs: " & name)
- result = newSym(skError, id, nextSymId(g.idgen), g.systemModule, g.systemModule.info, {})
- result.typ = newType(tyError, nextTypeId(g.idgen), g.systemModule)
+ rawMessage(errSystemNeeds, name)
+ result = newSym(skError, id, systemModule, systemModule.info)
+ result.typ = newType(tyError, systemModule)
-proc sysTypeFromName*(g: ModuleGraph; info: TLineInfo; name: string): PType =
- result = getSysSym(g, info, name).typ
+proc sysTypeFromName*(name: string): PType =
+ result = getSysSym(name).typ
-proc getSysType*(g: ModuleGraph; info: TLineInfo; kind: TTypeKind): PType =
- template sysTypeFromName(s: string): untyped = sysTypeFromName(g, info, s)
- result = g.sysTypes[kind]
+proc getSysType*(kind: TTypeKind): PType =
+ result = gSysTypes[kind]
if result == nil:
case kind
- of tyVoid: result = sysTypeFromName("void")
of tyInt: result = sysTypeFromName("int")
of tyInt8: result = sysTypeFromName("int8")
of tyInt16: result = sysTypeFromName("int16")
@@ -63,90 +73,119 @@ proc getSysType*(g: ModuleGraph; info: TLineInfo; kind: TTypeKind): PType =
of tyUInt64: result = sysTypeFromName("uint64")
of tyFloat: result = sysTypeFromName("float")
of tyFloat32: result = sysTypeFromName("float32")
- of tyFloat64: result = sysTypeFromName("float64")
+ of tyFloat64: return sysTypeFromName("float64")
of tyFloat128: result = sysTypeFromName("float128")
of tyBool: result = sysTypeFromName("bool")
of tyChar: result = sysTypeFromName("char")
of tyString: result = sysTypeFromName("string")
- of tyCstring: result = sysTypeFromName("cstring")
+ of tyCString: result = sysTypeFromName("cstring")
of tyPointer: result = sysTypeFromName("pointer")
- of tyNil: result = newSysType(g, tyNil, g.config.target.ptrSize)
- else: internalError(g.config, "request for typekind: " & $kind)
- g.sysTypes[kind] = result
+ of tyNil: result = newSysType(tyNil, ptrSize)
+ else: internalError("request for typekind: " & $kind)
+ gSysTypes[kind] = result
if result.kind != kind:
- if kind == tyFloat64 and result.kind == tyFloat: discard # because of aliasing
- else:
- internalError(g.config, "wanted: " & $kind & " got: " & $result.kind)
- if result == nil: internalError(g.config, "type not found: " & $kind)
+ internalError("wanted: " & $kind & " got: " & $result.kind)
+ if result == nil: internalError("type not found: " & $kind)
-proc resetSysTypes*(g: ModuleGraph) =
- g.systemModule = nil
- initStrTable(g.compilerprocs)
- initStrTable(g.exposed)
- for i in low(g.sysTypes)..high(g.sysTypes):
- g.sysTypes[i] = nil
+var
+ intTypeCache: array[-5..64, PType]
-proc getFloatLitType*(g: ModuleGraph; literal: PNode): PType =
+proc resetSysTypes* =
+ systemModule = nil
+ initStrTable(compilerprocs)
+ initStrTable(exposed)
+ for i in low(gSysTypes)..high(gSysTypes):
+ gSysTypes[i] = nil
+
+ for i in low(intTypeCache)..high(intTypeCache):
+ intTypeCache[i] = nil
+
+proc getIntLitType*(literal: PNode): PType =
+ # we cache some common integer literal types for performance:
+ let value = literal.intVal
+ if value >= low(intTypeCache) and value <= high(intTypeCache):
+ result = intTypeCache[value.int]
+ if result == nil:
+ let ti = getSysType(tyInt)
+ result = copyType(ti, ti.owner, false)
+ result.n = literal
+ intTypeCache[value.int] = result
+ else:
+ let ti = getSysType(tyInt)
+ result = copyType(ti, ti.owner, false)
+ result.n = literal
+
+proc getFloatLitType*(literal: PNode): PType =
# for now we do not cache these:
- result = newSysType(g, tyFloat, size=8)
+ result = newSysType(tyFloat, size=8)
result.n = literal
-proc skipIntLit*(t: PType; id: IdGenerator): PType {.inline.} =
- if t.n != nil and t.kind in {tyInt, tyFloat}:
- result = copyType(t, nextTypeId(id), t.owner)
- result.n = nil
- else:
- result = t
+proc skipIntLit*(t: PType): PType {.inline.} =
+ if t.n != nil:
+ if t.kind in {tyInt, tyFloat}:
+ return getSysType(t.kind)
+ result = t
-proc addSonSkipIntLit*(father, son: PType; id: IdGenerator) =
- let s = son.skipIntLit(id)
- father.sons.add(s)
+proc addSonSkipIntLit*(father, son: PType) =
+ if isNil(father.sons): father.sons = @[]
+ let s = son.skipIntLit
+ add(father.sons, s)
propagateToOwner(father, s)
-proc getCompilerProc*(g: ModuleGraph; name: string): PSym =
- let ident = getIdent(g.cache, name)
- result = strTableGet(g.compilerprocs, ident)
+proc setIntLitType*(result: PNode) =
+ let i = result.intVal
+ case platform.intSize
+ of 8: result.typ = getIntLitType(result)
+ of 4:
+ if i >= low(int32) and i <= high(int32):
+ result.typ = getIntLitType(result)
+ else:
+ result.typ = getSysType(tyInt64)
+ of 2:
+ if i >= low(int16) and i <= high(int16):
+ result.typ = getIntLitType(result)
+ elif i >= low(int32) and i <= high(int32):
+ result.typ = getSysType(tyInt32)
+ else:
+ result.typ = getSysType(tyInt64)
+ of 1:
+ # 8 bit CPUs are insane ...
+ if i >= low(int8) and i <= high(int8):
+ result.typ = getIntLitType(result)
+ elif i >= low(int16) and i <= high(int16):
+ result.typ = getSysType(tyInt16)
+ elif i >= low(int32) and i <= high(int32):
+ result.typ = getSysType(tyInt32)
+ else:
+ result.typ = getSysType(tyInt64)
+ else: internalError(result.info, "invalid int size")
+
+proc getCompilerProc*(name: string): PSym =
+ let ident = getIdent(name)
+ result = strTableGet(compilerprocs, ident)
if result == nil:
- result = loadCompilerProc(g, name)
+ result = strTableGet(rodCompilerprocs, ident)
+ if result != nil:
+ strTableAdd(compilerprocs, result)
+ if result.kind == skStub: loadStub(result)
+ if result.kind == skAlias: result = result.owner
-proc registerCompilerProc*(g: ModuleGraph; s: PSym) =
- strTableAdd(g.compilerprocs, s)
+proc registerCompilerProc*(s: PSym) =
+ strTableAdd(compilerprocs, s)
-proc registerNimScriptSymbol*(g: ModuleGraph; s: PSym) =
+proc registerNimScriptSymbol*(s: PSym) =
# Nimscript symbols must be al unique:
- let conflict = strTableGet(g.exposed, s.name)
+ let conflict = strTableGet(exposed, s.name)
if conflict == nil:
- strTableAdd(g.exposed, s)
+ strTableAdd(exposed, s)
else:
- localError(g.config, s.info,
- "symbol conflicts with other .exportNims symbol at: " & g.config$conflict.info)
+ localError(s.info, "symbol conflicts with other .exportNims symbol at: " &
+ $conflict.info)
-proc getNimScriptSymbol*(g: ModuleGraph; name: string): PSym =
- strTableGet(g.exposed, getIdent(g.cache, name))
-
-proc resetNimScriptSymbols*(g: ModuleGraph) = initStrTable(g.exposed)
-
-proc getMagicEqSymForType*(g: ModuleGraph; t: PType; info: TLineInfo): PSym =
- case t.kind
- of tyInt, tyInt8, tyInt16, tyInt32, tyInt64,
- tyUInt, tyUInt8, tyUInt16, tyUInt32, tyUInt64:
- result = getSysMagic(g, info, "==", mEqI)
- of tyEnum:
- result = getSysMagic(g, info, "==", mEqEnum)
- of tyBool:
- result = getSysMagic(g, info, "==", mEqB)
- of tyRef, tyPtr, tyPointer:
- result = getSysMagic(g, info, "==", mEqRef)
- of tyString:
- result = getSysMagic(g, info, "==", mEqStr)
- of tyChar:
- result = getSysMagic(g, info, "==", mEqCh)
- of tySet:
- result = getSysMagic(g, info, "==", mEqSet)
- of tyProc:
- result = getSysMagic(g, info, "==", mEqProc)
- else:
- globalError(g.config, info,
- "can't find magic equals operator for type kind " & $t.kind)
+proc getNimScriptSymbol*(name: string): PSym =
+ strTableGet(exposed, getIdent(name))
+proc resetNimScriptSymbols*() = initStrTable(exposed)
+initStrTable(compilerprocs)
+initStrTable(exposed)
diff --git a/compiler/main.nim b/compiler/main.nim
index ff870a14a..b1b9006bd 100644
--- a/compiler/main.nim
+++ b/compiler/main.nim
@@ -9,419 +9,371 @@
# implements the command dispatcher and several commands
-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,
- modules,
- modulegraphs, lineinfos, pathutils, vmprofiler
+ llstream, strutils, ast, astalgo, lexer, syntaxes, renderer, options, msgs,
+ os, condsyms, rodread, rodwrite, times,
+ wordrecg, sem, semdata, idents, passes, docgen, extccomp,
+ cgen, jsgen, json, nversion,
+ platform, nimconf, importer, passaux, depends, vm, vmdef, types, idgen,
+ docgen2, service, parser, modules, ccgutils, sigmatch, ropes, lists
+from magicsys import systemModule, resetSysTypes
-when defined(nimPreviewSlimSystem):
- import std/[syncio, assertions]
+proc rodPass =
+ if optSymbolFiles in gGlobalOptions:
+ registerPass(rodwritePass)
-import ic / [cbackend, integrity, navigator]
-from ic / ic import rodViewer
+proc codegenPass =
+ registerPass cgenPass
-import pipelines
+proc semanticPasses =
+ registerPass verbosePass
+ registerPass semPass
-when not defined(leanCompiler):
- import docgen
+proc commandGenDepend =
+ semanticPasses()
+ registerPass(gendependPass)
+ registerPass(cleanupPass)
+ compileProject()
+ generateDot(gProjectFull)
+ execExternalProgram("dot -Tpng -o" & changeFileExt(gProjectFull, "png") &
+ ' ' & changeFileExt(gProjectFull, "dot"))
-proc writeDepsFile(g: ModuleGraph) =
- let fname = g.config.nimcacheDir / RelativeFile(g.config.projectName & ".deps")
- let f = open(fname.string, fmWrite)
- for m in g.ifaces:
- if m.module != nil:
- f.writeLine(toFullPath(g.config, m.module.position.FileIndex))
- for k in g.inclToMod.keys:
- if g.getModule(k).isNil: # don't repeat includes which are also modules
- f.writeLine(toFullPath(g.config, k))
- f.close()
+proc commandCheck =
+ msgs.gErrorMax = high(int) # do not stop after first error
+ defineSymbol("nimcheck")
+ semanticPasses() # use an empty backend for semantic checking only
+ rodPass()
+ compileProject()
-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 commandDoc2 =
+ msgs.gErrorMax = high(int) # do not stop after first error
+ semanticPasses()
+ registerPass(docgen2Pass)
+ #registerPass(cleanupPass())
+ compileProject()
+ finishDoc2Pass(gProjectName)
-proc commandGenDepend(graph: ModuleGraph) =
- setPipeLinePass(graph, GenDependPass)
- compilePipelineProject(graph)
- let project = graph.config.projectFull
- writeDepsFile(graph)
- generateDot(graph, project)
+proc commandCompileToC =
+ extccomp.initVars()
+ semanticPasses()
+ registerPass(cgenPass)
+ rodPass()
+ #registerPass(cleanupPass())
- # 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")
+ compileProject()
+ cgenWriteModules()
+ if gCmd != cmdRun:
+ extccomp.callCCompiler(changeFileExt(gProjectFull, ""))
- execExternalProgram(graph.config, "dot -Tpng -o" &
- changeFileExt(project, "png").string &
- ' ' & changeFileExt(project, "dot").string)
+ if isServing:
+ # caas will keep track only of the compilation commands
+ lastCaasCmd = curCaasCmd
+ resetCgenModules()
+ for i in 0 .. 0:
+ discard compileModule(fileInfoIdx(gProjectFull), {})
else:
- setPipeLinePass(graph, SemPass)
- compilePipelineProject(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:
- 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)
-
-proc commandCompileToJS(graph: ModuleGraph) =
- let conf = graph.config
- when defined(leanCompiler):
- globalError(conf, unknownLineInfo, "compiler wasn't built with JS code generator")
- else:
- 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:
- writeDepsFile(graph)
-
-proc commandInteractive(graph: ModuleGraph) =
- graph.config.setErrorMaxHighMaybe
- 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)
- if graph.config.commandArgs.len > 0:
- discard graph.compilePipelineModule(fileInfoIdx(graph.config, graph.config.projectFull), {})
- else:
- var m = graph.makeStdinModule()
+ var m = 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(m, llStreamOpenStdIn(), nil)
-proc commandScan(cache: IdentCache, config: ConfigRef) =
- var f = addFileExt(AbsoluteFile mainCommandArg(config), NimExt)
+const evalPasses = [verbosePass, semPass, evalPass]
+
+proc evalNim(nodes: PNode, module: PSym) =
+ carryPasses(nodes, module, evalPasses)
+
+proc commandEval(exp: string) =
+ if systemModule == nil:
+ interactivePasses()
+ compileSystemModule()
+ var echoExp = "echo \"eval\\t\", " & "repr(" & exp & ")"
+ evalNim(echoExp.parseString, makeStdinModule())
+
+proc commandScan =
+ var f = addFileExt(mainCommandArg(), NimExt)
var stream = llStreamOpen(f, fmRead)
if stream != nil:
var
- L: Lexer
- tok: Token
+ L: TLexer
+ tok: TToken
initToken(tok)
- openLexer(L, f, stream, cache, config)
+ openLexer(L, f, stream)
while true:
rawGetTok(L, tok)
- printTok(config, tok)
+ printTok(tok)
if tok.tokType == tkEof: break
closeLexer(L)
else:
- rawMessage(config, errGenerated, "cannot open file: " & f.string)
+ rawMessage(errCannotOpenFile, f)
-proc commandView(graph: ModuleGraph) =
- let f = toAbsolute(mainCommandArg(graph.config), AbsoluteDir getCurrentDir()).addFileExt(RodExt)
- rodViewer(f, graph.config, graph.cache)
+proc commandSuggest =
+ if isServing:
+ # XXX: hacky work-around ahead
+ # Currently, it's possible to issue a idetools command, before
+ # issuing the first compile command. This will leave the compiler
+ # cache in a state where "no recompilation is necessary", but the
+ # cgen pass was never executed at all.
+ commandCompileToC()
+ let gDirtyBufferIdx = gTrackPos.fileIndex
+ discard compileModule(gDirtyBufferIdx, {sfDirty})
+ resetModule(gDirtyBufferIdx)
+ else:
+ msgs.gErrorMax = high(int) # do not stop after first error
+ semanticPasses()
+ rodPass()
+ # XXX: this handles the case when the dirty buffer is the main file,
+ # but doesn't handle the case when it's imported module
+ #var projFile = if gProjectMainIdx == gDirtyOriginalIdx: gDirtyBufferIdx
+ # else: gProjectMainIdx
+ compileProject() #(projFile)
+
+proc resetMemory =
+ resetCompilationLists()
+ ccgutils.resetCaches()
+ resetAllModules()
+ resetRopeCache()
+ resetSysTypes()
+ gOwners = @[]
+ for i in low(buckets)..high(buckets):
+ buckets[i] = nil
+ idAnon = nil
+
+ # XXX: clean these global vars
+ # ccgstmts.gBreakpoints
+ # ccgthreadvars.nimtv
+ # ccgthreadvars.nimtVDeps
+ # ccgthreadvars.nimtvDeclared
+ # cgendata
+ # cgmeth?
+ # condsyms?
+ # depends?
+ # lexer.gLinesCompiled
+ # msgs - error counts
+ # magicsys, when system.nim changes
+ # rodread.rodcompilerProcs
+ # rodread.gTypeTable
+ # rodread.gMods
+
+ # !! ropes.cache
+ #
+ # suggest.usageSym
+ #
+ # XXX: can we run out of IDs?
+ # XXX: detect config reloading (implement as error/require restart)
+ # XXX: options are appended (they will accumulate over time)
+ # vis = visimpl
+ when compileOption("gc", "v2"):
+ gcDebugging = true
+ echo "COLLECT 1"
+ GC_fullCollect()
+ echo "COLLECT 2"
+ GC_fullCollect()
+ echo "COLLECT 3"
+ GC_fullCollect()
+ echo GC_getStatistics()
const
+ SimulateCaasMemReset = false
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 mainCommand* =
+ when SimulateCaasMemReset:
+ gGlobalOptions.incl(optCaasEnabled)
-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
-
- conf.lastCmdTime = epochTime()
- conf.searchPaths.add(conf.libpath)
-
- proc customizeForBackend(backend: TBackend) =
- ## Sets backend specific options but don't compile to backend yet in
- ## case command doesn't require it. This must be called by all commands.
- if conf.backend == backendInvalid:
- # only set if wasn't already set, to allow override via `nim c -b:cpp`
- conf.backend = backend
-
- defineSymbol(graph.config.symbols, $conf.backend)
- case conf.backend
- of backendC:
- if conf.exc == excNone: conf.exc = excSetjmp
- of backendCpp:
- if conf.exc == excNone: conf.exc = excCpp
- of backendObjc: discard
- of backendJs:
- if conf.hcrOn:
- # XXX: At the moment, system.nim cannot be compiled in JS mode
- # with "-d:useNimRtl". The HCR option has been processed earlier
- # and it has added this define implictly, so we must undo that here.
- # A better solution might be to fix system.nim
- undefSymbol(conf.symbols, "useNimRtl")
- of backendInvalid: doAssert false
-
- proc compileToBackend() =
- customizeForBackend(conf.backend)
- setOutFile(conf)
- case conf.backend
- of backendC: commandCompileToC(graph)
- of backendCpp: commandCompileToC(graph)
- of backendObjc: commandCompileToC(graph)
- of backendJs: commandCompileToJS(graph)
- of backendInvalid: doAssert false
-
- template docLikeCmd(body) =
- when defined(leanCompiler):
- conf.quitOrRaise "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)
- 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
-
- ## process all commands
- case conf.cmd
- of cmdBackends: compileToBackend()
- of cmdTcc:
+ # In "nim serve" scenario, each command must reset the registered passes
+ clearPasses()
+ gLastCmdTime = epochTime()
+ appendStr(searchPaths, options.libpath)
+ when false: # gProjectFull.len != 0:
+ # current path is always looked first for modules
+ prependStr(searchPaths, gProjectPath)
+ setId(100)
+ case command.normalize
+ of "c", "cc", "compile", "compiletoc":
+ # compile means compileToC currently
+ gCmd = cmdCompileToC
+ commandCompileToC()
+ of "cpp", "compiletocpp":
+ gCmd = cmdCompileToCpp
+ defineSymbol("cpp")
+ commandCompileToC()
+ of "objc", "compiletooc":
+ gCmd = cmdCompileToOC
+ defineSymbol("objc")
+ commandCompileToC()
+ of "run":
+ gCmd = cmdRun
when hasTinyCBackend:
- extccomp.setCC(conf, "tcc", unknownLineInfo)
- if conf.backend != backendC:
- rawMessage(conf, errGenerated, "'run' requires c backend, got: '$1'" % $conf.backend)
- compileToBackend()
+ extccomp.setCC("tcc")
+ commandCompileToC()
else:
- rawMessage(conf, errGenerated, "'run' command not available; rebuild with -d:tinyc")
- of cmdDoc0: docLikeCmd commandDoc(cache, conf)
- of cmdDoc:
- docLikeCmd():
- conf.setNoteDefaults(warnRstRedefinitionOfLabel, 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)
- 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
- when defined(leanCompiler):
- conf.quitOrRaise "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)
- when defined(leanCompiler):
- conf.quitOrRaise "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)
- 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 cmdCtags: docLikeCmd commandTags(cache, conf)
- of cmdBuildindex: docLikeCmd commandBuildIndex(conf, $conf.projectFull, conf.outFile)
- of cmdGendepend: commandGenDepend(graph)
- of cmdDump:
- if getConfigVar(conf, "dump.format") == "json":
- wantMainModule(conf)
+ rawMessage(errInvalidCommandX, command)
+ of "js", "compiletojs":
+ gCmd = cmdCompileToJS
+ commandCompileToJS()
+ of "php":
+ gCmd = cmdCompileToPHP
+ commandCompileToJS()
+ of "doc":
+ wantMainModule()
+ gCmd = cmdDoc
+ loadConfigs(DocConfig)
+ commandDoc()
+ of "doc2":
+ gCmd = cmdDoc
+ loadConfigs(DocConfig)
+ defineSymbol("nimdoc")
+ commandDoc2()
+ of "rst2html":
+ gCmd = cmdRst2html
+ loadConfigs(DocConfig)
+ commandRst2Html()
+ of "rst2tex":
+ gCmd = cmdRst2tex
+ loadConfigs(DocTexConfig)
+ commandRst2TeX()
+ of "jsondoc":
+ wantMainModule()
+ gCmd = cmdDoc
+ loadConfigs(DocConfig)
+ wantMainModule()
+ defineSymbol("nimdoc")
+ commandJSON()
+ of "buildindex":
+ gCmd = cmdDoc
+ loadConfigs(DocConfig)
+ commandBuildIndex()
+ of "gendepend":
+ gCmd = cmdGenDepend
+ commandGenDepend()
+ of "dump":
+ gCmd = cmdDump
+ if getConfigVar("dump.format") == "json":
+ wantMainModule()
var definedSymbols = newJArray()
- for s in definedSymbolNames(conf.symbols): definedSymbols.elems.add(%s)
+ for s in definedSymbolNames(): definedSymbols.elems.add(%s)
var libpaths = newJArray()
- var lazyPaths = newJArray()
- for dir in conf.searchPaths: libpaths.elems.add(%dir.string)
- for dir in conf.lazyPaths: lazyPaths.elems.add(%dir.string)
+ for dir in iterSearchPath(searchPaths): libpaths.elems.add(%dir)
- var hints = newJObject() # consider factoring with `listHints`
- for a in hintMin..hintMax:
- hints[$a] = %(a in conf.notes)
- var warnings = newJObject()
- for a in warnMin..warnMax:
- warnings[$a] = %(a in conf.notes)
-
- var dumpdata = %[
+ var dumpdata = % [
(key: "version", val: %VersionAsString),
- (key: "nimExe", val: %(getAppFilename())),
- (key: "prefixdir", val: %conf.getPrefixDir().string),
- (key: "libpath", val: %conf.libpath.string),
- (key: "project_path", val: %conf.projectFull.string),
+ (key: "project_path", val: %gProjectFull),
(key: "defined_symbols", val: definedSymbols),
- (key: "lib_paths", val: %libpaths),
- (key: "lazyPaths", val: %lazyPaths),
- (key: "outdir", val: %conf.outDir.string),
- (key: "out", val: %conf.outFile.string),
- (key: "nimcache", val: %getNimcacheDir(conf).string),
- (key: "hints", val: hints),
- (key: "warnings", val: warnings),
+ (key: "lib_paths", val: libpaths)
]
- msgWriteln(conf, $dumpdata, {msgStdout, msgSkipHook, msgNoUnitSep})
- # `msgNoUnitSep` to avoid generating invalid json, refs bug #17853
+ msgWriteln($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})
- msgWriteln(conf, "-- end of list --", {msgStdout, msgSkipHook})
+ msgWriteln("-- list of currently defined symbols --",
+ {msgStdout, msgSkipHook})
+ for s in definedSymbolNames(): msgWriteln(s, {msgStdout, msgSkipHook})
+ msgWriteln("-- end of list --", {msgStdout, msgSkipHook})
- for it in conf.searchPaths: msgWriteln(conf, it.string)
- of cmdCheck:
- commandCheck(graph)
- of cmdParse:
- wantMainModule(conf)
- discard parseFile(conf.projectMainIdx, cache, conf)
- of cmdRod:
- wantMainModule(conf)
- commandView(graph)
- #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)
- # main NimScript logic handled in `loadConfigs`.
- of cmdNop: discard
- of cmdJsonscript:
- setOutFile(graph.config)
- commandJsonScript(graph)
- of cmdUnknown, cmdNone, cmdIdeTools, cmdNimfix:
- rawMessage(conf, errGenerated, "invalid command: " & conf.command)
+ for it in iterSearchPath(searchPaths): msgWriteln(it)
+ of "check":
+ gCmd = cmdCheck
+ commandCheck()
+ of "parse":
+ gCmd = cmdParse
+ wantMainModule()
+ discard parseFile(gProjectMainIdx)
+ of "scan":
+ gCmd = cmdScan
+ wantMainModule()
+ commandScan()
+ msgWriteln("Beware: Indentation tokens depend on the parser\'s state!")
+ of "secret":
+ gCmd = cmdInteractive
+ commandInteractive()
+ of "e":
+ # XXX: temporary command for easier testing
+ commandEval(mainCommandArg())
+ of "reset":
+ resetMemory()
+ of "idetools":
+ gCmd = cmdIdeTools
+ if gEvalExpr != "":
+ commandEval(gEvalExpr)
+ else:
+ commandSuggest()
+ of "serve":
+ isServing = true
+ gGlobalOptions.incl(optCaasEnabled)
+ msgs.gErrorMax = high(int) # do not stop after first error
+ serve(mainCommand)
+ of "nop", "help":
+ # prevent the "success" message:
+ gCmd = cmdDump
+ else:
+ rawMessage(errInvalidCommandX, command)
- if conf.errorCounter == 0 and conf.cmd notin {cmdTcc, cmdDump, cmdNop}:
- if optProfileVM in conf.globalOptions:
- echo conf.dump(conf.vmProfileData)
- genSuccessX(conf)
+ if msgs.gErrorCounter == 0 and
+ gCmd notin {cmdInterpret, cmdRun, cmdDump}:
+ rawMessage(hintSuccessX, [$gLinesCompiled,
+ formatFloat(epochTime() - gLastCmdTime, ffDecimal, 3),
+ formatSize(getTotalMem()),
+ if condSyms.isDefined("release"): "Release Build"
+ else: "Debug Build"])
when PrintRopeCacheStats:
echo "rope cache stats: "
@@ -430,3 +382,8 @@ proc mainCommand*(graph: ModuleGraph) =
echo " int tries: ", gCacheIntTries
echo " efficiency: ", formatFloat(1-(gCacheMisses.float/gCacheTries.float),
ffDecimal, 3)
+
+ when SimulateCaasMemReset:
+ resetMemory()
+
+ resetAttributes()
diff --git a/compiler/modulegraphs.nim b/compiler/modulegraphs.nim
deleted file mode 100644
index 8ffbe20a5..000000000
--- a/compiler/modulegraphs.nim
+++ /dev/null
@@ -1,679 +0,0 @@
-#
-#
-# The Nim Compiler
-# (c) Copyright 2017 Andreas Rumpf
-#
-# See the file "copying.txt", included in this
-# distribution, for details about the copyright.
-#
-
-## 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.
-
-import intsets, tables, hashes, md5
-import ast, astalgo, options, lineinfos,idents, btrees, ropes, msgs, pathutils, packages
-import ic / [packed_ast, ic]
-
-when defined(nimPreviewSlimSystem):
- import std/assertions
-
-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
-
- 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
- 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
- suggestMode*: bool # whether we are in nimsuggest mode or not.
- invalidTransitiveClosure: bool
- inclToMod*: Table[FileIndex, FileIndex] # mapping of include file to the
- # first module that included it
- importStack*: seq[FileIndex] # The current import stack. Used for detecting recursive
- # module dependencies.
- backend*: RootRef # minor hack so that a backend can extend this easily
- config*: ConfigRef
- cache*: IdentCache
- vm*: RootRef # unfortunately the 'vm' state is shared project-wise, this will
- # be clarified in later compiler implementations.
- 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
- 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
- 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.}
- globalDestructors*: seq[PNode]
- 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
- PPassContext* = ref TPassContext
-
- TPassOpen* = proc (graph: ModuleGraph; module: PSym; idgen: IdGenerator): PPassContext {.nimcall.}
- TPassClose* = proc (graph: ModuleGraph; p: PPassContext, n: PNode): PNode {.nimcall.}
- TPassProcess* = proc (p: PPassContext, topLevelStmt: PNode): PNode {.nimcall.}
-
- TPass* = tuple[open: TPassOpen,
- process: TPassProcess,
- 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 = [
- "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N",
- "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z",
- "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n",
- "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z",
- "0", "1", "2", "3", "4", "5", "6", "7", "8", "9a",
- "9b", "9c"]
-
-proc toBase64a(s: cstring, len: int): string =
- ## encodes `s` into base64 representation.
- result = newStringOfCap(((len + 2) div 3) * 4)
- result.add "__"
- var i = 0
- while i < len - 2:
- let a = ord(s[i])
- let b = ord(s[i+1])
- let c = ord(s[i+2])
- result.add cb64[a shr 2]
- result.add cb64[((a and 3) shl 4) or ((b and 0xF0) shr 4)]
- result.add cb64[((b and 0x0F) shl 2) or ((c and 0xC0) shr 6)]
- result.add cb64[c and 0x3F]
- inc(i, 3)
- if i < len-1:
- let a = ord(s[i])
- let b = ord(s[i+1])
- result.add cb64[a shr 2]
- result.add cb64[((a and 3) shl 4) or ((b and 0xF0) shr 4)]
- result.add cb64[((b and 0x0F) shl 2)]
- elif i < len:
- let a = ord(s[i])
- 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))
-
-proc `==`*(a, b: SigHash): bool =
- result = equalMem(unsafeAddr a, unsafeAddr b, sizeof(a))
-
-proc hash*(u: SigHash): Hash =
- result = 0
- for x in 0..3:
- result = (result shl 8) or u.MD5Digest[x].int
-
-proc hash*(x: FileIndex): Hash {.borrow.}
-
-template getPContext(): untyped =
- when c is PContext: c
- else: c.c
-
-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
- template onDefResolveForward*(info: TLineInfo; s: PSym) = discard
-
-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, {})
- 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)
-
- 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:
- result.idgen = IdGenerator(module: -1'i32, symId: 0'i32, typeId: 0'i32)
- initStrTable(result.packageSyms)
- result.deps = initIntSet()
- result.importDeps = initTable[FileIndex, seq[FileIndex]]()
- result.ifaces = @[]
- result.importStack = @[]
- result.inclToMod = initTable[FileIndex, FileIndex]()
- 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.emptyNode = newNode(nkEmpty)
- 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)
- g.deps = initIntSet()
- g.ifaces = @[]
- g.importStack = @[]
- g.inclToMod = initTable[FileIndex, FileIndex]()
- g.usageSym = nil
- g.owners = @[]
- 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
-
-proc dependsOn(a, b: int): int {.inline.} = (a shl 15) + b
-
-proc addDep*(g: ModuleGraph; m: PSym, dep: FileIndex) =
- assert m.position == m.info.fileIndex.int32
- if g.suggestMode:
- g.deps.incl m.position.dependsOn(dep.int)
- # we compute the transitive closure later when querying the graph lazily.
- # this improves efficiency quite a lot:
- #invalidTransitiveClosure = true
-
-proc addIncludeDep*(g: ModuleGraph; module, includeFile: FileIndex) =
- discard hasKeyOrPut(g.inclToMod, includeFile, module)
-
-proc parentModule*(g: ModuleGraph; fileIdx: FileIndex): FileIndex =
- ## returns 'fileIdx' if the file belonging to this index is
- ## directly used as a module or else the module that first
- ## references this include file.
- if fileIdx.int32 >= 0 and fileIdx.int32 < g.ifaces.len and g.ifaces[fileIdx.int32].module != nil:
- result = fileIdx
- else:
- result = g.inclToMod.getOrDefault(fileIdx)
-
-proc transitiveClosure(g: var IntSet; n: int) =
- # warshall's algorithm
- for k in 0.. 0: modulename else: $n
- localError(conf, n.info, "cannot open file: " & m)
- 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..8ac964321 100644
--- a/compiler/modules.nim
+++ b/compiler/modules.nim
@@ -7,52 +7,227 @@
# distribution, for details about the copyright.
#
-## Implements the module handling, including the caching of modules.
+## implements the module handling
import
- ast, magicsys, msgs, options,
- idents, lexer, syntaxes, modulegraphs,
- lineinfos, pathutils
+ ast, astalgo, magicsys, securehash, rodread, msgs, cgendata, sigmatch, options,
+ idents, os, lexer, idgen, passes, syntaxes, llstream
-proc resetSystemArtifacts*(g: ModuleGraph) =
- magicsys.resetSysTypes(g)
+type
+ TNeedRecompile* = enum Maybe, No, Yes, Probing, Recompiled
+ THashStatus* = enum hashNotTaken, hashCached, hashHasChanged, hashNotChanged
-template getModuleIdent(graph: ModuleGraph, filename: AbsoluteFile): PIdent =
- getIdent(graph.cache, splitFile(filename).name)
+ TModuleInMemory* = object
+ compiledAt*: float
+ hash*: SecureHash
+ deps*: seq[int32] ## XXX: slurped files are currently not tracked
+ needsRecompile*: TNeedRecompile
+ hashStatus*: THashStatus
-proc partialInitModule*(result: PSym; graph: ModuleGraph; fileIdx: FileIndex; filename: AbsoluteFile) =
- let packSym = getPackage(graph, fileIdx)
- result.owner = packSym
- result.position = int fileIdx
+var
+ gCompiledModules: seq[PSym] = @[]
+ gMemCacheData*: seq[TModuleInMemory] = @[]
+ ## XXX: we should implement recycling of file IDs
+ ## if the user keeps renaming modules, the file IDs will keep growing
-proc newModule*(graph: ModuleGraph; fileIdx: FileIndex): PSym =
- let filename = AbsoluteFile toFullPath(graph.config, fileIdx)
+proc getModule*(fileIdx: int32): PSym =
+ if fileIdx >= 0 and fileIdx < gCompiledModules.len:
+ result = gCompiledModules[fileIdx]
+
+template hash(x: PSym): expr =
+ gMemCacheData[x.position].hash
+
+proc hashChanged(fileIdx: int32): bool =
+ internalAssert fileIdx >= 0 and fileIdx < gMemCacheData.len
+
+ template updateStatus =
+ gMemCacheData[fileIdx].hashStatus = if result: hashHasChanged
+ else: hashNotChanged
+ # echo "TESTING Hash: ", fileIdx.toFilename, " ", result
+
+ case gMemCacheData[fileIdx].hashStatus:
+ of hashHasChanged:
+ result = true
+ of hashNotChanged:
+ result = false
+ of hashCached:
+ let newHash = secureHashFile(fileIdx.toFullPath)
+ result = newHash != gMemCacheData[fileIdx].hash
+ gMemCacheData[fileIdx].hash = newHash
+ updateStatus()
+ of hashNotTaken:
+ gMemCacheData[fileIdx].hash = secureHashFile(fileIdx.toFullPath)
+ result = true
+ updateStatus()
+
+proc doHash(fileIdx: int32) =
+ if gMemCacheData[fileIdx].hashStatus == hashNotTaken:
+ # echo "FIRST Hash: ", fileIdx.ToFilename
+ gMemCacheData[fileIdx].hash = secureHashFile(fileIdx.toFullPath)
+
+proc addDep(x: PSym, dep: int32) =
+ growCache gMemCacheData, dep
+ gMemCacheData[x.position].deps.safeAdd(dep)
+
+proc resetModule*(fileIdx: int32) =
+ # echo "HARD RESETTING ", fileIdx.toFilename
+ if fileIdx <% gMemCacheData.len:
+ gMemCacheData[fileIdx].needsRecompile = Yes
+ if fileIdx <% gCompiledModules.len:
+ gCompiledModules[fileIdx] = nil
+ if fileIdx <% cgendata.gModules.len:
+ cgendata.gModules[fileIdx] = nil
+
+proc resetModule*(module: PSym) =
+ let conflict = getModule(module.position.int32)
+ if conflict == nil: return
+ doAssert conflict == module
+ resetModule(module.position.int32)
+ initStrTable(module.tab)
+
+proc resetAllModules* =
+ for i in 0..gCompiledModules.high:
+ if gCompiledModules[i] != nil:
+ resetModule(i.int32)
+ resetPackageCache()
+ # for m in cgenModules(): echo "CGEN MODULE FOUND"
+
+proc resetAllModulesHard* =
+ resetPackageCache()
+ gCompiledModules.setLen 0
+ gMemCacheData.setLen 0
+ magicsys.resetSysTypes()
+ # XXX
+ #gOwners = @[]
+
+proc checkDepMem(fileIdx: int32): TNeedRecompile =
+ template markDirty =
+ resetModule(fileIdx)
+ return Yes
+
+ if gMemCacheData[fileIdx].needsRecompile != Maybe:
+ return gMemCacheData[fileIdx].needsRecompile
+
+ if optForceFullMake in gGlobalOptions or
+ hashChanged(fileIdx):
+ markDirty
+
+ if gMemCacheData[fileIdx].deps != nil:
+ gMemCacheData[fileIdx].needsRecompile = Probing
+ for dep in gMemCacheData[fileIdx].deps:
+ let d = checkDepMem(dep)
+ if d in {Yes, Recompiled}:
+ # echo fileIdx.toFilename, " depends on ", dep.toFilename, " ", d
+ markDirty
+
+ gMemCacheData[fileIdx].needsRecompile = No
+ return No
+
+proc newModule(fileIdx: int32): PSym =
# 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.
- result = PSym(kind: skModule, itemId: ItemId(module: int32(fileIdx), item: 0'i32),
- name: getModuleIdent(graph, filename),
- info: newLineInfo(fileIdx, 1, 1))
+ new(result)
+ result.id = - 1 # for better error checking
+ result.kind = skModule
+ let filename = fileIdx.toFullPath
+ result.name = getIdent(splitFile(filename).name)
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.")
- partialInitModule(result, graph, fileIdx, filename)
- graph.registerModule(result)
+ rawMessage(errInvalidModuleName, result.name.s)
-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)
+ result.info = newLineInfo(fileIdx, 1, 1)
+ result.owner = newSym(skPackage, getIdent(getPackageName(filename)), nil,
+ result.info)
+ result.position = fileIdx
-proc wantMainModule*(conf: ConfigRef) =
- if conf.projectFull.isEmpty:
- fatal(conf, gCmdLineInfo, "command expects a filename")
- conf.projectMainIdx = fileInfoIdx(conf, addFileExt(conf.projectFull, NimExt))
+ growCache gMemCacheData, fileIdx
+ growCache gCompiledModules, fileIdx
+ gCompiledModules[result.position] = result
-proc makeModule*(graph: ModuleGraph; filename: AbsoluteFile): PSym =
- result = graph.newModule(fileInfoIdx(graph.config, filename))
- registerModule(graph, result)
+ incl(result.flags, sfUsed)
+ initStrTable(result.tab)
+ strTableAdd(result.tab, result) # a module knows itself
-proc makeModule*(graph: ModuleGraph; filename: string): PSym =
- result = makeModule(graph, AbsoluteFile filename)
+proc compileModule*(fileIdx: int32, flags: TSymFlags): PSym =
+ result = getModule(fileIdx)
+ if result == nil:
+ growCache gMemCacheData, fileIdx
+ gMemCacheData[fileIdx].needsRecompile = Probing
+ result = newModule(fileIdx)
+ #var rd = handleSymbolFile(result)
+ var rd: PRodReader
+ result.flags = result.flags + flags
+ if sfMainModule in result.flags:
+ gMainPackageId = result.owner.id
-proc makeStdinModule*(graph: ModuleGraph): PSym = graph.makeModule(AbsoluteFile"stdin")
+ if gCmd in {cmdCompileToC, cmdCompileToCpp, cmdCheck, cmdIdeTools}:
+ rd = handleSymbolFile(result)
+ if result.id < 0:
+ internalError("handleSymbolFile should have set the module\'s ID")
+ return
+ else:
+ result.id = getID()
+ if sfMainModule in flags and gProjectIsStdin:
+ processModule(result, llStreamOpen(stdin), rd)
+ else:
+ processModule(result, nil, rd)
+ if optCaasEnabled in gGlobalOptions:
+ gMemCacheData[fileIdx].compiledAt = gLastCmdTime
+ gMemCacheData[fileIdx].needsRecompile = Recompiled
+ doHash fileIdx
+ else:
+ if checkDepMem(fileIdx) == Yes:
+ result = compileModule(fileIdx, flags)
+ else:
+ result = gCompiledModules[fileIdx]
+
+proc importModule*(s: PSym, fileIdx: int32): PSym {.procvar.} =
+ # this is called by the semantic checking phase
+ result = compileModule(fileIdx, {})
+ if optCaasEnabled in gGlobalOptions: addDep(s, fileIdx)
+ if sfSystemModule in result.flags:
+ localError(result.info, errAttemptToRedefine, result.name.s)
+ # restore the notes for outer module:
+ gNotes = if s.owner.id == gMainPackageId: gMainPackageNotes
+ else: ForeignPackageNotes
+
+proc includeModule*(s: PSym, fileIdx: int32): PNode {.procvar.} =
+ result = syntaxes.parseFile(fileIdx)
+ if optCaasEnabled in gGlobalOptions:
+ growCache gMemCacheData, fileIdx
+ addDep(s, fileIdx)
+ doHash(fileIdx)
+
+proc `==^`(a, b: string): bool =
+ try:
+ result = sameFile(a, b)
+ except OSError:
+ result = false
+
+proc compileSystemModule* =
+ if magicsys.systemModule == nil:
+ systemFileIdx = fileInfoIdx(options.libpath/"system.nim")
+ discard compileModule(systemFileIdx, {sfSystemModule})
+
+proc wantMainModule* =
+ if gProjectFull.len == 0:
+ fatal(gCmdLineInfo, errCommandExpectsFilename)
+ gProjectMainIdx = addFileExt(gProjectFull, NimExt).fileInfoIdx
+
+passes.gIncludeFile = includeModule
+passes.gImportModule = importModule
+
+proc compileProject*(projectFileIdx = -1'i32) =
+ wantMainModule()
+ let systemFileIdx = fileInfoIdx(options.libpath / "system.nim")
+ let projectFile = if projectFileIdx < 0: gProjectMainIdx else: projectFileIdx
+ if projectFile == systemFileIdx:
+ discard compileModule(projectFile, {sfMainModule, sfSystemModule})
+ else:
+ compileSystemModule()
+ discard compileModule(projectFile, {sfMainModule})
+
+proc makeModule*(filename: string): PSym =
+ result = newModule(fileInfoIdx filename)
+ result.id = getID()
+
+proc makeStdinModule*(): PSym = makeModule"stdin"
diff --git a/compiler/msgs.nim b/compiler/msgs.nim
index 8e391f2fb..098370e41 100644
--- a/compiler/msgs.nim
+++ b/compiler/msgs.nim
@@ -8,162 +8,639 @@
#
import
- std/[strutils, os, tables, terminal, macros, times],
- std/private/miscdollars,
- options, lineinfos, pathutils
+ options, strutils, os, tables, ropes, platform, terminal, macros
-import ropes except `%`
+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, errUndeclaredField,
+ errUndeclaredRoutine, 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,
+ errNoCommand, 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,
+ errExternalAssemblerNotFound,
+ errExternalAssemblerNotValid,
+ errUser,
+ warnCannotOpenFile,
+ warnOctalEscape, warnXIsNeverRead, warnXmightNotBeenInit,
+ warnDeprecated, warnConfigDeprecated,
+ warnSmallLshouldNotBeUsed, warnUnknownMagic, warnRedefinitionOfLabel,
+ warnUnknownSubstitutionX, warnLanguageXNotSupported,
+ warnFieldXNotSupported, warnCommentXIgnored,
+ warnNilStatement, warnTypelessParam,
+ warnUseBase, warnWriteToForeignHeap, warnUnsafeCode,
+ warnEachIdentIsTuple, warnShadowIdent,
+ 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
-when defined(nimPreviewSlimSystem):
- import std/[syncio, assertions]
+const
+ MsgKindToStr*: array[TMsgKind, string] = [
+ errUnknown: "unknown error",
+ errIllFormedAstX: "illformed AST: $1",
+ errInternal: "internal error: $1",
+ errCannotOpenFile: "cannot open \'$1\'",
+ errGenerated: "$1",
+ errXCompilerDoesNotSupportCpp: "\'$1\' compiler does not support C++",
+ errStringLiteralExpected: "string literal expected",
+ errIntLiteralExpected: "integer literal expected",
+ errInvalidCharacterConstant: "invalid character constant",
+ errClosingTripleQuoteExpected: "closing \"\"\" expected, but end of file reached",
+ errClosingQuoteExpected: "closing \" expected",
+ errTabulatorsAreNotAllowed: "tabulators are not allowed",
+ errInvalidToken: "invalid token: $1",
+ errLineTooLong: "line too long",
+ errInvalidNumber: "$1 is not a valid number",
+ errInvalidNumberOctalCode: "$1 is not a valid number; did you mean octal? Then use one of '0o', '0c' or '0C'.",
+ errNumberOutOfRange: "number $1 out of valid range",
+ errNnotAllowedInCharacter: "\\n not allowed in character literal",
+ errClosingBracketExpected: "closing ']' expected, but end of file reached",
+ errMissingFinalQuote: "missing final \' for character literal",
+ errIdentifierExpected: "identifier expected, but found \'$1\'",
+ errNewlineExpected: "newline expected, but found \'$1\'",
+ errInvalidModuleName: "invalid module name: '$1'",
+ errOperatorExpected: "operator expected, but found \'$1\'",
+ errTokenExpected: "\'$1\' expected",
+ errStringAfterIncludeExpected: "string after \'include\' expected",
+ errRecursiveDependencyX: "recursive dependency: \'$1\'",
+ errOnOrOffExpected: "\'on\' or \'off\' expected",
+ errNoneSpeedOrSizeExpected: "\'none\', \'speed\' or \'size\' expected",
+ errInvalidPragma: "invalid pragma",
+ errUnknownPragma: "unknown pragma: \'$1\'",
+ errInvalidDirectiveX: "invalid directive: \'$1\'",
+ errAtPopWithoutPush: "\'pop\' without a \'push\' pragma",
+ errEmptyAsm: "empty asm statement",
+ errInvalidIndentation: "invalid indentation",
+ errExceptionExpected: "exception expected",
+ errExceptionAlreadyHandled: "exception already handled",
+ errYieldNotAllowedHere: "'yield' only allowed in an iterator",
+ errYieldNotAllowedInTryStmt: "'yield' cannot be used within 'try' in a non-inlined iterator",
+ errInvalidNumberOfYieldExpr: "invalid number of \'yield\' expressions",
+ errCannotReturnExpr: "current routine cannot return an expression",
+ errAttemptToRedefine: "redefinition of \'$1\'",
+ errStmtInvalidAfterReturn: "statement not allowed after \'return\', \'break\', \'raise\' or \'continue'",
+ errStmtExpected: "statement expected",
+ errInvalidLabel: "\'$1\' is no label",
+ errInvalidCmdLineOption: "invalid command line option: \'$1\'",
+ errCmdLineArgExpected: "argument for command line option expected: \'$1\'",
+ errCmdLineNoArgExpected: "invalid argument for command line option: \'$1\'",
+ errInvalidVarSubstitution: "invalid variable substitution in \'$1\'",
+ errUnknownVar: "unknown variable: \'$1\'",
+ errUnknownCcompiler: "unknown C compiler: \'$1\'",
+ errOnOrOffExpectedButXFound: "\'on\' or \'off\' expected, but \'$1\' found",
+ errOnOffOrListExpectedButXFound: "\'on\', \'off\' or \'list\' expected, but \'$1\' found",
+ errNoneBoehmRefcExpectedButXFound: "'none', 'boehm' or 'refc' expected, but '$1' found",
+ errNoneSpeedOrSizeExpectedButXFound: "'none', 'speed' or 'size' expected, but '$1' found",
+ errGuiConsoleOrLibExpectedButXFound: "'gui', 'console' or 'lib' expected, but '$1' found",
+ errUnknownOS: "unknown OS: '$1'",
+ errUnknownCPU: "unknown CPU: '$1'",
+ errGenOutExpectedButXFound: "'c', 'c++' or 'yaml' expected, but '$1' found",
+ errArgsNeedRunOption: "arguments can only be given if the '--run' option is selected",
+ errInvalidMultipleAsgn: "multiple assignment is not allowed",
+ errColonOrEqualsExpected: "\':\' or \'=\' expected, but found \'$1\'",
+ errExprExpected: "expression expected, but found \'$1\'",
+ errUndeclaredIdentifier: "undeclared identifier: \'$1\'",
+ errUndeclaredField: "undeclared field: \'$1\'",
+ errUndeclaredRoutine: "attempting to call undeclared routine: \'$1\'",
+ errUseQualifier: "ambiguous identifier: \'$1\' -- use a qualifier",
+ errTypeExpected: "type expected",
+ errSystemNeeds: "system module needs \'$1\'",
+ errExecutionOfProgramFailed: "execution of an external program failed: '$1'",
+ errNotOverloadable: "overloaded \'$1\' leads to ambiguous calls",
+ errInvalidArgForX: "invalid argument for \'$1\'",
+ errStmtHasNoEffect: "statement has no effect",
+ errXExpectsTypeOrValue: "\'$1\' expects a type or value",
+ errXExpectsArrayType: "\'$1\' expects an array type",
+ errIteratorCannotBeInstantiated: "'$1' cannot be instantiated because its body has not been compiled yet",
+ errExprXAmbiguous: "expression '$1' ambiguous in this context",
+ errConstantDivisionByZero: "division by zero",
+ errOrdinalTypeExpected: "ordinal type expected",
+ errOrdinalOrFloatTypeExpected: "ordinal or float type expected",
+ errOverOrUnderflow: "over- or underflow",
+ errCannotEvalXBecauseIncompletelyDefined: "cannot evalutate '$1' because type is not defined completely",
+ errChrExpectsRange0_255: "\'chr\' expects an int in the range 0..255",
+ errDynlibRequiresExportc: "\'dynlib\' requires \'exportc\'",
+ errUndeclaredFieldX: "undeclared field: \'$1\'",
+ errNilAccess: "attempt to access a nil address",
+ errIndexOutOfBounds: "index out of bounds",
+ errIndexTypesDoNotMatch: "index types do not match",
+ errBracketsInvalidForType: "\'[]\' operator invalid for this type",
+ errValueOutOfSetBounds: "value out of set bounds",
+ errFieldInitTwice: "field initialized twice: \'$1\'",
+ errFieldNotInit: "field \'$1\' not initialized",
+ errExprXCannotBeCalled: "expression \'$1\' cannot be called",
+ errExprHasNoType: "expression has no type",
+ errExprXHasNoType: "expression \'$1\' has no type (or is ambiguous)",
+ errCastNotInSafeMode: "\'cast\' not allowed in safe mode",
+ errExprCannotBeCastedToX: "expression cannot be casted to $1",
+ errCommaOrParRiExpected: "',' or ')' expected",
+ errCurlyLeOrParLeExpected: "\'{\' or \'(\' expected",
+ errSectionExpected: "section (\'type\', \'proc\', etc.) expected",
+ errRangeExpected: "range expected",
+ errMagicOnlyInSystem: "\'magic\' only allowed in system module",
+ errPowerOfTwoExpected: "power of two expected",
+ errStringMayNotBeEmpty: "string literal may not be empty",
+ errCallConvExpected: "calling convention expected",
+ errProcOnlyOneCallConv: "a proc can only have one calling convention",
+ errSymbolMustBeImported: "symbol must be imported if 'lib' pragma is used",
+ errExprMustBeBool: "expression must be of type 'bool'",
+ errConstExprExpected: "constant expression expected",
+ errDuplicateCaseLabel: "duplicate case label",
+ errRangeIsEmpty: "range is empty",
+ errSelectorMustBeOfCertainTypes: "selector must be of an ordinal type, float or string",
+ errSelectorMustBeOrdinal: "selector must be of an ordinal type",
+ errOrdXMustNotBeNegative: "ord($1) must not be negative",
+ errLenXinvalid: "len($1) must be less than 32768",
+ errWrongNumberOfVariables: "wrong number of variables",
+ errExprCannotBeRaised: "only a 'ref object' can be raised",
+ errBreakOnlyInLoop: "'break' only allowed in loop construct",
+ errTypeXhasUnknownSize: "type \'$1\' has unknown size",
+ errConstNeedsConstExpr: "a constant can only be initialized with a constant expression",
+ errConstNeedsValue: "a constant needs a value",
+ errResultCannotBeOpenArray: "the result type cannot be on open array",
+ errSizeTooBig: "computing the type\'s size produced an overflow",
+ errSetTooBig: "set is too large",
+ errBaseTypeMustBeOrdinal: "base type of a set must be an ordinal",
+ errInheritanceOnlyWithNonFinalObjects: "inheritance only works with non-final objects",
+ errInheritanceOnlyWithEnums: "inheritance only works with an enum",
+ errIllegalRecursionInTypeX: "illegal recursion in type \'$1\'",
+ errCannotInstantiateX: "cannot instantiate: \'$1\'",
+ errExprHasNoAddress: "expression has no address",
+ errXStackEscape: "address of '$1' may not escape its stack frame",
+ errVarForOutParamNeeded: "for a \'var\' type a variable needs to be passed",
+ errPureTypeMismatch: "type mismatch",
+ errTypeMismatch: "type mismatch: got (",
+ errButExpected: "but expected one of: ",
+ errButExpectedX: "but expected \'$1\'",
+ errAmbiguousCallXYZ: "ambiguous call; both $1 and $2 match for: $3",
+ errWrongNumberOfArguments: "wrong number of arguments",
+ errXCannotBePassedToProcVar: "\'$1\' cannot be passed to a procvar",
+ errXCannotBeInParamDecl: "$1 cannot be declared in parameter declaration",
+ errPragmaOnlyInHeaderOfProc: "pragmas are only allowed in the header of a proc",
+ errImplOfXNotAllowed: "implementation of \'$1\' is not allowed",
+ errImplOfXexpected: "implementation of \'$1\' expected",
+ errNoSymbolToBorrowFromFound: "no symbol to borrow from found",
+ errDiscardValueX: "value of type '$1' has to be discarded",
+ errInvalidDiscard: "statement returns no value that can be discarded",
+ errIllegalConvFromXtoY: "conversion from $1 to $2 is invalid",
+ errCannotBindXTwice: "cannot bind parameter \'$1\' twice",
+ errInvalidOrderInArrayConstructor: "invalid order in array constructor",
+ errInvalidOrderInEnumX: "invalid order in enum \'$1\'",
+ errEnumXHasHoles: "enum \'$1\' has holes",
+ errExceptExpected: "\'except\' or \'finally\' expected",
+ errInvalidTry: "after catch all \'except\' or \'finally\' no section may follow",
+ errOptionExpected: "option expected, but found \'$1\'",
+ errXisNoLabel: "\'$1\' is not a label",
+ errNotAllCasesCovered: "not all cases are covered",
+ errUnknownSubstitionVar: "unknown substitution variable: \'$1\'",
+ errComplexStmtRequiresInd: "complex statement requires indentation",
+ errXisNotCallable: "\'$1\' is not callable",
+ errNoPragmasAllowedForX: "no pragmas allowed for $1",
+ errNoGenericParamsAllowedForX: "no generic parameters allowed for $1",
+ errInvalidParamKindX: "invalid param kind: \'$1\'",
+ errDefaultArgumentInvalid: "default argument invalid",
+ errNamedParamHasToBeIdent: "named parameter has to be an identifier",
+ errNoReturnTypeForX: "no return type allowed for $1",
+ errConvNeedsOneArg: "a type conversion needs exactly one argument",
+ errInvalidPragmaX: "invalid pragma: $1",
+ errXNotAllowedHere: "$1 not allowed here",
+ errInvalidControlFlowX: "invalid control flow: $1",
+ errXisNoType: "invalid type: \'$1\'",
+ errCircumNeedsPointer: "'[]' needs a pointer or reference type",
+ errInvalidExpression: "invalid expression",
+ errInvalidExpressionX: "invalid expression: \'$1\'",
+ errEnumHasNoValueX: "enum has no value \'$1\'",
+ errNamedExprExpected: "named expression expected",
+ errNamedExprNotAllowed: "named expression not allowed here",
+ errXExpectsOneTypeParam: "\'$1\' expects one type parameter",
+ errArrayExpectsTwoTypeParams: "array expects two type parameters",
+ errInvalidVisibilityX: "invalid visibility: \'$1\'",
+ errInitHereNotAllowed: "initialization not allowed here",
+ errXCannotBeAssignedTo: "\'$1\' cannot be assigned to",
+ errIteratorNotAllowed: "iterators can only be defined at the module\'s top level",
+ errXNeedsReturnType: "$1 needs a return type",
+ errNoReturnTypeDeclared: "no return type declared",
+ errNoCommand: "no command given",
+ errInvalidCommandX: "invalid command: \'$1\'",
+ errXOnlyAtModuleScope: "\'$1\' is only allowed at top level",
+ errXNeedsParamObjectType: "'$1' needs a parameter that has an object type",
+ errTemplateInstantiationTooNested: "template/macro instantiation too nested",
+ errInstantiationFrom: "template/generic instantiation from here",
+ errInvalidIndexValueForTuple: "invalid index value for tuple subscript",
+ errCommandExpectsFilename: "command expects a filename argument",
+ errMainModuleMustBeSpecified: "please, specify a main module in the project configuration file",
+ errXExpected: "\'$1\' expected",
+ errTIsNotAConcreteType: "\'$1\' is not a concrete type.",
+ errInvalidSectionStart: "invalid section start",
+ errGridTableNotImplemented: "grid table is not implemented",
+ errGeneralParseError: "general parse error",
+ errNewSectionExpected: "new section expected",
+ errWhitespaceExpected: "whitespace expected, got \'$1\'",
+ errXisNoValidIndexFile: "\'$1\' is no valid index file",
+ errCannotRenderX: "cannot render reStructuredText element \'$1\'",
+ errVarVarTypeNotAllowed: "type \'var var\' is not allowed",
+ errInstantiateXExplicitly: "instantiate '$1' explicitly",
+ errOnlyACallOpCanBeDelegator: "only a call operator can be a delegator",
+ errUsingNoSymbol: "'$1' is not a variable, constant or a proc name",
+ errMacroBodyDependsOnGenericTypes: "the macro body cannot be compiled, " &
+ "because the parameter '$1' has a generic type",
+ errDestructorNotGenericEnough: "Destructor signature is too specific. " &
+ "A destructor must be associated will all instantiations of a generic type",
+ errInlineIteratorsAsProcParams: "inline iterators can be used as parameters only for " &
+ "templates, macros and other inline iterators",
+ errXExpectsTwoArguments: "\'$1\' expects two arguments",
+ errXExpectsObjectTypes: "\'$1\' expects object types",
+ errXcanNeverBeOfThisSubtype: "\'$1\' can never be of this subtype",
+ errTooManyIterations: "interpretation requires too many iterations",
+ errCannotInterpretNodeX: "cannot evaluate \'$1\'",
+ errFieldXNotFound: "field \'$1\' cannot be found",
+ errInvalidConversionFromTypeX: "invalid conversion from type \'$1\'",
+ errAssertionFailed: "assertion failed",
+ errCannotGenerateCodeForX: "cannot generate code for \'$1\'",
+ errXRequiresOneArgument: "$1 requires one parameter",
+ errUnhandledExceptionX: "unhandled exception: $1",
+ errCyclicTree: "macro returned a cyclic abstract syntax tree",
+ errXisNoMacroOrTemplate: "\'$1\' is no macro or template",
+ errXhasSideEffects: "\'$1\' can have side effects",
+ errIteratorExpected: "iterator within for loop context expected",
+ errLetNeedsInit: "'let' symbol requires an initialization",
+ errThreadvarCannotInit: "a thread var cannot be initialized explicitly",
+ errWrongSymbolX: "usage of \'$1\' is a user-defined error",
+ errIllegalCaptureX: "illegal capture '$1'",
+ errXCannotBeClosure: "'$1' cannot have 'closure' calling convention",
+ errXMustBeCompileTime: "'$1' can only be used in compile-time context",
+ errCannotInferTypeOfTheLiteral: "cannot infer the type of the $1",
+ errCannotInferReturnType: "cannot infer the return type of the proc",
+ errGenericLambdaNotAllowed: "A nested proc can have generic parameters only when " &
+ "it is used as an operand to another routine and the types " &
+ "of the generic paramers can be inferred from the expected signature.",
+ errCompilerDoesntSupportTarget: "The current compiler \'$1\' doesn't support the requested compilation target",
+ errExternalAssemblerNotFound: "External assembler not found",
+ errExternalAssemblerNotValid: "External assembler '$1' is not a valid assembler",
+ errUser: "$1",
+ warnCannotOpenFile: "cannot open \'$1\'",
+ warnOctalEscape: "octal escape sequences do not exist; leading zero is ignored",
+ warnXIsNeverRead: "\'$1\' is never read",
+ warnXmightNotBeenInit: "\'$1\' might not have been initialized",
+ warnDeprecated: "$1 is deprecated",
+ warnConfigDeprecated: "config file '$1' is deprecated",
+ warnSmallLshouldNotBeUsed: "\'l\' should not be used as an identifier; may look like \'1\' (one)",
+ warnUnknownMagic: "unknown magic \'$1\' might crash the compiler",
+ warnRedefinitionOfLabel: "redefinition of label \'$1\'",
+ warnUnknownSubstitutionX: "unknown substitution \'$1\'",
+ warnLanguageXNotSupported: "language \'$1\' not supported",
+ warnFieldXNotSupported: "field \'$1\' not supported",
+ warnCommentXIgnored: "comment \'$1\' ignored",
+ warnNilStatement: "'nil' statement is deprecated; use an empty 'discard' statement instead",
+ warnTypelessParam: "'$1' has no type. Typeless parameters are deprecated; only allowed for 'template'",
+ warnUseBase: "use {.base.} for base methods; baseless methods are deprecated",
+ warnWriteToForeignHeap: "write to foreign heap",
+ warnUnsafeCode: "unsafe code: '$1'",
+ warnEachIdentIsTuple: "each identifier is a tuple",
+ warnShadowIdent: "shadowed identifier: '$1'",
+ warnProveInit: "Cannot prove that '$1' is initialized. This will become a compile time error in the future.",
+ warnProveField: "cannot prove that field '$1' is accessible",
+ warnProveIndex: "cannot prove index '$1' is valid",
+ warnGcUnsafe: "not GC-safe: '$1'",
+ warnGcUnsafe2: "$1",
+ warnUninit: "'$1' might not have been initialized",
+ warnGcMem: "'$1' uses GC'ed memory",
+ warnDestructor: "usage of a type with a destructor in a non destructible context. This will become a compile time error in the future.",
+ warnLockLevel: "$1",
+ warnResultShadowed: "Special variable 'result' is shadowed.",
+ warnUser: "$1",
+ hintSuccess: "operation successful",
+ hintSuccessX: "operation successful ($# lines compiled; $# sec total; $#; $#)",
+ hintLineTooLong: "line too long",
+ hintXDeclaredButNotUsed: "\'$1\' is declared but not used",
+ hintConvToBaseNotNeeded: "conversion to base object is not needed",
+ hintConvFromXtoItselfNotNeeded: "conversion from $1 to itself is pointless",
+ hintExprAlwaysX: "expression evaluates always to \'$1\'",
+ hintQuitCalled: "quit() called",
+ hintProcessing: "$1",
+ hintCodeBegin: "generated code listing:",
+ hintCodeEnd: "end of listing",
+ hintConf: "used config file \'$1\'",
+ hintPath: "added path: '$1'",
+ hintConditionAlwaysTrue: "condition is always true: '$1'",
+ hintName: "name should be: '$1'",
+ hintPattern: "$1",
+ hintExecuting: "$1",
+ hintLinking: "",
+ hintDependency: "$1",
+ hintSource: "$1",
+ hintStackTrace: "$1",
+ hintGCStats: "$1",
+ hintUser: "$1"]
+const
+ WarningsToStr*: array[0..30, string] = ["CannotOpenFile", "OctalEscape",
+ "XIsNeverRead", "XmightNotBeenInit",
+ "Deprecated", "ConfigDeprecated",
+ "SmallLshouldNotBeUsed", "UnknownMagic",
+ "RedefinitionOfLabel", "UnknownSubstitutionX",
+ "LanguageXNotSupported", "FieldXNotSupported",
+ "CommentXIgnored", "NilStmt",
+ "TypelessParam", "UseBase", "WriteToForeignHeap",
+ "UnsafeCode", "EachIdentIsTuple", "ShadowIdent",
+ "ProveInit", "ProveField", "ProveIndex", "GcUnsafe", "GcUnsafe2", "Uninit",
+ "GcMem", "Destructor", "LockLevel", "ResultShadowed", "User"]
-type InstantiationInfo* = typeof(instantiationInfo())
-template instLoc*(): InstantiationInfo = instantiationInfo(-2, fullPaths = true)
+ HintsToStr*: array[0..22, string] = ["Success", "SuccessX", "LineTooLong",
+ "XDeclaredButNotUsed", "ConvToBaseNotNeeded", "ConvFromXtoItselfNotNeeded",
+ "ExprAlwaysX", "QuitCalled", "Processing", "CodeBegin", "CodeEnd", "Conf",
+ "Path", "CondTrue", "Name", "Pattern", "Exec", "Link", "Dependency",
+ "Source", "StackTrace", "GCStats",
+ "User"]
-template toStdOrrKind(stdOrr): untyped =
- if stdOrr == stdout: stdOrrStdout else: stdOrrStderr
+const
+ fatalMin* = errUnknown
+ fatalMax* = errInternal
+ errMin* = errUnknown
+ errMax* = errUser
+ warnMin* = warnCannotOpenFile
+ warnMax* = pred(hintSuccess)
+ hintMin* = hintSuccess
+ hintMax* = high(TMsgKind)
-proc toLowerAscii(a: var string) {.inline.} =
- for c in mitems(a):
- if isUpperAscii(c): c = char(uint8(c) xor 0b0010_0000'u8)
+type
+ TNoteKind* = range[warnMin..hintMax] # "notes" are warnings or hints
+ TNoteKinds* = set[TNoteKind]
-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`.
- let stdOrr = if optStdout in conf.globalOptions: stdout else: stderr
- let stdOrrKind = toStdOrrKind(stdOrr)
- if stdOrrKind in conf.lastMsgWasDot:
- conf.lastMsgWasDot.excl stdOrrKind
- write(stdOrr, "\n")
+ TFileInfo* = object
+ fullPath: string # This is a canonical full filesystem path
+ projPath*: string # This is relative to the project's root
+ shortName*: string # short name of the module
+ quotedName*: Rope # cached quoted short name for codegen
+ # purposes
+ quotedFullName*: Rope # cached quoted full name for codegen
+ # purposes
-proc toCChar*(c: char; result: var string) {.inline.} =
+ lines*: seq[Rope] # the source code of the module
+ # used for better error messages and
+ # embedding the original source in the
+ # generated code
+ dirtyfile: string # the file that is actually read into memory
+ # and parsed; usually 'nil' but is used
+ # for 'nimsuggest'
+
+ TLineInfo* = object # This is designed to be as small as possible,
+ # because it is used
+ # in syntax nodes. We save space here by using
+ # two int16 and an int32.
+ # On 64 bit and on 32 bit systems this is
+ # only 8 bytes.
+ line*, col*: int16
+ fileIndex*: int32
+
+ TErrorOutput* = enum
+ eStdOut
+ eStdErr
+ eInMemory
+
+ TErrorOutputs* = set[TErrorOutput]
+
+ ERecoverableError* = object of ValueError
+ ESuggestDone* = object of Exception
+
+const
+ NotesVerbosity*: array[0..3, TNoteKinds] = [
+ {low(TNoteKind)..high(TNoteKind)} - {warnShadowIdent, warnUninit,
+ warnProveField, warnProveIndex,
+ warnGcUnsafe,
+ hintSuccessX, hintPath, hintConf,
+ hintProcessing, hintPattern,
+ hintDependency,
+ hintExecuting, hintLinking,
+ hintCodeBegin, hintCodeEnd,
+ hintSource, hintStackTrace,
+ hintGCStats},
+ {low(TNoteKind)..high(TNoteKind)} - {warnShadowIdent, warnUninit,
+ warnProveField, warnProveIndex,
+ warnGcUnsafe,
+ hintPath,
+ hintDependency,
+ hintExecuting,
+ hintCodeBegin, hintCodeEnd,
+ hintSource, hintStackTrace,
+ hintGCStats},
+ {low(TNoteKind)..high(TNoteKind)} - {hintStackTrace},
+ {low(TNoteKind)..high(TNoteKind)}]
+
+const
+ InvalidFileIDX* = int32(-1)
+
+var
+ ForeignPackageNotes*: TNoteKinds = {hintProcessing, warnUnknownMagic,
+ hintQuitCalled}
+ filenameToIndexTbl = initTable[string, int32]()
+ fileInfos*: seq[TFileInfo] = @[]
+ systemFileIdx*: int32
+
+proc toCChar*(c: char): string =
case c
- of '\0'..'\x1F', '\x7F'..'\xFF':
- result.add '\\'
- result.add toOctal(c)
- of '\'', '\"', '\\', '?':
- result.add '\\'
- result.add c
- else:
- result.add c
+ of '\0'..'\x1F', '\x80'..'\xFF': result = '\\' & toOctal(c)
+ of '\'', '\"', '\\': result = '\\' & c
+ else: result = $(c)
proc makeCString*(s: string): Rope =
- result = newStringOfCap(int(s.len.toFloat * 1.1) + 1)
- result.add("\"")
- for i in 0..=% conf.m.msgContext.len: result = unknownLineInfo
- else: result = conf.m.msgContext[i].info
+proc getInfoContext*(index: int): TLineInfo =
+ let L = msgContext.len
+ let i = if index < 0: L + index else: index
+ if i >=% L: result = unknownLineInfo()
+ else: result = msgContext[i]
-template toFilename*(conf: ConfigRef; fileIdx: FileIndex): string =
- if fileIdx.int32 < 0 or conf == nil:
- (if fileIdx == commandLineIdx: commandLineDesc else: "???")
+proc toFilename*(fileIdx: int32): string =
+ if fileIdx < 0: result = "???"
+ else: result = fileInfos[fileIdx].projPath
+
+proc toFullPath*(fileIdx: int32): string =
+ if fileIdx < 0: result = "???"
+ else: result = fileInfos[fileIdx].fullPath
+
+proc setDirtyFile*(fileIdx: int32; filename: string) =
+ assert fileIdx >= 0
+ fileInfos[fileIdx].dirtyFile = filename
+
+proc toFullPathConsiderDirty*(fileIdx: int32): string =
+ if fileIdx < 0:
+ result = "???"
+ elif not fileInfos[fileIdx].dirtyFile.isNil:
+ result = fileInfos[fileIdx].dirtyFile
else:
- conf.m.fileInfos[fileIdx.int32].shortName
+ result = fileInfos[fileIdx].fullPath
-proc toProjPath*(conf: ConfigRef; fileIdx: FileIndex): string =
- if fileIdx.int32 < 0 or conf == nil:
- (if fileIdx == commandLineIdx: commandLineDesc else: "???")
- else: conf.m.fileInfos[fileIdx.int32].projPath.string
+template toFilename*(info: TLineInfo): string =
+ info.fileIndex.toFilename
-proc toFullPath*(conf: ConfigRef; fileIdx: FileIndex): string =
- if fileIdx.int32 < 0 or conf == nil:
- result = (if fileIdx == commandLineIdx: commandLineDesc else: "???")
+template toFullPath*(info: TLineInfo): string =
+ info.fileIndex.toFullPath
+
+proc toMsgFilename*(info: TLineInfo): string =
+ if info.fileIndex < 0:
+ result = "???"
+ elif gListFullPaths:
+ result = fileInfos[info.fileIndex].fullPath
else:
- result = conf.m.fileInfos[fileIdx.int32].fullPath.string
-
-proc setDirtyFile*(conf: ConfigRef; fileIdx: FileIndex; filename: AbsoluteFile) =
- assert fileIdx.int32 >= 0
- conf.m.fileInfos[fileIdx.int32].dirtyFile = filename
- setLen conf.m.fileInfos[fileIdx.int32].lines, 0
-
-proc setHash*(conf: ConfigRef; fileIdx: FileIndex; hash: string) =
- assert fileIdx.int32 >= 0
- when defined(gcArc) or defined(gcOrc):
- conf.m.fileInfos[fileIdx.int32].hash = hash
- else:
- shallowCopy(conf.m.fileInfos[fileIdx.int32].hash, hash)
-
-
-proc getHash*(conf: ConfigRef; fileIdx: FileIndex): string =
- assert fileIdx.int32 >= 0
- when defined(gcArc) or defined(gcOrc):
- result = conf.m.fileInfos[fileIdx.int32].hash
- else:
- shallowCopy(result, conf.m.fileInfos[fileIdx.int32].hash)
-
-proc toFullPathConsiderDirty*(conf: ConfigRef; fileIdx: FileIndex): AbsoluteFile =
- if fileIdx.int32 < 0:
- result = AbsoluteFile(if fileIdx == commandLineIdx: commandLineDesc else: "???")
- elif not conf.m.fileInfos[fileIdx.int32].dirtyFile.isEmpty:
- result = conf.m.fileInfos[fileIdx.int32].dirtyFile
- else:
- result = conf.m.fileInfos[fileIdx.int32].fullPath
-
-template toFilename*(conf: ConfigRef; info: TLineInfo): string =
- toFilename(conf, info.fileIndex)
-
-template toProjPath*(conf: ConfigRef; info: TLineInfo): string =
- toProjPath(conf, info.fileIndex)
-
-template toFullPath*(conf: ConfigRef; info: TLineInfo): string =
- toFullPath(conf, info.fileIndex)
-
-template toFullPathConsiderDirty*(conf: ConfigRef; info: TLineInfo): string =
- string toFullPathConsiderDirty(conf, info.fileIndex)
-
-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:
- let
- absPath = toFullPath(conf, fileIdx)
- relPath = toProjPath(conf, fileIdx)
- result = if (relPath.len > absPath.len) or (relPath.count("..") > 2):
- absPath
- else:
- relPath
- 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)
-
-template toMsgFilename*(conf: ConfigRef; info: TLineInfo): string =
- toMsgFilename(conf, info.fileIndex)
+ result = fileInfos[info.fileIndex].projPath
proc toLinenumber*(info: TLineInfo): int {.inline.} =
- result = int info.line
+ result = info.line
proc toColumn*(info: TLineInfo): int {.inline.} =
result = info.col
-proc toFileLineCol(info: InstantiationInfo): string {.inline.} =
- result.toLocation(info.filename, info.line, info.column + ColOffset)
+proc toFileLine*(info: TLineInfo): string {.inline.} =
+ result = info.toFilename & ":" & $info.line
-proc toFileLineCol*(conf: ConfigRef; info: TLineInfo): string {.inline.} =
- result.toLocation(toMsgFilename(conf, info), info.line.int, info.col.int + ColOffset)
+proc toFileLineCol*(info: TLineInfo): string {.inline.} =
+ result = info.toFilename & "(" & $info.line & "," & $info.col & ")"
-proc `$`*(conf: ConfigRef; info: TLineInfo): string = toFileLineCol(conf, info)
+proc `$`*(info: TLineInfo): string = toFileLineCol(info)
-proc `$`*(info: TLineInfo): string {.error.} = discard
-
-proc `??`* (conf: ConfigRef; info: TLineInfo, filename: string): bool =
+proc `??`* (info: TLineInfo, filename: string): bool =
# only for debugging purposes
- result = filename in toFilename(conf, info)
+ result = filename in info.toFilename
+
+var gTrackPos*: TLineInfo
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 = {}) =
+proc msgWriteln*(s: string, flags: MsgFlags = {}) =
## Writes given message string to stderr by default.
## If ``--stdout`` option is given, writes to stdout instead. If message hook
## is present, then it is used to output message rather than stderr/stdout.
## This behavior can be altered by given optional flags.
- ## 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)
- elif optStdout in conf.globalOptions or msgStdout in flags:
- if eStdOut in conf.m.errorOutputs:
- flushDot(conf)
- write stdout, s
- writeLine(stdout, sep)
+ #if gCmd == cmdIdeTools and optCDebug notin gGlobalOptions: return
+
+ if not isNil(writelnHook) and msgSkipHook notin flags:
+ writelnHook(s)
+ elif optStdout in gGlobalOptions or msgStdout in flags:
+ if eStdOut in errorOutputs:
+ writeLine(stdout, s)
flushFile(stdout)
else:
- if eStdErr in conf.m.errorOutputs:
- flushDot(conf)
- write stderr, s
- writeLine(stderr, sep)
+ if eStdErr in errorOutputs:
+ writeLine(stderr, s)
# On Windows stderr is fully-buffered when piped, regardless of C std.
when defined(windows):
flushFile(stderr)
macro callIgnoringStyle(theProc: typed, first: typed,
- args: varargs[typed]): untyped =
+ args: varargs[expr]): stmt =
let typForegroundColor = bindSym"ForegroundColor".getType
let typBackgroundColor = bindSym"BackgroundColor".getType
let typStyle = bindSym"Style".getType
@@ -359,375 +777,284 @@ macro callIgnoringStyle(theProc: typed, first: typed,
typ != typTerminalCmd:
result.add(arg)
-macro callStyledWriteLineStderr(args: varargs[typed]): untyped =
+macro callStyledWriteLineStderr(args: varargs[expr]): stmt =
result = newCall(bindSym"styledWriteLine")
result.add(bindSym"stderr")
for arg in children(args[0][1]):
result.add(arg)
- when false:
- # not needed because styledWriteLine already ends with resetAttributes
- result = newStmtList(result, newCall(bindSym"resetAttributes", bindSym"stderr"))
template callWritelnHook(args: varargs[string, `$`]) =
- conf.writelnHook concat(args)
+ var s = ""
+ for arg in args:
+ s.add arg
+ writelnHook s
-proc msgWrite(conf: ConfigRef; s: string) =
- if conf.m.errorOutputs != {}:
- let stdOrr =
- if optStdout in conf.globalOptions:
- stdout
- else:
- stderr
- write(stdOrr, s)
- flushFile(stdOrr)
- conf.lastMsgWasDot.incl stdOrr.toStdOrrKind() # subsequent writes need `flushDot`
-
-template styledMsgWriteln(args: varargs[typed]) =
- if not isNil(conf.writelnHook):
+template styledMsgWriteln*(args: varargs[expr]) =
+ if not isNil(writelnHook):
callIgnoringStyle(callWritelnHook, nil, args)
- elif optStdout in conf.globalOptions:
- if eStdOut in conf.m.errorOutputs:
- flushDot(conf)
+ elif optStdout in gGlobalOptions:
+ if eStdOut in errorOutputs:
callIgnoringStyle(writeLine, stdout, args)
flushFile(stdout)
- elif eStdErr in conf.m.errorOutputs:
- flushDot(conf)
- if optUseColors in conf.globalOptions:
- callStyledWriteLineStderr(args)
- else:
- callIgnoringStyle(writeLine, stderr, args)
- # On Windows stderr is fully-buffered when piped, regardless of C std.
- when defined(windows):
- flushFile(stderr)
+ else:
+ if eStdErr in errorOutputs:
+ if optUseColors in gGlobalOptions:
+ callStyledWriteLineStderr(args)
+ else:
+ callIgnoringStyle(writeLine, stderr, args)
+ # On Windows stderr is fully-buffered when piped, regardless of C std.
+ when defined(windows):
+ flushFile(stderr)
-proc msgKindToString*(kind: TMsgKind): string = MsgKindToStr[kind]
+proc coordToStr(coord: int): string =
+ if coord == -1: result = "???"
+ else: result = $coord
+
+proc msgKindToString*(kind: TMsgKind): string =
# later versions may provide translated error messages
+ result = MsgKindToStr[kind]
-proc getMessageStr(msg: TMsgKind, arg: string): string = msgKindToString(msg) % [arg]
+proc getMessageStr(msg: TMsgKind, arg: string): string =
+ result = msgKindToString(msg) % [arg]
-type TErrorHandling* = enum doNothing, doAbort, doRaise
+type
+ TErrorHandling = enum doNothing, doAbort, doRaise
-proc log*(s: string) =
+proc quit(msg: TMsgKind) =
+ if defined(debug) or msg == errInternal or hintStackTrace in gNotes:
+ if stackTraceAvailable() and isNil(writelnHook):
+ writeStackTrace()
+ else:
+ styledMsgWriteln(fgRed, "No stack traceback available\n" &
+ "To create a stacktrace, rerun compilation with ./koch temp " &
+ options.command & " ")
+ quit 1
+
+proc log*(s: string) {.procvar.} =
var f: File
- if open(f, getHomeDir() / "nimsuggest.log", fmAppend):
+ if open(f, "nimsuggest.log", fmAppend):
f.writeLine(s)
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):
- {.gcsafe.}:
- if stackTraceAvailable() and isNil(conf.writelnHook):
- writeStackTrace()
- else:
- 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)
- quit 1
-
-proc handleError(conf: ConfigRef; msg: TMsgKind, eh: TErrorHandling, s: string, ignoreMsg: bool) =
- if msg in fatalMsgs:
- if conf.cmd == cmdIdeTools: log(s)
- quit(conf, msg)
- if msg >= errMin and msg <= errMax or
- (msg in warnMin..hintMax and msg in conf.warningAsErrors and not ignoreMsg):
- 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)
- elif eh == doAbort and conf.cmd != cmdIdeTools:
- quit(conf, msg)
+proc handleError(msg: TMsgKind, eh: TErrorHandling, s: string) =
+ if msg >= fatalMin and msg <= fatalMax:
+ if gCmd == cmdIdeTools: log(s)
+ quit(msg)
+ if msg >= errMin and msg <= errMax:
+ inc(gErrorCounter)
+ options.gExitcode = 1'i8
+ if gErrorCounter >= gErrorMax:
+ quit(msg)
+ elif eh == doAbort and gCmd != cmdIdeTools:
+ quit(msg)
elif eh == doRaise:
raiseRecoverableError(s)
proc `==`*(a, b: TLineInfo): bool =
result = a.line == b.line and a.fileIndex == b.fileIndex
-proc exactEquals*(a, b: TLineInfo): bool =
- result = a.fileIndex == b.fileIndex and a.line == b.line and a.col == b.col
-
-proc writeContext(conf: ConfigRef; lastinfo: TLineInfo) =
- const instantiationFrom = "template/generic instantiation from here"
- const instantiationOfFrom = "template/generic instantiation of `$1` from here"
+proc writeContext(lastinfo: TLineInfo) =
var info = lastinfo
- for i in 0..= errGenerated and conf.cmd == cmdIdeTools and optIdeDebug notin conf.globalOptions
+proc ignoreMsgBecauseOfIdeTools(msg: TMsgKind): bool =
+ msg >= errGenerated and gCmd == cmdIdeTools and optIdeDebug notin gGlobalOptions
-proc addSourceLine(conf: ConfigRef; fileIdx: FileIndex, line: string) =
- conf.m.fileInfos[fileIdx.int32].lines.add line
+proc rawMessage*(msg: TMsgKind, args: openArray[string]) =
+ var
+ title: string
+ color: ForegroundColor
+ kind: string
+ case msg
+ of errMin..errMax:
+ writeContext(unknownLineInfo())
+ title = ErrorTitle
+ color = ErrorColor
+ of warnMin..warnMax:
+ if optWarns notin gOptions: return
+ if msg notin gNotes: return
+ writeContext(unknownLineInfo())
+ title = WarningTitle
+ color = WarningColor
+ kind = WarningsToStr[ord(msg) - ord(warnMin)]
+ inc(gWarnCounter)
+ of hintMin..hintMax:
+ if optHints notin gOptions: return
+ if msg notin gNotes: return
+ title = HintTitle
+ color = HintColor
+ kind = HintsToStr[ord(msg) - ord(hintMin)]
+ inc(gHintCounter)
+ let s = `%`(msgKindToString(msg), args)
+ if not ignoreMsgBecauseOfIdeTools(msg):
+ if kind != nil:
+ styledMsgWriteln(color, title, resetStyle, s,
+ KindColor, `%`(KindFormat, kind))
+ else:
+ styledMsgWriteln(color, title, resetStyle, s)
+ handleError(msg, doAbort, s)
-proc numLines*(conf: ConfigRef, fileIdx: FileIndex): int =
- ## xxx there's an off by 1 error that should be fixed; if a file ends with "foo" or "foo\n"
- ## it will return same number of lines (ie, a trailing empty line is discounted)
- result = conf.m.fileInfos[fileIdx.int32].lines.len
- if result == 0:
- try:
- for line in lines(toFullPathConsiderDirty(conf, fileIdx).string):
- addSourceLine conf, fileIdx, line
- except IOError:
- discard
- result = conf.m.fileInfos[fileIdx.int32].lines.len
+proc rawMessage*(msg: TMsgKind, arg: string) =
+ rawMessage(msg, [arg])
-proc sourceLine*(conf: ConfigRef; i: TLineInfo): string =
- ## 1-based index (matches editor line numbers); 1st line is for i.line = 1
- ## last valid line is `numLines` inclusive
- if i.fileIndex.int32 < 0: return ""
- let num = numLines(conf, i.fileIndex)
- # can happen if the error points to EOF:
- if i.line.int > num: return ""
+proc resetAttributes* =
+ if {optUseColors, optStdout} * gGlobalOptions == {optUseColors}:
+ terminal.resetAttributes(stderr)
- result = conf.m.fileInfos[i.fileIndex.int32].lines[i.line.int-1]
+proc writeSurroundingSrc(info: TLineInfo) =
+ const indent = " "
+ msgWriteln(indent & $info.sourceLine)
+ msgWriteln(indent & spaces(info.col) & '^')
-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 formatMsg*(conf: ConfigRef; info: TLineInfo, msg: TMsgKind, arg: string): string =
+proc formatMsg*(info: TLineInfo, msg: TMsgKind, arg: string): string =
let title = case msg
of warnMin..warnMax: WarningTitle
of hintMin..hintMax: HintTitle
else: ErrorTitle
- conf.toFileLineCol(info) & " " & title & getMessageStr(msg, arg)
+ result = PosFormat % [toMsgFilename(info), coordToStr(info.line),
+ coordToStr(info.col+1)] &
+ title &
+ getMessageStr(msg, arg)
-proc liMessage*(conf: ConfigRef; info: TLineInfo, msg: TMsgKind, arg: string,
- eh: TErrorHandling, info2: InstantiationInfo, isRaw = false) {.gcsafe, noinline.} =
+proc liMessage(info: TLineInfo, msg: TMsgKind, arg: string,
+ eh: TErrorHandling) =
var
title: string
color: ForegroundColor
+ kind: string
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:
- sev = Severity.Error
- writeContext(conf, info)
+ writeContext(info)
title = ErrorTitle
color = ErrorColor
- when false:
- # we try to filter error messages so that not two error message
- # in the same file and line are produced:
- # xxx `lastError` is only used in this disabled code; but could be useful to revive
- ignoreMsg = conf.m.lastError == info and info != unknownLineInfo and eh != doAbort
- if info != unknownLineInfo: conf.m.lastError = info
+ # we try to filter error messages so that not two error message
+ # in the same file and line are produced:
+ #ignoreMsg = lastError == info and eh != doAbort
+ lastError = info
of warnMin..warnMax:
- sev = Severity.Warning
- ignoreMsg = not conf.hasWarn(msg)
- if not ignoreMsg and msg in conf.warningAsErrors:
- title = ErrorTitle
- color = ErrorColor
- else:
- title = WarningTitle
- color = WarningColor
- if not ignoreMsg: writeContext(conf, info)
- inc(conf.warnCounter)
+ ignoreMsg = optWarns notin gOptions or msg notin gNotes
+ if not ignoreMsg: writeContext(info)
+ title = WarningTitle
+ color = WarningColor
+ kind = WarningsToStr[ord(msg) - ord(warnMin)]
+ inc(gWarnCounter)
of hintMin..hintMax:
- sev = Severity.Hint
- ignoreMsg = not conf.hasHint(msg)
- if not ignoreMsg and msg in conf.warningAsErrors:
- title = ErrorTitle
- else:
- title = HintTitle
+ ignoreMsg = optHints notin gOptions or msg notin gNotes
+ title = HintTitle
color = HintColor
- inc(conf.hintCounter)
+ kind = HintsToStr[ord(msg) - ord(hintMin)]
+ inc(gHintCounter)
+ # NOTE: currently line info line numbers start with 1,
+ # but column numbers start with 0, however most editors expect
+ # first column to be 1, so we need to +1 here
+ let x = PosFormat % [toMsgFilename(info), coordToStr(info.line),
+ coordToStr(info.col+1)]
+ let s = getMessageStr(msg, arg)
+ if not ignoreMsg and not ignoreMsgBecauseOfIdeTools(msg):
+ if kind != nil:
+ styledMsgWriteln(styleBright, x, resetStyle, color, title, resetStyle, s,
+ KindColor, `%`(KindFormat, kind))
+ else:
+ styledMsgWriteln(styleBright, x, resetStyle, color, title, resetStyle, s)
+ if msg in errMin..errMax and hintSource in gNotes:
+ info.writeSurroundingSrc
+ handleError(msg, eh, s)
- let s = if isRaw: arg else: getMessageStr(msg, arg)
- if not ignoreMsg:
- let loc = if info != unknownLineInfo: conf.toFileLineCol(info) & " " else: ""
- # we could also show `conf.cmdInput` here for `projectIsCmd`
- var kindmsg = if kind.len > 0: KindFormat % kind else: ""
- if conf.structuredErrorHook != nil:
- conf.structuredErrorHook(conf, info, s & kindmsg, sev)
- if not ignoreMsgBecauseOfIdeTools(conf, msg):
- if msg == hintProcessing and conf.hintProcessingDots:
- msgWrite(conf, ".")
- else:
- styledMsgWriteln(styleBright, loc, resetStyle, color, title, resetStyle, s, KindColor, kindmsg,
- resetStyle, conf.getSurroundingSrc(info), conf.unitSep)
- 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
+proc fatal*(info: TLineInfo, msg: TMsgKind, arg = "") =
+ liMessage(info, msg, arg, doAbort)
-template rawMessage*(conf: ConfigRef; msg: TMsgKind, args: openArray[string]) =
- let arg = msgKindToString(msg) % args
- liMessage(conf, unknownLineInfo, msg, arg, eh = doAbort, instLoc(), isRaw = true)
+proc globalError*(info: TLineInfo, msg: TMsgKind, arg = "") =
+ liMessage(info, msg, arg, doRaise)
-template rawMessage*(conf: ConfigRef; msg: TMsgKind, arg: string) =
- liMessage(conf, unknownLineInfo, msg, arg, eh = doAbort, instLoc())
+proc globalError*(info: TLineInfo, arg: string) =
+ liMessage(info, errGenerated, arg, doRaise)
-template fatal*(conf: ConfigRef; info: TLineInfo, arg = "", msg = errFatal) =
- liMessage(conf, info, msg, arg, doAbort, instLoc())
+proc localError*(info: TLineInfo, msg: TMsgKind, arg = "") =
+ liMessage(info, msg, arg, doNothing)
-template globalAssert*(conf: ConfigRef; cond: untyped, info: TLineInfo = unknownLineInfo, arg = "") =
- ## avoids boilerplate
- if not cond:
- var arg2 = "'$1' failed" % [astToStr(cond)]
- if arg.len > 0: arg2.add "; " & astToStr(arg) & ": " & arg
- liMessage(conf, info, errGenerated, arg2, doRaise, instLoc())
+proc localError*(info: TLineInfo, arg: string) =
+ liMessage(info, errGenerated, arg, doNothing)
-template globalError*(conf: ConfigRef; info: TLineInfo, msg: TMsgKind, arg = "") =
- ## `local` means compilation keeps going until errorMax is reached (via `doNothing`),
- ## `global` means it stops.
- liMessage(conf, info, msg, arg, doRaise, instLoc())
+proc localError*(info: TLineInfo, format: string, params: openarray[string]) =
+ localError(info, format % params)
-template globalError*(conf: ConfigRef; info: TLineInfo, arg: string) =
- liMessage(conf, info, errGenerated, arg, doRaise, instLoc())
+proc message*(info: TLineInfo, msg: TMsgKind, arg = "") =
+ liMessage(info, msg, arg, doNothing)
-template localError*(conf: ConfigRef; info: TLineInfo, msg: TMsgKind, arg = "") =
- liMessage(conf, info, msg, arg, doNothing, instLoc())
+proc internalError*(info: TLineInfo, errMsg: string) =
+ if gCmd == cmdIdeTools: return
+ writeContext(info)
+ liMessage(info, errInternal, errMsg, doAbort)
-template localError*(conf: ConfigRef; info: TLineInfo, arg: string) =
- liMessage(conf, info, errGenerated, arg, doNothing, instLoc())
+proc internalError*(errMsg: string) =
+ if gCmd == cmdIdeTools: return
+ writeContext(unknownLineInfo())
+ rawMessage(errInternal, errMsg)
-template message*(conf: ConfigRef; info: TLineInfo, msg: TMsgKind, arg = "") =
- liMessage(conf, info, msg, arg, doNothing, instLoc())
+template assertNotNil*(e: expr): expr =
+ if e == nil: internalError($instantiationInfo())
+ e
-proc warningDeprecated*(conf: ConfigRef, info: TLineInfo = gCmdLineInfo, msg = "") {.inline.} =
- message(conf, info, warnDeprecated, msg)
+template internalAssert*(e: bool): stmt =
+ if not e: internalError($instantiationInfo())
-proc internalErrorImpl(conf: ConfigRef; info: TLineInfo, errMsg: string, info2: InstantiationInfo) =
- if conf.cmd == cmdIdeTools and conf.structuredErrorHook.isNil: return
- writeContext(conf, info)
- liMessage(conf, info, errInternal, errMsg, doAbort, info2)
+proc addSourceLine*(fileIdx: int32, line: string) =
+ fileInfos[fileIdx].lines.add line.rope
-template internalError*(conf: ConfigRef; info: TLineInfo, errMsg: string) =
- internalErrorImpl(conf, info, errMsg, instLoc())
+proc sourceLine*(i: TLineInfo): Rope =
+ if i.fileIndex < 0: return nil
-template internalError*(conf: ConfigRef; errMsg: string) =
- internalErrorImpl(conf, unknownLineInfo, errMsg, instLoc())
+ if not optPreserveOrigSource and fileInfos[i.fileIndex].lines.len == 0:
+ try:
+ for line in lines(i.toFullPath):
+ addSourceLine i.fileIndex, line.string
+ except IOError:
+ discard
+ internalAssert i.fileIndex < fileInfos.len
+ # can happen if the error points to EOF:
+ if i.line > fileInfos[i.fileIndex].lines.len: return nil
-template internalAssert*(conf: ConfigRef, e: bool) =
- # xxx merge with `globalAssert`
- if not e:
- const info2 = instLoc()
- let arg = info2.toFileLineCol
- internalErrorImpl(conf, unknownLineInfo, arg, info2)
+ result = fileInfos[i.fileIndex].lines[i.line-1]
-template lintReport*(conf: ConfigRef; info: TLineInfo, beau, got: string, extraMsg = "") =
- let m = "'$1' should be: '$2'$3" % [got, beau, extraMsg]
- let msg = if optStyleError in conf.globalOptions: errGenerated else: hintName
- liMessage(conf, info, msg, m, doNothing, instLoc())
-
-proc quotedFilename*(conf: ConfigRef; i: TLineInfo): Rope =
- if i.fileIndex.int32 < 0:
- result = makeCString "???"
- elif optExcessiveStackTrace in conf.globalOptions:
- result = conf.m.fileInfos[i.fileIndex.int32].quotedFullName
+proc quotedFilename*(i: TLineInfo): Rope =
+ internalAssert i.fileIndex >= 0
+ if optExcessiveStackTrace in gGlobalOptions:
+ result = fileInfos[i.fileIndex].quotedFullName
else:
- result = conf.m.fileInfos[i.fileIndex.int32].quotedName
+ result = fileInfos[i.fileIndex].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})
+ropes.errorHandler = proc (err: RopesError, msg: string, useWarning: bool) =
+ case err
+ of rInvalidFormatStr:
+ internalError("ropes: invalid format string: " & msg)
+ of rCannotOpenFile:
+ rawMessage(if useWarning: warnCannotOpenFile else: errCannotOpenFile, msg)
-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,
+proc listWarnings*() =
+ msgWriteln("Warnings:")
+ for warn in warnMin..warnMax:
+ msgWriteln(" [$1] $2" % [
+ if warn in gNotes: "x" else: " ",
+ msgs.WarningsToStr[ord(warn) - ord(warnMin)]
])
+
+proc listHints*() =
+ msgWriteln("Hints:")
+ for hint in hintMin..hintMax:
+ msgWriteln(" [$1] $2" % [
+ if hint in gNotes: "x" else: " ",
+ msgs.HintsToStr[ord(hint) - ord(hintMin)]
+ ])
+
+# enable colors by default on terminals
+if terminal.isatty(stderr):
+ incl(gGlobalOptions, optUseColors)
diff --git a/compiler/ndi.nim b/compiler/ndi.nim
deleted file mode 100644
index a9d9cfe79..000000000
--- a/compiler/ndi.nim
+++ /dev/null
@@ -1,52 +0,0 @@
-#
-#
-# The Nim Compiler
-# (c) Copyright 2017 Andreas Rumpf
-#
-# See the file "copying.txt", included in this
-# distribution, for details about the copyright.
-#
-
-## This module implements the generation of ``.ndi`` files for better debugging
-## support of Nim code. "ndi" stands for "Nim debug info".
-
-import ast, msgs, ropes, options, pathutils
-
-when defined(nimPreviewSlimSystem):
- import std/[syncio, assertions]
-
-type
- NdiFile* = object
- enabled: bool
- f: File
- buf: string
- filename: AbsoluteFile
- syms: seq[PSym]
-
-proc doWrite(f: var NdiFile; s: PSym; conf: ConfigRef) =
- f.buf.setLen 0
- f.buf.addInt s.info.line.int
- f.buf.add "\t"
- f.buf.addInt s.info.col.int
- f.f.write(s.name.s, "\t")
- f.f.writeRope(s.loc.r)
- f.f.writeLine("\t", toFullPath(conf, s.info), "\t", f.buf)
-
-template writeMangledName*(f: NdiFile; s: PSym; conf: ConfigRef) =
- if f.enabled: f.syms.add s
-
-proc open*(f: var NdiFile; filename: AbsoluteFile; conf: ConfigRef) =
- f.enabled = not filename.isEmpty
- if f.enabled:
- f.filename = filename
- f.buf = newStringOfCap(20)
-
-proc close*(f: var NdiFile, conf: ConfigRef) =
- if f.enabled:
- f.f = open(f.filename.string, fmWrite, 8000)
- doAssert f.f != nil, f.filename.string
- for s in f.syms:
- doWrite(f, s, conf)
- close(f.f)
- f.syms.reset
- f.filename.reset
diff --git a/compiler/nilcheck.nim b/compiler/nilcheck.nim
deleted file mode 100644
index 91d76b2b8..000000000
--- a/compiler/nilcheck.nim
+++ /dev/null
@@ -1,1385 +0,0 @@
-#
-#
-# The Nim Compiler
-# (c) Copyright 2017 Andreas Rumpf
-#
-# See the file "copying.txt", included in this
-# distribution, for details about the copyright.
-#
-
-import ast, renderer, intsets, tables, msgs, options, lineinfos, strformat, idents, treetab, hashes
-import sequtils, strutils, sets
-
-when defined(nimPreviewSlimSystem):
- import std/assertions
-
-# IMPORTANT: notes not up to date, i'll update this comment again
-#
-# notes:
-#
-# Env: int => nilability
-# a = b
-# nilability a <- nilability b
-# deref a
-# if Nil error is nil
-# if MaybeNil error might be nil, hint add if isNil
-# if Safe fine
-# fun(arg: A)
-# nilability arg <- for ref MaybeNil, for not nil or others Safe
-# map is env?
-# a or b
-# each one forks a different env
-# result = union(envL, envR)
-# a and b
-# b forks a's env
-# if a: code
-# result = union(previousEnv after not a, env after code)
-# if a: b else: c
-# result = union(env after b, env after c)
-# result = b
-# nilability result <- nilability b, if return type is not nil and result not safe, error
-# return b
-# as result = b
-# try: a except: b finally: c
-# in b and c env is union of all possible try first n lines, after union of a and b and c
-# keep in mind canRaise and finally
-# case a: of b: c
-# similar to if
-# call(arg)
-# if it returns ref, assume it's MaybeNil: hint that one can add not nil to the return type
-# call(var arg) # zahary comment
-# if arg is ref, assume it's MaybeNil after call
-# loop
-# union of env for 0, 1, 2 iterations as Herb Sutter's paper
-# why 2?
-# return
-# if something: stop (break return etc)
-# is equivalent to if something: .. else: remain
-# new(ref)
-# ref becomes Safe
-# objConstr(a: b)
-# returns safe
-# each check returns its nilability and map
-
-type
- SeqOfDistinct[T, U] = distinct seq[U]
-
-# TODO use distinct base type instead of int?
-func `[]`[T, U](a: SeqOfDistinct[T, U], index: T): U =
- (seq[U])(a)[index.int]
-
-proc `[]=`[T, U](a: var SeqOfDistinct[T, U], index: T, value: U) =
- ((seq[U])(a))[index.int] = value
-
-func `[]`[T, U](a: var SeqOfDistinct[T, U], index: T): var U =
- (seq[U])(a)[index.int]
-
-func len[T, U](a: SeqOfDistinct[T, U]): T =
- (seq[U])(a).len.T
-
-func low[T, U](a: SeqOfDistinct[T, U]): T =
- (seq[U])(a).low.T
-
-func high[T, U](a: SeqOfDistinct[T, U]): T =
- (seq[U])(a).high.T
-
-proc setLen[T, U](a: var SeqOfDistinct[T, U], length: T) =
- ((seq[U])(a)).setLen(length.Natural)
-
-
-proc newSeqOfDistinct[T, U](length: T = 0.T): SeqOfDistinct[T, U] =
- (SeqOfDistinct[T, U])(newSeq[U](length.int))
-
-func newSeqOfDistinct[T, U](length: int = 0): SeqOfDistinct[T, U] =
- # newSeqOfDistinct(length.T)
- # ? newSeqOfDistinct[T, U](length.T)
- (SeqOfDistinct[T, U])(newSeq[U](length))
-
-iterator items[T, U](a: SeqOfDistinct[T, U]): U =
- for element in (seq[U])(a):
- yield element
-
-iterator pairs[T, U](a: SeqOfDistinct[T, U]): (T, U) =
- for i, element in (seq[U])(a):
- yield (i.T, element)
-
-func `$`[T, U](a: SeqOfDistinct[T, U]): string =
- $((seq[U])(a))
-
-proc add*[T, U](a: var SeqOfDistinct[T, U], value: U) =
- ((seq[U])(a)).add(value)
-
-type
- ## a hashed representation of a node: should be equal for structurally equal nodes
- Symbol = distinct int
-
- ## the index of an expression in the pre-indexed sequence of those
- ExprIndex = distinct int16
-
- ## the set index
- SetIndex = distinct int
-
- ## transition kind:
- ## what was the reason for changing the nilability of an expression
- ## useful for error messages and showing why an expression is being detected as nil / maybe nil
- TransitionKind = enum TArg, TAssign, TType, TNil, TVarArg, TResult, TSafe, TPotentialAlias, TDependant
-
- ## keep history for each transition
- History = object
- info: TLineInfo ## the location
- nilability: Nilability ## the nilability
- kind: TransitionKind ## what kind of transition was that
- node: PNode ## the node of the expression
-
- ## the context for the checker: an instance for each procedure
- NilCheckerContext = ref object
- # abstractTime: AbstractTime
- # partitions: Partitions
- # symbolGraphs: Table[Symbol, ]
- symbolIndices: Table[Symbol, ExprIndex] ## index for each symbol
- expressions: SeqOfDistinct[ExprIndex, PNode] ## a sequence of pre-indexed expressions
- dependants: SeqOfDistinct[ExprIndex, IntSet] ## expr indices for expressions which are compound and based on others
- warningLocations: HashSet[TLineInfo] ## warning locations to check we don't warn twice for stuff like warnings in for loops
- idgen: IdGenerator ## id generator
- config: ConfigRef ## the config of the compiler
-
- ## a map that is containing the current nilability for usually a branch
- ## and is pointing optionally to a parent map: they make a stack of maps
- NilMap = ref object
- expressions: SeqOfDistinct[ExprIndex, Nilability] ## the expressions with the same order as in NilCheckerContext
- history: SeqOfDistinct[ExprIndex, seq[History]] ## history for each of them
- # what about gc and refs?
- setIndices: SeqOfDistinct[ExprIndex, SetIndex] ## set indices for each expression
- sets: SeqOfDistinct[SetIndex, IntSet] ## disjoint sets with the aliased expressions
- parent: NilMap ## the parent map
-
- ## Nilability : if a value is nilable.
- ## we have maybe nil and nil, so we can differentiate between
- ## cases where we know for sure a value is nil and not
- ## otherwise we can have Safe, MaybeNil
- ## Parent: is because we just use a sequence with the same length
- ## instead of a table, and we need to check if something was initialized
- ## at all: if Parent is set, then you need to check the parent nilability
- ## if the parent is nil, then for now we return MaybeNil
- ## unreachable is the result of add(Safe, Nil) and others
- ## it is a result of no states left, so it's usually e.g. in unreachable else branches?
- Nilability* = enum Parent, Safe, MaybeNil, Nil, Unreachable
-
- ## check
- Check = object
- nilability: Nilability
- map: NilMap
- elements: seq[(PNode, Nilability)]
-
-
-# useful to have known resultId so we can set it in the beginning and on return
-const resultId: Symbol = (-1).Symbol
-const resultExprIndex: ExprIndex = 0.ExprIndex
-const noSymbol = (-2).Symbol
-
-func `<`*(a: ExprIndex, b: ExprIndex): bool =
- a.int16 < b.int16
-
-func `<=`*(a: ExprIndex, b: ExprIndex): bool =
- a.int16 <= b.int16
-
-func `>`*(a: ExprIndex, b: ExprIndex): bool =
- a.int16 > b.int16
-
-func `>=`*(a: ExprIndex, b: ExprIndex): bool =
- a.int16 >= b.int16
-
-func `==`*(a: ExprIndex, b: ExprIndex): bool =
- a.int16 == b.int16
-
-func `$`*(a: ExprIndex): string =
- $(a.int16)
-
-func `+`*(a: ExprIndex, b: ExprIndex): ExprIndex =
- (a.int16 + b.int16).ExprIndex
-
-# TODO overflowing / < 0?
-func `-`*(a: ExprIndex, b: ExprIndex): ExprIndex =
- (a.int16 - b.int16).ExprIndex
-
-func `$`*(a: SetIndex): string =
- $(a.int)
-
-func `==`*(a: SetIndex, b: SetIndex): bool =
- a.int == b.int
-
-func `+`*(a: SetIndex, b: SetIndex): SetIndex =
- (a.int + b.int).SetIndex
-
-# TODO over / under limit?
-func `-`*(a: SetIndex, b: SetIndex): SetIndex =
- (a.int - b.int).SetIndex
-
-proc check(n: PNode, ctx: NilCheckerContext, map: NilMap): Check
-proc checkCondition(n: PNode, ctx: NilCheckerContext, map: NilMap, reverse: bool, base: bool): NilMap
-
-# the NilMap structure
-
-proc newNilMap(parent: NilMap = nil, count: int = -1): NilMap =
- var expressionsCount = 0
- if count != -1:
- expressionsCount = count
- elif not parent.isNil:
- expressionsCount = parent.expressions.len.int
- result = NilMap(
- expressions: newSeqOfDistinct[ExprIndex, Nilability](expressionsCount),
- history: newSeqOfDistinct[ExprIndex, seq[History]](expressionsCount),
- setIndices: newSeqOfDistinct[ExprIndex, SetIndex](expressionsCount),
- parent: parent)
- if parent.isNil:
- for i, expr in result.expressions:
- result.setIndices[i] = i.SetIndex
- var newSet = initIntSet()
- newSet.incl(i.int)
- result.sets.add(newSet)
- else:
- for i, exprs in parent.sets:
- result.sets.add(exprs)
- for i, index in parent.setIndices:
- result.setIndices[i] = index
- # result.sets = parent.sets
- # if not parent.isNil:
- # # optimize []?
- # result.expressions = parent.expressions
- # result.history = parent.history
- # result.sets = parent.sets
- # result.base = if parent.isNil: result else: parent.base
-
-proc `[]`(map: NilMap, index: ExprIndex): Nilability =
- if index < 0.ExprIndex or index >= map.expressions.len:
- return MaybeNil
- var now = map
- while not now.isNil:
- if now.expressions[index] != Parent:
- return now.expressions[index]
- now = now.parent
- return MaybeNil
-
-proc history(map: NilMap, index: ExprIndex): seq[History] =
- if index < map.expressions.len:
- map.history[index]
- else:
- @[]
-
-
-# helpers for debugging
-
-# import macros
-
-# echo-s only when nilDebugInfo is defined
-# macro aecho*(a: varargs[untyped]): untyped =
-# var e = nnkCall.newTree(ident"echo")
-# for b in a:
-# e.add(b)
-# result = quote:
-# when defined(nilDebugInfo):
-# `e`
-
-# end of helpers for debugging
-
-
-proc symbol(n: PNode): Symbol
-func `$`(map: NilMap): string
-proc reverseDirect(map: NilMap): NilMap
-proc checkBranch(n: PNode, ctx: NilCheckerContext, map: NilMap): Check
-proc hasUnstructuredControlFlowJump(n: PNode): bool
-
-proc symbol(n: PNode): Symbol =
- ## returns a Symbol for each expression
- ## the goal is to get an unique Symbol
- ## but we have to ensure hashTree does it as we expect
- case n.kind:
- of nkIdent:
- # TODO ensure no idents get passed to symbol
- result = noSymbol
- of nkSym:
- if n.sym.kind == skResult: # credit to disruptek for showing me that
- result = resultId
- else:
- result = n.sym.id.Symbol
- of nkHiddenAddr, nkAddr:
- result = symbol(n[0])
- else:
- result = hashTree(n).Symbol
- # echo "symbol ", n, " ", n.kind, " ", result.int
-
-func `$`(map: NilMap): string =
- var now = map
- var stack: seq[NilMap] = @[]
- while not now.isNil:
- stack.add(now)
- now = now.parent
- result.add("### start\n")
- for i in 0 .. stack.len - 1:
- now = stack[i]
- result.add(" ###\n")
- for index, value in now.expressions:
- result.add(&" {index} {value}\n")
- result.add "### end\n"
-
-proc namedMapDebugInfo(ctx: NilCheckerContext, map: NilMap): string =
- result = ""
- var now = map
- var stack: seq[NilMap] = @[]
- while not now.isNil:
- stack.add(now)
- now = now.parent
- result.add("### start\n")
- for i in 0 .. stack.len - 1:
- now = stack[i]
- result.add(" ###\n")
- for index, value in now.expressions:
- let name = ctx.expressions[index]
- result.add(&" {name} {index} {value}\n")
- result.add("### end\n")
-
-proc namedSetsDebugInfo(ctx: NilCheckerContext, map: NilMap): string =
- result = "### sets "
- for index, setIndex in map.setIndices:
- var aliasSet = map.sets[setIndex]
- result.add("{")
- let expressions = aliasSet.mapIt($ctx.expressions[it.ExprIndex])
- result.add(join(expressions, ", "))
- result.add("} ")
- result.add("\n")
-
-proc namedMapAndSetsDebugInfo(ctx: NilCheckerContext, map: NilMap): string =
- result = namedMapDebugInfo(ctx, map) & namedSetsDebugInfo(ctx, map)
-
-
-
-const noExprIndex = (-1).ExprIndex
-const noSetIndex = (-1).SetIndex
-
-proc `==`(a: Symbol, b: Symbol): bool =
- a.int == b.int
-
-func `$`(a: Symbol): string =
- $(a.int)
-
-template isConstBracket(n: PNode): bool =
- n.kind == nkBracketExpr and n[1].kind in nkLiterals
-
-proc index(ctx: NilCheckerContext, n: PNode): ExprIndex =
- # echo "n ", n, " ", n.kind
- let a = symbol(n)
- if ctx.symbolIndices.hasKey(a):
- return ctx.symbolIndices[a]
- else:
- #for a, e in ctx.expressions:
- # echo a, " ", e
- #echo n.kind
- # internalError(ctx.config, n.info, "expected " & $a & " " & $n & " to have a index")
- return noExprIndex
- #
- #ctx.symbolIndices[symbol(n)]
-
-
-proc aliasSet(ctx: NilCheckerContext, map: NilMap, n: PNode): IntSet =
- result = map.sets[map.setIndices[ctx.index(n)]]
-
-proc aliasSet(ctx: NilCheckerContext, map: NilMap, index: ExprIndex): IntSet =
- result = map.sets[map.setIndices[index]]
-
-
-
-proc store(map: NilMap, ctx: NilCheckerContext, index: ExprIndex, value: Nilability, kind: TransitionKind, info: TLineInfo, node: PNode = nil) =
- if index == noExprIndex:
- return
- map.expressions[index] = value
- map.history[index].add(History(info: info, kind: kind, node: node, nilability: value))
- #echo node, " ", index, " ", value
- #echo ctx.namedMapAndSetsDebugInfo(map)
- #for a, b in map.sets:
- # echo a, " ", b
- # echo map
-
- var exprAliases = aliasSet(ctx, map, index)
- for a in exprAliases:
- if a.ExprIndex != index:
- #echo "alias ", a, " ", index
- map.expressions[a.ExprIndex] = value
- if value == Safe:
- map.history[a.ExprIndex] = @[]
- else:
- map.history[a.ExprIndex].add(History(info: info, kind: TPotentialAlias, node: node, nilability: value))
-
-proc moveOut(ctx: NilCheckerContext, map: NilMap, target: PNode) =
- #echo "move out ", target
- var targetIndex = ctx.index(target)
- var targetSetIndex = map.setIndices[targetIndex]
- if targetSetIndex != noSetIndex:
- var targetSet = map.sets[targetSetIndex]
- if targetSet.len > 1:
- var other: ExprIndex
-
- for element in targetSet:
- if element.ExprIndex != targetIndex:
- other = element.ExprIndex
- break
- # map.sets[element].excl(targetIndex)
- map.sets[map.setIndices[other]].excl(targetIndex.int)
- var newSet = initIntSet()
- newSet.incl(targetIndex.int)
- map.sets.add(newSet)
- map.setIndices[targetIndex] = map.sets.len - 1.SetIndex
-
-proc moveOutDependants(ctx: NilCheckerContext, map: NilMap, node: PNode) =
- let index = ctx.index(node)
- for dependant in ctx.dependants[index]:
- moveOut(ctx, map, ctx.expressions[dependant.ExprIndex])
-
-proc storeDependants(ctx: NilCheckerContext, map: NilMap, node: PNode, value: Nilability) =
- let index = ctx.index(node)
- for dependant in ctx.dependants[index]:
- map.store(ctx, dependant.ExprIndex, value, TDependant, node.info, node)
-
-proc move(ctx: NilCheckerContext, map: NilMap, target: PNode, assigned: PNode) =
- #echo "move ", target, " ", assigned
- var targetIndex = ctx.index(target)
- var assignedIndex: ExprIndex
- var targetSetIndex = map.setIndices[targetIndex]
- var assignedSetIndex: SetIndex
- if assigned.kind == nkSym:
- assignedIndex = ctx.index(assigned)
- assignedSetIndex = map.setIndices[assignedIndex]
- else:
- assignedIndex = noExprIndex
- assignedSetIndex = noSetIndex
- if assignedIndex == noExprIndex:
- moveOut(ctx, map, target)
- elif targetSetIndex != assignedSetIndex:
- map.sets[targetSetIndex].excl(targetIndex.int)
- map.sets[assignedSetIndex].incl(targetIndex.int)
- map.setIndices[targetIndex] = assignedSetIndex
-
-# proc hasKey(map: NilMap, ): bool =
-# var now = map
-# result = false
-# while not now.isNil:
-# if now.locals.hasKey(graphIndex):
-# return true
-# now = now.previous
-
-iterator pairs(map: NilMap): (ExprIndex, Nilability) =
- for index, value in map.expressions:
- yield (index, map[index])
-
-proc copyMap(map: NilMap): NilMap =
- if map.isNil:
- return nil
- result = newNilMap(map.parent) # no need for copy? if we change only this
- result.expressions = map.expressions
- result.history = map.history
- result.sets = map.sets
- result.setIndices = map.setIndices
-
-using
- n: PNode
- conf: ConfigRef
- ctx: NilCheckerContext
- map: NilMap
-
-proc typeNilability(typ: PType): Nilability
-
-# maybe: if canRaise, return MaybeNil ?
-# no, because the target might be safe already
-# with or without an exception
-proc checkCall(n, ctx, map): Check =
- # checks each call
- # special case for new(T) -> result is always Safe
- # for the others it depends on the return type of the call
- # check args and handle possible mutations
-
- var isNew = false
- result.map = map
- for i, child in n:
- discard check(child, ctx, map)
-
- if i > 0:
- # var args make a new map with MaybeNil for our node
- # as it might have been mutated
- # TODO similar for normal refs and fields: find dependent exprs: brackets
-
- if child.kind == nkHiddenAddr and not child.typ.isNil and child.typ.kind == tyVar and child.typ[0].kind == tyRef:
- if not isNew:
- result.map = newNilMap(map)
- isNew = true
- # result.map[$child] = MaybeNil
- var arg = child
- while arg.kind == nkHiddenAddr:
- arg = arg[0]
- let a = ctx.index(arg)
- if a != noExprIndex:
- moveOut(ctx, result.map, arg)
- moveOutDependants(ctx, result.map, arg)
- result.map.store(ctx, a, MaybeNil, TVarArg, n.info, arg)
- storeDependants(ctx, result.map, arg, MaybeNil)
- elif not child.typ.isNil and child.typ.kind == tyRef:
- if child.kind in {nkSym, nkDotExpr} or isConstBracket(child):
- let a = ctx.index(child)
- if ctx.dependants[a].len > 0:
- if not isNew:
- result.map = newNilMap(map)
- isNew = true
- moveOutDependants(ctx, result.map, child)
- storeDependants(ctx, result.map, child, MaybeNil)
-
- if n[0].kind == nkSym and n[0].sym.magic == mNew:
- # new hidden deref?
- var value = if n[1].kind == nkHiddenDeref: n[1][0] else: n[1]
- let b = ctx.index(value)
- result.map.store(ctx, b, Safe, TAssign, value.info, value)
- result.nilability = Safe
- else:
- # echo "n ", n, " ", n.typ.isNil
- if not n.typ.isNil:
- result.nilability = typeNilability(n.typ)
- else:
- result.nilability = Safe
- # echo result.map
-
-template event(b: History): string =
- case b.kind:
- of TArg: "param with nilable type"
- of TNil: "it returns true for isNil"
- of TAssign: "assigns a value which might be nil"
- of TVarArg: "passes it as a var arg which might change to nil"
- of TResult: "it is nil by default"
- of TType: "it has ref type"
- of TSafe: "it is safe here as it returns false for isNil"
- of TPotentialAlias: "it might be changed directly or through an alias"
- of TDependant: "it might be changed because its base might be changed"
-
-proc derefWarning(n, ctx, map; kind: Nilability) =
- ## a warning for potentially unsafe dereference
- if n.info in ctx.warningLocations:
- return
- ctx.warningLocations.incl(n.info)
- var a: seq[History]
- if n.kind == nkSym:
- a = history(map, ctx.index(n))
- var res = ""
- var issue = case kind:
- of Nil: "it is nil"
- of MaybeNil: "it might be nil"
- of Unreachable: "it is unreachable"
- else: ""
- res.add("can't deref " & $n & ", " & issue)
- if a.len > 0:
- res.add("\n")
- for b in a:
- res.add(" " & event(b) & " on line " & $b.info.line & ":" & $b.info.col)
- message(ctx.config, n.info, warnStrictNotNil, res)
-
-proc handleNilability(check: Check; n, ctx, map) =
- ## handle the check:
- ## register a warning(error?) for Nil/MaybeNil
- case check.nilability:
- of Nil:
- derefWarning(n, ctx, map, Nil)
- of MaybeNil:
- derefWarning(n, ctx, map, MaybeNil)
- of Unreachable:
- derefWarning(n, ctx, map, Unreachable)
- else:
- when defined(nilDebugInfo):
- message(ctx.config, n.info, hintUser, "can deref " & $n)
-
-proc checkDeref(n, ctx, map): Check =
- ## check dereference: deref n should be ok only if n is Safe
- result = check(n[0], ctx, map)
-
- handleNilability(result, n[0], ctx, map)
-
-
-proc checkRefExpr(n, ctx; check: Check): Check =
- ## check ref expressions: TODO not sure when this happens
- result = check
- if n.typ.kind != tyRef:
- result.nilability = typeNilability(n.typ)
- elif tfNotNil notin n.typ.flags:
- # echo "ref key ", n, " ", n.kind
- if n.kind in {nkSym, nkDotExpr} or isConstBracket(n):
- let key = ctx.index(n)
- result.nilability = result.map[key]
- elif n.kind == nkBracketExpr:
- # sometimes false positive
- result.nilability = MaybeNil
- else:
- # sometimes maybe false positive
- result.nilability = MaybeNil
-
-proc checkDotExpr(n, ctx, map): Check =
- ## check dot expressions: make sure we can dereference the base
- result = check(n[0], ctx, map)
- result = checkRefExpr(n, ctx, result)
-
-proc checkBracketExpr(n, ctx, map): Check =
- ## check bracket expressions: make sure we can dereference the base
- result = check(n[0], ctx, map)
- # if might be deref: [] == *(a + index) for cstring
- handleNilability(result, n[0], ctx, map)
- result = check(n[1], ctx, result.map)
- result = checkRefExpr(n, ctx, result)
- # echo n, " ", result.nilability
-
-
-template union(l: Nilability, r: Nilability): Nilability =
- ## unify two states
- if l == r:
- l
- else:
- MaybeNil
-
-template add(l: Nilability, r: Nilability): Nilability =
- if l == r: # Safe Safe -> Safe etc
- l
- elif l == Parent: # Parent Safe -> Safe etc
- r
- elif r == Parent: # Safe Parent -> Safe etc
- l
- elif l == Unreachable or r == Unreachable: # Safe Unreachable -> Unreachable etc
- Unreachable
- elif l == MaybeNil: # Safe MaybeNil -> Safe etc
- r
- elif r == MaybeNil: # MaybeNil Nil -> Nil etc
- l
- else: # Safe Nil -> Unreachable etc
- Unreachable
-
-proc findCommonParent(l: NilMap, r: NilMap): NilMap =
- result = l.parent
- while not result.isNil:
- var rparent = r.parent
- while not rparent.isNil:
- if result == rparent:
- return result
- rparent = rparent.parent
- result = result.parent
-
-proc union(ctx: NilCheckerContext, l: NilMap, r: NilMap): NilMap =
- ## unify two maps from different branches
- ## combine their locals
- ## what if they are from different parts of the same tree
- ## e.g.
- ## a -> b -> c
- ## -> b1
- ## common then?
- ##
- if l.isNil:
- return r
- elif r.isNil:
- return l
-
- let common = findCommonParent(l, r)
- result = newNilMap(common, ctx.expressions.len.int)
-
- for index, value in l:
- let h = history(r, index)
- let info = if h.len > 0: h[^1].info else: TLineInfo(line: 0) # assert h.len > 0
- # echo "history", name, value, r[name], h[^1].info.line
- result.store(ctx, index, union(value, r[index]), TAssign, info)
-
-proc add(ctx: NilCheckerContext, l: NilMap, r: NilMap): NilMap =
- #echo "add "
- #echo namedMapDebugInfo(ctx, l)
- #echo " : "
- #echo namedMapDebugInfo(ctx, r)
- if l.isNil:
- return r
- elif r.isNil:
- return l
-
- let common = findCommonParent(l, r)
- result = newNilMap(common, ctx.expressions.len.int)
-
- for index, value in l:
- let h = history(r, index)
- let info = if h.len > 0: h[^1].info else: TLineInfo(line: 0)
- # TODO: refactor and also think: is TAssign a good one
- result.store(ctx, index, add(value, r[index]), TAssign, info)
-
- #echo "result"
- #echo namedMapDebugInfo(ctx, result)
- #echo ""
- #echo ""
-
-
-proc checkAsgn(target: PNode, assigned: PNode; ctx, map): Check =
- ## check assignment
- ## update map based on `assigned`
- if assigned.kind != nkEmpty:
- result = check(assigned, ctx, map)
- else:
- result = Check(nilability: typeNilability(target.typ), map: map)
-
- # we need to visit and check those, but we don't use the result for now
- # is it possible to somehow have another event happen here?
- discard check(target, ctx, map)
-
- if result.map.isNil:
- result.map = map
- if target.kind in {nkSym, nkDotExpr} or isConstBracket(target):
- let t = ctx.index(target)
- move(ctx, map, target, assigned)
- case assigned.kind:
- of nkNilLit:
- result.map.store(ctx, t, Nil, TAssign, target.info, target)
- else:
- result.map.store(ctx, t, result.nilability, TAssign, target.info, target)
- moveOutDependants(ctx, map, target)
- storeDependants(ctx, map, target, MaybeNil)
- if assigned.kind in {nkObjConstr, nkTupleConstr}:
- for (element, value) in result.elements:
- var elementNode = nkDotExpr.newTree(nkHiddenDeref.newTree(target), element)
- if symbol(elementNode) in ctx.symbolIndices:
- var elementIndex = ctx.index(elementNode)
- result.map.store(ctx, elementIndex, value, TAssign, target.info, elementNode)
-
-
-proc checkReturn(n, ctx, map): Check =
- ## check return
- # return n same as result = n; return ?
- result = check(n[0], ctx, map)
- result.map.store(ctx, resultExprIndex, result.nilability, TAssign, n.info)
-
-
-proc checkIf(n, ctx, map): Check =
- ## check branches based on condition
- var mapIf: NilMap = map
-
- # first visit the condition
-
- # the structure is not If(Elif(Elif, Else), Else)
- # it is
- # If(Elif, Elif, Else)
-
- var mapCondition = checkCondition(n.sons[0].sons[0], ctx, mapIf, false, true)
-
- # the state of the conditions: negating conditions before the current one
- var layerHistory = newNilMap(mapIf)
- # the state after branch effects
- var afterLayer: NilMap
- # the result nilability for expressions
- var nilability = Safe
-
- for branch in n.sons:
- var branchConditionLayer = newNilMap(layerHistory)
- var branchLayer: NilMap
- var code: PNode
- if branch.kind in {nkIfStmt, nkElifBranch}:
- var mapCondition = checkCondition(branch[0], ctx, branchConditionLayer, false, true)
- let reverseMapCondition = reverseDirect(mapCondition)
- layerHistory = ctx.add(layerHistory, reverseMapCondition)
- branchLayer = mapCondition
- code = branch[1]
- else:
- branchLayer = layerHistory
- code = branch
-
- let branchCheck = checkBranch(code, ctx, branchLayer)
- # handles nil afterLayer -> returns branchCheck.map
- afterLayer = ctx.union(afterLayer, branchCheck.map)
- nilability = if n.kind == nkIfStmt: Safe else: union(nilability, branchCheck.nilability)
- if n.sons.len > 1:
- result.map = afterLayer
- result.nilability = nilability
- else:
- if not hasUnstructuredControlFlowJump(n[0][1]):
- # here it matters what happend inside, because
- # we might continue in the parent branch after entering this one
- # either we enter the branch, so we get mapIf and effect of branch -> afterLayer
- # or we dont , so we get mapIf and (not condition) effect -> layerHistory
- result.map = ctx.union(layerHistory, afterLayer)
- result.nilability = Safe # no expr?
- else:
- # similar to else: because otherwise we are jumping out of
- # the branch, so no union with the mapIf (we dont continue if the condition was true)
- # here it also doesn't matter for the parent branch what happened in the branch, e.g. assigning to nil
- # as if we continue there, we haven't entered the branch probably
- # so we don't do an union with afterLayer
- # layerHistory has the effect of mapIf and (not condition)
- result.map = layerHistory
- result.nilability = Safe
-
-proc checkFor(n, ctx, map): Check =
- ## check for loops
- ## try to repeat the unification of the code twice
- ## to detect what can change after a several iterations
- ## approach based on discussions with Zahary/Araq
- ## similar approach used for other loops
- var m = map.copyMap()
- var map0 = map.copyMap()
- #echo namedMapDebugInfo(ctx, map)
- m = check(n.sons[2], ctx, map).map.copyMap()
- if n[0].kind == nkSym:
- m.store(ctx, ctx.index(n[0]), typeNilability(n[0].typ), TAssign, n[0].info)
- # echo namedMapDebugInfo(ctx, map)
- var check2 = check(n.sons[2], ctx, m)
- var map2 = check2.map
-
- result.map = ctx.union(map0, m)
- result.map = ctx.union(result.map, map2)
- result.nilability = Safe
-
-# check:
-# while code:
-# code2
-
-# if code:
-# code2
-# if code:
-# code2
-
-# if code:
-# code2
-
-# check(code), check(code2 in code's map)
-
-proc checkWhile(n, ctx, map): Check =
- ## check while loops
- ## try to repeat the unification of the code twice
- var m = checkCondition(n[0], ctx, map, false, false)
- var map0 = map.copyMap()
- m = check(n.sons[1], ctx, m).map
- var map1 = m.copyMap()
- var check2 = check(n.sons[1], ctx, m)
- var map2 = check2.map
-
- result.map = ctx.union(map0, map1)
- result.map = ctx.union(result.map, map2)
- result.nilability = Safe
-
-proc checkInfix(n, ctx, map): Check =
- ## check infix operators in condition
- ## a and b : map is based on a; next b
- ## a or b : map is an union of a and b's
- ## a == b : use checkCondition
- ## else: no change, just check args
- if n[0].kind == nkSym:
- var mapL: NilMap
- var mapR: NilMap
- if n[0].sym.magic notin {mAnd, mEqRef}:
- mapL = checkCondition(n[1], ctx, map, false, false)
- mapR = checkCondition(n[2], ctx, map, false, false)
- case n[0].sym.magic:
- of mOr:
- result.map = ctx.union(mapL, mapR)
- of mAnd:
- result.map = checkCondition(n[1], ctx, map, false, false)
- result.map = checkCondition(n[2], ctx, result.map, false, false)
- of mEqRef:
- if n[2].kind == nkIntLit:
- if $n[2] == "true":
- result.map = checkCondition(n[1], ctx, map, false, false)
- elif $n[2] == "false":
- result.map = checkCondition(n[1], ctx, map, true, false)
- elif n[1].kind == nkIntLit:
- if $n[1] == "true":
- result.map = checkCondition(n[2], ctx, map, false, false)
- elif $n[1] == "false":
- result.map = checkCondition(n[2], ctx, map, true, false)
-
- if result.map.isNil:
- result.map = map
- else:
- result.map = map
- else:
- result.map = map
- result.nilability = Safe
-
-proc checkIsNil(n, ctx, map; isElse: bool = false): Check =
- ## check isNil calls
- ## update the map depending on if it is not isNil or isNil
- result.map = newNilMap(map)
- let value = n[1]
- result.map.store(ctx, ctx.index(n[1]), if not isElse: Nil else: Safe, TArg, n.info, n)
-
-proc infix(ctx: NilCheckerContext, l: PNode, r: PNode, magic: TMagic): PNode =
- var name = case magic:
- of mEqRef: "=="
- of mAnd: "and"
- of mOr: "or"
- else: ""
-
- var cache = newIdentCache()
- var op = newSym(skVar, cache.getIdent(name), nextSymId ctx.idgen, nil, r.info)
-
- op.magic = magic
- result = nkInfix.newTree(
- newSymNode(op, r.info),
- l,
- r)
- result.typ = newType(tyBool, nextTypeId ctx.idgen, nil)
-
-proc prefixNot(ctx: NilCheckerContext, node: PNode): PNode =
- var cache = newIdentCache()
- var op = newSym(skVar, cache.getIdent("not"), nextSymId ctx.idgen, nil, node.info)
-
- op.magic = mNot
- result = nkPrefix.newTree(
- newSymNode(op, node.info),
- node)
- result.typ = newType(tyBool, nextTypeId ctx.idgen, nil)
-
-proc infixEq(ctx: NilCheckerContext, l: PNode, r: PNode): PNode =
- infix(ctx, l, r, mEqRef)
-
-proc infixOr(ctx: NilCheckerContext, l: PNode, r: PNode): PNode =
- infix(ctx, l, r, mOr)
-
-proc checkCase(n, ctx, map): Check =
- # case a:
- # of b: c
- # of b2: c2
- # is like
- # if a == b:
- # c
- # elif a == b2:
- # c2
- # also a == true is a , a == false is not a
- let base = n[0]
- result.map = map.copyMap()
- result.nilability = Safe
- var a: PNode
- for child in n:
- case child.kind:
- of nkOfBranch:
- if child.len < 2:
- # echo "case with of with < 2 ", n
- continue # TODO why does this happen
- let branchBase = child[0] # TODO a, b or a, b..c etc
- let code = child[^1]
- let test = infixEq(ctx, base, branchBase)
- if a.isNil:
- a = test
- else:
- a = infixOr(ctx, a, test)
- let conditionMap = checkCondition(test, ctx, map.copyMap(), false, false)
- let newCheck = checkBranch(code, ctx, conditionMap)
- result.map = ctx.union(result.map, newCheck.map)
- result.nilability = union(result.nilability, newCheck.nilability)
- of nkElifBranch:
- discard "TODO: maybe adapt to be similar to checkIf"
- of nkElse:
- let mapElse = checkCondition(prefixNot(ctx, a), ctx, map.copyMap(), false, false)
- let newCheck = checkBranch(child[0], ctx, mapElse)
- result.map = ctx.union(result.map, newCheck.map)
- result.nilability = union(result.nilability, newCheck.nilability)
- else:
- discard
-
-# notes
-# try:
-# a
-# b
-# except:
-# c
-# finally:
-# d
-#
-# if a doesnt raise, this is not an exit point:
-# so find what raises and update the map with that
-# (a, b); c; d
-# if nothing raises, except shouldn't happen
-# .. might be a false positive tho, if canRaise is not conservative?
-# so don't visit it
-#
-# nested nodes can raise as well: I hope nim returns canRaise for
-# their parents
-#
-# a lot of stuff can raise
-proc checkTry(n, ctx, map): Check =
- var newMap = map.copyMap()
- var currentMap = map
- # we don't analyze except if nothing canRaise in try
- var canRaise = false
- var hasFinally = false
- # var tryNodes: seq[PNode]
- # if n[0].kind == nkStmtList:
- # tryNodes = toSeq(n[0])
- # else:
- # tryNodes = @[n[0]]
- # for i, child in tryNodes:
- # let (childNilability, childMap) = check(child, conf, currentMap)
- # echo childMap
- # currentMap = childMap
- # # TODO what about nested
- # if child.canRaise:
- # newMap = union(newMap, childMap)
- # canRaise = true
- # else:
- # newMap = childMap
- let tryCheck = check(n[0], ctx, currentMap)
- newMap = ctx.union(currentMap, tryCheck.map)
- canRaise = n[0].canRaise
-
- var afterTryMap = newMap
- for a, branch in n:
- if a > 0:
- case branch.kind:
- of nkFinally:
- newMap = ctx.union(afterTryMap, newMap)
- let childCheck = check(branch[0], ctx, newMap)
- newMap = ctx.union(newMap, childCheck.map)
- hasFinally = true
- of nkExceptBranch:
- if canRaise:
- let childCheck = check(branch[^1], ctx, newMap)
- newMap = ctx.union(newMap, childCheck.map)
- else:
- discard
- if not hasFinally:
- # we might have not hit the except branches
- newMap = ctx.union(afterTryMap, newMap)
- result = Check(nilability: Safe, map: newMap)
-
-proc hasUnstructuredControlFlowJump(n: PNode): bool =
- ## if the node contains a direct stop
- ## as a continue/break/raise/return: then it means
- ## we should reverse some of the map in the code after the condition
- ## similar to else
- # echo "n ", n, " ", n.kind
- case n.kind:
- of nkStmtList:
- for child in n:
- if hasUnstructuredControlFlowJump(child):
- return true
- of nkReturnStmt, nkBreakStmt, nkContinueStmt, nkRaiseStmt:
- return true
- of nkIfStmt, nkIfExpr, nkElifExpr, nkElse:
- return false
- else:
- discard
- return false
-
-proc reverse(value: Nilability): Nilability =
- case value:
- of Nil: Safe
- of MaybeNil: MaybeNil
- of Safe: Nil
- of Parent: Parent
- of Unreachable: Unreachable
-
-proc reverse(kind: TransitionKind): TransitionKind =
- case kind:
- of TNil: TSafe
- of TSafe: TNil
- of TPotentialAlias: TPotentialAlias
- else:
- kind
- # raise newException(ValueError, "expected TNil or TSafe")
-
-proc reverseDirect(map: NilMap): NilMap =
- # we create a new layer
- # reverse the values only in this layer:
- # because conditions should've stored their changes there
- # b: Safe (not b.isNil)
- # b: Parent Parent
- # b: Nil (b.isNil)
-
- # layer block
- # [ Parent ] [ Parent ]
- # if -> if state
- # layer -> reverse
- # older older0 new
- # older new
- # [ b Nil ] [ Parent ]
- # elif
- # [ b Nil, c Nil] [ Parent ]
- #
-
- # if b.isNil:
- # # [ b Safe]
- # c = A() # Safe
- # elif not b.isNil:
- # # [ b Safe ] + [b Nil] MaybeNil Unreachable
- # # Unreachable defer can't deref b, it is unreachable
- # discard
- # else:
- # b
-
-
-# if
-
-
-
- # if: we just pass the map with a new layer for its block
- # elif: we just pass the original map but with a new layer is the reverse of the previous popped layer (?)
- # elif:
- # else: we just pass the original map but with a new layer which is initialized as the reverse of the
- # top layer of else
- # else:
- #
- # [ b MaybeNil ] [b Parent] [b Parent] [b Safe] [b Nil] []
- # Safe
- # c == 1
- # b Parent
- # c == 2
- # b Parent
- # not b.isNil
- # b Safe
- # c == 3
- # b Nil
- # (else)
- # b Nil
-
- result = map.copyMap()
- for index, value in result.expressions:
- result.expressions[index] = reverse(value)
- if result.history[index].len > 0:
- result.history[index][^1].kind = reverse(result.history[index][^1].kind)
- result.history[index][^1].nilability = result.expressions[index]
-
-proc checkCondition(n, ctx, map; reverse: bool, base: bool): NilMap =
- ## check conditions : used for if, some infix operators
- ## isNil(a)
- ## it returns a new map: you need to reverse all the direct elements for else
-
- # echo "condition ", n, " ", n.kind
- if n.kind == nkCall:
- result = newNilMap(map)
- for element in n:
- if element.kind == nkHiddenDeref and n[0].kind == nkSym and n[0].sym.magic == mIsNil:
- result = check(element[0], ctx, result).map
- else:
- result = check(element, ctx, result).map
-
- if n[0].kind == nkSym and n[0].sym.magic == mIsNil:
- # isNil(arg)
- var arg = n[1]
- while arg.kind == nkHiddenDeref:
- arg = arg[0]
- if arg.kind in {nkSym, nkDotExpr} or isConstBracket(arg):
- let a = ctx.index(arg)
- result.store(ctx, a, if not reverse: Nil else: Safe, if not reverse: TNil else: TSafe, n.info, arg)
- else:
- discard
- else:
- discard
- elif n.kind == nkPrefix and n[0].kind == nkSym and n[0].sym.magic == mNot:
- result = checkCondition(n[1], ctx, map, not reverse, false)
- elif n.kind == nkInfix:
- result = newNilMap(map)
- result = checkInfix(n, ctx, result).map
- else:
- result = check(n, ctx, map).map
- result = newNilMap(map)
- assert not result.isNil
- assert not result.parent.isNil
-
-proc checkResult(n, ctx, map) =
- let resultNilability = map[resultExprIndex]
- case resultNilability:
- of Nil:
- message(ctx.config, n.info, warnStrictNotNil, "return value is nil")
- of MaybeNil:
- message(ctx.config, n.info, warnStrictNotNil, "return value might be nil")
- of Unreachable:
- message(ctx.config, n.info, warnStrictNotNil, "return value is unreachable")
- of Safe, Parent:
- discard
-
-proc checkBranch(n: PNode, ctx: NilCheckerContext, map: NilMap): Check =
- result = check(n, ctx, map)
-
-
-# Faith!
-
-proc check(n: PNode, ctx: NilCheckerContext, map: NilMap): Check =
- assert not map.isNil
-
- # echo "check n ", n, " ", n.kind
- # echo "map ", namedMapDebugInfo(ctx, map)
- case n.kind:
- of nkSym:
- result = Check(nilability: map[ctx.index(n)], map: map)
- of nkCallKinds:
- if n.sons[0].kind == nkSym:
- let callSym = n.sons[0].sym
- case callSym.magic:
- of mAnd, mOr:
- result = checkInfix(n, ctx, map)
- of mIsNil:
- result = checkIsNil(n, ctx, map)
- else:
- result = checkCall(n, ctx, map)
- else:
- result = checkCall(n, ctx, map)
- of nkHiddenStdConv, nkHiddenSubConv, nkConv, nkExprColonExpr, nkExprEqExpr,
- nkCast:
- result = check(n.sons[1], ctx, map)
- of nkStmtList, nkStmtListExpr, nkChckRangeF, nkChckRange64, nkChckRange,
- nkBracket, nkCurly, nkPar, nkTupleConstr, nkClosure, nkObjConstr, nkElse:
- result.map = map
- if n.kind in {nkObjConstr, nkTupleConstr}:
- # TODO deeper nested elements?
- # A(field: B()) #
- # field: Safe ->
- var elements: seq[(PNode, Nilability)]
- for i, child in n:
- result = check(child, ctx, result.map)
- if i > 0:
- if child.kind == nkExprColonExpr:
- elements.add((child[0], result.nilability))
- result.elements = elements
- result.nilability = Safe
- else:
- for child in n:
- result = check(child, ctx, result.map)
-
- of nkDotExpr:
- result = checkDotExpr(n, ctx, map)
- of nkDerefExpr, nkHiddenDeref:
- result = checkDeref(n, ctx, map)
- of nkAddr, nkHiddenAddr:
- result = check(n.sons[0], ctx, map)
- of nkIfStmt, nkIfExpr:
- result = checkIf(n, ctx, map)
- of nkAsgn, nkFastAsgn, nkSinkAsgn:
- result = checkAsgn(n[0], n[1], ctx, map)
- of nkVarSection:
- result.map = map
- for child in n:
- result = checkAsgn(child[0], child[2], ctx, result.map)
- of nkForStmt:
- result = checkFor(n, ctx, map)
- of nkCaseStmt:
- result = checkCase(n, ctx, map)
- of nkReturnStmt:
- result = checkReturn(n, ctx, map)
- of nkBracketExpr:
- result = checkBracketExpr(n, ctx, map)
- of nkTryStmt:
- result = checkTry(n, ctx, map)
- of nkWhileStmt:
- result = checkWhile(n, ctx, map)
- 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:
-
- discard "don't follow this : same as varpartitions"
- result = Check(nilability: Nil, map: map)
- else:
-
- var elementMap = map.copyMap()
- var elementCheck: Check
- elementCheck.map = elementMap
- for element in n:
- elementCheck = check(element, ctx, elementCheck.map)
-
- result = Check(nilability: Nil, map: elementCheck.map)
-
-
-
-
-proc typeNilability(typ: PType): Nilability =
- assert not typ.isNil
- # echo "typeNilability ", $typ.flags, " ", $typ.kind
- result = if tfNotNil in typ.flags:
- Safe
- elif typ.kind in {tyRef, tyCstring, tyPtr, tyPointer}:
- #
- # tyVar ? tyVarargs ? tySink ? tyLent ?
- # TODO spec? tests?
- MaybeNil
- else:
- Safe
- # echo " result ", result
-
-proc preVisitNode(ctx: NilCheckerContext, node: PNode, conf: ConfigRef) =
- # echo "visit node ", node
- if node.kind in {nkSym, nkDotExpr} or isConstBracket(node):
- let nodeSymbol = symbol(node)
- if not ctx.symbolIndices.hasKey(nodeSymbol):
- ctx.symbolIndices[nodeSymbol] = ctx.expressions.len
- ctx.expressions.add(node)
- if node.kind in {nkDotExpr, nkBracketExpr}:
- if node.kind == nkDotExpr and (not node.typ.isNil and node.typ.kind == tyRef and tfNotNil notin node.typ.flags) or
- node.kind == nkBracketExpr:
- let index = ctx.symbolIndices[nodeSymbol]
- var baseIndex = noExprIndex
- # deref usually?
- # ok, we hit another case
- var base = if node[0].kind notin {nkSym, nkIdent}: node[0][0] else: node[0]
- if base.kind != nkIdent:
- let baseSymbol = symbol(base)
- if not ctx.symbolIndices.hasKey(baseSymbol):
- baseIndex = ctx.expressions.len # next visit should add it
- else:
- baseIndex = ctx.symbolIndices[baseSymbol]
- if ctx.dependants.len <= baseIndex:
- ctx.dependants.setLen(baseIndex + 1.ExprIndex)
- ctx.dependants[baseIndex].incl(index.int)
- case node.kind:
- of nkSym, nkEmpty, nkNilLit, nkType, nkIdent, nkCharLit .. nkUInt64Lit, nkFloatLit .. nkFloat64Lit, nkStrLit .. nkTripleStrLit:
- discard
- of nkDotExpr:
- # visit only the base
- ctx.preVisitNode(node[0], conf)
- else:
- for element in node:
- ctx.preVisitNode(element, conf)
-
-proc preVisit(ctx: NilCheckerContext, s: PSym, body: PNode, conf: ConfigRef) =
- ctx.symbolIndices = {resultId: resultExprIndex}.toTable()
- var cache = newIdentCache()
- ctx.expressions = SeqOfDistinct[ExprIndex, PNode](@[newIdentNode(cache.getIdent("result"), s.ast.info)])
- var emptySet: IntSet # set[ExprIndex]
- ctx.dependants = SeqOfDistinct[ExprIndex, IntSet](@[emptySet])
- for i, arg in s.typ.n.sons:
- if i > 0:
- if arg.kind != nkSym:
- continue
- let argSymbol = symbol(arg)
- if not ctx.symbolIndices.hasKey(argSymbol):
- ctx.symbolIndices[argSymbol] = ctx.expressions.len
- ctx.expressions.add(arg)
- ctx.preVisitNode(body, conf)
- if ctx.dependants.len < ctx.expressions.len:
- ctx.dependants.setLen(ctx.expressions.len)
- # echo ctx.symbolIndices
- # echo ctx.expressions
- # echo ctx.dependants
-
-proc checkNil*(s: PSym; body: PNode; conf: ConfigRef, idgen: IdGenerator) =
- let line = s.ast.info.line
- let fileIndex = s.ast.info.fileIndex.int
- var filename = conf.m.fileInfos[fileIndex].fullPath.string
-
- var context = NilCheckerContext(config: conf, idgen: idgen)
- context.preVisit(s, body, conf)
- var map = newNilMap(nil, context.symbolIndices.len)
-
- for i, child in s.typ.n.sons:
- if i > 0:
- if child.kind != nkSym:
- continue
- map.store(context, context.index(child), typeNilability(child.typ), TArg, child.info, child)
-
- map.store(context, resultExprIndex, if not s.typ[0].isNil and s.typ[0].kind == tyRef: Nil else: Safe, TResult, s.ast.info)
-
- # echo "checking ", s.name.s, " ", filename
-
- let res = check(body, context, map)
- var canCheck = resultExprIndex in res.map.history.low .. res.map.history.high
- if res.nilability == Safe and canCheck and res.map.history[resultExprIndex].len <= 1:
- res.map.store(context, resultExprIndex, Safe, TAssign, s.ast.info)
- else:
- if res.nilability == Safe:
- res.map.store(context, resultExprIndex, Safe, TAssign, s.ast.info)
-
- # TODO check for nilability result
- # (ANotNil, BNotNil) :
- # do we check on asgn nilability at all?
-
- if not s.typ[0].isNil and s.typ[0].kind == tyRef and tfNotNil in s.typ[0].flags:
- checkResult(s.ast, context, res.map)
diff --git a/compiler/nim.cfg b/compiler/nim.cfg
index b9c8fdc08..0ff128ba3 100644
--- a/compiler/nim.cfg
+++ b/compiler/nim.cfg
@@ -1,48 +1,20 @@
# Special configuration file for the Nim project
hint[XDeclaredButNotUsed]:off
+path:"llvm"
+path:"$projectPath/.."
+
+path:"$lib/packages/docutils"
define:booting
-define:nimcore
-define:nimPreviewFloatRoundtrip
-define:nimPreviewSlimSystem
-define:nimPreviewCstringConversion
-define:nimPreviewRangeDefault
-threads:off
-
#import:"$projectpath/testability"
@if windows:
cincludes: "$lib/wrappers/libffi/common"
- tlsEmulation:off
@end
define:useStdoutAsStdmsg
-@if nimHasStyleChecks:
- styleCheck:error
-@end
-
-
+cs:partial
#define:useNodeIds
#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
-@end
diff --git a/compiler/nim.nim b/compiler/nim.nim
index 420579b97..a58afd593 100644
--- a/compiler/nim.nim
+++ b/compiler/nim.nim
@@ -7,32 +7,16 @@
# distribution, for details about the copyright.
#
-import std/[os, strutils, parseopt]
-
-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(gcc) and defined(windows):
+ when defined(x86):
+ {.link: "icons/nim.res".}
+ else:
+ {.link: "icons/nim_icon.o".}
import
- commands, options, msgs, extccomp, main, idents, lineinfos, cmdlinehelper,
- pathutils, modulegraphs
-
-from browsers import openDefaultBrowser
-from nodejs import findNodeJs
+ commands, lexer, condsyms, options, msgs, nversion, nimconf, ropes,
+ extccomp, strutils, os, osproc, platform, main, parseopt, service,
+ nodejs, scriptconfig
when hasTinyCBackend:
import tccgen
@@ -41,130 +25,92 @@ 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 prependCurDir(f: string): string =
+ when defined(unix):
+ if os.isAbsolute(f): result = f
+ else: result = "./" & f
+ else:
+ result = f
-proc processCmdLine(pass: TCmdLinePass, cmd: string; config: ConfigRef) =
- var p = parseopt.initOptParser(cmd)
- var argsCount = 0
-
- config.commandLine.setLen 0
- # bugfix: otherwise, config.commandLine ends up duplicated
-
- while true:
- parseopt.next(p)
- case p.kind
- of cmdEnd: break
- of cmdLongOption, cmdShortOption:
- config.commandLine.add " "
- config.commandLine.addCmdPrefix p.kind
- config.commandLine.add p.key.quoteShell # quoteShell to be future proof
- if p.val.len > 0:
- config.commandLine.add ':'
- config.commandLine.add p.val.quoteShell
-
- if p.key == "": # `-` was passed to indicate main project is stdin
- p.key = "-"
- if processArgument(pass, p, argsCount, config): break
- else:
- processSwitch(pass, p, config)
- of cmdArgument:
- config.commandLine.add " "
- config.commandLine.add p.key.quoteShell
- if processArgument(pass, p, argsCount, config): break
- if pass == passCmd2:
- if {optRun, optWasNimscript} * config.globalOptions == {} and
- 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,
- processCmdLine: processCmdLine
- )
- self.initDefinesProg(conf, "nim_compiler")
+proc handleCmdLine() =
if paramCount() == 0:
- writeCommandLineUsage(conf)
- 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())
- if conf.errorCounter != 0: return
- when hasTinyCBackend:
- if conf.cmd == cmdTcc:
- tccgen.run(conf, conf.arguments)
- if optRun in conf.globalOptions:
- 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"
- 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)
- if conf.arguments.len > 0:
- # reserved for future use
- rawMessage(conf, errGenerated, "'$1 cannot handle arguments" % [$conf.cmd])
- openDefaultBrowser($output)
+ writeCommandLineUsage()
+ else:
+ # Process command line arguments:
+ processCmdLine(passCmd1, "")
+ if gProjectName == "-":
+ gProjectName = "stdinfile"
+ gProjectFull = "stdinfile"
+ gProjectPath = getCurrentDir()
+ gProjectIsStdin = true
+ elif gProjectName != "":
+ try:
+ gProjectFull = canonicalizePath(gProjectName)
+ except OSError:
+ gProjectFull = gProjectName
+ let p = splitFile(gProjectFull)
+ gProjectPath = p.dir
+ gProjectName = p.name
else:
- # support as needed
- rawMessage(conf, errGenerated, "'$1 cannot handle --run" % [$conf.cmd])
+ gProjectPath = getCurrentDir()
+ loadConfigs(DefaultConfig) # load all config files
+ let scriptFile = gProjectFull.changeFileExt("nims")
+ if fileExists(scriptFile):
+ runNimScript(scriptFile, freshDefines=false)
+ # 'nim foo.nims' means to just run the NimScript file and do nothing more:
+ if scriptFile == gProjectFull: return
+ elif fileExists(gProjectPath / "config.nims"):
+ # directory wide NimScript file
+ runNimScript(gProjectPath / "config.nims", freshDefines=false)
+ # now process command line arguments again, because some options in the
+ # command line can overwite the config file's settings
+ extccomp.initVars()
+ processCmdLine(passCmd2, "")
+ if options.command == "":
+ rawMessage(errNoCommand, command)
+ mainCommand()
+ if optHints in gOptions and hintGCStats in gNotes: echo(GC_getStatistics())
+ #echo(GC_getStatistics())
+ if msgs.gErrorCounter == 0:
+ when hasTinyCBackend:
+ if gCmd == cmdRun:
+ tccgen.run(commands.arguments)
+ if optRun in gGlobalOptions:
+ if gCmd == cmdCompileToJS:
+ var ex: string
+ if options.outFile.len > 0:
+ ex = options.outFile.prependCurDir.quoteShell
+ else:
+ ex = quoteShell(
+ completeCFilePath(changeFileExt(gProjectFull, "js").prependCurDir))
+ execExternalProgram(findNodeJs() & " " & ex & ' ' & commands.arguments)
+ elif gCmd == cmdCompileToPHP:
+ var ex: string
+ if options.outFile.len > 0:
+ ex = options.outFile.prependCurDir.quoteShell
+ else:
+ ex = quoteShell(
+ completeCFilePath(changeFileExt(gProjectFull, "php").prependCurDir))
+ execExternalProgram("php " & ex & ' ' & commands.arguments)
+ else:
+ var binPath: string
+ if options.outFile.len > 0:
+ # If the user specified an outFile path, use that directly.
+ binPath = options.outFile.prependCurDir
+ else:
+ # Figure out ourselves a valid binary name.
+ binPath = changeFileExt(gProjectFull, ExeExt).prependCurDir
+ var ex = quoteShell(binPath)
+ execExternalProgram(ex & ' ' & commands.arguments)
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()
+condsyms.initDefines()
when not defined(selftest):
- let conf = newConfigRef()
- handleCmdLine(newIdentCache(), conf)
- when declared(GC_setMaxPause):
- echo GC_getStatistics()
- msgQuit(int8(conf.errorCounter > 0))
+ handleCmdLine()
+ msgQuit(int8(msgs.gErrorCounter > 0))
diff --git a/compiler/nimblecmd.nim b/compiler/nimblecmd.nim
index b6b08ccd4..c6c2ab058 100644
--- a/compiler/nimblecmd.nim
+++ b/compiler/nimblecmd.nim
@@ -9,153 +9,82 @@
## Implements some helper procs for Nimble (Nim's package manager) support.
-import parseutils, strutils, os, options, msgs, sequtils, lineinfos, pathutils,
- std/sha1, tables
+import parseutils, strutils, strtabs, os, options, msgs, lists
-when defined(nimPreviewSlimSystem):
- import std/[syncio, assertions]
+proc addPath*(path: string, info: TLineInfo) =
+ if not contains(options.searchPaths, path):
+ lists.prependStr(options.searchPaths, path)
-proc addPath*(conf: ConfigRef; path: AbsoluteDir, info: TLineInfo) =
- if not conf.searchPaths.contains(path):
- conf.searchPaths.insert(path, 0)
+proc versionSplitPos(s: string): int =
+ result = s.len-2
+ while result > 1 and s[result] in {'0'..'9', '.'}: dec result
+ if s[result] != '-': result = s.len
-type
- Version* = distinct string
- PackageInfo = Table[string, tuple[version, checksum: string]]
+const
+ latest = "head"
-proc `$`*(ver: Version): string {.borrow.}
+proc `<.`(a, b: string): bool =
+ # wether a has a smaller version than b:
+ if a == latest: return false
+ var i = 0
+ var j = 0
+ var verA = 0
+ var verB = 0
+ while true:
+ let ii = parseInt(a, verA, i)
+ let jj = parseInt(b, verB, j)
+ # if A has no number left, but B has, B is preferred: 0.8 vs 0.8.3
+ if ii <= 0 or jj <= 0: return jj > 0
+ if verA < verB: return true
+ elif verA > verB: return false
+ # else: same version number; continue:
+ inc i, ii
+ inc j, jj
+ if a[i] == '.': inc i
+ if b[j] == '.': inc j
-proc newVersion*(ver: string): Version =
- doAssert(ver.len == 0 or ver[0] in {'#', '\0'} + Digits,
- "Wrong version: " & ver)
- return Version(ver)
-
-proc isSpecial(ver: Version): bool =
- return ($ver).len > 0 and ($ver)[0] == '#'
-
-proc isValidVersion(v: string): bool =
- if v.len > 0:
- if v[0] in {'#'} + Digits: return true
-
-proc `<`*(ver: Version, ver2: Version): bool =
- ## This is synced from Nimble's version module.
-
- # Handling for special versions such as "#head" or "#branch".
- if ver.isSpecial or ver2.isSpecial:
- if ver2.isSpecial and ($ver2).normalize == "#head":
- return ($ver).normalize != "#head"
-
- if not ver2.isSpecial:
- # `#aa111 < 1.1`
- return ($ver).normalize != "#head"
-
- # Handling for normal versions such as "0.1.0" or "1.0".
- var sVer = string(ver).split('.')
- var sVer2 = string(ver2).split('.')
- for i in 0.. 0:
- # If the user has more than one .nimble-link file then... we just ignore it.
- # Spec for these files is available in Nimble's readme:
- # https://github.com/nim-lang/nimble#nimble-link
- let nimbleLinkLines = readFile(nimbleLinks[0]).splitLines()
- path = nimbleLinkLines[1]
- if not path.isAbsolute():
- path = p / path
+proc addNimblePath(p: string, info: TLineInfo) =
+ if not contains(options.searchPaths, p):
+ message(info, hintPath, p)
+ lists.prependStr(options.lazyPaths, p)
- if not contains(conf.searchPaths, AbsoluteDir path):
- message(conf, info, hintPath, path)
- conf.lazyPaths.insert(AbsoluteDir path, 0)
+proc addPathWithNimFiles(p: string, info: TLineInfo) =
+ proc hasNimFile(dir: string): bool =
+ for kind, path in walkDir(dir):
+ if kind == pcFile and path.endsWith(".nim"):
+ result = true
+ break
+ if hasNimFile(p):
+ addNimblePath(p, info)
+ else:
+ for kind, p2 in walkDir(p):
+ if hasNimFile(p2): addNimblePath(p2, info)
-proc addPathRec(conf: ConfigRef; dir: string, info: TLineInfo) =
- var packages: PackageInfo
+proc addPathRec(dir: string, info: TLineInfo) =
+ var packages = newStringTable(modeStyleInsensitive)
var pos = dir.len-1
if dir[pos] in {DirSep, AltSep}: inc(pos)
for k,p in os.walkDir(dir):
if k == pcDir and p[pos] != '.':
- addPackage(conf, packages, p, info)
+ addPackage(packages, p)
for p in packages.chosen:
- addNimblePath(conf, p, info)
+ addNimblePath(p, info)
-proc nimblePath*(conf: ConfigRef; path: AbsoluteDir, info: TLineInfo) =
- addPathRec(conf, path.string, info)
- addNimblePath(conf, path.string, info)
- let i = conf.nimblePaths.find(path)
- if i != -1:
- conf.nimblePaths.delete(i)
- conf.nimblePaths.insert(path, 0)
+proc nimblePath*(path: string, info: TLineInfo) =
+ addPathRec(path, info)
+ addNimblePath(path, info)
diff --git a/compiler/nimconf.nim b/compiler/nimconf.nim
index fceedb2c4..496bd0123 100644
--- a/compiler/nimconf.nim
+++ b/compiler/nimconf.nim
@@ -10,58 +10,55 @@
# This module handles the reading of the config file.
import
- llstream, commands, os, strutils, msgs, lexer, ast,
- options, idents, wordrecg, strtabs, lineinfos, pathutils, scriptconfig
-
-when defined(nimPreviewSlimSystem):
- import std/syncio
+ llstream, nversion, commands, os, strutils, msgs, platform, condsyms, lexer,
+ options, idents, wordrecg, strtabs
# ---------------- configuration file parser -----------------------------
-# we use Nim's lexer here to save space and work
+# we use Nim's scanner here to save space and work
-proc ppGetTok(L: var Lexer, tok: var Token) =
+proc ppGetTok(L: var TLexer, tok: var TToken) =
# simple filter
rawGetTok(L, tok)
while tok.tokType in {tkComment}: rawGetTok(L, tok)
-proc parseExpr(L: var Lexer, tok: var Token; config: ConfigRef): bool
-proc parseAtom(L: var Lexer, tok: var Token; config: ConfigRef): bool =
+proc parseExpr(L: var TLexer, tok: var TToken): bool
+proc parseAtom(L: var TLexer, tok: var TToken): bool =
if tok.tokType == tkParLe:
ppGetTok(L, tok)
- result = parseExpr(L, tok, config)
+ result = parseExpr(L, tok)
if tok.tokType == tkParRi: ppGetTok(L, tok)
- else: lexMessage(L, errGenerated, "expected closing ')'")
- elif tok.tokType == tkNot:
+ else: lexMessage(L, errTokenExpected, "\')\'")
+ elif tok.ident.id == ord(wNot):
ppGetTok(L, tok)
- result = not parseAtom(L, tok, config)
+ result = not parseAtom(L, tok)
else:
- result = isDefined(config, tok.ident.s)
+ result = isDefined(tok.ident)
ppGetTok(L, tok)
-proc parseAndExpr(L: var Lexer, tok: var Token; config: ConfigRef): bool =
- result = parseAtom(L, tok, config)
- while tok.tokType == tkAnd:
+proc parseAndExpr(L: var TLexer, tok: var TToken): bool =
+ result = parseAtom(L, tok)
+ while tok.ident.id == ord(wAnd):
ppGetTok(L, tok) # skip "and"
- var b = parseAtom(L, tok, config)
+ var b = parseAtom(L, tok)
result = result and b
-proc parseExpr(L: var Lexer, tok: var Token; config: ConfigRef): bool =
- result = parseAndExpr(L, tok, config)
- while tok.tokType == tkOr:
+proc parseExpr(L: var TLexer, tok: var TToken): bool =
+ result = parseAndExpr(L, tok)
+ while tok.ident.id == ord(wOr):
ppGetTok(L, tok) # skip "or"
- var b = parseAndExpr(L, tok, config)
+ var b = parseAndExpr(L, tok)
result = result or b
-proc evalppIf(L: var Lexer, tok: var Token; config: ConfigRef): bool =
+proc evalppIf(L: var TLexer, tok: var TToken): bool =
ppGetTok(L, tok) # skip 'if' or 'elif'
- result = parseExpr(L, tok, config)
+ result = parseExpr(L, tok)
if tok.tokType == tkColon: ppGetTok(L, tok)
- else: lexMessage(L, errGenerated, "expected ':'")
+ else: lexMessage(L, errTokenExpected, "\':\'")
-#var condStack: seq[bool] = @[]
+var condStack: seq[bool] = @[]
-proc doEnd(L: var Lexer, tok: var Token; condStack: var seq[bool]) =
- if high(condStack) < 0: lexMessage(L, errGenerated, "expected @if")
+proc doEnd(L: var TLexer, tok: var TToken) =
+ if high(condStack) < 0: lexMessage(L, errTokenExpected, "@if")
ppGetTok(L, tok) # skip 'end'
setLen(condStack, high(condStack))
@@ -69,22 +66,20 @@ type
TJumpDest = enum
jdEndif, jdElseEndif
-proc jumpToDirective(L: var Lexer, tok: var Token, dest: TJumpDest; config: ConfigRef;
- condStack: var seq[bool])
-proc doElse(L: var Lexer, tok: var Token; config: ConfigRef; condStack: var seq[bool]) =
- if high(condStack) < 0: lexMessage(L, errGenerated, "expected @if")
+proc jumpToDirective(L: var TLexer, tok: var TToken, dest: TJumpDest)
+proc doElse(L: var TLexer, tok: var TToken) =
+ if high(condStack) < 0: lexMessage(L, errTokenExpected, "@if")
ppGetTok(L, tok)
if tok.tokType == tkColon: ppGetTok(L, tok)
- if condStack[high(condStack)]: jumpToDirective(L, tok, jdEndif, config, condStack)
+ if condStack[high(condStack)]: jumpToDirective(L, tok, jdEndif)
-proc doElif(L: var Lexer, tok: var Token; config: ConfigRef; condStack: var seq[bool]) =
- if high(condStack) < 0: lexMessage(L, errGenerated, "expected @if")
- var res = evalppIf(L, tok, config)
- if condStack[high(condStack)] or not res: jumpToDirective(L, tok, jdElseEndif, config, condStack)
+proc doElif(L: var TLexer, tok: var TToken) =
+ if high(condStack) < 0: lexMessage(L, errTokenExpected, "@if")
+ var res = evalppIf(L, tok)
+ if condStack[high(condStack)] or not res: jumpToDirective(L, tok, jdElseEndif)
else: condStack[high(condStack)] = true
-proc jumpToDirective(L: var Lexer, tok: var Token, dest: TJumpDest; config: ConfigRef;
- condStack: var seq[bool]) =
+proc jumpToDirective(L: var TLexer, tok: var TToken, dest: TJumpDest) =
var nestedIfs = 0
while true:
if tok.ident != nil and tok.ident.s == "@":
@@ -94,223 +89,166 @@ proc jumpToDirective(L: var Lexer, tok: var Token, dest: TJumpDest; config: Conf
inc(nestedIfs)
of wElse:
if dest == jdElseEndif and nestedIfs == 0:
- doElse(L, tok, config, condStack)
+ doElse(L, tok)
break
of wElif:
if dest == jdElseEndif and nestedIfs == 0:
- doElif(L, tok, config, condStack)
+ doElif(L, tok)
break
of wEnd:
if nestedIfs == 0:
- doEnd(L, tok, condStack)
+ doEnd(L, tok)
break
if nestedIfs > 0: dec(nestedIfs)
else:
discard
ppGetTok(L, tok)
elif tok.tokType == tkEof:
- lexMessage(L, errGenerated, "expected @end")
+ lexMessage(L, errTokenExpected, "@end")
else:
ppGetTok(L, tok)
-proc parseDirective(L: var Lexer, tok: var Token; config: ConfigRef; condStack: var seq[bool]) =
+proc parseDirective(L: var TLexer, tok: var TToken) =
ppGetTok(L, tok) # skip @
case whichKeyword(tok.ident)
of wIf:
- setLen(condStack, condStack.len + 1)
- let res = evalppIf(L, tok, config)
+ setLen(condStack, len(condStack) + 1)
+ let res = evalppIf(L, tok)
condStack[high(condStack)] = res
- if not res: jumpToDirective(L, tok, jdElseEndif, config, condStack)
- of wElif: doElif(L, tok, config, condStack)
- of wElse: doElse(L, tok, config, condStack)
- of wEnd: doEnd(L, tok, condStack)
+ if not res: jumpToDirective(L, tok, jdElseEndif)
+ of wElif: doElif(L, tok)
+ of wElse: doElse(L, tok)
+ of wEnd: doEnd(L, tok)
of wWrite:
ppGetTok(L, tok)
- msgs.msgWriteln(config, strtabs.`%`($tok, config.configVars,
+ msgs.msgWriteln(strtabs.`%`(tokToStr(tok), options.gConfigVars,
{useEnvironment, useKey}))
ppGetTok(L, tok)
else:
case tok.ident.s.normalize
of "putenv":
ppGetTok(L, tok)
- var key = $tok
+ var key = tokToStr(tok)
ppGetTok(L, tok)
- os.putEnv(key, $tok)
+ os.putEnv(key, tokToStr(tok))
ppGetTok(L, tok)
of "prependenv":
ppGetTok(L, tok)
- var key = $tok
+ var key = tokToStr(tok)
ppGetTok(L, tok)
- os.putEnv(key, $tok & os.getEnv(key))
+ os.putEnv(key, tokToStr(tok) & os.getEnv(key))
ppGetTok(L, tok)
of "appendenv":
ppGetTok(L, tok)
- var key = $tok
+ var key = tokToStr(tok)
ppGetTok(L, tok)
- os.putEnv(key, os.getEnv(key) & $tok)
+ os.putEnv(key, os.getEnv(key) & tokToStr(tok))
ppGetTok(L, tok)
- else:
- lexMessage(L, errGenerated, "invalid directive: '$1'" % $tok)
+ else: lexMessage(L, errInvalidDirectiveX, tokToStr(tok))
-proc confTok(L: var Lexer, tok: var Token; config: ConfigRef; condStack: var seq[bool]) =
+proc confTok(L: var TLexer, tok: var TToken) =
ppGetTok(L, tok)
while tok.ident != nil and tok.ident.s == "@":
- parseDirective(L, tok, config, condStack) # else: give the token to the parser
+ parseDirective(L, tok) # else: give the token to the parser
-proc checkSymbol(L: Lexer, tok: Token) =
- if tok.tokType notin {tkSymbol..tkInt64Lit, tkStrLit..tkTripleStrLit}:
- lexMessage(L, errGenerated, "expected identifier, but got: " & $tok)
+proc checkSymbol(L: TLexer, tok: TToken) =
+ if tok.tokType notin {tkSymbol..pred(tkIntLit), tkStrLit..tkTripleStrLit}:
+ lexMessage(L, errIdentifierExpected, tokToStr(tok))
-proc parseAssignment(L: var Lexer, tok: var Token;
- config: ConfigRef; condStack: var seq[bool]) =
- if tok.ident != nil:
- if tok.ident.s == "-" or tok.ident.s == "--":
- confTok(L, tok, config, condStack) # skip unnecessary prefix
+proc parseAssignment(L: var TLexer, tok: var TToken) =
+ if tok.ident.id == getIdent("-").id or tok.ident.id == getIdent("--").id:
+ confTok(L, tok) # skip unnecessary prefix
var info = getLineInfo(L, tok) # save for later in case of an error
checkSymbol(L, tok)
- var s = $tok
- confTok(L, tok, config, condStack) # skip symbol
+ var s = tokToStr(tok)
+ confTok(L, tok) # skip symbol
var val = ""
while tok.tokType == tkDot:
- s.add('.')
- confTok(L, tok, config, condStack)
+ add(s, '.')
+ confTok(L, tok)
checkSymbol(L, tok)
- s.add($tok)
- confTok(L, tok, config, condStack)
+ add(s, tokToStr(tok))
+ confTok(L, tok)
if tok.tokType == tkBracketLe:
# BUGFIX: val, not s!
- confTok(L, tok, config, condStack)
+ # BUGFIX: do not copy '['!
+ confTok(L, tok)
checkSymbol(L, tok)
- val.add('[')
- val.add($tok)
- confTok(L, tok, config, condStack)
- if tok.tokType == tkBracketRi: confTok(L, tok, config, condStack)
- else: lexMessage(L, errGenerated, "expected closing ']'")
- val.add(']')
- let percent = tok.ident != nil and tok.ident.s == "%="
+ add(val, tokToStr(tok))
+ confTok(L, tok)
+ if tok.tokType == tkBracketRi: confTok(L, tok)
+ else: lexMessage(L, errTokenExpected, "']'")
+ add(val, ']')
+ let percent = tok.ident.id == getIdent("%=").id
if tok.tokType in {tkColon, tkEquals} or percent:
- if val.len > 0: val.add(':')
- confTok(L, tok, config, condStack) # skip ':' or '=' or '%'
+ if len(val) > 0: add(val, ':')
+ confTok(L, tok) # skip ':' or '=' or '%'
checkSymbol(L, tok)
- val.add($tok)
- confTok(L, tok, config, condStack) # skip symbol
- if tok.tokType in {tkColon, tkEquals}:
- val.add($tok) # add the :
- confTok(L, tok, config, condStack) # skip symbol
+ add(val, tokToStr(tok))
+ confTok(L, tok) # skip symbol
+ while tok.ident != nil and tok.ident.id == getIdent("&").id:
+ confTok(L, tok)
checkSymbol(L, tok)
- val.add($tok) # add the token after it
- confTok(L, tok, config, condStack) # skip symbol
- while tok.ident != nil and tok.ident.s == "&":
- confTok(L, tok, config, condStack)
- checkSymbol(L, tok)
- val.add($tok)
- confTok(L, tok, config, condStack)
+ add(val, tokToStr(tok))
+ confTok(L, tok)
if percent:
- processSwitch(s, strtabs.`%`(val, config.configVars,
- {useEnvironment, useEmpty}), passPP, info, config)
+ processSwitch(s, strtabs.`%`(val, options.gConfigVars,
+ {useEnvironment, useEmpty}), passPP, info)
else:
- processSwitch(s, val, passPP, info, config)
+ processSwitch(s, val, passPP, info)
-proc readConfigFile*(filename: AbsoluteFile; cache: IdentCache;
- config: ConfigRef): bool =
+proc readConfigFile(filename: string) =
var
- L: Lexer
- tok: Token
+ L: TLexer
+ tok: TToken
stream: PLLStream
stream = llStreamOpen(filename, fmRead)
if stream != nil:
initToken(tok)
- openLexer(L, filename, stream, cache, config)
+ openLexer(L, filename, stream)
tok.tokType = tkEof # to avoid a pointless warning
- var condStack: seq[bool] = @[]
- confTok(L, tok, config, condStack) # read in the first token
- while tok.tokType != tkEof: parseAssignment(L, tok, config, condStack)
- if condStack.len > 0: lexMessage(L, errGenerated, "expected @end")
+ confTok(L, tok) # read in the first token
+ while tok.tokType != tkEof: parseAssignment(L, tok)
+ if len(condStack) > 0: lexMessage(L, errTokenExpected, "@end")
closeLexer(L)
- return true
+ rawMessage(hintConf, filename)
-proc getUserConfigPath*(filename: RelativeFile): AbsoluteFile =
- result = getConfigDir().AbsoluteDir / RelativeDir"nim" / filename
+proc getUserConfigPath(filename: string): string =
+ result = joinPath(getConfigDir(), filename)
-proc getSystemConfigPath*(conf: ConfigRef; filename: RelativeFile): AbsoluteFile =
+proc getSystemConfigPath(filename: string): string =
# try standard configuration file (installation did not distribute files
# the UNIX way)
- let p = getPrefixDir(conf)
- result = p / RelativeDir"config" / filename
+ let p = getPrefixDir()
+ result = joinPath([p, "config", filename])
when defined(unix):
- if not fileExists(result): result = p / RelativeDir"etc/nim" / filename
- if not fileExists(result): result = AbsoluteDir"/etc/nim" / filename
+ if not existsFile(result): result = joinPath([p, "etc", filename])
+ if not existsFile(result): result = "/etc/" & filename
-proc loadConfigs*(cfg: RelativeFile; cache: IdentCache; conf: ConfigRef; idgen: IdGenerator) =
- setDefaultLibpath(conf)
- template readConfigFile(path) =
- let configPath = path
- if readConfigFile(configPath, cache, conf):
- conf.configFiles.add(configPath)
+proc loadConfigs*(cfg: string) =
+ setDefaultLibpath()
- template runNimScriptIfExists(path: AbsoluteFile, isMain = false) =
- let p = path # eval once
- var s: PLLStream
- if isMain and optWasNimscript in conf.globalOptions:
- if conf.projectIsStdin: s = stdin.llStreamOpen
- elif conf.projectIsCmd: s = llStreamOpen(conf.cmdInput)
- if s == nil and fileExists(p): s = llStreamOpen(p, fmRead)
- if s != nil:
- conf.configFiles.add(p)
- runNimScript(cache, p, idgen, freshDefines = false, conf, s)
+ if optSkipConfigFile notin gGlobalOptions:
+ readConfigFile(getSystemConfigPath(cfg))
- if optSkipSystemConfigFile notin conf.globalOptions:
- readConfigFile(getSystemConfigPath(conf, cfg))
-
- if cfg == DefaultConfig:
- runNimScriptIfExists(getSystemConfigPath(conf, DefaultConfigNims))
-
- if optSkipUserConfigFile notin conf.globalOptions:
+ if optSkipUserConfigFile notin gGlobalOptions:
readConfigFile(getUserConfigPath(cfg))
- if cfg == DefaultConfig:
- runNimScriptIfExists(getUserConfigPath(DefaultConfigNims))
+ var pd = if gProjectPath.len > 0: gProjectPath else: getCurrentDir()
+ if optSkipParentConfigFiles notin gGlobalOptions:
+ for dir in parentDirs(pd, fromRoot=true, inclusive=false):
+ readConfigFile(dir / cfg)
- let pd = if not conf.projectPath.isEmpty: conf.projectPath else: AbsoluteDir(getCurrentDir())
- if optSkipParentConfigFiles notin conf.globalOptions:
- for dir in parentDirs(pd.string, fromRoot=true, inclusive=false):
- readConfigFile(AbsoluteDir(dir) / cfg)
-
- if cfg == DefaultConfig:
- runNimScriptIfExists(AbsoluteDir(dir) / DefaultConfigNims)
-
- if optSkipProjConfigFile notin conf.globalOptions:
+ if optSkipProjConfigFile notin gGlobalOptions:
readConfigFile(pd / cfg)
- if cfg == DefaultConfig:
- runNimScriptIfExists(pd / DefaultConfigNims)
- if conf.projectName.len != 0:
+ if gProjectName.len != 0:
# new project wide config file:
- var projectConfig = changeFileExt(conf.projectFull, "nimcfg")
+ var projectConfig = changeFileExt(gProjectFull, "nimcfg")
if not fileExists(projectConfig):
- projectConfig = changeFileExt(conf.projectFull, "nim.cfg")
+ projectConfig = changeFileExt(gProjectFull, "nim.cfg")
+ if not fileExists(projectConfig):
+ projectConfig = changeFileExt(gProjectFull, "nimrod.cfg")
+ if fileExists(projectConfig):
+ rawMessage(warnDeprecated, projectConfig)
readConfigFile(projectConfig)
-
-
- let scriptFile = conf.projectFull.changeFileExt("nims")
- let scriptIsProj = scriptFile == conf.projectFull
- template showHintConf =
- for filename in conf.configFiles:
- # delayed to here so that `hintConf` is honored
- rawMessage(conf, hintConf, filename.string)
- if conf.cmd == cmdNimscript:
- showHintConf()
- conf.configFiles.setLen 0
- if conf.cmd notin {cmdIdeTools, cmdCheck, cmdDump}:
- if conf.cmd == cmdNimscript:
- runNimScriptIfExists(conf.projectFull, isMain = true)
- else:
- runNimScriptIfExists(scriptFile, isMain = true)
- else:
- if not scriptIsProj:
- runNimScriptIfExists(scriptFile, isMain = true)
- else:
- # 'nimsuggest foo.nims' means to just auto-complete the NimScript file
- # `nim check foo.nims' means to check the syntax of the NimScript file
- discard
- showHintConf()
diff --git a/compiler/nimeval.nim b/compiler/nimeval.nim
index 8e8f4ac7b..2bddb76e7 100644
--- a/compiler/nimeval.nim
+++ b/compiler/nimeval.nim
@@ -1,174 +1,33 @@
#
#
# The Nim Compiler
-# (c) Copyright 2018 Andreas Rumpf
+# (c) Copyright 2013 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
## exposes the Nim VM to clients.
+
import
- ast, modules, condsyms,
- options, llstream, lineinfos, vm,
- vmdef, modulegraphs, idents, os, pathutils,
- scriptconfig, std/compilesettings
+ ast, modules, passes, passaux, condsyms,
+ options, nimconf, lists, sem, semdata, llstream, vm
-import pipelines
+proc execute*(program: string) =
+ passes.gIncludeFile = includeModule
+ passes.gImportModule = importModule
+ initDefines()
+ loadConfigs(DefaultConfig)
+ initDefines()
+ defineSymbol("nimrodvm")
+ when hasFFI: defineSymbol("nimffi")
+ registerPass(verbosePass)
+ registerPass(semPass)
+ registerPass(evalPass)
-when defined(nimPreviewSlimSystem):
- import std/[assertions, syncio]
-
-type
- Interpreter* = ref object ## Use Nim as an interpreter with this object
- mainModule: PSym
- graph: ModuleGraph
- scriptName: string
- idgen: IdGenerator
-
-iterator exportedSymbols*(i: Interpreter): PSym =
- assert i != nil
- assert i.mainModule != nil, "no main module selected"
- for s in modulegraphs.allSyms(i.graph, i.mainModule):
- yield s
-
-proc selectUniqueSymbol*(i: Interpreter; name: string;
- symKinds: set[TSymKind] = {skLet, skVar}): PSym =
- ## Can be used to access a unique symbol of ``name`` and
- ## the given ``symKinds`` filter.
- assert i != nil
- assert i.mainModule != nil, "no main module selected"
- let n = getIdent(i.graph.cache, name)
- var it: ModuleIter
- var s = initModuleIter(it, i.graph, i.mainModule, n)
- result = nil
- while s != nil:
- if s.kind in symKinds:
- if result == nil: result = s
- else: return nil # ambiguous
- s = nextModuleIter(it, i.graph)
-
-proc selectRoutine*(i: Interpreter; name: string): PSym =
- ## Selects a declared routine (proc/func/etc) from the main module.
- ## The routine needs to have the export marker ``*``. The only matching
- ## routine is returned and ``nil`` if it is overloaded.
- result = selectUniqueSymbol(i, name, {skTemplate, skMacro, skFunc,
- skMethod, skProc, skConverter})
-
-proc callRoutine*(i: Interpreter; routine: PSym; args: openArray[PNode]): PNode =
- assert i != nil
- result = vm.execProc(PCtx i.graph.vm, routine, args)
-
-proc getGlobalValue*(i: Interpreter; letOrVar: PSym): PNode =
- result = vm.getGlobalValue(PCtx i.graph.vm, letOrVar)
-
-proc setGlobalValue*(i: Interpreter; letOrVar: PSym, val: PNode) =
- ## Sets a global value to a given PNode, does not do any type checking.
- vm.setGlobalValue(PCtx i.graph.vm, letOrVar, val)
-
-proc implementRoutine*(i: Interpreter; pkg, module, name: string;
- impl: proc (a: VmArgs) {.closure, gcsafe.}) =
- assert i != nil
- let vm = PCtx(i.graph.vm)
- vm.registerCallback(pkg & "." & module & "." & name, impl)
-
-proc evalScript*(i: Interpreter; scriptStream: PLLStream = nil) =
- ## This can also be used to *reload* the script.
- assert i != nil
- assert i.mainModule != nil, "no main module selected"
- initStrTables(i.graph, i.mainModule)
- i.mainModule.ast = nil
-
- let s = if scriptStream != nil: scriptStream
- else: llStreamOpen(findFile(i.graph.config, i.scriptName), fmRead)
- discard processPipelineModule(i.graph, i.mainModule, i.idgen, s)
-
-proc findNimStdLib*(): string =
- ## Tries to find a path to a valid "system.nim" file.
- ## Returns "" on failure.
- try:
- let nimexe = os.findExe("nim")
- # this can't work with choosenim shims, refs https://github.com/dom96/choosenim/issues/189
- # it'd need `nim dump --dump.format:json . | jq -r .libpath`
- # which we should simplify as `nim dump --key:libpath`
- if nimexe.len == 0: return ""
- result = nimexe.splitPath()[0] /../ "lib"
- if not fileExists(result / "system.nim"):
- when defined(unix):
- result = nimexe.expandSymlink.splitPath()[0] /../ "lib"
- if not fileExists(result / "system.nim"): return ""
- except OSError, ValueError:
- return ""
-
-proc findNimStdLibCompileTime*(): string =
- ## Same as `findNimStdLib` but uses source files used at compile time,
- ## and asserts on error.
- result = querySetting(libPath)
- doAssert fileExists(result / "system.nim"), "result:" & result
-
-proc createInterpreter*(scriptName: string;
- searchPaths: openArray[string];
- flags: TSandboxFlags = {},
- defines = @[("nimscript", "true")],
- registerOps = true): Interpreter =
- var conf = newConfigRef()
- var cache = newIdentCache()
- var graph = newModuleGraph(cache, conf)
- connectPipelineCallbacks(graph)
- initDefines(conf.symbols)
- for define in defines:
- defineSymbol(conf.symbols, define[0], define[1])
-
- for p in searchPaths:
- conf.searchPaths.add(AbsoluteDir p)
- if conf.libpath.isEmpty: conf.libpath = AbsoluteDir p
-
- var m = graph.makeModule(scriptName)
+ appendStr(searchPaths, options.libpath)
+ compileSystemModule()
+ var m = makeStdinModule()
incl(m.flags, sfMainModule)
- var idgen = idGeneratorFromModule(m)
- var vm = newCtx(m, cache, graph, idgen)
- vm.mode = emRepl
- vm.features = flags
- if registerOps:
- vm.registerAdditionalOps() # Required to register parts of stdlib modules
- graph.vm = vm
- setPipeLinePass(graph, EvalPass)
- graph.compilePipelineSystemModule()
- result = Interpreter(mainModule: m, graph: graph, scriptName: scriptName, idgen: idgen)
-
-proc destroyInterpreter*(i: Interpreter) =
- ## destructor.
- discard "currently nothing to do."
-
-proc registerErrorHook*(i: Interpreter, hook:
- proc (config: ConfigRef; info: TLineInfo; msg: string;
- severity: Severity) {.gcsafe.}) =
- i.graph.config.structuredErrorHook = hook
-
-proc runRepl*(r: TLLRepl;
- searchPaths: openArray[string];
- supportNimscript: bool) =
- ## deadcode but please don't remove... might be revived
- var conf = newConfigRef()
- var cache = newIdentCache()
- var graph = newModuleGraph(cache, conf)
-
- for p in searchPaths:
- conf.searchPaths.add(AbsoluteDir p)
- if conf.libpath.isEmpty: conf.libpath = AbsoluteDir p
-
- conf.cmd = cmdInteractive # see also `setCmd`
- conf.setErrorMaxHighMaybe
- initDefines(conf.symbols)
- defineSymbol(conf.symbols, "nimscript")
- if supportNimscript: defineSymbol(conf.symbols, "nimconfig")
- when hasFFI: defineSymbol(graph.config.symbols, "nimffi")
- var m = graph.makeStdinModule()
- incl(m.flags, sfMainModule)
- var idgen = idGeneratorFromModule(m)
-
- if supportNimscript: graph.vm = setupVM(m, cache, "stdin", graph, idgen)
- setPipeLinePass(graph, InterpreterPass)
- graph.compilePipelineSystemModule()
- discard processPipelineModule(graph, m, idgen, llStreamOpenStdIn(r))
+ processModule(m, llStreamOpen(program), nil)
diff --git a/compiler/nimfix/nimfix.nim b/compiler/nimfix/nimfix.nim
index 30c138f79..39436702f 100644
--- a/compiler/nimfix/nimfix.nim
+++ b/compiler/nimfix/nimfix.nim
@@ -10,11 +10,10 @@
## Nimfix is a tool that helps to convert old-style Nimrod code to Nim code.
import strutils, os, parseopt
-import compiler/[options, commands, modules, sem,
- passes, passaux, linter,
- msgs, nimconf,
- extccomp, condsyms,
- modulegraphs, idents]
+import compiler/options, compiler/commands, compiler/modules, compiler/sem,
+ compiler/passes, compiler/passaux, compiler/nimfix/pretty,
+ compiler/msgs, compiler/nimconf,
+ compiler/extccomp, compiler/condsyms, compiler/lists
const Usage = """
Nimfix - Tool to patch Nim code
@@ -38,16 +37,16 @@ In addition, all command line options of Nim are supported.
proc mainCommand =
registerPass verbosePass
registerPass semPass
- conf.setCmd cmdNimfix
- searchPaths.add options.libpath
+ gCmd = cmdPretty
+ appendStr(searchPaths, options.libpath)
if gProjectFull.len != 0:
# current path is always looked first for modules
- searchPaths.insert(gProjectPath, 0)
+ prependStr(searchPaths, gProjectPath)
- compileProject(newModuleGraph(), newIdentCache())
+ compileProject()
pretty.overwriteFiles()
-proc processCmdLine*(pass: TCmdLinePass, cmd: string, config: ConfigRef) =
+proc processCmdLine*(pass: TCmdLinePass, cmd: string) =
var p = parseopt.initOptParser(cmd)
var argsCount = 0
gOnlyMainfile = true
@@ -74,18 +73,18 @@ proc processCmdLine*(pass: TCmdLinePass, cmd: string, config: ConfigRef) =
of "auto": gStyleCheck = StyleCheck.Auto
else: localError(gCmdLineInfo, errOnOrOffExpected)
of "wholeproject": gOnlyMainfile = false
- of "besteffort": msgs.gErrorMax = high(int) # don't stop after first error
+ of "besteffort": msgs.gErrorMax = high(int) # dont stop after first error
else:
- processSwitch(pass, p, config)
+ processSwitch(pass, p)
of cmdArgument:
options.gProjectName = unixToNativePath(p.key)
# if processArgument(pass, p, argsCount): break
-proc handleCmdLine(config: ConfigRef) =
+proc handleCmdLine() =
if paramCount() == 0:
stdout.writeLine(Usage)
else:
- processCmdLine(passCmd1, "", config)
+ processCmdLine(passCmd1, "")
if gProjectName != "":
try:
gProjectFull = canonicalizePath(gProjectName)
@@ -96,16 +95,16 @@ proc handleCmdLine(config: ConfigRef) =
gProjectName = p.name
else:
gProjectPath = getCurrentDir()
- loadConfigs(DefaultConfig, config) # load all config files
+ loadConfigs(DefaultConfig) # load all config files
# now process command line arguments again, because some options in the
- # command line can overwrite the config file's settings
+ # command line can overwite the config file's settings
extccomp.initVars()
- processCmdLine(passCmd2, "", config)
+ processCmdLine(passCmd2, "")
mainCommand()
-when compileOption("gc", "refc"):
+when compileOption("gc", "v2") or compileOption("gc", "refc"):
GC_disableMarkAndSweep()
condsyms.initDefines()
defineSymbol "nimfix"
-handleCmdline newConfigRef()
+handleCmdline()
diff --git a/compiler/nimfix/pretty.nim b/compiler/nimfix/pretty.nim
new file mode 100644
index 000000000..1123afb9e
--- /dev/null
+++ b/compiler/nimfix/pretty.nim
@@ -0,0 +1,154 @@
+#
+#
+# The Nim Compiler
+# (c) Copyright 2015 Andreas Rumpf
+#
+# See the file "copying.txt", included in this
+# distribution, for details about the copyright.
+#
+
+## This module implements the code "prettifier". This is part of the toolchain
+## to convert Nim code into a consistent style.
+
+import
+ strutils, os, intsets, strtabs
+
+import compiler/options, compiler/ast, compiler/astalgo, compiler/msgs,
+ compiler/semdata, compiler/nimfix/prettybase, compiler/ropes, compiler/idents
+
+type
+ StyleCheck* {.pure.} = enum None, Warn, Auto
+
+var
+ gOverWrite* = true
+ gStyleCheck*: StyleCheck
+ gCheckExtern*, gOnlyMainfile*: bool
+
+proc overwriteFiles*() =
+ let doStrip = options.getConfigVar("pretty.strip").normalize == "on"
+ for i in 0 .. high(gSourceFiles):
+ if gSourceFiles[i].dirty and not gSourceFiles[i].isNimfixFile and
+ (not gOnlyMainfile or gSourceFiles[i].fileIdx == gProjectMainIdx):
+ let newFile = if gOverWrite: gSourceFiles[i].fullpath
+ else: gSourceFiles[i].fullpath.changeFileExt(".pretty.nim")
+ try:
+ var f = open(newFile, fmWrite)
+ for line in gSourceFiles[i].lines:
+ if doStrip:
+ f.write line.strip(leading = false, trailing = true)
+ else:
+ f.write line
+ f.write(gSourceFiles[i].newline)
+ f.close
+ except IOError:
+ rawMessage(errCannotOpenFile, newFile)
+
+proc `=~`(s: string, a: openArray[string]): bool =
+ for x in a:
+ if s.startsWith(x): return true
+
+proc beautifyName(s: string, k: TSymKind): string =
+ # minimal set of rules here for transition:
+ # GC_ is allowed
+
+ let allUpper = allCharsInSet(s, {'A'..'Z', '0'..'9', '_'})
+ if allUpper and k in {skConst, skEnumField, skType}: return s
+ result = newStringOfCap(s.len)
+ var i = 0
+ case k
+ of skType, skGenericParam:
+ # Types should start with a capital unless builtins like 'int' etc.:
+ if s =~ ["int", "uint", "cint", "cuint", "clong", "cstring", "string",
+ "char", "byte", "bool", "openArray", "seq", "array", "void",
+ "pointer", "float", "csize", "cdouble", "cchar", "cschar",
+ "cshort", "cu", "nil", "expr", "stmt", "typedesc", "auto", "any",
+ "range", "openarray", "varargs", "set", "cfloat"
+ ]:
+ result.add s[i]
+ else:
+ result.add toUpper(s[i])
+ of skConst, skEnumField:
+ # for 'const' we keep how it's spelt; either upper case or lower case:
+ result.add s[0]
+ else:
+ # as a special rule, don't transform 'L' to 'l'
+ if s.len == 1 and s[0] == 'L': result.add 'L'
+ elif '_' in s: result.add(s[i])
+ else: result.add toLower(s[0])
+ inc i
+ while i < s.len:
+ if s[i] == '_':
+ 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
+ result.add s[i]
+ else:
+ inc i
+ result.add toUpper(s[i])
+ elif allUpper:
+ result.add toLower(s[i])
+ else:
+ result.add s[i]
+ inc i
+
+proc replaceInFile(info: TLineInfo; newName: string) =
+ loadFile(info)
+
+ let line = gSourceFiles[info.fileIndex].lines[info.line-1]
+ var first = min(info.col.int, line.len)
+ if first < 0: return
+ #inc first, skipIgnoreCase(line, "proc ", first)
+ while first > 0 and line[first-1] in prettybase.Letters: dec first
+ if first < 0: return
+ if line[first] == '`': inc first
+
+ let last = first+identLen(line, first)-1
+ if differ(line, first, last, newName):
+ # last-first+1 != newName.len or
+ var x = line.substr(0, first-1) & newName & line.substr(last+1)
+ system.shallowCopy(gSourceFiles[info.fileIndex].lines[info.line-1], x)
+ gSourceFiles[info.fileIndex].dirty = true
+
+proc checkStyle(info: TLineInfo, s: string, k: TSymKind; sym: PSym) =
+ let beau = beautifyName(s, k)
+ if s != beau:
+ if gStyleCheck == StyleCheck.Auto:
+ sym.name = getIdent(beau)
+ replaceInFile(info, beau)
+ else:
+ message(info, hintName, beau)
+
+proc styleCheckDefImpl(info: TLineInfo; s: PSym; k: TSymKind) =
+ # operators stay as they are:
+ if k in {skResult, skTemp} or s.name.s[0] notin prettybase.Letters: return
+ if k in {skType, skGenericParam} and sfAnon in s.flags: return
+ if {sfImportc, sfExportc} * s.flags == {} or gCheckExtern:
+ checkStyle(info, s.name.s, k, s)
+
+template styleCheckDef*(info: TLineInfo; s: PSym; k: TSymKind) =
+ when defined(nimfix):
+ if gStyleCheck != StyleCheck.None: styleCheckDefImpl(info, s, k)
+
+template styleCheckDef*(info: TLineInfo; s: PSym) =
+ styleCheckDef(info, s, s.kind)
+template styleCheckDef*(s: PSym) =
+ styleCheckDef(s.info, s, s.kind)
+
+proc styleCheckUseImpl(info: TLineInfo; s: PSym) =
+ if info.fileIndex < 0: return
+ # we simply convert it to what it looks like in the definition
+ # for consistency
+
+ # operators stay as they are:
+ if s.kind in {skResult, skTemp} or s.name.s[0] notin prettybase.Letters:
+ return
+ if s.kind in {skType, skGenericParam} and sfAnon in s.flags: return
+ let newName = s.name.s
+
+ replaceInFile(info, newName)
+ #if newName == "File": writeStackTrace()
+
+template styleCheckUse*(info: TLineInfo; s: PSym) =
+ when defined(nimfix):
+ if gStyleCheck != StyleCheck.None: styleCheckUseImpl(info, s)
diff --git a/compiler/nimfix/prettybase.nim b/compiler/nimfix/prettybase.nim
index 78c24bae3..0f17cbcb1 100644
--- a/compiler/nimfix/prettybase.nim
+++ b/compiler/nimfix/prettybase.nim
@@ -7,11 +7,64 @@
# distribution, for details about the copyright.
#
-import strutils except Letters
-import ".." / [ast, msgs, lineinfos, idents, options, linter]
+import strutils, lexbase, streams
+import compiler/ast, compiler/msgs, compiler/idents
+from os import splitFile
-proc replaceDeprecated*(conf: ConfigRef; info: TLineInfo; oldSym, newSym: PIdent) =
- let line = sourceLine(conf, info)
+type
+ TSourceFile* = object
+ lines*: seq[string]
+ dirty*, isNimfixFile*: bool
+ fullpath*, newline*: string
+ fileIdx*: int32
+
+var
+ gSourceFiles*: seq[TSourceFile] = @[]
+
+proc loadFile*(info: TLineInfo) =
+ let i = info.fileIndex
+ if i >= gSourceFiles.len:
+ gSourceFiles.setLen(i+1)
+ if gSourceFiles[i].lines.isNil:
+ gSourceFiles[i].fileIdx = info.fileIndex
+ gSourceFiles[i].lines = @[]
+ let path = info.toFullPath
+ gSourceFiles[i].fullpath = path
+ gSourceFiles[i].isNimfixFile = path.splitFile.ext == ".nimfix"
+ # we want to die here for IOError:
+ for line in lines(path):
+ gSourceFiles[i].lines.add(line)
+ # extract line ending of the file:
+ var lex: BaseLexer
+ open(lex, newFileStream(path, fmRead))
+ var pos = lex.bufpos
+ while true:
+ case lex.buf[pos]
+ of '\c':
+ gSourceFiles[i].newline = "\c\L"
+ break
+ of '\L', '\0':
+ gSourceFiles[i].newline = "\L"
+ break
+ else: discard
+ inc pos
+ close(lex)
+
+const
+ Letters* = {'a'..'z', 'A'..'Z', '0'..'9', '\x80'..'\xFF', '_'}
+
+proc identLen*(line: string, start: int): int =
+ while start+result < line.len and line[start+result] in Letters:
+ inc result
+
+proc differ*(line: string, a, b: int, x: string): bool =
+ let y = line[a..b]
+ result = cmpIgnoreStyle(y, x) == 0 and y != x
+
+proc replaceDeprecated*(info: TLineInfo; oldSym, newSym: PIdent) =
+ loadFile(info)
+
+ let line = gSourceFiles[info.fileIndex].lines[info.line-1]
var first = min(info.col.int, line.len)
if first < 0: return
#inc first, skipIgnoreCase(line, "proc ", first)
@@ -22,24 +75,20 @@ proc replaceDeprecated*(conf: ConfigRef; info: TLineInfo; oldSym, newSym: PIdent
let last = first+identLen(line, first)-1
if cmpIgnoreStyle(line[first..last], oldSym.s) == 0:
var x = line.substr(0, first-1) & newSym.s & line.substr(last+1)
- when defined(gcArc) or defined(gcOrc):
- conf.m.fileInfos[info.fileIndex.int32].lines[info.line.int-1] = move x
- else:
- system.shallowCopy(conf.m.fileInfos[info.fileIndex.int32].lines[info.line.int-1], x)
- conf.m.fileInfos[info.fileIndex.int32].dirty = true
+ system.shallowCopy(gSourceFiles[info.fileIndex].lines[info.line-1], x)
+ gSourceFiles[info.fileIndex].dirty = true
#if newSym.s == "File": writeStackTrace()
-proc replaceDeprecated*(conf: ConfigRef; info: TLineInfo; oldSym, newSym: PSym) =
- replaceDeprecated(conf, info, oldSym.name, newSym.name)
+proc replaceDeprecated*(info: TLineInfo; oldSym, newSym: PSym) =
+ replaceDeprecated(info, oldSym.name, newSym.name)
-proc replaceComment*(conf: ConfigRef; info: TLineInfo) =
- let line = sourceLine(conf, info)
+proc replaceComment*(info: TLineInfo) =
+ loadFile(info)
+
+ let line = gSourceFiles[info.fileIndex].lines[info.line-1]
var first = info.col.int
if line[first] != '#': inc first
var x = line.substr(0, first-1) & "discard " & line.substr(first+1).escape
- when defined(gcArc) or defined(gcOrc):
- conf.m.fileInfos[info.fileIndex.int32].lines[info.line.int-1] = move x
- else:
- system.shallowCopy(conf.m.fileInfos[info.fileIndex.int32].lines[info.line.int-1], x)
- conf.m.fileInfos[info.fileIndex.int32].dirty = true
+ system.shallowCopy(gSourceFiles[info.fileIndex].lines[info.line-1], x)
+ gSourceFiles[info.fileIndex].dirty = true
diff --git a/compiler/nimlexbase.nim b/compiler/nimlexbase.nim
index 3bc9af9c9..047890c44 100644
--- a/compiler/nimlexbase.nim
+++ b/compiler/nimlexbase.nim
@@ -15,9 +15,6 @@
import
llstream, strutils
-when defined(nimPreviewSlimSystem):
- import std/assertions
-
const
Lrz* = ' '
Apo* = '\''
@@ -43,14 +40,12 @@ type
TBaseLexer* = object of RootObj
bufpos*: int
buf*: cstring
- bufStorage: string
- bufLen: int
+ bufLen*: int # length of buffer in characters
stream*: PLLStream # we read from this stream
lineNumber*: int # the current line number
# private data:
sentinel*: int
lineStart*: int # index of last line start in buffer
- offsetBase*: int # use ``offsetBase + bufpos`` to get the offset
proc openBaseLexer*(L: var TBaseLexer, inputstream: PLLStream,
@@ -64,12 +59,16 @@ proc handleCR*(L: var TBaseLexer, pos: int): int
# position to continue the scanning from. `pos` must be the position
# of the CR.
proc handleLF*(L: var TBaseLexer, pos: int): int
- # Call this if you scanned over LF in the buffer; it returns the
+ # Call this if you scanned over LF in the buffer; it returns the the
# position to continue the scanning from. `pos` must be the position
# of the LF.
# implementation
+const
+ chrSize = sizeof(char)
+
proc closeBaseLexer(L: var TBaseLexer) =
+ dealloc(L.buf)
llStreamClose(L.stream)
proc fillBuffer(L: var TBaseLexer) =
@@ -84,9 +83,10 @@ proc fillBuffer(L: var TBaseLexer) =
toCopy = L.bufLen - L.sentinel - 1
assert(toCopy >= 0)
if toCopy > 0:
- moveMem(addr L.buf[0], addr L.buf[L.sentinel + 1], toCopy)
+ moveMem(L.buf, addr(L.buf[L.sentinel + 1]), toCopy * chrSize)
# "moveMem" handles overlapping regions
- charsRead = llStreamRead(L.stream, addr L.buf[toCopy], L.sentinel + 1)
+ charsRead = llStreamRead(L.stream, addr(L.buf[toCopy]),
+ (L.sentinel + 1) * chrSize) div chrSize
s = toCopy + charsRead
if charsRead < L.sentinel + 1:
L.buf[s] = EndOfFile # set end marker
@@ -106,11 +106,10 @@ proc fillBuffer(L: var TBaseLexer) =
# double the buffer's size and try again:
oldBufLen = L.bufLen
L.bufLen = L.bufLen * 2
- L.bufStorage.setLen(L.bufLen)
- L.buf = L.bufStorage.cstring
+ L.buf = cast[cstring](realloc(L.buf, L.bufLen * chrSize))
assert(L.bufLen - oldBufLen == oldBufLen)
charsRead = llStreamRead(L.stream, addr(L.buf[oldBufLen]),
- oldBufLen)
+ oldBufLen * chrSize) div chrSize
if charsRead < oldBufLen:
L.buf[oldBufLen + charsRead] = EndOfFile
L.sentinel = oldBufLen + charsRead
@@ -123,8 +122,7 @@ proc fillBaseLexer(L: var TBaseLexer, pos: int): int =
result = pos + 1 # nothing to do
else:
fillBuffer(L)
- L.offsetBase += pos + 1
- L.bufpos = 0
+ L.bufpos = 0 # XXX: is this really correct?
result = 0
L.lineStart = result
@@ -148,10 +146,8 @@ proc skipUTF8BOM(L: var TBaseLexer) =
proc openBaseLexer(L: var TBaseLexer, inputstream: PLLStream, bufLen = 8192) =
assert(bufLen > 0)
L.bufpos = 0
- L.offsetBase = 0
- L.bufStorage = newString(bufLen)
- L.buf = L.bufStorage.cstring
L.bufLen = bufLen
+ L.buf = cast[cstring](alloc(bufLen * chrSize))
L.sentinel = bufLen - 1
L.lineStart = 0
L.lineNumber = 1 # lines start at 1
@@ -165,9 +161,9 @@ proc getColNumber(L: TBaseLexer, pos: int): int =
proc getCurrentLine(L: TBaseLexer, marker: bool = true): string =
result = ""
var i = L.lineStart
- while L.buf[i] notin {CR, LF, EndOfFile}:
- result.add L.buf[i]
- inc i
- result.add "\n"
+ while not (L.buf[i] in {CR, LF, EndOfFile}):
+ add(result, L.buf[i])
+ inc(i)
+ result.add("\n")
if marker:
- result.add spaces(getColNumber(L, L.bufpos)) & '^' & "\n"
+ result.add(spaces(getColNumber(L, L.bufpos)) & '^' & "\n")
diff --git a/compiler/nimpaths.nim b/compiler/nimpaths.nim
deleted file mode 100644
index 3756f956b..000000000
--- a/compiler/nimpaths.nim
+++ /dev/null
@@ -1,54 +0,0 @@
-##[
-Represents absolute paths, but using a symbolic variables (eg $nimr) which can be
-resolved at runtime; this avoids hardcoding at compile time absolute paths so
-that the project root can be relocated.
-
-xxx factor pending https://github.com/timotheecour/Nim/issues/616, see also
-$nim/testament/lib/stdtest/specialpaths.nim
-specialpaths is simpler because it doesn't need variables to be relocatable at
-runtime (eg for use in testament)
-
-interpolation variables:
-: $nimr: such that `$nimr/lib/system.nim` exists (avoids confusion with $nim binary)
- in compiler, it's obtainable via getPrefixDir(); for other tools (eg koch),
- this could be getCurrentDir() or getAppFilename().parentDir.parentDir,
- depending on use case
-
-Unstable API
-]##
-
-import os, strutils
-
-when defined(nimPreviewSlimSystem):
- import std/assertions
-
-
-const
- docCss* = "$nimr/doc/nimdoc.css"
- docCls* = "$nimr/doc/nimdoc.cls"
- docHackNim* = "$nimr/tools/dochack/dochack.nim"
- docHackJs* = docHackNim.changeFileExt("js")
- docHackJsFname* = docHackJs.lastPathPart
- theindexFname* = "theindex.html"
- nimdocOutCss* = "nimdoc.out.css"
- nimdocOutCls* = "nimdoc.cls"
- # `out` to make it easier to use with gitignore in user's repos
- htmldocsDirname* = "htmldocs"
- dotdotMangle* = "_._" ## refs #13223
- # if this changes, make sure it's consistent with `esc` and `escapeLink`
- # lots of other obvious options won't work, see #14454; `_` could work too
-
-proc interp*(path: string, nimr: string): string =
- result = path % ["nimr", nimr]
- doAssert '$' notin result, $(path, nimr, result) # avoids un-interpolated variables in output
-
-proc getDocHacksJs*(nimr: string, nim = getCurrentCompilerExe(), forceRebuild = false): string =
- ## return absolute path to dochack.js, rebuilding if it doesn't exist or if
- ## `forceRebuild`.
- let docHackJs2 = docHackJs.interp(nimr = nimr)
- if forceRebuild or not docHackJs2.fileExists:
- let cmd = "$nim js -d:release $file" % ["nim", nim.quoteShell, "file", docHackNim.interp(nimr = nimr).quoteShell]
- echo "getDocHacksJs: cmd: " & cmd
- doAssert execShellCmd(cmd) == 0, $(cmd)
- doAssert docHackJs2.fileExists
- result = docHackJs2
diff --git a/compiler/nimsets.nim b/compiler/nimsets.nim
index 49c80065a..f15ad6368 100644
--- a/compiler/nimsets.nim
+++ b/compiler/nimsets.nim
@@ -10,146 +10,167 @@
# this unit handles Nim sets; it implements symbolic sets
import
- ast, astalgo, lineinfos, bitsets, types, options
+ ast, astalgo, trees, nversion, msgs, platform, bitsets, types, renderer
-when defined(nimPreviewSlimSystem):
- import std/assertions
+proc toBitSet*(s: PNode, b: var TBitSet)
+ # this function is used for case statement checking:
+proc overlap*(a, b: PNode): bool
+proc inSet*(s: PNode, elem: PNode): bool
+proc someInSet*(s: PNode, a, b: PNode): bool
+proc emptyRange*(a, b: PNode): bool
+proc setHasRange*(s: PNode): bool
+ # returns true if set contains a range (needed by the code generator)
+ # these are used for constant folding:
+proc unionSets*(a, b: PNode): PNode
+proc diffSets*(a, b: PNode): PNode
+proc intersectSets*(a, b: PNode): PNode
+proc symdiffSets*(a, b: PNode): PNode
+proc containsSets*(a, b: PNode): bool
+proc equalSets*(a, b: PNode): bool
+proc cardSet*(s: PNode): BiggestInt
+# implementation
-proc inSet*(s: PNode, elem: PNode): bool =
- assert s.kind == nkCurly
+proc inSet(s: PNode, elem: PNode): bool =
if s.kind != nkCurly:
- #internalError(s.info, "inSet")
+ internalError(s.info, "inSet")
return false
- for i in 0..= s.len * ElemSize) or not bitSetIn(s, b): break
+ if (b >= len(s) * ElemSize) or not bitSetIn(s, b): break
dec(b)
- let aa = newIntTypeNode(a + first, elemType)
+ let aa = newIntTypeNode(nkIntLit, a + first, elemType)
aa.info = info
if a == b:
- result.add aa
+ addSon(result, aa)
else:
n = newNodeI(nkRange, info)
n.typ = elemType
- n.add aa
- let bb = newIntTypeNode(b + first, elemType)
+ addSon(n, aa)
+ let bb = newIntTypeNode(nkIntLit, b + first, elemType)
bb.info = info
- n.add bb
- result.add n
+ addSon(n, bb)
+ addSon(result, n)
e = b
inc(e)
-template nodeSetOp(a, b: PNode, op: untyped) {.dirty.} =
- var x = toBitSet(conf, a)
- let y = toBitSet(conf, b)
+template nodeSetOp(a, b: PNode, op: expr) {.dirty.} =
+ var x, y: TBitSet
+ toBitSet(a, x)
+ toBitSet(b, y)
op(x, y)
- result = toTreeSet(conf, x, a.typ, a.info)
+ result = toTreeSet(x, a.typ, a.info)
-proc unionSets*(conf: ConfigRef; a, b: PNode): PNode = nodeSetOp(a, b, bitSetUnion)
-proc diffSets*(conf: ConfigRef; a, b: PNode): PNode = nodeSetOp(a, b, bitSetDiff)
-proc intersectSets*(conf: ConfigRef; a, b: PNode): PNode = nodeSetOp(a, b, bitSetIntersect)
-proc symdiffSets*(conf: ConfigRef; a, b: PNode): PNode = nodeSetOp(a, b, bitSetSymDiff)
+proc unionSets(a, b: PNode): PNode = nodeSetOp(a, b, bitSetUnion)
+proc diffSets(a, b: PNode): PNode = nodeSetOp(a, b, bitSetDiff)
+proc intersectSets(a, b: PNode): PNode = nodeSetOp(a, b, bitSetIntersect)
+proc symdiffSets(a, b: PNode): PNode = nodeSetOp(a, b, bitSetSymDiff)
-proc containsSets*(conf: ConfigRef; a, b: PNode): bool =
- let x = toBitSet(conf, a)
- let y = toBitSet(conf, b)
+proc containsSets(a, b: PNode): bool =
+ var x, y: TBitSet
+ toBitSet(a, x)
+ toBitSet(b, y)
result = bitSetContains(x, y)
-proc equalSets*(conf: ConfigRef; a, b: PNode): bool =
- let x = toBitSet(conf, a)
- let y = toBitSet(conf, b)
+proc equalSets(a, b: PNode): bool =
+ var x, y: TBitSet
+ toBitSet(a, x)
+ toBitSet(b, y)
result = bitSetEquals(x, y)
-proc complement*(conf: ConfigRef; a: PNode): PNode =
- var x = toBitSet(conf, a)
- for i in 0..high(x): x[i] = not x[i]
- result = toTreeSet(conf, x, a.typ, a.info)
+proc complement*(a: PNode): PNode =
+ var x: TBitSet
+ toBitSet(a, x)
+ for i in countup(0, high(x)): x[i] = not x[i]
+ result = toTreeSet(x, a.typ, a.info)
-proc deduplicate*(conf: ConfigRef; a: PNode): PNode =
- let x = toBitSet(conf, a)
- result = toTreeSet(conf, x, a.typ, a.info)
+proc cardSet(s: PNode): BiggestInt =
+ # here we can do better than converting it into a compact set
+ # we just count the elements directly
+ result = 0
+ for i in countup(0, sonsLen(s) - 1):
+ if s.sons[i].kind == nkRange:
+ result = result + getOrdValue(s.sons[i].sons[1]) -
+ getOrdValue(s.sons[i].sons[0]) + 1
+ else:
+ inc(result)
-proc cardSet*(conf: ConfigRef; a: PNode): BiggestInt =
- let x = toBitSet(conf, a)
- result = bitSetCard(x)
-
-proc setHasRange*(s: PNode): bool =
- assert s.kind == nkCurly
+proc setHasRange(s: PNode): bool =
if s.kind != nkCurly:
+ internalError(s.info, "SetHasRange")
return false
- for i in 0.. b iff not (a <= b)
+
diff --git a/compiler/nimsuggest/nimsuggest.nim b/compiler/nimsuggest/nimsuggest.nim
new file mode 100644
index 000000000..2be368d68
--- /dev/null
+++ b/compiler/nimsuggest/nimsuggest.nim
@@ -0,0 +1,12 @@
+#
+#
+# The Nim Compiler
+# (c) Copyright 2015 Andreas Rumpf
+#
+# See the file "copying.txt", included in this
+# distribution, for details about the copyright.
+#
+
+## Nimsuggest has been moved to https://github.com/nim-lang/nimsuggest
+
+{.error: "This project has moved to the following repo: https://github.com/nim-lang/nimsuggest".}
diff --git a/compiler/nodejs.nim b/compiler/nodejs.nim
index c1feb196a..7f9f28aaf 100644
--- a/compiler/nodejs.nim
+++ b/compiler/nodejs.nim
@@ -1,10 +1,8 @@
import os
-proc findNodeJs*(): string {.inline.} =
- ## Find NodeJS executable and return it as a string.
+proc findNodeJs*(): string =
result = findExe("nodejs")
- if result.len == 0:
+ if result == "":
result = findExe("node")
- if result.len == 0:
- echo "Please install NodeJS first, see https://nodejs.org/en/download"
- raise newException(IOError, "NodeJS not found in PATH")
+ if result == "":
+ result = findExe("iojs")
diff --git a/compiler/nversion.nim b/compiler/nversion.nim
index 2da39b138..adeb0fb6d 100644
--- a/compiler/nversion.nim
+++ b/compiler/nversion.nim
@@ -13,8 +13,5 @@
const
MaxSetElements* = 1 shl 16 # (2^16) to support unicode character sets?
VersionAsString* = system.NimVersion
- RodFileVersion* = "1223" # modify this if the rod-format changes!
+ RodFileVersion* = "1215" # modify this if the rod-format changes!
- NimCompilerApiVersion* = 3 ## Check for the existence of this before accessing it
- ## as older versions of the compiler API do not
- ## declare this.
diff --git a/compiler/optimizer.nim b/compiler/optimizer.nim
deleted file mode 100644
index 0b26e8d34..000000000
--- a/compiler/optimizer.nim
+++ /dev/null
@@ -1,290 +0,0 @@
-#
-#
-# The Nim Compiler
-# (c) Copyright 2020 Andreas Rumpf
-#
-# See the file "copying.txt", included in this
-# distribution, for details about the copyright.
-#
-
-## Optimizer:
-## - elide 'wasMoved(x); destroy(x)' pairs
-## - recognize "all paths lead to 'wasMoved(x)'"
-
-import
- ast, renderer, idents, intsets
-
-from trees import exprStructuralEquivalent
-
-import std/strutils
-
-const
- nfMarkForDeletion = nfNone # faster than a lookup table
-
-type
- BasicBlock = object
- wasMovedLocs: seq[PNode]
- kind: TNodeKind
- hasReturn, hasBreak: bool
- label: PSym # can be nil
- parent: ptr BasicBlock
-
- Con = object
- somethingTodo: bool
- inFinally: int
-
-proc nestedBlock(parent: var BasicBlock; kind: TNodeKind): BasicBlock =
- BasicBlock(wasMovedLocs: @[], kind: kind, hasReturn: false, hasBreak: false,
- label: nil, parent: addr(parent))
-
-proc breakStmt(b: var BasicBlock; n: PNode) =
- var it = addr(b)
- while it != nil:
- it.wasMovedLocs.setLen 0
- it.hasBreak = true
-
- if n.kind == nkSym:
- if it.label == n.sym: break
- else:
- # unnamed break leaves the block is nkWhileStmt or the like:
- if it.kind in {nkWhileStmt, nkBlockStmt, nkBlockExpr}: break
-
- it = it.parent
-
-proc returnStmt(b: var BasicBlock) =
- b.hasReturn = true
- var it = addr(b)
- while it != nil:
- it.wasMovedLocs.setLen 0
- it = it.parent
-
-proc mergeBasicBlockInfo(parent: var BasicBlock; this: BasicBlock) {.inline.} =
- if this.hasReturn:
- parent.wasMovedLocs.setLen 0
- parent.hasReturn = true
-
-proc wasMovedTarget(matches: var IntSet; branch: seq[PNode]; moveTarget: PNode): bool =
- result = false
- for i in 0.. 0 and (b.hasReturn or b.hasBreak):
- discard "cannot optimize away the destructor"
- else:
- c.wasMovedDestroyPair b, n
- special = true
- elif name == "=sink":
- reverse = true
-
- if not special:
- if not reverse:
- for i in 0 ..< n.len:
- analyse(c, b, n[i])
- else:
- #[ Test destructor/tmatrix.test3:
- Prevent this from being elided. We should probably
- find a better solution...
-
- `=sink`(b, -
- let blitTmp = b;
- wasMoved(b);
- blitTmp + a)
- `=destroy`(b)
-
- ]#
- for i in countdown(n.len-1, 0):
- analyse(c, b, n[i])
- if canRaise(n[0]): returnStmt(b)
-
- of nkSym:
- # any usage of the location before destruction implies we
- # cannot elide the 'wasMoved(x)':
- b.invalidateWasMoved n
-
- 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:
- discard "do not follow the construct"
-
- of nkAsgn, nkFastAsgn, nkSinkAsgn:
- # reverse order, see remark for `=sink`:
- analyse(c, b, n[1])
- analyse(c, b, n[0])
-
- of nkIfStmt, nkIfExpr:
- let isExhaustive = n[^1].kind in {nkElse, nkElseExpr}
- var wasMovedSet: seq[PNode] = @[]
-
- for i in 0 ..< n.len:
- var branch = nestedBlock(b, n[i].kind)
-
- analyse(c, branch, n[i])
- mergeBasicBlockInfo(b, branch)
- if isExhaustive:
- if i == 0:
- wasMovedSet = move(branch.wasMovedLocs)
- else:
- wasMovedSet.intersect(branch.wasMovedLocs)
- for i in 0..= gcRefc
+
+template compilationCachePresent*: expr =
+ {optCaasEnabled, optSymbolFiles} * gGlobalOptions != {}
+
+template optPreserveOrigSource*: expr =
+ optEmbedOrigSrc in gGlobalOptions
+
+const
+ genSubDir* = "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
+ DefaultConfig* = "nim.cfg"
+ DocConfig* = "nimdoc.cfg"
+ DocTexConfig* = "nimdoc.tex.cfg"
-type
- TBackend* = enum
- backendInvalid = "" # for parseEnum
- backendC = "c"
- backendCpp = "cpp"
- backendJs = "js"
- backendObjc = "objc"
- # backendNimscript = "nimscript" # this could actually work
- # backendLlvm = "llvm" # probably not well supported; was cmdCompileToLLVM
+# additional configuration variables:
+var
+ gConfigVars* = newStringTable(modeStyleInsensitive)
+ gDllOverrides = newStringTable(modeCaseInsensitive)
+ gPrefixDir* = "" # Overrides the default prefix dir in getPrefixDir proc.
+ libpath* = ""
+ gProjectName* = "" # holds a name like 'nim'
+ gProjectPath* = "" # holds a path like /home/alice/projects/nim/compiler/
+ gProjectFull* = "" # projectPath/projectName
+ gProjectIsStdin* = false # whether we're compiling from stdin
+ gProjectMainIdx*: int32 # the canonical path id of the main module
+ nimcacheDir* = ""
+ command* = "" # the main command (e.g. cc, check, scan, etc)
+ commandArgs*: seq[string] = @[] # any arguments after the main command
+ gKeepComments*: bool = true # whether the parser needs to keep comments
+ implicitImports*: seq[string] = @[] # modules that are to be implicitly imported
+ implicitIncludes*: seq[string] = @[] # modules that are to be implicitly included
- Command* = enum ## Nim's commands
- cmdNone # not yet processed command
- cmdUnknown # command unmapped
- cmdCompileToC, cmdCompileToCpp, cmdCompileToOC, cmdCompileToJS
- cmdCrun # compile and run in nimache
- 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)
- cmdIdeTools # ide tools (e.g. nimsuggest)
- cmdNimscript # evaluate nimscript
- cmdDoc0
- cmdDoc # convert .nim doc comments to HTML
- cmdDoc2tex # convert .nim doc comments to LaTeX
- cmdRst2html # convert a reStructuredText file to HTML
- cmdRst2tex # convert a reStructuredText file to TeX
- cmdMd2html # convert a Markdown file to HTML
- cmdMd2tex # convert a Markdown file to TeX
- cmdJsondoc0
- cmdJsondoc
- cmdCtags
- cmdBuildindex
- cmdGendepend
- cmdDump
- cmdInteractive # start interactive session
- cmdNop
- cmdJsonscript # compile a .json build file
- cmdNimfix
- # old unused: cmdInterpret, cmdDef: def feature (find definition for IDEs)
+const oKeepVariableNames* = true
-const
- cmdBackends* = {cmdCompileToC, cmdCompileToCpp, cmdCompileToOC, cmdCompileToJS, cmdCrun}
- cmdDocLike* = {cmdDoc0, cmdDoc, cmdDoc2tex, cmdJsondoc0, cmdJsondoc,
- cmdCtags, cmdBuildindex}
+proc mainCommandArg*: string =
+ ## This is intended for commands like check or parse
+ ## which will work on the main project file unless
+ ## explicitly given a specific file argument
+ if commandArgs.len > 0:
+ result = commandArgs[0]
+ else:
+ result = gProjectName
-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"
- # gcRefc and the GCs that follow it use a write barrier,
- # as far as usesWriteBarrier() is concerned
+proc existsConfigVar*(key: string): bool =
+ result = hasKey(gConfigVars, key)
- IdeCmd* = enum
- ideNone, ideSug, ideCon, ideDef, ideUse, ideDus, ideChk, ideChkFile, ideMod,
- ideHighlight, ideOutline, ideKnown, ideMsg, ideProject, ideGlobalSymbols,
- ideRecompile, ideChanged, ideType, ideDeclaration, ideExpand
+proc getConfigVar*(key: string): string =
+ result = gConfigVars.getOrDefault key
- Feature* = enum ## experimental features; DO NOT RENAME THESE!
- dotOperators,
- callOperator,
- parallel,
- destructor,
- notnil,
- dynamicBindSym,
- forLoopMacros, # not experimental anymore; remains here for backwards compatibility
- caseStmtMacros, # ditto
- codeReordering,
- compiletimeFFI,
- ## This requires building nim with `-d:nimHasLibFFI`
- ## which itself requires `nimble install libffi`, see #10150
- ## Note: this feature can't be localized with {.push.}
- vmopsDanger,
- strictFuncs,
- views,
- strictNotNil,
- overloadableEnums, # deadcode
- strictEffects,
- unicodeOperators, # deadcode
- flexibleOptionalParams,
- strictDefs,
- strictCaseObjects
+proc setConfigVar*(key, val: string) =
+ gConfigVars[key] = val
- LegacyFeature* = enum
- allowSemcheckedAstModification,
- ## Allows to modify a NimNode where the type has already been
- ## flagged with nfSem. If you actually do this, it will cause
- ## bugs.
- checkUnsignedConversions
- ## 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
+proc getOutFile*(filename, ext: string): string =
+ if options.outFile != "": result = options.outFile
+ else: result = changeFileExt(filename, ext)
- SymbolFilesOption* = enum
- disabledSf, writeOnlySf, readOnlySf, v2Sf, stressTest
+proc getPrefixDir*(): string =
+ ## Gets the prefix dir, usually the parent directory where the binary resides.
+ ##
+ ## This is overrided by some tools (namely nimsuggest) via the ``gPrefixDir``
+ ## global.
+ if gPrefixDir != "": result = gPrefixDir
+ else:
+ result = splitPath(getAppDir()).head
- TSystemCC* = enum
- ccNone, ccGcc, ccNintendoSwitch, ccLLVM_Gcc, ccCLang, ccBcc, ccVcc,
- ccTcc, ccEnv, ccIcl, ccIcc, ccClangCl
+proc setDefaultLibpath*() =
+ # set default value (can be overwritten):
+ if libpath == "":
+ # choose default libpath:
+ var prefix = getPrefixDir()
+ when defined(posix):
+ if prefix == "/usr": libpath = "/usr/lib/nim"
+ elif prefix == "/usr/local": libpath = "/usr/local/lib/nim"
+ else: libpath = joinPath(prefix, "lib")
+ else: libpath = joinPath(prefix, "lib")
- ExceptionSystem* = enum
- excNone, # no exception system selected yet
- excSetjmp, # setjmp based exception handling
- excCpp, # use C++'s native exception handling
- excGoto, # exception handling based on goto (should become the new default for C)
- excQuirky # quirky exception handling
+ # Special rule to support other tools (nimble) which import the compiler
+ # modules and make use of them.
+ let realNimPath = # Make sure we expand the symlink
+ if symlinkExists(findExe("nim")): expandSymlink(findExe("nim"))
+ else: findExe("nim")
+ # Find out if $nim/../../lib/system.nim exists.
+ let parentNimLibPath = realNimPath.parentDir().parentDir() / "lib"
+ if not fileExists(libpath / "system.nim") and
+ fileExists(parentNimlibPath / "system.nim"):
+ libpath = parentNimLibPath
- CfileFlag* {.pure.} = enum
- Cached, ## no need to recompile this time
- External ## file was introduced via .compile pragma
+proc canonicalizePath*(path: string): string =
+ when not FileSystemCaseSensitive: result = path.expandFilename.toLower
+ else: result = path.expandFilename
- Cfile* = object
- nimname*: string
- cname*, obj*: AbsoluteFile
- flags*: set[CfileFlag]
- customArgs*: string
- CfileList* = seq[Cfile]
+proc shortenDir*(dir: string): string =
+ ## returns the interesting part of a dir
+ var prefix = getPrefixDir() & DirSep
+ if startsWith(dir, prefix):
+ return substr(dir, len(prefix))
+ prefix = gProjectPath & DirSep
+ if startsWith(dir, prefix):
+ return substr(dir, len(prefix))
+ result = dir
- Suggest* = ref object
- section*: IdeCmd
- qualifiedPath*: seq[string]
- name*: ptr string # not used beyond sorting purposes; name is also
- # part of 'qualifiedPath'
- filePath*: string
- line*: int # Starts at 1
- column*: int # Starts at 0
- doc*: string # Not escaped (yet)
- forth*: string # type
- quality*: range[0..100] # matching quality
- isGlobal*: bool # is a global variable
- contextFits*: bool # type/non-type context matches
- prefix*: PrefixMatch
- symkind*: byte
- scope*, localUsages*, globalUsages*: int # more usages is better
- tokenLen*: int
- version*: int
- endLine*: uint16
- endCol*: int
+proc removeTrailingDirSep*(path: string): string =
+ if (len(path) > 0) and (path[len(path) - 1] == DirSep):
+ result = substr(path, 0, len(path) - 2)
+ else:
+ result = path
- Suggestions* = seq[Suggest]
+proc getNimcacheDir*: string =
+ result = if nimcacheDir.len > 0: nimcacheDir else: gProjectPath.shortenDir /
+ genSubDir
- ProfileInfo* = object
- time*: float
- count*: int
- ProfileData* = ref object
- data*: TableRef[TLineInfo, ProfileInfo]
+proc pathSubs*(p, config: string): string =
+ let home = removeTrailingDirSep(os.getHomeDir())
+ result = unixToNativePath(p % [
+ "nim", getPrefixDir(),
+ "lib", libpath,
+ "home", home,
+ "config", config,
+ "projectname", options.gProjectName,
+ "projectpath", options.gProjectPath,
+ "projectdir", options.gProjectPath,
+ "nimcache", getNimcacheDir()])
+ if '~' in result:
+ result = result.replace("~", home)
- StdOrrKind* = enum
- 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
- ## fields marked with '*' are subject to
- ## the incremental compilation mechanisms
- ## (+) means "part of the dependency"
- backend*: TBackend # set via `nim x` or `nim --backend:x`
- target*: Target # (+)
- linesCompiled*: int # all lines that have been compiled
- options*: TOptions # (+)
- globalOptions*: TGlobalOptions # (+)
- macrosToExpand*: StringTableRef
- arcToExpand*: StringTableRef
- m*: MsgConfig
- filenameOption*: FilenameOption # how to render paths in compiler messages
- unitSep*: string
- evalTemplateCounter*: int
- evalMacroCounter*: int
- exitcode*: int8
- cmd*: Command # raw command parsed as enum
- cmdInput*: string # input command
- projectIsCmd*: bool # whether we're compiling from a command input
- 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
- features*: set[Feature]
- legacyFeatures*: set[LegacyFeature]
- arguments*: string ## the arguments to be passed to the program that
- ## should be run
- ideCmd*: IdeCmd
- oldNewlines*: bool
- cCompiler*: TSystemCC # the used compiler
- modifiedyNotes*: TNoteKinds # notes that have been set/unset from either cmdline/configs
- cmdlineNotes*: TNoteKinds # notes that have been set/unset from cmdline
- foreignPackageNotes*: TNoteKinds
- notes*: TNoteKinds # notes after resolving all logic(defaults, verbosity)/cmdline/configs
- warningAsErrors*: TNoteKinds
- mainPackageNotes*: TNoteKinds
- mainPackageId*: int
- errorCounter*: int
- hintCounter*: int
- 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
- ## insensitive. Otherwise hell would break lose.
- packageCache*: StringTableRef
- nimblePaths*: seq[AbsoluteDir]
- searchPaths*: seq[AbsoluteDir]
- lazyPaths*: seq[AbsoluteDir]
- outFile*: RelativeFile
- 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/
- projectFull*: AbsoluteFile # projectPath/projectName
- projectIsStdin*: bool # whether we're compiling from stdin
- lastMsgWasDot*: set[StdOrrKind] # the last compiler message was a single '.'
- projectMainIdx*: FileIndex # the canonical path id of the main module
- projectMainIdx2*: FileIndex # consider merging with projectMainIdx
- command*: string # the main command (e.g. cc, check, scan, etc)
- commandArgs*: seq[string] # any arguments after the main command
- commandLine*: string
- extraCmds*: seq[string] # for writeJsonBuildInstructions
- keepComments*: bool # whether the parser needs to keep comments
- implicitImports*: seq[string] # modules that are to be implicitly imported
- implicitIncludes*: seq[string] # modules that are to be implicitly included
- docSeeSrcUrl*: string # if empty, no seeSrc will be generated. \
- # The string uses the formatting variables `path` and `line`.
- docRoot*: string ## see nim --fullhelp for --docRoot
- docCmd*: string ## see nim --fullhelp for --docCmd
-
- configFiles*: seq[AbsoluteFile] # config files (cfg,nims)
- cIncludes*: seq[AbsoluteDir] # directories to search for included files
- cLibs*: seq[AbsoluteDir] # directories to search for lib files
- cLinkedLibs*: seq[string] # libraries to link
-
- externalToLink*: seq[string] # files to link in addition to the file
- # we compiled (*)
- linkOptionsCmd*: string
- compileOptionsCmd*: seq[string]
- linkOptions*: string # (*)
- compileOptions*: string # (*)
- cCompilerPath*: string
- toCompile*: CfileList # (*)
- suggestionResultHook*: proc (result: Suggest) {.closure.}
- suggestVersion*: int
- suggestMaxResults*: int
- lastLineInfo*: TLineInfo
- writelnHook*: proc (output: string) {.closure, gcsafe.}
- 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
-
-template setErrorMaxHighMaybe*(conf: ConfigRef) =
- ## do not stop after first error (but honor --errorMax if provided)
- assignIfDefault(conf.errorMax, high(int))
-
-proc setNoteDefaults*(conf: ConfigRef, note: TNoteKind, enabled = true) =
- template fun(op) =
- conf.notes.op note
- conf.mainPackageNotes.op note
- conf.foreignPackageNotes.op note
- if enabled: fun(incl) else: fun(excl)
-
-proc setNote*(conf: ConfigRef, note: TNoteKind, enabled = true) =
- # see also `prepareConfigNotes` which sets notes
- if note notin conf.cmdlineNotes:
- if enabled: incl(conf.notes, note) else: excl(conf.notes, note)
-
-proc hasHint*(conf: ConfigRef, note: TNoteKind): bool =
- # ternary states instead of binary states would simplify logic
- if optHints notin conf.options: false
- elif note in {hintConf, hintProcessing}:
- # could add here other special notes like hintSource
- # these notes apply globally.
- note in conf.mainPackageNotes
- else: note in conf.notes
-
-proc hasWarn*(conf: ConfigRef, note: TNoteKind): bool {.inline.} =
- 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)
-
-const oldExperimentalFeatures* = {dotOperators, callOperator, parallel}
-
-const
- ChecksOptions* = {optObjCheck, optFieldCheck, optRangeCheck,
- optOverflowCheck, optBoundsCheck, optAssert, optNaNCheck, optInfCheck,
- optStyleCheck}
-
- DefaultOptions* = {optObjCheck, optFieldCheck, optRangeCheck,
- optBoundsCheck, optOverflowCheck, optAssert, optWarns, optRefCheck,
- optHints, optStackTrace, optLineTrace, # consider adding `optStackTraceMsgs`
- optTrMacros, optStyleCheck, optCursorInference}
- DefaultGlobalOptions* = {optThreadAnalysis, optExcessiveStackTrace}
-
-proc getSrcTimestamp(): DateTime =
- try:
- result = utc(fromUnix(parseInt(getEnv("SOURCE_DATE_EPOCH",
- "not a number"))))
- except ValueError:
- # Environment variable malformed.
- # https://reproducible-builds.org/specs/source-date-epoch/: "If the
- # value is malformed, the build process SHOULD exit with a non-zero
- # error code", which this doesn't do. This uses local time, because
- # that maintains compatibility with existing usage.
- result = utc getTime()
-
-proc getDateStr*(): string =
- result = format(getSrcTimestamp(), "yyyy-MM-dd")
-
-proc getClockStr*(): string =
- result = format(getSrcTimestamp(), "HH:mm:ss")
-
-template newPackageCache*(): untyped =
+template newPackageCache(): expr =
newStringTable(when FileSystemCaseSensitive:
modeCaseInsensitive
else:
modeCaseSensitive)
-proc newProfileData(): ProfileData =
- ProfileData(data: newTable[TLineInfo, ProfileInfo]())
+var packageCache = newPackageCache()
-const foreignPackageNotesDefault* = {
- hintProcessing, warnUnknownMagic, hintQuitCalled, hintExecuting, hintUser, warnUser}
+proc resetPackageCache*() = packageCache = newPackageCache()
-proc isDefined*(conf: ConfigRef; symbol: string): bool
+iterator myParentDirs(p: string): string =
+ # XXX os's parentDirs is stupid (multiple yields) and triggers an old bug...
+ var current = p
+ while true:
+ current = current.parentDir
+ if current.len == 0: break
+ yield current
-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 getPackageName*(path: string): string =
+ var parents = 0
+ block packageSearch:
+ for d in myParentDirs(path):
+ if packageCache.hasKey(d):
+ #echo "from cache ", d, " |", packageCache[d], "|", path.splitFile.name
+ return packageCache[d]
+ inc parents
+ for file in walkFiles(d / "*.nimble"):
+ result = file.splitFile.name
+ break packageSearch
+ for file in walkFiles(d / "*.babel"):
+ result = file.splitFile.name
+ break packageSearch
+ # we also store if we didn't find anything:
+ if result.isNil: result = ""
+ for d in myParentDirs(path):
+ #echo "set cache ", d, " |", result, "|", parents
+ packageCache[d] = result
+ dec parents
+ if parents <= 0: break
-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]
-
-proc newConfigRef*(): ConfigRef =
- result = ConfigRef(
- cCompiler: ccGcc,
- macrosToExpand: newStringTable(modeStyleInsensitive),
- arcToExpand: newStringTable(modeStyleInsensitive),
- m: initMsgConfig(),
- cppDefines: initHashSet[string](),
- headerFile: "", features: {}, legacyFeatures: {},
- configVars: newStringTable(modeStyleInsensitive),
- symbols: newStringTable(modeStyleInsensitive),
- packageCache: newPackageCache(),
- searchPaths: @[],
- lazyPaths: @[],
- outFile: RelativeFile"",
- outDir: AbsoluteDir"",
- prefixDir: AbsoluteDir"",
- libpath: AbsoluteDir"", nimcacheDir: AbsoluteDir"",
- dllOverrides: newStringTable(modeCaseInsensitive),
- moduleOverrides: newStringTable(modeStyleInsensitive),
- cfileSpecificOptions: newStringTable(modeCaseSensitive),
- projectName: "", # holds a name like 'nim'
- projectPath: AbsoluteDir"", # holds a path like /home/alice/projects/nim/compiler/
- projectFull: AbsoluteFile"", # projectPath/projectName
- projectIsStdin: false, # whether we're compiling from stdin
- projectMainIdx: FileIndex(0'i32), # the canonical path id of the main module
- command: "", # the main command (e.g. cc, check, scan, etc)
- commandArgs: @[], # any arguments after the main command
- commandLine: "",
- keepComments: true, # whether the parser needs to keep comments
- implicitImports: @[], # modules that are to be implicitly imported
- implicitIncludes: @[], # modules that are to be implicitly included
- docSeeSrcUrl: "",
- cIncludes: @[], # directories to search for included files
- cLibs: @[], # directories to search for lib files
- cLinkedLibs: @[], # libraries to link
- backend: backendInvalid,
- externalToLink: @[],
- linkOptionsCmd: "",
- compileOptionsCmd: @[],
- linkOptions: "",
- compileOptions: "",
- ccompilerpath: "",
- toCompile: @[],
- arguments: "",
- 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)
-
-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
- elif cmpIgnoreStyle(symbol, CPU[conf.target.targetCPU].name) == 0:
- result = true
- elif cmpIgnoreStyle(symbol, platform.OS[conf.target.targetOS].name) == 0:
- result = true
- else:
- case symbol.normalize
- of "x86": result = conf.target.targetCPU == cpuI386
- of "itanium": result = conf.target.targetCPU == cpuIa64
- of "x8664": result = conf.target.targetCPU == cpuAmd64
- of "posix", "unix":
- result = conf.target.targetOS in {osLinux, osMorphos, osSkyos, osIrix, osPalmos,
- osQnx, osAtari, osAix,
- osHaiku, osVxWorks, osSolaris, osNetbsd,
- osFreebsd, osOpenbsd, osDragonfly, osMacosx, osIos,
- osAndroid, osNintendoSwitch, osFreeRTOS, osCrossos, osZephyr, osNuttX}
- 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}
- of "emulatedthreadvars":
- result = platform.OS[conf.target.targetOS].props.contains(ospLacksThreadVars)
- of "msdos": result = conf.target.targetOS == osDos
- of "mswindows", "win32": result = conf.target.targetOS == osWindows
- of "macintosh":
- result = conf.target.targetOS in {osMacos, osMacosx, osIos}
- of "osx", "macosx":
- result = conf.target.targetOS in {osMacosx, osIos}
- of "sunos": result = conf.target.targetOS == osSolaris
- of "nintendoswitch":
- result = conf.target.targetOS == osNintendoSwitch
- of "freertos", "lwip":
- 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 "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
- of "cpu64": result = CPU[conf.target.targetCPU].bit == 64
- of "nimrawsetjmp":
- result = conf.target.targetOS in {osSolaris, osNetbsd, osFreebsd, osOpenbsd,
- 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
-
-template compilationCachePresent*(conf: ConfigRef): untyped =
- false
-# conf.symbolFiles in {v2Sf, writeOnlySf}
-
-template optPreserveOrigSource*(conf: ConfigRef): untyped =
- optEmbedOrigSrc in conf.globalOptions
-
-proc mainCommandArg*(conf: ConfigRef): string =
- ## This is intended for commands like check or parse
- ## which will work on the main project file unless
- ## explicitly given a specific file argument
- if conf.commandArgs.len > 0:
- result = conf.commandArgs[0]
- else:
- result = conf.projectName
-
-proc existsConfigVar*(conf: ConfigRef; key: string): bool =
- result = hasKey(conf.configVars, key)
-
-proc getConfigVar*(conf: ConfigRef; key: string, default = ""): string =
- result = conf.configVars.getOrDefault(key, default)
-
-proc setConfigVar*(conf: ConfigRef; key, val: string) =
- conf.configVars[key] = val
-
-proc getOutFile*(conf: ConfigRef; filename: RelativeFile, ext: string): AbsoluteFile =
- # explains regression https://github.com/nim-lang/Nim/issues/6583#issuecomment-625711125
- # Yet another reason why "" should not mean "."; `""/something` should raise
- # instead of implying "" == "." as it's bug prone.
- doAssert conf.outDir.string.len > 0
- result = conf.outDir / changeFileExt(filename, ext)
-
-proc absOutFile*(conf: ConfigRef): AbsoluteFile =
- doAssert not conf.outDir.isEmpty
- doAssert not conf.outFile.isEmpty
- result = conf.outDir / conf.outFile
- when defined(posix):
- if dirExists(result.string): result.string.add ".out"
-
-proc prepareToWriteOutput*(conf: ConfigRef): AbsoluteFile =
- ## Create the output directory and returns a full path to the output file
- result = conf.absOutFile
- createDir result.string.parentDir
-
-proc getPrefixDir*(conf: ConfigRef): AbsoluteDir =
- ## Gets the prefix dir, usually the parent directory where the binary resides.
- ##
- ## This is overridden by some tools (namely nimsuggest) via the ``conf.prefixDir``
- ## field.
- ## This should resolve to root of nim sources, whether running nim from a local
- ## 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
-
-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"
-
- # Special rule to support other tools (nimble) which import the compiler
- # modules and make use of them.
- let realNimPath = findExe("nim")
- # Find out if $nim/../../lib/system.nim exists.
- let parentNimLibPath = realNimPath.parentDir.parentDir / "lib"
- if not fileExists(conf.libpath.string / "system.nim") and
- fileExists(parentNimLibPath / "system.nim"):
- conf.libpath = AbsoluteDir parentNimLibPath
-
-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)
- else:
+proc withPackageName*(path: string): string =
+ let x = path.getPackageName
+ if x.len == 0:
result = path
-
-proc disableNimblePath*(conf: ConfigRef) =
- incl conf.globalOptions, optNoNimblePath
- conf.lazyPaths.setLen(0)
- conf.nimblePaths.setLen(0)
-
-proc clearNimblePath*(conf: ConfigRef) =
- conf.lazyPaths.setLen(0)
- conf.nimblePaths.setLen(0)
-
-include packagehandling
-
-proc getOsCacheDir(): string =
- when defined(posix):
- result = getEnv("XDG_CACHE_HOME", getHomeDir() / ".cache") / "nim"
else:
- result = getHomeDir() / genSubDir.string
+ let (p, file, ext) = path.splitFile
+ result = (p / (x & '_' & file)) & ext
-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
- else:
- AbsoluteDir(getOsCacheDir() / splitFile(conf.projectName).name &
- nimcacheSuffix(conf))
-
-proc pathSubs*(conf: ConfigRef; p, config: string): string =
- let home = removeTrailingDirSep(os.getHomeDir())
- result = unixToNativePath(p % [
- "nim", getPrefixDir(conf).string,
- "lib", conf.libpath.string,
- "home", home,
- "config", config,
- "projectname", conf.projectName,
- "projectpath", conf.projectPath.string,
- "projectdir", conf.projectPath.string,
- "nimcache", getNimcacheDir(conf).string]).expandTilde
-
-iterator nimbleSubs*(conf: ConfigRef; p: string): string =
- let pl = p.toLowerAscii
- if "$nimblepath" in pl or "$nimbledir" in pl:
- for i in countdown(conf.nimblePaths.len-1, 0):
- let nimblePath = removeTrailingDirSep(conf.nimblePaths[i].string)
- yield p % ["nimblepath", nimblePath, "nimbledir", nimblePath]
- else:
- yield p
-
-proc toGeneratedFile*(conf: ConfigRef; path: AbsoluteFile,
- ext: string): AbsoluteFile =
+proc toGeneratedFile*(path, ext: string): string =
## converts "/home/a/mymodule.nim", "rod" to "/home/a/nimcache/mymodule.rod"
- result = getNimcacheDir(conf) / RelativeFile path.string.splitPath.tail.changeFileExt(ext)
+ var (head, tail) = splitPath(path)
+ #if len(head) > 0: head = shortenDir(head & dirSep)
+ result = joinPath([getNimcacheDir(), changeFileExt(tail, ext)])
+ #echo "toGeneratedFile(", path, ", ", ext, ") = ", result
-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)
+when noTimeMachine:
+ var alreadyExcludedDirs = initSet[string]()
+ proc excludeDirFromTimeMachine(dir: string) {.raises: [].} =
+ ## Calls a macosx command on the directory to exclude it from backups.
+ ##
+ ## The macosx tmutil command is invoked to mark the specified path as an
+ ## item to be excluded from time machine backups. If a path already exists
+ ## with files before excluding it, newer files won't be added to the
+ ## directory, but previous files won't be removed from the backup until the
+ ## user deletes that directory.
+ ##
+ ## The whole proc is optional and will ignore all kinds of errors. The only
+ ## way to be sure that it works is to call ``tmutil isexcluded path``.
+ if alreadyExcludedDirs.contains(dir): return
+ alreadyExcludedDirs.incl(dir)
+ try:
+ var p = startProcess("/usr/bin/tmutil", args = ["addexclusion", dir])
+ discard p.waitForExit
+ p.close
+ except Exception:
+ discard
+
+proc completeGeneratedFilePath*(f: string, createSubDir: bool = true): string =
+ var (head, tail) = splitPath(f)
+ #if len(head) > 0: head = removeTrailingDirSep(shortenDir(head & dirSep))
+ var subdir = getNimcacheDir() # / head
if createSubDir:
try:
- createDir(subdir.string)
+ createDir(subdir)
+ when noTimeMachine:
+ excludeDirFromTimeMachine(subdir)
except OSError:
- conf.quitOrRaise "cannot create directory: " & subdir.string
- result = subdir / RelativeFile f.string.splitPath.tail
+ writeLine(stdout, "cannot create directory: " & subdir)
+ quit(1)
+ result = joinPath(subdir, tail)
+ #echo "completeGeneratedFilePath(", f, ") = ", result
-proc rawFindFile(conf: ConfigRef; f: RelativeFile; suppressStdlib: bool): AbsoluteFile =
- for it in conf.searchPaths:
- if suppressStdlib and it.string.startsWith(conf.libpath.string):
- continue
- result = it / f
- if fileExists(result):
- return canonicalizePath(conf, result)
- result = AbsoluteFile""
+iterator iterSearchPath*(searchPaths: TLinkedList): string =
+ var it = PStrEntry(searchPaths.head)
+ while it != nil:
+ yield it.data
+ it = PStrEntry(it.next)
-proc rawFindFile2(conf: ConfigRef; f: RelativeFile): AbsoluteFile =
- for i, it in conf.lazyPaths:
- result = it / f
- if fileExists(result):
- # bring to front
- for j in countdown(i, 1):
- swap(conf.lazyPaths[j], conf.lazyPaths[j-1])
+proc rawFindFile(f: string): string =
+ for it in iterSearchPath(searchPaths):
+ result = joinPath(it, f)
+ if existsFile(result):
+ return result.canonicalizePath
+ result = ""
- return canonicalizePath(conf, result)
- result = AbsoluteFile""
+proc rawFindFile2(f: string): string =
+ var it = PStrEntry(lazyPaths.head)
+ while it != nil:
+ result = joinPath(it.data, f)
+ if existsFile(result):
+ bringToFront(lazyPaths, it)
+ return result.canonicalizePath
+ it = PStrEntry(it.next)
+ result = ""
-template patchModule(conf: ConfigRef) {.dirty.} =
- if not result.isEmpty and conf.moduleOverrides.len > 0:
- let key = getPackageName(conf, result.string) & "_" & splitFile(result).name
- if conf.moduleOverrides.hasKey(key):
- let ov = conf.moduleOverrides[key]
- if ov.len > 0: result = AbsoluteFile(ov)
-
-when (NimMajor, NimMinor) < (1, 1) or not declared(isRelativeTo):
- proc isRelativeTo(path, base: string): bool =
- # pending #13212 use os.isRelativeTo
- let path = path.normalizedPath
- let base = base.normalizedPath
- 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 =
- 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
- if f.isRelativeTo(it):
- return relativePath(f, it).RelativeFile
- search(conf.searchPaths)
- search(conf.lazyPaths)
-
-proc findFile*(conf: ConfigRef; f: string; suppressStdlib = false): AbsoluteFile =
+proc findFile*(f: string): string {.procvar.} =
if f.isAbsolute:
- result = if f.fileExists: AbsoluteFile(f) else: AbsoluteFile""
+ result = if f.existsFile: f else: ""
else:
- result = rawFindFile(conf, RelativeFile f, suppressStdlib)
- if result.isEmpty:
- result = rawFindFile(conf, RelativeFile f.toLowerAscii, suppressStdlib)
- if result.isEmpty:
- result = rawFindFile2(conf, RelativeFile f)
- if result.isEmpty:
- result = rawFindFile2(conf, RelativeFile f.toLowerAscii)
- patchModule(conf)
+ result = f.rawFindFile
+ if result.len == 0:
+ result = f.toLower.rawFindFile
+ if result.len == 0:
+ result = f.rawFindFile2
+ if result.len == 0:
+ result = f.toLower.rawFindFile2
-proc findModule*(conf: ConfigRef; modulename, currentModule: string): AbsoluteFile =
+proc findModule*(modulename, currentModule: string): string =
# returns path to module
- var m = addFileExt(modulename, NimExt)
- if m.startsWith(pkgPrefix):
- result = findFile(conf, m.substr(pkgPrefix.len), suppressStdlib = true)
- else:
- if m.startsWith(stdPrefix):
- let stripped = m.substr(stdPrefix.len)
- for candidate in stdlibDirs:
- let path = (conf.libpath.string / candidate / stripped)
- if fileExists(path):
- result = AbsoluteFile path
- break
- else: # If prefixed with std/ why would we add the current module path!
+ when defined(nimfix):
+ # '.nimfix' modules are preferred over '.nim' modules so that specialized
+ # versions can be kept for 'nimfix'.
+ block:
+ let m = addFileExt(modulename, "nimfix")
let currentPath = currentModule.splitFile.dir
- result = AbsoluteFile currentPath / m
- if not fileExists(result):
- result = findFile(conf, m)
- patchModule(conf)
+ result = currentPath / m
+ if not existsFile(result):
+ result = findFile(m)
+ if existsFile(result): return result
+ let m = addFileExt(modulename, NimExt)
+ let currentPath = currentModule.splitFile.dir
+ result = currentPath / m
+ if not existsFile(result):
+ result = findFile(m)
-proc findProjectNimFile*(conf: ConfigRef; pkg: string): string =
- const extensions = [".nims", ".cfg", ".nimcfg", ".nimble"]
- var
- candidates: seq[string] = @[]
- dir = pkg
- prev = dir
- nimblepkg = ""
- let pkgname = pkg.lastPathPart()
- while true:
- for k, f in os.walkDir(dir, relative = true):
- if k == pcFile and f != "config.nims":
- let (_, name, ext) = splitFile(f)
- if ext in extensions:
- let x = changeFileExt(dir / name, ".nim")
- if fileExists(x):
- candidates.add x
- if ext == ".nimble":
- if nimblepkg.len == 0:
- nimblepkg = name
- # Since nimble packages can have their source in a subfolder,
- # check the last folder we were in for a possible match.
- if dir != prev:
- let x = prev / x.extractFilename()
- if fileExists(x):
- candidates.add x
- else:
- # If we found more than one nimble file, chances are that we
- # missed the real project file, or this is an invalid nimble
- # package. Either way, bailing is the better choice.
- return ""
- let pkgname = if nimblepkg.len > 0: nimblepkg else: pkgname
- for c in candidates:
- if pkgname in c.extractFilename(): return c
- if candidates.len > 0:
- return candidates[0]
- prev = dir
- dir = parentDir(dir)
- 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 libCandidates*(s: string, dest: var seq[string]) =
+ var le = strutils.find(s, '(')
+ var ri = strutils.find(s, ')', le+1)
+ if le >= 0 and ri > le:
+ var prefix = substr(s, 0, le - 1)
+ var suffix = substr(s, ri + 1)
+ for middle in split(substr(s, le + 1, ri - 1), '|'):
+ libCandidates(prefix & middle & suffix, dest)
+ else:
+ add(dest, s)
proc canonDynlibName(s: string): string =
let start = if s.startsWith("lib"): 3 else: 0
@@ -1002,18 +423,30 @@ proc canonDynlibName(s: string): string =
else:
result = s.substr(start)
-proc inclDynlibOverride*(conf: ConfigRef; lib: string) =
- conf.dllOverrides[lib.canonDynlibName] = "true"
+proc inclDynlibOverride*(lib: string) =
+ gDllOverrides[lib.canonDynlibName] = "true"
-proc isDynlibOverride*(conf: ConfigRef; lib: string): bool =
- result = optDynlibOverrideAll in conf.globalOptions or
- conf.dllOverrides.hasKey(lib.canonDynlibName)
+proc isDynlibOverride*(lib: string): bool =
+ result = gDllOverrides.hasKey(lib.canonDynlibName)
-proc showNonExportedFields*(conf: ConfigRef) =
- incl(conf.globalOptions, optShowNonExportedFields)
+proc binaryStrSearch*(x: openArray[string], y: string): int =
+ var a = 0
+ var b = len(x) - 1
+ while a <= b:
+ var mid = (a + b) div 2
+ var c = cmpIgnoreCase(x[mid], y)
+ if c < 0:
+ a = mid + 1
+ elif c > 0:
+ b = mid - 1
+ else:
+ return mid
+ result = - 1
-proc expandDone*(conf: ConfigRef): bool =
- result = conf.ideCmd == ideExpand and conf.expandLevels == 0 and conf.expandProgress
+template nimdbg*: expr = c.module.fileIdx == gProjectMainIdx
+template cnimdbg*: expr = p.module.module.fileIdx == gProjectMainIdx
+template pnimdbg*: expr = p.lex.fileIdx == gProjectMainIdx
+template lnimdbg*: expr = L.fileIdx == gProjectMainIdx
proc parseIdeCmd*(s: string): IdeCmd =
case s:
@@ -1023,17 +456,9 @@ 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,27 +469,7 @@ proc `$`*(c: IdeCmd): string =
of ideUse: "use"
of ideDus: "dus"
of ideChk: "chk"
- of ideChkFile: "chkFile"
of ideMod: "mod"
of ideNone: "none"
of ideHighlight: "highlight"
of ideOutline: "outline"
- 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.
- if conf != nil and conf.target.targetCPU == cpuI386:
- #on Linux/BSD i386, double are aligned to 4bytes (except with -malign-double)
- if conf.target.targetOS != osWindows:
- # on i386 for all known POSIX systems, 64bits ints are aligned
- # to 4bytes (except with -malign-double)
- return 4
- return 8
diff --git a/compiler/packagehandling.nim b/compiler/packagehandling.nim
deleted file mode 100644
index 8cf209779..000000000
--- a/compiler/packagehandling.nim
+++ /dev/null
@@ -1,43 +0,0 @@
-#
-#
-# The Nim Compiler
-# (c) Copyright 2017 Andreas Rumpf
-#
-# See the file "copying.txt", included in this
-# distribution, for details about the copyright.
-#
-
-iterator myParentDirs(p: string): string =
- # XXX os's parentDirs is stupid (multiple yields) and triggers an old bug...
- var current = p
- while true:
- current = current.parentDir
- if current.len == 0: break
- yield current
-
-proc getNimbleFile*(conf: ConfigRef; path: string): string =
- ## returns absolute path to nimble file, e.g.: /pathto/cligen.nimble
- var parents = 0
- block packageSearch:
- for d in myParentDirs(path):
- if conf.packageCache.hasKey(d):
- #echo "from cache ", d, " |", packageCache[d], "|", path.splitFile.name
- return conf.packageCache[d]
- inc parents
- for file in walkFiles(d / "*.nimble"):
- result = file
- break packageSearch
- # we also store if we didn't find anything:
- for d in myParentDirs(path):
- #echo "set cache ", d, " |", result, "|", parents
- conf.packageCache[d] = result
- dec parents
- if parents <= 0: break
-
-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
- else:
- return "unknown"
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..f8f1f355c 100644
--- a/compiler/parampatterns.nim
+++ b/compiler/parampatterns.nim
@@ -10,8 +10,7 @@
## This module implements the pattern matching features for term rewriting
## macro support.
-import strutils, ast, types, msgs, idents, renderer, wordrecg, trees,
- options
+import strutils, ast, astalgo, types, msgs, idents, renderer, wordrecg, trees
# we precompile the pattern here for efficiency into some internal
# stack based VM :-) Why? Because it's fun; I did no benchmarks to see if that
@@ -42,11 +41,11 @@ type
const
MaxStackSize* = 64 ## max required stack size by the VM
-proc patternError(n: PNode; conf: ConfigRef) =
- localError(conf, n.info, "illformed AST: " & renderTree(n, {renderNoComments}))
+proc patternError(n: PNode) =
+ localError(n.info, errIllFormedAstX, renderTree(n, {renderNoComments}))
proc add(code: var TPatternCode, op: TOpcode) {.inline.} =
- code.add chr(ord(op))
+ add(code, chr(ord(op)))
proc whichAlias*(p: PSym): TAliasRequest =
if p.constraint != nil:
@@ -54,42 +53,42 @@ proc whichAlias*(p: PSym): TAliasRequest =
else:
result = aqNone
-proc compileConstraints(p: PNode, result: var TPatternCode; conf: ConfigRef) =
+proc compileConstraints(p: PNode, result: var TPatternCode) =
case p.kind
of nkCallKinds:
- if p[0].kind != nkIdent:
- patternError(p[0], conf)
+ if p.sons[0].kind != nkIdent:
+ patternError(p.sons[0])
return
- let op = p[0].ident
+ let op = p.sons[0].ident
if p.len == 3:
if op.s == "|" or op.id == ord(wOr):
- compileConstraints(p[1], result, conf)
- compileConstraints(p[2], result, conf)
+ compileConstraints(p.sons[1], result)
+ compileConstraints(p.sons[2], result)
result.add(ppOr)
elif op.s == "&" or op.id == ord(wAnd):
- compileConstraints(p[1], result, conf)
- compileConstraints(p[2], result, conf)
+ compileConstraints(p.sons[1], result)
+ compileConstraints(p.sons[2], result)
result.add(ppAnd)
else:
- patternError(p, conf)
+ patternError(p)
elif p.len == 2 and (op.s == "~" or op.id == ord(wNot)):
- compileConstraints(p[1], result, conf)
+ compileConstraints(p.sons[1], result)
result.add(ppNot)
else:
- patternError(p, conf)
+ patternError(p)
of nkAccQuoted, nkPar:
if p.len == 1:
- compileConstraints(p[0], result, conf)
+ compileConstraints(p.sons[0], result)
else:
- patternError(p, conf)
+ patternError(p)
of nkIdent:
let spec = p.ident.s.normalize
case spec
- of "atom": result.add(ppAtom)
- of "lit": result.add(ppLit)
- of "sym": result.add(ppSym)
+ of "atom": result.add(ppAtom)
+ of "lit": result.add(ppLit)
+ of "sym": result.add(ppSym)
of "ident": result.add(ppIdent)
- of "call": result.add(ppCall)
+ of "call": result.add(ppCall)
of "alias": result[0] = chr(aqShouldAlias.ord)
of "noalias": result[0] = chr(aqNoAlias.ord)
of "lvalue": result.add(ppLValue)
@@ -98,36 +97,37 @@ proc compileConstraints(p: PNode, result: var TPatternCode; conf: ConfigRef) =
of "nosideeffect": result.add(ppNoSideEffect)
else:
# check all symkinds:
- internalAssert conf, int(high(TSymKind)) < 255
- for i in TSymKind:
- if cmpIgnoreStyle(i.toHumanStr, spec) == 0:
+ internalAssert int(high(TSymKind)) < 255
+ for i in low(TSymKind)..high(TSymKind):
+ if cmpIgnoreStyle(($i).substr(2), spec) == 0:
result.add(ppSymKind)
result.add(chr(i.ord))
return
# check all nodekinds:
- internalAssert conf, int(high(TNodeKind)) < 255
- for i in TNodeKind:
+ internalAssert int(high(TNodeKind)) < 255
+ for i in low(TNodeKind)..high(TNodeKind):
if cmpIgnoreStyle($i, spec) == 0:
result.add(ppNodeKind)
result.add(chr(i.ord))
return
- patternError(p, conf)
+ patternError(p)
else:
- patternError(p, conf)
+ patternError(p)
-proc semNodeKindConstraints*(n: PNode; conf: ConfigRef; start: Natural): PNode =
+proc semNodeKindConstraints*(p: PNode): PNode =
## does semantic checking for a node kind pattern and compiles it into an
## efficient internal format.
- result = newNodeI(nkStrLit, n.info)
+ assert p.kind == nkCurlyExpr
+ result = newNodeI(nkStrLit, p.info)
result.strVal = newStringOfCap(10)
result.strVal.add(chr(aqNone.ord))
- if n.len >= 2:
- for i in start..= 2:
+ for i in 1.. MaxStackSize-1:
- internalError(conf, n.info, "parameter pattern too complex")
+ internalError(p.info, "parameter pattern too complex")
else:
- patternError(n, conf)
+ patternError(p)
result.strVal.add(ppEof)
type
@@ -138,16 +138,13 @@ proc checkForSideEffects*(n: PNode): TSideEffectAnalysis =
case n.kind
of nkCallKinds:
# only calls can produce side effects:
- let op = n[0]
+ let op = n.sons[0]
if op.kind == nkSym and isRoutine(op.sym):
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
@@ -155,8 +152,8 @@ proc checkForSideEffects*(n: PNode): TSideEffectAnalysis =
# indirect call: assume side effect:
return seSideEffect
# we need to check n[0] too: (FwithSideEffectButReturnsProcWithout)(args)
- for i in 0.. 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:
- # See RFC #7373, calls returning 'var T' are assumed to
- # return a view into the first argument (if there is one):
- it = it[1]
- else:
- break
- 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
of nkEmpty:
- if n.typ != nil and n.typ.kind in {tyVar}:
+ if n.typ != nil and n.typ.kind == 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
- elif n.sym.kind in kinds:
- if n.sym.kind in {skParam, skLet, skForVar}:
- result = arAddressableConst
+ let kinds = if isUnsafeAddr: {skVar, skResult, skTemp, skParam, skLet}
+ else: {skVar, skResult, skTemp}
+ if n.sym.kind in kinds:
+ if owner != nil and owner.id == n.sym.owner.id 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 == skParam and n.sym.typ.kind == tyVar:
+ result = arLValue
elif n.sym.kind == skType:
let t = n.sym.typ.skipTypes({tyTypeDesc})
- if t.kind in {tyVar}: result = arStrange
+ if t.kind == tyVar: result = arStrange
of nkDotExpr:
- let t = skipTypes(n[0].typ, abstractInst-{tyTypeDesc})
- if t.kind in {tyVar, tySink, tyPtr, tyRef}:
+ if skipTypes(n.sons[0].typ, abstractInst-{tyTypeDesc}).kind in
+ {tyVar, tyPtr, tyRef}:
result = arLValue
- elif t.kind == tyLent:
- result = arAddressableConst
else:
- result = isAssignable(owner, n[0])
- if result != arNone and n[1].kind == nkSym and
- sfDiscriminant in n[1].sym.flags:
+ result = isAssignable(owner, n.sons[0], isUnsafeAddr)
+ if result != arNone and sfDiscriminant in n.sons[1].sym.flags:
result = arDiscriminant
of nkBracketExpr:
- let t = skipTypes(n[0].typ, abstractInst-{tyTypeDesc})
- if t.kind in {tyVar, tySink, tyPtr, tyRef}:
+ if skipTypes(n.sons[0].typ, abstractInst-{tyTypeDesc}).kind in
+ {tyVar, tyPtr, tyRef}:
result = arLValue
- elif t.kind == tyLent:
- result = arAddressableConst
else:
- result = isAssignable(owner, n[0])
+ result = isAssignable(owner, n.sons[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])
- elif compareTypes(n.typ, n[1].typ, dcEqIgnoreDistinct):
+ result = isAssignable(owner, n.sons[1], isUnsafeAddr)
+ elif compareTypes(n.typ, n.sons[1].typ, dcEqIgnoreDistinct):
# types that are equal modulo distinction preserve l-value:
- result = isAssignable(owner, n[1])
- of nkHiddenDeref:
- let n0 = n[0]
- if n0.typ.kind == tyLent:
- if n0.kind == nkSym and n0.sym.kind == skResult:
- result = arLValue
- else:
- result = arLentValue
- else:
- result = arLValue
- of nkDerefExpr, nkHiddenAddr:
+ result = isAssignable(owner, n.sons[1], isUnsafeAddr)
+ of nkHiddenDeref, nkDerefExpr, nkHiddenAddr:
result = arLValue
of nkObjUpConv, nkObjDownConv, nkCheckedFieldExpr:
- result = isAssignable(owner, n[0])
+ result = isAssignable(owner, n.sons[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.sons[1], isUnsafeAddr)
of nkStmtList, nkStmtListExpr:
if n.typ != nil:
- result = isAssignable(owner, n.lastSon)
- 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)"
- # within concepts. Semantically, it's not correct to say that
- # nkVarTy denotes an lvalue, but the example above is the only
- # possible code which will get us here
- result = arLValue
+ result = isAssignable(owner, n.lastSon, isUnsafeAddr)
else:
discard
diff --git a/compiler/parser.nim b/compiler/parser.nim
index abfc7b323..6132216e1 100644
--- a/compiler/parser.nim
+++ b/compiler/parser.nim
@@ -16,300 +16,254 @@
# In fact the grammar is generated from this file:
-when isMainModule or defined(nimTestGrammar):
- # 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
-
- 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()
+when isMainModule:
+ 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
import
- llstream, lexer, idents, strutils, ast, msgs, options, lineinfos,
- pathutils
-
-when defined(nimpretty):
- import layouter
-
-when defined(nimPreviewSlimSystem):
- import std/assertions
+ llstream, lexer, idents, strutils, ast, astalgo, msgs
type
- Parser* = object # A Parser object represents a file that
+ TParser*{.final.} = object # A TParser object represents a module that
# is being parsed
currInd: int # current indentation level
- firstTok: bool # Has the first token been read?
- 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
- when defined(nimpretty):
- em*: Emitter
+ firstTok, strongSpaces: bool # Has the first token been read?
+ # Is strongSpaces on?
+ lex*: TLexer # The lexer that is used for parsing
+ tok*: TToken # The current token
+ inPragma: int # Pragma level
+ inSemiStmtList: int
- SymbolMode = enum
- smNormal, smAllowNil, smAfterDot
-
- PrimaryMode = enum
- pmNormal, pmTypeDesc, pmTypeDef, pmTrySimple
-
-proc parseAll*(p: var Parser): PNode
-proc closeParser*(p: var Parser)
-proc parseTopLevelStmt*(p: var Parser): PNode
+proc parseAll*(p: var TParser): PNode
+proc closeParser*(p: var TParser)
+proc parseTopLevelStmt*(p: var TParser): PNode
# helpers for the other parsers
-proc isOperator*(tok: Token): bool
-proc getTok*(p: var Parser)
-proc parMessage*(p: Parser, msg: TMsgKind, arg: string = "")
-proc skipComment*(p: var Parser, node: PNode)
-proc newNodeP*(kind: TNodeKind, p: Parser): PNode
-proc newIntNodeP*(kind: TNodeKind, intVal: BiggestInt, p: Parser): PNode
-proc newFloatNodeP*(kind: TNodeKind, floatVal: BiggestFloat, p: Parser): PNode
-proc newStrNodeP*(kind: TNodeKind, strVal: string, p: Parser): PNode
-proc newIdentNodeP*(ident: PIdent, p: Parser): PNode
-proc expectIdentOrKeyw*(p: Parser)
-proc expectIdent*(p: Parser)
-proc parLineInfo*(p: Parser): TLineInfo
-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 setBaseFlags*(n: PNode, base: NumericalBase)
-proc parseSymbol*(p: var Parser, mode = smNormal): PNode
-proc parseTry(p: var Parser; isExpr: bool): PNode
-proc parseCase(p: var Parser): PNode
-proc parseStmtPragma(p: var Parser): PNode
-proc parsePragma(p: var Parser): PNode
-proc postExprBlocks(p: var Parser, x: PNode): PNode
-proc parseExprStmt(p: var Parser): PNode
-proc parseBlock(p: var Parser): PNode
-proc primary(p: var Parser, mode: PrimaryMode): PNode
-proc simpleExprAux(p: var Parser, limit: int, mode: PrimaryMode): PNode
-
+proc isOperator*(tok: TToken): bool
+proc getTok*(p: var TParser)
+proc parMessage*(p: TParser, msg: TMsgKind, arg: string = "")
+proc skipComment*(p: var TParser, node: PNode)
+proc newNodeP*(kind: TNodeKind, p: TParser): PNode
+proc newIntNodeP*(kind: TNodeKind, intVal: BiggestInt, p: TParser): PNode
+proc newFloatNodeP*(kind: TNodeKind, floatVal: BiggestFloat, p: TParser): PNode
+proc newStrNodeP*(kind: TNodeKind, strVal: string, p: TParser): PNode
+proc newIdentNodeP*(ident: PIdent, p: TParser): PNode
+proc expectIdentOrKeyw*(p: TParser)
+proc expectIdent*(p: TParser)
+proc parLineInfo*(p: TParser): TLineInfo
+proc eat*(p: var TParser, tokType: TTokType)
+proc skipInd*(p: var TParser)
+proc optPar*(p: var TParser)
+proc optInd*(p: var TParser, n: PNode)
+proc indAndComment*(p: var TParser, n: PNode)
+proc setBaseFlags*(n: PNode, base: TNumericalBase)
+proc parseSymbol*(p: var TParser, allowNil = false): PNode
+proc parseTry(p: var TParser; isExpr: bool): PNode
+proc parseCase(p: var TParser): PNode
+proc parseStmtPragma(p: var TParser): PNode
# implementation
-template prettySection(body) =
- when defined(nimpretty): beginSection(p.em)
- body
- when defined(nimpretty): endSection(p.em)
-
-proc getTok(p: var Parser) =
+proc getTok(p: var TParser) =
## 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):
- emitTok(p.em, p.lex, p.tok)
- # skip the additional tokens that nimpretty needs but the parser has no
- # interest in:
- while p.tok.tokType == tkComment:
- rawGetTok(p.lex, p.tok)
- emitTok(p.em, p.lex, p.tok)
-proc openParser*(p: var Parser, fileIdx: FileIndex, inputStream: PLLStream,
- cache: IdentCache; config: ConfigRef) =
+proc openParser*(p: var TParser, fileIdx: int32, inputStream: PLLStream,
+ strongSpaces=false) =
## Open a parser, using the given arguments to set up its internal state.
##
initToken(p.tok)
- openLexer(p.lex, fileIdx, inputStream, cache, config)
- when defined(nimpretty):
- openEmitter(p.em, cache, config, fileIdx)
+ openLexer(p.lex, fileIdx, inputStream)
getTok(p) # read the first token
p.firstTok = true
- p.emptyNode = newNode(nkEmpty)
+ p.strongSpaces = strongSpaces
-proc openParser*(p: var Parser, filename: AbsoluteFile, inputStream: PLLStream,
- cache: IdentCache; config: ConfigRef) =
- openParser(p, fileInfoIdx(config, filename), inputStream, cache, config)
+proc openParser*(p: var TParser, filename: string, inputStream: PLLStream,
+ strongSpaces=false) =
+ openParser(p, filename.fileInfoIdx, inputStream, strongSpaces)
-proc closeParser(p: var Parser) =
+proc closeParser(p: var TParser) =
## Close a parser, freeing up its resources.
closeLexer(p.lex)
- when defined(nimpretty):
- closeEmitter(p.em)
-proc parMessage(p: Parser, msg: TMsgKind, arg = "") =
+proc parMessage(p: TParser, msg: TMsgKind, arg = "") =
## Produce and emit the parser message `arg` to output.
lexMessageTok(p.lex, msg, p.tok, arg)
-proc parMessage(p: Parser, msg: string, tok: Token) =
+proc parMessage(p: TParser, msg: TMsgKind, tok: TToken) =
## Produce and emit a parser message to output about the token `tok`
- parMessage(p, errGenerated, msg % prettyTok(tok))
+ parMessage(p, msg, prettyTok(tok))
-proc parMessage(p: Parser, arg: string) =
- ## Produce and emit the parser message `arg` to output.
- lexMessageTok(p.lex, errGenerated, p.tok, arg)
-
-template withInd(p, body: untyped) =
+template withInd(p: expr, body: stmt) {.immediate.} =
let oldInd = p.currInd
p.currInd = p.tok.indent
body
p.currInd = oldInd
-template newlineWasSplitting(p: var Parser) =
- when defined(nimpretty):
- layouter.newlineWasSplitting(p.em)
-
template realInd(p): bool = p.tok.indent > p.currInd
template sameInd(p): bool = p.tok.indent == p.currInd
template sameOrNoInd(p): bool = p.tok.indent == p.currInd or p.tok.indent < 0
-proc validInd(p: var Parser): bool {.inline.} =
- result = p.tok.indent < 0 or p.tok.indent > p.currInd
-
-proc rawSkipComment(p: var Parser, node: PNode) =
+proc rawSkipComment(p: var TParser, node: PNode) =
if p.tok.tokType == tkComment:
if node != nil:
- var rhs = 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)
- else:
- rhs.add p.tok.literal
- else:
- rhs.add p.tok.literal
- node.comment = move rhs
+ if node.comment == nil: node.comment = ""
+ add(node.comment, p.tok.literal)
else:
parMessage(p, errInternal, "skipComment")
getTok(p)
-proc skipComment(p: var Parser, node: PNode) =
+proc skipComment(p: var TParser, node: PNode) =
if p.tok.indent < 0: rawSkipComment(p, node)
-proc flexComment(p: var Parser, node: PNode) =
+proc flexComment(p: var TParser, node: PNode) =
if p.tok.indent < 0 or realInd(p): rawSkipComment(p, node)
-const
- errInvalidIndentation = "invalid indentation"
- errIdentifierExpected = "identifier expected, but got '$1'"
- errExprExpected = "expression expected, but found '$1'"
-
-proc skipInd(p: var Parser) =
+proc skipInd(p: var TParser) =
if p.tok.indent >= 0:
if not realInd(p): parMessage(p, errInvalidIndentation)
-proc optPar(p: var Parser) =
+proc optPar(p: var TParser) =
if p.tok.indent >= 0:
if p.tok.indent < p.currInd: parMessage(p, errInvalidIndentation)
-proc optInd(p: var Parser, n: PNode) =
+proc optInd(p: var TParser, n: PNode) =
skipComment(p, n)
skipInd(p)
-proc getTokNoInd(p: var Parser) =
+proc getTokNoInd(p: var TParser) =
getTok(p)
if p.tok.indent >= 0: parMessage(p, errInvalidIndentation)
-proc expectIdentOrKeyw(p: Parser) =
+proc expectIdentOrKeyw(p: TParser) =
if p.tok.tokType != tkSymbol and not isKeyword(p.tok.tokType):
- lexMessage(p.lex, errGenerated, errIdentifierExpected % prettyTok(p.tok))
+ lexMessage(p.lex, errIdentifierExpected, prettyTok(p.tok))
-proc expectIdent(p: Parser) =
+proc expectIdent(p: TParser) =
if p.tok.tokType != tkSymbol:
- lexMessage(p.lex, errGenerated, errIdentifierExpected % prettyTok(p.tok))
+ lexMessage(p.lex, errIdentifierExpected, prettyTok(p.tok))
-proc eat(p: var Parser, tokType: TokType) =
+proc eat(p: var TParser, tokType: TTokType) =
## Move the parser to the next token if the current token is of type
## `tokType`, otherwise error.
if p.tok.tokType == tokType:
getTok(p)
else:
- lexMessage(p.lex, errGenerated,
- "expected: '" & $tokType & "', but got: '" & prettyTok(p.tok) & "'")
+ lexMessageTok(p.lex, errTokenExpected, p.tok, TokTypeToStr[tokType])
-proc parLineInfo(p: Parser): TLineInfo =
+proc parLineInfo(p: TParser): 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 TParser, 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)
-proc newNodeP(kind: TNodeKind, p: Parser): PNode =
+proc newNodeP(kind: TNodeKind, p: TParser): PNode =
result = newNodeI(kind, parLineInfo(p))
-proc newIntNodeP(kind: TNodeKind, intVal: BiggestInt, p: Parser): PNode =
+proc newIntNodeP(kind: TNodeKind, intVal: BiggestInt, p: TParser): PNode =
result = newNodeP(kind, p)
result.intVal = intVal
proc newFloatNodeP(kind: TNodeKind, floatVal: BiggestFloat,
- p: Parser): PNode =
+ p: TParser): PNode =
result = newNodeP(kind, p)
result.floatVal = floatVal
-proc newStrNodeP(kind: TNodeKind, strVal: string, p: Parser): PNode =
+proc newStrNodeP(kind: TNodeKind, strVal: string, p: TParser): PNode =
result = newNodeP(kind, p)
result.strVal = strVal
-proc newIdentNodeP(ident: PIdent, p: Parser): PNode =
+proc newIdentNodeP(ident: PIdent, p: TParser): PNode =
result = newNodeP(nkIdent, p)
result.ident = ident
-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 parseParamList(p: var Parser, retColon = true): PNode
+proc parseExpr(p: var TParser): PNode
+proc parseStmt(p: var TParser): PNode
+proc parseTypeDesc(p: var TParser): PNode
+proc parseDoBlocks(p: var TParser, call: PNode)
+proc parseParamList(p: var TParser, retColon = true): PNode
-proc isSigilLike(tok: Token): bool {.inline.} =
+proc isSigilLike(tok: TToken): bool {.inline.} =
result = tok.tokType == tkOpr and tok.ident.s[0] == '@'
-proc isRightAssociative(tok: Token): bool {.inline.} =
+proc isRightAssociative(tok: TToken): bool {.inline.} =
## Determines whether the token is right assocative.
result = tok.tokType == tkOpr and tok.ident.s[0] == '^'
- # or (tok.ident.s.len > 1 and tok.ident.s[^1] == '>')
+ # or (let L = tok.ident.s.len; L > 1 and tok.ident.s[L-1] == '>'))
-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
+proc getPrecedence(tok: TToken, strongSpaces: bool): int =
+ ## Calculates the precedence of the given token.
+ template considerStrongSpaces(x): expr =
+ x + (if strongSpaces: 100 - tok.strongSpaceA.int*10 else: 0)
-proc checkBinary(p: Parser) {.inline.} =
+ case tok.tokType
+ of tkOpr:
+ let L = tok.ident.s.len
+ let relevantChar = tok.ident.s[0]
+
+ # arrow like?
+ if L > 1 and tok.ident.s[L-1] == '>' and
+ tok.ident.s[L-2] in {'-', '~', '='}: return considerStrongSpaces(1)
+
+ template considerAsgn(value: expr) =
+ result = if tok.ident.s[L-1] == '=': 1 else: value
+
+ case relevantChar
+ of '$', '^': considerAsgn(10)
+ of '*', '%', '/', '\\': considerAsgn(9)
+ of '~': result = 8
+ of '+', '-', '|': considerAsgn(8)
+ of '&': considerAsgn(7)
+ of '=', '<', '>', '!': result = 5
+ of '.': considerAsgn(6)
+ of '?': result = 2
+ else: considerAsgn(2)
+ of tkDiv, tkMod, tkShl, tkShr: result = 9
+ of tkIn, tkNotin, tkIs, tkIsnot, tkNot, tkOf, tkAs: result = 5
+ of tkDotDot: result = 6
+ of tkAnd: result = 4
+ of tkOr, tkXor, tkPtr, tkRef: result = 3
+ else: return -10
+ result = considerStrongSpaces(result)
+
+proc isOperator(tok: TToken): 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, tkDotDot, tkAnd, tkOr, tkXor}
+
+proc isUnary(p: TParser): bool =
+ ## Check if the current parser token is a unary operator
+ if p.tok.tokType in {tkOpr, tkDotDot} and
+ p.tok.strongSpaceB == 0 and
+ p.tok.strongSpaceA > 0:
+ result = true
+ # versions prior to 0.13.0 used to do this:
+ when false:
+ if p.strongSpaces:
+ result = true
+ else:
+ parMessage(p, warnDeprecated,
+ "will be parsed as unary operator; inconsistent spacing")
+
+proc checkBinary(p: TParser) {.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:
- parMessage(p, warnInconsistentSpacing, prettyTok(p.tok))
+ if p.strongSpaces and p.tok.tokType == tkOpr:
+ if p.tok.strongSpaceB > 0 and p.tok.strongSpaceA != p.tok.strongSpaceB:
+ parMessage(p, errGenerated,
+ "Number of spaces around '$#' not consistent" %
+ prettyTok(p.tok))
+ elif p.tok.strongSpaceA notin {0,1,2,4,8}:
+ parMessage(p, errGenerated, "Number of spaces must be 0,1,2,4 or 8")
#| module = stmt ^* (';' / IND{=})
#|
@@ -320,15 +274,15 @@ proc checkBinary(p: Parser) {.inline.} =
#|
#| operator = OP0 | OP1 | OP2 | OP3 | OP4 | OP5 | OP6 | OP7 | OP8 | OP9
#| | 'or' | 'xor' | 'and'
-#| | 'is' | 'isnot' | 'in' | 'notin' | 'of' | 'as' | 'from'
+#| | 'is' | 'isnot' | 'in' | 'notin' | 'of'
#| | 'div' | 'mod' | 'shl' | 'shr' | 'not' | 'static' | '..'
#|
#| prefixOperator = operator
#|
-#| optInd = COMMENT? IND?
+#| optInd = COMMENT?
#| optPar = (IND{>} | IND{=})?
#|
-#| simpleExpr = arrowExpr (OP0 optInd arrowExpr)* pragma?
+#| simpleExpr = arrowExpr (OP0 optInd arrowExpr)*
#| arrowExpr = assignExpr (OP1 optInd assignExpr)*
#| assignExpr = orExpr (OP2 optInd orExpr)*
#| orExpr = andExpr (OP3 optInd andExpr)*
@@ -340,48 +294,20 @@ 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) =
+proc colcom(p: var TParser, 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 =
+proc parseSymbol(p: var TParser, allowNil = false): PNode =
#| symbol = '`' (KEYW|IDENT|literal|(operator|'('|')'|'['|']'|'{'|'}'|'=')+)+ '`'
- #| | IDENT | KEYW
+ #| | IDENT | 'addr' | 'type'
case p.tok.tokType
- of tkSymbol:
+ of tkSymbol, tkAddr, tkType:
result = newIdentNodeP(p.tok.ident, p)
getTok(p)
- of tokKeywordLow..tokKeywordHigh:
- if p.tok.tokType in tkBuiltInMagics or mode == smAfterDot:
- # for backwards compatibility these 2 are always valid:
- result = newIdentNodeP(p.tok.ident, p)
- getTok(p)
- elif p.tok.tokType == tkNil and mode == smAllowNil:
- result = newNodeP(nkNilLit, p)
- getTok(p)
- else:
- parMessage(p, errIdentifierExpected, p.tok)
- result = p.emptyNode
of tkAccent:
result = newNodeP(nkAccQuoted, p)
getTok(p)
- # progress guaranteed
while true:
case p.tok.tokType
of tkAccent:
@@ -389,265 +315,197 @@ proc parseSymbol(p: var Parser, mode = smNormal): PNode =
parMessage(p, errIdentifierExpected, p.tok)
break
of tkOpr, tkDot, tkDotDot, tkEquals, tkParLe..tkParDotRi:
- let lineinfo = parLineInfo(p)
var accm = ""
while p.tok.tokType in {tkOpr, tkDot, tkDotDot, tkEquals,
tkParLe..tkParDotRi}:
- accm.add($p.tok)
+ accm.add(tokToStr(p.tok))
getTok(p)
- let node = newNodeI(nkIdent, lineinfo)
- node.ident = p.lex.cache.getIdent(accm)
- result.add(node)
- of tokKeywordLow..tokKeywordHigh, tkSymbol, tkIntLit..tkCustomLit:
- result.add(newIdentNodeP(p.lex.cache.getIdent($p.tok), p))
+ result.add(newIdentNodeP(getIdent(accm), p))
+ of tokKeywordLow..tokKeywordHigh, tkSymbol, tkIntLit..tkCharLit:
+ result.add(newIdentNodeP(getIdent(tokToStr(p.tok)), p))
getTok(p)
else:
parMessage(p, errIdentifierExpected, p.tok)
break
eat(p, tkAccent)
else:
- parMessage(p, errIdentifierExpected, p.tok)
- # BUGFIX: We must consume a token here to prevent endless loops!
- # But: this really sucks for idetools and keywords, so we don't do it
- # if it is a keyword:
- #if not isKeyword(p.tok.tokType): getTok(p)
- result = p.emptyNode
- setEndInfo()
+ if allowNil and p.tok.tokType == tkNil:
+ result = newNodeP(nkNilLit, p)
+ getTok(p)
+ else:
+ parMessage(p, errIdentifierExpected, p.tok)
+ # BUGFIX: We must consume a token here to prevent endless loops!
+ # But: this really sucks for idetools and keywords, so we don't do it
+ # if it is a keyword:
+ if not isKeyword(p.tok.tokType): getTok(p)
+ result = ast.emptyNode
-proc equals(p: var Parser, a: PNode): PNode =
- if p.tok.tokType == tkEquals:
- result = newNodeP(nkExprEqExpr, p)
+proc indexExpr(p: var TParser): PNode =
+ #| indexExpr = expr
+ result = parseExpr(p)
+
+proc indexExprList(p: var TParser, first: PNode, k: TNodeKind,
+ endToken: TTokType): PNode =
+ #| indexExprList = indexExpr ^+ comma
+ result = newNodeP(k, p)
+ addSon(result, first)
+ getTok(p)
+ optInd(p, result)
+ while p.tok.tokType notin {endToken, tkEof}:
+ var a = indexExpr(p)
+ addSon(result, a)
+ if p.tok.tokType != tkComma: break
getTok(p)
- #optInd(p, result)
- result.add(a)
- result.add(parseExpr(p))
- else:
- result = a
+ skipComment(p, a)
+ optPar(p)
+ eat(p, endToken)
-proc colonOrEquals(p: var Parser, a: PNode): PNode =
+proc colonOrEquals(p: var TParser, a: PNode): PNode =
if p.tok.tokType == tkColon:
result = newNodeP(nkExprColonExpr, p)
getTok(p)
- newlineWasSplitting(p)
#optInd(p, result)
- result.add(a)
- result.add(parseExpr(p))
+ addSon(result, a)
+ addSon(result, parseExpr(p))
+ elif p.tok.tokType == tkEquals:
+ result = newNodeP(nkExprEqExpr, p)
+ getTok(p)
+ #optInd(p, result)
+ addSon(result, a)
+ addSon(result, parseExpr(p))
else:
- result = equals(p, a)
+ result = a
-proc exprColonEqExpr(p: var Parser): PNode =
+proc exprColonEqExpr(p: var TParser): PNode =
#| exprColonEqExpr = expr (':'|'=' expr)?
var a = parseExpr(p)
- if p.tok.tokType == tkDo:
- result = postExprBlocks(p, a)
- else:
- result = colonOrEquals(p, a)
+ 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) =
+proc exprList(p: var TParser, endTok: TTokType, result: PNode) =
#| exprList = expr ^+ comma
- when defined(nimpretty):
- inc p.em.doIndentMore
getTok(p)
optInd(p, result)
- # progress guaranteed
- var a = parseExpr(p)
- result.add(a)
while (p.tok.tokType != endTok) and (p.tok.tokType != tkEof):
+ var a = parseExpr(p)
+ addSon(result, a)
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
+proc dotExpr(p: var TParser, a: PNode): PNode =
+ #| dotExpr = expr '.' optInd symbol
+ var info = p.parLineInfo
getTok(p)
+ result = newNodeI(nkDotExpr, info)
optInd(p, result)
- # progress guaranteed
- while (p.tok.tokType != endTok) and (p.tok.tokType != tkEof):
- var a = parseExpr(p)
- result.add(a)
- if p.tok.tokType != tkComma: break
- getTok(p)
- optInd(p, a)
- when defined(nimpretty):
- dec p.em.doIndentMore
+ addSon(result, a)
+ addSon(result, parseSymbol(p))
-proc exprColonEqExprListAux(p: var Parser, endTok: TokType, result: PNode) =
+proc qualifiedIdent(p: var TParser): PNode =
+ #| qualifiedIdent = symbol ('.' optInd symbol)?
+ result = parseSymbol(p)
+ if p.tok.tokType == tkDot: result = dotExpr(p, result)
+
+proc exprColonEqExprListAux(p: var TParser, endTok: TTokType, result: PNode) =
assert(endTok in {tkCurlyRi, tkCurlyDotRi, tkBracketRi, tkParRi})
getTok(p)
- flexComment(p, result)
- optPar(p)
- # progress guaranteed
+ optInd(p, result)
while p.tok.tokType != endTok and p.tok.tokType != tkEof:
var a = exprColonEqExpr(p)
- result.add(a)
+ addSon(result, a)
if p.tok.tokType != tkComma: break
- elif result.kind == nkPar:
- result.transitionSonsKind(nkTupleConstr)
getTok(p)
skipComment(p, a)
optPar(p)
eat(p, endTok)
-proc exprColonEqExprList(p: var Parser, kind: TNodeKind,
- endTok: TokType): PNode =
+proc exprColonEqExprList(p: var TParser, kind: TNodeKind,
+ endTok: TTokType): PNode =
#| exprColonEqExprList = exprColonEqExpr (comma exprColonEqExpr)* (comma)?
result = newNodeP(kind, p)
exprColonEqExprListAux(p, endTok, result)
-proc dotExpr(p: var Parser, a: PNode): PNode =
- 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:
- var x = newNodeI(nkBracketExpr, p.parLineInfo)
- # rewrite 'x.y[:z]()' to 'y[z](x)'
- x.add result[1]
- exprList(p, tkBracketRi, x)
- eat(p, tkBracketRi)
- var y = newNodeI(nkCall, p.parLineInfo)
- y.add x
- y.add result[0]
- if p.tok.tokType == tkParLe and not p.tok.strongSpaceA:
- 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)
- if p.tok.tokType == tkDot: result = dotExpr(p, result)
-
-proc setOrTableConstr(p: var Parser): PNode =
+proc setOrTableConstr(p: var TParser): PNode =
#| setOrTableConstr = '{' ((exprColonEqExpr comma)* | ':' ) '}'
result = newNodeP(nkCurly, p)
getTok(p) # skip '{'
optInd(p, result)
if p.tok.tokType == tkColon:
getTok(p) # skip ':'
- result.transitionSonsKind(nkTableConstr)
+ result.kind = nkTableConstr
else:
- # progress guaranteed
while p.tok.tokType notin {tkCurlyRi, tkEof}:
var a = exprColonEqExpr(p)
- if a.kind == nkExprColonExpr: result.transitionSonsKind(nkTableConstr)
- result.add(a)
+ if a.kind == nkExprColonExpr: result.kind = nkTableConstr
+ addSon(result, a)
if p.tok.tokType != tkComma: break
getTok(p)
skipComment(p, a)
optPar(p)
eat(p, tkCurlyRi) # skip '}'
-proc parseCast(p: var Parser): PNode =
- #| castExpr = 'cast' ('[' optInd typeDesc optPar ']' '(' optInd expr optPar ')') /
- # ('(' optInd exprColonEqExpr optPar ')')
+proc parseCast(p: var TParser): PNode =
+ #| castExpr = 'cast' '[' optInd typeDesc optPar ']' '(' optInd expr optPar ')'
result = newNodeP(nkCast, p)
getTok(p)
- if p.tok.tokType == tkBracketLe:
- getTok(p)
- optInd(p, result)
- result.add(parseTypeDesc(p))
- optPar(p)
- eat(p, tkBracketRi)
- eat(p, tkParLe)
- optInd(p, result)
- result.add(parseExpr(p))
- else:
- result.add p.emptyNode
- eat(p, tkParLe)
- optInd(p, result)
- result.add(exprColonEqExpr(p))
+ eat(p, tkBracketLe)
+ optInd(p, result)
+ addSon(result, parseTypeDesc(p))
+ optPar(p)
+ eat(p, tkBracketRi)
+ eat(p, tkParLe)
+ optInd(p, result)
+ addSon(result, parseExpr(p))
optPar(p)
eat(p, tkParRi)
- setEndInfo()
-proc setBaseFlags(n: PNode, base: NumericalBase) =
+proc setBaseFlags(n: PNode, base: TNumericalBase) =
case base
of base10: discard
of base2: incl(n.flags, nfBase2)
of base8: incl(n.flags, nfBase8)
of base16: incl(n.flags, nfBase16)
-proc parseGStrLit(p: var Parser, a: PNode): PNode =
+proc parseGStrLit(p: var TParser, a: PNode): PNode =
case p.tok.tokType
of tkGStrLit:
result = newNodeP(nkCallStrLit, p)
- result.add(a)
- result.add(newStrNodeP(nkRStrLit, p.tok.literal, p))
+ addSon(result, a)
+ addSon(result, newStrNodeP(nkRStrLit, p.tok.literal, p))
getTok(p)
of tkGTripleStrLit:
result = newNodeP(nkCallStrLit, p)
- result.add(a)
- result.add(newStrNodeP(nkTripleStrLit, p.tok.literal, p))
+ addSon(result, a)
+ addSon(result, newStrNodeP(nkTripleStrLit, p.tok.literal, p))
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
+type
+ TPrimaryMode = enum pmNormal, pmTypeDesc, pmTypeDef, pmSkipSuffix
-proc semiStmtList(p: var Parser, result: PNode) =
+proc complexOrSimpleStmt(p: var TParser): PNode
+proc simpleExpr(p: var TParser, mode = pmNormal): PNode
+
+proc semiStmtList(p: var TParser, 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)
- result.add a
-
- while p.tok.tokType != tkEof:
- if p.tok.tokType == tkSemiColon:
- getTok(p)
- if p.tok.tokType == tkParRi:
- break
- elif not (sameInd(p) or realInd(p)):
- parMessage(p, errInvalidIndentation)
- let a = complexOrSimpleStmt(p)
- if a.kind == nkEmpty:
- parMessage(p, errExprExpected, p.tok)
- getTok(p)
- else:
- result.add a
+ result.add(complexOrSimpleStmt(p))
+ while p.tok.tokType == tkSemiColon:
+ getTok(p)
+ optInd(p, result)
+ result.add(complexOrSimpleStmt(p))
dec p.inSemiStmtList
- result.transitionSonsKind(nkStmtListExpr)
+ result.kind = nkStmtListExpr
-proc parsePar(p: var Parser): PNode =
+proc parsePar(p: var TParser): PNode =
#| parKeyw = 'discard' | 'include' | 'if' | 'while' | 'case' | 'try'
#| | 'finally' | 'except' | 'for' | 'block' | 'const' | 'let'
#| | 'when' | 'var' | 'mixin'
#| par = '(' optInd
- #| ( &parKeyw (ifExpr / complexOrSimpleStmt) ^+ ';'
- #| | ';' (ifExpr / complexOrSimpleStmt) ^+ ';'
+ #| ( &parKeyw complexOrSimpleStmt ^+ ';'
+ #| | ';' complexOrSimpleStmt ^+ ';'
#| | pragmaStmt
- #| | simpleExpr ( ('=' expr (';' (ifExpr / complexOrSimpleStmt) ^+ ';' )? )
+ #| | simpleExpr ( ('=' expr (';' complexOrSimpleStmt ^+ ';' )? )
#| | (':' expr (',' exprColonEqExpr ^+ ',' )? ) ) )
#| optPar ')'
#
@@ -656,10 +514,9 @@ proc parsePar(p: var Parser): PNode =
result = newNodeP(nkPar, p)
getTok(p)
optInd(p, result)
- flexComment(p, result)
if p.tok.tokType in {tkDiscard, tkInclude, tkIf, tkWhile, tkCase,
- tkTry, tkDefer, tkFinally, tkExcept, tkBlock,
- tkConst, tkLet, tkWhen, tkVar, tkFor,
+ tkTry, tkDefer, tkFinally, tkExcept, tkFor, tkBlock,
+ tkConst, tkLet, tkWhen, tkVar,
tkMixin}:
# XXX 'bind' used to be an expression, so we exclude it here;
# tests/reject/tbind2 fails otherwise.
@@ -671,21 +528,16 @@ 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)
- elif p.tok.tokType == tkEquals:
+ if p.tok.tokType == tkEquals:
# special case: allow assignments
- let asgn = newNodeP(nkAsgn, p)
getTok(p)
optInd(p, result)
let b = parseExpr(p)
- asgn.add a
- asgn.add b
+ let asgn = newNodeI(nkAsgn, a.info, 2)
+ asgn.sons[0] = a
+ asgn.sons[1] = b
result.add(asgn)
if p.tok.tokType == tkSemiColon:
semiStmtList(p, result)
@@ -695,40 +547,34 @@ 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)
- # progress guaranteed
while p.tok.tokType != tkParRi and p.tok.tokType != tkEof:
var a = exprColonEqExpr(p)
- result.add(a)
+ addSon(result, a)
if p.tok.tokType != tkComma: break
getTok(p)
skipComment(p, a)
optPar(p)
eat(p, tkParRi)
- setEndInfo()
-proc identOrLiteral(p: var Parser, mode: PrimaryMode): PNode =
+proc identOrLiteral(p: var TParser, mode: TPrimaryMode): 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 ']'
case p.tok.tokType
- of tkSymbol, tkBuiltInMagics, tkOut:
+ of tkSymbol, tkType, tkAddr:
result = newIdentNodeP(p.tok.ident, p)
getTok(p)
result = parseGStrLit(p, result)
@@ -802,14 +648,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)
@@ -829,266 +667,223 @@ proc identOrLiteral(p: var Parser, mode: PrimaryMode): PNode =
result = parseCast(p)
else:
parMessage(p, errExprExpected, p.tok)
- getTok(p) # we must consume a token here to prevent endless loops!
- result = p.emptyNode
+ getTok(p) # we must consume a token here to prevend endless loops!
+ result = ast.emptyNode
-proc namedParams(p: var Parser, callee: PNode,
- kind: TNodeKind, endTok: TokType): PNode =
+proc namedParams(p: var TParser, callee: PNode,
+ kind: TNodeKind, endTok: TTokType): PNode =
let a = callee
result = newNodeP(kind, p)
- result.add(a)
- # progress guaranteed
+ addSon(result, a)
exprColonEqExprListAux(p, endTok, result)
-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)
- 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] != '.'
-
-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 exprColonEqExprList optPar ']'
- #| | '{' optInd exprColonEqExprList optPar '}'
- # XXX strong spaces need to be reflected above
+proc parseMacroColon(p: var TParser, x: PNode): PNode
+proc primarySuffix(p: var TParser, r: PNode, baseIndent: int): PNode =
+ #| primarySuffix = '(' (exprColonEqExpr comma?)* ')' doBlocks?
+ #| | doBlocks
+ #| | '.' optInd symbol generalizedLit?
+ #| | '[' optInd indexExprList optPar ']'
+ #| | '{' optInd indexExprList optPar '}'
+ #| | &( '`'|IDENT|literal|'cast'|'addr'|'type') expr # command syntax
result = r
-
- # progress guaranteed
while p.tok.indent < 0 or
(p.tok.tokType == tkDot and p.tok.indent >= baseIndent):
case p.tok.tokType
of tkParLe:
- # progress guaranteed
- if p.tok.strongSpaceA:
- result = commandExpr(p, result, mode)
- break
+ if p.strongSpaces and p.tok.strongSpaceA > 0: break
result = namedParams(p, result, nkCall, tkParRi)
- if result.len > 1 and result[1].kind == nkExprColonExpr:
- result.transitionSonsKind(nkObjConstr)
+ if result.len > 1 and result.sons[1].kind == nkExprColonExpr:
+ result.kind = nkObjConstr
+ else:
+ parseDoBlocks(p, result)
+ of tkDo:
+ var a = result
+ result = newNodeP(nkCall, p)
+ addSon(result, a)
+ parseDoBlocks(p, result)
of tkDot:
- # progress guaranteed
result = dotExpr(p, result)
result = parseGStrLit(p, result)
of tkBracketLe:
- # progress guaranteed
- if p.tok.strongSpaceA:
- result = commandExpr(p, result, mode)
- break
+ if p.strongSpaces and p.tok.strongSpaceA > 0: break
result = namedParams(p, result, nkBracketExpr, tkBracketRi)
of tkCurlyLe:
- # progress guaranteed
- if p.tok.strongSpaceA:
- result = commandExpr(p, result, mode)
- break
+ if p.strongSpaces and p.tok.strongSpaceA > 0: break
result = namedParams(p, result, nkCurlyExpr, tkCurlyRi)
- of tkSymbol, tkAccent, tkIntLit..tkCustomLit, tkNil, tkCast,
- tkOpr, tkDotDot, tkVar, tkOut, tkStatic, tkType, tkEnum, tkTuple,
- tkObject, tkProc:
- # XXX: In type sections we allow the free application of the
- # command syntax, with the exception of expressions such as
- # `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
+ of tkSymbol, tkAccent, tkIntLit..tkCharLit, tkNil, tkCast, tkAddr, tkType:
+ if p.inPragma == 0:
+ # actually parsing {.push hints:off.} as {.push(hints:off).} is a sweet
+ # solution, but pragmas.nim can't handle that
+ let a = result
+ result = newNodeP(nkCommand, p)
+ addSon(result, a)
+ when true:
+ addSon result, parseExpr(p)
+ else:
+ while p.tok.tokType != tkEof:
+ let x = parseExpr(p)
+ addSon(result, x)
+ if p.tok.tokType != tkComma: break
+ getTok(p)
+ optInd(p, x)
+ if p.tok.tokType == tkDo:
+ parseDoBlocks(p, result)
+ else:
+ result = parseMacroColon(p, result)
+ break
else:
break
-proc parseOperators(p: var Parser, headNode: PNode,
- limit: int, mode: PrimaryMode): PNode =
+proc primary(p: var TParser, mode: TPrimaryMode): PNode
+proc simpleExprAux(p: var TParser, limit: int, mode: TPrimaryMode): PNode
+
+proc parseOperators(p: var TParser, headNode: PNode,
+ limit: int, mode: TPrimaryMode): PNode =
result = headNode
# expand while operators have priorities higher than 'limit'
- var opPrec = getPrecedence(p.tok)
+ var opPrec = getPrecedence(p.tok, p.strongSpaces)
let modeB = if mode == pmTypeDef: pmTypeDesc else: mode
# the operator itself must not start on a new line:
- # progress guaranteed
- while opPrec >= limit and p.tok.indent < 0 and not isUnary(p.tok):
+ while opPrec >= limit and p.tok.indent < 0 and not isUnary(p):
checkBinary(p)
- let leftAssoc = ord(not isRightAssociative(p.tok))
+ var leftAssoc = 1-ord(isRightAssociative(p.tok))
var a = newNodeP(nkInfix, p)
var opNode = newIdentNodeP(p.tok.ident, p) # skip operator:
getTok(p)
- flexComment(p, a)
- optPar(p)
+ optInd(p, a)
# read sub-expression with higher priority:
var b = simpleExprAux(p, opPrec + leftAssoc, modeB)
- a.add(opNode)
- a.add(result)
- a.add(b)
+ addSon(a, opNode)
+ addSon(a, result)
+ addSon(a, b)
result = a
- opPrec = getPrecedence(p.tok)
- setEndInfo()
+ opPrec = getPrecedence(p.tok, p.strongSpaces)
-proc simpleExprAux(p: var Parser, limit: int, mode: PrimaryMode): PNode =
- var mode = mode
+proc simpleExprAux(p: var TParser, limit: int, mode: TPrimaryMode): PNode =
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)
- pragmaExp.add result
- pragmaExp.add p.parsePragma
- result = pragmaExp
result = parseOperators(p, result, limit, mode)
-proc simpleExpr(p: var Parser, mode = pmNormal): PNode =
- when defined(nimpretty):
- inc p.em.doIndentMore
+proc simpleExpr(p: var TParser, mode = pmNormal): PNode =
result = simpleExprAux(p, -1, mode)
- when defined(nimpretty):
- dec p.em.doIndentMore
-proc parsePragma(p: var Parser): PNode =
- #| pragma = '{.' optInd (exprColonEqExpr comma?)* optPar ('.}' | '}')
+proc parseIfExpr(p: var TParser, kind: TNodeKind): PNode =
+ #| condExpr = expr colcom expr optInd
+ #| ('elif' expr colcom expr optInd)*
+ #| 'else' colcom expr
+ #| ifExpr = 'if' condExpr
+ #| whenExpr = 'when' condExpr
+ result = newNodeP(kind, p)
+ while true:
+ getTok(p) # skip `if`, `elif`
+ var branch = newNodeP(nkElifExpr, p)
+ addSon(branch, parseExpr(p))
+ colcom(p, branch)
+ addSon(branch, parseExpr(p))
+ optInd(p, branch)
+ addSon(result, branch)
+ if p.tok.tokType != tkElif: break
+ var branch = newNodeP(nkElseExpr, p)
+ eat(p, tkElse)
+ colcom(p, branch)
+ addSon(branch, parseExpr(p))
+ addSon(result, branch)
+
+proc parsePragma(p: var TParser): PNode =
+ #| pragma = '{.' optInd (exprColonExpr comma?)* optPar ('.}' | '}')
result = newNodeP(nkPragma, p)
inc p.inPragma
- when defined(nimpretty):
- inc p.em.doIndentMore
- inc p.em.keepIndents
getTok(p)
optInd(p, result)
while p.tok.tokType notin {tkCurlyDotRi, tkCurlyRi, tkEof}:
- p.hasProgress = false
var a = exprColonEqExpr(p)
- if not p.hasProgress: break
- result.add(a)
+ addSon(result, a)
if p.tok.tokType == tkComma:
getTok(p)
skipComment(p, a)
optPar(p)
- if p.tok.tokType in {tkCurlyDotRi, tkCurlyRi}:
- when defined(nimpretty):
- if p.tok.tokType == tkCurlyRi: curlyRiWasPragma(p.em)
- getTok(p)
- else:
- parMessage(p, "expected '.}'")
+ if p.tok.tokType in {tkCurlyDotRi, tkCurlyRi}: getTok(p)
+ else: parMessage(p, errTokenExpected, ".}")
dec p.inPragma
- 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
- #| identVisDot = symbol '.' optInd symbol OPR?
+proc identVis(p: var TParser; allowDot=false): PNode =
+ #| identVis = symbol opr? # postfix position
+ #| identVisDot = symbol '.' optInd symbol opr?
var a = parseSymbol(p)
if p.tok.tokType == tkOpr:
- when defined(nimpretty):
- starWasExportMarker(p.em)
result = newNodeP(nkPostfix, p)
- result.add(newIdentNodeP(p.tok.ident, p))
- result.add(a)
+ addSon(result, newIdentNodeP(p.tok.ident, p))
+ addSon(result, a)
getTok(p)
elif p.tok.tokType == tkDot and allowDot:
result = dotExpr(p, a)
else:
result = a
-proc identWithPragma(p: var Parser; allowDot=false): PNode =
+proc identWithPragma(p: var TParser; allowDot=false): PNode =
#| identWithPragma = identVis pragma?
#| identWithPragmaDot = identVisDot pragma?
var a = identVis(p, allowDot)
if p.tok.tokType == tkCurlyDotLe:
result = newNodeP(nkPragmaExpr, p)
- result.add(a)
- result.add(parsePragma(p))
+ addSon(result, a)
+ addSon(result, parsePragma(p))
else:
result = a
type
- DeclaredIdentFlag = enum
+ TDeclaredIdentFlag = enum
withPragma, # identifier may have pragma
withBothOptional # both ':' and '=' parts are optional
- withDot # allow 'var ident.ident = value'
- DeclaredIdentFlags = set[DeclaredIdentFlag]
+ TDeclaredIdentFlags = set[TDeclaredIdentFlag]
-proc parseIdentColonEquals(p: var Parser, flags: DeclaredIdentFlags): PNode =
+proc parseIdentColonEquals(p: var TParser, flags: TDeclaredIdentFlags): PNode =
#| declColonEquals = identWithPragma (comma identWithPragma)* comma?
- #| (':' optInd typeDescExpr)? ('=' optInd expr)?
- #| identColonEquals = IDENT (comma IDENT)* comma?
- #| (':' optInd typeDescExpr)? ('=' optInd expr)?)
+ #| (':' optInd typeDesc)? ('=' optInd expr)?
+ #| identColonEquals = ident (comma ident)* comma?
+ #| (':' optInd typeDesc)? ('=' optInd expr)?)
var a: PNode
result = newNodeP(nkIdentDefs, p)
- # progress guaranteed
while true:
case p.tok.tokType
of tkSymbol, tkAccent:
- if withPragma in flags: a = identWithPragma(p, allowDot=withDot in flags)
+ if withPragma in flags: a = identWithPragma(p)
else: a = parseSymbol(p)
if a.kind == nkEmpty: return
else: break
- result.add(a)
+ addSon(result, a)
if p.tok.tokType != tkComma: break
getTok(p)
optInd(p, a)
if p.tok.tokType == tkColon:
getTok(p)
optInd(p, result)
- result.add(parseTypeDesc(p, fullExpr = true))
+ addSon(result, parseTypeDesc(p))
else:
- result.add(newNodeP(nkEmpty, p))
+ addSon(result, ast.emptyNode)
if p.tok.tokType != tkEquals and withBothOptional notin flags:
- parMessage(p, "':' or '=' expected, but got '$1'", p.tok)
+ parMessage(p, errColonOrEqualsExpected, p.tok)
if p.tok.tokType == tkEquals:
getTok(p)
optInd(p, result)
- result.add(parseExpr(p))
+ addSon(result, parseExpr(p))
else:
- result.add(newNodeP(nkEmpty, p))
- setEndInfo()
+ addSon(result, ast.emptyNode)
-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)*)?)
+proc parseTuple(p: var TParser, indentAllowed = false): PNode =
+ #| 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:
getTok(p)
optInd(p, result)
- # progress guaranteed
while p.tok.tokType in {tkSymbol, tkAccent}:
var a = parseIdentColonEquals(p, {})
- result.add(a)
+ addSon(result, a)
if p.tok.tokType notin {tkComma, tkSemiColon}: break
- when defined(nimpretty):
- commaWasSemicolon(p.em)
getTok(p)
skipComment(p, a)
optPar(p)
@@ -1098,372 +893,246 @@ proc parseTuple(p: var Parser, indentAllowed = false): PNode =
if realInd(p):
withInd(p):
rawSkipComment(p, result)
- # progress guaranteed
while true:
case p.tok.tokType
of tkSymbol, tkAccent:
var a = parseIdentColonEquals(p, {})
if p.tok.indent < 0 or p.tok.indent >= p.currInd:
rawSkipComment(p, a)
- result.add(a)
+ addSon(result, a)
of tkEof: break
else:
parMessage(p, errIdentifierExpected, p.tok)
break
if not sameInd(p): break
- elif p.tok.tokType == tkParLe:
- 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 =
+proc parseParamList(p: var TParser, retColon = true): PNode =
#| paramList = '(' declColonEquals ^* (comma/semicolon) ')'
#| paramListArrow = paramList? ('->' optInd typeDesc)?
#| paramListColon = paramList? (':' optInd typeDesc)?
var a: PNode
result = newNodeP(nkFormalParams, p)
- result.add(p.emptyNode) # return type
- when defined(nimpretty):
- inc p.em.doIndentMore
- inc p.em.keepIndents
+ addSon(result, ast.emptyNode) # return type
let hasParLe = p.tok.tokType == tkParLe and p.tok.indent < 0
if hasParLe:
getTok(p)
optInd(p, result)
- # progress guaranteed
while true:
case p.tok.tokType
of tkSymbol, tkAccent:
a = parseIdentColonEquals(p, {withBothOptional, withPragma})
of tkParRi:
break
- of tkVar:
- parMessage(p, errGenerated, "the syntax is 'parameter: var T', not 'var parameter: T'")
- break
else:
- parMessage(p, "expected closing ')'")
+ parMessage(p, errTokenExpected, ")")
break
- result.add(a)
+ addSon(result, a)
if p.tok.tokType notin {tkComma, tkSemiColon}: break
- when defined(nimpretty):
- commaWasSemicolon(p.em)
getTok(p)
skipComment(p, a)
optPar(p)
eat(p, tkParRi)
let hasRet = if retColon: p.tok.tokType == tkColon
- else: p.tok.tokType == tkOpr and p.tok.ident.s == "->"
+ else: p.tok.tokType == tkOpr and identEq(p.tok.ident, "->")
if hasRet and p.tok.indent < 0:
getTok(p)
optInd(p, result)
- result[0] = parseTypeDesc(p)
- elif not retColon and not hasParLe:
+ result.sons[0] = parseTypeDesc(p)
+ elif not retColon and not hasParle:
# Mark as "not there" in order to mark for deprecation in the semantic pass:
- result = p.emptyNode
- when defined(nimpretty):
- dec p.em.doIndentMore
- dec p.em.keepIndents
- setEndInfo()
+ result = ast.emptyNode
-proc optPragmas(p: var Parser): PNode =
+proc optPragmas(p: var TParser): PNode =
if p.tok.tokType == tkCurlyDotLe and (p.tok.indent < 0 or realInd(p)):
result = parsePragma(p)
else:
- result = p.emptyNode
+ result = ast.emptyNode
-proc parseDoBlock(p: var Parser; info: TLineInfo): PNode =
- #| doBlock = 'do' paramListArrow pragma? colcom stmt
- var params = parseParamList(p, retColon=false)
+proc parseDoBlock(p: var TParser): PNode =
+ #| doBlock = 'do' paramListArrow pragmas? colcom stmt
+ let info = parLineInfo(p)
+ getTok(p)
+ 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
- result = newProcNode(nkDo, info,
- body = result, params = params, name = p.emptyNode, pattern = p.emptyNode,
- genericParams = p.emptyNode, pragmas = pragmas, exceptions = p.emptyNode)
- setEndInfo()
+ result = newProcNode(nkDo, info, parseStmt(p),
+ params = params,
+ pragmas = pragmas)
-proc parseProcExpr(p: var Parser; isExpr: bool; kind: TNodeKind): PNode =
- #| routineExpr = ('proc' | 'func' | 'iterator') paramListColon pragma? ('=' COMMENT? stmt)?
- #| routineType = ('proc' | 'iterator') paramListColon pragma?
+proc parseDoBlocks(p: var TParser, call: PNode) =
+ #| doBlocks = doBlock ^* IND{=}
+ if p.tok.tokType == tkDo:
+ #withInd(p):
+ # addSon(call, parseDoBlock(p))
+ while sameOrNoInd(p) and p.tok.tokType == tkDo:
+ addSon(call, parseDoBlock(p))
+
+proc parseProcExpr(p: var TParser, isExpr: bool): PNode =
+ #| procExpr = 'proc' paramListColon pragmas? ('=' 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)
if p.tok.tokType == tkEquals and isExpr:
getTok(p)
skipComment(p, result)
- result = newProcNode(kind, info, body = parseStmt(p),
- params = params, name = p.emptyNode, pattern = p.emptyNode,
- genericParams = p.emptyNode, pragmas = pragmas, exceptions = p.emptyNode)
+ result = newProcNode(nkLambda, info, parseStmt(p),
+ params = params,
+ pragmas = pragmas)
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()
+ addSon(result, params)
+ addSon(result, pragmas)
-proc isExprStart(p: Parser): bool =
+proc isExprStart(p: TParser): 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:
+ of tkSymbol, tkAccent, tkOpr, tkNot, tkNil, tkCast, tkIf,
+ tkProc, tkIterator, tkBind, tkAddr,
+ tkParLe, tkBracketLe, tkCurlyLe, tkIntLit..tkCharLit, tkVar, tkRef, tkPtr,
+ tkTuple, tkObject, tkType, tkWhen, tkCase, tkOut:
result = true
else: result = false
-proc parseSymbolList(p: var Parser, result: PNode) =
- # progress guaranteed
+proc parseSymbolList(p: var TParser, result: PNode, allowNil = false) =
while true:
- var s = parseSymbol(p, smAllowNil)
+ var s = parseSymbol(p, allowNil)
if s.kind == nkEmpty: break
- result.add(s)
+ addSon(result, s)
if p.tok.tokType != tkComma: break
getTok(p)
optInd(p, s)
- setEndInfo()
-proc parseTypeDescKAux(p: var Parser, kind: TNodeKind,
- mode: PrimaryMode): PNode =
+proc parseTypeDescKAux(p: var TParser, kind: TNodeKind,
+ mode: TPrimaryMode): 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))
- if kind == nkDistinctTy and p.tok.tokType == tkSymbol:
- # XXX document this feature!
- var nodeKind: TNodeKind
- if p.tok.ident.s == "with":
- nodeKind = nkWith
- elif p.tok.ident.s == "without":
- nodeKind = nkWithout
- else:
- return result
+ addSon(result, primary(p, mode))
+ if kind == nkDistinctTy and p.tok.tokType in {tkWith, tkWithout}:
+ let nodeKind = if p.tok.tokType == tkWith: nkWith
+ else: nkWithout
getTok(p)
let list = newNodeP(nodeKind, p)
- result.add list
- parseSymbolList(p, list)
- if mode == pmTypeDef and not isTypedef:
- result = parseOperators(p, result, -1, mode)
- setEndInfo()
+ result.addSon list
+ parseSymbolList(p, list, allowNil = true)
-proc parseVarTuple(p: var Parser): PNode
-
-proc parseFor(p: var Parser): PNode =
- #| forStmt = 'for' ((varTuple / identWithPragma) ^+ comma) 'in' expr colcom stmt
- #| forExpr = forStmt
- getTokNoInd(p)
- result = newNodeP(nkForStmt, p)
- if p.tok.tokType == tkParLe:
- result.add(parseVarTuple(p))
- else:
- var a = identWithPragma(p)
- result.add(a)
- while p.tok.tokType == tkComma:
- getTok(p)
- optInd(p, a)
- if p.tok.tokType == tkParLe:
- result.add(parseVarTuple(p))
- break
- a = identWithPragma(p)
- result.add(a)
- eat(p, tkIn)
- result.add(parseExpr(p))
- colcom(p, result)
- result.add(parseStmt(p))
- setEndInfo()
-
-template nimprettyDontTouch(body) =
- when defined(nimpretty):
- inc p.em.keepIndents
- body
- when defined(nimpretty):
- dec p.em.keepIndents
-
-proc parseExpr(p: var Parser): PNode =
- #| expr = (blockExpr
- #| | ifExpr
+proc parseExpr(p: var TParser): PNode =
+ #| expr = (ifExpr
#| | whenExpr
- #| | caseStmt
- #| | forExpr
+ #| | caseExpr
#| | tryExpr)
#| / simpleExpr
- case p.tok.tokType
- of tkBlock:
- nimprettyDontTouch:
- result = parseBlock(p)
- of tkIf:
- nimprettyDontTouch:
- result = parseIfOrWhenExpr(p, nkIfExpr)
- of tkFor:
- nimprettyDontTouch:
- result = parseFor(p)
- of tkWhen:
- nimprettyDontTouch:
- result = parseIfOrWhenExpr(p, nkWhenExpr)
- of tkCase:
- # Currently we think nimpretty is good enough with case expressions,
- # so it is allowed to touch them:
- #nimprettyDontTouch:
- result = parseCase(p)
- of tkTry:
- nimprettyDontTouch:
- result = parseTry(p, isExpr=true)
+ case p.tok.tokType:
+ of tkIf: result = parseIfExpr(p, nkIfExpr)
+ of tkWhen: result = parseIfExpr(p, nkWhenExpr)
+ of tkCase: result = parseCase(p)
+ of tkTry: 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 parseEnum(p: var TParser): PNode
+proc parseObject(p: var TParser): PNode
+proc parseTypeClass(p: var TParser): 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:
+proc primary(p: var TParser, mode: TPrimaryMode): PNode =
+ #| typeKeyw = 'var' | 'out' | 'ref' | 'ptr' | 'shared' | 'tuple'
+ #| | 'proc' | 'iterator' | 'distinct' | 'object' | 'enum'
+ #| primary = typeKeyw typeDescK
+ #| / prefixOperator* identOrLiteral primarySuffix*
+ #| / 'static' primary
+ #| / '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)
+ addSon(result, 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 = primarySuffix(p, result, baseInd, mode)
+ addSon(result, primary(p, pmSkipSuffix))
+ result = primarySuffix(p, result, baseInd)
else:
- result.add(primary(p, pmNormal))
+ addSon(result, 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)
+ case p.tok.tokType:
+ of tkTuple: result = parseTuple(p, mode == pmTypeDef)
+ of tkProc: result = parseProcExpr(p, mode notin {pmTypeDesc, pmTypeDef})
of tkIterator:
- getTok(p)
- result = parseProcExpr(p, mode != pmTypeDesc, nkIteratorDef)
+ when false:
+ if mode in {pmTypeDesc, pmTypeDef}:
+ result = parseProcExpr(p, false)
+ result.kind = nkIteratorTy
+ else:
+ # no anon iterators for now:
+ parMessage(p, errExprExpected, p.tok)
+ getTok(p) # we must consume a token here to prevend endless loops!
+ result = ast.emptyNode
+ else:
+ result = parseProcExpr(p, mode notin {pmTypeDesc, pmTypeDef})
+ if result.kind == nkLambda: result.kind = nkIteratorDef
+ else: result.kind = nkIteratorTy
+ of tkEnum:
+ if mode == pmTypeDef:
+ result = parseEnum(p)
+ else:
+ result = newNodeP(nkEnumTy, p)
+ getTok(p)
+ of tkObject:
+ if mode == pmTypeDef:
+ result = parseObject(p)
+ else:
+ result = newNodeP(nkObjectTy, p)
+ getTok(p)
+ of tkGeneric, tkConcept:
+ if mode == pmTypeDef:
+ let wasGeneric = p.tok.tokType == tkGeneric
+ result = parseTypeClass(p)
+ # hack so that it's remembered and can be marked as deprecated in
+ # sem'check:
+ if wasGeneric: result.flags.incl nfBase2
+ else:
+ parMessage(p, errInvalidToken, p.tok)
+ of tkStatic:
+ let info = parLineInfo(p)
+ getTokNoInd(p)
+ let next = primary(p, pmNormal)
+ if next.kind == nkBracket and next.sonsLen == 1:
+ result = newNode(nkStaticTy, info, @[next.sons[0]])
+ else:
+ result = newNode(nkStaticExpr, info, @[next])
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)
+ addSon(result, primary(p, pmNormal))
+ of tkVar: result = parseTypeDescKAux(p, nkVarTy, mode)
+ of tkOut: result = parseTypeDescKAux(p, nkVarTy, 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)
-proc binaryNot(p: var Parser; a: PNode): PNode =
- if p.tok.tokType == tkNot:
- let notOpr = newIdentNodeP(p.tok.ident, p)
- getTok(p)
- optInd(p, notOpr)
- let b = parseExpr(p)
- result = newNodeP(nkInfix, p)
- result.add notOpr
- result.add a
- result.add b
- else:
- result = a
+proc parseTypeDesc(p: var TParser): PNode =
+ #| typeDesc = simpleExpr
+ result = simpleExpr(p, pmTypeDesc)
-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
- 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 = 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)
- result = binaryNot(p, result)
- setEndInfo()
+proc parseTypeDefAux(p: var TParser): PNode =
+ #| typeDefAux = simpleExpr
+ #| | 'concept' typeClass
+ result = simpleExpr(p, pmTypeDef)
proc makeCall(n: PNode): PNode =
## Creates a call if the given node isn't already a call.
@@ -1473,120 +1142,94 @@ proc makeCall(n: PNode): PNode =
result = newNodeI(nkCall, n.info)
result.add n
-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{=} 'else' ':' stmt )*
+proc parseMacroColon(p: var TParser, x: PNode): PNode =
+ #| macroColon = ':' stmt? ( IND{=} 'of' exprList ':' stmt
+ #| | IND{=} 'elif' expr ':' stmt
+ #| | IND{=} 'except' exprList ':' stmt
+ #| | IND{=} 'else' ':' stmt )*
result = x
- if p.tok.indent >= 0: return
-
- var
- openingParams = p.emptyNode
- openingPragmas = p.emptyNode
-
- if p.tok.tokType == tkDo:
- getTok(p)
- openingParams = parseParamList(p, retColon=false)
- openingPragmas = optPragmas(p)
-
- if p.tok.tokType == tkColon:
+ if p.tok.tokType == tkColon and p.tok.indent < 0:
result = makeCall(result)
getTok(p)
skipComment(p, result)
- if p.tok.tokType notin {tkOf, tkElif, tkElse, tkExcept, tkFinally}:
- 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
- result.add newProcNode(nkDo, stmtList.info, body = stmtList,
- params = openingParams,
- name = p.emptyNode, pattern = p.emptyNode,
- genericParams = p.emptyNode,
- pragmas = openingPragmas,
- exceptions = p.emptyNode)
- else:
- result.add stmtList
-
+ let stmtList = newNodeP(nkStmtList, p)
+ if p.tok.tokType notin {tkOf, tkElif, tkElse, tkExcept}:
+ let body = parseStmt(p)
+ stmtList.add body
+ #addSon(result, makeStmtList(body))
while sameInd(p):
- var nextBlock: PNode
- let nextToken = p.tok.tokType
- if nextToken == tkDo:
- let info = parLineInfo(p)
+ var b: PNode
+ case p.tok.tokType
+ of tkOf:
+ b = newNodeP(nkOfBranch, p)
+ exprList(p, tkColon, b)
+ of tkElif:
+ b = newNodeP(nkElifBranch, p)
getTok(p)
- nextBlock = parseDoBlock(p, info)
- else:
- case nextToken
- of tkOf:
- nextBlock = newNodeP(nkOfBranch, p)
- exprList(p, tkColon, nextBlock)
- of tkElif:
- nextBlock = newNodeP(nkElifBranch, p)
- getTok(p)
- optInd(p, nextBlock)
- nextBlock.add parseExpr(p)
- of tkExcept:
- nextBlock = newNodeP(nkExceptBranch, p)
- optionalExprList(p, tkColon, nextBlock)
- of tkFinally:
- nextBlock = newNodeP(nkFinally, p)
- getTok(p)
- of tkElse:
- nextBlock = newNodeP(nkElse, p)
- getTok(p)
- else: break
- eat(p, tkColon)
- nextBlock.add parseStmt(p)
+ optInd(p, b)
+ addSon(b, parseExpr(p))
+ of tkExcept:
+ b = newNodeP(nkExceptBranch, p)
+ exprList(p, tkColon, b)
+ of tkElse:
+ b = newNodeP(nkElse, p)
+ getTok(p)
+ else: break
+ eat(p, tkColon)
+ addSon(b, parseStmt(p))
+ addSon(stmtList, b)
+ if b.kind == nkElse: break
+ if stmtList.len == 1 and stmtList[0].kind == nkStmtList:
+ # to keep backwards compatibility (see tests/vm/tstringnil)
+ result.add stmtList[0]
+ else:
+ result.add stmtList
- nextBlock.flags.incl nfBlockArg
- result.add nextBlock
-
- if nextBlock.kind in {nkElse, nkFinally}: 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)
+proc parseExprStmt(p: var TParser): PNode =
+ #| exprStmt = simpleExpr
+ #| (( '=' optInd expr )
+ #| / ( expr ^+ comma
+ #| doBlocks
+ #| / macroColon
+ #| ))?
+ var a = simpleExpr(p)
if p.tok.tokType == tkEquals:
- result = newNodeP(nkAsgn, p)
getTok(p)
optInd(p, result)
var b = parseExpr(p)
- b = postExprBlocks(p, b)
- result.add(a)
- result.add(b)
+ result = newNodeI(nkAsgn, a.info)
+ addSon(result, a)
+ addSon(result, b)
else:
- 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):
- result = newTreeI(nkCommand, a.info, a)
- let baseIndent = p.currInd
+ # simpleExpr parsed 'p a' from 'p a, b'?
+ if p.tok.indent < 0 and p.tok.tokType == tkComma and a.kind == nkCommand:
+ result = a
while true:
- result.add(commandParam(p, isFirstParam, pmNormal))
- if p.tok.tokType != tkComma or
- (p.tok.indent >= 0 and p.tok.indent < baseIndent):
- break
+ getTok(p)
+ optInd(p, result)
+ var e = parseExpr(p)
+ addSon(result, e)
+ if p.tok.tokType != tkComma: break
+ elif p.tok.indent < 0 and isExprStart(p):
+ if a.kind == nkCommand:
+ result = a
+ else:
+ result = newNode(nkCommand, a.info, @[a])
+ while true:
+ var e = parseExpr(p)
+ addSon(result, e)
+ if p.tok.tokType != tkComma: break
getTok(p)
optInd(p, result)
else:
result = a
- result = postExprBlocks(p, result)
- setEndInfo()
+ if p.tok.tokType == tkDo and p.tok.indent < 0:
+ result = makeCall(result)
+ parseDoBlocks(p, result)
+ return result
+ result = parseMacroColon(p, result)
-proc parseModuleName(p: var Parser, kind: TNodeKind): PNode =
+proc parseModuleName(p: var TParser, kind: TNodeKind): PNode =
result = parseExpr(p)
when false:
# parseExpr already handles 'as' syntax ...
@@ -1596,98 +1239,84 @@ 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 =
+proc parseImport(p: var TParser, kind: TNodeKind): PNode =
#| importStmt = 'import' optInd expr
#| ((comma expr)*
#| / 'except' optInd (expr ^+ comma))
- #| exportStmt = 'export' optInd expr
- #| ((comma expr)*
- #| / 'except' optInd (expr ^+ comma))
result = newNodeP(kind, p)
getTok(p) # skip `import` or `export`
optInd(p, result)
var a = parseModuleName(p, kind)
- result.add(a)
+ addSon(result, a)
if p.tok.tokType in {tkComma, tkExcept}:
if p.tok.tokType == tkExcept:
- result.transitionSonsKind(succ(kind))
+ result.kind = succ(kind)
getTok(p)
optInd(p, result)
while true:
# was: while p.tok.tokType notin {tkEof, tkSad, tkDed}:
- p.hasProgress = false
a = parseModuleName(p, kind)
- if a.kind == nkEmpty or not p.hasProgress: break
- result.add(a)
+ if a.kind == nkEmpty: break
+ addSon(result, a)
if p.tok.tokType != tkComma: break
getTok(p)
optInd(p, a)
#expectNl(p)
- setEndInfo()
-proc parseIncludeStmt(p: var Parser): PNode =
+proc parseIncludeStmt(p: var TParser): PNode =
#| includeStmt = 'include' optInd expr ^+ comma
result = newNodeP(nkIncludeStmt, p)
getTok(p) # skip `import` or `include`
optInd(p, result)
while true:
# was: while p.tok.tokType notin {tkEof, tkSad, tkDed}:
- p.hasProgress = false
var a = parseExpr(p)
- if a.kind == nkEmpty or not p.hasProgress: break
- result.add(a)
+ if a.kind == nkEmpty: break
+ addSon(result, a)
if p.tok.tokType != tkComma: break
getTok(p)
optInd(p, a)
#expectNl(p)
- setEndInfo()
-proc parseFromStmt(p: var Parser): PNode =
- #| fromStmt = 'from' expr 'import' optInd expr (comma expr)*
+proc parseFromStmt(p: var TParser): PNode =
+ #| fromStmt = 'from' moduleName 'import' optInd expr (comma expr)*
result = newNodeP(nkFromStmt, p)
getTok(p) # skip `from`
optInd(p, result)
var a = parseModuleName(p, nkImportStmt)
- result.add(a) #optInd(p, a);
+ addSon(result, a) #optInd(p, a);
eat(p, tkImport)
optInd(p, result)
while true:
# p.tok.tokType notin {tkEof, tkSad, tkDed}:
- p.hasProgress = false
a = parseExpr(p)
- if a.kind == nkEmpty or not p.hasProgress: break
- result.add(a)
+ if a.kind == nkEmpty: break
+ addSon(result, a)
if p.tok.tokType != tkComma: break
getTok(p)
optInd(p, a)
#expectNl(p)
- setEndInfo()
-proc parseReturnOrRaise(p: var Parser, kind: TNodeKind): PNode =
+proc parseReturnOrRaise(p: var TParser, kind: TNodeKind): PNode =
#| returnStmt = 'return' optInd expr?
#| raiseStmt = 'raise' optInd expr?
#| 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:
skipComment(p, result)
- result.add(p.emptyNode)
+ addSon(result, ast.emptyNode)
elif p.tok.indent >= 0 and p.tok.indent <= p.currInd or not isExprStart(p):
# NL terminates:
- result.add(p.emptyNode)
- # nimpretty here!
+ addSon(result, ast.emptyNode)
else:
- var e = parseExpr(p)
- e = postExprBlocks(p, e)
- result.add(e)
- setEndInfo()
+ addSon(result, parseExpr(p))
-proc parseIfOrWhen(p: var Parser, kind: TNodeKind): PNode =
+proc parseIfOrWhen(p: var TParser, kind: TNodeKind): PNode =
#| condStmt = expr colcom stmt COMMENT?
#| (IND{=} 'elif' expr colcom stmt)*
#| (IND{=} 'else' colcom stmt)?
@@ -1698,56 +1327,29 @@ proc parseIfOrWhen(p: var Parser, kind: TNodeKind): PNode =
getTok(p) # skip `if`, `when`, `elif`
var branch = newNodeP(nkElifBranch, p)
optInd(p, branch)
- branch.add(parseExpr(p))
+ addSon(branch, parseExpr(p))
colcom(p, branch)
- branch.add(parseStmt(p))
+ addSon(branch, parseStmt(p))
skipComment(p, branch)
- result.add(branch)
+ addSon(result, branch)
if p.tok.tokType != tkElif or not sameOrNoInd(p): break
if p.tok.tokType == tkElse and sameOrNoInd(p):
var branch = newNodeP(nkElse, p)
eat(p, tkElse)
colcom(p, branch)
- branch.add(parseStmt(p))
- result.add(branch)
- setEndInfo()
+ addSon(branch, parseStmt(p))
+ addSon(result, branch)
-proc parseIfOrWhenExpr(p: var Parser, kind: TNodeKind): PNode =
- #| condExpr = expr colcom expr optInd
- #| ('elif' expr colcom expr optInd)*
- #| 'else' colcom expr
- #| ifExpr = 'if' condExpr
- #| whenExpr = 'when' condExpr
- result = newNodeP(kind, p)
- while true:
- getTok(p) # skip `if`, `when`, `elif`
- var branch = newNodeP(nkElifExpr, p)
- optInd(p, branch)
- branch.add(parseExpr(p))
- colcom(p, branch)
- branch.add(parseStmt(p))
- skipComment(p, branch)
- result.add(branch)
- if p.tok.tokType != tkElif: break
- if p.tok.tokType == tkElse:
- var branch = newNodeP(nkElseExpr, p)
- eat(p, tkElse)
- colcom(p, branch)
- branch.add(parseStmt(p))
- result.add(branch)
- setEndInfo()
-
-proc parseWhile(p: var Parser): PNode =
+proc parseWhile(p: var TParser): PNode =
#| whileStmt = 'while' expr colcom stmt
result = newNodeP(nkWhileStmt, p)
getTok(p)
optInd(p, result)
- result.add(parseExpr(p))
+ addSon(result, parseExpr(p))
colcom(p, result)
- result.add(parseStmt(p))
- setEndInfo()
+ addSon(result, parseStmt(p))
-proc parseCase(p: var Parser): PNode =
+proc parseCase(p: var TParser): PNode =
#| ofBranch = 'of' exprList colcom stmt
#| ofBranches = ofBranch (IND{=} ofBranch)*
#| (IND{=} 'elif' expr colcom stmt)*
@@ -1757,11 +1359,11 @@ proc parseCase(p: var Parser): PNode =
#| | IND{=} ofBranches)
var
b: PNode
- inElif = false
+ inElif= false
wasIndented = false
result = newNodeP(nkCaseStmt, p)
getTok(p)
- result.add(parseExpr(p))
+ addSon(result, parseExpr(p))
if p.tok.tokType == tkColon: getTok(p)
skipComment(p, result)
@@ -1781,321 +1383,308 @@ proc parseCase(p: var Parser): PNode =
b = newNodeP(nkElifBranch, p)
getTok(p)
optInd(p, b)
- b.add(parseExpr(p))
+ addSon(b, parseExpr(p))
of tkElse:
b = newNodeP(nkElse, p)
getTok(p)
else: break
colcom(p, b)
- b.add(parseStmt(p))
- result.add(b)
+ addSon(b, parseStmt(p))
+ addSon(result, b)
if b.kind == nkElse: break
if wasIndented:
p.currInd = oldInd
- setEndInfo()
-proc parseTry(p: var Parser; isExpr: bool): PNode =
+proc parseTry(p: var TParser; 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))
+ addSon(result, 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)
else: break
colcom(p, b)
- b.add(parseStmt(p))
- result.add(b)
- if b == nil: parMessage(p, "expected 'except'")
- setEndInfo()
+ addSon(b, parseStmt(p))
+ addSon(result, b)
+ if b.kind == nkFinally: break
+ if b == nil: parMessage(p, errTokenExpected, "except")
-proc parseExceptBlock(p: var Parser, kind: TNodeKind): PNode =
+proc parseExceptBlock(p: var TParser, kind: TNodeKind): PNode =
+ #| exceptBlock = 'except' colcom stmt
result = newNodeP(kind, p)
getTok(p)
colcom(p, result)
- result.add(parseStmt(p))
- setEndInfo()
+ addSon(result, parseStmt(p))
-proc parseBlock(p: var Parser): PNode =
+proc parseFor(p: var TParser): PNode =
+ #| forStmt = 'for' (identWithPragma ^+ comma) 'in' expr colcom stmt
+ result = newNodeP(nkForStmt, p)
+ getTokNoInd(p)
+ var a = identWithPragma(p)
+ addSon(result, a)
+ while p.tok.tokType == tkComma:
+ getTok(p)
+ optInd(p, a)
+ a = identWithPragma(p)
+ addSon(result, a)
+ eat(p, tkIn)
+ addSon(result, parseExpr(p))
+ colcom(p, result)
+ addSon(result, parseStmt(p))
+
+proc parseBlock(p: var TParser): PNode =
#| blockStmt = 'block' symbol? colcom stmt
- #| blockExpr = 'block' symbol? colcom stmt
result = newNodeP(nkBlockStmt, p)
getTokNoInd(p)
- if p.tok.tokType == tkColon: result.add(p.emptyNode)
- else: result.add(parseSymbol(p))
+ if p.tok.tokType == tkColon: addSon(result, ast.emptyNode)
+ else: addSon(result, parseSymbol(p))
colcom(p, result)
- result.add(parseStmt(p))
- setEndInfo()
+ addSon(result, parseStmt(p))
-proc parseStaticOrDefer(p: var Parser; k: TNodeKind): PNode =
+proc parseStaticOrDefer(p: var TParser; k: TNodeKind): PNode =
#| staticStmt = 'static' colcom stmt
#| deferStmt = 'defer' colcom stmt
result = newNodeP(k, p)
getTok(p)
colcom(p, result)
- result.add(parseStmt(p))
- setEndInfo()
+ addSon(result, parseStmt(p))
-proc parseAsm(p: var Parser): PNode =
- #| asmStmt = 'asm' pragma? (STR_LIT | RSTR_LIT | TRIPLESTR_LIT)
+proc parseAsm(p: var TParser): PNode =
+ #| asmStmt = 'asm' pragma? (STR_LIT | RSTR_LIT | TRIPLE_STR_LIT)
result = newNodeP(nkAsmStmt, p)
getTokNoInd(p)
- if p.tok.tokType == tkCurlyDotLe: result.add(parsePragma(p))
- else: result.add(p.emptyNode)
+ if p.tok.tokType == tkCurlyDotLe: addSon(result, parsePragma(p))
+ else: addSon(result, ast.emptyNode)
case p.tok.tokType
- of tkStrLit: result.add(newStrNodeP(nkStrLit, p.tok.literal, p))
- of tkRStrLit: result.add(newStrNodeP(nkRStrLit, p.tok.literal, p))
- of tkTripleStrLit: result.add(newStrNodeP(nkTripleStrLit, p.tok.literal, p))
+ of tkStrLit: addSon(result, newStrNodeP(nkStrLit, p.tok.literal, p))
+ of tkRStrLit: addSon(result, newStrNodeP(nkRStrLit, p.tok.literal, p))
+ of tkTripleStrLit: addSon(result,
+ newStrNodeP(nkTripleStrLit, p.tok.literal, p))
else:
- parMessage(p, "the 'asm' statement takes a string literal")
- result.add(p.emptyNode)
+ parMessage(p, errStringLiteralExpected)
+ addSon(result, ast.emptyNode)
return
getTok(p)
- setEndInfo()
-proc parseGenericParam(p: var Parser): PNode =
+proc parseGenericParam(p: var TParser): PNode =
#| genericParam = symbol (comma symbol)* (colon expr)? ('=' optInd expr)?
var a: PNode
result = newNodeP(nkIdentDefs, p)
- # progress guaranteed
while true:
case p.tok.tokType
- of tkIn, tkOut:
- let x = p.lex.cache.getIdent(if p.tok.tokType == tkIn: "in" else: "out")
- a = newNodeP(nkPrefix, p)
- a.add newIdentNodeP(x, p)
- getTok(p)
- expectIdent(p)
- a.add(parseSymbol(p))
of tkSymbol, tkAccent:
a = parseSymbol(p)
if a.kind == nkEmpty: return
else: break
- result.add(a)
+ addSon(result, a)
if p.tok.tokType != tkComma: break
getTok(p)
optInd(p, a)
if p.tok.tokType == tkColon:
getTok(p)
optInd(p, result)
- result.add(parseExpr(p))
+ addSon(result, parseExpr(p))
else:
- result.add(p.emptyNode)
+ addSon(result, ast.emptyNode)
if p.tok.tokType == tkEquals:
getTok(p)
optInd(p, result)
- result.add(parseExpr(p))
+ addSon(result, parseExpr(p))
else:
- result.add(p.emptyNode)
- setEndInfo()
+ addSon(result, ast.emptyNode)
-proc parseGenericParamList(p: var Parser): PNode =
+proc parseGenericParamList(p: var TParser): PNode =
#| genericParamList = '[' optInd
#| genericParam ^* (comma/semicolon) optPar ']'
result = newNodeP(nkGenericParams, p)
getTok(p)
optInd(p, result)
- # progress guaranteed
- while p.tok.tokType in {tkSymbol, tkAccent, tkIn, tkOut}:
+ while p.tok.tokType in {tkSymbol, tkAccent}:
var a = parseGenericParam(p)
- result.add(a)
+ addSon(result, a)
if p.tok.tokType notin {tkComma, tkSemiColon}: break
- when defined(nimpretty):
- commaWasSemicolon(p.em)
getTok(p)
skipComment(p, a)
optPar(p)
eat(p, tkBracketRi)
- setEndInfo()
-proc parsePattern(p: var Parser): PNode =
+proc parsePattern(p: var TParser): PNode =
#| pattern = '{' stmt '}'
eat(p, tkCurlyLe)
result = parseStmt(p)
eat(p, tkCurlyRi)
- setEndInfo()
-proc parseRoutine(p: var Parser, kind: TNodeKind): PNode =
+proc validInd(p: var TParser): bool =
+ result = p.tok.indent < 0 or p.tok.indent > p.currInd
+
+proc parseRoutine(p: var TParser, kind: TNodeKind): PNode =
#| indAndComment = (IND{>} COMMENT)? | COMMENT?
#| routine = optInd identVis pattern? genericParamList?
#| paramListColon pragma? ('=' COMMENT? stmt)? indAndComment
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)
+ addSon(result, identVis(p))
+ if p.tok.tokType == tkCurlyLe and p.validInd: addSon(result, p.parsePattern)
+ else: addSon(result, ast.emptyNode)
if p.tok.tokType == tkBracketLe and p.validInd:
result.add(p.parseGenericParamList)
else:
- result.add(p.emptyNode)
- result.add(p.parseParamList)
- if p.tok.tokType == tkCurlyDotLe and p.validInd: result.add(p.parsePragma)
- else: result.add(p.emptyNode)
+ addSon(result, ast.emptyNode)
+ addSon(result, p.parseParamList)
+ if p.tok.tokType == tkCurlyDotLe and p.validInd: addSon(result, p.parsePragma)
+ else: addSon(result, ast.emptyNode)
# empty exception tracking:
- result.add(p.emptyNode)
- let maybeMissEquals = p.tok.tokType != tkEquals
- if (not maybeMissEquals) and p.validInd:
+ addSon(result, ast.emptyNode)
+ if p.tok.tokType == tkEquals and p.validInd:
getTok(p)
skipComment(p, result)
- result.add(parseStmt(p))
+ addSon(result, 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()
+ addSon(result, ast.emptyNode)
+ indAndComment(p, result)
-proc newCommentStmt(p: var Parser): PNode =
+proc newCommentStmt(p: var TParser): PNode =
#| commentStmt = COMMENT
result = newNodeP(nkCommentStmt, p)
result.comment = p.tok.literal
getTok(p)
-proc parseSection(p: var Parser, kind: TNodeKind,
- defparser: proc (p: var Parser): PNode {.nimcall.}): PNode =
- #| section(RULE) = COMMENT? RULE / (IND{>} (RULE / COMMENT)^+IND{=} DED)
+type
+ TDefParser = proc (p: var TParser): PNode {.nimcall.}
+
+proc parseSection(p: var TParser, kind: TNodeKind,
+ defparser: TDefParser): PNode =
+ #| section(p) = COMMENT? p / (IND{>} (p / COMMENT)^+IND{=} DED)
result = newNodeP(kind, p)
if kind != nkTypeSection: getTok(p)
skipComment(p, result)
if realInd(p):
withInd(p):
skipComment(p, result)
- # progress guaranteed
while sameInd(p):
case p.tok.tokType
of tkSymbol, tkAccent, tkParLe:
var a = defparser(p)
skipComment(p, a)
- result.add(a)
+ addSon(result, a)
of tkComment:
var a = newCommentStmt(p)
- result.add(a)
+ addSon(result, a)
else:
parMessage(p, errIdentifierExpected, p.tok)
break
if result.len == 0: parMessage(p, errIdentifierExpected, p.tok)
elif p.tok.tokType in {tkSymbol, tkAccent, tkParLe} and p.tok.indent < 0:
# tkParLe is allowed for ``var (x, y) = ...`` tuple parsing
- result.add(defparser(p))
+ addSon(result, 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?)+
+proc parseConstant(p: var TParser): PNode =
+ #| constant = identWithPragma (colon typedesc)? '=' optInd expr indAndComment
+ result = newNodeP(nkConstDef, p)
+ addSon(result, identWithPragma(p))
+ if p.tok.tokType == tkColon:
+ getTok(p)
+ optInd(p, result)
+ addSon(result, parseTypeDesc(p))
+ else:
+ addSon(result, ast.emptyNode)
+ eat(p, tkEquals)
+ optInd(p, result)
+ addSon(result, parseExpr(p))
+ indAndComment(p, result)
+
+proc parseEnum(p: var TParser): PNode =
+ #| enum = 'enum' optInd (symbol optInd ('=' optInd expr COMMENT?)? comma?)+
result = newNodeP(nkEnumTy, p)
getTok(p)
- result.add(p.emptyNode)
+ addSon(result, ast.emptyNode)
optInd(p, result)
flexComment(p, result)
- # progress guaranteed
while true:
var a = parseSymbol(p)
if a.kind == nkEmpty: return
-
- var symPragma = a
- var pragma: PNode
- if (p.tok.indent < 0 or p.tok.indent >= p.currInd) and p.tok.tokType == tkCurlyDotLe:
- pragma = optPragmas(p)
- symPragma = newNodeP(nkPragmaExpr, p)
- symPragma.add(a)
- symPragma.add(pragma)
- # nimpretty support here
if p.tok.indent >= 0 and p.tok.indent <= p.currInd:
- result.add(symPragma)
+ add(result, a)
break
-
if p.tok.tokType == tkEquals and p.tok.indent < 0:
getTok(p)
- optInd(p, symPragma)
- var b = symPragma
- symPragma = newNodeP(nkEnumFieldDef, p)
- symPragma.add(b)
- symPragma.add(parseExpr(p))
+ optInd(p, a)
+ var b = a
+ a = newNodeP(nkEnumFieldDef, p)
+ addSon(a, b)
+ addSon(a, parseExpr(p))
if p.tok.indent < 0 or p.tok.indent >= p.currInd:
- rawSkipComment(p, symPragma)
+ rawSkipComment(p, a)
if p.tok.tokType == tkComma and p.tok.indent < 0:
getTok(p)
- rawSkipComment(p, symPragma)
+ rawSkipComment(p, a)
else:
if p.tok.indent < 0 or p.tok.indent >= p.currInd:
- rawSkipComment(p, symPragma)
- result.add(symPragma)
+ rawSkipComment(p, a)
+ addSon(result, a)
if p.tok.indent >= 0 and p.tok.indent <= p.currInd or
p.tok.tokType == tkEof:
break
if result.len <= 1:
- parMessage(p, errIdentifierExpected, p.tok)
- setEndInfo()
+ lexMessageTok(p.lex, errIdentifierExpected, p.tok, prettyTok(p.tok))
-proc parseObjectPart(p: var Parser): PNode
-proc parseObjectWhen(p: var Parser): PNode =
+proc parseObjectPart(p: var TParser): PNode
+proc parseObjectWhen(p: var TParser): PNode =
#| objectWhen = 'when' expr colcom objectPart COMMENT?
#| ('elif' expr colcom objectPart COMMENT?)*
#| ('else' colcom objectPart COMMENT?)?
result = newNodeP(nkRecWhen, p)
- # progress guaranteed
while sameInd(p):
getTok(p) # skip `when`, `elif`
var branch = newNodeP(nkElifBranch, p)
optInd(p, branch)
- branch.add(parseExpr(p))
+ addSon(branch, parseExpr(p))
colcom(p, branch)
- branch.add(parseObjectPart(p))
+ addSon(branch, parseObjectPart(p))
flexComment(p, branch)
- result.add(branch)
+ addSon(result, branch)
if p.tok.tokType != tkElif: break
if p.tok.tokType == tkElse and sameInd(p):
var branch = newNodeP(nkElse, p)
eat(p, tkElse)
colcom(p, branch)
- branch.add(parseObjectPart(p))
+ addSon(branch, parseObjectPart(p))
flexComment(p, branch)
- result.add(branch)
- setEndInfo()
+ addSon(result, branch)
-proc parseObjectCase(p: var Parser): PNode =
+proc parseObjectCase(p: var TParser): 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})
- result.add(a)
+ var a = newNodeP(nkIdentDefs, p)
+ addSon(a, identWithPragma(p))
+ eat(p, tkColon)
+ addSon(a, parseTypeDesc(p))
+ addSon(a, ast.emptyNode)
+ addSon(result, a)
if p.tok.tokType == tkColon: getTok(p)
flexComment(p, result)
var wasIndented = false
@@ -2103,7 +1692,6 @@ proc parseObjectCase(p: var Parser): PNode =
if realInd(p):
p.currInd = p.tok.indent
wasIndented = true
- # progress guaranteed
while sameInd(p):
var b: PNode
case p.tok.tokType
@@ -2119,14 +1707,13 @@ proc parseObjectCase(p: var Parser): PNode =
if fields.kind == nkEmpty:
parMessage(p, errIdentifierExpected, p.tok)
fields = newNodeP(nkNilLit, p) # don't break further semantic checking
- b.add(fields)
- result.add(b)
+ addSon(b, fields)
+ addSon(result, b)
if b.kind == nkElse: break
if wasIndented:
p.currInd = oldInd
- setEndInfo()
-proc parseObjectPart(p: var Parser): PNode =
+proc parseObjectPart(p: var TParser): PNode =
#| objectPart = IND{>} objectPart^+IND{=} DED
#| / objectWhen / objectCase / 'nil' / 'discard' / declColonEquals
if realInd(p):
@@ -2136,11 +1723,11 @@ proc parseObjectPart(p: var Parser): PNode =
while sameInd(p):
case p.tok.tokType
of tkCase, tkWhen, tkSymbol, tkAccent, tkNil, tkDiscard:
- result.add(parseObjectPart(p))
+ addSon(result, parseObjectPart(p))
else:
parMessage(p, errIdentifierExpected, p.tok)
break
- elif sameOrNoInd(p):
+ else:
case p.tok.tokType
of tkWhen:
result = parseObjectWhen(p)
@@ -2154,195 +1741,130 @@ proc parseObjectPart(p: var Parser): PNode =
result = newNodeP(nkNilLit, p)
getTok(p)
else:
- result = p.emptyNode
- else:
- result = p.emptyNode
- setEndInfo()
+ result = ast.emptyNode
-proc parseObject(p: var Parser): PNode =
- #| objectDecl = 'object' ('of' typeDesc)? COMMENT? objectPart
+proc parseObject(p: var TParser): PNode =
+ #| 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:
+ addSon(result, parsePragma(p))
+ else:
+ addSon(result, ast.emptyNode)
if p.tok.tokType == tkOf and p.tok.indent < 0:
var a = newNodeP(nkOfInherit, p)
getTok(p)
- a.add(parseTypeDesc(p))
- result.add(a)
+ addSon(a, parseTypeDesc(p))
+ addSon(result, a)
else:
- result.add(p.emptyNode)
+ addSon(result, ast.emptyNode)
if p.tok.tokType == tkComment:
skipComment(p, result)
# an initial IND{>} HAS to follow:
if not realInd(p):
- result.add(p.emptyNode)
- else:
- result.add(parseObjectPart(p))
- setEndInfo()
+ addSon(result, emptyNode)
+ return
+ addSon(result, parseObjectPart(p))
-proc parseTypeClassParam(p: var Parser): PNode =
- let modifier =
- case p.tok.tokType
- of tkOut, tkVar: nkVarTy
- of tkPtr: nkPtrTy
- of tkRef: nkRefTy
- of tkStatic: nkStaticTy
- of tkType: nkTypeOfExpr
- else: nkEmpty
-
- if modifier != nkEmpty:
- result = newNodeP(modifier, p)
+proc parseTypeClassParam(p: var TParser): PNode =
+ if p.tok.tokType in {tkOut, tkVar}:
+ result = newNodeP(nkVarTy, p)
getTok(p)
- result.add(p.parseSymbol)
+ result.addSon(p.parseSymbol)
else:
result = p.parseSymbol
- setEndInfo()
-proc parseTypeClass(p: var Parser): PNode =
- #| conceptParam = ('var' | 'out')? symbol
- #| conceptDecl = 'concept' conceptParam ^* ',' (pragma)? ('of' typeDesc ^* ',')?
+proc parseTypeClass(p: var TParser): PNode =
+ #| 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)
- args.add(p.parseTypeClassParam)
- while p.tok.tokType == tkComma:
- getTok(p)
- args.add(p.parseTypeClassParam)
- else:
- result.add(p.emptyNode) # see ast.isNewStyleConcept
+ var args = newNodeP(nkArgList, p)
+ addSon(result, args)
+ addSon(args, p.parseTypeClassParam)
+ while p.tok.tokType == tkComma:
+ getTok(p)
+ addSon(args, p.parseTypeClassParam)
if p.tok.tokType == tkCurlyDotLe and p.validInd:
- result.add(parsePragma(p))
+ addSon(result, parsePragma(p))
else:
- result.add(p.emptyNode)
+ addSon(result, ast.emptyNode)
if p.tok.tokType == tkOf and p.tok.indent < 0:
var a = newNodeP(nkOfInherit, p)
getTok(p)
- # progress guaranteed
while true:
- a.add(parseTypeDesc(p))
+ addSon(a, parseTypeDesc(p))
if p.tok.tokType != tkComma: break
getTok(p)
- result.add(a)
+ addSon(result, a)
else:
- result.add(p.emptyNode)
+ addSon(result, ast.emptyNode)
if p.tok.tokType == tkComment:
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)
+ addSon(result, emptyNode)
else:
- result.add(parseStmt(p))
- setEndInfo()
+ addSon(result, parseStmt(p))
-proc parseTypeDef(p: var Parser): PNode =
+proc parseTypeDef(p: var TParser): PNode =
#|
- #| typeDef = identVisDot genericParamList? pragma '=' optInd typeDefValue
+ #| typeDef = identWithPragmaDot genericParamList? '=' optInd typeDefAux
#| indAndComment?
result = newNodeP(nkTypeDef, p)
- var identifier = identVis(p, allowDot=true)
- var identPragma = identifier
- var pragma: PNode
- var genericParam: PNode
-
+ addSon(result, identWithPragma(p, allowDot=true))
if p.tok.tokType == tkBracketLe and p.validInd:
- genericParam = parseGenericParamList(p)
+ addSon(result, parseGenericParamList(p))
else:
- genericParam = p.emptyNode
-
- pragma = optPragmas(p)
- if pragma.kind != nkEmpty:
- identPragma = newNodeP(nkPragmaExpr, p)
- identPragma.add(identifier)
- identPragma.add(pragma)
-
- result.add(identPragma)
- result.add(genericParam)
-
+ addSon(result, ast.emptyNode)
if p.tok.tokType == tkEquals:
- result.info = parLineInfo(p)
getTok(p)
optInd(p, result)
- result.add(parseTypeDefValue(p))
+ addSon(result, parseTypeDefAux(p))
else:
- result.add(p.emptyNode)
+ addSon(result, ast.emptyNode)
indAndComment(p, result) # special extension!
- setEndInfo()
-proc parseVarTuple(p: var Parser): PNode =
+proc parseVarTuple(p: var TParser): PNode =
#| varTuple = '(' optInd identWithPragma ^+ comma optPar ')' '=' optInd expr
result = newNodeP(nkVarTuple, p)
getTok(p) # skip '('
optInd(p, result)
- # progress guaranteed
while p.tok.tokType in {tkSymbol, tkAccent}:
- var a = identWithPragma(p, allowDot=true)
- result.add(a)
+ var a = identWithPragma(p)
+ addSon(result, a)
if p.tok.tokType != tkComma: break
getTok(p)
skipComment(p, a)
- result.add(p.emptyNode) # no type desc
+ addSon(result, ast.emptyNode) # no type desc
optPar(p)
eat(p, tkParRi)
- setEndInfo()
-
-proc parseVariable(p: var Parser): PNode =
- #| colonBody = colcom stmt postExprBlocks?
- #| variable = (varTuple / identColonEquals) colonBody? indAndComment
- if p.tok.tokType == tkParLe:
- result = parseVarTuple(p)
- eat(p, tkEquals)
- optInd(p, result)
- result.add(parseExpr(p))
- 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
- if p.tok.tokType == tkParLe: result = parseVarTuple(p)
- else:
- result = newNodeP(nkConstDef, p)
- result.add(identWithPragma(p))
- if p.tok.tokType == tkColon:
- getTok(p)
- optInd(p, result)
- result.add(parseTypeDesc(p))
- else:
- result.add(p.emptyNode)
eat(p, tkEquals)
optInd(p, result)
- #add(result, parseStmtListExpr(p))
- result.add(parseExpr(p))
- result[^1] = postExprBlocks(p, result[^1])
- indAndComment(p, result)
- setEndInfo()
+ addSon(result, parseExpr(p))
-proc parseBind(p: var Parser, k: TNodeKind): PNode =
+proc parseVariable(p: var TParser): PNode =
+ #| variable = (varTuple / identColonEquals) indAndComment
+ if p.tok.tokType == tkParLe: result = parseVarTuple(p)
+ else: result = parseIdentColonEquals(p, {withPragma})
+ indAndComment(p, result)
+
+proc parseBind(p: var TParser, k: TNodeKind): PNode =
#| bindStmt = 'bind' optInd qualifiedIdent ^+ comma
#| mixinStmt = 'mixin' optInd qualifiedIdent ^+ comma
result = newNodeP(k, p)
getTok(p)
optInd(p, result)
- # progress guaranteed
while true:
var a = qualifiedIdent(p)
- result.add(a)
+ addSon(result, a)
if p.tok.tokType != tkComma: break
getTok(p)
optInd(p, a)
#expectNl(p)
- setEndInfo()
-proc parseStmtPragma(p: var Parser): PNode =
+proc parseStmtPragma(p: var TParser): PNode =
#| pragmaStmt = pragma (':' COMMENT? stmt)?
result = parsePragma(p)
if p.tok.tokType == tkColon and p.tok.indent < 0:
@@ -2352,9 +1874,8 @@ proc parseStmtPragma(p: var Parser): PNode =
skipComment(p, result)
result.add a
result.add parseStmt(p)
- setEndInfo()
-proc simpleStmt(p: var Parser): PNode =
+proc simpleStmt(p: var TParser): PNode =
#| simpleStmt = ((returnStmt | raiseStmt | yieldStmt | discardStmt | breakStmt
#| | continueStmt | pragmaStmt | importStmt | exportStmt | fromStmt
#| | includeStmt | commentStmt) / exprStmt) COMMENT?
@@ -2374,16 +1895,15 @@ proc simpleStmt(p: var Parser): PNode =
of tkComment: result = newCommentStmt(p)
else:
if isExprStart(p): result = parseExprStmt(p)
- else: result = p.emptyNode
+ else: result = ast.emptyNode
if result.kind notin {nkEmpty, nkCommentStmt}: skipComment(p, result)
-proc complexOrSimpleStmt(p: var Parser): PNode =
+proc complexOrSimpleStmt(p: var TParser): PNode =
#| complexOrSimpleStmt = (ifStmt | whenStmt | whileStmt
#| | tryStmt | forStmt
#| | blockStmt | staticStmt | deferStmt | asmStmt
#| | 'proc' routine
#| | 'method' routine
- #| | 'func' routine
#| | 'iterator' routine
#| | 'macro' routine
#| | 'template' routine
@@ -2406,7 +1926,6 @@ proc complexOrSimpleStmt(p: var Parser): PNode =
of tkDefer: result = parseStaticOrDefer(p, nkDefer)
of tkAsm: result = parseAsm(p)
of tkProc: result = parseRoutine(p, nkProcDef)
- of tkFunc: result = parseRoutine(p, nkFuncDef)
of tkMethod: result = parseRoutine(p, nkMethodDef)
of tkIterator: result = parseRoutine(p, nkIteratorDef)
of tkMacro: result = parseRoutine(p, nkMacroDef)
@@ -2417,31 +1936,24 @@ proc complexOrSimpleStmt(p: var Parser): PNode =
if p.tok.tokType == tkParLe:
getTok(p)
result = newNodeP(nkTypeOfExpr, p)
- result.add(primary(p, pmTypeDesc))
+ result.addSon(primary(p, pmTypeDesc))
eat(p, tkParRi)
result = parseOperators(p, result, -1, pmNormal)
else:
result = parseSection(p, nkTypeSection, parseTypeDef)
- of tkConst:
- prettySection:
- result = parseSection(p, nkConstSection, parseConstant)
- of tkLet:
- prettySection:
- result = parseSection(p, nkLetSection, parseVariable)
- of tkVar:
- prettySection:
- result = parseSection(p, nkVarSection, parseVariable)
+ of tkConst: result = parseSection(p, nkConstSection, parseConstant)
+ of tkLet: result = parseSection(p, nkLetSection, parseVariable)
of tkWhen: result = parseIfOrWhen(p, nkWhenStmt)
+ of tkVar: result = parseSection(p, nkVarSection, parseVariable)
of tkBind: result = parseBind(p, nkBindStmt)
of tkMixin: result = parseBind(p, nkMixinStmt)
of tkUsing: result = parseSection(p, nkUsingStmt, parseVariable)
else: result = simpleStmt(p)
-proc parseStmt(p: var Parser): PNode =
+proc parseStmt(p: var TParser): PNode =
#| stmt = (IND{>} complexOrSimpleStmt^+(IND{=} / ';') DED)
#| / simpleStmt ^+ ';'
if p.tok.indent > p.currInd:
- # nimpretty support here
result = newNodeP(nkStmtList, p)
withInd(p):
while true:
@@ -2459,25 +1971,19 @@ proc parseStmt(p: var Parser): PNode =
# XXX this ensures tnamedparamanonproc still compiles;
# deprecate this syntax later
break
- p.hasProgress = false
- if p.tok.tokType in {tkElse, tkElif}:
- break # Allow this too, see tests/parser/tifexprs
-
- let a = complexOrSimpleStmt(p)
- if a.kind == nkEmpty and not p.hasProgress:
- parMessage(p, errExprExpected, p.tok)
- break
+ var a = complexOrSimpleStmt(p)
+ if a.kind != nkEmpty:
+ addSon(result, a)
else:
- result.add a
-
- if not p.hasProgress and p.tok.tokType == tkEof: break
+ parMessage(p, errExprExpected, p.tok)
+ getTok(p)
else:
# the case statement is only needed for better error messages:
case p.tok.tokType
- of tkIf, tkWhile, tkCase, tkTry, tkFor, tkBlock, tkAsm, tkProc, tkFunc,
- tkIterator, tkMacro, tkType, tkConst, tkWhen, tkVar:
- parMessage(p, "nestable statement requires indentation")
- result = p.emptyNode
+ of tkIf, tkWhile, tkCase, tkTry, tkFor, tkBlock, tkAsm, tkProc, tkIterator,
+ tkMacro, tkType, tkConst, tkWhen, tkVar:
+ parMessage(p, errComplexStmtRequiresInd)
+ result = ast.emptyNode
else:
if p.inSemiStmtList > 0:
result = simpleStmt(p)
@@ -2487,52 +1993,35 @@ proc parseStmt(p: var Parser): PNode =
while true:
if p.tok.indent >= 0:
parMessage(p, errInvalidIndentation)
- p.hasProgress = false
let a = simpleStmt(p)
- let err = not p.hasProgress
if a.kind == nkEmpty: parMessage(p, errExprExpected, p.tok)
result.add(a)
if p.tok.tokType != tkSemiColon: break
getTok(p)
- if err and p.tok.tokType == tkEof: break
- setEndInfo()
-proc parseAll(p: var Parser): PNode =
+proc parseAll(p: var TParser): PNode =
## Parses the rest of the input stream held by the parser into a PNode.
result = newNodeP(nkStmtList, p)
while p.tok.tokType != tkEof:
- p.hasProgress = false
var a = complexOrSimpleStmt(p)
- if a.kind != nkEmpty and p.hasProgress:
- result.add(a)
+ if a.kind != nkEmpty:
+ addSon(result, a)
else:
parMessage(p, errExprExpected, p.tok)
# bugfix: consume a token here to prevent an endless loop:
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 =
+proc parseTopLevelStmt(p: var TParser): PNode =
## Implements an iterator which, when called repeatedly, returns the next
## top-level statement or emptyNode if end of stream.
- result = p.emptyNode
- # progress guaranteed
+ result = ast.emptyNode
while true:
- # nimpretty support here
if p.tok.indent != 0:
if p.firstTok and p.tok.indent < 0: discard
elif p.tok.tokType != tkSemiColon:
- # special casing for better error messages:
- if p.tok.tokType == tkOpr and p.tok.ident.s == "*":
- parMessage(p, errGenerated,
- "invalid indentation; an export marker '*' follows the declared identifier")
- else:
- parMessage(p, errInvalidIndentation)
+ parMessage(p, errInvalidIndentation)
p.firstTok = false
case p.tok.tokType
of tkSemiColon:
@@ -2545,11 +2034,9 @@ 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;
- errorHandler: ErrorHandler = nil): PNode =
+proc parseString*(s: string; filename: string = ""; line: int = 0;
+ errorHandler: TErrorHandler = nil): PNode =
## Parses a string into an AST, returning the top node.
## `filename` and `line`, although optional, provide info so that the
## compiler can generate correct error messages referring to the original
@@ -2557,10 +2044,11 @@ 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: TParser
+ # XXX for now the builtin 'parseStmt/Expr' functions do not know about strong
+ # spaces...
+ parser.lex.errorHandler = errorHandler
+ openParser(parser, filename, stream, false)
- result = p.parseAll
- closeParser(p)
- setEndInfo()
+ result = parser.parseAll
+ closeParser(parser)
diff --git a/compiler/passaux.nim b/compiler/passaux.nim
index af507d210..d4361a671 100644
--- a/compiler/passaux.nim
+++ b/compiler/passaux.nim
@@ -10,24 +10,38 @@
## implements some little helper passes
import
- ast, passes, msgs, options, lineinfos
+ strutils, ast, astalgo, passes, msgs, options, idgen
-from modulegraphs import ModuleGraph, PPassContext
-
-type
- VerboseRef = ref object of PPassContext
- 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
- result = VerboseRef(config: graph.config, idgen: idgen)
-
-import std/objectdollar
+proc verboseOpen(s: PSym): PPassContext =
+ #MessageOut('compiling ' + s.name.s);
+ result = nil # we don't need a context
+ rawMessage(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 context != nil: internalError("logpass: context is not nil")
+ if gVerbosity == 3:
+ # system.nim deactivates all hints, for verbosity:3 we want the processing
+ # messages nonetheless, so we activate them again unconditionally:
+ incl(msgs.gNotes, hintProcessing)
+ message(n.info, hintProcessing, $idgen.gFrontendId)
const verbosePass* = makePass(open = verboseOpen, process = verboseProcess)
+
+proc cleanUp(c: PPassContext, n: PNode): PNode =
+ result = n
+ # we cannot clean up if dead code elimination is activated
+ if optDeadCodeElim in gGlobalOptions or n == nil: return
+ case n.kind
+ of nkStmtList:
+ for i in countup(0, sonsLen(n) - 1): discard cleanUp(c, n.sons[i])
+ of nkProcDef, nkMethodDef:
+ if n.sons[namePos].kind == nkSym:
+ var s = n.sons[namePos].sym
+ if sfDeadCodeElim notin getModule(s).flags and not astNeeded(s):
+ s.ast.sons[bodyPos] = ast.emptyNode # free the memory
+ else:
+ discard
+
+const cleanupPass* = makePass(process = cleanUp, close = cleanUp)
+
diff --git a/compiler/passes.nim b/compiler/passes.nim
index 536a64714..ceb3e2b8a 100644
--- a/compiler/passes.nim
+++ b/compiler/passes.nim
@@ -7,45 +7,77 @@
# distribution, for details about the copyright.
#
-## This module implements the passes functionality. A pass must implement the
-## `TPass` interface.
+# This module implements the passes functionality. A pass must implement the
+# `TPass` interface.
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
+ strutils, lists, options, ast, astalgo, llstream, msgs, platform, os,
+ condsyms, idents, renderer, types, extccomp, math, magicsys, nversion,
+ nimsets, syntaxes, times, rodread, idgen
type
+ TPassContext* = object of RootObj # the pass's context
+ fromCache*: bool # true if created by "openCached"
+
+ PPassContext* = ref TPassContext
+
+ TPassOpen* = proc (module: PSym): PPassContext {.nimcall.}
+ TPassOpenCached* =
+ proc (module: PSym, rd: PRodReader): PPassContext {.nimcall.}
+ TPassClose* = proc (p: PPassContext, n: PNode): PNode {.nimcall.}
+ TPassProcess* = proc (p: PPassContext, topLevelStmt: PNode): PNode {.nimcall.}
+
+ TPass* = tuple[open: TPassOpen, openCached: TPassOpenCached,
+ process: TPassProcess, close: TPassClose]
+
TPassData* = tuple[input: PNode, closeOutput: PNode]
+ TPasses* = openArray[TPass]
# a pass is a tuple of procedure vars ``TPass.close`` may produce additional
# nodes. These are passed to the other close procedures.
# This mechanism used to be used for the instantiation of generics.
proc makePass*(open: TPassOpen = nil,
+ openCached: TPassOpenCached = nil,
process: TPassProcess = nil,
- close: TPassClose = nil,
- isFrontend = false): TPass =
+ close: TPassClose = nil): TPass =
result.open = open
+ result.openCached = openCached
result.close = close
result.process = process
- result.isFrontend = isFrontend
+
+ # This implements a memory preserving scheme: Top level statements are
+ # processed in a pipeline. The compiler never looks at a whole module
+ # any longer. However, this is simple to change, as new passes may perform
+ # whole program optimizations. For now, we avoid it to save a lot of memory.
+proc processModule*(module: PSym, stream: PLLStream, rd: PRodReader)
+
+# the semantic checker needs these:
+var
+ gImportModule*: proc (m: PSym, fileIdx: int32): PSym {.nimcall.}
+ gIncludeFile*: proc (m: PSym, fileIdx: int32): PNode {.nimcall.}
+
+# implementation
+
+proc skipCodegen*(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 = msgs.gErrorCounter > 0
+
+proc astNeeded*(s: PSym): bool =
+ # The ``rodwrite`` module uses this to determine if the body of a proc
+ # needs to be stored. The passes manager frees s.sons[codePos] when
+ # appropriate to free the procedure body's memory. This is important
+ # to keep memory usage down.
+ if (s.kind in {skMethod, skProc}) and
+ ({sfCompilerProc, sfCompileTime} * s.flags == {}) and
+ (s.typ.callConv != ccInline) and
+ (s.ast.sons[genericParamsPos].kind == nkEmpty):
+ result = false
+ # XXX this doesn't really make sense with excessive CTFE
+ else:
+ result = true
const
maxPasses = 10
@@ -53,203 +85,130 @@ const
type
TPassContextArray = array[0..maxPasses - 1, PPassContext]
-proc clearPasses*(g: ModuleGraph) =
- g.passes.setLen(0)
+var
+ gPasses: array[0..maxPasses - 1, TPass]
+ gPassesLen*: int
-proc registerPass*(g: ModuleGraph; p: TPass) =
- internalAssert g.config, g.passes.len < maxPasses
- g.passes.add(p)
+proc clearPasses* =
+ gPassesLen = 0
-proc openPasses(g: ModuleGraph; a: var TPassContextArray;
- module: PSym; idgen: IdGenerator) =
- 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..2336e44e7 100644
--- a/compiler/patterns.nim
+++ b/compiler/patterns.nim
@@ -11,10 +11,8 @@
## macro support.
import
- ast, types, semdata, sigmatch, idents, aliases, parampatterns, trees
-
-when defined(nimPreviewSlimSystem):
- import std/assertions
+ ast, astalgo, types, semdata, sigmatch, msgs, idents, aliases, parampatterns,
+ trees
type
TPatternContext = object
@@ -23,34 +21,31 @@ type
formals: int
c: PContext
subMatch: bool # subnode matches are special
- mappingIsFull: bool
PPatternContext = var TPatternContext
proc getLazy(c: PPatternContext, sym: PSym): PNode =
- if c.mappingIsFull:
+ if not isNil(c.mapping):
result = c.mapping[sym.position]
proc putLazy(c: PPatternContext, sym: PSym, n: PNode) =
- if not c.mappingIsFull:
- newSeq(c.mapping, c.formals)
- c.mappingIsFull = true
+ if isNil(c.mapping): newSeq(c.mapping, c.formals)
c.mapping[sym.position] = n
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.} =
result = a.kind == b.kind or a.canonKind == b.canonKind
-proc sameTrees*(a, b: PNode): bool =
+proc sameTrees(a, b: PNode): bool =
if sameKinds(a, b):
case a.kind
of nkSym: result = a.sym == b.sym
@@ -61,18 +56,18 @@ proc sameTrees*(a, b: PNode): bool =
of nkEmpty, nkNilLit: result = true
of nkType: result = sameTypeOrNil(a.typ, b.typ)
else:
- if a.len == b.len:
- for i in 0..= 2:
- for i in 1.. 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..dc414bfeb 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,
- osAmiga, osAtari, osNetware, osMacos, osMacosx, osIos, osHaiku, osAndroid, osVxWorks
- osGenode, osJS, osNimVM, osStandalone, osNintendoSwitch, osFreeRTOS, osZephyr,
- osNuttX, osAny
+ osIrix, osNetbsd, osFreebsd, osOpenbsd, osAix, osPalmos, osQnx, osAmiga,
+ osAtari, osNetware, osMacos, osMacosx, osHaiku, osVxworks,
+ osJS, osNimrodVM, osStandalone
type
TInfoOSProp* = enum
@@ -103,17 +98,6 @@ const
scriptExt: ".sh", curDir: ".",
exeExt: "", extSep: ".",
props: {ospNeedsPIC, ospPosix}),
- (name: "DragonFly", parDir: "..",
- dllFrmt: "lib$1.so", altDirSep: "/",
- objExt: ".o", newLine: "\x0A",
- pathSep: ":", dirSep: "/",
- 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: ".",
@@ -148,77 +132,43 @@ const
objExt: ".o", newLine: "\x0A", pathSep: ":", dirSep: "/",
scriptExt: ".sh", curDir: ".", exeExt: "", extSep: ".",
props: {ospNeedsPIC, ospPosix, ospLacksThreadVars}),
- (name: "iOS", parDir: "..", dllFrmt: "lib$1.so", altDirSep: "/",
- objExt: ".o", newLine: "\x0A", pathSep: ":", dirSep: "/",
- scriptExt: ".sh", curDir: ".", exeExt: "", extSep: ".",
- props: {ospNeedsPIC, ospPosix}),
(name: "Haiku", parDir: "..", dllFrmt: "lib$1.so", altDirSep: ":",
objExt: ".o", newLine: "\x0A", pathSep: ":", dirSep: "/",
scriptExt: ".sh", curDir: ".", exeExt: "", extSep: ".",
props: {ospNeedsPIC, ospPosix, ospLacksThreadVars}),
- (name: "Android", parDir: "..", dllFrmt: "lib$1.so", altDirSep: "/",
- objExt: ".o", newLine: "\x0A", pathSep: ":", dirSep: "/",
- scriptExt: ".sh", curDir: ".", exeExt: "", extSep: ".",
- props: {ospNeedsPIC, ospPosix}),
(name: "VxWorks", parDir: "..", dllFrmt: "lib$1.so", altDirSep: "/",
objExt: ".o", newLine: "\x0A", pathSep: ";", dirSep: "\\",
scriptExt: ".sh", curDir: ".", exeExt: ".vxe", extSep: ".",
props: {ospNeedsPIC, ospPosix, ospLacksThreadVars}),
- (name: "Genode", pardir: "..", dllFrmt: "$1.lib.so", altDirSep: "/",
- objExt: ".o", newLine: "\x0A", pathSep: ":", dirSep: "/",
- scriptExt: "", curDir: "/", exeExt: "", extSep: ".",
- props: {ospNeedsPIC, ospLacksThreadVars}),
-
(name: "JS", parDir: "..",
dllFrmt: "lib$1.so", altDirSep: "/",
objExt: ".o", newLine: "\x0A",
pathSep: ":", dirSep: "/",
scriptExt: ".sh", curDir: ".",
exeExt: "", extSep: ".", props: {}),
- (name: "NimVM", parDir: "..", dllFrmt: "lib$1.so", altDirSep: "/",
+ (name: "NimrodVM", parDir: "..", dllFrmt: "lib$1.so", altDirSep: "/",
objExt: ".o", newLine: "\x0A", pathSep: ":", dirSep: "/",
scriptExt: ".sh", curDir: ".", exeExt: "", extSep: ".", props: {}),
(name: "Standalone", parDir: "..", dllFrmt: "lib$1.so", altDirSep: "/",
objExt: ".o", newLine: "\x0A", pathSep: ":", dirSep: "/",
scriptExt: ".sh", curDir: ".", exeExt: "", extSep: ".",
- props: {}),
- (name: "NintendoSwitch", parDir: "..", dllFrmt: "lib$1.so", altDirSep: "/",
- objExt: ".o", newLine: "\x0A", pathSep: ":", dirSep: "/",
- scriptExt: ".sh", curDir: ".", exeExt: ".elf", extSep: ".",
- props: {ospNeedsPIC, ospPosix}),
- (name: "FreeRTOS", parDir: "..", dllFrmt: "lib$1.so", altDirSep: "/",
- 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: ".",
- props: {}),
- ]
+ props: {})]
type
TSystemCPU* = enum # Also add CPU for in initialization section and
# alias conditionals to condsyms (end of module).
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
+ cpuPowerpc64el, cpuSparc, cpuVm, cpuIa64, cpuAmd64, cpuMips, cpuMipsel,
+ cpuArm, cpuArm64, cpuJS, cpuNimrodVM, cpuAVR, cpuMSP430
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),
@@ -228,69 +178,55 @@ const
(name: "powerpc64el", intSize: 64, endian: littleEndian, floatSize: 64,bit: 64),
(name: "sparc", intSize: 32, endian: bigEndian, floatSize: 64, bit: 32),
(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: "nimvm", intSize: 32, endian: bigEndian, floatSize: 64, bit: 32),
- # xxx this seems buggy; on a 64bit machine, sizeof(int) is 64 in nimvm.
+ (name: "js", intSize: 32, endian: bigEndian,floatSize: 64,bit: 32),
+ (name: "nimrodvm", intSize: 32, endian: bigEndian, floatSize: 64, bit: 32),
(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),
- (name: "mips64", intSize: 64, endian: bigEndian, floatSize: 64, bit: 64),
- (name: "mips64el", intSize: 64, endian: littleEndian, floatSize: 64, bit: 64),
- (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: "msp430", intSize: 16, endian: littleEndian, floatSize: 32, bit: 16)]
-type
- Target* = object
- targetCPU*, hostCPU*: TSystemCPU
- targetOS*, hostOS*: TSystemOS
- intSize*: int
- floatSize*: int
- ptrSize*: int
- tnl*: string # target newline
+var
+ targetCPU*, hostCPU*: TSystemCPU
+ targetOS*, hostOS*: TSystemOS
-proc setTarget*(t: var Target; o: TSystemOS, c: TSystemCPU) =
+proc nameToOS*(name: string): TSystemOS
+proc nameToCPU*(name: string): TSystemCPU
+
+var
+ intSize*: int
+ floatSize*: int
+ ptrSize*: int
+ tnl*: string # target newline
+
+proc setTarget*(o: TSystemOS, c: TSystemCPU) =
assert(c != cpuNone)
assert(o != osNone)
#echo "new Target: OS: ", o, " CPU: ", c
- t.targetCPU = c
- t.targetOS = o
- t.intSize = CPU[c].intSize div 8
- t.floatSize = CPU[c].floatSize div 8
- t.ptrSize = CPU[c].bit div 8
- t.tnl = OS[o].newLine
+ targetCPU = c
+ targetOS = o
+ intSize = CPU[c].intSize div 8
+ floatSize = CPU[c].floatSize div 8
+ ptrSize = CPU[c].bit div 8
+ tnl = OS[o].newLine
-proc nameToOS*(name: string): TSystemOS =
- for i in succ(osNone)..high(TSystemOS):
+proc nameToOS(name: string): TSystemOS =
+ for i in countup(succ(osNone), high(TSystemOS)):
if cmpIgnoreStyle(name, OS[i].name) == 0:
return i
result = osNone
-proc listOSnames*(): seq[string] =
- for i in succ(osNone)..high(TSystemOS):
- result.add OS[i].name
-
-proc nameToCPU*(name: string): TSystemCPU =
- for i in succ(cpuNone)..high(TSystemCPU):
+proc nameToCPU(name: string): TSystemCPU =
+ for i in countup(succ(cpuNone), high(TSystemCPU)):
if cmpIgnoreStyle(name, CPU[i].name) == 0:
return i
result = cpuNone
-proc listCPUnames*(): seq[string] =
- for i in succ(cpuNone)..high(TSystemCPU):
- result.add CPU[i].name
+hostCPU = nameToCPU(system.hostCPU)
+hostOS = nameToOS(system.hostOS)
+
+setTarget(hostOS, hostCPU) # assume no cross-compiling
-proc setTargetFromSystem*(t: var Target) =
- t.hostOS = nameToOS(system.hostOS)
- t.hostCPU = nameToCPU(system.hostCPU)
- t.setTarget(t.hostOS, t.hostCPU)
diff --git a/compiler/plugins/active.nim b/compiler/plugins/active.nim
index 19c320aae..7b6411178 100644
--- a/compiler/plugins/active.nim
+++ b/compiler/plugins/active.nim
@@ -10,15 +10,4 @@
## Include file that imports all plugins that are active.
import
- ".." / [pluginsupport, idents, ast], locals, itersgen
-
-const
- plugins: array[2, Plugin] = [
- ("stdlib", "system", "iterToProc", iterToProcImpl),
- ("stdlib", "system", "locals", semLocals)
- ]
-
-proc getPlugin*(ic: IdentCache; fn: PSym): Transformation =
- for p in plugins:
- if pluginMatches(ic, p, fn): return p.t
- return nil
+ locals.locals, itersgen
diff --git a/compiler/plugins/itersgen.nim b/compiler/plugins/itersgen.nim
index 24e26b2b7..f44735b77 100644
--- a/compiler/plugins/itersgen.nim
+++ b/compiler/plugins/itersgen.nim
@@ -9,38 +9,43 @@
## Plugin to transform an inline iterator into a data structure.
-import ".." / [ast, modulegraphs, lookups, semdata, lambdalifting, msgs]
+import compiler/pluginsupport, compiler/ast, compiler/astalgo,
+ compiler/magicsys, compiler/lookups, compiler/semdata,
+ compiler/lambdalifting, compiler/rodread, compiler/msgs
-proc iterToProcImpl*(c: PContext, n: PNode): PNode =
+
+proc iterToProcImpl(c: PContext, n: PNode): PNode =
result = newNodeI(nkStmtList, n.info)
let iter = n[1]
if iter.kind != nkSym or iter.sym.kind != skIterator:
- localError(c.config, iter.info, "first argument needs to be an iterator")
+ localError(iter.info, "first argument needs to be an iterator")
return
if n[2].typ.isNil:
- localError(c.config, n[2].info, "second argument needs to be a type")
+ localError(n[2].info, "second argument needs to be a type")
return
if n[3].kind != nkIdent:
- localError(c.config, n[3].info, "third argument needs to be an identifier")
+ localError(n[3].info, "third argument needs to be an identifier")
return
let t = n[2].typ.skipTypes({tyTypeDesc, tyGenericInst})
if t.kind notin {tyRef, tyPtr} or t.lastSon.kind != tyObject:
- localError(c.config, n[2].info,
+ localError(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(iter.sym, iter.sym.getBody, t)
- 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)
+ let prc = newSym(skProc, n[3].ident, iter.sym.owner, iter.sym.info)
+ prc.typ = copyType(iter.sym.typ, prc, false)
excl prc.typ.flags, tfCapturesEnv
prc.typ.n.add newSymNode(getEnvParam(iter.sym))
prc.typ.rawAddSon t
let orig = iter.sym.ast
prc.ast = newProcNode(nkProcDef, n.info,
- body = body, params = orig[paramsPos], name = newSymNode(prc),
- pattern = c.graph.emptyNode, genericParams = c.graph.emptyNode,
- pragmas = orig[pragmasPos], exceptions = c.graph.emptyNode)
-
- prc.ast.add iter.sym.ast[resultPos]
+ name = newSymNode(prc),
+ params = orig[paramsPos],
+ pragmas = orig[pragmasPos],
+ body = body)
+ prc.ast.add iter.sym.ast.sons[resultPos]
addInterfaceDecl(c, prc)
+
+registerPlugin("stdlib", "system", "iterToProc", iterToProcImpl)
diff --git a/compiler/plugins/locals.nim b/compiler/plugins/locals.nim
deleted file mode 100644
index 384780f7b..000000000
--- a/compiler/plugins/locals.nim
+++ /dev/null
@@ -1,39 +0,0 @@
-#
-#
-# The Nim Compiler
-# (c) Copyright 2015 Andreas Rumpf
-#
-# See the file "copying.txt", included in this
-# distribution, for details about the copyright.
-#
-
-## The builtin 'system.locals' implemented as a plugin.
-
-import ".." / [ast, astalgo,
- magicsys, lookups, semdata, lowerings]
-
-proc semLocals*(c: PContext, n: PNode): PNode =
- var counter = 0
- var tupleType = newTypeS(tyTuple, c)
- result = newNodeIT(nkTupleConstr, n.info, tupleType)
- tupleType.n = newNodeI(nkRecList, n.info)
- let owner = getCurrOwner(c)
- # for now we skip openarrays ...
- for scope in localScopesFrom(c, c.currentScope):
- for it in items(scope.symbols):
- if it.kind in skLocalVars and
- it.typ.skipTypes({tyGenericInst, tyVar}).kind notin
- {tyVarargs, tyOpenArray, tyTypeDesc, tyStatic, tyUntyped, tyTyped, tyEmpty}:
-
- if it.owner == owner:
- var field = newSym(skField, it.name, nextSymId c.idgen, owner, n.info)
- field.typ = it.typ.skipTypes({tyVar})
- field.position = counter
- inc(counter)
-
- tupleType.n.add newSymNode(field)
- addSonSkipIntLit(tupleType, field.typ, c.idgen)
-
- var a = newSymNode(it, result.info)
- if it.typ.skipTypes({tyGenericInst}).kind == tyVar: a = newDeref(a)
- result.add(a)
diff --git a/compiler/plugins/locals/locals.nim b/compiler/plugins/locals/locals.nim
new file mode 100644
index 000000000..8a3f67dd4
--- /dev/null
+++ b/compiler/plugins/locals/locals.nim
@@ -0,0 +1,43 @@
+#
+#
+# The Nim Compiler
+# (c) Copyright 2015 Andreas Rumpf
+#
+# See the file "copying.txt", included in this
+# distribution, for details about the copyright.
+#
+
+## The builtin 'system.locals' implemented as a plugin.
+
+import compiler/pluginsupport, compiler/ast, compiler/astalgo,
+ compiler/magicsys, compiler/lookups, compiler/semdata, compiler/lowerings
+
+proc semLocals(c: PContext, n: PNode): PNode =
+ var counter = 0
+ var tupleType = newTypeS(tyTuple, c)
+ result = newNodeIT(nkPar, n.info, tupleType)
+ tupleType.n = newNodeI(nkRecList, n.info)
+ # for now we skip openarrays ...
+ for scope in walkScopes(c.currentScope):
+ if scope == c.topLevelScope: break
+ for it in items(scope.symbols):
+ # XXX parameters' owners are wrong for generics; this caused some pain
+ # for closures too; we should finally fix it.
+ #if it.owner != c.p.owner: return result
+ if it.kind in skLocalVars and
+ it.typ.skipTypes({tyGenericInst, tyVar}).kind notin
+ {tyVarargs, tyOpenArray, tyTypeDesc, tyStatic, tyExpr, tyStmt, tyEmpty}:
+
+ var field = newSym(skField, it.name, getCurrOwner(), n.info)
+ field.typ = it.typ.skipTypes({tyGenericInst, tyVar})
+ field.position = counter
+ inc(counter)
+
+ addSon(tupleType.n, newSymNode(field))
+ addSonSkipIntLit(tupleType, field.typ)
+
+ var a = newSymNode(it, result.info)
+ if it.typ.skipTypes({tyGenericInst}).kind == tyVar: a = newDeref(a)
+ result.add(a)
+
+registerPlugin("stdlib", "system", "locals", semLocals)
diff --git a/compiler/pluginsupport.nim b/compiler/pluginsupport.nim
index a44436f11..19a0bc84d 100644
--- a/compiler/pluginsupport.nim
+++ b/compiler/pluginsupport.nim
@@ -7,27 +7,40 @@
# distribution, for details about the copyright.
#
-## Plugin support for the Nim compiler. Right now plugins
-## need to be built with the compiler only: plugins using
-## DLLs or the FFI will not work.
+## Plugin support for the Nim compiler. Right now they
+## need to be build with the compiler, no DLL support.
import ast, semdata, idents
type
Transformation* = proc (c: PContext; n: PNode): PNode {.nimcall.}
- Plugin* = tuple
- package, module, fn: string
+ Plugin = ref object
+ fn, module, package: PIdent
t: Transformation
+ next: Plugin
-proc pluginMatches*(ic: IdentCache; p: Plugin; s: PSym): bool =
- if s.name.id != ic.getIdent(p.fn).id:
+proc pluginMatches(p: Plugin; s: PSym): bool =
+ if s.name.id != p.fn.id:
return false
let module = s.skipGenericOwner
if module == nil or module.kind != skModule or
- module.name.id != ic.getIdent(p.module).id:
+ module.name.id != p.module.id:
return false
let package = module.owner
if package == nil or package.kind != skPackage or
- package.name.id != ic.getIdent(p.package).id:
+ package.name.id != p.package.id:
return false
return true
+
+var head: Plugin
+
+proc getPlugin*(fn: PSym): Transformation =
+ var it = head
+ while it != nil:
+ if pluginMatches(it, fn): return it.t
+ it = it.next
+
+proc registerPlugin*(package, module, fn: string; t: Transformation) =
+ let oldHead = head
+ head = Plugin(fn: getIdent(fn), module: getIdent(module),
+ package: getIdent(package), t: t, next: oldHead)
diff --git a/compiler/pragmas.nim b/compiler/pragmas.nim
index 97182abab..dc618d9aa 100644
--- a/compiler/pragmas.nim
+++ b/compiler/pragmas.nim
@@ -10,230 +10,169 @@
# This module implements semantic checking for pragmas
import
- os, condsyms, ast, astalgo, idents, semdata, msgs, renderer,
- wordrecg, ropes, options, strutils, extccomp, math, magicsys, trees,
- types, lookups, lineinfos, pathutils, linter
-
-when defined(nimPreviewSlimSystem):
- import std/assertions
-
-from ic / ic import addCompilerProc
+ os, platform, condsyms, ast, astalgo, idents, semdata, msgs, renderer,
+ wordrecg, ropes, options, strutils, lists, extccomp, math, magicsys, trees,
+ rodread, types, lookups
const
FirstCallConv* = wNimcall
LastCallConv* = wNoconv
const
- declPragmas = {wImportc, wImportObjC, wImportCpp, wImportJs, wExportc, wExportCpp,
- wExportNims, wExtern, wDeprecated, wNodecl, wError, wUsed}
- ## 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,
- wConstructor, wLiftLocals, wStackTrace, wLineTrace, wNoDestroy,
- wRequires, wEnsures, wEnforceNoRaises, wSystemRaisesDefect}
+ procPragmas* = {FirstCallConv..LastCallConv, wImportc, wExportc, wNodecl,
+ wMagic, wNosideeffect, wSideeffect, wNoreturn, wDynlib, wHeader,
+ wCompilerproc, wProcVar, wDeprecated, wVarargs, wCompileTime, wMerge,
+ wBorrow, wExtern, wImportCompilerProc, wThread, wImportCpp, wImportObjC,
+ wAsmNoStackFrame, wError, wDiscardable, wNoInit, wDestructor, wCodegenDecl,
+ wGensym, wInject, wRaises, wTags, wLocks, wDelegator, wGcSafe,
+ wOverride, wConstructor, wExportNims}
converterPragmas* = procPragmas
- methodPragmas* = procPragmas+{wBase}-{wImportCpp}
- templatePragmas* = {wDeprecated, wError, wGensym, wInject, wDirty,
- wDelegator, wExportNims, wUsed, wPragma, wRedefine, wCallsite}
- 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}
- exprPragmas* = {wLine, wLocks, wNoRewrite, wGcSafe, wNoSideEffect}
- stmtPragmas* = {
- wHint, wWarning, wError,
+ methodPragmas* = procPragmas+{wBase}
+ templatePragmas* = {wImmediate, wDeprecated, wError, wGensym, wInject, wDirty,
+ wDelegator, wExportNims}
+ macroPragmas* = {FirstCallConv..LastCallConv, wImmediate, wImportc, wExportc,
+ wNodecl, wMagic, wNosideeffect, wCompilerproc, wDeprecated, wExtern,
+ wImportCpp, wImportObjC, wError, wDiscardable, wGensym, wInject, wDelegator,
+ wExportNims}
+ iteratorPragmas* = {FirstCallConv..LastCallConv, wNosideeffect, wSideeffect,
+ wImportc, wExportc, wNodecl, wMagic, wDeprecated, wBorrow, wExtern,
+ wImportCpp, wImportObjC, wError, wDiscardable, wGensym, wInject, wRaises,
+ wTags, wLocks, wGcSafe, wExportNims}
+ exprPragmas* = {wLine, wLocks, wNoRewrite}
+ stmtPragmas* = {wChecks, wObjChecks, wFieldChecks, wRangechecks,
+ wBoundchecks, wOverflowchecks, wNilchecks, 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,
- 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,
- wNoSideEffect, wSideEffect, wNoreturn, wNosinks, wDynlib, wHeader,
- wThread, wAsmNoStackFrame,
- wRaises, wLocks, wTags, wForbids, wRequires, wEnsures, wEffectsOf,
- wGcSafe, wCodegenDecl, wNoInit, wCompileTime}
- typePragmas* = declPragmas + {wMagic, wAcyclic,
- wPure, wHeader, wCompilerProc, wCore, wFinal, wSize, wShallow,
- wIncompleteStruct, wCompleteStruct, wByCopy, wByRef,
+ wBreakpoint, wWatchPoint, wPassl, wPassc, wDeadCodeElim, wDeprecated,
+ wFloatchecks, wInfChecks, wNanChecks, wPragma, wEmit, wUnroll,
+ wLinearScanEnd, wPatterns, wEffects, wNoForward, wComputedGoto,
+ wInjectStmt, wDeprecated, wExperimental, wThis}
+ lambdaPragmas* = {FirstCallConv..LastCallConv, wImportc, wExportc, wNodecl,
+ wNosideeffect, wSideeffect, wNoreturn, wDynlib, wHeader,
+ wDeprecated, wExtern, wThread, wImportCpp, wImportObjC, wAsmNoStackFrame,
+ wRaises, wLocks, wTags, wGcSafe}
+ typePragmas* = {wImportc, wExportc, wDeprecated, wMagic, wAcyclic, wNodecl,
+ wPure, wHeader, wCompilerproc, wFinal, wSize, wExtern, wShallow,
+ wImportCpp, wImportObjC, wError, wIncompleteStruct, 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?
- varPragmas* = declPragmas + {wVolatile, wRegister, wThreadVar,
- wMagic, wHeader, wCompilerProc, wCore, wDynlib,
- wNoInit, wCompileTime, wGlobal,
- wGensym, wInject, wCodegenDecl,
- wGuard, wGoto, wCursor, wNoalias, wAlign}
- constPragmas* = declPragmas + {wHeader, wMagic,
- wGensym, wInject,
- wIntDefine, wStrDefine, wBoolDefine, wDefine,
- wCompilerProc, wCore}
- paramPragmas* = {wNoalias, wInject, wGensym}
+ wBorrow, wGcSafe, wExportNims, wPartial}
+ fieldPragmas* = {wImportc, wExportc, wDeprecated, wExtern,
+ wImportCpp, wImportObjC, wError, wGuard, wBitsize}
+ varPragmas* = {wImportc, wExportc, wVolatile, wRegister, wThreadVar, wNodecl,
+ wMagic, wHeader, wDeprecated, wCompilerproc, wDynlib, wExtern,
+ wImportCpp, wImportObjC, wError, wNoInit, wCompileTime, wGlobal,
+ wGensym, wInject, wCodegenDecl, wGuard, wGoto, wExportNims}
+ constPragmas* = {wImportc, wExportc, wHeader, wDeprecated, wMagic, wNodecl,
+ wExtern, wImportCpp, wImportObjC, wError, wGensym, wInject, wExportNims}
letPragmas* = varPragmas
- procTypePragmas* = {FirstCallConv..LastCallConv, wVarargs, wNoSideEffect,
- wThread, wRaises, wEffectsOf, wLocks, wTags, wForbids, wGcSafe,
- wRequires, wEnsures}
- forVarPragmas* = {wInject, wGensym}
- allRoutinePragmas* = methodPragmas + iteratorPragmas + lambdaPragmas
- enumFieldPragmas* = {wDeprecated}
+ procTypePragmas* = {FirstCallConv..LastCallConv, wVarargs, wNosideeffect,
+ wThread, wRaises, wLocks, wTags, wGcSafe}
+ allRoutinePragmas* = procPragmas + iteratorPragmas + lambdaPragmas
-proc getPragmaVal*(procAst: PNode; name: TSpecialWord): PNode =
- let p = procAst[pragmasPos]
- if p.kind == nkEmpty: return nil
- for it in p:
- if it.kind in nkPragmaCallKinds and it.len == 2 and it[0].kind == nkIdent and
- it[0].ident.id == ord(name):
- return it[1]
+proc pragma*(c: PContext, sym: PSym, n: PNode, validPragmas: TSpecialWords)
+# implementation
-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)
- for i in 0..args.high:
- recorded.add newStrNode(args[i], n.info)
- addPragmaComputation(c, recorded)
-
-const
- errStringLiteralExpected = "string literal expected"
- errIntLiteralExpected = "integer literal expected"
-
-proc invalidPragma*(c: PContext; n: PNode) =
- localError(c.config, n.info, "invalid pragma: " & renderTree(n, {renderNoComments}))
-proc illegalCustomPragma*(c: PContext, n: PNode, s: PSym) =
- localError(c.config, n.info, "cannot attach a custom pragma to '" & s.name.s & "'")
-
-proc pragmaProposition(c: PContext, n: PNode) =
- if n.kind notin nkPragmaCallKinds or n.len != 2:
- localError(c.config, n.info, "proposition expected")
- else:
- n[1] = c.semExpr(c, n[1])
-
-proc pragmaEnsures(c: PContext, n: PNode) =
- if n.kind notin nkPragmaCallKinds or n.len != 2:
- localError(c.config, n.info, "proposition expected")
- else:
- openScope(c)
- let o = getCurrOwner(c)
- if o.kind in routineKinds and o.typ != nil and o.typ.sons[0] != nil:
- var s = newSym(skResult, getIdent(c.cache, "result"), nextSymId(c.idgen), o, n.info)
- s.typ = o.typ.sons[0]
- incl(s.flags, sfUsed)
- addDecl(c, s)
- n[1] = c.semExpr(c, n[1])
- closeScope(c)
+proc invalidPragma(n: PNode) =
+ localError(n.info, errInvalidPragmaX, renderTree(n, {renderNoComments}))
proc pragmaAsm*(c: PContext, n: PNode): char =
result = '\0'
if n != nil:
- for i in 0..= 0: nk = TNoteKind(x + ord(hintMin))
+ else: invalidPragma(n); return
+ of wWarning:
+ var x = findStr(msgs.WarningsToStr, n.sons[0].sons[1].ident.s)
+ if x >= 0: nk = TNoteKind(x + ord(warnMin))
+ else: invalidPragma(n); return
else:
- localError(c.config, n.info, errStringLiteralExpected)
+ invalidPragma(n)
+ return
-proc tryProcessOption(c: PContext, n: PNode, resOptions: var TOptions): bool =
- result = true
- if n.kind notin nkPragmaCallKinds or n.len != 2: result = false
- elif n[0].kind == nkBracketExpr: processNote(c, n)
- elif n[0].kind != nkIdent: result = false
+ let x = c.semConstBoolExpr(c, n.sons[1])
+ n.sons[1] = x
+ if x.kind == nkIntLit and x.intVal != 0: incl(gNotes, nk)
+ else: excl(gNotes, nk)
else:
- let sw = whichKeyword(n[0].ident)
- if sw == wExperimental:
- processExperimental(c, n)
- return true
- let opts = pragmaToOptions(sw)
- if opts != {}:
- onOff(c, n, opts, resOptions)
- else:
- case sw
- of wCallconv: processCallConv(c, n)
- of wDynlib: processDynLib(c, n, nil)
- of wOptimization:
- if n[1].kind != nkIdent:
- invalidPragma(c, n)
- else:
- case n[1].ident.s.normalize
- of "speed":
- incl(resOptions, optOptimizeSpeed)
- excl(resOptions, optOptimizeSize)
- of "size":
- excl(resOptions, optOptimizeSpeed)
- incl(resOptions, optOptimizeSize)
- of "none":
- excl(resOptions, optOptimizeSpeed)
- excl(resOptions, optOptimizeSize)
- else: localError(c.config, n.info, "'none', 'speed' or 'size' expected")
- else: result = false
+ invalidPragma(n)
-proc processOption(c: PContext, n: PNode, resOptions: var TOptions) =
- if not tryProcessOption(c, n, resOptions):
- # calling conventions (boring...):
- localError(c.config, n.info, "option expected")
+proc processOption(c: PContext, n: PNode): bool =
+ if n.kind != nkExprColonExpr: result = true
+ elif n.sons[0].kind == nkBracketExpr: processNote(c, n)
+ elif n.sons[0].kind != nkIdent: result = true
+ else:
+ var sw = whichKeyword(n.sons[0].ident)
+ case sw
+ of wChecks: onOff(c, n, ChecksOptions)
+ of wObjChecks: onOff(c, n, {optObjCheck})
+ of wFieldChecks: onOff(c, n, {optFieldCheck})
+ of wRangechecks: onOff(c, n, {optRangeCheck})
+ of wBoundchecks: onOff(c, n, {optBoundsCheck})
+ of wOverflowchecks: onOff(c, n, {optOverflowCheck})
+ of wNilchecks: onOff(c, n, {optNilCheck})
+ of wFloatchecks: onOff(c, n, {optNaNCheck, optInfCheck})
+ of wNanChecks: onOff(c, n, {optNaNCheck})
+ of wInfChecks: onOff(c, n, {optInfCheck})
+ of wAssertions: onOff(c, n, {optAssert})
+ of wWarnings: onOff(c, n, {optWarns})
+ of wHints: onOff(c, n, {optHints})
+ of wCallconv: processCallConv(c, n)
+ of wLinedir: onOff(c, n, {optLineDir})
+ of wStacktrace: onOff(c, n, {optStackTrace})
+ of wLinetrace: onOff(c, n, {optLineTrace})
+ of wDebugger: onOff(c, n, {optEndb})
+ of wProfiler: onOff(c, n, {optProfiler})
+ of wByRef: onOff(c, n, {optByRef})
+ of wDynlib: processDynLib(c, n, nil)
+ of wOptimization:
+ if n.sons[1].kind != nkIdent:
+ invalidPragma(n)
+ else:
+ case n.sons[1].ident.s.normalize
+ of "speed":
+ incl(gOptions, optOptimizeSpeed)
+ excl(gOptions, optOptimizeSize)
+ of "size":
+ excl(gOptions, optOptimizeSpeed)
+ incl(gOptions, optOptimizeSize)
+ of "none":
+ excl(gOptions, optOptimizeSpeed)
+ excl(gOptions, optOptimizeSize)
+ else: localError(n.info, errNoneSpeedOrSizeExpected)
+ of wImplicitStatic: onOff(c, n, {optImplicitStatic})
+ of wPatterns: onOff(c, n, {optPatterns})
+ else: result = true
proc processPush(c: PContext, n: PNode, start: int) =
- if n[start-1].kind in nkPragmaCallKinds:
- localError(c.config, n.info, "'push' cannot have arguments")
- var x = pushOptionEntry(c)
- for i in start.. 0 and splitFile(s).ext == "":
s = addFileExt(s, ext)
- result = AbsoluteFile parentDir(toFullPath(c.config, n.info)) / s
+ result = parentDir(n.info.toFullPath) / s
if not fileExists(result):
- if isAbsolute(s): result = AbsoluteFile s
- else:
- result = findFile(c.config, s)
- if result.isEmpty: result = AbsoluteFile s
+ if isAbsolute(s): result = s
+ else: result = findFile(s)
proc processCompile(c: PContext, n: PNode) =
- proc docompile(c: PContext; it: PNode; src, dest: AbsoluteFile; customArgs: string) =
- 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)
+ let found = relativeFile(c, n)
+ let trunc = found.changeFileExt("")
+ extccomp.addExternalFileToCompile(found)
+ extccomp.addFileToLink(completeCFilePath(trunc, false))
- 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)
- else:
- localError(c.config, n.info, errStringLiteralExpected)
- result = ""
+proc processCommonLink(c: PContext, n: PNode, feature: TLinkFeature) =
+ let found = relativeFile(c, n, CC[cCompiler].objExt)
+ case feature
+ of linkNormal: extccomp.addFileToLink(found)
+ of linkSys:
+ extccomp.addFileToLink(libpath / completeCFilePath(found, false))
+ else: internalError(n.info, "processCommonLink")
- let it = if n.kind in nkPragmaCallKinds and n.len == 2: n[1] else: n
- if it.kind in {nkPar, nkTupleConstr} and it.len == 2:
- let s = getStrLit(c, it, 0)
- let dest = getStrLit(c, it, 1)
- var found = parentDir(toFullPath(c.config, n.info)) / s
- for f in os.walkFiles(found):
- let obj = completeCfilePath(c.config, AbsoluteFile(dest % extractFilename(f)))
- docompile(c, it, AbsoluteFile f, obj, "")
+proc pragmaBreakpoint(c: PContext, n: PNode) =
+ discard getOptionalStr(c, n, "")
+
+proc pragmaWatchpoint(c: PContext, n: PNode) =
+ if n.kind == nkExprColonExpr:
+ n.sons[1] = c.semExpr(c, n.sons[1])
else:
- var s = ""
- var customArgs = ""
- if n.kind in nkCallKinds:
- s = getStrLit(c, n, 1)
- if n.len <= 3:
- customArgs = getStrLit(c, n, 2)
- else:
- localError(c.config, n.info, "'.compile' pragma takes up 2 arguments")
- else:
- s = expectStrLit(c, n)
-
- var found = AbsoluteFile(parentDir(toFullPath(c.config, n.info)) / s)
- if not fileExists(found):
- if isAbsolute(s): found = AbsoluteFile s
- else:
- found = findFile(c.config, s)
- if found.isEmpty: found = AbsoluteFile s
- let obj = toObjFile(c.config, completeCfilePath(c.config, found, false))
- docompile(c, it, found, obj, customArgs)
-
-proc processLink(c: PContext, n: PNode) =
- let found = relativeFile(c, n, CC[c.config.cCompiler].objExt)
- extccomp.addExternalFileToLink(c.config, found)
- recordPragma(c, n, "link", found.string)
+ invalidPragma(n)
proc semAsmOrEmit*(con: PContext, n: PNode, marker: char): PNode =
- case n[1].kind
+ case n.sons[1].kind
of nkStrLit, nkRStrLit, nkTripleStrLit:
- result = newNodeI(if n.kind == nkAsmStmt: nkAsmStmt else: nkArgList, n.info)
- var str = n[1].strVal
+ result = newNode(if n.kind == nkAsmStmt: nkAsmStmt else: nkArgList, n.info)
+ var str = n.sons[1].strVal
if str == "":
- localError(con.config, n.info, "empty 'asm' statement")
+ localError(n.info, errEmptyAsm)
return
# now parse the string literal and substitute symbols:
var a = 0
while true:
var b = strutils.find(str, marker, a)
var sub = if b < 0: substr(str, a) else: substr(str, a, b - 1)
- if sub != "": result.add newStrNode(nkStrLit, sub)
+ if sub != "": addSon(result, newStrNode(nkStrLit, sub))
if b < 0: break
var c = strutils.find(str, marker, b + 1)
if c < 0: sub = substr(str, b + 1)
else: sub = substr(str, b + 1, c - 1)
if sub != "":
- var amb = false
- var e = searchInScopes(con, getIdent(con.cache, sub), amb)
- # XXX what to do here if 'amb' is true?
+ var e = searchInScopes(con, getIdent(sub))
if e != nil:
+ if e.kind == skStub: loadStub(e)
incl(e.flags, sfUsed)
- result.add newSymNode(e)
+ addSon(result, newSymNode(e))
else:
- result.add newStrNode(nkStrLit, sub)
+ addSon(result, newStrNode(nkStrLit, sub))
else:
# an empty '``' produces a single '`'
- result.add newStrNode(nkStrLit, $marker)
+ addSon(result, newStrNode(nkStrLit, $marker))
if c < 0: break
a = c + 1
else:
- illFormedAstLocal(n, con.config)
- result = newNodeI(nkAsmStmt, n.info)
+ illFormedAstLocal(n)
+ result = newNode(nkAsmStmt, n.info)
proc pragmaEmit(c: PContext, n: PNode) =
- if n.kind notin nkPragmaCallKinds or n.len != 2:
- localError(c.config, n.info, errStringLiteralExpected)
- else:
- let n1 = n[1]
- if n1.kind == nkBracket:
- var b = newNodeI(nkBracket, n1.info, n1.len)
- for i in 0.. 1: invalidPragma(c, n)
+proc noVal(n: PNode) =
+ if n.kind == nkExprColonExpr: invalidPragma(n)
proc pragmaUnroll(c: PContext, n: PNode) =
if c.p.nestedLoopCounter <= 0:
- invalidPragma(c, n)
- elif n.kind in nkPragmaCallKinds and n.len == 2:
+ invalidPragma(n)
+ elif n.kind == nkExprColonExpr:
var unrollFactor = expectIntLit(c, n)
if unrollFactor <% 32:
- n[1] = newIntNode(nkIntLit, unrollFactor)
+ n.sons[1] = newIntNode(nkIntLit, unrollFactor)
else:
- invalidPragma(c, n)
+ invalidPragma(n)
proc pragmaLine(c: PContext, n: PNode) =
- if n.kind in nkPragmaCallKinds and n.len == 2:
- n[1] = c.semConstExpr(c, n[1])
- let a = n[1]
- if a.kind in {nkPar, nkTupleConstr}:
- # unpack the tuple
- var x = a[0]
- var y = a[1]
- if x.kind == nkExprColonExpr: x = x[1]
- if y.kind == nkExprColonExpr: y = y[1]
+ if n.kind == nkExprColonExpr:
+ n.sons[1] = c.semConstExpr(c, n.sons[1])
+ let a = n.sons[1]
+ if a.kind == nkPar:
+ var x = a.sons[0]
+ var y = a.sons[1]
+ if x.kind == nkExprColonExpr: x = x.sons[1]
+ if y.kind == nkExprColonExpr: y = y.sons[1]
if x.kind != nkStrLit:
- localError(c.config, n.info, errStringLiteralExpected)
+ localError(n.info, errStringLiteralExpected)
elif y.kind != nkIntLit:
- localError(c.config, n.info, errIntLiteralExpected)
+ localError(n.info, errIntLiteralExpected)
else:
- n.info.fileIndex = fileInfoIdx(c.config, AbsoluteFile(x.strVal))
- n.info.line = uint16(y.intVal)
+ n.info.fileIndex = msgs.fileInfoIdx(x.strVal)
+ n.info.line = int16(y.intVal)
else:
- localError(c.config, n.info, "tuple expected")
+ localError(n.info, errXExpected, "tuple")
else:
# sensible default:
- n.info = getInfoContext(c.config, -1)
+ n.info = getInfoContext(-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 it = n.sons[i]
+ if it.kind != nkExprColonExpr: invalidPragma(n)
+ elif it.sons[0].kind != nkIdent: invalidPragma(n)
+ elif it.sons[1].kind != nkIdent: invalidPragma(n)
- var userPragma = newSym(skTemplate, it[1].ident, nextSymId(c.idgen), c.module, it.info, c.config.options)
- userPragma.ast = newTreeI(nkPragma, n.info, n.sons[i+1..^1])
+ var userPragma = newSym(skTemplate, it.sons[1].ident, nil, it.info)
+ var body = newNodeI(nkPragma, n.info)
+ for j in i+1 .. sonsLen(n)-1: addSon(body, n.sons[j])
+ userPragma.ast = body
strTableAdd(c.userPragmas, userPragma)
proc pragmaRaisesOrTags(c: PContext, n: PNode) =
proc processExc(c: PContext, x: PNode) =
- if c.hasUnresolvedArgs(c, x):
- x.typ = makeTypeFromExpr(c, x)
- else:
- var t = skipTypes(c.semTypeNode(c, x, nil), skipPtrs)
- if t.kind notin {tyObject, tyOr}:
- localError(c.config, x.info, errGenerated, "invalid type for raises/tags list")
- x.typ = t
+ var t = skipTypes(c.semTypeNode(c, x, nil), skipPtrs)
+ if t.kind != tyObject:
+ localError(x.info, errGenerated, "invalid type for raises/tags list")
+ x.typ = t
- if n.kind in nkPragmaCallKinds and n.len == 2:
- let it = n[1]
+ if n.kind == nkExprColonExpr:
+ let it = n.sons[1]
if it.kind notin {nkCurly, nkBracket}:
processExc(c, it)
else:
for e in items(it): processExc(c, e)
else:
- invalidPragma(c, n)
+ invalidPragma(n)
proc pragmaLockStmt(c: PContext; it: PNode) =
- if it.kind notin nkPragmaCallKinds or it.len != 2:
- invalidPragma(c, it)
+ if it.kind != nkExprColonExpr:
+ invalidPragma(it)
else:
let n = it[1]
if n.kind != nkBracket:
- localError(c.config, n.info, errGenerated, "locks pragma takes a list of expressions")
+ localError(n.info, errGenerated, "locks pragma takes a list of expressions")
else:
- for i in 0.. MaxLockLevel:
+ localError(it[1].info, "integer must be within 0.." & $MaxLockLevel)
+ else:
+ result = TLockLevel(x)
+
+proc typeBorrow(sym: PSym, n: PNode) =
+ if n.kind == nkExprColonExpr:
+ let it = n.sons[1]
if it.kind != nkAccQuoted:
- localError(c.config, n.info, "a type can only borrow `.` for now")
+ localError(n.info, "a type can only borrow `.` for now")
incl(sym.typ.flags, tfBorrowDot)
-proc markCompilerProc(c: PContext; s: PSym) =
- # 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)
+proc markCompilerProc(s: PSym) =
+ makeExternExport(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)
+ registerCompilerProc(s)
-proc deprecatedStmt(c: PContext; outerPragma: PNode) =
- let pragma = outerPragma[1]
- if pragma.kind in {nkStrLit..nkTripleStrLit}:
- incl(c.module.flags, sfDeprecated)
- c.module.constraint = getStrLitNode(c, outerPragma)
- return
+proc deprecatedStmt(c: PContext; pragma: PNode) =
+ let pragma = pragma[1]
if pragma.kind != nkBracket:
- localError(c.config, pragma.info, "list of key:value pairs expected"); return
+ localError(pragma.info, "list of key:value pairs expected"); return
for n in pragma:
- if n.kind in nkPragmaCallKinds and n.len == 2:
- let dest = qualifiedLookUp(c, n[1], {checkUndeclared})
- if dest == nil or dest.kind in routineKinds:
- localError(c.config, n.info, warnUser, "the .deprecated pragma is unreliable for routines")
- let src = considerQuotedIdent(c, n[0])
- let alias = newSym(skAlias, src, nextSymId(c.idgen), dest, n[0].info, c.config.options)
+ if n.kind in {nkExprColonExpr, nkExprEqExpr}:
+ let dest = qualifiedLookUp(c, n[1])
+ let src = considerQuotedIdent(n[0])
+ let alias = newSym(skAlias, src, dest, n[0].info)
incl(alias.flags, sfExported)
- if sfCompilerProc in dest.flags: markCompilerProc(c, alias)
+ if sfCompilerProc in dest.flags: markCompilerProc(alias)
addInterfaceDecl(c, alias)
- n[1] = newSymNode(dest)
else:
- localError(c.config, n.info, "key:value pair expected")
+ localError(n.info, "key:value pair expected")
proc pragmaGuard(c: PContext; it: PNode; kind: TSymKind): PSym =
- if it.kind notin nkPragmaCallKinds or it.len != 2:
- invalidPragma(c, it); return
+ if it.kind != nkExprColonExpr:
+ invalidPragma(it); return
let n = it[1]
if n.kind == nkSym:
result = n.sym
@@ -750,207 +591,96 @@ proc pragmaGuard(c: PContext; it: PNode; kind: TSymKind): PSym =
# We return a dummy symbol; later passes over the type will repair it.
# Generic instantiation needs to know about this too. But we're lazy
# and perform the lookup on demand instead.
- result = newSym(skUnknown, considerQuotedIdent(c, n), nextSymId(c.idgen), nil, n.info,
- c.config.options)
+ result = newSym(skUnknown, considerQuotedIdent(n), nil, n.info)
else:
- result = qualifiedLookUp(c, n, {checkUndeclared})
+ result = qualifiedLookUp(c, n)
-proc semCustomPragma(c: PContext, n: PNode): PNode =
- var callNode: PNode
-
- if n.kind in {nkIdent, nkSym}:
- # pragma -> pragma()
- callNode = newTree(nkCall, n)
- elif n.kind == nkExprColonExpr:
- # pragma: arg -> pragma(arg)
- callNode = newTree(nkCall, n[0], n[1])
- elif n.kind in nkPragmaCallKinds:
- callNode = n
- else:
- invalidPragma(c, n)
- return n
-
- let r = c.semOverloadedCall(c, callNode, n, {skTemplate}, {efNoUndeclared})
- if r.isNil or sfCustomPragma notin r[0].sym.flags:
- invalidPragma(c, n)
- return n
-
- result = r
- # Transform the nkCall node back to its original form if possible
- if n.kind == nkIdent and r.len == 1:
- # pragma() -> pragma
- result = result[0]
- elif n.kind == nkExprColonExpr and r.len == 2:
- # 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 =
- var it = n[i]
- var key = if it.kind in nkPragmaCallKinds and it.len > 1: it[0] else: it
+proc singlePragma(c: PContext, sym: PSym, n: PNode, i: int,
+ validPragmas: TSpecialWords): bool =
+ var it = n.sons[i]
+ var key = if it.kind == nkExprColonExpr: it.sons[0] else: it
if key.kind == nkBracketExpr:
processNote(c, it)
return
- elif key.kind == nkCast:
- if comesFromPush:
- localError(c.config, n.info, "a 'cast' pragma cannot be pushed")
- 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])
- else: discard
- return
- elif key.kind notin nkIdentKinds:
- n[i] = semCustomPragma(c, it)
- return
- let ident = considerQuotedIdent(c, key)
+ let ident = considerQuotedIdent(key)
var userPragma = strTableGet(c.userPragmas, ident)
if userPragma != nil:
- styleCheckUse(c, key.info, userPragma)
-
- # number of pragmas increase/decrease with user pragma expansion
inc c.instCounter
if c.instCounter > 100:
- globalError(c.config, it.info, "recursive dependency: " & userPragma.name.s)
-
- pragma(c, sym, userPragma.ast, validPragmas, isStatement)
- n.sons[i..i] = userPragma.ast.sons # expand user pragma with its content
- i.inc(userPragma.ast.len - 1) # inc by -1 is ok, user pragmas was empty
+ globalError(it.info, errRecursiveDependencyX, userPragma.name.s)
+ pragma(c, sym, userPragma.ast, validPragmas)
+ # ensure the pragma is also remember for generic instantiations in other
+ # modules:
+ n.sons[i] = userPragma.ast
dec c.instCounter
else:
- let k = whichKeyword(ident)
+ var k = whichKeyword(ident)
if k in validPragmas:
- 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)
- else:
- incl(sym.flags, sfMangleCpp)
+ of wExportc:
+ makeExternExport(sym, getOptionalStr(c, it, "$1"), it.info)
incl(sym.flags, sfUsed) # avoid wrong hints
- of wImportc:
- let name = getOptionalStr(c, it, "$1")
- cppDefine(c.config, name)
- recordPragma(c, it, "cppdefine", name)
- makeExternImport(c, sym, name, it.info)
+ of wImportc: makeExternImport(sym, getOptionalStr(c, it, "$1"))
of wImportCompilerProc:
- let name = getOptionalStr(c, it, "$1")
- cppDefine(c.config, name)
- recordPragma(c, it, "cppdefine", name)
- processImportCompilerProc(c, sym, name, it.info)
- of wExtern: setExternName(c, sym, expectStrLit(c, it), it.info)
+ processImportCompilerProc(sym, getOptionalStr(c, it, "$1"))
+ of wExtern: setExternName(sym, expectStrLit(c, it))
+ of wImmediate:
+ if sym.kind in {skTemplate, skMacro}: incl(sym.flags, sfImmediate)
+ else: invalidPragma(it)
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)
+ else: invalidPragma(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")
- let name = getOptionalStr(c, it, "$1")
- incl(sym.flags, sfImportc)
- incl(sym.flags, sfInfixCall)
- if sym.kind in skProcKinds and {'(', '#', '@'} notin name:
- localError(c.config, n.info, "`importjs` for routines requires a pattern")
- setExternName(c, sym, name, it.info)
+ processImportCpp(sym, getOptionalStr(c, it, "$1"))
of wImportObjC:
- processImportObjC(c, sym, getOptionalStr(c, it, "$1"), it.info)
- of wSize:
- if sym.typ == nil: invalidPragma(c, it)
- var size = expectIntLit(c, it)
- case size
- of 1, 2, 4:
- sym.typ.size = size
- sym.typ.align = int16 size
- of 8:
- sym.typ.size = 8
- sym.typ.align = floatInt64Align(c.config)
- else:
- localError(c.config, it.info, "size may only be 1, 2, 4 or 8")
+ processImportObjC(sym, getOptionalStr(c, it, "$1"))
of wAlign:
- let alignment = expectIntLit(c, it)
- if isPowerOfTwo(alignment) and alignment > 0:
- sym.alignment = max(sym.alignment, alignment)
+ if sym.typ == nil: invalidPragma(it)
+ var align = expectIntLit(c, it)
+ if (not isPowerOfTwo(align) and align != 0) or align >% high(int16):
+ localError(it.info, errPowerOfTwoExpected)
else:
- localError(c.config, it.info, "power of two expected")
+ sym.typ.align = align.int16
+ of wSize:
+ if sym.typ == nil: invalidPragma(it)
+ var size = expectIntLit(c, it)
+ if not isPowerOfTwo(size) or size <= 0 or size > 8:
+ localError(it.info, errPowerOfTwoExpected)
+ else:
+ sym.typ.size = size
of wNodecl:
- noVal(c, it)
+ noVal(it)
incl(sym.loc.flags, lfNoDecl)
of wPure, wAsmNoStackFrame:
- noVal(c, it)
+ noVal(it)
if sym != nil:
- if k == wPure and sym.kind in routineKinds: invalidPragma(c, it)
+ if k == wPure and sym.kind in routineKinds: invalidPragma(it)
else: incl(sym.flags, sfPure)
of wVolatile:
- noVal(c, it)
+ noVal(it)
incl(sym.flags, sfVolatile)
- of wCursor:
- noVal(c, it)
- incl(sym.flags, sfCursor)
of wRegister:
- noVal(c, it)
+ noVal(it)
incl(sym.flags, sfRegister)
- 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
+ noVal(it)
+ incl(sym.flags, sfThread)
+ of wDeadCodeElim: pragmaDeadCodeElim(c, it)
of wNoForward: pragmaNoForward(c, it)
- of wReorder: pragmaNoForward(c, it, flag = sfReorder)
of wMagic: processMagic(c, it, sym)
of wCompileTime:
- noVal(c, it)
- if comesFromPush:
- if sym.kind in {skProc, skFunc}:
- incl(sym.flags, sfCompileTime)
- else:
- incl(sym.flags, sfCompileTime)
- #incl(sym.loc.flags, lfNoDecl)
+ noVal(it)
+ incl(sym.flags, sfCompileTime)
+ incl(sym.loc.flags, lfNoDecl)
of wGlobal:
- noVal(c, it)
+ noVal(it)
incl(sym.flags, sfGlobal)
incl(sym.flags, sfPure)
of wMerge:
# only supported for backwards compat, doesn't do anything anymore
- noVal(c, it)
+ noVal(it)
of wConstructor:
- noVal(c, it)
+ noVal(it)
incl(sym.flags, sfConstructor)
of wHeader:
var lib = getLib(c, libHeader, getStrLitNode(c, it))
@@ -959,367 +689,259 @@ 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)
- of wNoSideEffect:
- noVal(c, it)
- if sym != nil:
- incl(sym.flags, sfNoSideEffect)
- if sym.typ != nil: incl(sym.typ.flags, tfNoSideEffect)
- of wSideEffect:
- noVal(c, it)
+ if sym.loc.r == nil: sym.loc.r = rope(sym.name.s)
+ of wDestructor:
+ sym.flags.incl sfOverriden
+ if sym.name.s.normalize != "destroy":
+ localError(n.info, errGenerated, "destructor has to be named 'destroy'")
+ of wOverride:
+ sym.flags.incl sfOverriden
+ of wNosideeffect:
+ noVal(it)
+ incl(sym.flags, sfNoSideEffect)
+ if sym.typ != nil: incl(sym.typ.flags, tfNoSideEffect)
+ of wSideeffect:
+ noVal(it)
incl(sym.flags, sfSideEffect)
of wNoreturn:
- noVal(c, it)
- # Disable the 'noreturn' annotation when in the "Quirky Exceptions" mode!
- if c.config.exc != excQuirky:
- incl(sym.flags, sfNoReturn)
- if sym.typ[0] != nil:
- localError(c.config, sym.ast[paramsPos][0].info,
- ".noreturn with return type not allowed")
- of wNoDestroy:
- noVal(c, it)
- incl(sym.flags, sfGeneratedOp)
- of wNosinks:
- noVal(c, it)
- incl(sym.flags, sfWasForwarded)
+ noVal(it)
+ incl(sym.flags, sfNoReturn)
of wDynlib:
processDynLib(c, it, sym)
- of wCompilerProc, wCore:
- noVal(c, it) # compilerproc may not get a string!
- cppDefine(c.graph.config, sym.name.s)
- recordPragma(c, it, "cppdefine", sym.name.s)
- if sfFromGeneric notin sym.flags: markCompilerProc(c, sym)
- of wNonReloadable:
- sym.flags.incl sfNonReloadable
+ of wCompilerproc:
+ noVal(it) # compilerproc may not get a string!
+ if sfFromGeneric notin sym.flags: markCompilerProc(sym)
of wProcVar:
- # old procvar annotation, no longer needed
- noVal(c, it)
- of wExplain:
- sym.flags.incl sfExplain
+ noVal(it)
+ incl(sym.flags, sfProcvar)
of wDeprecated:
- if sym != nil and sym.kind in routineKinds + {skType, skVar, skLet, skConst}:
- if it.kind in nkPragmaCallKinds: discard getStrLitNode(c, it)
- incl(sym.flags, sfDeprecated)
- elif sym != nil and sym.kind != skModule:
- # We don't support the extra annotation field
- if it.kind in nkPragmaCallKinds:
- localError(c.config, it.info, "annotation to deprecated not supported here")
- incl(sym.flags, sfDeprecated)
- # At this point we're quite sure this is a statement and applies to the
- # whole module
- elif it.kind in nkPragmaCallKinds: deprecatedStmt(c, it)
+ if it.kind == nkExprColonExpr: deprecatedStmt(c, it)
+ elif sym != nil: incl(sym.flags, sfDeprecated)
else: incl(c.module.flags, sfDeprecated)
of wVarargs:
- noVal(c, it)
- if sym.typ == nil: invalidPragma(c, it)
+ noVal(it)
+ if sym.typ == nil: invalidPragma(it)
else: incl(sym.typ.flags, tfVarargs)
of wBorrow:
if sym.kind == skType:
- typeBorrow(c, sym, it)
+ typeBorrow(sym, it)
else:
- noVal(c, it)
+ noVal(it)
incl(sym.flags, sfBorrow)
of wFinal:
- noVal(c, it)
- if sym.typ == nil: invalidPragma(c, it)
+ noVal(it)
+ if sym.typ == nil: invalidPragma(it)
else: incl(sym.typ.flags, tfFinal)
of wInheritable:
- noVal(c, it)
- if sym.typ == nil or tfFinal in sym.typ.flags: invalidPragma(c, it)
+ noVal(it)
+ if sym.typ == nil or tfFinal in sym.typ.flags: invalidPragma(it)
else: incl(sym.typ.flags, tfInheritable)
- of wPackage:
- noVal(c, it)
- if sym.typ == nil: invalidPragma(c, it)
- else: incl(sym.flags, sfForward)
of wAcyclic:
- noVal(c, it)
- if sym.typ == nil: invalidPragma(c, it)
+ noVal(it)
+ if sym.typ == nil: invalidPragma(it)
else: incl(sym.typ.flags, tfAcyclic)
of wShallow:
- noVal(c, it)
- if sym.typ == nil: invalidPragma(c, it)
+ noVal(it)
+ if sym.typ == nil: invalidPragma(it)
else: incl(sym.typ.flags, tfShallow)
of wThread:
- noVal(c, it)
+ noVal(it)
incl(sym.flags, sfThread)
- if sym.typ != nil:
- incl(sym.typ.flags, tfThread)
- if sym.typ.callConv == ccClosure: sym.typ.callConv = ccNimCall
+ incl(sym.flags, sfProcvar)
+ if sym.typ != nil: incl(sym.typ.flags, tfThread)
of wGcSafe:
- noVal(c, it)
- if sym != nil:
- if sym.kind != skType: incl(sym.flags, sfThread)
- if sym.typ != nil: incl(sym.typ.flags, tfGcSafe)
- else: invalidPragma(c, it)
- else:
- discard "no checking if used as a code block"
+ noVal(it)
+ if sym.kind != skType: incl(sym.flags, sfThread)
+ if sym.typ != nil: incl(sym.typ.flags, tfGcSafe)
+ else: invalidPragma(it)
of wPacked:
- noVal(c, it)
- if sym.typ == nil: invalidPragma(c, it)
+ noVal(it)
+ if sym.typ == nil: invalidPragma(it)
else: incl(sym.typ.flags, tfPacked)
- of wHint:
- let s = expectStrLit(c, it)
- recordPragma(c, it, "hint", s)
- message(c.config, it.info, hintUser, s)
- of wWarning:
- let s = expectStrLit(c, it)
- recordPragma(c, it, "warning", s)
- message(c.config, it.info, warnUser, s)
+ of wHint: message(it.info, hintUser, expectStrLit(c, it))
+ of wWarning: message(it.info, warnUser, expectStrLit(c, it))
of wError:
- if sym != nil and (sym.isRoutine or sym.kind == skType) and not isStatement:
+ if sym != nil and sym.isRoutine:
# This is subtle but correct: the error *statement* is only
- # allowed when 'wUsed' is not in validPragmas. Here this is the easiest way to
- # distinguish properly between
+ # allowed for top level statements. Seems to be easier than
+ # distinguishing properly between
# ``proc p() {.error}`` and ``proc p() = {.error: "msg".}``
- if it.kind in nkPragmaCallKinds: discard getStrLitNode(c, it)
+ noVal(it)
incl(sym.flags, sfError)
- excl(sym.flags, sfForward)
else:
- 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)
+ localError(it.info, errUser, expectStrLit(c, it))
+ of wFatal: fatal(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)
- of wPassl:
- let s = expectStrLit(c, it)
- extccomp.addLinkOption(c.config, s)
- recordPragma(c, it, "passl", s)
- of wPassc:
- let s = expectStrLit(c, it)
- extccomp.addCompileOption(c.config, s)
- recordPragma(c, it, "passc", s)
- of wLocalPassc:
- assert sym != nil and sym.kind == skModule
- let s = expectStrLit(c, it)
- extccomp.addLocalCompileOption(c.config, s, toFullPathConsiderDirty(c.config, sym.info.fileIndex))
- recordPragma(c, it, "localpassl", s)
+ of wLink: processCommonLink(c, it, linkNormal)
+ of wLinksys: processCommonLink(c, it, linkSys)
+ of wPassl: extccomp.addLinkOption(expectStrLit(c, it))
+ of wPassc: extccomp.addCompileOption(expectStrLit(c, it))
+ of wBreakpoint: pragmaBreakpoint(c, it)
+ of wWatchPoint: pragmaWatchpoint(c, it)
of wPush:
processPush(c, n, i + 1)
result = true
- of wPop:
- processPop(c, it)
- result = true
+ of wPop: processPop(c, it)
of wPragma:
- if not sym.isNil and sym.kind == skTemplate:
- sym.flags.incl sfCustomPragma
- else:
- processPragma(c, n, i)
- result = true
+ processPragma(c, n, i)
+ result = true
of wDiscardable:
- noVal(c, it)
+ noVal(it)
if sym != nil: incl(sym.flags, sfDiscardable)
of wNoInit:
- noVal(c, it)
+ noVal(it)
if sym != nil: incl(sym.flags, sfNoInit)
of wCodegenDecl: processCodegenDecl(c, it, sym)
- of wChecks, wObjChecks, wFieldChecks, wRangeChecks, wBoundChecks,
- wOverflowChecks, wNilChecks, wAssertions, wWarnings, wHints,
- wLineDir, wOptimization, wStaticBoundchecks, wStyleChecks,
- wCallconv, wDebugger, wProfiler,
- wFloatChecks, wNanChecks, wInfChecks, wPatterns, wTrMacros:
- processOption(c, it, c.config.options)
- of wStackTrace, wLineTrace:
- if sym.kind in {skProc, skMethod, skConverter}:
- processOption(c, it, sym.options)
- else:
- processOption(c, it, c.config.options)
+ of wChecks, wObjChecks, wFieldChecks, wRangechecks, wBoundchecks,
+ wOverflowchecks, wNilchecks, wAssertions, wWarnings, wHints,
+ wLinedir, wStacktrace, wLinetrace, wOptimization,
+ wCallconv,
+ wDebugger, wProfiler, wFloatchecks, wNanChecks, wInfChecks,
+ wPatterns:
+ if processOption(c, it):
+ # calling conventions (boring...):
+ localError(it.info, errOptionExpected)
of FirstCallConv..LastCallConv:
assert(sym != nil)
- if sym.typ == nil: invalidPragma(c, it)
- else:
- sym.typ.callConv = wordToCallConv(k)
- sym.typ.flags.incl tfExplicitCallConv
+ if sym.typ == nil: invalidPragma(it)
+ else: sym.typ.callConv = wordToCallConv(k)
of wEmit: pragmaEmit(c, it)
of wUnroll: pragmaUnroll(c, it)
- of wLinearScanEnd, wComputedGoto: noVal(c, it)
+ of wLinearScanEnd, wComputedGoto: noVal(it)
of wEffects:
# is later processed in effect analysis:
- noVal(c, it)
+ noVal(it)
of wIncompleteStruct:
- noVal(c, it)
- if sym.typ == nil: invalidPragma(c, it)
+ noVal(it)
+ if sym.typ == nil: invalidPragma(it)
else: incl(sym.typ.flags, tfIncompleteStruct)
- of wCompleteStruct:
- noVal(c, it)
- if sym.typ == nil: invalidPragma(c, it)
- else: incl(sym.typ.flags, tfCompleteStruct)
of wUnchecked:
- noVal(c, it)
- if sym.typ == nil or sym.typ.kind notin {tyArray, tyUncheckedArray}:
- invalidPragma(c, it)
- else:
- sym.typ.kind = tyUncheckedArray
+ noVal(it)
+ if sym.typ == nil: invalidPragma(it)
+ else: incl(sym.typ.flags, tfUncheckedArray)
of wUnion:
- if c.config.backend == backendJs:
- localError(c.config, it.info, "`{.union.}` is not implemented for js backend.")
- else:
- noVal(c, it)
- if sym.typ == nil: invalidPragma(c, it)
- else: incl(sym.typ.flags, tfUnion)
+ noVal(it)
+ if sym.typ == nil: invalidPragma(it)
+ else: incl(sym.typ.flags, tfUnion)
of wRequiresInit:
- noVal(c, it)
- if sym.kind == skField:
- sym.flags.incl sfRequiresInit
- elif sym.typ != nil:
- incl(sym.typ.flags, tfNeedsFullInit)
- else:
- invalidPragma(c, it)
+ noVal(it)
+ if sym.typ == nil: invalidPragma(it)
+ else: incl(sym.typ.flags, tfNeedsInit)
of wByRef:
- noVal(c, it)
+ noVal(it)
if sym == nil or sym.typ == nil:
- processOption(c, it, c.config.options)
+ if processOption(c, it): localError(it.info, errOptionExpected)
else:
incl(sym.typ.flags, tfByRef)
of wByCopy:
- noVal(c, it)
- if sym.kind != skType or sym.typ == nil: invalidPragma(c, it)
+ noVal(it)
+ if sym.kind != skType or sym.typ == nil: invalidPragma(it)
else: incl(sym.typ.flags, tfByCopy)
of wPartial:
- noVal(c, it)
- if sym.kind != skType or sym.typ == nil: invalidPragma(c, it)
+ noVal(it)
+ if sym.kind != skType or sym.typ == nil: invalidPragma(it)
else:
incl(sym.typ.flags, tfPartial)
+ # .partial types can only work with dead code elimination
+ # to prevent the codegen from doing anything before we compiled
+ # the whole program:
+ incl gGlobalOptions, optDeadCodeElim
of wInject, wGensym:
# We check for errors, but do nothing with these pragmas otherwise
# as they are handled directly in 'evalTemplate'.
- noVal(c, it)
- if sym == nil: invalidPragma(c, it)
+ noVal(it)
+ if sym == nil: invalidPragma(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")
+ elif sym.typ == nil: invalidPragma(it)
+ else: sym.typ.lockLevel = pragmaLocks(c, it)
of wBitsize:
- if sym == nil or sym.kind != skField:
- invalidPragma(c, it)
+ if sym == nil or sym.kind != skField or it.kind != nkExprColonExpr:
+ invalidPragma(it)
else:
sym.bitsize = expectIntLit(c, it)
- if sym.bitsize <= 0:
- localError(c.config, it.info, "bitsize needs to be positive")
of wGuard:
if sym == nil or sym.kind notin {skVar, skLet, skField}:
- invalidPragma(c, it)
+ invalidPragma(it)
else:
sym.guard = pragmaGuard(c, it, sym.kind)
of wGoto:
if sym == nil or sym.kind notin {skVar, skLet}:
- invalidPragma(c, it)
+ invalidPragma(it)
else:
sym.flags.incl sfGoto
of wExportNims:
- if sym == nil: invalidPragma(c, it)
- else: magicsys.registerNimScriptSymbol(c.graph, sym)
+ if sym == nil: invalidPragma(it)
+ else: magicsys.registerNimScriptSymbol(sym)
+ of wInjectStmt:
+ if it.kind != nkExprColonExpr:
+ localError(it.info, errExprExpected)
+ else:
+ it.sons[1] = c.semExpr(c, it.sons[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")
+ noVal(it)
+ if isTopLevel(c):
+ c.module.flags.incl sfExperimental
+ else:
+ localError(it.info, "'experimental' pragma only valid as toplevel statement")
+ of wThis:
+ if it.kind == nkExprColonExpr:
+ c.selfName = considerQuotedIdent(it[1])
+ else:
+ c.selfName = getIdent("self")
of wNoRewrite:
- noVal(c, it)
+ noVal(it)
of wBase:
- noVal(c, it)
+ noVal(it)
sym.flags.incl sfBase
- of wIntDefine:
- processDefineConst(c, n, sym, mIntDefine)
- of wStrDefine:
- processDefineConst(c, n, sym, mStrDefine)
- of wBoolDefine:
- processDefineConst(c, n, sym, mBoolDefine)
- of wUsed:
- noVal(c, it)
- if sym == nil: invalidPragma(c, it)
- else: sym.flags.incl sfUsed
- of wLiftLocals: discard
- of wRequires, wInvariant, wAssume, wAssert:
- 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,
- skField, skProc, skFunc, skConverter, skMethod, skType}):
- n[i] = semCustomPragma(c, it)
- elif sym != nil:
- illegalCustomPragma(c, it, sym)
- else:
- invalidPragma(c, it)
+ else: invalidPragma(it)
+ else: invalidPragma(it)
-proc overwriteLineInfo(n: PNode; info: TLineInfo) =
- n.info = info
- for i in 0.. 1: p[0] else: p
+ for p in n.sons:
+ var key = if p.kind == nkExprColonExpr: p[0] else: p
if key.kind == nkIdent and whichKeyword(key.ident) == pragma:
return true
return false
-proc pragmaRec(c: PContext, sym: PSym, n: PNode, validPragmas: TSpecialWords;
- isStatement: bool) =
+proc pragmaRec(c: PContext, sym: PSym, n: PNode, validPragmas: TSpecialWords) =
if n == nil: return
- var i = 0
- while i < n.len:
- if singlePragma(c, sym, n, i, validPragmas, false, isStatement): break
- inc i
+ for i in countup(0, sonsLen(n) - 1):
+ if n.sons[i].kind == nkPragma: pragmaRec(c, sym, n.sons[i], validPragmas)
+ elif singlePragma(c, sym, n, i, validPragmas): break
-proc pragma(c: PContext, sym: PSym, n: PNode, validPragmas: TSpecialWords;
- isStatement: bool) =
+proc pragma(c: PContext, sym: PSym, n: PNode, validPragmas: TSpecialWords) =
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)
+ pragmaRec(c, sym, n, validPragmas)
+ implicitPragmas(c, sym, n, validPragmas)
diff --git a/compiler/prefixmatches.nim b/compiler/prefixmatches.nim
deleted file mode 100644
index 71ae9a844..000000000
--- a/compiler/prefixmatches.nim
+++ /dev/null
@@ -1,56 +0,0 @@
-#
-#
-# The Nim Compiler
-# (c) Copyright 2017 Andreas Rumpf
-#
-# See the file "copying.txt", included in this
-# distribution, for details about the copyright.
-#
-
-from strutils import toLowerAscii
-
-type
- PrefixMatch* {.pure.} = enum
- None, ## no prefix detected
- Abbrev ## prefix is an abbreviation of the symbol
- Substr, ## prefix is a substring of the symbol
- Prefix, ## prefix does match the symbol
-
-proc prefixMatch*(p, s: string): PrefixMatch =
- template eq(a, b): bool = a.toLowerAscii == b.toLowerAscii
- if p.len > s.len: return PrefixMatch.None
- var i = 0
- # check for prefix/contains:
- while i < s.len:
- if s[i] == '_': inc i
- if i < s.len and eq(s[i], p[0]):
- var ii = i+1
- var jj = 1
- while ii < s.len and jj < p.len:
- if p[jj] == '_': inc jj
- if s[ii] == '_': inc ii
- if not eq(s[ii], p[jj]): break
- inc ii
- inc jj
-
- if jj >= p.len:
- if i == 0: return PrefixMatch.Prefix
- else: return PrefixMatch.Substr
- inc i
- # check for abbrev:
- if eq(s[0], p[0]):
- i = 1
- var j = 1
- while i < s.len:
- if i < s.len-1 and s[i] == '_':
- if j < p.len and eq(p[j], s[i+1]): inc j
- else: return PrefixMatch.None
- if i < s.len and s[i] in {'A'..'Z'} and s[i-1] notin {'A'..'Z'}:
- if j < p.len and eq(p[j], s[i]): inc j
- else: return PrefixMatch.None
- inc i
- if j >= p.len:
- return PrefixMatch.Abbrev
- else:
- return PrefixMatch.None
- return PrefixMatch.None
diff --git a/compiler/procfind.nim b/compiler/procfind.nim
index 0bdb3dae6..523ea2e2f 100644
--- a/compiler/procfind.nim
+++ b/compiler/procfind.nim
@@ -11,68 +11,111 @@
# This is needed for proper handling of forward declarations.
import
- ast, astalgo, msgs, semdata, types, trees, strutils, lookups
+ ast, astalgo, msgs, semdata, types, trees, strutils
proc equalGenericParams(procA, procB: PNode): bool =
- if procA.len != procB.len: return false
- for i in 0..= ord(tokKeywordLow) - ord(tkSymbol)) and
(i.id <= ord(tokKeywordHigh) - ord(tkSymbol)):
result = true
+proc isKeyword*(s: string): bool = isKeyword(getIdent(s))
+
proc renderDefinitionName*(s: PSym, noQuotes = false): string =
## Returns the definition name of the symbol.
##
@@ -87,30 +70,11 @@ proc renderDefinitionName*(s: PSym, noQuotes = false): string =
const
IndentWidth = 2
- longIndentWid = IndentWidth * 2
-
-when defined(nimpretty):
- proc minmaxLine(n: PNode): (int, int) =
- case n.kind
- of nkTripleStrLit:
- result = (n.info.line.int, n.info.line.int + countLines(n.strVal))
- of nkCommentStmt:
- result = (n.info.line.int, n.info.line.int + countLines(n.comment))
- else:
- result = (n.info.line.int, n.info.line.int)
- for i in 0.. result[1]: result[1] = currMax
-
- proc lineDiff(a, b: PNode): int =
- result = minmaxLine(b)[0] - minmaxLine(a)[1]
-
-const
+ longIndentWid = 4
MaxLineLen = 80
LineCommentColumn = 30
-proc initSrcGen(g: var TSrcGen, renderFlags: TRenderFlags; config: ConfigRef) =
+proc initSrcGen(g: var TSrcGen, renderFlags: TRenderFlags) =
g.comStack = @[]
g.tokens = @[]
g.indent = 0
@@ -121,24 +85,19 @@ proc initSrcGen(g: var TSrcGen, renderFlags: TRenderFlags; config: ConfigRef) =
g.flags = renderFlags
g.pendingNL = -1
g.pendingWhitespace = -1
- g.inGenericParams = false
- g.config = config
+ g.checkAnon = false
-proc addTok(g: var TSrcGen, kind: TokType, s: string; sym: PSym = nil) =
- g.tokens.add TRenderTok(kind: kind, length: int16(s.len), sym: sym)
- g.buf.add(s)
- if kind != tkSpaces:
- inc g.col, s.len
+proc addTok(g: var TSrcGen, kind: TTokType, s: string) =
+ var length = len(g.tokens)
+ setLen(g.tokens, length + 1)
+ g.tokens[length].kind = kind
+ g.tokens[length].length = int16(len(s))
+ add(g.buf, s)
proc addPendingNL(g: var TSrcGen) =
if g.pendingNL >= 0:
- when defined(nimpretty):
- let newlines = repeat("\n", clamp(g.pendingNewlineCount, 1, 3))
- else:
- const newlines = "\n"
- addTok(g, tkSpaces, newlines & spaces(g.pendingNL))
+ addTok(g, tkSpaces, "\n" & spaces(g.pendingNL))
g.lineLen = g.pendingNL
- g.col = g.pendingNL
g.pendingNL = - 1
g.pendingWhitespace = -1
elif g.pendingWhitespace >= 0:
@@ -147,10 +106,7 @@ proc addPendingNL(g: var TSrcGen) =
proc putNL(g: var TSrcGen, indent: int) =
if g.pendingNL >= 0: addPendingNL(g)
- else:
- addTok(g, tkSpaces, "\n")
- g.col = 0
-
+ else: addTok(g, tkSpaces, "\n")
g.pendingNL = indent
g.lineLen = indent
g.pendingWhitespace = -1
@@ -164,19 +120,11 @@ 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
+ g.lineLen = indent # BUGFIX
proc optNL(g: var TSrcGen) =
optNL(g, g.indent)
-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) =
inc(g.indent, IndentWidth)
g.pendingNL = g.indent
@@ -189,72 +137,87 @@ proc dedent(g: var TSrcGen) =
dec(g.pendingNL, IndentWidth)
dec(g.lineLen, IndentWidth)
-proc put(g: var TSrcGen, kind: TokType, s: string; sym: PSym = nil) =
+proc put(g: var TSrcGen, kind: TTokType, s: string) =
if kind != tkSpaces:
addPendingNL(g)
- if s.len > 0 or kind in {tkHideableStart, tkHideableEnd}:
- addTok(g, kind, s, sym)
+ if len(s) > 0:
+ addTok(g, kind, s)
+ inc(g.lineLen, len(s))
else:
g.pendingWhitespace = s.len
- inc g.col, s.len
- inc(g.lineLen, s.len)
+
+proc putLong(g: var TSrcGen, kind: TTokType, s: string, lineLen: int) =
+ # use this for tokens over multiple lines.
+ addPendingNL(g)
+ addTok(g, kind, s)
+ g.lineLen = lineLen
+
+proc toNimChar(c: char): string =
+ case c
+ of '\0': result = "\\0"
+ of '\x01'..'\x1F', '\x80'..'\xFF': result = "\\x" & strutils.toHex(ord(c), 2)
+ of '\'', '\"', '\\': result = '\\' & c
+ else: result = c & ""
+
+proc makeNimString(s: string): string =
+ result = "\""
+ for i in countup(0, len(s)-1): add(result, toNimChar(s[i]))
+ add(result, '\"')
proc putComment(g: var TSrcGen, s: string) =
- if s.len == 0: return
+ if s.isNil: return
var i = 0
- let hi = s.len - 1
- let isCode = (s.len >= 2) and (s[1] != ' ')
- let ind = g.col
+ var isCode = (len(s) >= 2) and (s[1] != ' ')
+ var ind = g.lineLen
var com = "## "
- while i <= hi:
+ while true:
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 s[i] == '\x0A': inc(i)
optNL(g, ind)
- of '\n':
+ of '\x0A':
put(g, tkComment, com)
com = "## "
inc(i)
optNL(g, ind)
- of ' ', '\t':
- com.add(s[i])
+ of ' ', '\x09':
+ add(com, s[i])
inc(i)
else:
# we may break the comment into a multi-line comment if the line
# gets too long:
# compute length of the following word:
var j = i
- while j <= hi and s[j] > ' ': inc(j)
- if not isCode and (g.col + (j - i) > MaxLineLen):
+ while s[j] > ' ': inc(j)
+ if not isCode and (g.lineLen + (j - i) > MaxLineLen):
put(g, tkComment, com)
optNL(g, ind)
com = "## "
- while i <= hi and s[i] > ' ':
- com.add(s[i])
+ while s[i] > ' ':
+ add(com, s[i])
inc(i)
put(g, tkComment, com)
optNL(g)
proc maxLineLength(s: string): int =
- if s.len == 0: return 0
+ if s.isNil: return 0
var i = 0
- let hi = s.len - 1
var lineLen = 0
- while i <= hi:
+ while true:
case s[i]
of '\0':
break
- of '\r':
+ of '\x0D':
inc(i)
- if i <= hi and s[i] == '\n': inc(i)
+ if s[i] == '\x0A': inc(i)
result = max(result, lineLen)
lineLen = 0
- of '\n':
+ of '\x0A':
inc(i)
result = max(result, lineLen)
lineLen = 0
@@ -262,329 +225,274 @@ proc maxLineLength(s: string): int =
inc(lineLen)
inc(i)
-proc putRawStr(g: var TSrcGen, kind: TokType, s: string) =
+proc putRawStr(g: var TSrcGen, kind: TTokType, s: string) =
var i = 0
- let hi = s.len - 1
+ var hi = len(s) - 1
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)
optNL(g, 0)
else:
- str.add(s[i])
+ add(str, s[i])
inc(i)
put(g, kind, str)
proc containsNL(s: string): bool =
- for i in 0.. 0
+proc shouldRenderComment(g: var TSrcGen, n: PNode): bool =
+ result = false
+ if n.comment != nil:
+ result = (renderNoComments notin g.flags) or
+ (renderDocComments in g.flags)
proc gcom(g: var TSrcGen, n: PNode) =
assert(n != nil)
if shouldRenderComment(g, n):
- var oneSpaceAdded = 0
- if (g.pendingNL < 0) and (g.buf.len > 0) and (g.buf[^1] != ' '):
+ if (g.pendingNL < 0) and (len(g.buf) > 0) and (g.buf[len(g.buf)-1] != ' '):
put(g, tkSpaces, Space)
- oneSpaceAdded = 1
# Before long comments we cannot make sure that a newline is generated,
# because this might be wrong. But it is no problem in practice.
- if (g.pendingNL < 0) and (g.buf.len > 0) and
- (g.col < LineCommentColumn):
+ if (g.pendingNL < 0) and (len(g.buf) > 0) and
+ (g.lineLen < LineCommentColumn):
var ml = maxLineLength(n.comment)
if ml + LineCommentColumn <= MaxLineLen:
- put(g, tkSpaces, spaces(LineCommentColumn - g.col))
- dec g.col, oneSpaceAdded
+ put(g, tkSpaces, spaces(LineCommentColumn - g.lineLen))
putComment(g, n.comment) #assert(g.comStack[high(g.comStack)] = n);
proc gcoms(g: var TSrcGen) =
- for i in 0..high(g.comStack): gcom(g, g.comStack[i])
+ for i in countup(0, high(g.comStack)): gcom(g, g.comStack[i])
popAllComs(g)
-proc lsub(g: TSrcGen; n: PNode): int
-proc litAux(g: TSrcGen; n: PNode, x: BiggestInt, size: int): string =
+proc lsub(n: PNode): int
+proc litAux(n: PNode, x: BiggestInt, size: int): string =
proc skip(t: PType): PType =
result = t
- while result != nil and result.kind in {tyGenericInst, tyRange, tyVar,
- tyLent, tyDistinct, tyOrdinal, tyAlias, tySink}:
+ while result.kind in {tyGenericInst, tyRange, tyVar, tyDistinct, tyOrdinal,
+ tyConst, tyMutable}:
result = lastSon(result)
-
- let typ = n.typ.skip
- if typ != nil and typ.kind in {tyBool, tyEnum}:
- if sfPure in typ.sym.flags:
- result = typ.sym.name.s & '.'
- let enumfields = typ.n
+ if n.typ != nil and n.typ.skip.kind in {tyBool, tyEnum}:
+ let enumfields = n.typ.skip.n
# we need a slow linear search because of enums with holes:
for e in items(enumfields):
- if e.sym.position == x:
- result &= e.sym.name.s
- return
+ if e.sym.position == x: return e.sym.name.s
- if nfBase2 in n.flags: result = "0b" & toBin(x, size * 8)
- elif nfBase8 in n.flags:
- var y = if size < sizeof(BiggestInt): x and ((1.BiggestInt shl (size*8)) - 1)
- else: x
- result = "0o" & toOct(y, size * 3)
- elif nfBase16 in n.flags: result = "0x" & toHex(x, size * 2)
- else: result = $x
-
-proc ulitAux(g: TSrcGen; n: PNode, x: BiggestInt, size: int): string =
if nfBase2 in n.flags: result = "0b" & toBin(x, size * 8)
elif nfBase8 in n.flags: result = "0o" & toOct(x, size * 3)
elif nfBase16 in n.flags: result = "0x" & toHex(x, size * 2)
- else: result = $cast[BiggestUInt](x)
+ else: result = $x
-proc atom(g: TSrcGen; n: PNode): string =
- when defined(nimpretty):
- doAssert g.config != nil, "g.config not initialized!"
- let comment = if n.info.commentOffsetA < n.info.commentOffsetB:
- " " & fileSection(g.config, g.fid, n.info.commentOffsetA, n.info.commentOffsetB)
- else:
- ""
- if n.info.offsetA <= n.info.offsetB:
- # for some constructed tokens this can not be the case and we're better
- # off to not mess with the offset then.
- return fileSection(g.config, g.fid, n.info.offsetA, n.info.offsetB) & comment
+proc ulitAux(n: PNode, x: BiggestInt, size: int): string =
+ if nfBase2 in n.flags: result = "0b" & toBin(x, size * 8)
+ elif nfBase8 in n.flags: result = "0o" & toOct(x, size * 3)
+ elif nfBase16 in n.flags: result = "0x" & toHex(x, size * 2)
+ else: result = $x
+ # XXX proper unsigned output!
+
+proc atom(n: PNode): string =
var f: float32
case n.kind
of nkEmpty: result = ""
of nkIdent: result = n.ident.s
of nkSym: result = n.sym.name.s
- of nkStrLit: result = ""; result.addQuoted(n.strVal)
- of nkRStrLit: result = "r\"" & replace(n.strVal, "\"", "\"\"") & '\"'
+ of nkStrLit: result = makeNimString(n.strVal)
+ of nkRStrLit: result = "r\"" & replace(n.strVal, "\"", "\"\"") & '\"'
of nkTripleStrLit: result = "\"\"\"" & n.strVal & "\"\"\""
- of nkCharLit:
- result = "\'"
- result.addEscapedChar(chr(int(n.intVal)));
- result.add '\''
- of nkIntLit: result = litAux(g, n, n.intVal, 4)
- of nkInt8Lit: result = litAux(g, n, n.intVal, 1) & "\'i8"
- of nkInt16Lit: result = litAux(g, n, n.intVal, 2) & "\'i16"
- of nkInt32Lit: result = litAux(g, n, n.intVal, 4) & "\'i32"
- of nkInt64Lit: result = litAux(g, n, n.intVal, 8) & "\'i64"
- of nkUIntLit: result = ulitAux(g, n, n.intVal, 4) & "\'u"
- of nkUInt8Lit: result = ulitAux(g, n, n.intVal, 1) & "\'u8"
- of nkUInt16Lit: result = ulitAux(g, n, n.intVal, 2) & "\'u16"
- of nkUInt32Lit: result = ulitAux(g, n, n.intVal, 4) & "\'u32"
- of nkUInt64Lit: result = ulitAux(g, n, n.intVal, 8) & "\'u64"
+ of nkCharLit: result = '\'' & toNimChar(chr(int(n.intVal))) & '\''
+ of nkIntLit: result = litAux(n, n.intVal, 4)
+ of nkInt8Lit: result = litAux(n, n.intVal, 1) & "\'i8"
+ of nkInt16Lit: result = litAux(n, n.intVal, 2) & "\'i16"
+ of nkInt32Lit: result = litAux(n, n.intVal, 4) & "\'i32"
+ of nkInt64Lit: result = litAux(n, n.intVal, 8) & "\'i64"
+ of nkUIntLit: result = ulitAux(n, n.intVal, 4) & "\'u"
+ of nkUInt8Lit: result = ulitAux(n, n.intVal, 1) & "\'u8"
+ of nkUInt16Lit: result = ulitAux(n, n.intVal, 2) & "\'u16"
+ of nkUInt32Lit: result = ulitAux(n, n.intVal, 4) & "\'u32"
+ of nkUInt64Lit: result = ulitAux(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(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(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(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("rnimsyn.atom " & $n.kind)
result = ""
-proc lcomma(g: TSrcGen; n: PNode, start: int = 0, theEnd: int = - 1): int =
+proc lcomma(n: PNode, start: int = 0, theEnd: int = - 1): int =
assert(theEnd < 0)
result = 0
- for i in start..n.len + theEnd:
- let param = n[i]
- if nfDefaultParam notin param.flags:
- inc(result, lsub(g, param))
- inc(result, 2) # for ``, ``
+ for i in countup(start, sonsLen(n) + theEnd):
+ inc(result, lsub(n.sons[i]))
+ inc(result, 2) # for ``, ``
if result > 0:
dec(result, 2) # last does not get a comma!
-proc lsons(g: TSrcGen; n: PNode, start: int = 0, theEnd: int = - 1): int =
+proc lsons(n: PNode, start: int = 0, theEnd: int = - 1): int =
assert(theEnd < 0)
result = 0
- for i in start..n.len + theEnd: inc(result, lsub(g, n[i]))
+ for i in countup(start, sonsLen(n) + theEnd): inc(result, lsub(n.sons[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 =
+proc lsub(n: PNode): int =
# computes the length of a tree
if isNil(n): return 0
- if shouldRenderComment(g, n): return MaxLineLen + 1
+ if n.comment != nil: return MaxLineLen + 1
case n.kind
of nkEmpty: result = 0
of nkTripleStrLit:
if containsNL(n.strVal): result = MaxLineLen + 1
- else: result = atom(g, n).len
+ else: result = len(atom(n))
of succ(nkEmpty)..pred(nkTripleStrLit), succ(nkTripleStrLit)..nkNilLit:
- result = atom(g, n).len
+ result = len(atom(n))
of nkCall, nkBracketExpr, nkCurlyExpr, nkConv, nkPattern, nkObjConstr:
- result = lsub(g, n[0]) + lcomma(g, n, 1) + 2
- of nkHiddenStdConv, nkHiddenSubConv, nkHiddenCallConv: result = lsub(g, n[1])
- of nkCast: result = lsub(g, n[0]) + lsub(g, n[1]) + len("cast[]()")
- of nkAddr: result = (if n.len>0: lsub(g, n[0]) + len("addr()") else: 4)
- of nkStaticExpr: result = lsub(g, n[0]) + len("static_")
- of nkHiddenAddr, nkHiddenDeref, nkStringToCString, nkCStringToString: result = lsub(g, n[0])
- of nkCommand: result = lsub(g, n[0]) + lcomma(g, n, 1) + 1
- of nkExprEqExpr, nkAsgn, nkFastAsgn: result = lsons(g, n) + 3
- of nkPar, nkCurly, nkBracket, nkClosure: result = lcomma(g, n) + 2
- of nkTupleConstr:
- # assume the trailing comma:
- result = lcomma(g, n) + 3
- of nkArgList: result = lcomma(g, n)
+ result = lsub(n.sons[0]) + lcomma(n, 1) + 2
+ of nkHiddenStdConv, nkHiddenSubConv, nkHiddenCallConv: result = lsub(n[1])
+ of nkCast: result = lsub(n.sons[0]) + lsub(n.sons[1]) + len("cast[]()")
+ of nkAddr: result = (if n.len>0: lsub(n.sons[0]) + len("addr()") else: 4)
+ of nkStaticExpr: result = lsub(n.sons[0]) + len("static_")
+ of nkHiddenAddr, nkHiddenDeref: result = lsub(n.sons[0])
+ of nkCommand: result = lsub(n.sons[0]) + lcomma(n, 1) + 1
+ of nkExprEqExpr, nkAsgn, nkFastAsgn: result = lsons(n) + 3
+ of nkPar, nkCurly, nkBracket, nkClosure: result = lcomma(n) + 2
+ of nkArgList: result = lcomma(n)
of nkTableConstr:
- result = if n.len > 0: lcomma(g, n) + 2 else: len("{:}")
+ result = if n.len > 0: lcomma(n) + 2 else: len("{:}")
of nkClosedSymChoice, nkOpenSymChoice:
- if n.len > 0: result += lsub(g, n[0])
- of nkTupleTy: result = lcomma(g, n) + len("tuple[]")
+ result = lsons(n) + len("()") + sonsLen(n) - 1
+ of nkTupleTy: result = lcomma(n) + len("tuple[]")
of nkTupleClassTy: result = len("tuple")
- of nkDotExpr: result = lsons(g, n) + 1
- of nkBind: result = lsons(g, n) + len("bind_")
- of nkBindStmt: result = lcomma(g, n) + len("bind_")
- of nkMixinStmt: result = lcomma(g, n) + len("mixin_")
- of nkCheckedFieldExpr: result = lsub(g, n[0])
- of nkLambda: result = lsons(g, n) + len("proc__=_")
- of nkDo: result = lsons(g, n) + len("do__:_")
+ of nkDotExpr: result = lsons(n) + 1
+ of nkBind: result = lsons(n) + len("bind_")
+ of nkBindStmt: result = lcomma(n) + len("bind_")
+ of nkMixinStmt: result = lcomma(n) + len("mixin_")
+ of nkCheckedFieldExpr: result = lsub(n.sons[0])
+ of nkLambda: result = lsons(n) + len("proc__=_")
+ of nkDo: result = lsons(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
- of nkVarTuple:
- if n[^1].kind == nkEmpty:
- result = lcomma(g, n, 0, - 2) + len("()")
- else:
- result = lcomma(g, n, 0, - 3) + len("() = ") + lsub(g, lastSon(n))
- of nkChckRangeF: result = len("chckRangeF") + 2 + lcomma(g, n)
- of nkChckRange64: result = len("chckRange64") + 2 + lcomma(g, n)
- of nkChckRange: result = len("chckRange") + 2 + lcomma(g, n)
- of nkObjDownConv, nkObjUpConv:
+ result = lcomma(n, 0, - 3)
+ var L = sonsLen(n)
+ if n.sons[L - 2].kind != nkEmpty: result = result + lsub(n.sons[L - 2]) + 2
+ if n.sons[L - 1].kind != nkEmpty: result = result + lsub(n.sons[L - 1]) + 3
+ of nkVarTuple: result = lcomma(n, 0, - 3) + len("() = ") + lsub(lastSon(n))
+ of nkChckRangeF: result = len("chckRangeF") + 2 + lcomma(n)
+ of nkChckRange64: result = len("chckRange64") + 2 + lcomma(n)
+ of nkChckRange: result = len("chckRange") + 2 + lcomma(n)
+ of nkObjDownConv, nkObjUpConv, nkStringToCString, nkCStringToString:
result = 2
- if n.len >= 1: result += lsub(g, n[0])
- result += lcomma(g, n, 1)
- of nkExprColonExpr: result = lsons(g, n) + 2
- of nkInfix: result = lsons(g, n) + 2
+ if sonsLen(n) >= 1: result = result + lsub(n.sons[0])
+ result = result + lcomma(n, 1)
+ of nkExprColonExpr: result = lsons(n) + 2
+ of nkInfix: result = lsons(n) + 2
of nkPrefix:
- result = lsons(g, n)+1+(if n.len > 0 and n[1].kind == nkInfix: 2 else: 0)
- of nkPostfix: result = lsons(g, n)
- of nkCallStrLit: result = lsons(g, n)
- of nkPragmaExpr: result = lsub(g, n[0]) + lcomma(g, n, 1)
- of nkRange: result = lsons(g, n) + 2
- of nkDerefExpr: result = lsub(g, n[0]) + 2
- of nkAccQuoted: result = lsons(g, n) + 2
+ result = lsons(n)+1+(if n.len > 0 and n.sons[1].kind == nkInfix: 2 else: 0)
+ of nkPostfix: result = lsons(n)
+ of nkCallStrLit: result = lsons(n)
+ of nkPragmaExpr: result = lsub(n.sons[0]) + lcomma(n, 1)
+ of nkRange: result = lsons(n) + 2
+ of nkDerefExpr: result = lsub(n.sons[0]) + 2
+ of nkAccQuoted: result = lsons(n) + 2
of nkIfExpr:
- result = lsub(g, n[0][0]) + lsub(g, n[0][1]) + lsons(g, n, 1) +
+ result = lsub(n.sons[0].sons[0]) + lsub(n.sons[0].sons[1]) + lsons(n, 1) +
len("if_:_")
- of nkElifExpr: result = lsons(g, n) + len("_elif_:_")
- of nkElseExpr: result = lsub(g, n[0]) + len("_else:_") # type descriptions
- 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 nkElifExpr: result = lsons(n) + len("_elif_:_")
+ of nkElseExpr: result = lsub(n.sons[0]) + len("_else:_") # type descriptions
+ of nkTypeOfExpr: result = (if n.len > 0: lsub(n.sons[0]) else: 0)+len("type()")
+ of nkRefTy: result = (if n.len > 0: lsub(n.sons[0])+1 else: 0) + len("ref")
+ of nkPtrTy: result = (if n.len > 0: lsub(n.sons[0])+1 else: 0) + len("ptr")
+ of nkVarTy: result = (if n.len > 0: lsub(n.sons[0])+1 else: 0) + len("var")
of nkDistinctTy:
- result = len("distinct") + (if n.len > 0: lsub(g, n[0])+1 else: 0)
+ result = len("distinct") + (if n.len > 0: lsub(n.sons[0])+1 else: 0)
if n.len > 1:
result += (if n[1].kind == nkWith: len("_with_") else: len("_without_"))
- result += lcomma(g, n[1])
- of nkStaticTy: result = (if n.len > 0: lsub(g, n[0]) else: 0) +
+ result += lcomma(n[1])
+ of nkStaticTy: result = (if n.len > 0: lsub(n.sons[0]) else: 0) +
len("static[]")
- of nkTypeDef: result = lsons(g, n) + 3
- 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 nkTypeDef: result = lsons(n) + 3
+ of nkOfInherit: result = lsub(n.sons[0]) + len("of_")
+ of nkProcTy: result = lsons(n) + len("proc_")
+ of nkIteratorTy: result = lsons(n) + len("iterator_")
+ of nkSharedTy: result = lsons(n) + len("shared_")
of nkEnumTy:
- if n.len > 0:
- result = lsub(g, n[0]) + lcomma(g, n, 1) + len("enum_")
+ if sonsLen(n) > 0:
+ result = lsub(n.sons[0]) + lcomma(n, 1) + len("enum_")
else:
result = len("enum")
- of nkEnumFieldDef: result = lsons(g, n) + 3
+ of nkEnumFieldDef: result = lsons(n) + 3
of nkVarSection, nkLetSection:
- if n.len > 1: result = MaxLineLen + 1
- else: result = lsons(g, n) + len("var_")
+ if sonsLen(n) > 1: result = MaxLineLen + 1
+ else: result = lsons(n) + len("var_")
of nkUsingStmt:
- if n.len > 1: result = MaxLineLen + 1
- else: result = lsons(g, n) + len("using_")
- of nkReturnStmt:
- if n.len > 0 and n[0].kind == nkAsgn:
- result = len("return_") + lsub(g, n[0][1])
- else:
- result = len("return_") + lsub(g, n[0])
- of nkRaiseStmt: result = lsub(g, n[0]) + len("raise_")
- of nkYieldStmt: result = lsub(g, n[0]) + len("yield_")
- of nkDiscardStmt: result = lsub(g, n[0]) + len("discard_")
- of nkBreakStmt: result = lsub(g, n[0]) + len("break_")
- of nkContinueStmt: result = lsub(g, n[0]) + len("continue_")
- of nkPragma: result = lcomma(g, n) + 4
- of nkCommentStmt: result = n.comment.len
- of nkOfBranch: result = lcomma(g, n, 0, - 2) + lsub(g, lastSon(n)) + len("of_:_")
- of nkImportAs: result = lsub(g, n[0]) + len("_as_") + lsub(g, n[1])
- of nkElifBranch: result = lsons(g, n) + len("elif_:_")
- of nkElse: result = lsub(g, n[0]) + len("else:_")
- of nkFinally: result = lsub(g, n[0]) + len("finally:_")
- of nkGenericParams: result = lcomma(g, n) + 2
+ if sonsLen(n) > 1: result = MaxLineLen + 1
+ else: result = lsons(n) + len("using_")
+ of nkReturnStmt: result = lsub(n.sons[0]) + len("return_")
+ of nkRaiseStmt: result = lsub(n.sons[0]) + len("raise_")
+ of nkYieldStmt: result = lsub(n.sons[0]) + len("yield_")
+ of nkDiscardStmt: result = lsub(n.sons[0]) + len("discard_")
+ of nkBreakStmt: result = lsub(n.sons[0]) + len("break_")
+ of nkContinueStmt: result = lsub(n.sons[0]) + len("continue_")
+ of nkPragma: result = lcomma(n) + 4
+ of nkCommentStmt: result = if n.comment.isNil: 0 else: len(n.comment)
+ of nkOfBranch: result = lcomma(n, 0, - 2) + lsub(lastSon(n)) + len("of_:_")
+ of nkImportAs: result = lsub(n.sons[0]) + len("_as_") + lsub(n.sons[1])
+ of nkElifBranch: result = lsons(n) + len("elif_:_")
+ of nkElse: result = lsub(n.sons[0]) + len("else:_")
+ of nkFinally: result = lsub(n.sons[0]) + len("finally:_")
+ of nkGenericParams: result = lcomma(n) + 2
of nkFormalParams:
- result = lcomma(g, n, 1) + 2
- if n[0].kind != nkEmpty: result += lsub(g, n[0]) + 2
+ result = lcomma(n, 1) + 2
+ if n.sons[0].kind != nkEmpty: result = result + lsub(n.sons[0]) + 2
of nkExceptBranch:
- result = lcomma(g, n, 0, -2) + lsub(g, lastSon(n)) + len("except_:_")
- of nkObjectTy:
- result = len("object_")
+ result = lcomma(n, 0, -2) + lsub(lastSon(n)) + len("except_:_")
else: result = MaxLineLen + 1
proc fits(g: TSrcGen, x: int): bool =
- result = x <= MaxLineLen
+ result = x + g.lineLen <= MaxLineLen
type
TSubFlag = enum
- rfLongMode, rfInConstExpr
+ rfLongMode, rfNoIndent, rfInConstExpr
TSubFlags = set[TSubFlag]
TContext = tuple[spacing: int, flags: TSubFlags]
@@ -595,65 +503,43 @@ 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
if n.isNil: return false
- if n.comment.len > 0: return true
+ if n.comment != nil: return true
case n.kind
of nkEmpty..nkNilLit: discard
else:
- for i in 0.. oldLen:
- putWithSpace(g, separator, $separator)
- if shouldRenderComment(g) and hasCom(n[i]):
+ putWithSpace(g, separator, TokTypeToStr[separator])
+ if hasCom(n.sons[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
@@ -674,26 +560,26 @@ proc gsemicolon(g: var TSrcGen, n: PNode, start: int = 0, theEnd: int = - 1) =
proc gsons(g: var TSrcGen, n: PNode, c: TContext, start: int = 0,
theEnd: int = - 1) =
- for i in start..n.len + theEnd: gsub(g, n[i], c)
+ for i in countup(start, sonsLen(n) + theEnd): gsub(g, n.sons[i], c)
-proc gsection(g: var TSrcGen, n: PNode, c: TContext, kind: TokType,
+proc gsection(g: var TSrcGen, n: PNode, c: TContext, kind: TTokType,
k: string) =
- if n.len == 0: return # empty var sections are possible
+ if sonsLen(n) == 0: return # empty var sections are possible
putWithSpace(g, kind, k)
gcoms(g)
indentNL(g)
- for i in 0.. MaxLineLen):
+ for i in countup(start, sonsLen(n) + theEnd):
+ if (lsub(n.sons[i]) > MaxLineLen):
result = true
break
@@ -701,61 +587,51 @@ proc gstmts(g: var TSrcGen, n: PNode, c: TContext, doIndent=true) =
if n.kind == nkEmpty: return
if n.kind in {nkStmtList, nkStmtListExpr, nkStmtListType}:
if doIndent: indentNL(g)
- for i in 0.. 0:
- optNL(g, n[i-1], n[i])
+ for i in countup(0, sonsLen(n) - 1):
+ optNL(g)
+ if n.sons[i].kind in {nkStmtList, nkStmtListExpr, nkStmtListType}:
+ gstmts(g, n.sons[i], c, doIndent=false)
else:
- optNL(g)
- 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.sons[i])
gcoms(g)
if doIndent: dedent(g)
else:
- indentNL(g)
+ if rfLongMode in c.flags: indentNL(g)
gsub(g, n)
gcoms(g)
- dedent(g)
optNL(g)
-
-
-proc gcond(g: var TSrcGen, n: PNode) =
- if n.kind == nkStmtListExpr:
- put(g, tkParLe, "(")
- gsub(g, n)
- if n.kind == nkStmtListExpr:
- put(g, tkParRi, ")")
+ if rfLongMode in c.flags: dedent(g)
proc gif(g: var TSrcGen, n: PNode) =
var c: TContext
- gcond(g, n[0][0])
+ gsub(g, n.sons[0].sons[0])
initContext(c)
putWithSpace(g, tkColon, ":")
- if longMode(g, n) or (lsub(g, n[0][1]) + g.lineLen > MaxLineLen):
+ if longMode(n) or (lsub(n.sons[0].sons[1]) + g.lineLen > MaxLineLen):
incl(c.flags, rfLongMode)
gcoms(g) # a good place for comments
- gstmts(g, n[0][1], c)
- for i in 1.. MaxLineLen):
+ if longMode(n) or (lsub(n.sons[1]) + g.lineLen > MaxLineLen):
incl(c.flags, rfLongMode)
gcoms(g) # a good place for comments
- gstmts(g, n[1], c)
+ gstmts(g, n.sons[1], c)
proc gpattern(g: var TSrcGen, n: PNode) =
var c: TContext
put(g, tkCurlyLe, "{")
initContext(c)
- if longMode(g, n) or (lsub(g, n[0]) + g.lineLen > MaxLineLen):
+ if longMode(n) or (lsub(n.sons[0]) + g.lineLen > MaxLineLen):
incl(c.flags, rfLongMode)
gcoms(g) # a good place for comments
gstmts(g, n, c)
@@ -763,93 +639,96 @@ proc gpattern(g: var TSrcGen, n: PNode) =
proc gpragmaBlock(g: var TSrcGen, n: PNode) =
var c: TContext
- gsub(g, n[0])
+ gsub(g, n.sons[0])
putWithSpace(g, tkColon, ":")
initContext(c)
- if longMode(g, n) or (lsub(g, n[1]) + g.lineLen > MaxLineLen):
+ if longMode(n) or (lsub(n.sons[1]) + g.lineLen > MaxLineLen):
incl(c.flags, rfLongMode)
gcoms(g) # a good place for comments
- gstmts(g, n[1], c)
+ gstmts(g, n.sons[1], c)
proc gtry(g: var TSrcGen, n: PNode) =
var c: TContext
put(g, tkTry, "try")
putWithSpace(g, tkColon, ":")
initContext(c)
- if longMode(g, n) or (lsub(g, n[0]) + g.lineLen > MaxLineLen):
+ if longMode(n) or (lsub(n.sons[0]) + g.lineLen > MaxLineLen):
incl(c.flags, rfLongMode)
gcoms(g) # a good place for comments
- gstmts(g, n[0], c)
+ gstmts(g, n.sons[0], c)
gsons(g, n, c, 1)
proc gfor(g: var TSrcGen, n: PNode) =
var c: TContext
+ var length = sonsLen(n)
putWithSpace(g, tkFor, "for")
initContext(c)
- if longMode(g, n) or
- (lsub(g, n[^1]) + lsub(g, n[^2]) + 6 + g.lineLen > MaxLineLen):
+ if longMode(n) or
+ (lsub(n.sons[length - 1]) + lsub(n.sons[length - 2]) + 6 + g.lineLen >
+ MaxLineLen):
incl(c.flags, rfLongMode)
gcomma(g, n, c, 0, - 3)
put(g, tkSpaces, Space)
putWithSpace(g, tkIn, "in")
- gsub(g, n[^2], c)
+ gsub(g, n.sons[length - 2], c)
putWithSpace(g, tkColon, ":")
gcoms(g)
- gstmts(g, n[^1], c)
+ gstmts(g, n.sons[length - 1], c)
+
+proc gmacro(g: var TSrcGen, n: PNode) =
+ var c: TContext
+ initContext(c)
+ gsub(g, n.sons[0])
+ putWithSpace(g, tkColon, ":")
+ if longMode(n) or (lsub(n.sons[1]) + g.lineLen > MaxLineLen):
+ incl(c.flags, rfLongMode)
+ gcoms(g)
+ gsons(g, n, c, 1)
proc gcase(g: var TSrcGen, n: PNode) =
var c: TContext
initContext(c)
- if n.len == 0: return
- var last = if n[^1].kind == nkElse: -2 else: -1
- if longMode(g, n, 0, last): incl(c.flags, rfLongMode)
+ var length = sonsLen(n)
+ var last = if n.sons[length-1].kind == nkElse: -2 else: -1
+ if longMode(n, 0, last): incl(c.flags, rfLongMode)
putWithSpace(g, tkCase, "case")
- gcond(g, n[0])
+ gsub(g, n.sons[0])
gcoms(g)
optNL(g)
gsons(g, n, c, 1, last)
if last == - 2:
initContext(c)
- if longMode(g, n[^1]): incl(c.flags, rfLongMode)
- gsub(g, n[^1], c)
-
-proc genSymSuffix(result: var string, s: PSym) {.inline.} =
- if sfGenSym in s.flags:
- result.add '_'
- result.addInt s.id
+ if longMode(n.sons[length - 1]): incl(c.flags, rfLongMode)
+ gsub(g, n.sons[length - 1], c)
proc gproc(g: var TSrcGen, n: PNode) =
var c: TContext
- if n[namePos].kind == nkSym:
- let s = n[namePos].sym
- var ret = renderDefinitionName(s)
- ret.genSymSuffix(s)
- put(g, tkSymbol, ret)
+ if n.sons[namePos].kind == nkSym:
+ put(g, tkSymbol, renderDefinitionName(n.sons[namePos].sym))
else:
- gsub(g, n[namePos])
+ gsub(g, n.sons[namePos])
- if n[patternPos].kind != nkEmpty:
- gpattern(g, n[patternPos])
- let oldInGenericParams = g.inGenericParams
- g.inGenericParams = true
+ if n.sons[patternPos].kind != nkEmpty:
+ gpattern(g, n.sons[patternPos])
+ let oldCheckAnon = g.checkAnon
+ g.checkAnon = true
if renderNoBody in g.flags and n[miscPos].kind != nkEmpty and
n[miscPos][1].kind != nkEmpty:
gsub(g, n[miscPos][1])
else:
- gsub(g, n[genericParamsPos])
- g.inGenericParams = oldInGenericParams
- gsub(g, n[paramsPos])
- if renderNoPragmas notin g.flags:
- gsub(g, n[pragmasPos])
+ gsub(g, n.sons[genericParamsPos])
+ g.checkAnon = oldCheckAnon
+ gsub(g, n.sons[paramsPos])
+ gsub(g, n.sons[pragmasPos])
if renderNoBody notin g.flags:
- if n[bodyPos].kind != nkEmpty:
+ if n.sons[bodyPos].kind != nkEmpty:
put(g, tkSpaces, Space)
putWithSpace(g, tkEquals, "=")
indentNL(g)
gcoms(g)
dedent(g)
initContext(c)
- gstmts(g, n[bodyPos], c)
+ gstmts(g, n.sons[bodyPos], c)
putNL(g)
else:
indentNL(g)
@@ -870,82 +749,56 @@ 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:
+ if n.sons[0].kind != nkEmpty:
putWithSpace(g, tkBlock, "block")
- gsub(g, n[0])
+ gsub(g, n.sons[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):
+ if longMode(n) or (lsub(n.sons[1]) + g.lineLen > MaxLineLen):
incl(c.flags, rfLongMode)
gcoms(g)
- gstmts(g, n[1], c)
+ # XXX I don't get why this is needed here! gstmts should already handle this!
+ indentNL(g)
+ gstmts(g, n.sons[1], c)
+ dedent(g)
proc gstaticStmt(g: var TSrcGen, n: PNode) =
var c: TContext
putWithSpace(g, tkStatic, "static")
putWithSpace(g, tkColon, ":")
initContext(c)
- if longMode(g, n) or (lsub(g, n[0]) + g.lineLen > MaxLineLen):
+ if longMode(n) or (lsub(n.sons[0]) + g.lineLen > MaxLineLen):
incl(c.flags, rfLongMode)
gcoms(g) # a good place for comments
- gstmts(g, n[0], c)
+ gstmts(g, n.sons[0], c)
proc gasm(g: var TSrcGen, n: PNode) =
putWithSpace(g, tkAsm, "asm")
- gsub(g, n[0])
+ gsub(g, n.sons[0])
gcoms(g)
- if n.len > 1:
- gsub(g, n[1])
+ if n.sons.len > 1:
+ gsub(g, n.sons[1])
proc gident(g: var TSrcGen, n: PNode) =
- if g.inGenericParams and n.kind == nkSym:
- if sfAnon in n.sym.flags or
- (n.typ != nil and tfImplicitTypeParam in n.typ.flags): return
-
- var t: TokType
- var s = atom(g, n)
- if s.len > 0 and s[0] in lexer.SymChars:
- if n.kind == nkIdent:
+ if g.checkAnon and n.kind == nkSym and sfAnon in n.sym.flags: return
+ var t: TTokType
+ var s = atom(n)
+ if (s[0] in lexer.SymChars):
+ if (n.kind == nkIdent):
if (n.ident.id < ord(tokKeywordLow) - ord(tkSymbol)) or
(n.ident.id > ord(tokKeywordHigh) - ord(tkSymbol)):
t = tkSymbol
else:
- t = TokType(n.ident.id + ord(tkSymbol))
+ t = TTokType(n.ident.id + ord(tkSymbol))
else:
t = tkSymbol
else:
t = tkOpr
- if renderIr in g.flags and n.kind == nkSym:
- let localId = disamb(g, n.sym)
- if localId != 0 and n.sym.magic == mNone:
- s.add '_'
- s.addInt localId
- if sfCursor in n.sym.flags:
- s.add "_cursor"
- elif n.kind == nkSym and (renderIds in g.flags or sfGenSym in n.sym.flags or n.sym.kind == skTemp):
- s.add '_'
- s.addInt n.sym.id
- when defined(debugMagics):
- s.add '_'
- s.add $n.sym.magic
- put(g, t, s, if n.kind == nkSym and renderSyms in g.flags: n.sym else: nil)
+ put(g, t, s)
+ if n.kind == nkSym and renderIds in g.flags: put(g, tkIntLit, $n.sym.id)
proc doParamsAux(g: var TSrcGen, params: PNode) =
if params.len > 1:
@@ -953,10 +806,9 @@ proc doParamsAux(g: var TSrcGen, params: PNode) =
gsemicolon(g, params, 1)
put(g, tkParRi, ")")
- if params.len > 0 and params[0].kind != nkEmpty:
- put(g, tkSpaces, Space)
+ if params.len > 0 and params.sons[0].kind != nkEmpty:
putWithSpace(g, tkOpr, "->")
- gsub(g, params[0])
+ gsub(g, params.sons[0])
proc gsub(g: var TSrcGen; n: PNode; i: int) =
if i < n.len:
@@ -964,204 +816,64 @@ proc gsub(g: var TSrcGen; n: PNode; i: int) =
else:
put(g, tkOpr, "<<" & $i & "th child missing for " & $n.kind & " >>")
-type
- BracketKind = enum
- bkNone, bkBracket, bkBracketAsgn, bkCurly, bkCurlyAsgn
+proc isBracket*(n: PNode): bool =
+ case n.kind
+ of nkClosedSymChoice, nkOpenSymChoice:
+ if n.len > 0: result = isBracket(n[0])
+ of nkSym: result = n.sym.name.s == "[]"
+ else: result = false
-proc bracketKind*(g: TSrcGen, n: PNode): BracketKind =
- if renderIds notin g.flags:
- case n.kind
- of nkClosedSymChoice, nkOpenSymChoice:
- if n.len > 0: result = bracketKind(g, n[0])
- of nkSym:
- result = case n.sym.name.s
- of "[]": bkBracket
- of "[]=": bkBracketAsgn
- of "{}": bkCurly
- of "{}=": bkCurlyAsgn
- else: bkNone
- else: result = bkNone
-
-proc skipHiddenNodes(n: PNode): PNode =
- result = n
- while result != nil:
- if result.kind in {nkHiddenStdConv, nkHiddenSubConv, nkHiddenCallConv} and result.len > 1:
- result = result[1]
- elif result.kind in {nkCheckedFieldExpr, nkHiddenAddr, nkHiddenDeref, nkStringToCString, nkCStringToString} and
- result.len > 0:
- result = result[0]
- 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 + {'[', '{', '\''}
- if n == nil: return
- let ident = n.getPIdent
- if ident != nil and ident.s[0] in backticksNeeded:
- put(g, tkAccent, "`")
- gident(g, n)
- put(g, tkAccent, "`")
- else:
- gsub(g, n)
-
-proc infixArgument(g: var TSrcGen, n: PNode, i: int) =
- if i < 1 and i > 2: return
- var needsParenthesis = false
- let nNext = n[i].skipHiddenNodes
- if nNext.kind == nkInfix:
- if nNext[0].kind in {nkSym, nkIdent} and n[0].kind in {nkSym, nkIdent}:
- let nextId = if nNext[0].kind == nkSym: nNext[0].sym.name else: nNext[0].ident
- let nnId = if n[0].kind == nkSym: n[0].sym.name else: n[0].ident
- if i == 1:
- if getPrecedence(nextId) < getPrecedence(nnId):
- needsParenthesis = true
- elif i == 2:
- if getPrecedence(nextId) <= getPrecedence(nnId):
- needsParenthesis = true
- if needsParenthesis:
- put(g, tkParLe, "(")
- gsub(g, n, i)
- 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 != nil: pushCom(g, n)
case n.kind # atoms:
- of nkTripleStrLit: put(g, tkTripleStrLit, atom(g, n))
+ of nkTripleStrLit: putRawStr(g, tkTripleStrLit, n.strVal)
of nkEmpty: discard
- of nkType: put(g, tkInvalid, atom(g, n))
+ of nkType: put(g, tkInvalid, atom(n))
of nkSym, nkIdent: gident(g, n)
- of nkIntLit: put(g, tkIntLit, atom(g, n))
- of nkInt8Lit: put(g, tkInt8Lit, atom(g, n))
- of nkInt16Lit: put(g, tkInt16Lit, atom(g, n))
- of nkInt32Lit: put(g, tkInt32Lit, atom(g, n))
- of nkInt64Lit: put(g, tkInt64Lit, atom(g, n))
- of nkUIntLit: put(g, tkUIntLit, atom(g, n))
- of nkUInt8Lit: put(g, tkUInt8Lit, atom(g, n))
- of nkUInt16Lit: put(g, tkUInt16Lit, atom(g, n))
- of nkUInt32Lit: put(g, tkUInt32Lit, atom(g, n))
- of nkUInt64Lit: put(g, tkUInt64Lit, atom(g, n))
- of nkFloatLit: put(g, tkFloatLit, atom(g, n))
- of nkFloat32Lit: put(g, tkFloat32Lit, atom(g, n))
- of nkFloat64Lit: put(g, tkFloat64Lit, atom(g, n))
- of nkFloat128Lit: put(g, tkFloat128Lit, atom(g, n))
- of nkStrLit: put(g, tkStrLit, atom(g, n))
- of nkRStrLit: put(g, tkRStrLit, atom(g, n))
- of nkCharLit: put(g, tkCharLit, atom(g, n))
- of nkNilLit: put(g, tkNil, atom(g, n)) # complex expressions
+ of nkIntLit: put(g, tkIntLit, atom(n))
+ of nkInt8Lit: put(g, tkInt8Lit, atom(n))
+ of nkInt16Lit: put(g, tkInt16Lit, atom(n))
+ of nkInt32Lit: put(g, tkInt32Lit, atom(n))
+ of nkInt64Lit: put(g, tkInt64Lit, atom(n))
+ of nkUIntLit: put(g, tkUIntLit, atom(n))
+ of nkUInt8Lit: put(g, tkUInt8Lit, atom(n))
+ of nkUInt16Lit: put(g, tkUInt16Lit, atom(n))
+ of nkUInt32Lit: put(g, tkUInt32Lit, atom(n))
+ of nkUInt64Lit: put(g, tkUInt64Lit, atom(n))
+ of nkFloatLit: put(g, tkFloatLit, atom(n))
+ of nkFloat32Lit: put(g, tkFloat32Lit, atom(n))
+ of nkFloat64Lit: put(g, tkFloat64Lit, atom(n))
+ of nkFloat128Lit: put(g, tkFloat128Lit, atom(n))
+ of nkStrLit: put(g, tkStrLit, atom(n))
+ of nkRStrLit: put(g, tkRStrLit, atom(n))
+ of nkCharLit: put(g, tkCharLit, atom(n))
+ of nkNilLit: put(g, tkNil, atom(n)) # complex expressions
of nkCall, nkConv, nkDotCall, nkPattern, nkObjConstr:
- if n.len > 1 and n.lastSon.kind in postExprBlocks:
- accentedName(g, n[0])
- var i = 1
- while i < n.len and n[i].kind notin postExprBlocks: i.inc
- if i > 1:
- put(g, tkParLe, "(")
- gcomma(g, n, 1, i - 1 - n.len)
- put(g, tkParRi, ")")
- postStatements(g, n, i, fromStmtList)
- elif n.len >= 1:
- case bracketKind(g, n[0])
- of bkBracket:
- gsub(g, n, 1)
- put(g, tkBracketLe, "[")
- gcomma(g, n, 2)
- put(g, tkBracketRi, "]")
- of bkBracketAsgn:
- gsub(g, n, 1)
- put(g, tkBracketLe, "[")
- gcomma(g, n, 2, -2)
- put(g, tkBracketRi, "]")
- put(g, tkSpaces, Space)
- putWithSpace(g, tkEquals, "=")
- gsub(g, n, n.len - 1)
- of bkCurly:
- gsub(g, n, 1)
- put(g, tkCurlyLe, "{")
- gcomma(g, n, 2)
- put(g, tkCurlyRi, "}")
- of bkCurlyAsgn:
- gsub(g, n, 1)
- put(g, tkCurlyLe, "{")
- gcomma(g, n, 2, -2)
- put(g, tkCurlyRi, "}")
- put(g, tkSpaces, Space)
- putWithSpace(g, tkEquals, "=")
- gsub(g, n, n.len - 1)
- of bkNone:
- accentedName(g, n[0])
- put(g, tkParLe, "(")
- gcomma(g, n, 1)
- put(g, tkParRi, ")")
- else:
- put(g, tkParLe, "(")
- put(g, tkParRi, ")")
- of nkCallStrLit:
- if n.len > 0: accentedName(g, n[0])
- if n.len > 1 and n[1].kind == nkRStrLit:
- put(g, tkRStrLit, '\"' & replace(n[1].strVal, "\"", "\"\"") & '\"')
- else:
+ if n.len > 0 and isBracket(n[0]):
gsub(g, n, 1)
- of nkHiddenStdConv, nkHiddenSubConv:
- if n.len >= 2:
- when false:
- # if {renderIds, renderIr} * g.flags != {}:
- put(g, tkSymbol, "(conv)")
- put(g, tkParLe, "(")
- gsub(g, n[1])
- put(g, tkParRi, ")")
- else:
- gsub(g, n[1])
+ put(g, tkBracketLe, "[")
+ gcomma(g, n, 2)
+ put(g, tkBracketRi, "]")
else:
- put(g, tkSymbol, "(wrong conv)")
- of nkHiddenCallConv:
- if {renderIds, renderIr} * g.flags != {}:
- accentedName(g, n[0])
+ if sonsLen(n) >= 1: gsub(g, n.sons[0])
put(g, tkParLe, "(")
gcomma(g, n, 1)
put(g, tkParRi, ")")
- elif n.len >= 2:
- gsub(g, n[1])
+ of nkCallStrLit:
+ gsub(g, n, 0)
+ if n.len > 1 and n.sons[1].kind == nkRStrLit:
+ put(g, tkRStrLit, '\"' & replace(n[1].strVal, "\"", "\"\"") & '\"')
else:
- put(g, tkSymbol, "(wrong conv)")
+ gsub(g, n.sons[1])
+ of nkHiddenStdConv, nkHiddenSubConv, nkHiddenCallConv: gsub(g, n.sons[1])
of nkCast:
put(g, tkCast, "cast")
- if n.len > 0 and n[0].kind != nkEmpty:
- put(g, tkBracketLe, "[")
- gsub(g, n, 0)
- put(g, tkBracketRi, "]")
+ put(g, tkBracketLe, "[")
+ gsub(g, n, 0)
+ put(g, tkBracketRi, "]")
put(g, tkParLe, "(")
gsub(g, n, 1)
put(g, tkParRi, ")")
@@ -1169,7 +881,7 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext, fromStmtList = false) =
put(g, tkAddr, "addr")
if n.len > 0:
put(g, tkParLe, "(")
- gsub(g, n[0])
+ gsub(g, n.sons[0])
put(g, tkParRi, ")")
of nkStaticExpr:
put(g, tkStatic, "static")
@@ -1189,26 +901,14 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext, fromStmtList = false) =
gsub(g, n, 0)
gcomma(g, n, 1)
of nkCommand:
- accentedName(g, n[0])
+ gsub(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)
- else:
- gcomma(g, n, 1)
+ gcomma(g, n, 1)
of nkExprEqExpr, nkAsgn, nkFastAsgn:
gsub(g, n, 0)
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, "(")
@@ -1224,24 +924,24 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext, fromStmtList = false) =
put(g, tkParLe, "(")
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])
+ of nkObjDownConv, nkObjUpConv, nkStringToCString, nkCStringToString:
+ if sonsLen(n) >= 1: gsub(g, n.sons[0])
+ put(g, tkParLe, "(")
+ gcomma(g, n, 1)
put(g, tkParRi, ")")
of nkClosedSymChoice, nkOpenSymChoice:
if renderIds in g.flags:
put(g, tkParLe, "(")
- for i in 0.. 0: put(g, tkOpr, "|")
- if n[i].kind == nkSym:
+ if n.sons[i].kind == nkSym:
let s = n[i].sym
if s.owner != nil:
put g, tkSymbol, n[i].sym.owner.name.s
put g, tkOpr, "."
put g, tkSymbol, n[i].sym.name.s
else:
- gsub(g, n[i], c)
+ gsub(g, n.sons[i], c)
put(g, tkParRi, if n.kind == nkOpenSymChoice: "|...)" else: ")")
else:
gsub(g, n, 0)
@@ -1249,11 +949,6 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext, fromStmtList = false) =
put(g, tkParLe, "(")
gcomma(g, n, c)
put(g, tkParRi, ")")
- of nkTupleConstr:
- put(g, tkParLe, "(")
- gcomma(g, n, c)
- if n.len == 1 and n[0].kind != nkExprColonExpr: put(g, tkComma, ",")
- put(g, tkParRi, ")")
of nkCurly:
put(g, tkCurlyLe, "{")
gcomma(g, n, c)
@@ -1270,25 +965,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, "(")
+ of nkCheckedFieldExpr, nkHiddenAddr, nkHiddenDeref:
gsub(g, n, 0)
- if renderIds in g.flags:
- put(g, tkParRi, ")")
-
of nkLambda:
putWithSpace(g, tkProc, "proc")
gsub(g, n, paramsPos)
@@ -1299,74 +983,50 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext, fromStmtList = false) =
of nkDo:
putWithSpace(g, tkDo, "do")
if paramsPos < n.len:
- doParamsAux(g, n[paramsPos])
+ doParamsAux(g, n.sons[paramsPos])
gsub(g, n, pragmasPos)
put(g, tkColon, ":")
gsub(g, n, bodyPos)
of nkConstDef, nkIdentDefs:
gcomma(g, n, 0, -3)
- if n.len >= 2 and n[^2].kind != nkEmpty:
+ var L = sonsLen(n)
+ if L >= 2 and n.sons[L - 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)
-
- if n.len >= 1 and n[^1].kind != nkEmpty:
+ gsub(g, n, L - 2)
+ if L >= 1 and n.sons[L - 1].kind != nkEmpty:
put(g, tkSpaces, Space)
putWithSpace(g, tkEquals, "=")
- gsub(g, n[^1], c)
+ gsub(g, n.sons[L - 1], c)
of nkVarTuple:
- if n[^1].kind == nkEmpty:
- put(g, tkParLe, "(")
- gcomma(g, n, 0, -2)
- put(g, tkParRi, ")")
- else:
- put(g, tkParLe, "(")
- gcomma(g, n, 0, -3)
- put(g, tkParRi, ")")
- put(g, tkSpaces, Space)
- putWithSpace(g, tkEquals, "=")
- gsub(g, lastSon(n), c)
+ put(g, tkParLe, "(")
+ gcomma(g, n, 0, -3)
+ put(g, tkParRi, ")")
+ put(g, tkSpaces, Space)
+ putWithSpace(g, tkEquals, "=")
+ gsub(g, lastSon(n), c)
of nkExprColonExpr:
gsub(g, n, 0)
putWithSpace(g, tkColon, ":")
gsub(g, n, 1)
of nkInfix:
- let oldLineLen = g.lineLen # we cache this because lineLen gets updated below
- infixArgument(g, n, 1)
+ gsub(g, n, 1)
put(g, tkSpaces, Space)
gsub(g, n, 0) # binary operator
- # e.g.: `n1 == n2` decompses as following sum:
- if n.len == 3 and not fits(g, oldLineLen + lsub(g, n[1]) + lsub(g, n[2]) + lsub(g, n[0]) + len(" ")):
+ if not fits(g, lsub(n.sons[2]) + lsub(n.sons[0]) + 1):
optNL(g, g.indent + longIndentWid)
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)
+ gsub(g, n, 2)
of nkPrefix:
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
- elif n[0].kind in {nkOpenSymChoice, nkClosedSymChoice}: n[0][0].sym.name
- else: nil
- let nNext = skipHiddenNodes(n[1])
- if nNext.kind == nkPrefix or (opr != nil and renderer.isKeyword(opr)):
- put(g, tkSpaces, Space)
- if nNext.kind == nkInfix:
+ put(g, tkSpaces, Space)
+ if n.sons[1].kind == nkInfix:
put(g, tkParLe, "(")
- gsub(g, n[1])
+ gsub(g, n.sons[1])
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)
+ gsub(g, n.sons[1])
of nkPostfix:
gsub(g, n, 1)
gsub(g, n, 0)
@@ -1379,37 +1039,20 @@ 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)
- gsub(g, n[i])
+ if n.len > 0: gsub(g, n.sons[0])
+ for i in 1 .. 0: gcond(g, n[0][0])
+ if n.len > 0: gsub(g, n.sons[0], 0)
putWithSpace(g, tkColon, ":")
- if n.len > 0: gsub(g, n[0], 1)
+ if n.len > 0: gsub(g, n.sons[0], 1)
gsons(g, n, emptyContext, 1)
of nkElifExpr:
putWithSpace(g, tkElif, " elif")
- gcond(g, n[0])
+ gsub(g, n, 0)
putWithSpace(g, tkColon, ":")
gsub(g, n, 1)
of nkElseExpr:
@@ -1417,90 +1060,76 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext, fromStmtList = false) =
putWithSpace(g, tkColon, ":")
gsub(g, n, 0)
of nkTypeOfExpr:
- put(g, tkType, "typeof")
+ put(g, tkType, "type")
put(g, tkParLe, "(")
- if n.len > 0: gsub(g, n[0])
+ if n.len > 0: gsub(g, n.sons[0])
put(g, tkParRi, ")")
of nkRefTy:
- if n.len > 0:
+ if sonsLen(n) > 0:
putWithSpace(g, tkRef, "ref")
- gsub(g, n[0])
+ gsub(g, n.sons[0])
else:
put(g, tkRef, "ref")
of nkPtrTy:
- if n.len > 0:
+ if sonsLen(n) > 0:
putWithSpace(g, tkPtr, "ptr")
- gsub(g, n[0])
+ gsub(g, n.sons[0])
else:
put(g, tkPtr, "ptr")
of nkVarTy:
- if n.len > 0:
+ if sonsLen(n) > 0:
putWithSpace(g, tkVar, "var")
- gsub(g, n[0])
+ gsub(g, n.sons[0])
else:
put(g, tkVar, "var")
- of nkOutTy:
- if n.len > 0:
- putWithSpace(g, tkOut, "out")
- gsub(g, n[0])
- else:
- put(g, tkOut, "out")
of nkDistinctTy:
if n.len > 0:
putWithSpace(g, tkDistinct, "distinct")
- gsub(g, n[0])
+ gsub(g, n.sons[0])
if n.len > 1:
if n[1].kind == nkWith:
- putWithSpace(g, tkSymbol, " with")
+ putWithSpace(g, tkWith, " with")
else:
- putWithSpace(g, tkSymbol, " without")
+ putWithSpace(g, tkWithout, " without")
gcomma(g, n[1])
else:
put(g, tkDistinct, "distinct")
of nkTypeDef:
- if n[0].kind == nkPragmaExpr:
- # generate pragma after generic
- gsub(g, n[0], 0)
- gsub(g, n, 1)
- gsub(g, n[0], 1)
- else:
- gsub(g, n, 0)
- gsub(g, n, 1)
+ gsub(g, n, 0)
+ gsub(g, n, 1)
put(g, tkSpaces, Space)
- if n.len > 2 and n[2].kind != nkEmpty:
+ if n.len > 2 and n.sons[2].kind != nkEmpty:
putWithSpace(g, tkEquals, "=")
- gsub(g, n[2])
+ gsub(g, n.sons[2])
of nkObjectTy:
- if n.len > 0:
+ if sonsLen(n) > 0:
putWithSpace(g, tkObject, "object")
- gsub(g, n[0])
- gsub(g, n[1])
+ gsub(g, n.sons[0])
+ gsub(g, n.sons[1])
gcoms(g)
- gsub(g, n[2])
+ gsub(g, n.sons[2])
else:
put(g, tkObject, "object")
of nkRecList:
indentNL(g)
- for i in 0.. 0:
+ if sonsLen(n) > 0:
putWithSpace(g, tkProc, "proc")
gsub(g, n, 0)
gsub(g, n, 1)
else:
put(g, tkProc, "proc")
of nkIteratorTy:
- if n.len > 0:
+ if sonsLen(n) > 0:
putWithSpace(g, tkIterator, "iterator")
gsub(g, n, 0)
gsub(g, n, 1)
@@ -1510,12 +1139,12 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext, fromStmtList = false) =
put(g, tkStatic, "static")
put(g, tkBracketLe, "[")
if n.len > 0:
- gsub(g, n[0])
+ gsub(g, n.sons[0])
put(g, tkBracketRi, "]")
of nkEnumTy:
- if n.len > 0:
+ if sonsLen(n) > 0:
putWithSpace(g, tkEnum, "enum")
- gsub(g, n[0])
+ gsub(g, n.sons[0])
gcoms(g)
indentNL(g)
gcommaAux(g, n, g.indent, 1)
@@ -1528,13 +1157,7 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext, fromStmtList = false) =
put(g, tkSpaces, Space)
putWithSpace(g, tkEquals, "=")
gsub(g, n, 1)
- of nkStmtList, nkStmtListExpr, nkStmtListType:
- if n.len == 1 and n[0].kind == nkDiscardStmt:
- put(g, tkParLe, "(")
- gsub(g, n[0])
- put(g, tkParRi, ")")
- else:
- gstmts(g, n, emptyContext)
+ of nkStmtList, nkStmtListExpr, nkStmtListType: gstmts(g, n, emptyContext)
of nkIfStmt:
putWithSpace(g, tkIf, "if")
gif(g, n)
@@ -1544,7 +1167,7 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext, fromStmtList = false) =
of nkWhileStmt: gwhile(g, n)
of nkPragmaBlock: gpragmaBlock(g, n)
of nkCaseStmt, nkRecCase: gcase(g, n)
- of nkTryStmt, nkHiddenTryStmt: gtry(g, n)
+ of nkTryStmt: gtry(g, n)
of nkForStmt, nkParForStmt: gfor(g, n)
of nkBlockStmt, nkBlockExpr: gblock(g, n)
of nkStaticStmt: gstaticStmt(g, n)
@@ -1552,9 +1175,6 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext, fromStmtList = false) =
of nkProcDef:
if renderNoProcDefs notin g.flags: putWithSpace(g, tkProc, "proc")
gproc(g, n)
- of nkFuncDef:
- if renderNoProcDefs notin g.flags: putWithSpace(g, tkFunc, "func")
- gproc(g, n)
of nkConverterDef:
if renderNoProcDefs notin g.flags: putWithSpace(g, tkConverter, "converter")
gproc(g, n)
@@ -1577,26 +1197,24 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext, fromStmtList = false) =
incl(a.flags, rfInConstExpr)
gsection(g, n, a, tkConst, "const")
of nkVarSection, nkLetSection, nkUsingStmt:
- if n.len == 0: return
+ var L = sonsLen(n)
+ if L == 0: return
if n.kind == nkVarSection: putWithSpace(g, tkVar, "var")
elif n.kind == nkLetSection: putWithSpace(g, tkLet, "let")
else: putWithSpace(g, tkUsing, "using")
- if n.len > 1:
+ if L > 1:
gcoms(g)
indentNL(g)
- for i in 0.. 0 and n[0].kind == nkAsgn:
- gsub(g, n[0], 1)
- else:
- gsub(g, n, 0)
+ gsub(g, n, 0)
of nkRaiseStmt:
putWithSpace(g, tkRaise, "raise")
gsub(g, n, 0)
@@ -1613,16 +1231,17 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext, fromStmtList = false) =
putWithSpace(g, tkContinue, "continue")
gsub(g, n, 0)
of nkPragma:
- if g.inPragma <= 0:
- inc g.inPragma
- #if not previousNL(g):
- put(g, tkSpaces, Space)
- put(g, tkCurlyDotLe, "{.")
- gcomma(g, n, emptyContext)
- put(g, tkCurlyDotRi, ".}")
- dec g.inPragma
- else:
- gcomma(g, n, emptyContext)
+ if renderNoPragmas notin g.flags:
+ if g.inPragma <= 0:
+ inc g.inPragma
+ #if not previousNL(g):
+ put(g, tkSpaces, Space)
+ put(g, tkCurlyDotLe, "{.")
+ gcomma(g, n, emptyContext)
+ put(g, tkCurlyDotRi, ".}")
+ dec g.inPragma
+ else:
+ gcomma(g, n, emptyContext)
of nkImportStmt, nkExportStmt:
if n.kind == nkImportStmt:
putWithSpace(g, tkImport, "import")
@@ -1685,13 +1304,13 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext, fromStmtList = false) =
gsub(g, n, 0)
putWithSpace(g, tkColon, ":")
gcoms(g)
- gstmts(g, n[1], c)
+ gstmts(g, n.sons[1], c)
of nkElse:
optNL(g)
put(g, tkElse, "else")
putWithSpace(g, tkColon, ":")
gcoms(g)
- gstmts(g, n[0], c)
+ gstmts(g, n.sons[0], c)
of nkFinally, nkDefer:
optNL(g)
if n.kind == nkFinally:
@@ -1700,35 +1319,25 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext, fromStmtList = false) =
put(g, tkDefer, "defer")
putWithSpace(g, tkColon, ":")
gcoms(g)
- gstmts(g, n[0], c)
+ gstmts(g, n.sons[0], c)
of nkExceptBranch:
optNL(g)
- if n.len != 1:
- putWithSpace(g, tkExcept, "except")
- else:
- put(g, tkExcept, "except")
- gcomma(g, n, 0, -2)
+ putWithSpace(g, tkExcept, "except")
+ gcomma(g, n, 0, - 2)
putWithSpace(g, tkColon, ":")
gcoms(g)
gstmts(g, lastSon(n), c)
of nkGenericParams:
- proc hasExplicitParams(gp: PNode): bool =
- for p in gp:
- if p.typ == nil or tfImplicitTypeParam notin p.typ.flags:
- return true
- return false
-
- if n.hasExplicitParams:
- put(g, tkBracketLe, "[")
- gsemicolon(g, n)
- put(g, tkBracketRi, "]")
+ put(g, tkBracketLe, "[")
+ gcomma(g, n)
+ put(g, tkBracketRi, "]")
of nkFormalParams:
put(g, tkParLe, "(")
gsemicolon(g, n, 1)
put(g, tkParRi, ")")
- if n.len > 0 and n[0].kind != nkEmpty:
+ if n.len > 0 and n.sons[0].kind != nkEmpty:
putWithSpace(g, tkColon, ":")
- gsub(g, n[0])
+ gsub(g, n.sons[0])
of nkTupleTy:
put(g, tkTuple, "tuple")
put(g, tkBracketLe, "[")
@@ -1736,93 +1345,61 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext, fromStmtList = false) =
put(g, tkBracketRi, "]")
of nkTupleClassTy:
put(g, tkTuple, "tuple")
- of nkComesFrom:
- put(g, tkParLe, "(ComesFrom|")
+ of nkMetaNode_Obsolete:
+ put(g, tkParLe, "(META|")
gsub(g, n, 0)
put(g, tkParRi, ")")
- of nkGotoState:
+ of nkGotoState, nkState:
var c: TContext
initContext c
- putWithSpace g, tkSymbol, "goto"
+ putWithSpace g, tkSymbol, if n.kind == nkState: "state" else: "goto"
gsons(g, n, c)
- of nkState:
- var c: TContext
- initContext c
- putWithSpace g, tkSymbol, "state"
- gsub(g, n[0], c)
- putWithSpace(g, tkColon, ":")
- indentNL(g)
- gsons(g, n, c, 1)
- dedent(g)
-
of nkBreakState:
put(g, tkTuple, "breakstate")
- if renderIds in g.flags:
- gsons(g, n, c, 0)
of nkTypeClassTy:
gTypeClassTy(g, n)
- of nkError:
- putWithSpace(g, tkSymbol, "error")
- #gcomma(g, n, c)
- gsub(g, n[0], c)
else:
#nkNone, nkExplicitTypeListCall:
- internalError(g.config, n.info, "renderer.gsub(" & $n.kind & ')')
+ internalError(n.info, "rnimsyn.gsub(" & $n.kind & ')')
-proc renderTree*(n: PNode, renderFlags: TRenderFlags = {}): string =
- if n == nil: return ""
+proc renderTree(n: PNode, renderFlags: TRenderFlags = {}): string =
var g: TSrcGen
- initSrcGen(g, renderFlags, newPartialConfigRef())
- # do not indent the initial statement list so that
- # writeFile("file.nim", repr n)
- # produces working Nim code:
- if n.kind in {nkStmtList, nkStmtListExpr, nkStmtListType}:
- gstmts(g, n, emptyContext, doIndent = false)
- else:
- gsub(g, n)
+ initSrcGen(g, renderFlags)
+ gsub(g, n)
result = g.buf
-proc `$`*(n: PNode): string = n.renderTree
-
-proc renderModule*(n: PNode, outfile: string,
- renderFlags: TRenderFlags = {};
- fid = FileIndex(-1);
- conf: ConfigRef = nil) =
+proc renderModule(n: PNode, filename: string,
+ renderFlags: TRenderFlags = {}) =
var
f: File
g: TSrcGen
- initSrcGen(g, renderFlags, conf)
- g.fid = fid
- for i in 0.. 0 and r.idx <= r.tokens.len:
- result = r.tokens[r.idx-1].sym
- else:
- result = nil
diff --git a/compiler/renderverbatim.nim b/compiler/renderverbatim.nim
deleted file mode 100644
index 792079b3f..000000000
--- a/compiler/renderverbatim.nim
+++ /dev/null
@@ -1,136 +0,0 @@
-import strutils
-
-import ast, options, msgs
-
-when defined(nimPreviewSlimSystem):
- import std/assertions
-
-
-const isDebug = false
-when isDebug:
- import renderer
- import astalgo
-
-proc lastNodeRec(n: PNode): PNode =
- result = n
- while result.safeLen > 0: result = result[^1]
-
-proc isInIndentationBlock(src: string, indent: int): bool =
- #[
- we stop at the first de-indentation; there's an inherent ambiguity with non
- doc comments since they can have arbitrary indentation, so we just take the
- practical route and require a runnableExamples to keep its code (including non
- doc comments) to its indentation level.
- ]#
- for j in 0..= ldata.lines.len: false
- else: ldata.lines[index]
-
-proc extractRunnableExamplesSource*(conf: ConfigRef; n: PNode, indent = 0): 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
- if first.line == n[0].info.line:
- #[
- runnableExamples: assert true
- ]#
- discard
- else:
- #[
- runnableExamples:
- # non-doc comment that we want to capture even though `first` points to `assert true`
- assert true
- ]#
- first.line = n[0].info.line + 1
-
- let last = n.lastNodeRec.info
- var info = first
- var indent2 = info.col
- let numLines = numLines(conf, info.fileIndex).uint16
- var lastNonemptyPos = 0
-
- var ldata = LineData(lineFirst: first.line.int, conf: conf)
- visitMultilineStrings(ldata, n[^1])
- when isDebug:
- debug(n)
- for i in 0.. last.line and not special and not isInIndentationBlock(src, indent2):
- break
- if line > first.line: result.add "\n"
- if special:
- result.add src
- lastNonemptyPos = result.len
- elif src.len > indent2:
- for i in 0..= 3:
- decl(a[0])
- for i in 1.. 1:
- for b in a:
- var s = newNode(a.kind)
- s.info = b.info
- s.add b
- result.add s
- else:
- result.add a
-
-proc haveSameKind(dns: seq[DepN]): bool =
- # Check if all the nodes in a strongly connected
- # component have the same kind
- result = true
- let kind = dns[0].pnode.kind
- for dn in dns:
- if dn.pnode.kind != kind:
- return false
-
-proc mergeSections(conf: ConfigRef; comps: seq[seq[DepN]], res: PNode) =
- # Merges typeSections and ConstSections when they form
- # a strong component (ex: circular type definition)
- for c in comps:
- assert c.len > 0
- if c.len == 1:
- res.add c[0].pnode
- else:
- let fstn = c[0].pnode
- let kind = fstn.kind
- # always return to the original order when we got circular dependencies
- let cs = c.sortedByIt(it.id)
- if kind in {nkTypeSection, nkConstSection} and haveSameKind(cs):
- # Circular dependency between type or const sections, we just
- # need to merge them
- var sn = newNode(kind)
- for dn in cs:
- sn.add dn.pnode[0]
- res.add sn
- else:
- # Problematic circular dependency, we arrange the nodes into
- # their original relative order and make sure to re-merge
- # consecutive type and const sections
- var wmsg = "Circular dependency detected. `codeReordering` pragma may not be able to" &
- " reorder some nodes properly"
- when defined(nimDebugReorder):
- wmsg &= ":\n"
- for i in 0.. \n # fileidx is the LINE in the file section!
+# )
+# - a module dependency section:
+# DEPS: \n
+# - an interface section:
+# INTERF(
+# identifier1 id\n # id is the symbol's id
+# identifier2 id\n
+# )
+# - a compiler proc section:
+# COMPILERPROCS(
+# identifier1 id\n # id is the symbol's id
+# )
+# - an index consisting of (ID, linenumber)-pairs:
+# INDEX(
+# id-diff idx-diff\n
+# id-diff idx-diff\n
+# )
+#
+# Since the whole index has to be read in advance, we compress it by
+# storing the integer differences to the last entry instead of using the
+# real numbers.
+#
+# - an import index consisting of (ID, moduleID)-pairs:
+# IMPORTS(
+# id-diff moduleID-diff\n
+# id-diff moduleID-diff\n
+# )
+# - a list of all exported type converters because they are needed for correct
+# semantic checking:
+# CONVERTERS:id id\n # symbol ID
+#
+# This is a misnomer now; it's really a "load unconditionally" section as
+# it is also used for pattern templates.
+#
+# - a list of all (private or exported) methods because they are needed for
+# correct dispatcher generation:
+# METHODS: id id\n # symbol ID
+# - an AST section that contains the module's AST:
+# INIT(
+# idx\n # position of the node in the DATA section
+# idx\n
+# )
+# - a data section, where each type, symbol or AST is stored.
+# DATA(
+# type
+# (node)
+# sym
+# )
+#
+# The data section MUST be the last section of the file, because processing
+# stops immediately after ``DATA(`` and the rest is only loaded on demand
+# by using a mem'mapped file.
+#
+
+import
+ os, options, strutils, nversion, ast, astalgo, msgs, platform, condsyms,
+ ropes, idents, securehash, idgen, types, rodutils, memfiles, tables
+
+type
+ TReasonForRecompile* = enum ## all the reasons that can trigger recompilation
+ rrEmpty, # dependencies not yet computed
+ rrNone, # no need to recompile
+ rrRodDoesNotExist, # rod file does not exist
+ rrRodInvalid, # rod file is invalid
+ rrHashChange, # file has been edited since last recompilation
+ rrDefines, # defines have changed
+ rrOptions, # options have changed
+ rrInclDeps, # an include has changed
+ rrModDeps # a module this module depends on has been changed
+
+const
+ reasonToFrmt*: array[TReasonForRecompile, string] = ["",
+ "no need to recompile: $1", "symbol file for $1 does not exist",
+ "symbol file for $1 has the wrong version",
+ "file edited since last compilation: $1",
+ "list of conditional symbols changed for: $1",
+ "list of options changed for: $1",
+ "an include file edited: $1",
+ "a module $1 depends on has changed"]
+
+type
+ TIndex*{.final.} = object # an index with compression
+ lastIdxKey*, lastIdxVal*: int
+ tab*: TIITable
+ r*: string # writers use this
+ offset*: int # readers use this
+
+ TRodReader* = object of RootObj
+ pos: int # position; used for parsing
+ s: cstring # mmap'ed file contents
+ options: TOptions
+ reason: TReasonForRecompile
+ modDeps: seq[int32]
+ files: seq[int32]
+ dataIdx: int # offset of start of data section
+ convertersIdx: int # offset of start of converters section
+ initIdx, interfIdx, compilerProcsIdx, methodsIdx: int
+ filename: string
+ index, imports: TIndex
+ readerIndex: int
+ line: int # only used for debugging, but is always in the code
+ moduleID: int
+ syms: Table[int, PSym] # already processed symbols
+ memfile: MemFile # unfortunately there is no point in time where we
+ # can close this! XXX
+ methods*: TSymSeq
+ origFile: string
+ inViewMode: bool
+
+ PRodReader* = ref TRodReader
+
+var rodCompilerprocs*: TStrTable
+
+proc handleSymbolFile*(module: PSym): PRodReader
+# global because this is needed by magicsys
+proc loadInitSection*(r: PRodReader): PNode
+
+# implementation
+
+proc rawLoadStub(s: PSym)
+
+var gTypeTable: TIdTable
+
+proc rrGetSym(r: PRodReader, id: int, info: TLineInfo): PSym
+ # `info` is only used for debugging purposes
+proc rrGetType(r: PRodReader, id: int, info: TLineInfo): PType
+
+proc decodeLineInfo(r: PRodReader, info: var TLineInfo) =
+ if r.s[r.pos] == '?':
+ inc(r.pos)
+ if r.s[r.pos] == ',': info.col = -1'i16
+ else: info.col = int16(decodeVInt(r.s, r.pos))
+ if r.s[r.pos] == ',':
+ inc(r.pos)
+ if r.s[r.pos] == ',': info.line = -1'i16
+ else: info.line = int16(decodeVInt(r.s, r.pos))
+ if r.s[r.pos] == ',':
+ inc(r.pos)
+ info = newLineInfo(r.files[decodeVInt(r.s, r.pos)], info.line, info.col)
+
+proc skipNode(r: PRodReader) =
+ assert r.s[r.pos] == '('
+ var par = 0
+ var pos = r.pos+1
+ while true:
+ case r.s[pos]
+ of ')':
+ if par == 0: break
+ dec par
+ of '(': inc par
+ else: discard
+ inc pos
+ r.pos = pos+1 # skip ')'
+
+proc decodeNodeLazyBody(r: PRodReader, fInfo: TLineInfo,
+ belongsTo: PSym): PNode =
+ result = nil
+ if r.s[r.pos] == '(':
+ inc(r.pos)
+ if r.s[r.pos] == ')':
+ inc(r.pos)
+ return # nil node
+ result = newNodeI(TNodeKind(decodeVInt(r.s, r.pos)), fInfo)
+ decodeLineInfo(r, result.info)
+ if r.s[r.pos] == '$':
+ inc(r.pos)
+ result.flags = cast[TNodeFlags](int32(decodeVInt(r.s, r.pos)))
+ if r.s[r.pos] == '^':
+ inc(r.pos)
+ var id = decodeVInt(r.s, r.pos)
+ result.typ = rrGetType(r, id, result.info)
+ case result.kind
+ of nkCharLit..nkInt64Lit:
+ if r.s[r.pos] == '!':
+ inc(r.pos)
+ result.intVal = decodeVBiggestInt(r.s, r.pos)
+ of nkFloatLit..nkFloat64Lit:
+ if r.s[r.pos] == '!':
+ inc(r.pos)
+ var fl = decodeStr(r.s, r.pos)
+ result.floatVal = parseFloat(fl)
+ of nkStrLit..nkTripleStrLit:
+ if r.s[r.pos] == '!':
+ inc(r.pos)
+ result.strVal = decodeStr(r.s, r.pos)
+ else:
+ result.strVal = "" # BUGFIX
+ of nkIdent:
+ if r.s[r.pos] == '!':
+ inc(r.pos)
+ var fl = decodeStr(r.s, r.pos)
+ result.ident = getIdent(fl)
+ else:
+ internalError(result.info, "decodeNode: nkIdent")
+ of nkSym:
+ if r.s[r.pos] == '!':
+ inc(r.pos)
+ var id = decodeVInt(r.s, r.pos)
+ result.sym = rrGetSym(r, id, result.info)
+ else:
+ internalError(result.info, "decodeNode: nkSym")
+ else:
+ var i = 0
+ while r.s[r.pos] != ')':
+ if belongsTo != nil and i == bodyPos:
+ addSonNilAllowed(result, nil)
+ belongsTo.offset = r.pos
+ skipNode(r)
+ else:
+ addSonNilAllowed(result, decodeNodeLazyBody(r, result.info, nil))
+ inc i
+ if r.s[r.pos] == ')': inc(r.pos)
+ else: internalError(result.info, "decodeNode: ')' missing")
+ else:
+ internalError(fInfo, "decodeNode: '(' missing " & $r.pos)
+
+proc decodeNode(r: PRodReader, fInfo: TLineInfo): PNode =
+ result = decodeNodeLazyBody(r, fInfo, nil)
+
+proc decodeLoc(r: PRodReader, loc: var TLoc, info: TLineInfo) =
+ if r.s[r.pos] == '<':
+ inc(r.pos)
+ if r.s[r.pos] in {'0'..'9', 'a'..'z', 'A'..'Z'}:
+ loc.k = TLocKind(decodeVInt(r.s, r.pos))
+ else:
+ loc.k = low(loc.k)
+ if r.s[r.pos] == '*':
+ inc(r.pos)
+ loc.s = TStorageLoc(decodeVInt(r.s, r.pos))
+ else:
+ loc.s = low(loc.s)
+ if r.s[r.pos] == '$':
+ inc(r.pos)
+ loc.flags = cast[TLocFlags](int32(decodeVInt(r.s, r.pos)))
+ else:
+ loc.flags = {}
+ if r.s[r.pos] == '^':
+ inc(r.pos)
+ loc.t = rrGetType(r, decodeVInt(r.s, r.pos), info)
+ else:
+ loc.t = nil
+ if r.s[r.pos] == '!':
+ inc(r.pos)
+ loc.r = rope(decodeStr(r.s, r.pos))
+ else:
+ loc.r = nil
+ if r.s[r.pos] == '>': inc(r.pos)
+ else: internalError(info, "decodeLoc " & r.s[r.pos])
+
+proc decodeType(r: PRodReader, info: TLineInfo): PType =
+ result = nil
+ if r.s[r.pos] == '[':
+ inc(r.pos)
+ if r.s[r.pos] == ']':
+ inc(r.pos)
+ return # nil type
+ new(result)
+ result.kind = TTypeKind(decodeVInt(r.s, r.pos))
+ if r.s[r.pos] == '+':
+ inc(r.pos)
+ result.id = decodeVInt(r.s, r.pos)
+ setId(result.id)
+ if debugIds: registerID(result)
+ else:
+ internalError(info, "decodeType: no id")
+ # here this also avoids endless recursion for recursive type
+ idTablePut(gTypeTable, result, result)
+ if r.s[r.pos] == '(': result.n = decodeNode(r, unknownLineInfo())
+ if r.s[r.pos] == '$':
+ inc(r.pos)
+ result.flags = cast[TTypeFlags](int32(decodeVInt(r.s, r.pos)))
+ if r.s[r.pos] == '?':
+ inc(r.pos)
+ result.callConv = TCallingConvention(decodeVInt(r.s, r.pos))
+ if r.s[r.pos] == '*':
+ inc(r.pos)
+ result.owner = rrGetSym(r, decodeVInt(r.s, r.pos), info)
+ if r.s[r.pos] == '&':
+ inc(r.pos)
+ result.sym = rrGetSym(r, decodeVInt(r.s, r.pos), info)
+ if r.s[r.pos] == '/':
+ inc(r.pos)
+ result.size = decodeVInt(r.s, r.pos)
+ else:
+ result.size = - 1
+ if r.s[r.pos] == '=':
+ inc(r.pos)
+ result.align = decodeVInt(r.s, r.pos).int16
+ else:
+ result.align = 2
+ decodeLoc(r, result.loc, info)
+ while r.s[r.pos] == '^':
+ inc(r.pos)
+ if r.s[r.pos] == '(':
+ inc(r.pos)
+ if r.s[r.pos] == ')': inc(r.pos)
+ else: internalError(info, "decodeType ^(" & r.s[r.pos])
+ rawAddSon(result, nil)
+ else:
+ var d = decodeVInt(r.s, r.pos)
+ rawAddSon(result, rrGetType(r, d, info))
+
+proc decodeLib(r: PRodReader, info: TLineInfo): PLib =
+ result = nil
+ if r.s[r.pos] == '|':
+ new(result)
+ inc(r.pos)
+ result.kind = TLibKind(decodeVInt(r.s, r.pos))
+ if r.s[r.pos] != '|': internalError("decodeLib: 1")
+ inc(r.pos)
+ result.name = rope(decodeStr(r.s, r.pos))
+ if r.s[r.pos] != '|': internalError("decodeLib: 2")
+ inc(r.pos)
+ result.path = decodeNode(r, info)
+
+proc decodeSym(r: PRodReader, info: TLineInfo): PSym =
+ var
+ id: int
+ ident: PIdent
+ result = nil
+ if r.s[r.pos] == '{':
+ inc(r.pos)
+ if r.s[r.pos] == '}':
+ inc(r.pos)
+ return # nil sym
+ var k = TSymKind(decodeVInt(r.s, r.pos))
+ if r.s[r.pos] == '+':
+ inc(r.pos)
+ id = decodeVInt(r.s, r.pos)
+ setId(id)
+ else:
+ internalError(info, "decodeSym: no id")
+ if r.s[r.pos] == '&':
+ inc(r.pos)
+ ident = getIdent(decodeStr(r.s, r.pos))
+ else:
+ internalError(info, "decodeSym: no ident")
+ #echo "decoding: {", ident.s
+ result = r.syms.getOrDefault(id)
+ if result == nil:
+ new(result)
+ result.id = id
+ r.syms[result.id] = result
+ if debugIds: registerID(result)
+ elif result.id != id:
+ internalError(info, "decodeSym: wrong id")
+ elif result.kind != skStub and not r.inViewMode:
+ # we already loaded the symbol
+ return
+ else:
+ reset(result[])
+ result.id = id
+ result.kind = k
+ result.name = ident # read the rest of the symbol description:
+ if r.s[r.pos] == '^':
+ inc(r.pos)
+ result.typ = rrGetType(r, decodeVInt(r.s, r.pos), info)
+ decodeLineInfo(r, result.info)
+ if r.s[r.pos] == '*':
+ inc(r.pos)
+ result.owner = rrGetSym(r, decodeVInt(r.s, r.pos), result.info)
+ if r.s[r.pos] == '$':
+ inc(r.pos)
+ result.flags = cast[TSymFlags](int32(decodeVInt(r.s, r.pos)))
+ if r.s[r.pos] == '@':
+ inc(r.pos)
+ result.magic = TMagic(decodeVInt(r.s, r.pos))
+ if r.s[r.pos] == '!':
+ inc(r.pos)
+ result.options = cast[TOptions](int32(decodeVInt(r.s, r.pos)))
+ else:
+ result.options = r.options
+ if r.s[r.pos] == '%':
+ inc(r.pos)
+ result.position = decodeVInt(r.s, r.pos)
+ elif result.kind notin routineKinds + {skModule}:
+ result.position = 0
+ # this may have been misused as reader index! But we still
+ # need it for routines as the body is loaded lazily.
+ if r.s[r.pos] == '`':
+ inc(r.pos)
+ result.offset = decodeVInt(r.s, r.pos)
+ else:
+ result.offset = - 1
+ decodeLoc(r, result.loc, result.info)
+ result.annex = decodeLib(r, info)
+ if r.s[r.pos] == '#':
+ inc(r.pos)
+ result.constraint = decodeNode(r, unknownLineInfo())
+ if r.s[r.pos] == '(':
+ if result.kind in routineKinds:
+ result.ast = decodeNodeLazyBody(r, result.info, result)
+ # since we load the body lazily, we need to set the reader to
+ # be able to reload:
+ result.position = r.readerIndex
+ else:
+ result.ast = decodeNode(r, result.info)
+ #echo "decoded: ", ident.s, "}"
+
+proc skipSection(r: PRodReader) =
+ if r.s[r.pos] == ':':
+ while r.s[r.pos] > '\x0A': inc(r.pos)
+ elif r.s[r.pos] == '(':
+ var c = 0 # count () pairs
+ inc(r.pos)
+ while true:
+ case r.s[r.pos]
+ of '\x0A': inc(r.line)
+ of '(': inc(c)
+ of ')':
+ if c == 0:
+ inc(r.pos)
+ break
+ elif c > 0:
+ dec(c)
+ of '\0': break # end of file
+ else: discard
+ inc(r.pos)
+ else:
+ internalError("skipSection " & $r.line)
+
+proc rdWord(r: PRodReader): string =
+ result = ""
+ while r.s[r.pos] in {'A'..'Z', '_', 'a'..'z', '0'..'9'}:
+ add(result, r.s[r.pos])
+ inc(r.pos)
+
+proc newStub(r: PRodReader, name: string, id: int): PSym =
+ new(result)
+ result.kind = skStub
+ result.id = id
+ result.name = getIdent(name)
+ result.position = r.readerIndex
+ setId(id) #MessageOut(result.name.s);
+ if debugIds: registerID(result)
+
+proc processInterf(r: PRodReader, module: PSym) =
+ if r.interfIdx == 0: internalError("processInterf")
+ r.pos = r.interfIdx
+ while (r.s[r.pos] > '\x0A') and (r.s[r.pos] != ')'):
+ var w = decodeStr(r.s, r.pos)
+ inc(r.pos)
+ var key = decodeVInt(r.s, r.pos)
+ inc(r.pos) # #10
+ var s = newStub(r, w, key)
+ s.owner = module
+ strTableAdd(module.tab, s)
+ r.syms[s.id] = s
+
+proc processCompilerProcs(r: PRodReader, module: PSym) =
+ if r.compilerProcsIdx == 0: internalError("processCompilerProcs")
+ r.pos = r.compilerProcsIdx
+ while (r.s[r.pos] > '\x0A') and (r.s[r.pos] != ')'):
+ var w = decodeStr(r.s, r.pos)
+ inc(r.pos)
+ var key = decodeVInt(r.s, r.pos)
+ inc(r.pos) # #10
+ var s = r.syms.getOrDefault(key)
+ if s == nil:
+ s = newStub(r, w, key)
+ s.owner = module
+ r.syms[s.id] = s
+ strTableAdd(rodCompilerprocs, s)
+
+proc processIndex(r: PRodReader; idx: var TIndex; outf: File = nil) =
+ var key, val, tmp: int
+ inc(r.pos, 2) # skip "(\10"
+ inc(r.line)
+ while (r.s[r.pos] > '\x0A') and (r.s[r.pos] != ')'):
+ tmp = decodeVInt(r.s, r.pos)
+ if r.s[r.pos] == ' ':
+ inc(r.pos)
+ key = idx.lastIdxKey + tmp
+ val = decodeVInt(r.s, r.pos) + idx.lastIdxVal
+ else:
+ key = idx.lastIdxKey + 1
+ val = tmp + idx.lastIdxVal
+ iiTablePut(idx.tab, key, val)
+ if not outf.isNil: outf.write(key, " ", val, "\n")
+ idx.lastIdxKey = key
+ idx.lastIdxVal = val
+ setId(key) # ensure that this id will not be used
+ if r.s[r.pos] == '\x0A':
+ inc(r.pos)
+ inc(r.line)
+ if r.s[r.pos] == ')': inc(r.pos)
+
+proc cmdChangeTriggersRecompilation(old, new: TCommands): bool =
+ if old == new: return false
+ # we use a 'case' statement without 'else' so that addition of a
+ # new command forces us to consider it here :-)
+ case old
+ of cmdCompileToC, cmdCompileToCpp, cmdCompileToOC,
+ cmdCompileToJS, cmdCompileToPHP, cmdCompileToLLVM:
+ if new in {cmdDoc, cmdCheck, cmdIdeTools, cmdPretty, cmdDef,
+ cmdInteractive}:
+ return false
+ of cmdNone, cmdDoc, cmdInterpret, cmdPretty, cmdGenDepend, cmdDump,
+ cmdCheck, cmdParse, cmdScan, cmdIdeTools, cmdDef,
+ cmdRst2html, cmdRst2tex, cmdInteractive, cmdRun:
+ discard
+ # else: trigger recompilation:
+ result = true
+
+proc processRodFile(r: PRodReader, hash: SecureHash) =
+ var
+ w: string
+ d: int
+ var inclHash: SecureHash
+ while r.s[r.pos] != '\0':
+ var section = rdWord(r)
+ if r.reason != rrNone:
+ break # no need to process this file further
+ case section
+ of "HASH":
+ inc(r.pos) # skip ':'
+ if hash != parseSecureHash(decodeStr(r.s, r.pos)):
+ r.reason = rrHashChange
+ of "ID":
+ inc(r.pos) # skip ':'
+ r.moduleID = decodeVInt(r.s, r.pos)
+ setId(r.moduleID)
+ of "ORIGFILE":
+ inc(r.pos)
+ r.origFile = decodeStr(r.s, r.pos)
+ of "OPTIONS":
+ inc(r.pos) # skip ':'
+ r.options = cast[TOptions](int32(decodeVInt(r.s, r.pos)))
+ if options.gOptions != r.options: r.reason = rrOptions
+ of "GOPTIONS":
+ inc(r.pos) # skip ':'
+ var dep = cast[TGlobalOptions](int32(decodeVInt(r.s, r.pos)))
+ if gGlobalOptions != dep: r.reason = rrOptions
+ of "CMD":
+ inc(r.pos) # skip ':'
+ var dep = cast[TCommands](int32(decodeVInt(r.s, r.pos)))
+ if cmdChangeTriggersRecompilation(dep, gCmd): r.reason = rrOptions
+ of "DEFINES":
+ inc(r.pos) # skip ':'
+ d = 0
+ while r.s[r.pos] > '\x0A':
+ w = decodeStr(r.s, r.pos)
+ inc(d)
+ if not condsyms.isDefined(getIdent(w)):
+ r.reason = rrDefines #MessageOut('not defined, but should: ' + w);
+ if r.s[r.pos] == ' ': inc(r.pos)
+ if (d != countDefinedSymbols()): r.reason = rrDefines
+ of "FILES":
+ inc(r.pos, 2) # skip "(\10"
+ inc(r.line)
+ while r.s[r.pos] != ')':
+ let relativePath = decodeStr(r.s, r.pos)
+ let resolvedPath = relativePath.findModule(r.origFile)
+ let finalPath = if resolvedPath.len > 0: resolvedPath else: relativePath
+ r.files.add(finalPath.fileInfoIdx)
+ inc(r.pos) # skip #10
+ inc(r.line)
+ if r.s[r.pos] == ')': inc(r.pos)
+ of "INCLUDES":
+ inc(r.pos, 2) # skip "(\10"
+ inc(r.line)
+ while r.s[r.pos] != ')':
+ w = r.files[decodeVInt(r.s, r.pos)].toFullPath
+ inc(r.pos) # skip ' '
+ inclHash = parseSecureHash(decodeStr(r.s, r.pos))
+ if r.reason == rrNone:
+ if not existsFile(w) or (inclHash != secureHashFile(w)):
+ r.reason = rrInclDeps
+ if r.s[r.pos] == '\x0A':
+ inc(r.pos)
+ inc(r.line)
+ if r.s[r.pos] == ')': inc(r.pos)
+ of "DEPS":
+ inc(r.pos) # skip ':'
+ while r.s[r.pos] > '\x0A':
+ r.modDeps.add(r.files[int32(decodeVInt(r.s, r.pos))])
+ if r.s[r.pos] == ' ': inc(r.pos)
+ of "INTERF":
+ r.interfIdx = r.pos + 2
+ skipSection(r)
+ of "COMPILERPROCS":
+ r.compilerProcsIdx = r.pos + 2
+ skipSection(r)
+ of "INDEX":
+ processIndex(r, r.index)
+ of "IMPORTS":
+ processIndex(r, r.imports)
+ of "CONVERTERS":
+ r.convertersIdx = r.pos + 1
+ skipSection(r)
+ of "METHODS":
+ r.methodsIdx = r.pos + 1
+ skipSection(r)
+ of "DATA":
+ r.dataIdx = r.pos + 2 # "(\10"
+ # We do not read the DATA section here! We read the needed objects on
+ # demand. And the DATA section comes last in the file, so we stop here:
+ break
+ of "INIT":
+ r.initIdx = r.pos + 2 # "(\10"
+ skipSection(r)
+ else:
+ internalError("invalid section: '" & section &
+ "' at " & $r.line & " in " & r.filename)
+ #MsgWriteln("skipping section: " & section &
+ # " at " & $r.line & " in " & r.filename)
+ skipSection(r)
+ if r.s[r.pos] == '\x0A':
+ inc(r.pos)
+ inc(r.line)
+
+
+proc startsWith(buf: cstring, token: string, pos = 0): bool =
+ var s = 0
+ while s < token.len and buf[pos+s] == token[s]: inc s
+ result = s == token.len
+
+proc newRodReader(modfilename: string, hash: SecureHash,
+ readerIndex: int): PRodReader =
+ new(result)
+ try:
+ result.memfile = memfiles.open(modfilename)
+ except OSError:
+ return nil
+ result.files = @[]
+ result.modDeps = @[]
+ result.methods = @[]
+ var r = result
+ r.reason = rrNone
+ r.pos = 0
+ r.line = 1
+ r.readerIndex = readerIndex
+ r.filename = modfilename
+ r.syms = initTable[int, PSym]()
+ # we terminate the file explicitly with ``\0``, so the cast to `cstring`
+ # is safe:
+ r.s = cast[cstring](r.memfile.mem)
+ if startsWith(r.s, "NIM:"):
+ initIiTable(r.index.tab)
+ initIiTable(r.imports.tab) # looks like a ROD file
+ inc(r.pos, 4)
+ var version = ""
+ while r.s[r.pos] notin {'\0', '\x0A'}:
+ add(version, r.s[r.pos])
+ inc(r.pos)
+ if r.s[r.pos] == '\x0A': inc(r.pos)
+ if version != RodFileVersion:
+ # since ROD files are only for caching, no backwards compatibility is
+ # needed
+ result = nil
+ else:
+ result = nil
+
+proc rrGetType(r: PRodReader, id: int, info: TLineInfo): PType =
+ result = PType(idTableGet(gTypeTable, id))
+ if result == nil:
+ # load the type:
+ var oldPos = r.pos
+ var d = iiTableGet(r.index.tab, id)
+ if d == InvalidKey: internalError(info, "rrGetType")
+ r.pos = d + r.dataIdx
+ result = decodeType(r, info)
+ r.pos = oldPos
+
+type
+ TFileModuleRec{.final.} = object
+ filename*: string
+ reason*: TReasonForRecompile
+ rd*: PRodReader
+ hash*: SecureHash
+ hashDone*: bool
+
+ TFileModuleMap = seq[TFileModuleRec]
+
+var gMods*: TFileModuleMap = @[]
+
+proc decodeSymSafePos(rd: PRodReader, offset: int, info: TLineInfo): PSym =
+ # all compiled modules
+ if rd.dataIdx == 0: internalError(info, "dataIdx == 0")
+ var oldPos = rd.pos
+ rd.pos = offset + rd.dataIdx
+ result = decodeSym(rd, info)
+ rd.pos = oldPos
+
+proc findSomeWhere(id: int) =
+ for i in countup(0, high(gMods)):
+ var rd = gMods[i].rd
+ if rd != nil:
+ var d = iiTableGet(rd.index.tab, id)
+ if d != InvalidKey:
+ echo "found id ", id, " in ", gMods[i].filename
+
+proc getReader(moduleId: int): PRodReader =
+ # we can't index 'gMods' here as it's indexed by a *file index* which is not
+ # the module ID! We could introduce a mapping ID->PRodReader but I'll leave
+ # this for later versions if benchmarking shows the linear search causes
+ # problems:
+ for i in 0 .. '\x0A' and r.s[r.pos] != ')':
+ var d = decodeVInt(r.s, r.pos)
+ inc(r.pos) # #10
+ var p = r.pos
+ r.pos = d + r.dataIdx
+ addSon(result, decodeNode(r, unknownLineInfo()))
+ r.pos = p
+ r.pos = oldPos
+
+proc loadConverters(r: PRodReader) =
+ # We have to ensure that no exported converter is a stub anymore, and the
+ # import mechanism takes care of the rest.
+ if r.convertersIdx == 0 or r.dataIdx == 0:
+ internalError("importConverters")
+ r.pos = r.convertersIdx
+ while r.s[r.pos] > '\x0A':
+ var d = decodeVInt(r.s, r.pos)
+ discard rrGetSym(r, d, unknownLineInfo())
+ if r.s[r.pos] == ' ': inc(r.pos)
+
+proc loadMethods(r: PRodReader) =
+ if r.methodsIdx == 0 or r.dataIdx == 0:
+ internalError("loadMethods")
+ r.pos = r.methodsIdx
+ while r.s[r.pos] > '\x0A':
+ var d = decodeVInt(r.s, r.pos)
+ r.methods.add(rrGetSym(r, d, unknownLineInfo()))
+ if r.s[r.pos] == ' ': inc(r.pos)
+
+proc getHash*(fileIdx: int32): SecureHash =
+ internalAssert fileIdx >= 0 and fileIdx < gMods.len
+
+ if gMods[fileIdx].hashDone:
+ return gMods[fileIdx].hash
+
+ result = secureHashFile(fileIdx.toFullPath)
+ gMods[fileIdx].hash = result
+
+template growCache*(cache, pos) =
+ if cache.len <= pos: cache.setLen(pos+1)
+
+proc checkDep(fileIdx: int32): TReasonForRecompile =
+ assert fileIdx != InvalidFileIDX
+ growCache gMods, fileIdx
+ if gMods[fileIdx].reason != rrEmpty:
+ # reason has already been computed for this module:
+ return gMods[fileIdx].reason
+ let filename = fileIdx.toFilename
+ var hash = getHash(fileIdx)
+ gMods[fileIdx].reason = rrNone # we need to set it here to avoid cycles
+ result = rrNone
+ var r: PRodReader = nil
+ var rodfile = toGeneratedFile(filename.withPackageName, RodExt)
+ r = newRodReader(rodfile, hash, fileIdx)
+ if r == nil:
+ result = (if existsFile(rodfile): rrRodInvalid else: rrRodDoesNotExist)
+ else:
+ processRodFile(r, hash)
+ result = r.reason
+ if result == rrNone:
+ # check modules it depends on
+ # NOTE: we need to process the entire module graph so that no ID will
+ # be used twice! However, compilation speed does not suffer much from
+ # this, since results are cached.
+ var res = checkDep(systemFileIdx)
+ if res != rrNone: result = rrModDeps
+ for i in countup(0, high(r.modDeps)):
+ res = checkDep(r.modDeps[i])
+ if res != rrNone:
+ result = rrModDeps
+ # we cannot break here, because of side-effects of `checkDep`
+ if result != rrNone:
+ rawMessage(hintProcessing, reasonToFrmt[result] % filename)
+ if result != rrNone or optForceFullMake in gGlobalOptions:
+ # recompilation is necessary:
+ if r != nil: memfiles.close(r.memfile)
+ r = nil
+ gMods[fileIdx].rd = r
+ gMods[fileIdx].reason = result # now we know better
+
+proc handleSymbolFile(module: PSym): PRodReader =
+ let fileIdx = module.fileIdx
+ if optSymbolFiles notin gGlobalOptions:
+ module.id = getID()
+ return nil
+ idgen.loadMaxIds(options.gProjectPath / options.gProjectName)
+
+ discard checkDep(fileIdx)
+ if gMods[fileIdx].reason == rrEmpty: internalError("handleSymbolFile")
+ result = gMods[fileIdx].rd
+ if result != nil:
+ module.id = result.moduleID
+ result.syms[module.id] = module
+ processInterf(result, module)
+ processCompilerProcs(result, module)
+ loadConverters(result)
+ loadMethods(result)
+ else:
+ module.id = getID()
+
+proc rawLoadStub(s: PSym) =
+ if s.kind != skStub: internalError("loadStub")
+ var rd = gMods[s.position].rd
+ var theId = s.id # used for later check
+ var d = iiTableGet(rd.index.tab, s.id)
+ if d == InvalidKey: internalError("loadStub: invalid key")
+ var rs = decodeSymSafePos(rd, d, unknownLineInfo())
+ if rs != s:
+ #echo "rs: ", toHex(cast[int](rs.position), int.sizeof * 2),
+ # "\ns: ", toHex(cast[int](s.position), int.sizeof * 2)
+ internalError(rs.info, "loadStub: wrong symbol")
+ elif rs.id != theId:
+ internalError(rs.info, "loadStub: wrong ID")
+ #MessageOut('loaded stub: ' + s.name.s);
+
+proc loadStub*(s: PSym) =
+ ## loads the stub symbol `s`.
+
+ # deactivate the GC here because we do a deep recursion and generate no
+ # garbage when restoring parts of the object graph anyway.
+ # Since we die with internal errors if this fails, no try-finally is
+ # necessary.
+ GC_disable()
+ rawLoadStub(s)
+ GC_enable()
+
+proc getBody*(s: PSym): PNode =
+ ## retrieves the AST's body of `s`. If `s` has been loaded from a rod-file
+ ## it may perform an expensive reload operation. Otherwise it's a simple
+ ## accessor.
+ assert s.kind in routineKinds
+ # prevent crashes due to incorrect macro transformations (bug #2377)
+ if s.ast.isNil or bodyPos >= s.ast.len: return ast.emptyNode
+ result = s.ast.sons[bodyPos]
+ if result == nil:
+ assert s.offset != 0
+ var r = gMods[s.position].rd
+ var oldPos = r.pos
+ r.pos = s.offset
+ result = decodeNode(r, s.info)
+ r.pos = oldPos
+ s.ast.sons[bodyPos] = result
+ s.offset = 0
+
+initIdTable(gTypeTable)
+initStrTable(rodCompilerprocs)
+
+# viewer:
+proc writeNode(f: File; n: PNode) =
+ f.write("(")
+ if n != nil:
+ f.write($n.kind)
+ if n.typ != nil:
+ f.write('^')
+ f.write(n.typ.id)
+ case n.kind
+ of nkCharLit..nkInt64Lit:
+ if n.intVal != 0:
+ f.write('!')
+ f.write(n.intVal)
+ of nkFloatLit..nkFloat64Lit:
+ if n.floatVal != 0.0:
+ f.write('!')
+ f.write($n.floatVal)
+ of nkStrLit..nkTripleStrLit:
+ if n.strVal != "":
+ f.write('!')
+ f.write(n.strVal.escape)
+ of nkIdent:
+ f.write('!')
+ f.write(n.ident.s)
+ of nkSym:
+ f.write('!')
+ f.write(n.sym.id)
+ else:
+ for i in countup(0, sonsLen(n) - 1):
+ writeNode(f, n.sons[i])
+ f.write(")")
+
+proc writeSym(f: File; s: PSym) =
+ if s == nil:
+ f.write("{}\n")
+ return
+ f.write("{")
+ f.write($s.kind)
+ f.write('+')
+ f.write(s.id)
+ f.write('&')
+ f.write(s.name.s)
+ if s.typ != nil:
+ f.write('^')
+ f.write(s.typ.id)
+ if s.owner != nil:
+ f.write('*')
+ f.write(s.owner.id)
+ if s.flags != {}:
+ f.write('$')
+ f.write($s.flags)
+ if s.magic != mNone:
+ f.write('@')
+ f.write($s.magic)
+ if s.options != gOptions:
+ f.write('!')
+ f.write($s.options)
+ if s.position != 0:
+ f.write('%')
+ f.write($s.position)
+ if s.offset != -1:
+ f.write('`')
+ f.write($s.offset)
+ if s.constraint != nil:
+ f.write('#')
+ f.writeNode(s.constraint)
+ if s.ast != nil:
+ f.writeNode(s.ast)
+ f.write("}\n")
+
+proc writeType(f: File; t: PType) =
+ if t == nil:
+ f.write("[]\n")
+ return
+ f.write('[')
+ f.write($t.kind)
+ f.write('+')
+ f.write($t.id)
+ if t.n != nil:
+ f.writeNode(t.n)
+ if t.flags != {}:
+ f.write('$')
+ f.write($t.flags)
+ if t.callConv != low(t.callConv):
+ f.write('?')
+ f.write($t.callConv)
+ if t.owner != nil:
+ f.write('*')
+ f.write($t.owner.id)
+ if t.sym != nil:
+ f.write('&')
+ f.write(t.sym.id)
+ if t.size != -1:
+ f.write('/')
+ f.write($t.size)
+ if t.align != 2:
+ f.write('=')
+ f.write($t.align)
+ for i in countup(0, sonsLen(t) - 1):
+ if t.sons[i] == nil:
+ f.write("^()")
+ else:
+ f.write('^')
+ f.write($t.sons[i].id)
+ f.write("]\n")
+
+proc viewFile(rodfile: string) =
+ var r = newRodReader(rodfile, secureHash(""), 0)
+ if r == nil:
+ rawMessage(errGenerated, "cannot open file (or maybe wrong version):" &
+ rodfile)
+ return
+ r.inViewMode = true
+ var outf = system.open(rodfile.changeFileExt(".rod.txt"), fmWrite)
+ while r.s[r.pos] != '\0':
+ let section = rdWord(r)
+ case section
+ of "HASH":
+ inc(r.pos) # skip ':'
+ outf.writeLine("HASH:", $decodeVInt(r.s, r.pos))
+ of "ID":
+ inc(r.pos) # skip ':'
+ r.moduleID = decodeVInt(r.s, r.pos)
+ setId(r.moduleID)
+ outf.writeLine("ID:", $r.moduleID)
+ of "ORIGFILE":
+ inc(r.pos)
+ r.origFile = decodeStr(r.s, r.pos)
+ outf.writeLine("ORIGFILE:", r.origFile)
+ of "OPTIONS":
+ inc(r.pos) # skip ':'
+ r.options = cast[TOptions](int32(decodeVInt(r.s, r.pos)))
+ outf.writeLine("OPTIONS:", $r.options)
+ of "GOPTIONS":
+ inc(r.pos) # skip ':'
+ let dep = cast[TGlobalOptions](int32(decodeVInt(r.s, r.pos)))
+ outf.writeLine("GOPTIONS:", $dep)
+ of "CMD":
+ inc(r.pos) # skip ':'
+ let dep = cast[TCommands](int32(decodeVInt(r.s, r.pos)))
+ outf.writeLine("CMD:", $dep)
+ of "DEFINES":
+ inc(r.pos) # skip ':'
+ var d = 0
+ outf.write("DEFINES:")
+ while r.s[r.pos] > '\x0A':
+ let w = decodeStr(r.s, r.pos)
+ inc(d)
+ outf.write(" ", w)
+ if r.s[r.pos] == ' ': inc(r.pos)
+ outf.write("\n")
+ of "FILES":
+ inc(r.pos, 2) # skip "(\10"
+ inc(r.line)
+ outf.write("FILES(\n")
+ while r.s[r.pos] != ')':
+ let relativePath = decodeStr(r.s, r.pos)
+ let resolvedPath = relativePath.findModule(r.origFile)
+ let finalPath = if resolvedPath.len > 0: resolvedPath else: relativePath
+ r.files.add(finalPath.fileInfoIdx)
+ inc(r.pos) # skip #10
+ inc(r.line)
+ outf.writeLine finalPath
+ if r.s[r.pos] == ')': inc(r.pos)
+ outf.write(")\n")
+ of "INCLUDES":
+ inc(r.pos, 2) # skip "(\10"
+ inc(r.line)
+ outf.write("INCLUDES(\n")
+ while r.s[r.pos] != ')':
+ let w = r.files[decodeVInt(r.s, r.pos)]
+ inc(r.pos) # skip ' '
+ let inclHash = decodeVInt(r.s, r.pos)
+ if r.s[r.pos] == '\x0A':
+ inc(r.pos)
+ inc(r.line)
+ outf.write(w, " ", inclHash, "\n")
+ if r.s[r.pos] == ')': inc(r.pos)
+ outf.write(")\n")
+ of "DEPS":
+ inc(r.pos) # skip ':'
+ outf.write("DEPS:")
+ while r.s[r.pos] > '\x0A':
+ let v = int32(decodeVInt(r.s, r.pos))
+ r.modDeps.add(r.files[v])
+ if r.s[r.pos] == ' ': inc(r.pos)
+ outf.write(" ", r.files[v])
+ outf.write("\n")
+ of "INTERF", "COMPILERPROCS":
+ inc r.pos, 2
+ if section == "INTERF": r.interfIdx = r.pos
+ else: r.compilerProcsIdx = r.pos
+ outf.write(section, "(\n")
+ while (r.s[r.pos] > '\x0A') and (r.s[r.pos] != ')'):
+ let w = decodeStr(r.s, r.pos)
+ inc(r.pos)
+ let key = decodeVInt(r.s, r.pos)
+ inc(r.pos) # #10
+ outf.write(w, " ", key, "\n")
+ if r.s[r.pos] == ')': inc r.pos
+ outf.write(")\n")
+ of "INDEX":
+ outf.write(section, "(\n")
+ processIndex(r, r.index, outf)
+ outf.write(")\n")
+ of "IMPORTS":
+ outf.write(section, "(\n")
+ processIndex(r, r.imports, outf)
+ outf.write(")\n")
+ of "CONVERTERS", "METHODS":
+ inc r.pos
+ if section == "METHODS": r.methodsIdx = r.pos
+ else: r.convertersIdx = r.pos
+ outf.write(section, ":")
+ while r.s[r.pos] > '\x0A':
+ let d = decodeVInt(r.s, r.pos)
+ outf.write(" ", $d)
+ if r.s[r.pos] == ' ': inc(r.pos)
+ outf.write("\n")
+ of "DATA":
+ inc(r.pos, 2)
+ r.dataIdx = r.pos
+ outf.write("DATA(\n")
+ while r.s[r.pos] != ')':
+ if r.s[r.pos] == '(':
+ outf.writeNode decodeNode(r, unknownLineInfo())
+ outf.write("\n")
+ elif r.s[r.pos] == '[':
+ outf.writeType decodeType(r, unknownLineInfo())
+ else:
+ outf.writeSym decodeSym(r, unknownLineInfo())
+ if r.s[r.pos] == '\x0A':
+ inc(r.pos)
+ inc(r.line)
+ if r.s[r.pos] == ')': inc r.pos
+ outf.write(")\n")
+ of "INIT":
+ outf.write("INIT(\n")
+ inc r.pos, 2
+ r.initIdx = r.pos
+ while r.s[r.pos] > '\x0A' and r.s[r.pos] != ')':
+ let d = decodeVInt(r.s, r.pos)
+ inc(r.pos) # #10
+ #let p = r.pos
+ #r.pos = d + r.dataIdx
+ #outf.writeNode decodeNode(r, UnknownLineInfo())
+ #outf.write("\n")
+ #r.pos = p
+ if r.s[r.pos] == ')': inc r.pos
+ outf.write(")\n")
+ else:
+ internalError("invalid section: '" & section &
+ "' at " & $r.line & " in " & r.filename)
+ skipSection(r)
+ if r.s[r.pos] == '\x0A':
+ inc(r.pos)
+ inc(r.line)
+ outf.close
+
+when isMainModule:
+ viewFile(paramStr(1).addFileExt(rodExt))
diff --git a/compiler/rodutils.nim b/compiler/rodutils.nim
index e2e9e1eb2..91f93d279 100644
--- a/compiler/rodutils.nim
+++ b/compiler/rodutils.nim
@@ -8,67 +8,28 @@
#
## Serialization utilities for the compiler.
-import strutils, math
+import strutils
-when defined(nimPreviewSlimSystem):
- import std/assertions
+proc c_sprintf(buf, frmt: cstring) {.importc: "sprintf", header: "", nodecl, varargs.}
-# bcc on windows doesn't have C99 functions
-when defined(windows) and defined(bcc):
- {.emit: """#if defined(_MSC_VER) && _MSC_VER < 1900
- #include
- static int c99_vsnprintf(char *outBuf, size_t size, const char *format, va_list ap) {
- int count = -1;
- if (size != 0) count = _vsnprintf_s(outBuf, size, _TRUNCATE, format, ap);
- if (count == -1) count = _vscprintf(format, ap);
- return count;
- }
- int snprintf(char *outBuf, size_t size, const char *format, ...) {
- int count;
- va_list ap;
- va_start(ap, format);
- count = c99_vsnprintf(outBuf, size, format, ap);
- va_end(ap);
- return count;
- }
- #endif
- """.}
-
-proc c_snprintf(s: cstring; n:uint; frmt: cstring): cint {.importc: "snprintf", header: "", 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: ""
- case classify(f)
- of fcNan:
- if signbit(f):
- result = "-NAN"
- else:
- result = "NAN"
- of fcNegZero:
- result = "-0.0" & literalPostfix
- of fcZero:
- result = "0.0" & literalPostfix
- of fcInf:
- result = "INF"
- of fcNegInf:
- result = "-INF"
+proc toStrMaxPrecision*(f: BiggestFloat): string =
+ if f != f:
+ result = "NAN"
+ elif f == 0.0:
+ result = "0.0"
+ elif f == 0.5 * f:
+ if f > 0.0: result = "INF"
+ else: result = "-INF"
else:
- result.addFloatRoundtrip(f)
- result.add literalPostfix
+ var buf: array [0..80, char]
+ c_sprintf(buf, "%#.16e", f)
+ result = $buf
proc encodeStr*(s: string, result: var string) =
- for i 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:
+ add(result, '[')
+ encodeVInt(ord(t.kind), result)
+ add(result, '+')
+ encodeVInt(t.id, result)
+ if t.n != nil:
+ encodeNode(w, unknownLineInfo(), t.n, result)
+ if t.flags != {}:
+ add(result, '$')
+ encodeVInt(cast[int32](t.flags), result)
+ if t.callConv != low(t.callConv):
+ add(result, '?')
+ encodeVInt(ord(t.callConv), result)
+ if t.owner != nil:
+ add(result, '*')
+ encodeVInt(t.owner.id, result)
+ pushSym(w, t.owner)
+ if t.sym != nil:
+ add(result, '&')
+ encodeVInt(t.sym.id, result)
+ pushSym(w, t.sym)
+ if t.size != - 1:
+ add(result, '/')
+ encodeVBiggestInt(t.size, result)
+ if t.align != 2:
+ add(result, '=')
+ encodeVInt(t.align, result)
+ encodeLoc(w, t.loc, result)
+ for i in countup(0, sonsLen(t) - 1):
+ if t.sons[i] == nil:
+ add(result, "^()")
+ else:
+ add(result, '^')
+ encodeVInt(t.sons[i].id, result)
+ pushType(w, t.sons[i])
+
+proc encodeLib(w: PRodWriter, lib: PLib, info: TLineInfo, result: var string) =
+ add(result, '|')
+ encodeVInt(ord(lib.kind), result)
+ add(result, '|')
+ encodeStr($lib.name, result)
+ add(result, '|')
+ encodeNode(w, info, lib.path, result)
+
+proc encodeSym(w: PRodWriter, s: PSym, result: var string) =
+ if s == nil:
+ # nil nodes have to be stored too:
+ result.add("{}")
+ return
+ # we need no surrounding {} here because the symbol is in a line of its own
+ encodeVInt(ord(s.kind), result)
+ result.add('+')
+ encodeVInt(s.id, result)
+ result.add('&')
+ encodeStr(s.name.s, result)
+ if s.typ != nil:
+ result.add('^')
+ encodeVInt(s.typ.id, result)
+ pushType(w, s.typ)
+ result.add('?')
+ if s.info.col != -1'i16: encodeVInt(s.info.col, result)
+ result.add(',')
+ if s.info.line != -1'i16: encodeVInt(s.info.line, result)
+ result.add(',')
+ encodeVInt(fileIdx(w, toFilename(s.info)), result)
+ if s.owner != nil:
+ result.add('*')
+ encodeVInt(s.owner.id, result)
+ pushSym(w, s.owner)
+ if s.flags != {}:
+ result.add('$')
+ encodeVInt(cast[int32](s.flags), result)
+ if s.magic != mNone:
+ result.add('@')
+ encodeVInt(ord(s.magic), result)
+ if s.options != w.options:
+ result.add('!')
+ encodeVInt(cast[int32](s.options), result)
+ if s.position != 0:
+ result.add('%')
+ encodeVInt(s.position, result)
+ if s.offset != - 1:
+ result.add('`')
+ encodeVInt(s.offset, result)
+ encodeLoc(w, s.loc, result)
+ if s.annex != nil: encodeLib(w, s.annex, s.info, result)
+ if s.constraint != nil:
+ add(result, '#')
+ encodeNode(w, unknownLineInfo(), s.constraint, result)
+ # lazy loading will soon reload the ast lazily, so the ast needs to be
+ # the last entry of a symbol:
+ if s.ast != nil:
+ # we used to attempt to save space here by only storing a dummy AST if
+ # it is not necessary, but Nim's heavy compile-time evaluation features
+ # make that unfeasible nowadays:
+ encodeNode(w, s.info, s.ast, result)
+ when false:
+ var codeAst: PNode = nil
+ if not astNeeded(s):
+ codeAst = s.ast.sons[codePos]
+ # ugly hack to not store the AST:
+ s.ast.sons[codePos] = ast.emptyNode
+ encodeNode(w, s.info, s.ast, result)
+ if codeAst != nil:
+ # resore the AST:
+ s.ast.sons[codePos] = codeAst
+
+proc addToIndex(w: var TIndex, key, val: int) =
+ if key - w.lastIdxKey == 1:
+ # we do not store a key-diff of 1 to safe space
+ encodeVInt(val - w.lastIdxVal, w.r)
+ else:
+ encodeVInt(key - w.lastIdxKey, w.r)
+ add(w.r, ' ')
+ encodeVInt(val - w.lastIdxVal, w.r)
+ add(w.r, rodNL)
+ w.lastIdxKey = key
+ w.lastIdxVal = val
+ iiTablePut(w.tab, key, val)
+
+const debugWrittenIds = false
+
+when debugWrittenIds:
+ var debugWritten = initIntSet()
+
+proc symStack(w: PRodWriter): int =
+ var i = 0
+ while i < len(w.sstack):
+ var s = w.sstack[i]
+ if sfForward in s.flags:
+ w.sstack[result] = s
+ inc result
+ elif iiTableGet(w.index.tab, s.id) == InvalidKey:
+ var m = getModule(s)
+ if m == nil and s.kind != skPackage:
+ internalError("symStack: module nil: " & s.name.s)
+ if s.kind == skPackage or m.id == w.module.id or sfFromGeneric in s.flags:
+ # put definition in here
+ var L = w.data.len
+ addToIndex(w.index, s.id, L)
+ when debugWrittenIds: incl(debugWritten, s.id)
+ encodeSym(w, s, w.data)
+ add(w.data, rodNL)
+ # put into interface section if appropriate:
+ if {sfExported, sfFromGeneric} * s.flags == {sfExported} and
+ s.kind in ExportableSymKinds:
+ encodeStr(s.name.s, w.interf)
+ add(w.interf, ' ')
+ encodeVInt(s.id, w.interf)
+ add(w.interf, rodNL)
+ if sfCompilerProc in s.flags:
+ encodeStr(s.name.s, w.compilerProcs)
+ add(w.compilerProcs, ' ')
+ encodeVInt(s.id, w.compilerProcs)
+ add(w.compilerProcs, rodNL)
+ if s.kind == skConverter or hasPattern(s):
+ if w.converters.len != 0: add(w.converters, ' ')
+ encodeVInt(s.id, w.converters)
+ if s.kind == skMethod and sfDispatcher notin s.flags:
+ if w.methods.len != 0: add(w.methods, ' ')
+ encodeVInt(s.id, w.methods)
+ elif iiTableGet(w.imports.tab, s.id) == InvalidKey:
+ addToIndex(w.imports, s.id, m.id)
+ when debugWrittenIds:
+ if not contains(debugWritten, s.id):
+ echo(w.filename)
+ debug(s)
+ debug(s.owner)
+ debug(m)
+ internalError("Symbol referred to but never written")
+ inc(i)
+ setLen(w.sstack, result)
+
+proc typeStack(w: PRodWriter): int =
+ var i = 0
+ while i < len(w.tstack):
+ var t = w.tstack[i]
+ if t.kind == tyForward:
+ w.tstack[result] = t
+ inc result
+ elif iiTableGet(w.index.tab, t.id) == InvalidKey:
+ var L = w.data.len
+ addToIndex(w.index, t.id, L)
+ encodeType(w, t, w.data)
+ add(w.data, rodNL)
+ inc(i)
+ setLen(w.tstack, result)
+
+proc processStacks(w: PRodWriter, finalPass: bool) =
+ var oldS = 0
+ var oldT = 0
+ while true:
+ var slen = symStack(w)
+ var tlen = typeStack(w)
+ if slen == oldS and tlen == oldT: break
+ oldS = slen
+ oldT = tlen
+ if finalPass and (oldS != 0 or oldT != 0):
+ internalError("could not serialize some forwarded symbols/types")
+
+proc rawAddInterfaceSym(w: PRodWriter, s: PSym) =
+ pushSym(w, s)
+ processStacks(w, false)
+
+proc addInterfaceSym(w: PRodWriter, s: PSym) =
+ if w == nil: return
+ if s.kind in ExportableSymKinds and
+ {sfExported, sfCompilerProc} * s.flags != {}:
+ rawAddInterfaceSym(w, s)
+
+proc addStmt(w: PRodWriter, n: PNode) =
+ encodeVInt(w.data.len, w.init)
+ add(w.init, rodNL)
+ encodeNode(w, unknownLineInfo(), n, w.data)
+ add(w.data, rodNL)
+ processStacks(w, false)
+
+proc writeRod(w: PRodWriter) =
+ processStacks(w, true)
+ var f: File
+ if not open(f, completeGeneratedFilePath(changeFileExt(
+ w.filename.withPackageName, RodExt)),
+ fmWrite):
+ #echo "couldn't write rod file for: ", w.filename
+ return
+ # write header:
+ f.write("NIM:")
+ f.write(RodFileVersion)
+ f.write(rodNL)
+ var id = "ID:"
+ encodeVInt(w.module.id, id)
+ f.write(id)
+ f.write(rodNL)
+
+ var orig = "ORIGFILE:"
+ encodeStr(w.origFile, orig)
+ f.write(orig)
+ f.write(rodNL)
+
+ var hash = "HASH:"
+ encodeStr($w.hash, hash)
+ f.write(hash)
+ f.write(rodNL)
+
+ var options = "OPTIONS:"
+ encodeVInt(cast[int32](w.options), options)
+ f.write(options)
+ f.write(rodNL)
+
+ var goptions = "GOPTIONS:"
+ encodeVInt(cast[int32](gGlobalOptions), goptions)
+ f.write(goptions)
+ f.write(rodNL)
+
+ var cmd = "CMD:"
+ encodeVInt(cast[int32](gCmd), cmd)
+ f.write(cmd)
+ f.write(rodNL)
+
+ f.write("DEFINES:")
+ f.write(w.defines)
+ f.write(rodNL)
+
+ var files = "FILES(" & rodNL
+ for i in countup(0, high(w.files)):
+ encodeStr(w.files[i], files)
+ files.add(rodNL)
+ f.write(files)
+ f.write(')' & rodNL)
+
+ f.write("INCLUDES(" & rodNL)
+ f.write(w.inclDeps)
+ f.write(')' & rodNL)
+
+ f.write("DEPS:")
+ f.write(w.modDeps)
+ f.write(rodNL)
+
+ f.write("INTERF(" & rodNL)
+ f.write(w.interf)
+ f.write(')' & rodNL)
+
+ f.write("COMPILERPROCS(" & rodNL)
+ f.write(w.compilerProcs)
+ f.write(')' & rodNL)
+
+ f.write("INDEX(" & rodNL)
+ f.write(w.index.r)
+ f.write(')' & rodNL)
+
+ f.write("IMPORTS(" & rodNL)
+ f.write(w.imports.r)
+ f.write(')' & rodNL)
+
+ f.write("CONVERTERS:")
+ f.write(w.converters)
+ f.write(rodNL)
+
+ f.write("METHODS:")
+ f.write(w.methods)
+ f.write(rodNL)
+
+ f.write("INIT(" & rodNL)
+ f.write(w.init)
+ f.write(')' & rodNL)
+
+ f.write("DATA(" & rodNL)
+ f.write(w.data)
+ f.write(')' & rodNL)
+ # write trailing zero which is necessary because we use memory mapped files
+ # for reading:
+ f.write("\0")
+ f.close()
+
+ #echo "interf: ", w.interf.len
+ #echo "index: ", w.index.r.len
+ #echo "init: ", w.init.len
+ #echo "data: ", w.data.len
+
+proc process(c: PPassContext, n: PNode): PNode =
+ result = n
+ if c == nil: return
+ var w = PRodWriter(c)
+ case n.kind
+ of nkStmtList:
+ for i in countup(0, sonsLen(n) - 1): discard process(c, n.sons[i])
+ #var s = n.sons[namePos].sym
+ #addInterfaceSym(w, s)
+ of nkProcDef, nkMethodDef, nkIteratorDef, nkConverterDef,
+ nkTemplateDef, nkMacroDef:
+ var s = n.sons[namePos].sym
+ if s == nil: internalError(n.info, "rodwrite.process")
+ if n.sons[bodyPos] == nil:
+ internalError(n.info, "rodwrite.process: body is nil")
+ if n.sons[bodyPos].kind != nkEmpty or s.magic != mNone or
+ sfForward notin s.flags:
+ addInterfaceSym(w, s)
+ of nkVarSection, nkLetSection, nkConstSection:
+ for i in countup(0, sonsLen(n) - 1):
+ var a = n.sons[i]
+ if a.kind == nkCommentStmt: continue
+ addInterfaceSym(w, a.sons[0].sym)
+ of nkTypeSection:
+ for i in countup(0, sonsLen(n) - 1):
+ var a = n.sons[i]
+ if a.kind == nkCommentStmt: continue
+ if a.sons[0].kind != nkSym: internalError(a.info, "rodwrite.process")
+ var s = a.sons[0].sym
+ addInterfaceSym(w, s)
+ # this takes care of enum fields too
+ # Note: The check for ``s.typ.kind = tyEnum`` is wrong for enum
+ # type aliasing! Otherwise the same enum symbol would be included
+ # several times!
+ #
+ # if (a.sons[2] <> nil) and (a.sons[2].kind = nkEnumTy) then begin
+ # a := s.typ.n;
+ # for j := 0 to sonsLen(a)-1 do
+ # addInterfaceSym(w, a.sons[j].sym);
+ # end
+ of nkImportStmt:
+ for i in countup(0, sonsLen(n) - 1): addModDep(w, getModuleName(n.sons[i]))
+ addStmt(w, n)
+ of nkFromStmt:
+ addModDep(w, getModuleName(n.sons[0]))
+ addStmt(w, n)
+ of nkIncludeStmt:
+ for i in countup(0, sonsLen(n) - 1): addInclDep(w, getModuleName(n.sons[i]))
+ of nkPragma:
+ addStmt(w, n)
+ else:
+ discard
+
+proc myOpen(module: PSym): PPassContext =
+ if module.id < 0: internalError("rodwrite: module ID not set")
+ var w = newRodWriter(module.fileIdx.getHash, module)
+ rawAddInterfaceSym(w, module)
+ result = w
+
+proc myClose(c: PPassContext, n: PNode): PNode =
+ result = process(c, n)
+ var w = PRodWriter(c)
+ writeRod(w)
+ idgen.saveMaxIds(options.gProjectPath / options.gProjectName)
+
+const rodwritePass* = makePass(open = myOpen, close = myClose, process = process)
+
diff --git a/compiler/ropes.nim b/compiler/ropes.nim
index 5bf154393..bfae7aaa4 100644
--- a/compiler/ropes.nim
+++ b/compiler/ropes.nim
@@ -7,78 +7,255 @@
# distribution, for details about the copyright.
#
-# Ropes for the C code generator. Ropes are mapped to `string` directly nowadays.
+# Ropes for the C code generator
+#
+# Ropes are a data structure that represents a very long string
+# efficiently; especially concatenation is done in O(1) instead of O(N).
+# Ropes make use a lazy evaluation: They are essentially concatenation
+# trees that are only flattened when converting to a native Nim
+# string or when written to disk. The empty string is represented by a
+# nil pointer.
+# A little picture makes everything clear:
+#
+# "this string" & " is internally " & "represented as"
+#
+# con -- inner nodes do not contain raw data
+# / \
+# / \
+# / \
+# con "represented as"
+# / \
+# / \
+# / \
+# / \
+# / \
+#"this string" " is internally "
+#
+# Note that this is the same as:
+# "this string" & (" is internally " & "represented as")
+#
+# con
+# / \
+# / \
+# / \
+# "this string" con
+# / \
+# / \
+# / \
+# / \
+# / \
+#" is internally " "represented as"
+#
+# The 'con' operator is associative! This does not matter however for
+# the algorithms we use for ropes.
+#
+# Note that the left and right pointers are not needed for leaves.
+# Leaves have relatively high memory overhead (~30 bytes on a 32
+# bit machines) and we produce many of them. This is why we cache and
+# share leaves across different rope trees.
+# To cache them they are inserted in a `cache` array.
-from pathutils import AbsoluteFile
-
-when defined(nimPreviewSlimSystem):
- import std/[assertions, syncio, formatfloat]
+import
+ platform, hashes
type
FormatStr* = string # later we may change it to CString for better
# performance of the code generator (assignments
# copy the format strings
# though it is not necessary)
- Rope* = string
+ Rope* = ref RopeObj
+ RopeObj*{.acyclic.} = object of RootObj # the empty rope is represented
+ # by nil to safe space
+ left*, right*: Rope
+ length*: int
+ data*: string # != nil if a leaf
-proc newRopeAppender*(): string {.inline.} =
- result = newString(0)
+ RopeSeq* = seq[Rope]
-proc freeze*(r: Rope) {.inline.} = discard
+ RopesError* = enum
+ rCannotOpenFile
+ rInvalidFormatStr
-proc resetRopeCache* = discard
+# implementation
-template rope*(s: string): string = s
+var errorHandler*: proc(err: RopesError, msg: string, useWarning = false)
+ # avoid dependency on msgs.nim
+
+proc len*(a: Rope): int =
+ ## the rope's length
+ if a == nil: result = 0
+ else: result = a.length
+
+proc newRope(data: string = nil): Rope =
+ new(result)
+ if data != nil:
+ result.length = len(data)
+ result.data = data
+
+proc newMutableRope*(capacity = 30): Rope =
+ ## creates a new rope that supports direct modifications of the rope's
+ ## 'data' and 'length' fields.
+ new(result)
+ result.data = newStringOfCap(capacity)
+
+proc freezeMutableRope*(r: Rope) {.inline.} =
+ r.length = r.data.len
+
+var
+ cache: array[0..2048*2 - 1, Rope]
+
+proc resetRopeCache* =
+ for i in low(cache)..high(cache):
+ cache[i] = nil
+
+proc ropeInvariant(r: Rope): bool =
+ if r == nil:
+ result = true
+ else:
+ result = true #
+ # if r.data <> snil then
+ # result := true
+ # else begin
+ # result := (r.left <> nil) and (r.right <> nil);
+ # if result then result := ropeInvariant(r.left);
+ # if result then result := ropeInvariant(r.right);
+ # end
+
+var gCacheTries* = 0
+var gCacheMisses* = 0
+var gCacheIntTries* = 0
+
+proc insertInCache(s: string): Rope =
+ 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
+
+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.length = a.length + b.length
+ 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 countup(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 isNil(it.data):
+ stack.add(it.right)
+ it = it.left
+ assert(it != nil)
+ assert(it.data != 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 =
+proc writeRope*(head: Rope, filename: string, useWarning = false) =
var f: File
- if open(f, filename.string, fmWrite):
- writeRope(f, head)
+ if open(f, filename, fmWrite):
+ if head != nil: writeRope(f, head)
close(f)
- result = true
else:
- result = false
+ errorHandler(rCannotOpenFile, filename, useWarning)
+proc `$`*(r: Rope): string =
+ ## converts a rope back to a string.
+ result = newString(r.len)
+ setLen(result, 0)
+ for s in leaves(r): add(result, s)
+
+proc ropeConcat*(a: varargs[Rope]): Rope =
+ # not overloaded version of concat to speed-up `rfmt` a little bit
+ for i in countup(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
+ rnl* = tnl.newRope
+ softRnl* = tnl.newRope
+
+proc `%`*(frmt: FormatStr, args: openArray[Rope]): Rope =
var i = 0
- result = newRopeAppender()
+ var length = len(frmt)
+ result = nil
var num = 0
- while i < frmt.len:
+ while i < length:
if frmt[i] == '$':
inc(i) # skip '$'
case frmt[i]
of '$':
- result.add("$")
+ add(result, "$")
inc(i)
of '#':
inc(i)
- result.add(args[num])
+ add(result, args[num])
inc(num)
of '0'..'9':
var j = 0
while true:
j = j * 10 + ord(frmt[i]) - ord('0')
inc(i)
- if i >= frmt.len or frmt[i] notin {'0'..'9'}: break
+ if frmt[i] notin {'0'..'9'}: break
num = j
if j > high(args) + 1:
- doAssert false, "invalid format string: " & frmt
+ errorHandler(rInvalidFormatStr, $(j))
else:
- result.add(args[j-1])
+ add(result, args[j-1])
of '{':
inc(i)
var j = 0
@@ -87,60 +264,65 @@ proc runtimeFormat*(frmt: FormatStr, args: openArray[Rope]): Rope =
inc(i)
num = j
if frmt[i] == '}': inc(i)
- else:
- doAssert false, "invalid format string: " & frmt
+ else: errorHandler(rInvalidFormatStr, $(frmt[i]))
if j > high(args) + 1:
- doAssert false, "invalid format string: " & frmt
+ errorHandler(rInvalidFormatStr, $(j))
else:
- result.add(args[j-1])
+ add(result, args[j-1])
of 'n':
- result.add("\n")
+ add(result, softRnl)
inc(i)
of 'N':
- result.add("\n")
+ add(result, rnl)
inc(i)
else:
- doAssert false, "invalid format string: " & frmt
+ errorHandler(rInvalidFormatStr, $(frmt[i]))
var start = i
- while i < frmt.len:
+ while i < length:
if frmt[i] != '$': inc(i)
else: break
if i - 1 >= start:
- result.add(substr(frmt, start, i - 1))
+ add(result, substr(frmt, start, i - 1))
+ assert(ropeInvariant(result))
-proc `%`*(frmt: static[FormatStr], args: openArray[Rope]): Rope =
- runtimeFormat(frmt, args)
-
-template addf*(c: var Rope, frmt: FormatStr, args: openArray[Rope]) =
+proc addf*(c: var Rope, frmt: FormatStr, args: openArray[Rope]) =
## shortcut for ``add(c, frmt % args)``.
- c.add(frmt % args)
+ add(c, 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]
bpos = buf.len
blen = buf.len
- btotal = 0
- rtotal = 0
- when true:
+ for s in leaves(r):
var spos = 0
- rtotal += s.len
- while spos < s.len:
+ let slen = s.len
+ while spos < slen:
if bpos == blen:
# Read more data
bpos = 0
blen = readBuffer(f, addr(buf[0]), buf.len)
- btotal += blen
if blen == 0: # no more data in file
result = false
return
- let n = min(blen - bpos, s.len - spos)
+ let n = min(blen - bpos, slen - spos)
# 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):
result = false
@@ -148,14 +330,21 @@ proc equalsFile*(s: Rope, f: File): bool =
spos += n
bpos += n
- result = readBuffer(f, addr(buf[0]), 1) == 0 and
- btotal == rtotal # check that we've read all
+ result = readBuffer(f, addr(buf[0]), 1) == 0 # check that we've read all
-proc equalsFile*(r: Rope, filename: AbsoluteFile): bool =
+proc equalsFile*(r: Rope, filename: string): bool =
## returns true if the contents of the file `f` equal `r`. If `f` does not
## exist, false is returned.
var f: File
- result = open(f, filename.string)
+ result = open(f, filename)
if result:
result = equalsFile(r, f)
close(f)
+
+proc writeRopeIfNotEqual*(r: Rope, filename: string): bool =
+ # returns true if overwritten
+ if not equalsFile(r, filename):
+ writeRope(r, filename)
+ result = true
+ else:
+ result = false
diff --git a/compiler/saturate.nim b/compiler/saturate.nim
index fe6e03c8b..065cb5128 100644
--- a/compiler/saturate.nim
+++ b/compiler/saturate.nim
@@ -15,33 +15,33 @@ proc `|+|`*(a, b: BiggestInt): BiggestInt =
if (result xor a) >= 0'i64 or (result xor b) >= 0'i64:
return result
if a < 0 or b < 0:
- result = low(typeof(result))
+ result = low(result)
else:
- result = high(typeof(result))
+ result = high(result)
proc `|-|`*(a, b: BiggestInt): BiggestInt =
result = a -% b
if (result xor a) >= 0'i64 or (result xor not b) >= 0'i64:
return result
if b > 0:
- result = low(typeof(result))
+ result = low(result)
else:
- result = high(typeof(result))
+ result = high(result)
proc `|abs|`*(a: BiggestInt): BiggestInt =
- if a != low(typeof(a)):
+ if a != low(a):
if a >= 0: result = a
else: result = -a
else:
- result = low(typeof(a))
+ result = low(a)
proc `|div|`*(a, b: BiggestInt): BiggestInt =
- # (0..5) div (0..4) == (0..5) div (1..4) == (0 div 4)..(5 div 1)
+ # (0..5) div (0..4) == (0..5) div (1..4) == (0 div 4) .. (5 div 1)
if b == 0'i64:
# make the same as ``div 1``:
result = a
- elif a == low(typeof(a)) and b == -1'i64:
- result = high(typeof(result))
+ elif a == low(a) and b == -1'i64:
+ result = high(result)
else:
result = a div b
@@ -74,6 +74,6 @@ proc `|*|`*(a, b: BiggestInt): BiggestInt =
return result
if floatProd >= 0.0:
- result = high(typeof(result))
+ result = high(result)
else:
- result = low(typeof(result))
+ result = low(result)
diff --git a/compiler/scriptconfig.nim b/compiler/scriptconfig.nim
index 27ea94aae..d04fd5231 100644
--- a/compiler/scriptconfig.nim
+++ b/compiler/scriptconfig.nim
@@ -11,16 +11,12 @@
## language.
import
- ast, modules, idents, condsyms,
- options, llstream, vm, vmdef, commands,
- os, times, osproc, wordrecg, strtabs, modulegraphs,
- pathutils, pipelines
-
-when defined(nimPreviewSlimSystem):
- import std/syncio
+ ast, modules, passes, passaux, condsyms,
+ options, nimconf, lists, sem, semdata, llstream, vm, vmdef, commands, msgs,
+ os, times, osproc
# we support 'cmpIgnoreStyle' natively for efficiency:
-from strutils import cmpIgnoreStyle, contains
+from strutils import cmpIgnoreStyle
proc listDirs(a: VmArgs, filter: set[PathComponent]) =
let dir = getString(a, 0)
@@ -29,13 +25,11 @@ proc listDirs(a: VmArgs, filter: set[PathComponent]) =
if kind in filter: result.add path
setResult(a, result)
-proc setupVM*(module: PSym; cache: IdentCache; scriptName: string;
- graph: ModuleGraph; idgen: IdGenerator): PEvalContext =
+proc setupVM*(module: PSym; scriptName: string): PEvalContext =
# For Nimble we need to export 'setupVM'.
- result = newCtx(module, cache, graph, idgen)
+ result = newCtx(module)
result.mode = emRepl
registerAdditionalOps(result)
- let conf = graph.config
# captured vars:
var errorMsg: string
@@ -46,104 +40,61 @@ proc setupVM*(module: PSym; cache: IdentCache; scriptName: string;
proc (a: VmArgs) =
body
- template cbexc(name, exc, body) {.dirty.} =
+ template cbos(name, body) {.dirty.} =
result.registerCallback "stdlib.system." & astToStr(name),
proc (a: VmArgs) =
- errorMsg = ""
try:
body
- except exc:
+ except OSError:
errorMsg = getCurrentExceptionMsg()
- template cbos(name, body) {.dirty.} =
- cbexc(name, OSError, body)
-
# Idea: Treat link to file as a file, but ignore link to directory to prevent
# endless recursions out of the box.
- cbos listFilesImpl:
+ cbos listFiles:
listDirs(a, {pcFile, pcLinkToFile})
- cbos listDirsImpl:
+ cbos listDirs:
listDirs(a, {pcDir})
cbos removeDir:
- if defined(nimsuggest) or graph.config.cmd == cmdCheck:
- discard
- else:
- os.removeDir(getString(a, 0), getBool(a, 1))
+ os.removeDir getString(a, 0)
cbos removeFile:
- if defined(nimsuggest) or graph.config.cmd == cmdCheck:
- discard
- else:
- os.removeFile getString(a, 0)
+ os.removeFile getString(a, 0)
cbos createDir:
os.createDir getString(a, 0)
-
- result.registerCallback "stdlib.system.getError",
- proc (a: VmArgs) = setResult(a, errorMsg)
-
+ cbos getOsError:
+ setResult(a, errorMsg)
cbos setCurrentDir:
os.setCurrentDir getString(a, 0)
cbos getCurrentDir:
setResult(a, os.getCurrentDir())
cbos moveFile:
- if defined(nimsuggest) or graph.config.cmd == cmdCheck:
- discard
- else:
- os.moveFile(getString(a, 0), getString(a, 1))
- cbos moveDir:
- if defined(nimsuggest) or graph.config.cmd == cmdCheck:
- discard
- else:
- os.moveDir(getString(a, 0), getString(a, 1))
+ os.moveFile(getString(a, 0), getString(a, 1))
cbos copyFile:
- if defined(nimsuggest) or graph.config.cmd == cmdCheck:
- discard
- else:
- os.copyFile(getString(a, 0), getString(a, 1))
- cbos copyDir:
- if defined(nimsuggest) or graph.config.cmd == cmdCheck:
- discard
- else:
- os.copyDir(getString(a, 0), getString(a, 1))
+ os.copyFile(getString(a, 0), getString(a, 1))
cbos getLastModificationTime:
- setResult(a, getLastModificationTime(getString(a, 0)).toUnix)
- cbos findExe:
- setResult(a, os.findExe(getString(a, 0)))
+ setResult(a, toSeconds(getLastModificationTime(getString(a, 0))))
cbos rawExec:
- if defined(nimsuggest) or graph.config.cmd == cmdCheck:
- discard
- else:
- setResult(a, osproc.execCmd getString(a, 0))
+ setResult(a, osproc.execCmd getString(a, 0))
cbconf getEnv:
- setResult(a, os.getEnv(a.getString 0, a.getString 1))
+ setResult(a, os.getEnv(a.getString 0))
cbconf existsEnv:
setResult(a, os.existsEnv(a.getString 0))
- cbconf putEnv:
- os.putEnv(a.getString 0, a.getString 1)
- cbconf delEnv:
- os.delEnv(a.getString 0)
cbconf dirExists:
setResult(a, os.dirExists(a.getString 0))
cbconf fileExists:
setResult(a, os.fileExists(a.getString 0))
- cbconf projectName:
- setResult(a, conf.projectName)
- cbconf projectDir:
- setResult(a, conf.projectPath.string)
- cbconf projectPath:
- setResult(a, conf.projectFull.string)
cbconf thisDir:
setResult(a, vthisDir)
cbconf put:
- options.setConfigVar(conf, getString(a, 0), getString(a, 1))
+ options.setConfigVar(getString(a, 0), getString(a, 1))
cbconf get:
- setResult(a, options.getConfigVar(conf, a.getString 0))
+ setResult(a, options.getConfigVar(a.getString 0))
cbconf exists:
- setResult(a, options.existsConfigVar(conf, a.getString 0))
+ setResult(a, options.existsConfigVar(a.getString 0))
cbconf nimcacheDir:
- setResult(a, options.getNimcacheDir(conf).string)
+ setResult(a, options.getNimcacheDir())
cbconf paramStr:
setResult(a, os.paramStr(int a.getInt 0))
cbconf paramCount:
@@ -153,88 +104,43 @@ proc setupVM*(module: PSym; cache: IdentCache; scriptName: string;
cbconf cmpIgnoreCase:
setResult(a, strutils.cmpIgnoreCase(a.getString 0, a.getString 1))
cbconf setCommand:
- conf.setCommandEarly(a.getString 0)
+ options.command = a.getString 0
let arg = a.getString 1
- incl(conf.globalOptions, optWasNimscript)
- if arg.len > 0: setFromProjectName(conf, arg)
+ if arg.len > 0:
+ gProjectName = arg
+ try:
+ gProjectFull = canonicalizePath(gProjectPath / gProjectName)
+ except OSError:
+ gProjectFull = gProjectName
cbconf getCommand:
- setResult(a, conf.command)
+ setResult(a, options.command)
cbconf switch:
- processSwitch(a.getString 0, a.getString 1, passPP, module.info, conf)
- cbconf hintImpl:
- processSpecificNote(a.getString 0, wHint, passPP, module.info,
- a.getString 1, conf)
- cbconf warningImpl:
- processSpecificNote(a.getString 0, wWarning, passPP, module.info,
- a.getString 1, conf)
- cbconf patchFile:
- let key = a.getString(0) & "_" & a.getString(1)
- var val = a.getString(2).addFileExt(NimExt)
- if {'$', '~'} in val:
- val = pathSubs(conf, val, vthisDir)
- elif not isAbsolute(val):
- val = vthisDir / val
- conf.moduleOverrides[key] = val
- cbconf selfExe:
- setResult(a, os.getAppFilename())
- cbconf cppDefine:
- options.cppDefine(conf, a.getString(0))
- cbexc stdinReadLine, EOFError:
- if defined(nimsuggest) or graph.config.cmd == cmdCheck:
- setResult(a, "")
- else:
- setResult(a, stdin.readLine())
- cbexc stdinReadAll, EOFError:
- if defined(nimsuggest) or graph.config.cmd == cmdCheck:
- setResult(a, "")
- else:
- setResult(a, stdin.readAll())
+ processSwitch(a.getString 0, a.getString 1, passPP, unknownLineInfo())
-proc runNimScript*(cache: IdentCache; scriptName: AbsoluteFile;
- idgen: IdGenerator;
- freshDefines=true; conf: ConfigRef, stream: PLLStream) =
- let oldSymbolFiles = conf.symbolFiles
- conf.symbolFiles = disabledSf
- let graph = newModuleGraph(cache, conf)
- connectPipelineCallbacks(graph)
- if freshDefines: initDefines(conf.symbols)
+proc runNimScript*(scriptName: string; freshDefines=true) =
+ passes.gIncludeFile = includeModule
+ passes.gImportModule = importModule
+ if freshDefines: initDefines()
- defineSymbol(conf.symbols, "nimscript")
- defineSymbol(conf.symbols, "nimconfig")
+ defineSymbol("nimscript")
+ defineSymbol("nimconfig")
+ registerPass(semPass)
+ registerPass(evalPass)
- conf.searchPaths.add(conf.libpath)
+ appendStr(searchPaths, options.libpath)
- let oldGlobalOptions = conf.globalOptions
- let oldSelectedGC = conf.selectedGC
- undefSymbol(conf.symbols, "nimv2")
- conf.globalOptions.excl {optTinyRtti, optOwnedRefs, optSeqDestructors}
- conf.selectedGC = gcUnselected
-
- var m = graph.makeModule(scriptName)
+ var m = makeModule(scriptName)
incl(m.flags, sfMainModule)
- var vm = setupVM(m, cache, scriptName.string, graph, idgen)
- graph.vm = vm
+ vm.globalCtx = setupVM(m, scriptName)
- graph.setPipeLinePass(EvalPass)
- graph.compilePipelineSystemModule()
- discard graph.processPipelineModule(m, vm.idgen, stream)
-
- # watch out, "newruntime" can be set within NimScript itself and then we need
- # to remember this:
- if conf.selectedGC == gcUnselected:
- conf.selectedGC = oldSelectedGC
- if optOwnedRefs in oldGlobalOptions:
- conf.globalOptions.incl {optTinyRtti, optOwnedRefs, optSeqDestructors}
- defineSymbol(conf.symbols, "nimv2")
- if conf.selectedGC in {gcArc, gcOrc}:
- conf.globalOptions.incl {optTinyRtti, optSeqDestructors}
- defineSymbol(conf.symbols, "nimv2")
+ compileSystemModule()
+ processModule(m, llStreamOpen(scriptName, fmRead), nil)
# ensure we load 'system.nim' again for the real non-config stuff!
- resetSystemArtifacts(graph)
+ resetAllModulesHard()
+ vm.globalCtx = nil
# do not remove the defined symbols
#initDefines()
- undefSymbol(conf.symbols, "nimscript")
- undefSymbol(conf.symbols, "nimconfig")
- conf.symbolFiles = oldSymbolFiles
+ undefSymbol("nimscript")
+ undefSymbol("nimconfig")
diff --git a/compiler/sem.nim b/compiler/sem.nim
index 48a7d56c8..a8ec2229f 100644
--- a/compiler/sem.nim
+++ b/compiler/sem.nim
@@ -10,61 +10,47 @@
# This module implements the semantic checking pass.
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,
- 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
+ ast, strutils, hashes, lists, options, lexer, astalgo, trees, treetab,
+ wordrecg, ropes, msgs, os, condsyms, idents, renderer, types, platform, math,
+ magicsys, parser, nversion, nimsets, semfold, importer,
+ procfind, lookups, rodread, pragmas, passes, semdata, semtypinst, sigmatch,
+ intsets, transf, vmdef, vm, idgen, aliases, cgmeth, lambdalifting,
+ evaltempl, patterns, parampatterns, sempass2, nimfix.pretty, semmacrosanity,
+ semparallel, lowerings, pluginsupport, plugins.active
when defined(nimfix):
- import nimfix/prettybase
-
-when not defined(leanCompiler):
- import spawn
-
-when defined(nimPreviewSlimSystem):
- import std/formatfloat
+ import nimfix.prettybase
# 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 {.procvar.}
+proc semExprWithType(c: PContext, n: PNode, flags: TExprFlags = {}): PNode {.
+ procvar.}
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 fitNode(c: PContext, formal: PType, arg: PNode): PNode
+proc changeType(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)
+proc semStmt(c: PContext, n: PNode): PNode
proc semParamList(c: PContext, n, genericParams: PNode, s: PSym)
proc addParams(c: PContext, n: PNode, kind: TSymKind)
proc maybeAddResult(c: PContext, s: PSym, n: PNode)
+proc instGenericContainer(c: PContext, n: PNode, header: PType): PType
proc tryExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode
proc activate(c: PContext, n: PNode)
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 semStaticType(c: PContext, childNode: PNode, prev: PType): PType
-proc semTypeOf(c: PContext; n: PNode): PNode
-proc computeRequiresInit(c: PContext, t: PType): bool
-proc defaultConstructionError(c: PContext, t: PType, info: TLineInfo)
-proc hasUnresolvedArgs(c: PContext, n: PNode): bool
-proc isArrayConstr(n: PNode): bool {.inline.} =
- result = n.kind == nkBracket and
- n.typ.skipTypes(abstractInst).kind == tyArray
-template semIdeForTemplateOrGenericCheck(conf, n, requiresCheck) =
+proc indexTypesMatch(c: PContext, f, a: PType, arg: PNode): PNode
+
+template semIdeForTemplateOrGenericCheck(n, requiresCheck) =
# we check quickly if the node is where the cursor is
when defined(nimsuggest):
- if n.info.fileIndex == conf.m.trackPos.fileIndex and n.info.line == conf.m.trackPos.line:
+ if n.info.fileIndex == gTrackPos.fileIndex and n.info.line == gTrackPos.line:
requiresCheck = true
template semIdeForTemplateOrGeneric(c: PContext; n: PNode;
@@ -73,103 +59,82 @@ template semIdeForTemplateOrGeneric(c: PContext; n: PNode;
# templates perform some quick check whether the cursor is actually in
# the generic or template.
when defined(nimsuggest):
- if c.config.cmd == cmdIdeTools and requiresCheck:
+ assert gCmd == cmdIdeTools
+ if requiresCheck:
#if optIdeDebug in gGlobalOptions:
# echo "passing to safeSemExpr: ", renderTree(n)
discard safeSemExpr(c, n)
-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}:
- changeType(c, x, formal, check=true)
- result = arg
- result = skipHiddenSubConv(result, c.graph, c.idgen)
-
-
-proc fitNode(c: PContext, formal: PType, arg: PNode; info: TLineInfo): PNode =
+proc fitNode(c: PContext, formal: PType, arg: PNode): PNode =
if arg.typ.isNil:
- localError(c.config, arg.info, "expression has no type: " &
+ localError(arg.info, errExprXHasNoType,
renderTree(arg, {renderNoComments}))
# error correction:
- result = copyTree(arg)
+ result = copyNode(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:
- typeMismatch(c.config, info, formal, arg.typ, arg)
+ typeMismatch(arg, formal, arg.typ)
# error correction:
result = copyTree(arg)
result.typ = formal
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
+ let x = result.skipConv
+ if x.kind == nkPar and formal.kind != tyExpr:
+ changeType(x, formal, check=true)
+ else:
+ result = skipHiddenSubConv(result)
+ #result.typ = takeType(formal, arg.typ)
+ #echo arg.info, " picked ", result.typ.typeToString
proc inferWithMetatype(c: PContext, formal: PType,
arg: PNode, coerceDistincts = false): PNode
-template commonTypeBegin*(): PType = PType(kind: tyUntyped)
+var commonTypeBegin = PType(kind: tyExpr)
-proc commonType*(c: PContext; x, y: PType): PType =
+proc commonType*(x, y: PType): PType =
# new type relation that is used for array constructors,
# if expressions, etc.:
if x == nil: return x
if y == nil: return y
- var a = skipTypes(x, {tyGenericInst, tyAlias, tySink})
- var b = skipTypes(y, {tyGenericInst, tyAlias, tySink})
+ var a = skipTypes(x, {tyGenericInst})
+ var b = skipTypes(y, {tyGenericInst})
result = x
- if a.kind in {tyUntyped, tyNil}: result = y
- elif b.kind in {tyUntyped, tyNil}: result = x
- elif a.kind == tyTyped: result = a
- elif b.kind == tyTyped: result = b
+ if a.kind in {tyExpr, tyNil}: result = y
+ elif b.kind in {tyExpr, tyNil}: result = x
+ elif a.kind == tyStmt: result = a
+ elif b.kind == tyStmt: result = b
elif a.kind == tyTypeDesc:
# turn any concrete typedesc into the abstract typedesc type
- if a.len == 0: result = a
+ if a.sons == nil: result = a
else:
- result = newType(tyTypeDesc, nextTypeId(c.idgen), a.owner)
- rawAddSon(result, newType(tyNone, nextTypeId(c.idgen), a.owner))
- elif b.kind in {tyArray, tySet, tySequence} and
+ result = newType(tyTypeDesc, a.owner)
+ rawAddSon(result, newType(tyNone, a.owner))
+ elif b.kind in {tyArray, tyArrayConstr, tySet, tySequence} and
a.kind == b.kind:
# check for seq[empty] vs. seq[int]
- let idx = ord(b.kind == tyArray)
- if a[idx].kind == tyEmpty: return y
+ let idx = ord(b.kind in {tyArray, tyArrayConstr})
+ if a.sons[idx].kind == tyEmpty: return y
elif a.kind == tyTuple and b.kind == tyTuple and a.len == b.len:
var nt: PType
- for i in 0..
- # ill-formed AST, no need for additional tyRef/tyPtr
- if k != tyNone and x.kind != tyGenericInst:
+ if k != tyNone:
let r = result
- result = newType(k, nextTypeId(c.idgen), r.owner)
- result.addSonSkipIntLit(r, c.idgen)
-
-proc endsInNoReturn(n: PNode): bool =
- # check if expr ends in raise exception or call of noreturn proc
- var it = n
- while it.kind in {nkStmtList, nkStmtListExpr} and it.len > 0:
- it = it.lastSon
- result = it.kind in nkLastBlockStmts or
- it.kind in nkCallKinds and it[0].kind == nkSym and sfNoReturn in it[0].sym.flags
-
-proc commonType*(c: PContext; x: PType, y: PNode): PType =
- # ignore exception raising branches in case/if expressions
- if endsInNoReturn(y): return x
- commonType(c, x, y.typ)
+ result = newType(k, r.owner)
+ result.addSonSkipIntLit(r)
proc newSymS(kind: TSymKind, n: PNode, c: PContext): PSym =
- result = newSym(kind, considerQuotedIdent(c, n), nextSymId c.idgen, getCurrOwner(c), n.info)
- when defined(nimsuggest):
- suggestDecl(c, n, result)
+ result = newSym(kind, considerQuotedIdent(n), getCurrOwner(), n.info)
proc newSymG*(kind: TSymKind, n: PNode, c: PContext): PSym =
+ proc `$`(kind: TSymKind): string = substr(system.`$`(kind), 2).toLower
+
# like newSymS, but considers gensym'ed symbols
if n.kind == nkSym:
# and sfGenSym in n.sym.flags:
result = n.sym
- if result.kind notin {kind, skTemp}:
- localError(c.config, n.info, "cannot use symbol of kind '$1' as a '$2'" %
- [result.kind.toHumanStr, kind.toHumanStr])
- when false:
- if sfGenSym in result.flags and result.kind notin {skTemplate, skMacro, skParam}:
- # declarative context, so produce a fresh gensym:
- result = copySym(result)
- result.ast = n.sym.ast
- put(c.p, n.sym, result)
+ if result.kind != kind:
+ localError(n.info, "cannot use symbol of kind '" &
+ $result.kind & "' as a '" & $kind & "'")
# when there is a nested proc inside a template, semtmpl
# will assign a wrong owner during the first pass over the
# template; we must fix it here: see #909
- result.owner = getCurrOwner(c)
+ result.owner = getCurrOwner()
else:
- result = newSym(kind, considerQuotedIdent(c, n), nextSymId c.idgen, getCurrOwner(c), n.info)
+ result = newSym(kind, considerQuotedIdent(n), getCurrOwner(), n.info)
#if kind in {skForVar, skLet, skVar} and result.owner.kind == skModule:
# incl(result.flags, sfGlobal)
- when defined(nimsuggest):
- suggestDecl(c, n, result)
proc semIdentVis(c: PContext, kind: TSymKind, n: PNode,
allowed: TSymFlags): PSym
# identifier with visibility
proc semIdentWithPragma(c: PContext, kind: TSymKind, n: PNode,
allowed: TSymFlags): PSym
+proc semStmtScope(c: PContext, n: PNode): PNode
-proc typeAllowedCheck(c: PContext; info: TLineInfo; typ: PType; kind: TSymKind;
- flags: TTypeAllowedFlags = {}) =
- let t = typeAllowed(typ, kind, c, flags)
+proc typeAllowedCheck(info: TLineInfo; typ: PType; kind: TSymKind) =
+ let t = typeAllowed(typ, kind)
if t != nil:
- var err: string
- if t == typ:
- err = "invalid type: '$1' for $2" % [typeToString(typ), toHumanStr(kind)]
- if kind in {skVar, skLet, skConst} and taIsTemplateOrMacro in flags:
- err &= ". Did you mean to call the $1 with '()'?" % [toHumanStr(typ.owner.kind)]
- else:
- err = "invalid type: '$1' in this context: '$2' for $3" % [typeToString(t),
- typeToString(typ), toHumanStr(kind)]
- localError(c.config, info, err)
+ if t == typ: localError(info, "invalid type: '" & typeToString(typ) & "'")
+ else: localError(info, "invalid type: '" & typeToString(t) &
+ "' in this context: '" & typeToString(typ) & "'")
proc paramsTypeCheck(c: PContext, typ: PType) {.inline.} =
- typeAllowedCheck(c, typ.n.info, typ, skProc)
+ typeAllowedCheck(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 isOpImpl(c: PContext, n: PNode): 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 =
+proc symFromType(t: PType, info: TLineInfo): PSym =
if t.sym != nil: return t.sym
- result = newSym(skType, getIdent(c.cache, "AnonType"), nextSymId c.idgen, t.owner, info)
+ result = newSym(skType, getIdent"AnonType", t.owner, info)
result.flags.incl sfAnon
result.typ = t
proc symNodeFromType(c: PContext, t: PType, info: TLineInfo): PNode =
- result = newSymNode(symFromType(c, t, info), info)
+ result = newSymNode(symFromType(t, info), info)
result.typ = makeTypeDesc(c, t)
when false:
@@ -304,27 +234,16 @@ when false:
result.handleIsOperator = proc (n: PNode): PNode =
result = isOpImpl(c, n)
-proc hasCycle(n: PNode): bool =
- incl n.flags, nfNone
- for i in 0.. evalTemplateLimit:
- globalError(c.config, s.info, "template instantiation too nested")
+ inc(evalTemplateCounter)
+ if evalTemplateCounter > 100:
+ globalError(s.info, errTemplateInstantiationTooNested)
c.friendModules.add(s.owner.getModule)
- result = macroResult
- resetSemFlag result
- if s.typ[0] == nil:
- result = semStmt(c, result, flags)
+
+ result = n
+ if s.typ.sons[0] == nil:
+ result = semStmt(c, result)
else:
- var retType = s.typ[0]
- if retType.kind == tyTypeDesc and tfUnresolved in retType.flags and
- retType.len == 1:
- # bug #11941: template fails(T: type X, v: auto): T
- # does not mean we expect a tyTypeDesc.
- retType = retType[0]
- case retType.kind
- of tyUntyped, tyAnything:
- # Not expecting a type here allows templates like in ``tmodulealias.in``.
- result = semExpr(c, result, flags, expectedType)
- of tyTyped:
- # More restrictive version.
- result = semExprWithType(c, result, flags, expectedType)
+ case s.typ.sons[0].kind
+ of tyExpr:
+ # BUGFIX: we cannot expect a type here, because module aliases would not
+ # work then (see the ``tmodulealias`` test)
+ # semExprWithType(c, result)
+ result = semExpr(c, result, flags)
+ of tyStmt:
+ result = semStmt(c, result)
of tyTypeDesc:
- if result.kind == nkStmtList: result.transitionSonsKind(nkStmtListType)
+ if n.kind == nkStmtList: result.kind = nkStmtListType
var typ = semTypeNode(c, result, nil)
- if typ == nil:
- localError(c.config, result.info, "expression has no type: " &
- renderTree(result, {renderNoComments}))
- result = newSymNode(errorSym(c, result))
- else:
- result.typ = makeTypeDesc(c, typ)
+ result.typ = makeTypeDesc(c, typ)
#result = symNodeFromType(c, typ, n.info)
else:
- if s.ast[genericParamsPos] != nil and retType.isMetaType:
- # The return type may depend on the Macro arguments
- # e.g. template foo(T: typedesc): seq[T]
- # We will instantiate the return type here, because
- # we now know the supplied arguments
- var paramTypes = newIdTable()
- for param, value in genericParamsInMacroCall(s, call):
- idTablePut(paramTypes, param.typ, value.typ)
-
- retType = generateTypeInstance(c, paramTypes,
- macroResult.info, retType)
-
- result = semExpr(c, result, flags, expectedType)
- result = fitNode(c, retType, result, result.info)
- #globalError(s.info, errInvalidParamKindX, typeToString(s.typ[0]))
- dec(c.config.evalTemplateCounter)
+ result = semExpr(c, result, flags)
+ result = fitNode(c, s.typ.sons[0], result)
+ #GlobalError(s.info, errInvalidParamKindX, typeToString(s.typ.sons[0]))
+ dec(evalTemplateCounter)
discard c.friendModules.pop()
-const
- errMissingGenericParamsForTemplate = "'$1' has unspecified generic parameters"
- errFloatToString = "cannot convert '$1' to '$2'"
-
proc semMacroExpr(c: PContext, n, nOrig: PNode, sym: PSym,
- flags: TExprFlags = {}; expectedType: PType = nil): PNode =
- rememberExpansion(c, nOrig.info, sym)
- pushInfoContext(c.config, nOrig.info, sym.detailedInfo)
+ flags: TExprFlags = {}): PNode =
+ pushInfoContext(nOrig.info)
- let info = getCallLineInfo(n)
- markUsed(c, info, sym)
- onUse(info, sym)
+ markUsed(n.info, sym)
+ styleCheckUse(n.info, sym)
if sym == c.p.owner:
- globalError(c.config, info, "recursive dependency: '$1'" % sym.name.s)
-
- let genericParams = sym.ast[genericParamsPos].len
- let suppliedParams = max(n.safeLen - 1, 0)
-
- if suppliedParams < genericParams:
- globalError(c.config, info, errMissingGenericParamsForTemplate % n.renderTree)
+ globalError(n.info, errRecursiveDependencyX, sym.name.s)
#if c.evalContext == nil:
# c.evalContext = c.createEvalContext(emStatic)
- result = evalMacroCall(c.module, c.idgen, c.graph, c.templInstCounter, n, nOrig, sym)
+ result = evalMacroCall(c.module, n, nOrig, sym)
if efNoSemCheck notin flags:
- result = semAfterMacroCall(c, n, result, sym, flags, expectedType)
- if c.config.macrosToExpand.hasKey(sym.name.s):
- message(c.config, nOrig.info, hintExpandMacro, renderTree(result))
- result = wrapInComesFrom(nOrig.info, sym, result)
- popInfoContext(c.config)
+ result = semAfterMacroCall(c, result, sym, flags)
+ popInfoContext()
proc forceBool(c: PContext, n: PNode): PNode =
- result = fitNode(c, getSysType(c.graph, n.info, tyBool), n, n.info)
+ result = fitNode(c, getSysType(tyBool), n)
if result == nil: result = n
proc semConstBoolExpr(c: PContext, n: PNode): PNode =
- result = forceBool(c, semConstExpr(c, n, getSysType(c.graph, n.info, tyBool)))
- if result.kind != nkIntLit:
- localError(c.config, n.info, errConstExprExpected)
+ let nn = semExprWithType(c, n)
+ result = fitNode(c, getSysType(tyBool), nn)
+ if result == nil:
+ localError(n.info, errConstExprExpected)
+ return nn
+ result = getConstExpr(c.module, result)
+ if result == nil:
+ localError(n.info, errConstExprExpected)
+ result = nn
proc semGenericStmt(c: PContext, n: PNode): PNode
-proc semConceptBody(c: PContext, n: PNode): PNode
-include semtypes
-
-proc setGenericParamsMisc(c: PContext; n: PNode) =
- ## used by call defs (procs, templates, macros, ...) to analyse their generic
- ## params, and store the originals in miscPos for better error reporting.
- let orig = n[genericParamsPos]
-
- doAssert orig.kind in {nkEmpty, nkGenericParams}
-
- if n[genericParamsPos].kind == nkEmpty:
- n[genericParamsPos] = newNodeI(nkGenericParams, n.info)
- else:
- # we keep the original params around for better error messages, see
- # issue https://github.com/nim-lang/Nim/issues/1713
- n[genericParamsPos] = semGenericParamList(c, orig)
-
- if n[miscPos].kind == nkEmpty:
- n[miscPos] = newTree(nkBracket, c.graph.emptyNode, orig)
- else:
- n[miscPos][1] = orig
-
-proc caseBranchMatchesExpr(branch, matched: PNode): bool =
- for i in 0 ..< branch.len-1:
- if branch[i].kind == nkRange:
- if overlap(branch[i], matched): return true
- elif exprStructuralEquivalent(branch[i], matched):
- return true
-
-proc pickCaseBranchIndex(caseExpr, matched: PNode): int =
- let endsWithElse = caseExpr[^1].kind == nkElse
- for i in 1.. 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.. 0:
- # a generic has been added to `a`:
- if result.kind != nkEmpty: a.add result
- result = a
+ result = semStmt(c, result)
+ # BUGFIX: process newly generated generics here, not at the end!
+ if c.lastGenericIdx < c.generics.len:
+ var a = newNodeI(nkStmtList, n.info)
+ addCodeForGenerics(c, a)
+ if sonsLen(a) > 0:
+ # a generic has been added to `a`:
+ if result.kind != nkEmpty: addSon(a, result)
+ result = a
result = hloStmt(c, result)
- if c.config.cmd == cmdInteractive and not isEmptyType(result.typ):
+ if gCmd == cmdInteractive and not isEmptyType(result.typ):
result = buildEchoStmt(c, result)
- if c.config.cmd == cmdIdeTools:
+ if gCmd == cmdIdeTools:
appendToModule(c.module, result)
- trackStmt(c, c.module, result, isTopLevel = true)
+ result = transformStmt(c.module, result)
proc recoverContext(c: PContext) =
# clean up in case of a semantic error: We clean up the stacks, etc. This is
# faster than wrapping every stack operation in a 'try finally' block and
# requires far less code.
c.currentScope = c.topLevelScope
- while getCurrOwner(c).kind != skModule: popOwner(c)
+ while getCurrOwner().kind != skModule: popOwner()
while c.p != nil and c.p.owner.kind != skModule: c.p = c.p.next
-proc semWithPContext*(c: PContext, n: PNode): PNode =
+proc myProcess(context: PPassContext, n: PNode): PNode =
+ var c = PContext(context)
# no need for an expensive 'try' if we stop after the first error anyway:
- if c.config.errorMax <= 1:
+ if msgs.gErrorMax <= 1:
result = semStmtAndGenerateGenerics(c, n)
else:
- let oldContextLen = msgs.getInfoContextLen(c.config)
+ let oldContextLen = msgs.getInfoContextLen()
let oldInGenericInst = c.inGenericInst
try:
result = semStmtAndGenerateGenerics(c, n)
except ERecoverableError, ESuggestDone:
recoverContext(c)
c.inGenericInst = oldInGenericInst
- msgs.setInfoContextLen(c.config, oldContextLen)
- if getCurrentException() of ESuggestDone:
- c.suggestionsMade = true
- result = nil
- else:
- result = newNodeI(nkEmpty, n.info)
- #if c.config.cmd == cmdIdeTools: findSuggest(c, n)
- storeRodNode(c, result)
+ msgs.setInfoContextLen(oldContextLen)
+ if getCurrentException() of ESuggestDone: result = nil
+ else: result = ast.emptyNode
+ #if gCmd == cmdIdeTools: findSuggest(c, n)
-
-proc reportUnusedModules(c: PContext) =
- for i in 0..high(c.unusedImports):
- if sfUsed notin c.unusedImports[i][0].flags:
- message(c.config, c.unusedImports[i][1], warnUnusedImportX, c.unusedImports[i][0].name.s)
-
-proc closePContext*(graph: ModuleGraph; c: PContext, n: PNode): PNode =
- if c.config.cmd == cmdIdeTools and not c.suggestionsMade:
- suggestSentinel(c)
+proc myClose(context: PPassContext, n: PNode): PNode =
+ var c = PContext(context)
closeScope(c) # close module's scope
rawCloseScope(c) # imported symbols; don't check for unused ones!
- reportUnusedModules(c)
result = newNode(nkStmtList)
if n != nil:
- internalError(c.config, n.info, "n is not nil") #result := n;
+ internalError(n.info, "n is not nil") #result := n;
addCodeForGenerics(c, result)
if c.module.ast != nil:
result.add(c.module.ast)
- popOwner(c)
+ popOwner()
popProcCon(c)
- sealRodFile(c)
+
+const semPass* = makePass(myOpen, myOpenCached, myProcess, myClose)
diff --git a/compiler/semasgn.nim b/compiler/semasgn.nim
new file mode 100644
index 000000000..a1e209263
--- /dev/null
+++ b/compiler/semasgn.nim
@@ -0,0 +1,284 @@
+#
+#
+# The Nim Compiler
+# (c) Copyright 2015 Andreas Rumpf
+#
+# See the file "copying.txt", included in this
+# distribution, for details about the copyright.
+#
+
+## This module implements lifting for assignments. Later versions of this code
+## will be able to also lift ``=deepCopy`` and ``=destroy``.
+
+# included from sem.nim
+
+type
+ TLiftCtx = object
+ c: PContext
+ info: TLineInfo # for construction
+ kind: TTypeAttachedOp
+ fn: PSym
+ asgnForType: PType
+ recurse: bool
+
+proc liftBodyAux(c: var TLiftCtx; t: PType; body, x, y: PNode)
+proc liftBody(c: PContext; typ: PType; info: TLineInfo): PSym
+
+proc at(a, i: PNode, elemType: PType): PNode =
+ result = newNodeI(nkBracketExpr, a.info, 2)
+ result.sons[0] = a
+ result.sons[1] = i
+ result.typ = elemType
+
+proc liftBodyTup(c: var TLiftCtx; t: PType; body, x, y: PNode) =
+ for i in 0 .. ]