Merge pull request #1 from nim-lang/devel
Getting the latest from nim-lang
This commit is contained in:
commit
a6de0274ee
285 changed files with 10804 additions and 6931 deletions
24
.github/ISSUE_TEMPLATE/bug_report.md
vendored
24
.github/ISSUE_TEMPLATE/bug_report.md
vendored
|
|
@ -1,37 +1,32 @@
|
||||||
---
|
---
|
||||||
name: Bug report
|
name: Bug report
|
||||||
about: You found an unexpected behaviour? Use this template.
|
about: Have you found an unexpected behavior? Use this template.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
<!-- Think about the title, twice. -->
|
<!-- Think about the title, twice. -->
|
||||||
|
|
||||||
<!-- Summarize the Problem here, keep it simple. -->
|
<!-- Summarize the problem here, keep it short and simple. -->
|
||||||
<!-- e.g. `echo` outputs the wrong string. -->
|
Function `echo` outputs the wrong string.
|
||||||
|
|
||||||
|
|
||||||
### Example
|
### Example
|
||||||
<!-- This should be a source code block.
|
<!-- Paste your example in the code-block below. -->
|
||||||
```nim
|
```nim
|
||||||
echo "Hello World!"
|
echo "Hello World!"
|
||||||
```
|
```
|
||||||
-->
|
|
||||||
|
|
||||||
|
|
||||||
### Current Output
|
### Current Output
|
||||||
<!--
|
|
||||||
```
|
```
|
||||||
Hola mundo!
|
Hola mundo!
|
||||||
```
|
```
|
||||||
-->
|
|
||||||
|
|
||||||
|
|
||||||
### Expected Output
|
### Expected Output
|
||||||
<!--
|
<!-- What should be the correct output? -->
|
||||||
```
|
```
|
||||||
Hello World!
|
Hello World!
|
||||||
```
|
```
|
||||||
-->
|
|
||||||
|
|
||||||
|
|
||||||
### Possible Solution
|
### Possible Solution
|
||||||
|
|
@ -40,6 +35,11 @@ Hello World!
|
||||||
|
|
||||||
### Additional Information
|
### Additional Information
|
||||||
<!--- For Example:
|
<!--- For Example:
|
||||||
A link to a project where the issue is relevant.
|
* Your Nim version (output of `nim -v`).
|
||||||
A link to a related issue or discussion.
|
* Was it working in the previous Nim releases?
|
||||||
|
* A link to a related issue or discussion.
|
||||||
-->
|
-->
|
||||||
|
```
|
||||||
|
$ nim -v
|
||||||
|
Nim Compiler Version 0.1.2
|
||||||
|
```
|
||||||
|
|
|
||||||
15
.github/ISSUE_TEMPLATE/feature_request.md
vendored
15
.github/ISSUE_TEMPLATE/feature_request.md
vendored
|
|
@ -1,9 +1,18 @@
|
||||||
---
|
---
|
||||||
name: Feature request
|
name: Feature request
|
||||||
about: You want to suggest a new feature? Use this template.
|
about: Do you want to suggest a new feature? Use this template.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
<!---
|
||||||
|
Notice there is now 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 template,
|
||||||
|
but bear in mind that adding new features to the language is currently a low priority.
|
||||||
|
-->
|
||||||
|
|
||||||
|
|
||||||
### Summary
|
### Summary
|
||||||
<!--- Short summary of your proposed feature -->
|
<!--- Short summary of your proposed feature -->
|
||||||
|
|
||||||
|
|
@ -18,6 +27,6 @@ about: You want to suggest a new feature? Use this template.
|
||||||
|
|
||||||
### Additional Information
|
### Additional Information
|
||||||
<!--- For Example:
|
<!--- For Example:
|
||||||
A link to a project where the issue is relevant.
|
* A link to a project where the issue is relevant.
|
||||||
A link to a related issue or discussion.
|
* A link to a related issue or discussion.
|
||||||
-->
|
-->
|
||||||
|
|
|
||||||
6
.gitignore
vendored
6
.gitignore
vendored
|
|
@ -32,7 +32,7 @@ doc/*.html
|
||||||
doc/*.pdf
|
doc/*.pdf
|
||||||
doc/*.idx
|
doc/*.idx
|
||||||
/web/upload
|
/web/upload
|
||||||
build/*
|
/build/*
|
||||||
bin/*
|
bin/*
|
||||||
|
|
||||||
# iOS specific wildcards.
|
# iOS specific wildcards.
|
||||||
|
|
@ -71,3 +71,7 @@ test.txt
|
||||||
tweeter.db
|
tweeter.db
|
||||||
tweeter_test.db
|
tweeter_test.db
|
||||||
megatest.nim
|
megatest.nim
|
||||||
|
|
||||||
|
/outputExpected.txt
|
||||||
|
/outputGotten.txt
|
||||||
|
|
||||||
|
|
|
||||||
26
.travis.yml
26
.travis.yml
|
|
@ -14,8 +14,9 @@ matrix:
|
||||||
- os: osx
|
- os: osx
|
||||||
env: NIM_COMPILE_TO_CPP=true
|
env: NIM_COMPILE_TO_CPP=true
|
||||||
|
|
||||||
allow_failures:
|
# To allow failures for a failing configuration, use something like:
|
||||||
- env: NIM_COMPILE_TO_CPP=true
|
# allow_failures:
|
||||||
|
# - env: NIM_COMPILE_TO_CPP=true
|
||||||
# - os: osx
|
# - os: osx
|
||||||
|
|
||||||
addons:
|
addons:
|
||||||
|
|
@ -40,26 +41,11 @@ before_script:
|
||||||
- sh build.sh
|
- sh build.sh
|
||||||
- cd ..
|
- cd ..
|
||||||
- export PATH=$(pwd)/bin${PATH:+:$PATH}
|
- export PATH=$(pwd)/bin${PATH:+:$PATH}
|
||||||
|
- echo PATH:${PATH}
|
||||||
|
|
||||||
script:
|
script:
|
||||||
- nim c koch
|
- nim c koch
|
||||||
- env NIM_COMPILE_TO_CPP=false ./koch boot
|
- ./koch runCI
|
||||||
- ./koch boot -d:release
|
|
||||||
- ./koch nimble
|
|
||||||
- nim e tests/test_nimscript.nims
|
|
||||||
#- nimble install zip -y
|
|
||||||
#- nimble install opengl
|
|
||||||
#- nimble install sdl1
|
|
||||||
#- nimble install jester@#head -y
|
|
||||||
#- nimble install niminst
|
|
||||||
- nim c -d:nimCoroutines testament/tester
|
|
||||||
- testament/tester --pedantic all -d:nimCoroutines
|
|
||||||
- nim c -o:bin/nimpretty nimpretty/nimpretty.nim
|
|
||||||
- nim c -r nimpretty/tester.nim
|
|
||||||
- ./koch docs --git.commit:devel
|
|
||||||
- ./koch csource
|
|
||||||
- ./koch nimsuggest
|
|
||||||
- nim c -r nimsuggest/tester
|
|
||||||
- nim c -r nimdoc/tester
|
|
||||||
|
|
||||||
before_deploy:
|
before_deploy:
|
||||||
# Make https://nim-lang.github.io/Nim work the same as https://nim-lang.github.io/Nim/overview.html
|
# Make https://nim-lang.github.io/Nim work the same as https://nim-lang.github.io/Nim/overview.html
|
||||||
|
|
|
||||||
18
appveyor.yml
18
appveyor.yml
|
|
@ -44,22 +44,6 @@ install:
|
||||||
|
|
||||||
build_script:
|
build_script:
|
||||||
- bin\nim c koch
|
- bin\nim c koch
|
||||||
- koch boot -d:release
|
- koch runCI
|
||||||
- koch nimble
|
|
||||||
- nim e tests/test_nimscript.nims
|
|
||||||
- nim c -o:bin/nimpretty.exe nimpretty/nimpretty.nim
|
|
||||||
- nim c -r nimpretty/tester.nim
|
|
||||||
# - nimble install zip -y
|
|
||||||
# - nimble install opengl -y
|
|
||||||
# - nimble install sdl1 -y
|
|
||||||
# - nimble install jester@#head -y
|
|
||||||
- nim c -d:nimCoroutines --os:genode -d:posix --compileOnly testament/tester
|
|
||||||
- nim c -d:nimCoroutines testament/tester
|
|
||||||
|
|
||||||
test_script:
|
|
||||||
- testament\tester --pedantic all -d:nimCoroutines
|
|
||||||
- nim c -r nimdoc\tester
|
|
||||||
# - koch csource
|
|
||||||
# - koch zip
|
|
||||||
|
|
||||||
deploy: off
|
deploy: off
|
||||||
|
|
|
||||||
|
|
@ -26,6 +26,9 @@ build_nim_csources(){
|
||||||
|
|
||||||
[ -f $nim_csources ] || echo_run build_nim_csources
|
[ -f $nim_csources ] || echo_run build_nim_csources
|
||||||
|
|
||||||
echo_run bin/nim c koch
|
# Note: if fails, may need to `cd csources && git pull`
|
||||||
|
# see D20190115T162028
|
||||||
|
echo_run bin/nim c --skipUserCfg --skipParentCfg koch
|
||||||
|
|
||||||
echo_run ./koch boot -d:release
|
echo_run ./koch boot -d:release
|
||||||
echo_run ./koch tools # Compile Nimble and other tools.
|
echo_run ./koch tools # Compile Nimble and other tools.
|
||||||
|
|
|
||||||
54
changelog.md
54
changelog.md
|
|
@ -1,4 +1,5 @@
|
||||||
## v0.20.0 - XX/XX/2018
|
## v0.20.0 - XX/XX/2019
|
||||||
|
|
||||||
|
|
||||||
### Changes affecting backwards compatibility
|
### Changes affecting backwards compatibility
|
||||||
|
|
||||||
|
|
@ -23,13 +24,19 @@
|
||||||
- The undocumented ``#? strongSpaces`` parsing mode has been removed.
|
- The undocumented ``#? strongSpaces`` parsing mode has been removed.
|
||||||
- The `not` operator is now always a unary operator, this means that code like
|
- The `not` operator is now always a unary operator, this means that code like
|
||||||
``assert not isFalse(3)`` compiles.
|
``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.
|
||||||
|
|
||||||
|
|
||||||
#### Breaking changes in the standard library
|
#### Breaking changes in the standard library
|
||||||
|
|
||||||
- `osproc.execProcess` now also takes a `workingDir` parameter.
|
- `osproc.execProcess` now also takes a `workingDir` parameter.
|
||||||
|
|
||||||
- `options.UnpackError` is no longer a ref type and inherits from `System.Defect` instead of `System.ValueError`.
|
- `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.parseBiggsetInt`, `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 a `OverflowError` and sometimes returned `0`.
|
||||||
|
|
||||||
- nre's `RegexMatch.{captureBounds,captures}[]` no longer return `Option` or
|
- nre's `RegexMatch.{captureBounds,captures}[]` no longer return `Option` or
|
||||||
`nil`/`""`, respectivly. Use the newly added `n in p.captures` method to
|
`nil`/`""`, respectivly. Use the newly added `n in p.captures` method to
|
||||||
|
|
@ -50,6 +57,18 @@
|
||||||
`IndexError` for un-named captures. This is consistant with
|
`IndexError` for un-named captures. This is consistant with
|
||||||
`RegexMatch.{captureBounds,captures}[]`.
|
`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 equallity: `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)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### Breaking changes in the compiler
|
#### Breaking changes in the compiler
|
||||||
|
|
||||||
- The compiler now implements the "generic symbol prepass" for `when` statements
|
- The compiler now implements the "generic symbol prepass" for `when` statements
|
||||||
|
|
@ -66,6 +85,9 @@ proc enumToString*(enums: openArray[enum]): string =
|
||||||
```
|
```
|
||||||
|
|
||||||
- ``discard x`` is now illegal when `x` is a function symbol.
|
- ``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
|
### Library additions
|
||||||
|
|
||||||
|
|
@ -87,11 +109,18 @@ proc enumToString*(enums: openArray[enum]): string =
|
||||||
is instantiation of generic proc symbol.
|
is instantiation of generic proc symbol.
|
||||||
|
|
||||||
- Added the parameter ``isSorted`` for the ``sequtils.deduplicate`` proc.
|
- Added the parameter ``isSorted`` for the ``sequtils.deduplicate`` proc.
|
||||||
|
|
||||||
- There is a new stdlib module `std/diff` to compute the famous "diff"
|
- There is a new stdlib module `std/diff` to compute the famous "diff"
|
||||||
of two texts by line.
|
of two texts by line.
|
||||||
|
|
||||||
- Added `os.relativePath`.
|
- Added `os.relativePath`.
|
||||||
|
|
||||||
|
- Added `parseopt.remainingArgs`.
|
||||||
|
|
||||||
|
- Added `os.getCurrentCompilerExe` (implmented as `getAppFilename` at CT),
|
||||||
|
can be used to retrieve the currently executing compiler.
|
||||||
|
|
||||||
|
|
||||||
### Library changes
|
### Library changes
|
||||||
|
|
||||||
- The string output of `macros.lispRepr` proc has been tweaked
|
- The string output of `macros.lispRepr` proc has been tweaked
|
||||||
|
|
@ -111,16 +140,23 @@ proc enumToString*(enums: openArray[enum]): string =
|
||||||
(default value: true) that can be set to `false` for better Posix
|
(default value: true) that can be set to `false` for better Posix
|
||||||
interoperability. (Bug #9619.)
|
interoperability. (Bug #9619.)
|
||||||
|
|
||||||
|
- `os.joinPath` and `os.normalizePath` handle edge cases like ``"a/b/../../.."``
|
||||||
|
differently.
|
||||||
|
|
||||||
|
- `securehash` is moved to `lib/deprecated`
|
||||||
|
|
||||||
|
|
||||||
### Language additions
|
### Language additions
|
||||||
|
|
||||||
- Vm suport for float32<->int32 and float64<->int64 casts was added.
|
- Vm support for float32<->int32 and float64<->int64 casts was added.
|
||||||
- There is a new pragma block `noSideEffect` that works like
|
- There is a new pragma block `noSideEffect` that works like
|
||||||
the `gcsafe` pragma block.
|
the `gcsafe` pragma block.
|
||||||
- added os.getCurrentProcessId()
|
- added os.getCurrentProcessId()
|
||||||
- User defined pragmas are now allowed in the pragma blocks
|
- User defined pragmas are now allowed in the pragma blocks
|
||||||
- Pragma blocks are now longer eliminated from the typed AST tree to preserve
|
- Pragma blocks are no longer eliminated from the typed AST tree to preserve
|
||||||
pragmas for further analysis by macros
|
pragmas for further analysis by macros
|
||||||
|
- Custom pragmas are now supported for `var` and `let` symbols.
|
||||||
|
|
||||||
|
|
||||||
### Language changes
|
### Language changes
|
||||||
|
|
||||||
|
|
@ -129,13 +165,23 @@ proc enumToString*(enums: openArray[enum]): string =
|
||||||
it's more recognizable and allows tools like github to recognize it as Nim,
|
it's more recognizable and allows tools like github to recognize it as Nim,
|
||||||
see [#9647](https://github.com/nim-lang/Nim/issues/9647).
|
see [#9647](https://github.com/nim-lang/Nim/issues/9647).
|
||||||
The previous extension will continue to work.
|
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.
|
||||||
|
|
||||||
|
|
||||||
### Tool changes
|
### Tool changes
|
||||||
- `jsondoc` now include a `moduleDescription` field with the module
|
- `jsondoc` now include a `moduleDescription` field with the module
|
||||||
description. `jsondoc0` shows comments as it's own objects as shown in the
|
description. `jsondoc0` shows comments as it's own objects as shown in the
|
||||||
documentation.
|
documentation.
|
||||||
|
- `nimpretty`: --backup now defaults to `off` instead of `on` and the flag was
|
||||||
|
un-documented; use `git` instead of relying on backup files.
|
||||||
|
|
||||||
|
|
||||||
### Compiler changes
|
### Compiler changes
|
||||||
- The deprecated `fmod` proc is now unavailable on the VM'.
|
- The deprecated `fmod` proc is now unavailable on the VM'.
|
||||||
|
|
||||||
|
|
||||||
### Bugfixes
|
### Bugfixes
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,6 @@ author = "Andreas Rumpf"
|
||||||
description = "Compiler package providing the compiler sources as a library."
|
description = "Compiler package providing the compiler sources as a library."
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
|
|
||||||
installDirs = @["compiler"]
|
installDirs = @["compiler", "nimsuggest"]
|
||||||
|
|
||||||
requires "nim >= 0.14.0"
|
requires "nim >= 0.14.0"
|
||||||
|
|
|
||||||
83
compiler/asciitables.nim
Normal file
83
compiler/asciitables.nim
Normal file
|
|
@ -0,0 +1,83 @@
|
||||||
|
#[
|
||||||
|
move to std/asciitables.nim once stable, or to a nimble paackage
|
||||||
|
once compiler can depend on nimble
|
||||||
|
]#
|
||||||
|
|
||||||
|
type Cell* = object
|
||||||
|
text*: string
|
||||||
|
width*, row*, col*, ncols*, nrows*: int
|
||||||
|
|
||||||
|
iterator parseTableCells*(s: string, delim = '\t'): Cell =
|
||||||
|
## iterates over all cells in a `delim`-delimited `s`, after a 1st
|
||||||
|
## pass that computes number of rows, columns, and width of each column.
|
||||||
|
var widths: seq[int]
|
||||||
|
var cell: Cell
|
||||||
|
template update() =
|
||||||
|
if widths.len<=cell.col:
|
||||||
|
widths.setLen cell.col+1
|
||||||
|
widths[cell.col] = cell.width
|
||||||
|
else:
|
||||||
|
widths[cell.col] = max(widths[cell.col], cell.width)
|
||||||
|
cell.width = 0
|
||||||
|
|
||||||
|
for a in s:
|
||||||
|
case a
|
||||||
|
of '\n':
|
||||||
|
update()
|
||||||
|
cell.col = 0
|
||||||
|
cell.row.inc
|
||||||
|
elif a == delim:
|
||||||
|
update()
|
||||||
|
cell.col.inc
|
||||||
|
else:
|
||||||
|
# todo: consider multi-width chars when porting to non-ascii implementation
|
||||||
|
cell.width.inc
|
||||||
|
if s.len > 0 and s[^1] != '\n':
|
||||||
|
update()
|
||||||
|
|
||||||
|
cell.ncols = widths.len
|
||||||
|
cell.nrows = cell.row + 1
|
||||||
|
cell.row = 0
|
||||||
|
cell.col = 0
|
||||||
|
cell.width = 0
|
||||||
|
|
||||||
|
template update2() =
|
||||||
|
cell.width = widths[cell.col]
|
||||||
|
yield cell
|
||||||
|
cell.text = ""
|
||||||
|
cell.width = 0
|
||||||
|
cell.col.inc
|
||||||
|
|
||||||
|
template finishRow() =
|
||||||
|
for col in cell.col..<cell.ncols:
|
||||||
|
cell.col = col
|
||||||
|
update2()
|
||||||
|
cell.col = 0
|
||||||
|
|
||||||
|
for a in s:
|
||||||
|
case a
|
||||||
|
of '\n':
|
||||||
|
finishRow()
|
||||||
|
cell.row.inc
|
||||||
|
elif a == delim:
|
||||||
|
update2()
|
||||||
|
else:
|
||||||
|
cell.width.inc
|
||||||
|
cell.text.add a
|
||||||
|
|
||||||
|
if s.len > 0 and s[^1] != '\n':
|
||||||
|
finishRow()
|
||||||
|
|
||||||
|
proc alignTable*(s: string, delim = '\t', fill = ' ', sep = " "): string =
|
||||||
|
## formats a `delim`-delimited `s` representing a table; each cell is aligned
|
||||||
|
## to a width that's computed for each column; consecutive columns are
|
||||||
|
## delimted by `sep`, and alignment space is filled using `fill`.
|
||||||
|
## More customized formatting can be done by calling `parseTableCells` directly.
|
||||||
|
for cell in parseTableCells(s, delim):
|
||||||
|
result.add cell.text
|
||||||
|
for i in cell.text.len..<cell.width:
|
||||||
|
result.add fill
|
||||||
|
if cell.col < cell.ncols-1:
|
||||||
|
result.add sep
|
||||||
|
if cell.col == cell.ncols-1 and cell.row < cell.nrows - 1:
|
||||||
|
result.add '\n'
|
||||||
|
|
@ -1087,6 +1087,13 @@ proc newSym*(symKind: TSymKind, name: PIdent, owner: PSym,
|
||||||
when debugIds:
|
when debugIds:
|
||||||
registerId(result)
|
registerId(result)
|
||||||
|
|
||||||
|
proc astdef*(s: PSym): PNode =
|
||||||
|
# get only the definition (initializer) portion of the ast
|
||||||
|
if s.ast != nil and s.ast.kind == nkIdentDefs:
|
||||||
|
s.ast[2]
|
||||||
|
else:
|
||||||
|
s.ast
|
||||||
|
|
||||||
proc isMetaType*(t: PType): bool =
|
proc isMetaType*(t: PType): bool =
|
||||||
return t.kind in tyMetaTypes or
|
return t.kind in tyMetaTypes or
|
||||||
(t.kind == tyStatic and t.n == nil) or
|
(t.kind == tyStatic and t.n == nil) or
|
||||||
|
|
|
||||||
|
|
@ -1501,7 +1501,7 @@ proc genRepr(p: BProc, e: PNode, d: var TLoc) =
|
||||||
addrLoc(p.config, a), genTypeInfo(p.module, t, e.info)]), a.storage)
|
addrLoc(p.config, a), genTypeInfo(p.module, t, e.info)]), a.storage)
|
||||||
of tyOpenArray, tyVarargs:
|
of tyOpenArray, tyVarargs:
|
||||||
var b: TLoc
|
var b: TLoc
|
||||||
case a.t.kind
|
case skipTypes(a.t, abstractVarRange).kind
|
||||||
of tyOpenArray, tyVarargs:
|
of tyOpenArray, tyVarargs:
|
||||||
putIntoDest(p, b, e, "$1, $1Len_0" % [rdLoc(a)], a.storage)
|
putIntoDest(p, b, e, "$1, $1Len_0" % [rdLoc(a)], a.storage)
|
||||||
of tyString, tySequence:
|
of tyString, tySequence:
|
||||||
|
|
|
||||||
|
|
@ -31,6 +31,7 @@ const
|
||||||
cfsData: "NIM_merge_DATA",
|
cfsData: "NIM_merge_DATA",
|
||||||
cfsProcs: "NIM_merge_PROCS",
|
cfsProcs: "NIM_merge_PROCS",
|
||||||
cfsInitProc: "NIM_merge_INIT_PROC",
|
cfsInitProc: "NIM_merge_INIT_PROC",
|
||||||
|
cfsDatInitProc: "NIM_merge_DATINIT_PROC",
|
||||||
cfsTypeInit1: "NIM_merge_TYPE_INIT1",
|
cfsTypeInit1: "NIM_merge_TYPE_INIT1",
|
||||||
cfsTypeInit2: "NIM_merge_TYPE_INIT2",
|
cfsTypeInit2: "NIM_merge_TYPE_INIT2",
|
||||||
cfsTypeInit3: "NIM_merge_TYPE_INIT3",
|
cfsTypeInit3: "NIM_merge_TYPE_INIT3",
|
||||||
|
|
|
||||||
|
|
@ -436,30 +436,25 @@ proc mangleRecFieldName(m: BModule; field: PSym): Rope =
|
||||||
if result == nil: internalError(m.config, field.info, "mangleRecFieldName")
|
if result == nil: internalError(m.config, field.info, "mangleRecFieldName")
|
||||||
|
|
||||||
proc genRecordFieldsAux(m: BModule, n: PNode,
|
proc genRecordFieldsAux(m: BModule, n: PNode,
|
||||||
accessExpr: Rope, rectype: PType,
|
rectype: PType,
|
||||||
check: var IntSet): Rope =
|
check: var IntSet): Rope =
|
||||||
result = nil
|
result = nil
|
||||||
case n.kind
|
case n.kind
|
||||||
of nkRecList:
|
of nkRecList:
|
||||||
for i in countup(0, sonsLen(n) - 1):
|
for i in countup(0, sonsLen(n) - 1):
|
||||||
add(result, genRecordFieldsAux(m, n.sons[i], accessExpr, rectype, check))
|
add(result, genRecordFieldsAux(m, n.sons[i], rectype, check))
|
||||||
of nkRecCase:
|
of nkRecCase:
|
||||||
if n.sons[0].kind != nkSym: internalError(m.config, n.info, "genRecordFieldsAux")
|
if n.sons[0].kind != nkSym: internalError(m.config, n.info, "genRecordFieldsAux")
|
||||||
add(result, genRecordFieldsAux(m, n.sons[0], accessExpr, rectype, check))
|
add(result, genRecordFieldsAux(m, n.sons[0], rectype, check))
|
||||||
# prefix mangled name with "_U" to avoid clashes with other field names,
|
# prefix mangled name with "_U" to avoid clashes with other field names,
|
||||||
# since identifiers are not allowed to start with '_'
|
# since identifiers are not allowed to start with '_'
|
||||||
let uname = rope("_U" & mangle(n.sons[0].sym.name.s))
|
|
||||||
let ae = if accessExpr != nil: "$1.$2" % [accessExpr, uname]
|
|
||||||
else: uname
|
|
||||||
var unionBody: Rope = nil
|
var unionBody: Rope = nil
|
||||||
for i in countup(1, sonsLen(n) - 1):
|
for i in countup(1, sonsLen(n) - 1):
|
||||||
case n.sons[i].kind
|
case n.sons[i].kind
|
||||||
of nkOfBranch, nkElse:
|
of nkOfBranch, nkElse:
|
||||||
let k = lastSon(n.sons[i])
|
let k = lastSon(n.sons[i])
|
||||||
if k.kind != nkSym:
|
if k.kind != nkSym:
|
||||||
let sname = "S" & rope(i)
|
let a = genRecordFieldsAux(m, k, rectype, check)
|
||||||
let a = genRecordFieldsAux(m, k, "$1.$2" % [ae, sname], rectype,
|
|
||||||
check)
|
|
||||||
if a != nil:
|
if a != nil:
|
||||||
if tfPacked notin rectype.flags:
|
if tfPacked notin rectype.flags:
|
||||||
add(unionBody, "struct {")
|
add(unionBody, "struct {")
|
||||||
|
|
@ -469,22 +464,20 @@ proc genRecordFieldsAux(m: BModule, n: PNode,
|
||||||
else:
|
else:
|
||||||
addf(unionBody, "#pragma pack(push, 1)$nstruct{", [])
|
addf(unionBody, "#pragma pack(push, 1)$nstruct{", [])
|
||||||
add(unionBody, a)
|
add(unionBody, a)
|
||||||
addf(unionBody, "} $1;$n", [sname])
|
addf(unionBody, "};$n", [])
|
||||||
if tfPacked in rectype.flags and hasAttribute notin CC[m.config.cCompiler].props:
|
if tfPacked in rectype.flags and hasAttribute notin CC[m.config.cCompiler].props:
|
||||||
addf(unionBody, "#pragma pack(pop)$n", [])
|
addf(unionBody, "#pragma pack(pop)$n", [])
|
||||||
else:
|
else:
|
||||||
add(unionBody, genRecordFieldsAux(m, k, ae, rectype, check))
|
add(unionBody, genRecordFieldsAux(m, k, rectype, check))
|
||||||
else: internalError(m.config, "genRecordFieldsAux(record case branch)")
|
else: internalError(m.config, "genRecordFieldsAux(record case branch)")
|
||||||
if unionBody != nil:
|
if unionBody != nil:
|
||||||
addf(result, "union{$n$1} $2;$n", [unionBody, uname])
|
addf(result, "union{$n$1};$n", [unionBody])
|
||||||
of nkSym:
|
of nkSym:
|
||||||
let field = n.sym
|
let field = n.sym
|
||||||
if field.typ.kind == tyVoid: return
|
if field.typ.kind == tyVoid: return
|
||||||
#assert(field.ast == nil)
|
#assert(field.ast == nil)
|
||||||
let sname = mangleRecFieldName(m, field)
|
let sname = mangleRecFieldName(m, field)
|
||||||
let ae = if accessExpr != nil: "$1.$2" % [accessExpr, sname]
|
fillLoc(field.loc, locField, n, sname, OnUnknown)
|
||||||
else: sname
|
|
||||||
fillLoc(field.loc, locField, n, ae, OnUnknown)
|
|
||||||
# for importcpp'ed objects, we only need to set field.loc, but don't
|
# 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
|
# have to recurse via 'getTypeDescAux'. And not doing so prevents problems
|
||||||
# with heavily templatized C++ code:
|
# with heavily templatized C++ code:
|
||||||
|
|
@ -505,7 +498,7 @@ proc genRecordFieldsAux(m: BModule, n: PNode,
|
||||||
else: internalError(m.config, n.info, "genRecordFieldsAux()")
|
else: internalError(m.config, n.info, "genRecordFieldsAux()")
|
||||||
|
|
||||||
proc getRecordFields(m: BModule, typ: PType, check: var IntSet): Rope =
|
proc getRecordFields(m: BModule, typ: PType, check: var IntSet): Rope =
|
||||||
result = genRecordFieldsAux(m, typ.n, nil, typ, check)
|
result = genRecordFieldsAux(m, typ.n, typ, check)
|
||||||
|
|
||||||
proc fillObjectFields*(m: BModule; typ: PType) =
|
proc fillObjectFields*(m: BModule; typ: PType) =
|
||||||
# sometimes generic objects are not consistently merged. We patch over
|
# sometimes generic objects are not consistently merged. We patch over
|
||||||
|
|
@ -544,12 +537,18 @@ proc getRecordDesc(m: BModule, typ: PType, name: Rope,
|
||||||
appcg(m, result, "virtual void raise() {throw *this;}$n") # required for polymorphic exceptions
|
appcg(m, result, "virtual void raise() {throw *this;}$n") # required for polymorphic exceptions
|
||||||
if typ.sym.magic == mException:
|
if typ.sym.magic == mException:
|
||||||
# Add cleanup destructor to Exception base class
|
# Add cleanup destructor to Exception base class
|
||||||
appcg(m, result, "~$1() {if(this->raiseId) popCurrentExceptionEx(this->raiseId);}$n", [name])
|
appcg(m, result, "~$1() {if(this->raiseId) #popCurrentExceptionEx(this->raiseId);}$n", [name])
|
||||||
# hack: forward declare popCurrentExceptionEx() on top of type description,
|
# hack: forward declare popCurrentExceptionEx() on top of type description,
|
||||||
# proper request to generate popCurrentExceptionEx not possible for 2 reasons:
|
# proper request to generate popCurrentExceptionEx not possible for 2 reasons:
|
||||||
# generated function will be below declared Exception type and circular dependency
|
# generated function will be below declared Exception type and circular dependency
|
||||||
# between Exception and popCurrentExceptionEx function
|
# between Exception and popCurrentExceptionEx function
|
||||||
result = genProcHeader(m, magicsys.getCompilerProc(m.g.graph, "popCurrentExceptionEx")) & ";" & result
|
|
||||||
|
let popExSym = magicsys.getCompilerProc(m.g.graph, "popCurrentExceptionEx")
|
||||||
|
if lfDynamicLib in popExSym.loc.flags and sfImportc in popExSym.flags:
|
||||||
|
# echo popExSym.flags, " ma flags ", popExSym.loc.flags
|
||||||
|
result = "extern " & getTypeDescAux(m, popExSym.typ, check) & " " & mangleName(m, popExSym) & ";\L" & result
|
||||||
|
else:
|
||||||
|
result = genProcHeader(m, popExSym) & ";\L" & result
|
||||||
hasField = true
|
hasField = true
|
||||||
else:
|
else:
|
||||||
appcg(m, result, " {$n $1 Sup;$n",
|
appcg(m, result, " {$n $1 Sup;$n",
|
||||||
|
|
@ -971,9 +970,9 @@ proc genTypeInfoAux(m: BModule, typ, origType: PType, name: Rope;
|
||||||
|
|
||||||
proc discriminatorTableName(m: BModule, objtype: PType, d: PSym): Rope =
|
proc discriminatorTableName(m: BModule, objtype: PType, d: PSym): Rope =
|
||||||
# bugfix: we need to search the type that contains the discriminator:
|
# bugfix: we need to search the type that contains the discriminator:
|
||||||
var objtype = objtype
|
var objtype = objtype.skipTypes(abstractPtrs)
|
||||||
while lookupInRecord(objtype.n, d.name) == nil:
|
while lookupInRecord(objtype.n, d.name) == nil:
|
||||||
objtype = objtype.sons[0]
|
objtype = objtype.sons[0].skipTypes(abstractPtrs)
|
||||||
if objtype.sym == nil:
|
if objtype.sym == nil:
|
||||||
internalError(m.config, d.info, "anonymous obj with discriminator")
|
internalError(m.config, d.info, "anonymous obj with discriminator")
|
||||||
result = "NimDT_$1_$2" % [rope($hashType(objtype)), rope(d.name.s.mangle)]
|
result = "NimDT_$1_$2" % [rope($hashType(objtype)), rope(d.name.s.mangle)]
|
||||||
|
|
@ -1042,6 +1041,8 @@ proc genObjectFields(m: BModule, typ, origType: PType, n: PNode, expr: Rope;
|
||||||
else: internalError(m.config, n.info, "genObjectFields(nkRecCase)")
|
else: internalError(m.config, n.info, "genObjectFields(nkRecCase)")
|
||||||
of nkSym:
|
of nkSym:
|
||||||
var field = n.sym
|
var field = n.sym
|
||||||
|
# Do not produce code for void types
|
||||||
|
if isEmptyType(field.typ): return
|
||||||
if field.bitsize == 0:
|
if field.bitsize == 0:
|
||||||
if field.loc.r == nil: fillObjectFields(m, typ)
|
if field.loc.r == nil: fillObjectFields(m, typ)
|
||||||
if field.loc.t == nil:
|
if field.loc.t == nil:
|
||||||
|
|
|
||||||
|
|
@ -1087,30 +1087,24 @@ proc getFileHeader(conf: ConfigRef; cfile: Cfile): Rope =
|
||||||
result = getCopyright(conf, cfile)
|
result = getCopyright(conf, cfile)
|
||||||
addIntTypes(result, conf)
|
addIntTypes(result, conf)
|
||||||
|
|
||||||
proc genFilenames(m: BModule): Rope =
|
|
||||||
discard cgsym(m, "dbgRegisterFilename")
|
|
||||||
result = nil
|
|
||||||
for i in 0..<m.config.m.fileInfos.len:
|
|
||||||
result.addf("dbgRegisterFilename($1);$N",
|
|
||||||
[m.config.m.fileInfos[i].projPath.string.makeCString])
|
|
||||||
|
|
||||||
proc genMainProc(m: BModule) =
|
proc genMainProc(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 propogate
|
||||||
|
## into other modules, only simple rope manipulations are allowed
|
||||||
|
|
||||||
const
|
const
|
||||||
# The use of a volatile function pointer to call Pre/NimMainInner
|
# The use of a volatile function pointer to call Pre/NimMainInner
|
||||||
# prevents inlining of the NimMainInner function and dependent
|
# prevents inlining of the NimMainInner function and dependent
|
||||||
# functions, which might otherwise merge their stack frames.
|
# functions, which might otherwise merge their stack frames.
|
||||||
PreMainBody =
|
PreMainBody =
|
||||||
"void PreMainInner(void) {$N" &
|
"void PreMainInner(void) {$N" &
|
||||||
"\tsystemInit000();$N" &
|
|
||||||
"$1" &
|
|
||||||
"$2" &
|
"$2" &
|
||||||
"$3" &
|
"$3" &
|
||||||
"}$N$N" &
|
"}$N$N" &
|
||||||
"void PreMain(void) {$N" &
|
"void PreMain(void) {$N" &
|
||||||
"\tvoid (*volatile inner)(void);$N" &
|
"\tvoid (*volatile inner)(void);$N" &
|
||||||
"\tsystemDatInit000();$N" &
|
|
||||||
"\tinner = PreMainInner;$N" &
|
"\tinner = PreMainInner;$N" &
|
||||||
"$4$5" &
|
"$1" &
|
||||||
"\t(*inner)();$N" &
|
"\t(*inner)();$N" &
|
||||||
"}$N$N"
|
"}$N$N"
|
||||||
|
|
||||||
|
|
@ -1217,21 +1211,17 @@ proc genMainProc(m: BModule) =
|
||||||
else:
|
else:
|
||||||
nimMain = PosixNimMain
|
nimMain = PosixNimMain
|
||||||
otherMain = PosixCMain
|
otherMain = PosixCMain
|
||||||
if m.g.breakpoints != nil: discard cgsym(m, "dbgRegisterBreakpoint")
|
|
||||||
if optEndb in m.config.options:
|
if optEndb in m.config.options:
|
||||||
m.g.breakpoints.add(m.genFilenames)
|
for i in 0..<m.config.m.fileInfos.len:
|
||||||
|
m.g.breakpoints.addf("dbgRegisterFilename($1);$N",
|
||||||
|
[m.config.m.fileInfos[i].projPath.string.makeCString])
|
||||||
|
|
||||||
let initStackBottomCall =
|
let initStackBottomCall =
|
||||||
if m.config.target.targetOS == osStandalone or m.config.selectedGC == gcNone: "".rope
|
if m.config.target.targetOS == osStandalone or m.config.selectedGC == gcNone: "".rope
|
||||||
else: ropecg(m, "\t#initStackBottomWith((void *)&inner);$N")
|
else: ropecg(m, "\t#initStackBottomWith((void *)&inner);$N")
|
||||||
inc(m.labels)
|
inc(m.labels)
|
||||||
appcg(m, m.s[cfsProcs], PreMainBody, [
|
appcg(m, m.s[cfsProcs], PreMainBody, [
|
||||||
m.g.mainDatInit, m.g.breakpoints, m.g.otherModsInit,
|
m.g.mainDatInit, m.g.breakpoints, m.g.otherModsInit])
|
||||||
if emulatedThreadVars(m.config) and m.config.target.targetOS != osStandalone:
|
|
||||||
ropecg(m, "\t#initThreadVarsEmulation();$N")
|
|
||||||
else:
|
|
||||||
"".rope,
|
|
||||||
initStackBottomCall])
|
|
||||||
|
|
||||||
appcg(m, m.s[cfsProcs], nimMain,
|
appcg(m, m.s[cfsProcs], nimMain,
|
||||||
[m.g.mainModInit, initStackBottomCall, rope(m.labels)])
|
[m.g.mainModInit, initStackBottomCall, rope(m.labels)])
|
||||||
|
|
@ -1260,22 +1250,61 @@ proc getInitName(m: PSym): Rope =
|
||||||
|
|
||||||
proc getDatInitName(m: PSym): Rope = getSomeInitName(m, "DatInit000")
|
proc getDatInitName(m: PSym): Rope = getSomeInitName(m, "DatInit000")
|
||||||
|
|
||||||
proc registerModuleToMain(g: BModuleList; m: PSym) =
|
|
||||||
var
|
proc registerModuleToMain(g: BModuleList; m: BModule) =
|
||||||
init = m.getInitName
|
if m.s[cfsDatInitProc].len > 0:
|
||||||
datInit = m.getDatInitName
|
let datInit = m.module.getDatInitName
|
||||||
addf(g.mainModProcs, "N_LIB_PRIVATE N_NIMCALL(void, $1)(void);$N", [init])
|
|
||||||
addf(g.mainModProcs, "N_LIB_PRIVATE N_NIMCALL(void, $1)(void);$N", [datInit])
|
addf(g.mainModProcs, "N_LIB_PRIVATE N_NIMCALL(void, $1)(void);$N", [datInit])
|
||||||
if sfSystemModule notin m.flags:
|
|
||||||
addf(g.mainDatInit, "\t$1();$N", [datInit])
|
addf(g.mainDatInit, "\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:
|
||||||
|
add(g.mainDatInit, ropecg(m, "\t#initThreadVarsEmulation();$N"))
|
||||||
|
if m.config.target.targetOS != osStandalone and m.config.selectedGC != gcNone:
|
||||||
|
add(g.mainDatInit, ropecg(m, "\t#initStackBottomWith((void *)&inner);$N"))
|
||||||
|
|
||||||
|
if m.s[cfsInitProc].len > 0:
|
||||||
|
let init = m.module.getInitName
|
||||||
|
addf(g.mainModProcs, "N_LIB_PRIVATE N_NIMCALL(void, $1)(void);$N", [init])
|
||||||
let initCall = "\t$1();$N" % [init]
|
let initCall = "\t$1();$N" % [init]
|
||||||
if sfMainModule in m.flags:
|
if sfMainModule in m.module.flags:
|
||||||
add(g.mainModInit, initCall)
|
add(g.mainModInit, initCall)
|
||||||
|
elif sfSystemModule in m.module.flags:
|
||||||
|
add(g.mainDatInit, initCall) # systemInit must called right after systemDatInit if any
|
||||||
else:
|
else:
|
||||||
add(g.otherModsInit, initCall)
|
add(g.otherModsInit, 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 propogate
|
||||||
|
## into other modules, only simple rope manipulations are allowed
|
||||||
|
|
||||||
|
var moduleDatInitRequired = false
|
||||||
|
|
||||||
|
var prc = "N_LIB_PRIVATE N_NIMCALL(void, $1)(void) {$N" %
|
||||||
|
[getDatInitName(m.module)]
|
||||||
|
|
||||||
|
for i in cfsTypeInit1..cfsDynLibInit:
|
||||||
|
if m.s[i].len != 0:
|
||||||
|
moduleDatInitRequired = true
|
||||||
|
add(prc, genSectionStart(i, m.config))
|
||||||
|
add(prc, m.s[i])
|
||||||
|
add(prc, genSectionEnd(i, m.config))
|
||||||
|
|
||||||
|
addf(prc, "}$N$N", [])
|
||||||
|
|
||||||
|
if moduleDatInitRequired:
|
||||||
|
add(m.s[cfsDatInitProc], prc)
|
||||||
|
|
||||||
proc genInitCode(m: BModule) =
|
proc genInitCode(m: BModule) =
|
||||||
var initname = getInitName(m.module)
|
## 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 propogate
|
||||||
|
## into other modules, only simple rope manipulations are allowed
|
||||||
|
|
||||||
|
var moduleInitRequired = false
|
||||||
|
let initname = getInitName(m.module)
|
||||||
var prc = "N_LIB_PRIVATE N_NIMCALL(void, $1)(void) {$N" % [initname]
|
var prc = "N_LIB_PRIVATE N_NIMCALL(void, $1)(void) {$N" % [initname]
|
||||||
if m.typeNodes > 0:
|
if m.typeNodes > 0:
|
||||||
appcg(m, m.s[cfsTypeInit1], "static #TNimNode $1[$2];$n",
|
appcg(m, m.s[cfsTypeInit1], "static #TNimNode $1[$2];$n",
|
||||||
|
|
@ -1290,25 +1319,37 @@ proc genInitCode(m: BModule) =
|
||||||
# Keep a bogus frame in case the code needs one
|
# Keep a bogus frame in case the code needs one
|
||||||
add(prc, ~"\tTFrame FR_; FR_.len = 0;$N")
|
add(prc, ~"\tTFrame FR_; FR_.len = 0;$N")
|
||||||
|
|
||||||
|
if m.preInitProc.s(cpsLocals).len > 0:
|
||||||
|
moduleInitRequired = true
|
||||||
add(prc, genSectionStart(cpsLocals, m.config))
|
add(prc, genSectionStart(cpsLocals, m.config))
|
||||||
add(prc, m.preInitProc.s(cpsLocals))
|
add(prc, m.preInitProc.s(cpsLocals))
|
||||||
add(prc, genSectionEnd(cpsLocals, m.config))
|
add(prc, genSectionEnd(cpsLocals, m.config))
|
||||||
|
|
||||||
|
if m.preInitProc.s(cpsInit).len > 0:
|
||||||
|
moduleInitRequired = true
|
||||||
add(prc, genSectionStart(cpsInit, m.config))
|
add(prc, genSectionStart(cpsInit, m.config))
|
||||||
add(prc, m.preInitProc.s(cpsInit))
|
add(prc, m.preInitProc.s(cpsInit))
|
||||||
add(prc, genSectionEnd(cpsInit, m.config))
|
add(prc, genSectionEnd(cpsInit, m.config))
|
||||||
|
|
||||||
|
if m.preInitProc.s(cpsStmts).len > 0:
|
||||||
|
moduleInitRequired = true
|
||||||
add(prc, genSectionStart(cpsStmts, m.config))
|
add(prc, genSectionStart(cpsStmts, m.config))
|
||||||
add(prc, m.preInitProc.s(cpsStmts))
|
add(prc, m.preInitProc.s(cpsStmts))
|
||||||
add(prc, genSectionEnd(cpsStmts, m.config))
|
add(prc, genSectionEnd(cpsStmts, m.config))
|
||||||
addf(prc, "}$N", [])
|
addf(prc, "}$N", [])
|
||||||
|
|
||||||
|
if m.initProc.gcFrameId > 0:
|
||||||
|
moduleInitRequired = true
|
||||||
add(prc, initGCFrame(m.initProc))
|
add(prc, initGCFrame(m.initProc))
|
||||||
|
|
||||||
|
if m.initProc.s(cpsLocals).len > 0:
|
||||||
|
moduleInitRequired = true
|
||||||
add(prc, genSectionStart(cpsLocals, m.config))
|
add(prc, genSectionStart(cpsLocals, m.config))
|
||||||
add(prc, m.initProc.s(cpsLocals))
|
add(prc, m.initProc.s(cpsLocals))
|
||||||
add(prc, genSectionEnd(cpsLocals, m.config))
|
add(prc, genSectionEnd(cpsLocals, m.config))
|
||||||
|
|
||||||
|
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:
|
if optStackTrace in m.initProc.options and frameDeclared notin m.flags:
|
||||||
# BUT: the generated init code might depend on a current frame, so
|
# BUT: the generated init code might depend on a current frame, so
|
||||||
# declare it nevertheless:
|
# declare it nevertheless:
|
||||||
|
|
@ -1329,43 +1370,64 @@ proc genInitCode(m: BModule) =
|
||||||
|
|
||||||
if optStackTrace in m.initProc.options and preventStackTrace notin m.flags:
|
if optStackTrace in m.initProc.options and preventStackTrace notin m.flags:
|
||||||
add(prc, deinitFrame(m.initProc))
|
add(prc, deinitFrame(m.initProc))
|
||||||
|
|
||||||
|
if m.initProc.gcFrameId > 0:
|
||||||
|
moduleInitRequired = true
|
||||||
add(prc, deinitGCFrame(m.initProc))
|
add(prc, deinitGCFrame(m.initProc))
|
||||||
addf(prc, "}$N$N", [])
|
|
||||||
|
|
||||||
prc.addf("N_LIB_PRIVATE N_NIMCALL(void, $1)(void) {$N",
|
|
||||||
[getDatInitName(m.module)])
|
|
||||||
|
|
||||||
for i in cfsTypeInit1..cfsDynLibInit:
|
|
||||||
add(prc, genSectionStart(i, m.config))
|
|
||||||
add(prc, m.s[i])
|
|
||||||
add(prc, genSectionEnd(i, m.config))
|
|
||||||
|
|
||||||
addf(prc, "}$N$N", [])
|
addf(prc, "}$N$N", [])
|
||||||
|
|
||||||
# we cannot simply add the init proc to ``m.s[cfsProcs]`` anymore because
|
# 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
|
# that would lead to a *nesting* of merge sections which the merger does
|
||||||
# not support. So we add it to another special section: ``cfsInitProc``
|
# not support. So we add it to another special section: ``cfsInitProc``
|
||||||
add(m.s[cfsInitProc], prc)
|
|
||||||
|
|
||||||
for i, el in pairs(m.extensionLoaders):
|
for i, el in pairs(m.extensionLoaders):
|
||||||
if el != nil:
|
if el != nil:
|
||||||
let ex = "NIM_EXTERNC N_NIMCALL(void, nimLoadProcs$1)(void) {$2}$N$N" %
|
let ex = "NIM_EXTERNC N_NIMCALL(void, nimLoadProcs$1)(void) {$2}$N$N" %
|
||||||
[(i.ord - '0'.ord).rope, el]
|
[(i.ord - '0'.ord).rope, el]
|
||||||
add(m.s[cfsInitProc], ex)
|
moduleInitRequired = true
|
||||||
|
add(prc, ex)
|
||||||
|
|
||||||
|
if moduleInitRequired or sfMainModule in m.module.flags:
|
||||||
|
add(m.s[cfsInitProc], prc)
|
||||||
|
|
||||||
|
genDatInitCode(m)
|
||||||
|
registerModuleToMain(m.g, m)
|
||||||
|
|
||||||
proc genModule(m: BModule, cfile: Cfile): Rope =
|
proc genModule(m: BModule, cfile: Cfile): Rope =
|
||||||
|
var moduleIsEmpty = true
|
||||||
|
|
||||||
result = getFileHeader(m.config, cfile)
|
result = getFileHeader(m.config, cfile)
|
||||||
result.add(genMergeInfo(m))
|
result.add(genMergeInfo(m))
|
||||||
|
|
||||||
|
if m.config.cppCustomNamespace.len > 0:
|
||||||
|
result.add openNamespaceNim(m.config.cppCustomNamespace)
|
||||||
|
|
||||||
generateThreadLocalStorage(m)
|
generateThreadLocalStorage(m)
|
||||||
generateHeaders(m)
|
generateHeaders(m)
|
||||||
for i in countup(cfsHeaders, cfsProcs):
|
add(result, genSectionStart(cfsHeaders, m.config))
|
||||||
|
add(result, m.s[cfsHeaders])
|
||||||
|
add(result, genSectionEnd(cfsHeaders, m.config))
|
||||||
|
|
||||||
|
for i in countup(cfsForwardTypes, cfsProcs):
|
||||||
|
if m.s[i].len > 0:
|
||||||
|
moduleIsEmpty = false
|
||||||
add(result, genSectionStart(i, m.config))
|
add(result, genSectionStart(i, m.config))
|
||||||
add(result, m.s[i])
|
add(result, m.s[i])
|
||||||
add(result, genSectionEnd(i, m.config))
|
add(result, genSectionEnd(i, m.config))
|
||||||
if m.config.cppCustomNamespace.len > 0 and i == cfsHeaders:
|
|
||||||
result.add openNamespaceNim(m.config.cppCustomNamespace)
|
if m.s[cfsInitProc].len > 0:
|
||||||
|
moduleIsEmpty = false
|
||||||
add(result, m.s[cfsInitProc])
|
add(result, m.s[cfsInitProc])
|
||||||
if m.config.cppCustomNamespace.len > 0: result.add closeNamespaceNim()
|
if m.s[cfsDatInitProc].len > 0:
|
||||||
|
moduleIsEmpty = false
|
||||||
|
add(result, m.s[cfsDatInitProc])
|
||||||
|
|
||||||
|
if m.config.cppCustomNamespace.len > 0:
|
||||||
|
result.add closeNamespaceNim()
|
||||||
|
|
||||||
|
if moduleIsEmpty:
|
||||||
|
result = nil
|
||||||
|
|
||||||
proc newPreInitProc(m: BModule): BProc =
|
proc newPreInitProc(m: BModule): BProc =
|
||||||
result = newProc(nil, m)
|
result = newProc(nil, m)
|
||||||
|
|
@ -1522,11 +1584,13 @@ proc writeModule(m: BModule, pending: bool) =
|
||||||
finishTypeDescriptions(m)
|
finishTypeDescriptions(m)
|
||||||
if sfMainModule in m.module.flags:
|
if sfMainModule in m.module.flags:
|
||||||
# generate main file:
|
# generate main file:
|
||||||
|
genMainProc(m)
|
||||||
add(m.s[cfsProcHeaders], m.g.mainModProcs)
|
add(m.s[cfsProcHeaders], m.g.mainModProcs)
|
||||||
generateThreadVarsSize(m)
|
generateThreadVarsSize(m)
|
||||||
|
|
||||||
var cf = Cfile(cname: cfile, obj: completeCFilePath(m.config, toObjFile(m.config, cfile)), flags: {})
|
var cf = Cfile(cname: cfile, obj: completeCFilePath(m.config, toObjFile(m.config, cfile)), flags: {})
|
||||||
var code = genModule(m, cf)
|
var code = genModule(m, cf)
|
||||||
|
if code != nil:
|
||||||
when hasTinyCBackend:
|
when hasTinyCBackend:
|
||||||
if conf.cmd == cmdRun:
|
if conf.cmd == cmdRun:
|
||||||
tccgen.compileCCode($code)
|
tccgen.compileCCode($code)
|
||||||
|
|
@ -1540,6 +1604,7 @@ proc writeModule(m: BModule, pending: bool) =
|
||||||
genInitCode(m)
|
genInitCode(m)
|
||||||
finishTypeDescriptions(m)
|
finishTypeDescriptions(m)
|
||||||
var code = genModule(m, cf)
|
var code = genModule(m, cf)
|
||||||
|
if code != nil:
|
||||||
if not writeRope(code, cfile):
|
if not writeRope(code, cfile):
|
||||||
rawMessage(m.config, errCannotOpenFile, cfile.string)
|
rawMessage(m.config, errCannotOpenFile, cfile.string)
|
||||||
addFileToCompile(m.config, cf)
|
addFileToCompile(m.config, cf)
|
||||||
|
|
@ -1560,11 +1625,14 @@ proc updateCachedModule(m: BModule) =
|
||||||
mergeFiles(cfile, m)
|
mergeFiles(cfile, m)
|
||||||
genInitCode(m)
|
genInitCode(m)
|
||||||
finishTypeDescriptions(m)
|
finishTypeDescriptions(m)
|
||||||
|
|
||||||
var code = genModule(m, cf)
|
var code = genModule(m, cf)
|
||||||
|
if code != nil:
|
||||||
if not writeRope(code, cfile):
|
if not writeRope(code, cfile):
|
||||||
rawMessage(m.config, errCannotOpenFile, cfile.string)
|
rawMessage(m.config, errCannotOpenFile, cfile.string)
|
||||||
|
addFileToCompile(m.config, cf)
|
||||||
else:
|
else:
|
||||||
|
if sfMainModule notin m.module.flags:
|
||||||
|
genMainProc(m)
|
||||||
cf.flags = {CfileFlag.Cached}
|
cf.flags = {CfileFlag.Cached}
|
||||||
addFileToCompile(m.config, cf)
|
addFileToCompile(m.config, cf)
|
||||||
|
|
||||||
|
|
@ -1579,15 +1647,25 @@ proc myClose(graph: ModuleGraph; b: PPassContext, n: PNode): PNode =
|
||||||
if n != nil:
|
if n != nil:
|
||||||
m.initProc.options = initProcOptions(m)
|
m.initProc.options = initProcOptions(m)
|
||||||
genStmts(m.initProc, n)
|
genStmts(m.initProc, n)
|
||||||
# cached modules need to registered too:
|
|
||||||
registerModuleToMain(m.g, m.module)
|
|
||||||
|
|
||||||
if sfMainModule in m.module.flags:
|
if sfMainModule in m.module.flags:
|
||||||
|
# raise dependencies on behalf of genMainProc
|
||||||
|
if m.config.target.targetOS != osStandalone and m.config.selectedGC != gcNone:
|
||||||
|
discard cgsym(m, "initStackBottomWith")
|
||||||
|
if emulatedThreadVars(m.config) and m.config.target.targetOS != osStandalone:
|
||||||
|
discard cgsym(m, "initThreadVarsEmulation")
|
||||||
|
|
||||||
|
if m.g.breakpoints != nil:
|
||||||
|
discard cgsym(m, "dbgRegisterBreakpoint")
|
||||||
|
if optEndb in m.config.options:
|
||||||
|
discard cgsym(m, "dbgRegisterFilename")
|
||||||
|
|
||||||
if m.g.forwardedProcs.len == 0:
|
if m.g.forwardedProcs.len == 0:
|
||||||
incl m.flags, objHasKidsValid
|
incl m.flags, objHasKidsValid
|
||||||
let disp = generateMethodDispatchers(graph)
|
let disp = generateMethodDispatchers(graph)
|
||||||
for x in disp: genProcAux(m, x.sym)
|
for x in disp: genProcAux(m, x.sym)
|
||||||
genMainProc(m)
|
|
||||||
|
m.g.modules_closed.add m
|
||||||
|
|
||||||
proc genForwardedProcs(g: BModuleList) =
|
proc genForwardedProcs(g: BModuleList) =
|
||||||
# Forward declared proc:s lack bodies when first encountered, so they're given
|
# Forward declared proc:s lack bodies when first encountered, so they're given
|
||||||
|
|
|
||||||
|
|
@ -32,6 +32,7 @@ type
|
||||||
cfsData, # section for C constant data
|
cfsData, # section for C constant data
|
||||||
cfsProcs, # section for C procs that are not inline
|
cfsProcs, # section for C procs that are not inline
|
||||||
cfsInitProc, # section for the C init proc
|
cfsInitProc, # section for the C init proc
|
||||||
|
cfsDatInitProc, # section for the C datInit proc
|
||||||
cfsTypeInit1, # section 1 for declarations of type information
|
cfsTypeInit1, # section 1 for declarations of type information
|
||||||
cfsTypeInit2, # section 2 for init of type information
|
cfsTypeInit2, # section 2 for init of type information
|
||||||
cfsTypeInit3, # section 3 for init of type information
|
cfsTypeInit3, # section 3 for init of type information
|
||||||
|
|
@ -112,6 +113,7 @@ type
|
||||||
mainModProcs*, mainModInit*, otherModsInit*, mainDatInit*: Rope
|
mainModProcs*, mainModInit*, otherModsInit*, mainDatInit*: Rope
|
||||||
mapping*: Rope # the generated mapping file (if requested)
|
mapping*: Rope # the generated mapping file (if requested)
|
||||||
modules*: seq[BModule] # list of all compiled modules
|
modules*: seq[BModule] # list of all compiled modules
|
||||||
|
modules_closed*: 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
|
forwardedProcs*: seq[PSym] # proc:s that did not yet have a body
|
||||||
generatedHeader*: BModule
|
generatedHeader*: BModule
|
||||||
breakPointId*: int
|
breakPointId*: int
|
||||||
|
|
@ -191,8 +193,6 @@ proc newModuleList*(g: ModuleGraph): BModuleList =
|
||||||
graph: g, nimtvDeclared: initIntSet())
|
graph: g, nimtvDeclared: initIntSet())
|
||||||
|
|
||||||
iterator cgenModules*(g: BModuleList): BModule =
|
iterator cgenModules*(g: BModuleList): BModule =
|
||||||
for m in g.modules:
|
for m in g.modules_closed:
|
||||||
# ultimately, we are iterating over the file ids here.
|
# iterate modules in the order they were closed
|
||||||
# some "files" won't have an associated cgen module (like stdin)
|
yield m
|
||||||
# and we must skip over them.
|
|
||||||
if m != nil: yield m
|
|
||||||
|
|
|
||||||
|
|
@ -48,18 +48,10 @@ proc loadConfigsAndRunMainCommand*(self: NimProg, cache: IdentCache; conf: Confi
|
||||||
if self.suggestMode:
|
if self.suggestMode:
|
||||||
conf.command = "nimsuggest"
|
conf.command = "nimsuggest"
|
||||||
|
|
||||||
# These defines/options should not be enabled while processing nimscript
|
template runNimScriptIfExists(path: AbsoluteFile) =
|
||||||
# bug #4446, #9420, #8991, #9589, #9153
|
let p = path # eval once
|
||||||
undefSymbol(conf.symbols, "profiler")
|
if fileExists(p):
|
||||||
undefSymbol(conf.symbols, "memProfiler")
|
runNimScript(cache, p, freshDefines = false, conf)
|
||||||
undefSymbol(conf.symbols, "nodejs")
|
|
||||||
|
|
||||||
# bug #9120
|
|
||||||
conf.globalOptions.excl(optTaintMode)
|
|
||||||
|
|
||||||
proc runNimScriptIfExists(path: AbsoluteFile)=
|
|
||||||
if fileExists(path):
|
|
||||||
runNimScript(cache, path, freshDefines = false, conf)
|
|
||||||
|
|
||||||
# Caution: make sure this stays in sync with `loadConfigs`
|
# Caution: make sure this stays in sync with `loadConfigs`
|
||||||
if optSkipSystemConfigFile notin conf.globalOptions:
|
if optSkipSystemConfigFile notin conf.globalOptions:
|
||||||
|
|
@ -88,9 +80,6 @@ proc loadConfigsAndRunMainCommand*(self: NimProg, cache: IdentCache; conf: Confi
|
||||||
# 'nimsuggest foo.nims' means to just auto-complete the NimScript file
|
# 'nimsuggest foo.nims' means to just auto-complete the NimScript file
|
||||||
discard
|
discard
|
||||||
|
|
||||||
# Reload configuration from .cfg file
|
|
||||||
loadConfigs(DefaultConfig, cache, conf)
|
|
||||||
|
|
||||||
# now process command line arguments again, because some options in the
|
# now process command line arguments again, because some options in the
|
||||||
# command line can overwite the config file's settings
|
# command line can overwite the config file's settings
|
||||||
extccomp.initVars(conf)
|
extccomp.initVars(conf)
|
||||||
|
|
|
||||||
|
|
@ -142,7 +142,7 @@ proc splitSwitch(conf: ConfigRef; switch: string, cmd, arg: var string, pass: TC
|
||||||
proc processOnOffSwitch(conf: ConfigRef; op: TOptions, arg: string, pass: TCmdLinePass,
|
proc processOnOffSwitch(conf: ConfigRef; op: TOptions, arg: string, pass: TCmdLinePass,
|
||||||
info: TLineInfo) =
|
info: TLineInfo) =
|
||||||
case arg.normalize
|
case arg.normalize
|
||||||
of "on": conf.options = conf.options + op
|
of "","on": conf.options = conf.options + op
|
||||||
of "off": conf.options = conf.options - op
|
of "off": conf.options = conf.options - op
|
||||||
else: localError(conf, info, errOnOrOffExpectedButXFound % arg)
|
else: localError(conf, info, errOnOrOffExpectedButXFound % arg)
|
||||||
|
|
||||||
|
|
@ -158,7 +158,7 @@ proc processOnOffSwitchOrList(conf: ConfigRef; op: TOptions, arg: string, pass:
|
||||||
proc processOnOffSwitchG(conf: ConfigRef; op: TGlobalOptions, arg: string, pass: TCmdLinePass,
|
proc processOnOffSwitchG(conf: ConfigRef; op: TGlobalOptions, arg: string, pass: TCmdLinePass,
|
||||||
info: TLineInfo) =
|
info: TLineInfo) =
|
||||||
case arg.normalize
|
case arg.normalize
|
||||||
of "on": conf.globalOptions = conf.globalOptions + op
|
of "", "on": conf.globalOptions = conf.globalOptions + op
|
||||||
of "off": conf.globalOptions = conf.globalOptions - op
|
of "off": conf.globalOptions = conf.globalOptions - op
|
||||||
else: localError(conf, info, errOnOrOffExpectedButXFound % arg)
|
else: localError(conf, info, errOnOrOffExpectedButXFound % arg)
|
||||||
|
|
||||||
|
|
@ -224,7 +224,7 @@ proc testCompileOptionArg*(conf: ConfigRef; switch, arg: string, info: TLineInfo
|
||||||
case arg.normalize
|
case arg.normalize
|
||||||
of "boehm": result = conf.selectedGC == gcBoehm
|
of "boehm": result = conf.selectedGC == gcBoehm
|
||||||
of "refc": result = conf.selectedGC == gcRefc
|
of "refc": result = conf.selectedGC == gcRefc
|
||||||
of "v2": result = conf.selectedGC == gcV2
|
of "v2": result = false
|
||||||
of "markandsweep": result = conf.selectedGC == gcMarkAndSweep
|
of "markandsweep": result = conf.selectedGC == gcMarkAndSweep
|
||||||
of "generational": result = false
|
of "generational": result = false
|
||||||
of "destructors": result = conf.selectedGC == gcDestructors
|
of "destructors": result = conf.selectedGC == gcDestructors
|
||||||
|
|
@ -291,6 +291,7 @@ proc testCompileOption*(conf: ConfigRef; switch: string, info: TLineInfo): bool
|
||||||
of "patterns": result = contains(conf.options, optPatterns)
|
of "patterns": result = contains(conf.options, optPatterns)
|
||||||
of "excessivestacktrace": result = contains(conf.globalOptions, optExcessiveStackTrace)
|
of "excessivestacktrace": result = contains(conf.globalOptions, optExcessiveStackTrace)
|
||||||
of "nilseqs": result = contains(conf.options, optNilSeqs)
|
of "nilseqs": result = contains(conf.options, optNilSeqs)
|
||||||
|
of "oldast": result = contains(conf.options, optOldAst)
|
||||||
else: invalidCmdLineOption(conf, passCmd1, switch, info)
|
else: invalidCmdLineOption(conf, passCmd1, switch, info)
|
||||||
|
|
||||||
proc processPath(conf: ConfigRef; path: string, info: TLineInfo,
|
proc processPath(conf: ConfigRef; path: string, info: TLineInfo,
|
||||||
|
|
@ -413,26 +414,19 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
|
||||||
if pass in {passCmd2, passPP}:
|
if pass in {passCmd2, passPP}:
|
||||||
addExternalFileToLink(conf, AbsoluteFile arg)
|
addExternalFileToLink(conf, AbsoluteFile arg)
|
||||||
of "debuginfo":
|
of "debuginfo":
|
||||||
expectNoArg(conf, switch, arg, pass, info)
|
processOnOffSwitchG(conf, {optCDebug}, arg, pass, info)
|
||||||
incl(conf.globalOptions, optCDebug)
|
|
||||||
of "embedsrc":
|
of "embedsrc":
|
||||||
expectNoArg(conf, switch, arg, pass, info)
|
processOnOffSwitchG(conf, {optEmbedOrigSrc}, arg, pass, info)
|
||||||
incl(conf.globalOptions, optEmbedOrigSrc)
|
|
||||||
of "compileonly", "c":
|
of "compileonly", "c":
|
||||||
expectNoArg(conf, switch, arg, pass, info)
|
processOnOffSwitchG(conf, {optCompileOnly}, arg, pass, info)
|
||||||
incl(conf.globalOptions, optCompileOnly)
|
|
||||||
of "nolinking":
|
of "nolinking":
|
||||||
expectNoArg(conf, switch, arg, pass, info)
|
processOnOffSwitchG(conf, {optNoLinking}, arg, pass, info)
|
||||||
incl(conf.globalOptions, optNoLinking)
|
|
||||||
of "nomain":
|
of "nomain":
|
||||||
expectNoArg(conf, switch, arg, pass, info)
|
processOnOffSwitchG(conf, {optNoMain}, arg, pass, info)
|
||||||
incl(conf.globalOptions, optNoMain)
|
|
||||||
of "forcebuild", "f":
|
of "forcebuild", "f":
|
||||||
expectNoArg(conf, switch, arg, pass, info)
|
processOnOffSwitchG(conf, {optForceFullMake}, arg, pass, info)
|
||||||
incl(conf.globalOptions, optForceFullMake)
|
|
||||||
of "project":
|
of "project":
|
||||||
expectNoArg(conf, switch, arg, pass, info)
|
processOnOffSwitchG(conf, {optWholeProject}, arg, pass, info)
|
||||||
incl conf.globalOptions, optWholeProject
|
|
||||||
of "gc":
|
of "gc":
|
||||||
expectArg(conf, switch, arg, pass, info)
|
expectArg(conf, switch, arg, pass, info)
|
||||||
case arg.normalize
|
case arg.normalize
|
||||||
|
|
@ -442,7 +436,7 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
|
||||||
of "refc":
|
of "refc":
|
||||||
conf.selectedGC = gcRefc
|
conf.selectedGC = gcRefc
|
||||||
of "v2":
|
of "v2":
|
||||||
conf.selectedGC = gcV2
|
message(conf, info, warnDeprecated, "--gc:v2 is deprecated; using default gc")
|
||||||
of "markandsweep":
|
of "markandsweep":
|
||||||
conf.selectedGC = gcMarkAndSweep
|
conf.selectedGC = gcMarkAndSweep
|
||||||
defineSymbol(conf.symbols, "gcmarkandsweep")
|
defineSymbol(conf.symbols, "gcmarkandsweep")
|
||||||
|
|
@ -498,7 +492,7 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
|
||||||
else: undefSymbol(conf.symbols, "hotcodereloading")
|
else: undefSymbol(conf.symbols, "hotcodereloading")
|
||||||
of "oldnewlines":
|
of "oldnewlines":
|
||||||
case arg.normalize
|
case arg.normalize
|
||||||
of "on":
|
of "","on":
|
||||||
conf.oldNewlines = true
|
conf.oldNewlines = true
|
||||||
defineSymbol(conf.symbols, "nimOldNewlines")
|
defineSymbol(conf.symbols, "nimOldNewlines")
|
||||||
of "off":
|
of "off":
|
||||||
|
|
@ -508,6 +502,7 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
|
||||||
localError(conf, info, errOnOrOffExpectedButXFound % arg)
|
localError(conf, info, errOnOrOffExpectedButXFound % arg)
|
||||||
of "laxstrings": processOnOffSwitch(conf, {optLaxStrings}, arg, pass, info)
|
of "laxstrings": processOnOffSwitch(conf, {optLaxStrings}, arg, pass, info)
|
||||||
of "nilseqs": processOnOffSwitch(conf, {optNilSeqs}, arg, pass, info)
|
of "nilseqs": processOnOffSwitch(conf, {optNilSeqs}, arg, pass, info)
|
||||||
|
of "oldast": processOnOffSwitch(conf, {optOldAst}, arg, pass, info)
|
||||||
of "checks", "x": processOnOffSwitch(conf, ChecksOptions, arg, pass, info)
|
of "checks", "x": processOnOffSwitch(conf, ChecksOptions, arg, pass, info)
|
||||||
of "floatchecks":
|
of "floatchecks":
|
||||||
processOnOffSwitch(conf, {optNaNCheck, optInfCheck}, arg, pass, info)
|
processOnOffSwitch(conf, {optNaNCheck, optInfCheck}, arg, pass, info)
|
||||||
|
|
@ -590,16 +585,16 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
|
||||||
processOnOffSwitchG(conf, {optGenIndex}, arg, pass, info)
|
processOnOffSwitchG(conf, {optGenIndex}, arg, pass, info)
|
||||||
of "import":
|
of "import":
|
||||||
expectArg(conf, switch, arg, pass, info)
|
expectArg(conf, switch, arg, pass, info)
|
||||||
if pass in {passCmd2, passPP}: conf.implicitImports.add arg
|
if pass in {passCmd2, passPP}:
|
||||||
|
conf.implicitImports.add findModule(conf, arg, toFullPath(conf, info)).string
|
||||||
of "include":
|
of "include":
|
||||||
expectArg(conf, switch, arg, pass, info)
|
expectArg(conf, switch, arg, pass, info)
|
||||||
if pass in {passCmd2, passPP}: conf.implicitIncludes.add arg
|
if pass in {passCmd2, passPP}:
|
||||||
|
conf.implicitIncludes.add findModule(conf, arg, toFullPath(conf, info)).string
|
||||||
of "listcmd":
|
of "listcmd":
|
||||||
expectNoArg(conf, switch, arg, pass, info)
|
processOnOffSwitchG(conf, {optListCmd}, arg, pass, info)
|
||||||
incl(conf.globalOptions, optListCmd)
|
|
||||||
of "genmapping":
|
of "genmapping":
|
||||||
expectNoArg(conf, switch, arg, pass, info)
|
processOnOffSwitchG(conf, {optGenMapping}, arg, pass, info)
|
||||||
incl(conf.globalOptions, optGenMapping)
|
|
||||||
of "os":
|
of "os":
|
||||||
expectArg(conf, switch, arg, pass, info)
|
expectArg(conf, switch, arg, pass, info)
|
||||||
if pass in {passCmd1, passPP}:
|
if pass in {passCmd1, passPP}:
|
||||||
|
|
@ -615,8 +610,7 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
|
||||||
elif cpu != conf.target.hostCPU:
|
elif cpu != conf.target.hostCPU:
|
||||||
setTarget(conf.target, conf.target.targetOS, cpu)
|
setTarget(conf.target, conf.target.targetOS, cpu)
|
||||||
of "run", "r":
|
of "run", "r":
|
||||||
expectNoArg(conf, switch, arg, pass, info)
|
processOnOffSwitchG(conf, {optRun}, arg, pass, info)
|
||||||
incl(conf.globalOptions, optRun)
|
|
||||||
of "errormax":
|
of "errormax":
|
||||||
expectArg(conf, switch, arg, pass, info)
|
expectArg(conf, switch, arg, pass, info)
|
||||||
# Note: `nim check` (etc) can overwrite this.
|
# Note: `nim check` (etc) can overwrite this.
|
||||||
|
|
@ -664,21 +658,16 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
|
||||||
of "v2": conf.symbolFiles = v2Sf
|
of "v2": conf.symbolFiles = v2Sf
|
||||||
else: localError(conf, info, "invalid option for --incremental: " & arg)
|
else: localError(conf, info, "invalid option for --incremental: " & arg)
|
||||||
of "skipcfg":
|
of "skipcfg":
|
||||||
expectNoArg(conf, switch, arg, pass, info)
|
processOnOffSwitchG(conf, {optSkipSystemConfigFile}, arg, pass, info)
|
||||||
incl(conf.globalOptions, optSkipSystemConfigFile)
|
|
||||||
of "skipprojcfg":
|
of "skipprojcfg":
|
||||||
expectNoArg(conf, switch, arg, pass, info)
|
processOnOffSwitchG(conf, {optSkipProjConfigFile}, arg, pass, info)
|
||||||
incl(conf.globalOptions, optSkipProjConfigFile)
|
|
||||||
of "skipusercfg":
|
of "skipusercfg":
|
||||||
expectNoArg(conf, switch, arg, pass, info)
|
processOnOffSwitchG(conf, {optSkipUserConfigFile}, arg, pass, info)
|
||||||
incl(conf.globalOptions, optSkipUserConfigFile)
|
|
||||||
of "skipparentcfg":
|
of "skipparentcfg":
|
||||||
expectNoArg(conf, switch, arg, pass, info)
|
processOnOffSwitchG(conf, {optSkipParentConfigFiles}, arg, pass, info)
|
||||||
incl(conf.globalOptions, optSkipParentConfigFiles)
|
|
||||||
of "genscript", "gendeps":
|
of "genscript", "gendeps":
|
||||||
expectNoArg(conf, switch, arg, pass, info)
|
processOnOffSwitchG(conf, {optGenScript}, arg, pass, info)
|
||||||
incl(conf.globalOptions, optGenScript)
|
processOnOffSwitchG(conf, {optCompileOnly}, arg, pass, info)
|
||||||
incl(conf.globalOptions, optCompileOnly)
|
|
||||||
of "colors": processOnOffSwitchG(conf, {optUseColors}, arg, pass, info)
|
of "colors": processOnOffSwitchG(conf, {optUseColors}, arg, pass, info)
|
||||||
of "lib":
|
of "lib":
|
||||||
expectArg(conf, switch, arg, pass, info)
|
expectArg(conf, switch, arg, pass, info)
|
||||||
|
|
@ -712,16 +701,13 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
|
||||||
expectNoArg(conf, switch, arg, pass, info)
|
expectNoArg(conf, switch, arg, pass, info)
|
||||||
conf.ideCmd = ideUse
|
conf.ideCmd = ideUse
|
||||||
of "stdout":
|
of "stdout":
|
||||||
expectNoArg(conf, switch, arg, pass, info)
|
processOnOffSwitchG(conf, {optStdout}, arg, pass, info)
|
||||||
incl(conf.globalOptions, optStdout)
|
|
||||||
of "listfullpaths":
|
of "listfullpaths":
|
||||||
expectNoArg(conf, switch, arg, pass, info)
|
processOnOffSwitchG(conf, {optListFullPaths}, arg, pass, info)
|
||||||
incl conf.globalOptions, optListFullPaths
|
|
||||||
of "dynliboverride":
|
of "dynliboverride":
|
||||||
dynlibOverride(conf, switch, arg, pass, info)
|
dynlibOverride(conf, switch, arg, pass, info)
|
||||||
of "dynliboverrideall":
|
of "dynliboverrideall":
|
||||||
expectNoArg(conf, switch, arg, pass, info)
|
processOnOffSwitchG(conf, {optDynlibOverrideAll}, arg, pass, info)
|
||||||
incl conf.globalOptions, optDynlibOverrideAll
|
|
||||||
of "cs":
|
of "cs":
|
||||||
# only supported for compatibility. Does nothing.
|
# only supported for compatibility. Does nothing.
|
||||||
expectArg(conf, switch, arg, pass, info)
|
expectArg(conf, switch, arg, pass, info)
|
||||||
|
|
|
||||||
|
|
@ -296,7 +296,8 @@ proc makePtrType(c: Con, baseType: PType): PType =
|
||||||
template genOp(opr, opname, ri) =
|
template genOp(opr, opname, ri) =
|
||||||
let op = opr
|
let op = opr
|
||||||
if op == nil:
|
if op == nil:
|
||||||
globalError(c.graph.config, dest.info, "internal error: '" & opname & "' operator not found for type " & typeToString(t))
|
globalError(c.graph.config, dest.info, "internal error: '" & opname &
|
||||||
|
"' operator not found for type " & typeToString(t))
|
||||||
elif op.ast[genericParamsPos].kind != nkEmpty:
|
elif op.ast[genericParamsPos].kind != nkEmpty:
|
||||||
globalError(c.graph.config, dest.info, "internal error: '" & opname & "' operator is generic")
|
globalError(c.graph.config, dest.info, "internal error: '" & opname & "' operator is generic")
|
||||||
patchHead op
|
patchHead op
|
||||||
|
|
|
||||||
|
|
@ -86,7 +86,7 @@ proc codeListing(c: ControlFlowGraph, result: var string, start=0; last = -1) =
|
||||||
result.add("\n")
|
result.add("\n")
|
||||||
inc i
|
inc i
|
||||||
if i in jumpTargets: result.add("L" & $i & ": End\n")
|
if i in jumpTargets: result.add("L" & $i & ": End\n")
|
||||||
|
# consider calling `asciitables.alignTable`
|
||||||
|
|
||||||
proc echoCfg*(c: ControlFlowGraph; start=0; last = -1) {.deprecated.} =
|
proc echoCfg*(c: ControlFlowGraph; start=0; last = -1) {.deprecated.} =
|
||||||
## echos the ControlFlowGraph for debugging purposes.
|
## echos the ControlFlowGraph for debugging purposes.
|
||||||
|
|
|
||||||
|
|
@ -40,6 +40,7 @@ type
|
||||||
# already. See bug #3655
|
# already. See bug #3655
|
||||||
destFile*: AbsoluteFile
|
destFile*: AbsoluteFile
|
||||||
thisDir*: AbsoluteDir
|
thisDir*: AbsoluteDir
|
||||||
|
examples: string
|
||||||
|
|
||||||
PDoc* = ref TDocumentor ## Alias to type less.
|
PDoc* = ref TDocumentor ## Alias to type less.
|
||||||
|
|
||||||
|
|
@ -118,7 +119,7 @@ proc newDocumentor*(filename: AbsoluteFile; cache: IdentCache; conf: ConfigRef,
|
||||||
result.conf = conf
|
result.conf = conf
|
||||||
result.cache = cache
|
result.cache = cache
|
||||||
initRstGenerator(result[], (if conf.cmd != cmdRst2tex: outHtml else: outLatex),
|
initRstGenerator(result[], (if conf.cmd != cmdRst2tex: outHtml else: outLatex),
|
||||||
conf.configVars, filename.string, {roSupportRawDirective},
|
conf.configVars, filename.string, {roSupportRawDirective, roSupportMarkdown},
|
||||||
docgenFindFile, compilerMsgHandler)
|
docgenFindFile, compilerMsgHandler)
|
||||||
|
|
||||||
if conf.configVars.hasKey("doc.googleAnalytics"):
|
if conf.configVars.hasKey("doc.googleAnalytics"):
|
||||||
|
|
@ -378,6 +379,14 @@ proc testExample(d: PDoc; ex: PNode) =
|
||||||
"_examples" & $d.exampleCounter & ".nim"))
|
"_examples" & $d.exampleCounter & ".nim"))
|
||||||
#let nimcache = outp.changeFileExt"" & "_nimcache"
|
#let nimcache = outp.changeFileExt"" & "_nimcache"
|
||||||
renderModule(ex, d.filename, outp.string, conf = d.conf)
|
renderModule(ex, d.filename, outp.string, conf = d.conf)
|
||||||
|
d.examples.add "import r\"" & outp.string & "\"\n"
|
||||||
|
|
||||||
|
proc runAllExamples(d: PDoc) =
|
||||||
|
if d.examples.len == 0: return
|
||||||
|
let outputDir = d.conf.getNimcacheDir / RelativeDir"runnableExamples"
|
||||||
|
let outp = outputDir / RelativeFile(extractFilename(d.filename.changeFileExt"" &
|
||||||
|
"_examples.nim"))
|
||||||
|
writeFile(outp, d.examples)
|
||||||
let backend = if isDefined(d.conf, "js"): "js"
|
let backend = if isDefined(d.conf, "js"): "js"
|
||||||
elif isDefined(d.conf, "cpp"): "cpp"
|
elif isDefined(d.conf, "cpp"): "cpp"
|
||||||
elif isDefined(d.conf, "objc"): "objc"
|
elif isDefined(d.conf, "objc"): "objc"
|
||||||
|
|
@ -400,11 +409,9 @@ proc extractImports(n: PNode; result: PNode) =
|
||||||
for i in 0..<n.safeLen: extractImports(n[i], result)
|
for i in 0..<n.safeLen: extractImports(n[i], result)
|
||||||
|
|
||||||
proc prepareExamples(d: PDoc; n: PNode) =
|
proc prepareExamples(d: PDoc; n: PNode) =
|
||||||
|
|
||||||
var docComment = newTree(nkCommentStmt)
|
var docComment = newTree(nkCommentStmt)
|
||||||
let loc = d.conf.toFileLineCol(n.info)
|
let loc = d.conf.toFileLineCol(n.info)
|
||||||
docComment.comment = "autogenerated by docgen from " & loc
|
docComment.comment = "autogenerated by docgen from " & loc
|
||||||
|
|
||||||
var runnableExamples = newTree(nkStmtList,
|
var runnableExamples = newTree(nkStmtList,
|
||||||
docComment,
|
docComment,
|
||||||
newTree(nkImportStmt, newStrNode(nkStrLit, d.filename)))
|
newTree(nkImportStmt, newStrNode(nkStrLit, d.filename)))
|
||||||
|
|
@ -935,6 +942,7 @@ proc generateIndex*(d: PDoc) =
|
||||||
writeIndexFile(d[], dest.string)
|
writeIndexFile(d[], dest.string)
|
||||||
|
|
||||||
proc writeOutput*(d: PDoc, useWarning = false) =
|
proc writeOutput*(d: PDoc, useWarning = false) =
|
||||||
|
runAllExamples(d)
|
||||||
var content = genOutFile(d)
|
var content = genOutFile(d)
|
||||||
if optStdout in d.conf.globalOptions:
|
if optStdout in d.conf.globalOptions:
|
||||||
writeRope(stdout, content)
|
writeRope(stdout, content)
|
||||||
|
|
@ -947,6 +955,7 @@ proc writeOutput*(d: PDoc, useWarning = false) =
|
||||||
outfile.string)
|
outfile.string)
|
||||||
|
|
||||||
proc writeOutputJson*(d: PDoc, useWarning = false) =
|
proc writeOutputJson*(d: PDoc, useWarning = false) =
|
||||||
|
runAllExamples(d)
|
||||||
var modDesc: string
|
var modDesc: string
|
||||||
for desc in d.modDesc:
|
for desc in d.modDesc:
|
||||||
modDesc &= desc
|
modDesc &= desc
|
||||||
|
|
@ -982,7 +991,7 @@ proc commandRstAux(cache: IdentCache, conf: ConfigRef;
|
||||||
|
|
||||||
d.isPureRst = true
|
d.isPureRst = true
|
||||||
var rst = parseRst(readFile(filen.string), filen.string, 0, 1, d.hasToc,
|
var rst = parseRst(readFile(filen.string), filen.string, 0, 1, d.hasToc,
|
||||||
{roSupportRawDirective}, conf)
|
{roSupportRawDirective, roSupportMarkdown}, conf)
|
||||||
var modDesc = newStringOfCap(30_000)
|
var modDesc = newStringOfCap(30_000)
|
||||||
renderRstToOut(d[], rst, modDesc)
|
renderRstToOut(d[], rst, modDesc)
|
||||||
d.modDesc = rope(modDesc)
|
d.modDesc = rope(modDesc)
|
||||||
|
|
|
||||||
|
|
@ -63,8 +63,8 @@ compiler gcc:
|
||||||
result = (
|
result = (
|
||||||
name: "gcc",
|
name: "gcc",
|
||||||
objExt: "o",
|
objExt: "o",
|
||||||
optSpeed: " -O3 -ffast-math ",
|
optSpeed: " -O3 ",
|
||||||
optSize: " -Os -ffast-math ",
|
optSize: " -Os ",
|
||||||
compilerExe: "gcc",
|
compilerExe: "gcc",
|
||||||
cppCompiler: "g++",
|
cppCompiler: "g++",
|
||||||
compileTmpl: "-c $options $include -o $objfile $file",
|
compileTmpl: "-c $options $include -o $objfile $file",
|
||||||
|
|
@ -88,8 +88,8 @@ compiler nintendoSwitchGCC:
|
||||||
result = (
|
result = (
|
||||||
name: "switch_gcc",
|
name: "switch_gcc",
|
||||||
objExt: "o",
|
objExt: "o",
|
||||||
optSpeed: " -O3 -ffast-math ",
|
optSpeed: " -O3 ",
|
||||||
optSize: " -Os -ffast-math ",
|
optSize: " -Os ",
|
||||||
compilerExe: "aarch64-none-elf-gcc",
|
compilerExe: "aarch64-none-elf-gcc",
|
||||||
cppCompiler: "aarch64-none-elf-g++",
|
cppCompiler: "aarch64-none-elf-g++",
|
||||||
compileTmpl: "-w -MMD -MP -MF $dfile -c $options $include -o $objfile $file",
|
compileTmpl: "-w -MMD -MP -MF $dfile -c $options $include -o $objfile $file",
|
||||||
|
|
@ -153,6 +153,13 @@ compiler vcc:
|
||||||
structStmtFmt: "$3$n$1 $2",
|
structStmtFmt: "$3$n$1 $2",
|
||||||
props: {hasCpp, hasAssume, hasDeclspec})
|
props: {hasCpp, hasAssume, hasDeclspec})
|
||||||
|
|
||||||
|
compiler clangcl:
|
||||||
|
result = vcc()
|
||||||
|
result.name = "clang_cl"
|
||||||
|
result.compilerExe = "clang-cl"
|
||||||
|
result.cppCompiler = "clang-cl"
|
||||||
|
result.linkerExe = "clang-cl"
|
||||||
|
|
||||||
# Intel C/C++ Compiler
|
# Intel C/C++ Compiler
|
||||||
compiler icl:
|
compiler icl:
|
||||||
result = vcc()
|
result = vcc()
|
||||||
|
|
@ -353,7 +360,8 @@ const
|
||||||
pcc(),
|
pcc(),
|
||||||
ucc(),
|
ucc(),
|
||||||
icl(),
|
icl(),
|
||||||
icc()]
|
icc(),
|
||||||
|
clangcl()]
|
||||||
|
|
||||||
hExt* = ".h"
|
hExt* = ".h"
|
||||||
|
|
||||||
|
|
@ -567,6 +575,8 @@ proc getCompileCFileCmd*(conf: ConfigRef; cfile: Cfile): string =
|
||||||
includeCmd = ""
|
includeCmd = ""
|
||||||
compilePattern = getCompilerExe(conf, c, cfile.cname)
|
compilePattern = getCompilerExe(conf, c, cfile.cname)
|
||||||
|
|
||||||
|
includeCmd.add(join([CC[c].includeCmd, quoteShell(conf.projectPath.string)]))
|
||||||
|
|
||||||
var cf = if noAbsolutePaths(conf): AbsoluteFile extractFilename(cfile.cname.string)
|
var cf = if noAbsolutePaths(conf): AbsoluteFile extractFilename(cfile.cname.string)
|
||||||
else: cfile.cname
|
else: cfile.cname
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -257,9 +257,9 @@ proc canon*(n: PNode; o: Operators): PNode =
|
||||||
for i in 0 ..< n.len:
|
for i in 0 ..< n.len:
|
||||||
result.sons[i] = canon(n.sons[i], o)
|
result.sons[i] = canon(n.sons[i], o)
|
||||||
elif n.kind == nkSym and n.sym.kind == skLet and
|
elif n.kind == nkSym and n.sym.kind == skLet and
|
||||||
n.sym.ast.getMagic in (someEq + someAdd + someMul + someMin +
|
n.sym.astdef.getMagic in (someEq + someAdd + someMul + someMin +
|
||||||
someMax + someHigh + {mUnaryLt} + someSub + someLen + someDiv):
|
someMax + someHigh + {mUnaryLt} + someSub + someLen + someDiv):
|
||||||
result = n.sym.ast.copyTree
|
result = n.sym.astdef.copyTree
|
||||||
else:
|
else:
|
||||||
result = n
|
result = n
|
||||||
case result.getMagic
|
case result.getMagic
|
||||||
|
|
@ -395,8 +395,8 @@ proc usefulFact(n: PNode; o: Operators): PNode =
|
||||||
# if a:
|
# if a:
|
||||||
# ...
|
# ...
|
||||||
# We make can easily replace 'a' by '2 < x' here:
|
# We make can easily replace 'a' by '2 < x' here:
|
||||||
if n.sym.ast != nil:
|
if n.sym.astdef != nil:
|
||||||
result = usefulFact(n.sym.ast, o)
|
result = usefulFact(n.sym.astdef, o)
|
||||||
elif n.kind == nkStmtListExpr:
|
elif n.kind == nkStmtListExpr:
|
||||||
result = usefulFact(n.lastSon, o)
|
result = usefulFact(n.lastSon, o)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -92,6 +92,7 @@ proc importSymbol(c: PContext, n: PNode, fromMod: PSym) =
|
||||||
rawImportSymbol(c, e)
|
rawImportSymbol(c, e)
|
||||||
e = nextIdentIter(it, fromMod.tab)
|
e = nextIdentIter(it, fromMod.tab)
|
||||||
else: rawImportSymbol(c, s)
|
else: rawImportSymbol(c, s)
|
||||||
|
suggestSym(c.config, n.info, s, c.graph.usageSym, false)
|
||||||
|
|
||||||
proc importAllSymbolsExcept(c: PContext, fromMod: PSym, exceptSet: IntSet) =
|
proc importAllSymbolsExcept(c: PContext, fromMod: PSym, exceptSet: IntSet) =
|
||||||
var i: TTabIter
|
var i: TTabIter
|
||||||
|
|
@ -161,9 +162,9 @@ proc myImportModule(c: PContext, n: PNode; importStmtResult: PNode): PSym =
|
||||||
localError(c.config, n.info, "A module cannot import itself")
|
localError(c.config, n.info, "A module cannot import itself")
|
||||||
if sfDeprecated in result.flags:
|
if sfDeprecated in result.flags:
|
||||||
if result.constraint != nil:
|
if result.constraint != nil:
|
||||||
message(c.config, n.info, warnDeprecated, result.constraint.strVal & "; " & result.name.s)
|
message(c.config, n.info, warnDeprecated, result.constraint.strVal & "; " & result.name.s & " is deprecated")
|
||||||
else:
|
else:
|
||||||
message(c.config, n.info, warnDeprecated, result.name.s)
|
message(c.config, n.info, warnDeprecated, result.name.s & " is deprecated")
|
||||||
suggestSym(c.config, n.info, result, c.graph.usageSym, false)
|
suggestSym(c.config, n.info, result, c.graph.usageSym, false)
|
||||||
importStmtResult.add newSymNode(result, n.info)
|
importStmtResult.add newSymNode(result, n.info)
|
||||||
#newStrNode(toFullPath(c.config, f), n.info)
|
#newStrNode(toFullPath(c.config, f), n.info)
|
||||||
|
|
|
||||||
|
|
@ -94,6 +94,7 @@ type
|
||||||
options: TOptions
|
options: TOptions
|
||||||
module: BModule
|
module: BModule
|
||||||
g: PGlobals
|
g: PGlobals
|
||||||
|
generatedParamCopies: IntSet
|
||||||
beforeRetNeeded: bool
|
beforeRetNeeded: bool
|
||||||
unique: int # for temp identifier generation
|
unique: int # for temp identifier generation
|
||||||
blocks: seq[TBlock]
|
blocks: seq[TBlock]
|
||||||
|
|
@ -918,35 +919,15 @@ proc countJsParams(typ: PType): int =
|
||||||
const
|
const
|
||||||
nodeKindsNeedNoCopy = {nkCharLit..nkInt64Lit, nkStrLit..nkTripleStrLit,
|
nodeKindsNeedNoCopy = {nkCharLit..nkInt64Lit, nkStrLit..nkTripleStrLit,
|
||||||
nkFloatLit..nkFloat64Lit, nkCurly, nkPar, nkStringToCString,
|
nkFloatLit..nkFloat64Lit, nkCurly, nkPar, nkStringToCString,
|
||||||
|
nkObjConstr, nkTupleConstr, nkBracket,
|
||||||
nkCStringToString, nkCall, nkPrefix, nkPostfix, nkInfix,
|
nkCStringToString, nkCall, nkPrefix, nkPostfix, nkInfix,
|
||||||
nkCommand, nkHiddenCallConv, nkCallStrLit}
|
nkCommand, nkHiddenCallConv, nkCallStrLit}
|
||||||
|
|
||||||
proc needsNoCopy(p: PProc; y: PNode): bool =
|
proc needsNoCopy(p: PProc; y: PNode): bool =
|
||||||
# if the node is a literal object constructor we have to recursively
|
return y.kind in nodeKindsNeedNoCopy or
|
||||||
# check the expressions passed into it
|
((mapType(y.typ) != etyBaseIndex or (y.kind == nkSym and y.sym.kind == skParam)) and
|
||||||
case y.kind
|
|
||||||
of nkObjConstr:
|
|
||||||
for arg in y.sons[1..^1]:
|
|
||||||
if not needsNoCopy(p, arg[1]):
|
|
||||||
return false
|
|
||||||
of nkTupleConstr:
|
|
||||||
for arg in y.sons:
|
|
||||||
var arg = arg
|
|
||||||
if arg.kind == nkExprColonExpr:
|
|
||||||
arg = arg[1]
|
|
||||||
if not needsNoCopy(p, arg):
|
|
||||||
return false
|
|
||||||
of nkBracket:
|
|
||||||
for arg in y.sons:
|
|
||||||
if not needsNoCopy(p, arg):
|
|
||||||
return false
|
|
||||||
of nodeKindsNeedNoCopy:
|
|
||||||
return true
|
|
||||||
else:
|
|
||||||
return (mapType(y.typ) != etyBaseIndex and
|
|
||||||
(skipTypes(y.typ, abstractInst).kind in
|
(skipTypes(y.typ, abstractInst).kind in
|
||||||
{tyRef, tyPtr, tyLent, tyVar, tyCString, tyProc} + IntegralTypes))
|
{tyRef, tyPtr, tyLent, tyVar, tyCString, tyProc} + IntegralTypes))
|
||||||
return true
|
|
||||||
|
|
||||||
proc genAsgnAux(p: PProc, x, y: PNode, noCopyNeeded: bool) =
|
proc genAsgnAux(p: PProc, x, y: PNode, noCopyNeeded: bool) =
|
||||||
var a, b: TCompRes
|
var a, b: TCompRes
|
||||||
|
|
@ -969,7 +950,7 @@ proc genAsgnAux(p: PProc, x, y: PNode, noCopyNeeded: bool) =
|
||||||
lineF(p, "$1 = nimCopy(null, $2, $3);$n",
|
lineF(p, "$1 = nimCopy(null, $2, $3);$n",
|
||||||
[a.rdLoc, b.res, genTypeInfo(p, y.typ)])
|
[a.rdLoc, b.res, genTypeInfo(p, y.typ)])
|
||||||
of etyObject:
|
of etyObject:
|
||||||
if (needsNoCopy(p, y) and needsNoCopy(p, x)) or noCopyNeeded:
|
if x.typ.kind == tyVar or (needsNoCopy(p, y) and needsNoCopy(p, x)) or noCopyNeeded:
|
||||||
lineF(p, "$1 = $2;$n", [a.rdLoc, b.rdLoc])
|
lineF(p, "$1 = $2;$n", [a.rdLoc, b.rdLoc])
|
||||||
else:
|
else:
|
||||||
useMagic(p, "nimCopy")
|
useMagic(p, "nimCopy")
|
||||||
|
|
@ -1252,12 +1233,29 @@ proc genProcForSymIfNeeded(p: PProc, s: PSym) =
|
||||||
if owner != nil: add(owner.locals, newp)
|
if owner != nil: add(owner.locals, newp)
|
||||||
else: attachProc(p, newp, s)
|
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)]
|
||||||
|
add(owner.locals, owner.indentLine(copy))
|
||||||
|
return
|
||||||
|
owner = owner.up
|
||||||
|
|
||||||
proc genSym(p: PProc, n: PNode, r: var TCompRes) =
|
proc genSym(p: PProc, n: PNode, r: var TCompRes) =
|
||||||
var s = n.sym
|
var s = n.sym
|
||||||
case s.kind
|
case s.kind
|
||||||
of skVar, skLet, skParam, skTemp, skResult, skForVar:
|
of skVar, skLet, skParam, skTemp, skResult, skForVar:
|
||||||
if s.loc.r == nil:
|
if s.loc.r == nil:
|
||||||
internalError(p.config, n.info, "symbol has no generated name: " & s.name.s)
|
internalError(p.config, n.info, "symbol has no generated name: " & s.name.s)
|
||||||
|
if s.kind == skParam:
|
||||||
|
genCopyForParamIfNeeded(p, n)
|
||||||
let k = mapType(p, s.typ)
|
let k = mapType(p, s.typ)
|
||||||
if k == etyBaseIndex:
|
if k == etyBaseIndex:
|
||||||
r.typ = etyBaseIndex
|
r.typ = etyBaseIndex
|
||||||
|
|
@ -1504,6 +1502,8 @@ proc createRecordVarAux(p: PProc, rec: PNode, excludedFieldIDs: IntSet, output:
|
||||||
for i in countup(1, sonsLen(rec) - 1):
|
for i in countup(1, sonsLen(rec) - 1):
|
||||||
createRecordVarAux(p, lastSon(rec.sons[i]), excludedFieldIDs, output)
|
createRecordVarAux(p, lastSon(rec.sons[i]), excludedFieldIDs, output)
|
||||||
of nkSym:
|
of nkSym:
|
||||||
|
# Do not produce code for void types
|
||||||
|
if isEmptyType(rec.sym.typ): return
|
||||||
if rec.sym.id notin excludedFieldIDs:
|
if rec.sym.id notin excludedFieldIDs:
|
||||||
if output.len > 0: output.add(", ")
|
if output.len > 0: output.add(", ")
|
||||||
output.addf("$#: ", [mangleName(p.module, rec.sym)])
|
output.addf("$#: ", [mangleName(p.module, rec.sym)])
|
||||||
|
|
@ -1886,12 +1886,13 @@ proc genMagic(p: PProc, n: PNode, r: var TCompRes) =
|
||||||
of mLtStr:
|
of mLtStr:
|
||||||
binaryExpr(p, n, r, "cmpStrings", "(cmpStrings($1, $2) < 0)")
|
binaryExpr(p, n, r, "cmpStrings", "(cmpStrings($1, $2) < 0)")
|
||||||
of mIsNil:
|
of mIsNil:
|
||||||
|
# we want to accept undefined, so we ==
|
||||||
if mapType(n[1].typ) != etyBaseIndex:
|
if mapType(n[1].typ) != etyBaseIndex:
|
||||||
unaryExpr(p, n, r, "", "($1 === null)")
|
unaryExpr(p, n, r, "", "($1 == null)")
|
||||||
else:
|
else:
|
||||||
var x: TCompRes
|
var x: TCompRes
|
||||||
gen(p, n[1], x)
|
gen(p, n[1], x)
|
||||||
r.res = "($# === null && $# === 0)" % [x.address, x.res]
|
r.res = "($# == null && $# === 0)" % [x.address, x.res]
|
||||||
of mEnumToStr: genRepr(p, n, r)
|
of mEnumToStr: genRepr(p, n, r)
|
||||||
of mNew, mNewFinalize: genNew(p, n)
|
of mNew, mNewFinalize: genNew(p, n)
|
||||||
of mChr: gen(p, n.sons[1], r)
|
of mChr: gen(p, n.sons[1], r)
|
||||||
|
|
@ -1924,7 +1925,7 @@ proc genMagic(p: PProc, n: PNode, r: var TCompRes) =
|
||||||
if optOverflowCheck notin p.options: binaryExpr(p, n, r, "", "$1 -= $2")
|
if optOverflowCheck notin p.options: binaryExpr(p, n, r, "", "$1 -= $2")
|
||||||
else: binaryExpr(p, n, r, "subInt", "$1 = subInt($3, $2)")
|
else: binaryExpr(p, n, r, "subInt", "$1 = subInt($3, $2)")
|
||||||
of mSetLengthStr:
|
of mSetLengthStr:
|
||||||
binaryExpr(p, n, r, "mnewString", "($1 === null ? $3 = mnewString($2) : $3.length = $2)")
|
binaryExpr(p, n, r, "mnewString", "($1 == null ? $3 = mnewString($2) : $3.length = $2)")
|
||||||
of mSetLengthSeq:
|
of mSetLengthSeq:
|
||||||
var x, y: TCompRes
|
var x, y: TCompRes
|
||||||
gen(p, n.sons[1], x)
|
gen(p, n.sons[1], x)
|
||||||
|
|
@ -2005,6 +2006,10 @@ proc genArrayConstr(p: PProc, n: PNode, r: var TCompRes) =
|
||||||
if a.typ == etyBaseIndex:
|
if a.typ == etyBaseIndex:
|
||||||
addf(r.res, "[$1, $2]", [a.address, a.res])
|
addf(r.res, "[$1, $2]", [a.address, a.res])
|
||||||
else:
|
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)]
|
||||||
add(r.res, a.res)
|
add(r.res, a.res)
|
||||||
add(r.res, "]")
|
add(r.res, "]")
|
||||||
|
|
||||||
|
|
@ -2017,9 +2022,13 @@ proc genTupleConstr(p: PProc, n: PNode, r: var TCompRes) =
|
||||||
var it = n.sons[i]
|
var it = n.sons[i]
|
||||||
if it.kind == nkExprColonExpr: it = it.sons[1]
|
if it.kind == nkExprColonExpr: it = it.sons[1]
|
||||||
gen(p, it, a)
|
gen(p, it, a)
|
||||||
|
let typ = it.typ.skipTypes(abstractInst)
|
||||||
if a.typ == etyBaseIndex:
|
if a.typ == etyBaseIndex:
|
||||||
addf(r.res, "Field$#: [$#, $#]", [i.rope, a.address, a.res])
|
addf(r.res, "Field$#: [$#, $#]", [i.rope, a.address, a.res])
|
||||||
else:
|
else:
|
||||||
|
if not needsNoCopy(p, it):
|
||||||
|
useMagic(p, "nimCopy")
|
||||||
|
a.res = "nimCopy(null, $1, $2)" % [a.rdLoc, genTypeInfo(p, typ)]
|
||||||
addf(r.res, "Field$#: $#", [i.rope, a.res])
|
addf(r.res, "Field$#: $#", [i.rope, a.res])
|
||||||
r.res.add("}")
|
r.res.add("}")
|
||||||
|
|
||||||
|
|
@ -2039,17 +2048,12 @@ proc genObjConstr(p: PProc, n: PNode, r: var TCompRes) =
|
||||||
fieldIDs.incl(f.id)
|
fieldIDs.incl(f.id)
|
||||||
|
|
||||||
let typ = val.typ.skipTypes(abstractInst)
|
let typ = val.typ.skipTypes(abstractInst)
|
||||||
if (typ.kind in IntegralTypes+{tyCstring, tyRef, tyPtr} and
|
|
||||||
mapType(p, typ) != etyBaseIndex) or
|
|
||||||
a.typ == etyBaseIndex or
|
|
||||||
needsNoCopy(p, it.sons[1]):
|
|
||||||
discard
|
|
||||||
else:
|
|
||||||
useMagic(p, "nimCopy")
|
|
||||||
a.res = "nimCopy(null, $1, $2)" % [a.rdLoc, genTypeInfo(p, typ)]
|
|
||||||
if a.typ == etyBaseIndex:
|
if a.typ == etyBaseIndex:
|
||||||
addf(initList, "$#: [$#, $#]", [f.loc.r, a.address, a.res])
|
addf(initList, "$#: [$#, $#]", [f.loc.r, a.address, a.res])
|
||||||
else:
|
else:
|
||||||
|
if not needsNoCopy(p, val):
|
||||||
|
useMagic(p, "nimCopy")
|
||||||
|
a.res = "nimCopy(null, $1, $2)" % [a.rdLoc, genTypeInfo(p, typ)]
|
||||||
addf(initList, "$#: $#", [f.loc.r, a.res])
|
addf(initList, "$#: $#", [f.loc.r, a.res])
|
||||||
let t = skipTypes(n.typ, abstractInst + skipPtrs)
|
let t = skipTypes(n.typ, abstractInst + skipPtrs)
|
||||||
createObjInitList(p, t, fieldIDs, initList)
|
createObjInitList(p, t, fieldIDs, initList)
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,7 @@
|
||||||
|
|
||||||
import
|
import
|
||||||
hashes, options, msgs, strutils, platform, idents, nimlexbase, llstream,
|
hashes, options, msgs, strutils, platform, idents, nimlexbase, llstream,
|
||||||
wordrecg, lineinfos, pathutils
|
wordrecg, lineinfos, pathutils, parseutils
|
||||||
|
|
||||||
const
|
const
|
||||||
MaxLineLength* = 80 # lines longer than this lead to a warning
|
MaxLineLength* = 80 # lines longer than this lead to a warning
|
||||||
|
|
@ -307,20 +307,6 @@ template tokenEndPrevious(tok, pos) =
|
||||||
when defined(nimpretty):
|
when defined(nimpretty):
|
||||||
tok.offsetB = L.offsetBase + pos
|
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 i < s.len and s[i] in {'0'..'9'}:
|
|
||||||
b = 0
|
|
||||||
while i < s.len and s[i] in {'0'..'9'}:
|
|
||||||
b = b * 10 + (ord(s[i]) - ord('0'))
|
|
||||||
inc(i)
|
|
||||||
while i < s.len and s[i] == '_': inc(i) # underscores are allowed and ignored
|
|
||||||
result = i - start
|
|
||||||
{.pop.} # overflowChecks
|
|
||||||
|
|
||||||
|
|
||||||
template eatChar(L: var TLexer, t: var TToken, replacementChar: char) =
|
template eatChar(L: var TLexer, t: var TToken, replacementChar: char) =
|
||||||
add(t.literal, replacementChar)
|
add(t.literal, replacementChar)
|
||||||
inc(L.bufpos)
|
inc(L.bufpos)
|
||||||
|
|
@ -586,33 +572,43 @@ proc getNumber(L: var TLexer, result: var TToken) =
|
||||||
of floatTypes:
|
of floatTypes:
|
||||||
result.fNumber = parseFloat(result.literal)
|
result.fNumber = parseFloat(result.literal)
|
||||||
of tkUint64Lit:
|
of tkUint64Lit:
|
||||||
xi = 0
|
var iNumber: uint64
|
||||||
let len = unsafeParseUInt(result.literal, xi)
|
var len: int
|
||||||
if len != result.literal.len or len == 0:
|
try:
|
||||||
raise newException(ValueError, "invalid integer: " & $xi)
|
len = parseBiggestUInt(result.literal, iNumber)
|
||||||
result.iNumber = xi
|
except ValueError:
|
||||||
|
raise newException(OverflowError, "number out of range: " & $result.literal)
|
||||||
|
if len != result.literal.len:
|
||||||
|
raise newException(ValueError, "invalid integer: " & $result.literal)
|
||||||
|
result.iNumber = cast[int64](iNumber)
|
||||||
else:
|
else:
|
||||||
result.iNumber = parseBiggestInt(result.literal)
|
var iNumber: int64
|
||||||
|
var len: int
|
||||||
|
try:
|
||||||
|
len = parseBiggestInt(result.literal, iNumber)
|
||||||
|
except ValueError:
|
||||||
|
raise newException(OverflowError, "number out of range: " & $result.literal)
|
||||||
|
if len != result.literal.len:
|
||||||
|
raise newException(ValueError, "invalid integer: " & $result.literal)
|
||||||
|
result.iNumber = iNumber
|
||||||
|
|
||||||
# Explicit bounds checks
|
# Explicit bounds checks. Only T.high needs to be considered
|
||||||
|
# since result.iNumber can't be negative.
|
||||||
let outOfRange =
|
let outOfRange =
|
||||||
case result.tokType
|
case result.tokType
|
||||||
of tkInt8Lit: (result.iNumber < int8.low or result.iNumber > int8.high)
|
of tkInt8Lit: result.iNumber > int8.high
|
||||||
of tkUInt8Lit: (result.iNumber < BiggestInt(uint8.low) or
|
of tkUInt8Lit: result.iNumber > BiggestInt(uint8.high)
|
||||||
result.iNumber > BiggestInt(uint8.high))
|
of tkInt16Lit: result.iNumber > int16.high
|
||||||
of tkInt16Lit: (result.iNumber < int16.low or result.iNumber > int16.high)
|
of tkUInt16Lit: result.iNumber > BiggestInt(uint16.high)
|
||||||
of tkUInt16Lit: (result.iNumber < BiggestInt(uint16.low) or
|
of tkInt32Lit: result.iNumber > int32.high
|
||||||
result.iNumber > BiggestInt(uint16.high))
|
of tkUInt32Lit: result.iNumber > BiggestInt(uint32.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
|
else: false
|
||||||
|
|
||||||
if outOfRange: lexMessageLitNum(L, "number out of range: '$1'", startpos)
|
if outOfRange: lexMessageLitNum(L, "number out of range: '$1'", startpos)
|
||||||
|
|
||||||
# Promote int literal to int64? Not always necessary, but more consistent
|
# Promote int literal to int64? Not always necessary, but more consistent
|
||||||
if result.tokType == tkIntLit:
|
if result.tokType == tkIntLit:
|
||||||
if (result.iNumber < low(int32)) or (result.iNumber > high(int32)):
|
if result.iNumber > high(int32):
|
||||||
result.tokType = tkInt64Lit
|
result.tokType = tkInt64Lit
|
||||||
|
|
||||||
except ValueError:
|
except ValueError:
|
||||||
|
|
|
||||||
|
|
@ -65,7 +65,7 @@ const
|
||||||
warnOctalEscape: "octal escape sequences do not exist; leading zero is ignored",
|
warnOctalEscape: "octal escape sequences do not exist; leading zero is ignored",
|
||||||
warnXIsNeverRead: "'$1' is never read",
|
warnXIsNeverRead: "'$1' is never read",
|
||||||
warnXmightNotBeenInit: "'$1' might not have been initialized",
|
warnXmightNotBeenInit: "'$1' might not have been initialized",
|
||||||
warnDeprecated: "$1 is deprecated",
|
warnDeprecated: "$1",
|
||||||
warnConfigDeprecated: "config file '$1' is deprecated",
|
warnConfigDeprecated: "config file '$1' is deprecated",
|
||||||
warnSmallLshouldNotBeUsed: "'l' should not be used as an identifier; may look like '1' (one)",
|
warnSmallLshouldNotBeUsed: "'l' should not be used as an identifier; may look like '1' (one)",
|
||||||
warnUnknownMagic: "unknown magic '$1' might crash the compiler",
|
warnUnknownMagic: "unknown magic '$1' might crash the compiler",
|
||||||
|
|
@ -171,7 +171,7 @@ proc computeNotesVerbosity(): array[0..3, TNoteKinds] =
|
||||||
warnGcUnsafe, hintPath, hintDependency, hintCodeBegin, hintCodeEnd,
|
warnGcUnsafe, hintPath, hintDependency, hintCodeBegin, hintCodeEnd,
|
||||||
hintSource, hintGlobalVar, hintGCStats}
|
hintSource, hintGlobalVar, hintGCStats}
|
||||||
result[0] = result[1] - {hintSuccessX, hintSuccess, hintConf,
|
result[0] = result[1] - {hintSuccessX, hintSuccess, hintConf,
|
||||||
hintProcessing, hintPattern, hintExecuting, hintLinking}
|
hintProcessing, hintPattern, hintExecuting, hintLinking, hintCC}
|
||||||
|
|
||||||
const
|
const
|
||||||
NotesVerbosity* = computeNotesVerbosity()
|
NotesVerbosity* = computeNotesVerbosity()
|
||||||
|
|
|
||||||
|
|
@ -1,28 +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 is deprecated, don't use it.
|
|
||||||
# TODO Remove this
|
|
||||||
|
|
||||||
import os
|
|
||||||
|
|
||||||
static:
|
|
||||||
echo "WARNING: imported deprecated module compiler/lists.nim, use seq ore lists from the standard library"
|
|
||||||
|
|
||||||
proc appendStr*(list: var seq[string]; data: string) {.deprecated.} =
|
|
||||||
# just use system.add
|
|
||||||
list.add(data)
|
|
||||||
|
|
||||||
proc includeStr(list: var seq[string]; data: string): bool {.deprecated.} =
|
|
||||||
if list.contains(data):
|
|
||||||
result = true
|
|
||||||
else:
|
|
||||||
result = false
|
|
||||||
list.add data
|
|
||||||
|
|
||||||
|
|
@ -89,7 +89,7 @@ proc skipAlias*(s: PSym; n: PNode; conf: ConfigRef): PSym =
|
||||||
prettybase.replaceDeprecated(conf, n.info, s, result)
|
prettybase.replaceDeprecated(conf, n.info, s, result)
|
||||||
else:
|
else:
|
||||||
message(conf, n.info, warnDeprecated, "use " & result.name.s & " instead; " &
|
message(conf, n.info, warnDeprecated, "use " & result.name.s & " instead; " &
|
||||||
s.name.s)
|
s.name.s & " is deprecated")
|
||||||
|
|
||||||
proc localSearchInScope*(c: PContext, s: PIdent): PSym =
|
proc localSearchInScope*(c: PContext, s: PIdent): PSym =
|
||||||
result = strTableGet(c.currentScope.symbols, s)
|
result = strTableGet(c.currentScope.symbols, s)
|
||||||
|
|
@ -150,7 +150,7 @@ type
|
||||||
|
|
||||||
proc getSymRepr*(conf: ConfigRef; s: PSym): string =
|
proc getSymRepr*(conf: ConfigRef; s: PSym): string =
|
||||||
case s.kind
|
case s.kind
|
||||||
of skProc, skFunc, skMethod, skConverter, skIterator:
|
of routineKinds, skType:
|
||||||
result = getProcHeader(conf, s)
|
result = getProcHeader(conf, s)
|
||||||
else:
|
else:
|
||||||
result = s.name.s
|
result = s.name.s
|
||||||
|
|
@ -174,7 +174,7 @@ proc ensureNoMissingOrUnusedSymbols(c: PContext; scope: PScope) =
|
||||||
# maybe they can be made skGenericParam as well.
|
# maybe they can be made skGenericParam as well.
|
||||||
if s.typ != nil and tfImplicitTypeParam notin s.typ.flags and
|
if s.typ != nil and tfImplicitTypeParam notin s.typ.flags and
|
||||||
s.typ.kind != tyGenericParam:
|
s.typ.kind != tyGenericParam:
|
||||||
message(c.config, s.info, hintXDeclaredButNotUsed, getSymRepr(c.config, s))
|
message(c.config, s.info, hintXDeclaredButNotUsed, s.name.s)
|
||||||
s = nextIter(it, scope.symbols)
|
s = nextIter(it, scope.symbols)
|
||||||
|
|
||||||
proc wrongRedefinition*(c: PContext; info: TLineInfo, s: string;
|
proc wrongRedefinition*(c: PContext; info: TLineInfo, s: string;
|
||||||
|
|
@ -190,7 +190,7 @@ proc addDecl*(c: PContext, sym: PSym, info: TLineInfo) =
|
||||||
wrongRedefinition(c, info, sym.name.s, conflict.info)
|
wrongRedefinition(c, info, sym.name.s, conflict.info)
|
||||||
|
|
||||||
proc addDecl*(c: PContext, sym: PSym) =
|
proc addDecl*(c: PContext, sym: PSym) =
|
||||||
let conflict = c.currentScope.addUniqueSym(sym)
|
let conflict = strTableInclReportConflict(c.currentScope.symbols, sym, true)
|
||||||
if conflict != nil:
|
if conflict != nil:
|
||||||
wrongRedefinition(c, sym.info, sym.name.s, conflict.info)
|
wrongRedefinition(c, sym.info, sym.name.s, conflict.info)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -76,6 +76,8 @@ proc commandCompileToC(graph: ModuleGraph) =
|
||||||
registerPass(graph, cgenPass)
|
registerPass(graph, cgenPass)
|
||||||
|
|
||||||
compileProject(graph)
|
compileProject(graph)
|
||||||
|
if graph.config.errorCounter > 0:
|
||||||
|
return # issue #9933
|
||||||
cgenWriteModules(graph.backend, conf)
|
cgenWriteModules(graph.backend, conf)
|
||||||
if conf.cmd != cmdRun:
|
if conf.cmd != cmdRun:
|
||||||
let proj = changeFileExt(conf.projectFull, "")
|
let proj = changeFileExt(conf.projectFull, "")
|
||||||
|
|
@ -90,6 +92,7 @@ proc commandJsonScript(graph: ModuleGraph) =
|
||||||
|
|
||||||
when not defined(leanCompiler):
|
when not defined(leanCompiler):
|
||||||
proc commandCompileToJS(graph: ModuleGraph) =
|
proc commandCompileToJS(graph: ModuleGraph) =
|
||||||
|
let conf = graph.config
|
||||||
#incl(gGlobalOptions, optSafeCode)
|
#incl(gGlobalOptions, optSafeCode)
|
||||||
setTarget(graph.config.target, osJS, cpuJS)
|
setTarget(graph.config.target, osJS, cpuJS)
|
||||||
#initDefines()
|
#initDefines()
|
||||||
|
|
@ -98,6 +101,8 @@ when not defined(leanCompiler):
|
||||||
semanticPasses(graph)
|
semanticPasses(graph)
|
||||||
registerPass(graph, JSgenPass)
|
registerPass(graph, JSgenPass)
|
||||||
compileProject(graph)
|
compileProject(graph)
|
||||||
|
if optGenScript in graph.config.globalOptions:
|
||||||
|
writeDepsFile(graph, toGeneratedFile(conf, conf.projectFull, ""))
|
||||||
|
|
||||||
proc interactivePasses(graph: ModuleGraph) =
|
proc interactivePasses(graph: ModuleGraph) =
|
||||||
initDefines(graph.config.symbols)
|
initDefines(graph.config.symbols)
|
||||||
|
|
@ -165,6 +170,7 @@ proc mainCommand*(graph: ModuleGraph) =
|
||||||
of "c", "cc", "compile", "compiletoc":
|
of "c", "cc", "compile", "compiletoc":
|
||||||
# compile means compileToC currently
|
# compile means compileToC currently
|
||||||
conf.cmd = cmdCompileToC
|
conf.cmd = cmdCompileToC
|
||||||
|
defineSymbol(graph.config.symbols, "c")
|
||||||
commandCompileToC(graph)
|
commandCompileToC(graph)
|
||||||
of "cpp", "compiletocpp":
|
of "cpp", "compiletocpp":
|
||||||
conf.cmd = cmdCompileToCpp
|
conf.cmd = cmdCompileToCpp
|
||||||
|
|
@ -281,6 +287,7 @@ proc mainCommand*(graph: ModuleGraph) =
|
||||||
(key: "defined_symbols", val: definedSymbols),
|
(key: "defined_symbols", val: definedSymbols),
|
||||||
(key: "lib_paths", val: %libpaths),
|
(key: "lib_paths", val: %libpaths),
|
||||||
(key: "out", val: %conf.outFile.string),
|
(key: "out", val: %conf.outFile.string),
|
||||||
|
(key: "nimcache", val: %getNimcacheDir(conf).string),
|
||||||
(key: "hints", val: hints),
|
(key: "hints", val: hints),
|
||||||
(key: "warnings", val: warnings),
|
(key: "warnings", val: warnings),
|
||||||
]
|
]
|
||||||
|
|
|
||||||
|
|
@ -147,7 +147,7 @@ proc getModuleName*(conf: ConfigRef; n: PNode): string =
|
||||||
# hacky way to implement 'x / y /../ z':
|
# hacky way to implement 'x / y /../ z':
|
||||||
result = renderTree(n, {renderNoComments}).replace(" ")
|
result = renderTree(n, {renderNoComments}).replace(" ")
|
||||||
of nkDotExpr:
|
of nkDotExpr:
|
||||||
localError(conf, n.info, warnDeprecated, "using '.' instead of '/' in import paths")
|
localError(conf, n.info, warnDeprecated, "using '.' instead of '/' in import paths is deprecated")
|
||||||
result = renderTree(n, {renderNoComments}).replace(".", "/")
|
result = renderTree(n, {renderNoComments}).replace(".", "/")
|
||||||
of nkImportAs:
|
of nkImportAs:
|
||||||
result = getModuleName(conf, n.sons[0])
|
result = getModuleName(conf, n.sons[0])
|
||||||
|
|
|
||||||
|
|
@ -135,6 +135,10 @@ const
|
||||||
WarningColor = fgYellow
|
WarningColor = fgYellow
|
||||||
HintTitle = "Hint: "
|
HintTitle = "Hint: "
|
||||||
HintColor = fgGreen
|
HintColor = fgGreen
|
||||||
|
# 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
|
||||||
|
ColOffset* = 1
|
||||||
|
|
||||||
proc getInfoContextLen*(conf: ConfigRef): int = return conf.m.msgContext.len
|
proc getInfoContextLen*(conf: ConfigRef): int = return conf.m.msgContext.len
|
||||||
proc setInfoContextLen*(conf: ConfigRef; L: int) = setLen(conf.m.msgContext, L)
|
proc setInfoContextLen*(conf: ConfigRef; L: int) = setLen(conf.m.msgContext, L)
|
||||||
|
|
@ -154,6 +158,9 @@ proc getInfoContext*(conf: ConfigRef; index: int): TLineInfo =
|
||||||
template toFilename*(conf: ConfigRef; fileIdx: FileIndex): string =
|
template toFilename*(conf: ConfigRef; fileIdx: FileIndex): string =
|
||||||
if fileIdx.int32 < 0 or conf == nil:
|
if fileIdx.int32 < 0 or conf == nil:
|
||||||
"???"
|
"???"
|
||||||
|
else:
|
||||||
|
if optListFullPaths in conf.globalOptions:
|
||||||
|
conf.m.fileInfos[fileIdx.int32].fullPath.string
|
||||||
else:
|
else:
|
||||||
conf.m.fileInfos[fileIdx.int32].projPath.string
|
conf.m.fileInfos[fileIdx.int32].projPath.string
|
||||||
|
|
||||||
|
|
@ -192,10 +199,10 @@ proc toMsgFilename*(conf: ConfigRef; info: TLineInfo): string =
|
||||||
result = "???"
|
result = "???"
|
||||||
return
|
return
|
||||||
let absPath = conf.m.fileInfos[info.fileIndex.int32].fullPath.string
|
let absPath = conf.m.fileInfos[info.fileIndex.int32].fullPath.string
|
||||||
let relPath = conf.m.fileInfos[info.fileIndex.int32].projPath.string
|
|
||||||
if optListFullPaths in conf.globalOptions:
|
if optListFullPaths in conf.globalOptions:
|
||||||
result = absPath
|
result = absPath
|
||||||
else:
|
else:
|
||||||
|
let relPath = conf.m.fileInfos[info.fileIndex.int32].projPath.string
|
||||||
result = if absPath.len < relPath.len: absPath else: relPath
|
result = if absPath.len < relPath.len: absPath else: relPath
|
||||||
|
|
||||||
proc toLinenumber*(info: TLineInfo): int {.inline.} =
|
proc toLinenumber*(info: TLineInfo): int {.inline.} =
|
||||||
|
|
@ -208,7 +215,9 @@ proc toFileLine*(conf: ConfigRef; info: TLineInfo): string {.inline.} =
|
||||||
result = toFilename(conf, info) & ":" & $info.line
|
result = toFilename(conf, info) & ":" & $info.line
|
||||||
|
|
||||||
proc toFileLineCol*(conf: ConfigRef; info: TLineInfo): string {.inline.} =
|
proc toFileLineCol*(conf: ConfigRef; info: TLineInfo): string {.inline.} =
|
||||||
result = toFilename(conf, info) & "(" & $info.line & ", " & $info.col & ")"
|
# consider calling `helpers.lineInfoToString` instead
|
||||||
|
result = toFilename(conf, info) & "(" & $info.line & ", " &
|
||||||
|
$(info.col + ColOffset) & ")"
|
||||||
|
|
||||||
proc `$`*(conf: ConfigRef; info: TLineInfo): string = toFileLineCol(conf, info)
|
proc `$`*(conf: ConfigRef; info: TLineInfo): string = toFileLineCol(conf, info)
|
||||||
|
|
||||||
|
|
@ -359,7 +368,7 @@ proc writeContext(conf: ConfigRef; lastinfo: TLineInfo) =
|
||||||
styledMsgWriteln(styleBright,
|
styledMsgWriteln(styleBright,
|
||||||
PosFormat % [toMsgFilename(conf, context.info),
|
PosFormat % [toMsgFilename(conf, context.info),
|
||||||
coordToStr(context.info.line.int),
|
coordToStr(context.info.line.int),
|
||||||
coordToStr(context.info.col+1)],
|
coordToStr(context.info.col+ColOffset)],
|
||||||
resetStyle,
|
resetStyle,
|
||||||
message)
|
message)
|
||||||
info = context.info
|
info = context.info
|
||||||
|
|
@ -446,7 +455,7 @@ proc formatMsg*(conf: ConfigRef; info: TLineInfo, msg: TMsgKind, arg: string): s
|
||||||
of hintMin..hintMax: HintTitle
|
of hintMin..hintMax: HintTitle
|
||||||
else: ErrorTitle
|
else: ErrorTitle
|
||||||
result = PosFormat % [toMsgFilename(conf, info), coordToStr(info.line.int),
|
result = PosFormat % [toMsgFilename(conf, info), coordToStr(info.line.int),
|
||||||
coordToStr(info.col+1)] &
|
coordToStr(info.col+ColOffset)] &
|
||||||
title &
|
title &
|
||||||
getMessageStr(msg, arg)
|
getMessageStr(msg, arg)
|
||||||
|
|
||||||
|
|
@ -483,11 +492,8 @@ proc liMessage(conf: ConfigRef; info: TLineInfo, msg: TMsgKind, arg: string,
|
||||||
color = HintColor
|
color = HintColor
|
||||||
if msg != hintUserRaw: kind = HintsToStr[ord(msg) - ord(hintMin)]
|
if msg != hintUserRaw: kind = HintsToStr[ord(msg) - ord(hintMin)]
|
||||||
inc(conf.hintCounter)
|
inc(conf.hintCounter)
|
||||||
# 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(conf, info), coordToStr(info.line.int),
|
let x = PosFormat % [toMsgFilename(conf, info), coordToStr(info.line.int),
|
||||||
coordToStr(info.col+1)]
|
coordToStr(info.col+ColOffset)]
|
||||||
let s = getMessageStr(msg, arg)
|
let s = getMessageStr(msg, arg)
|
||||||
|
|
||||||
if not ignoreMsg:
|
if not ignoreMsg:
|
||||||
|
|
|
||||||
|
|
@ -40,7 +40,8 @@ type # please make sure we have under 32 options
|
||||||
optMemTracker,
|
optMemTracker,
|
||||||
optHotCodeReloading,
|
optHotCodeReloading,
|
||||||
optLaxStrings,
|
optLaxStrings,
|
||||||
optNilSeqs
|
optNilSeqs,
|
||||||
|
optOldAst
|
||||||
|
|
||||||
TOptions* = set[TOption]
|
TOptions* = set[TOption]
|
||||||
TGlobalOption* = enum # **keep binary compatible**
|
TGlobalOption* = enum # **keep binary compatible**
|
||||||
|
|
@ -77,7 +78,7 @@ type # please make sure we have under 32 options
|
||||||
optShowAllMismatches # show all overloading resolution candidates
|
optShowAllMismatches # show all overloading resolution candidates
|
||||||
optWholeProject # for 'doc2': output any dependency
|
optWholeProject # for 'doc2': output any dependency
|
||||||
optMixedMode # true if some module triggered C++ codegen
|
optMixedMode # true if some module triggered C++ codegen
|
||||||
optListFullPaths
|
optListFullPaths # use full paths in toMsgFilename, toFilename
|
||||||
optNoNimblePath
|
optNoNimblePath
|
||||||
optDynlibOverrideAll
|
optDynlibOverrideAll
|
||||||
|
|
||||||
|
|
@ -132,7 +133,7 @@ type
|
||||||
|
|
||||||
TSystemCC* = enum
|
TSystemCC* = enum
|
||||||
ccNone, ccGcc, ccNintendoSwitch, ccLLVM_Gcc, ccCLang, ccLcc, ccBcc, ccDmc, ccWcc, ccVcc,
|
ccNone, ccGcc, ccNintendoSwitch, ccLLVM_Gcc, ccCLang, ccLcc, ccBcc, ccDmc, ccWcc, ccVcc,
|
||||||
ccTcc, ccPcc, ccUcc, ccIcl, ccIcc
|
ccTcc, ccPcc, ccUcc, ccIcl, ccIcc, ccClangCl
|
||||||
|
|
||||||
CfileFlag* {.pure.} = enum
|
CfileFlag* {.pure.} = enum
|
||||||
Cached, ## no need to recompile this time
|
Cached, ## no need to recompile this time
|
||||||
|
|
@ -482,15 +483,6 @@ proc setDefaultLibpath*(conf: ConfigRef) =
|
||||||
conf.libpath = AbsoluteDir parentNimLibPath
|
conf.libpath = AbsoluteDir parentNimLibPath
|
||||||
|
|
||||||
proc canonicalizePath*(conf: ConfigRef; path: AbsoluteFile): AbsoluteFile =
|
proc canonicalizePath*(conf: ConfigRef; path: AbsoluteFile): AbsoluteFile =
|
||||||
# on Windows, 'expandFilename' calls getFullPathName which doesn't do
|
|
||||||
# case corrections, so we have to use this convoluted way of retrieving
|
|
||||||
# the true filename (see tests/modules and Nimble uses 'import Uri' instead
|
|
||||||
# of 'import uri'):
|
|
||||||
when defined(windows):
|
|
||||||
result = AbsoluteFile path.string.expandFilename
|
|
||||||
for x in walkFiles(result.string):
|
|
||||||
return AbsoluteFile x
|
|
||||||
else:
|
|
||||||
result = AbsoluteFile path.string.expandFilename
|
result = AbsoluteFile path.string.expandFilename
|
||||||
|
|
||||||
proc shortenDir*(conf: ConfigRef; dir: string): string {.
|
proc shortenDir*(conf: ConfigRef; dir: string): string {.
|
||||||
|
|
|
||||||
|
|
@ -115,20 +115,19 @@ proc compileConstraints(p: PNode, result: var TPatternCode; conf: ConfigRef) =
|
||||||
else:
|
else:
|
||||||
patternError(p, conf)
|
patternError(p, conf)
|
||||||
|
|
||||||
proc semNodeKindConstraints*(p: PNode; conf: ConfigRef): PNode =
|
proc semNodeKindConstraints*(n: PNode; conf: ConfigRef; start: Natural): PNode =
|
||||||
## does semantic checking for a node kind pattern and compiles it into an
|
## does semantic checking for a node kind pattern and compiles it into an
|
||||||
## efficient internal format.
|
## efficient internal format.
|
||||||
assert p.kind == nkCurlyExpr
|
result = newNodeI(nkStrLit, n.info)
|
||||||
result = newNodeI(nkStrLit, p.info)
|
|
||||||
result.strVal = newStringOfCap(10)
|
result.strVal = newStringOfCap(10)
|
||||||
result.strVal.add(chr(aqNone.ord))
|
result.strVal.add(chr(aqNone.ord))
|
||||||
if p.len >= 2:
|
if n.len >= 2:
|
||||||
for i in 1..<p.len:
|
for i in start..<n.len:
|
||||||
compileConstraints(p.sons[i], result.strVal, conf)
|
compileConstraints(n[i], result.strVal, conf)
|
||||||
if result.strVal.len > MaxStackSize-1:
|
if result.strVal.len > MaxStackSize-1:
|
||||||
internalError(conf, p.info, "parameter pattern too complex")
|
internalError(conf, n.info, "parameter pattern too complex")
|
||||||
else:
|
else:
|
||||||
patternError(p, conf)
|
patternError(n, conf)
|
||||||
result.strVal.add(ppEof)
|
result.strVal.add(ppEof)
|
||||||
|
|
||||||
type
|
type
|
||||||
|
|
|
||||||
|
|
@ -332,12 +332,15 @@ proc parseSymbol(p: var TParser, mode = smNormal): PNode =
|
||||||
parMessage(p, errIdentifierExpected, p.tok)
|
parMessage(p, errIdentifierExpected, p.tok)
|
||||||
break
|
break
|
||||||
of tkOpr, tkDot, tkDotDot, tkEquals, tkParLe..tkParDotRi:
|
of tkOpr, tkDot, tkDotDot, tkEquals, tkParLe..tkParDotRi:
|
||||||
|
let lineinfo = parLineinfo(p)
|
||||||
var accm = ""
|
var accm = ""
|
||||||
while p.tok.tokType in {tkOpr, tkDot, tkDotDot, tkEquals,
|
while p.tok.tokType in {tkOpr, tkDot, tkDotDot, tkEquals,
|
||||||
tkParLe..tkParDotRi}:
|
tkParLe..tkParDotRi}:
|
||||||
accm.add(tokToStr(p.tok))
|
accm.add(tokToStr(p.tok))
|
||||||
getTok(p)
|
getTok(p)
|
||||||
result.add(newIdentNodeP(p.lex.cache.getIdent(accm), p))
|
let node = newNodeI(nkIdent, lineinfo)
|
||||||
|
node.ident = p.lex.cache.getIdent(accm)
|
||||||
|
result.add(node)
|
||||||
of tokKeywordLow..tokKeywordHigh, tkSymbol, tkIntLit..tkCharLit:
|
of tokKeywordLow..tokKeywordHigh, tkSymbol, tkIntLit..tkCharLit:
|
||||||
result.add(newIdentNodeP(p.lex.cache.getIdent(tokToStr(p.tok)), p))
|
result.add(newIdentNodeP(p.lex.cache.getIdent(tokToStr(p.tok)), p))
|
||||||
getTok(p)
|
getTok(p)
|
||||||
|
|
@ -1764,23 +1767,8 @@ proc parseSection(p: var TParser, kind: TNodeKind,
|
||||||
else:
|
else:
|
||||||
parMessage(p, errIdentifierExpected, p.tok)
|
parMessage(p, errIdentifierExpected, p.tok)
|
||||||
|
|
||||||
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, p.emptyNode)
|
|
||||||
eat(p, tkEquals)
|
|
||||||
optInd(p, result)
|
|
||||||
addSon(result, parseExpr(p))
|
|
||||||
indAndComment(p, result)
|
|
||||||
|
|
||||||
proc parseEnum(p: var TParser): PNode =
|
proc parseEnum(p: var TParser): PNode =
|
||||||
#| enum = 'enum' optInd (symbol optInd ('=' optInd expr COMMENT?)? comma?)+
|
#| enum = 'enum' optInd (symbol optPragmas optInd ('=' optInd expr COMMENT?)? comma?)+
|
||||||
result = newNodeP(nkEnumTy, p)
|
result = newNodeP(nkEnumTy, p)
|
||||||
getTok(p)
|
getTok(p)
|
||||||
addSon(result, p.emptyNode)
|
addSon(result, p.emptyNode)
|
||||||
|
|
@ -1790,25 +1778,35 @@ proc parseEnum(p: var TParser): PNode =
|
||||||
while true:
|
while true:
|
||||||
var a = parseSymbol(p)
|
var a = parseSymbol(p)
|
||||||
if a.kind == nkEmpty: return
|
if a.kind == nkEmpty: return
|
||||||
|
|
||||||
|
var symPragma = a
|
||||||
|
var pragma: PNode
|
||||||
|
if p.tok.tokType == tkCurlyDotLe:
|
||||||
|
pragma = optPragmas(p)
|
||||||
|
symPragma = newNodeP(nkPragmaExpr, p)
|
||||||
|
addSon(symPragma, a)
|
||||||
|
addSon(symPragma, pragma)
|
||||||
|
|
||||||
if p.tok.indent >= 0 and p.tok.indent <= p.currInd:
|
if p.tok.indent >= 0 and p.tok.indent <= p.currInd:
|
||||||
add(result, a)
|
add(result, symPragma)
|
||||||
break
|
break
|
||||||
|
|
||||||
if p.tok.tokType == tkEquals and p.tok.indent < 0:
|
if p.tok.tokType == tkEquals and p.tok.indent < 0:
|
||||||
getTok(p)
|
getTok(p)
|
||||||
optInd(p, a)
|
optInd(p, symPragma)
|
||||||
var b = a
|
var b = symPragma
|
||||||
a = newNodeP(nkEnumFieldDef, p)
|
symPragma = newNodeP(nkEnumFieldDef, p)
|
||||||
addSon(a, b)
|
addSon(symPragma, b)
|
||||||
addSon(a, parseExpr(p))
|
addSon(symPragma, parseExpr(p))
|
||||||
if p.tok.indent < 0 or p.tok.indent >= p.currInd:
|
if p.tok.indent < 0 or p.tok.indent >= p.currInd:
|
||||||
rawSkipComment(p, a)
|
rawSkipComment(p, symPragma)
|
||||||
if p.tok.tokType == tkComma and p.tok.indent < 0:
|
if p.tok.tokType == tkComma and p.tok.indent < 0:
|
||||||
getTok(p)
|
getTok(p)
|
||||||
rawSkipComment(p, a)
|
rawSkipComment(p, symPragma)
|
||||||
else:
|
else:
|
||||||
if p.tok.indent < 0 or p.tok.indent >= p.currInd:
|
if p.tok.indent < 0 or p.tok.indent >= p.currInd:
|
||||||
rawSkipComment(p, a)
|
rawSkipComment(p, symPragma)
|
||||||
addSon(result, a)
|
addSon(result, symPragma)
|
||||||
if p.tok.indent >= 0 and p.tok.indent <= p.currInd or
|
if p.tok.indent >= 0 and p.tok.indent <= p.currInd or
|
||||||
p.tok.tokType == tkEof:
|
p.tok.tokType == tkEof:
|
||||||
break
|
break
|
||||||
|
|
@ -1920,6 +1918,8 @@ proc parseObject(p: var TParser): PNode =
|
||||||
result = newNodeP(nkObjectTy, p)
|
result = newNodeP(nkObjectTy, p)
|
||||||
getTok(p)
|
getTok(p)
|
||||||
if p.tok.tokType == tkCurlyDotLe and p.validInd:
|
if p.tok.tokType == tkCurlyDotLe and p.validInd:
|
||||||
|
# Deprecated since v0.20.0
|
||||||
|
parMessage(p, warnDeprecated, "type pragmas follow the type name; this form of writing pragmas is deprecated")
|
||||||
addSon(result, parsePragma(p))
|
addSon(result, parsePragma(p))
|
||||||
else:
|
else:
|
||||||
addSon(result, p.emptyNode)
|
addSon(result, p.emptyNode)
|
||||||
|
|
@ -1992,13 +1992,42 @@ proc parseTypeClass(p: var TParser): PNode =
|
||||||
proc parseTypeDef(p: var TParser): PNode =
|
proc parseTypeDef(p: var TParser): PNode =
|
||||||
#|
|
#|
|
||||||
#| typeDef = identWithPragmaDot genericParamList? '=' optInd typeDefAux
|
#| typeDef = identWithPragmaDot genericParamList? '=' optInd typeDefAux
|
||||||
|
#| indAndComment? / identVisDot genericParamList? pragma '=' optInd typeDefAux
|
||||||
#| indAndComment?
|
#| indAndComment?
|
||||||
result = newNodeP(nkTypeDef, p)
|
result = newNodeP(nkTypeDef, p)
|
||||||
addSon(result, identWithPragma(p, allowDot=true))
|
var identifier = identVis(p, allowDot=true)
|
||||||
|
var identPragma = identifier
|
||||||
|
var pragma: PNode
|
||||||
|
var genericParam: PNode
|
||||||
|
var noPragmaYet = true
|
||||||
|
|
||||||
|
if p.tok.tokType == tkCurlyDotLe:
|
||||||
|
pragma = optPragmas(p)
|
||||||
|
identPragma = newNodeP(nkPragmaExpr, p)
|
||||||
|
addSon(identPragma, identifier)
|
||||||
|
addSon(identPragma, pragma)
|
||||||
|
noPragmaYet = false
|
||||||
|
|
||||||
if p.tok.tokType == tkBracketLe and p.validInd:
|
if p.tok.tokType == tkBracketLe and p.validInd:
|
||||||
addSon(result, parseGenericParamList(p))
|
if not noPragmaYet:
|
||||||
|
# Deprecated since v0.20.0
|
||||||
|
parMessage(p, warnDeprecated, "pragma before generic parameter list is deprecated")
|
||||||
|
genericParam = parseGenericParamList(p)
|
||||||
else:
|
else:
|
||||||
addSon(result, p.emptyNode)
|
genericParam = p.emptyNode
|
||||||
|
|
||||||
|
if noPragmaYet:
|
||||||
|
pragma = optPragmas(p)
|
||||||
|
if pragma.kind != nkEmpty:
|
||||||
|
identPragma = newNodeP(nkPragmaExpr, p)
|
||||||
|
addSon(identPragma, identifier)
|
||||||
|
addSon(identPragma, pragma)
|
||||||
|
elif p.tok.tokType == tkCurlyDotLe:
|
||||||
|
parMessage(p, errGenerated, "pragma already present")
|
||||||
|
|
||||||
|
addSon(result, identPragma)
|
||||||
|
addSon(result, genericParam)
|
||||||
|
|
||||||
if p.tok.tokType == tkEquals:
|
if p.tok.tokType == tkEquals:
|
||||||
result.info = parLineInfo(p)
|
result.info = parLineInfo(p)
|
||||||
getTok(p)
|
getTok(p)
|
||||||
|
|
@ -2035,6 +2064,23 @@ proc parseVariable(p: var TParser): PNode =
|
||||||
result[^1] = postExprBlocks(p, result[^1])
|
result[^1] = postExprBlocks(p, result[^1])
|
||||||
indAndComment(p, result)
|
indAndComment(p, result)
|
||||||
|
|
||||||
|
proc parseConstant(p: var TParser): PNode =
|
||||||
|
#| constant = (parseVarTuple / identWithPragma) (colon typeDesc)? '=' optInd expr indAndComment
|
||||||
|
if p.tok.tokType == tkParLe: result = parseVarTuple(p)
|
||||||
|
else:
|
||||||
|
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, p.emptyNode)
|
||||||
|
eat(p, tkEquals)
|
||||||
|
optInd(p, result)
|
||||||
|
addSon(result, parseExpr(p))
|
||||||
|
indAndComment(p, result)
|
||||||
|
|
||||||
proc parseBind(p: var TParser, k: TNodeKind): PNode =
|
proc parseBind(p: var TParser, k: TNodeKind): PNode =
|
||||||
#| bindStmt = 'bind' optInd qualifiedIdent ^+ comma
|
#| bindStmt = 'bind' optInd qualifiedIdent ^+ comma
|
||||||
#| mixinStmt = 'mixin' optInd qualifiedIdent ^+ comma
|
#| mixinStmt = 'mixin' optInd qualifiedIdent ^+ comma
|
||||||
|
|
|
||||||
|
|
@ -102,9 +102,9 @@ proc processImplicits(graph: ModuleGraph; implicits: seq[string], nodeKind: TNod
|
||||||
for module in items(implicits):
|
for module in items(implicits):
|
||||||
# implicit imports should not lead to a module importing itself
|
# implicit imports should not lead to a module importing itself
|
||||||
if m.position != resolveMod(graph.config, module, relativeTo).int32:
|
if m.position != resolveMod(graph.config, module, relativeTo).int32:
|
||||||
var importStmt = newNodeI(nodeKind, gCmdLineInfo)
|
var importStmt = newNodeI(nodeKind, m.info)
|
||||||
var str = newStrNode(nkStrLit, module)
|
var str = newStrNode(nkStrLit, module)
|
||||||
str.info = gCmdLineInfo
|
str.info = m.info
|
||||||
importStmt.addSon str
|
importStmt.addSon str
|
||||||
if not processTopLevelStmt(graph, importStmt, a): break
|
if not processTopLevelStmt(graph, importStmt, a): break
|
||||||
|
|
||||||
|
|
@ -155,7 +155,7 @@ proc processModule*(graph: ModuleGraph; module: PSym, stream: PLLStream): bool {
|
||||||
while true:
|
while true:
|
||||||
openParsers(p, fileIdx, s, graph.cache, graph.config)
|
openParsers(p, fileIdx, s, graph.cache, graph.config)
|
||||||
|
|
||||||
if sfSystemModule notin module.flags:
|
if module.owner == nil or module.owner.name.s != "stdlib" or module.name.s == "distros":
|
||||||
# XXX what about caching? no processing then? what if I change the
|
# XXX what about caching? no processing then? what if I change the
|
||||||
# modules to include between compilation runs? we'd need to track that
|
# modules to include between compilation runs? we'd need to track that
|
||||||
# in ROD files. I think we should enable this feature only
|
# in ROD files. I think we should enable this feature only
|
||||||
|
|
|
||||||
|
|
@ -17,11 +17,9 @@ type
|
||||||
AbsoluteDir* = distinct string
|
AbsoluteDir* = distinct string
|
||||||
RelativeFile* = distinct string
|
RelativeFile* = distinct string
|
||||||
RelativeDir* = distinct string
|
RelativeDir* = distinct string
|
||||||
|
AnyPath* = AbsoluteFile|AbsoluteDir|RelativeFile|RelativeDir
|
||||||
|
|
||||||
proc isEmpty*(x: AbsoluteFile): bool {.inline.} = x.string.len == 0
|
proc isEmpty*(x: AnyPath): bool {.inline.} = x.string.len == 0
|
||||||
proc isEmpty*(x: AbsoluteDir): bool {.inline.} = x.string.len == 0
|
|
||||||
proc isEmpty*(x: RelativeFile): bool {.inline.} = x.string.len == 0
|
|
||||||
proc isEmpty*(x: RelativeDir): bool {.inline.} = x.string.len == 0
|
|
||||||
|
|
||||||
proc copyFile*(source, dest: AbsoluteFile) =
|
proc copyFile*(source, dest: AbsoluteFile) =
|
||||||
os.copyFile(source.string, dest.string)
|
os.copyFile(source.string, dest.string)
|
||||||
|
|
@ -44,14 +42,13 @@ proc cmpPaths*(x, y: AbsoluteDir): int {.borrow.}
|
||||||
|
|
||||||
proc createDir*(x: AbsoluteDir) {.borrow.}
|
proc createDir*(x: AbsoluteDir) {.borrow.}
|
||||||
|
|
||||||
|
proc `$`*(x: AnyPath): string = x.string
|
||||||
|
|
||||||
when true:
|
when true:
|
||||||
proc eqImpl(x, y: string): bool {.inline.} =
|
proc eqImpl(x, y: string): bool {.inline.} =
|
||||||
result = cmpPaths(x, y) == 0
|
result = cmpPaths(x, y) == 0
|
||||||
|
|
||||||
proc `==`*(x, y: AbsoluteFile): bool = eqImpl(x.string, y.string)
|
proc `==`*[T: AnyPath](x, y: T): bool = eqImpl(x.string, y.string)
|
||||||
proc `==`*(x, y: AbsoluteDir): bool = eqImpl(x.string, y.string)
|
|
||||||
proc `==`*(x, y: RelativeFile): bool = eqImpl(x.string, y.string)
|
|
||||||
proc `==`*(x, y: RelativeDir): bool = eqImpl(x.string, y.string)
|
|
||||||
|
|
||||||
proc `/`*(base: AbsoluteDir; f: RelativeFile): AbsoluteFile =
|
proc `/`*(base: AbsoluteDir; f: RelativeFile): AbsoluteFile =
|
||||||
#assert isAbsolute(base.string)
|
#assert isAbsolute(base.string)
|
||||||
|
|
@ -88,6 +85,12 @@ when true:
|
||||||
when isMainModule:
|
when isMainModule:
|
||||||
doAssert AbsoluteDir"/Users/me///" / RelativeFile"z.nim" == AbsoluteFile"/Users/me/z.nim"
|
doAssert AbsoluteDir"/Users/me///" / RelativeFile"z.nim" == AbsoluteFile"/Users/me/z.nim"
|
||||||
doAssert relativePath("/foo/bar.nim", "/foo/", '/') == "bar.nim"
|
doAssert relativePath("/foo/bar.nim", "/foo/", '/') == "bar.nim"
|
||||||
|
doAssert $RelativeDir"foo/bar" == "foo/bar"
|
||||||
|
doAssert RelativeDir"foo/bar" == RelativeDir"foo/bar"
|
||||||
|
doAssert RelativeFile"foo/bar".changeFileExt(".txt") == RelativeFile"foo/bar.txt"
|
||||||
|
doAssert RelativeFile"foo/bar".addFileExt(".txt") == RelativeFile"foo/bar.txt"
|
||||||
|
doAssert not RelativeDir"foo/bar".isEmpty
|
||||||
|
doAssert RelativeDir"".isEmpty
|
||||||
|
|
||||||
when isMainModule and defined(windows):
|
when isMainModule and defined(windows):
|
||||||
let nasty = string(AbsoluteDir(r"C:\Users\rumpf\projects\nim\tests\nimble\nimbleDir\linkedPkgs\pkgB-#head\../../simplePkgs/pkgB-#head/") / RelativeFile"pkgA/module.nim")
|
let nasty = string(AbsoluteDir(r"C:\Users\rumpf\projects\nim\tests\nimble\nimbleDir\linkedPkgs\pkgB-#head\../../simplePkgs/pkgB-#head/") / RelativeFile"pkgA/module.nim")
|
||||||
|
|
|
||||||
|
|
@ -73,6 +73,7 @@ const
|
||||||
wThread, wRaises, wLocks, wTags, wGcSafe}
|
wThread, wRaises, wLocks, wTags, wGcSafe}
|
||||||
forVarPragmas* = {wInject, wGensym}
|
forVarPragmas* = {wInject, wGensym}
|
||||||
allRoutinePragmas* = methodPragmas + iteratorPragmas + lambdaPragmas
|
allRoutinePragmas* = methodPragmas + iteratorPragmas + lambdaPragmas
|
||||||
|
enumFieldPragmas* = {wDeprecated}
|
||||||
|
|
||||||
proc getPragmaVal*(procAst: PNode; name: TSpecialWord): PNode =
|
proc getPragmaVal*(procAst: PNode; name: TSpecialWord): PNode =
|
||||||
let p = procAst[pragmasPos]
|
let p = procAst[pragmasPos]
|
||||||
|
|
@ -241,7 +242,7 @@ proc pragmaNoForward(c: PContext, n: PNode; flag=sfNoForward) =
|
||||||
# deprecated as of 0.18.1
|
# deprecated as of 0.18.1
|
||||||
message(c.config, n.info, warnDeprecated,
|
message(c.config, n.info, warnDeprecated,
|
||||||
"use {.experimental: \"codeReordering.\".} instead; " &
|
"use {.experimental: \"codeReordering.\".} instead; " &
|
||||||
(if flag == sfNoForward: "{.noForward.}" else: "{.reorder.}"))
|
(if flag == sfNoForward: "{.noForward.}" else: "{.reorder.}") & " is deprecated")
|
||||||
|
|
||||||
proc processCallConv(c: PContext, n: PNode) =
|
proc processCallConv(c: PContext, n: PNode) =
|
||||||
if n.kind in nkPragmaCallKinds and n.len == 2 and n.sons[1].kind == nkIdent:
|
if n.kind in nkPragmaCallKinds and n.len == 2 and n.sons[1].kind == nkIdent:
|
||||||
|
|
@ -446,14 +447,14 @@ proc processPop(c: PContext, n: PNode) =
|
||||||
proc processDefine(c: PContext, n: PNode) =
|
proc processDefine(c: PContext, n: PNode) =
|
||||||
if (n.kind in nkPragmaCallKinds and n.len == 2) and (n[1].kind == nkIdent):
|
if (n.kind in nkPragmaCallKinds and n.len == 2) and (n[1].kind == nkIdent):
|
||||||
defineSymbol(c.config.symbols, n[1].ident.s)
|
defineSymbol(c.config.symbols, n[1].ident.s)
|
||||||
message(c.config, n.info, warnDeprecated, "define")
|
message(c.config, n.info, warnDeprecated, "define is deprecated")
|
||||||
else:
|
else:
|
||||||
invalidPragma(c, n)
|
invalidPragma(c, n)
|
||||||
|
|
||||||
proc processUndef(c: PContext, n: PNode) =
|
proc processUndef(c: PContext, n: PNode) =
|
||||||
if (n.kind in nkPragmaCallKinds and n.len == 2) and (n[1].kind == nkIdent):
|
if (n.kind in nkPragmaCallKinds and n.len == 2) and (n[1].kind == nkIdent):
|
||||||
undefSymbol(c.config.symbols, n[1].ident.s)
|
undefSymbol(c.config.symbols, n[1].ident.s)
|
||||||
message(c.config, n.info, warnDeprecated, "undef")
|
message(c.config, n.info, warnDeprecated, "undef is deprecated")
|
||||||
else:
|
else:
|
||||||
invalidPragma(c, n)
|
invalidPragma(c, n)
|
||||||
|
|
||||||
|
|
@ -740,7 +741,7 @@ proc semCustomPragma(c: PContext, n: PNode): PNode =
|
||||||
result.kind = n.kind # pragma(arg) -> pragma: arg
|
result.kind = n.kind # pragma(arg) -> pragma: arg
|
||||||
|
|
||||||
proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int,
|
proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int,
|
||||||
validPragmas: TSpecialWords): bool =
|
validPragmas: TSpecialWords, comesFromPush: bool) : bool =
|
||||||
var it = n.sons[i]
|
var it = n.sons[i]
|
||||||
var key = if it.kind in nkPragmaCallKinds and it.len > 1: it.sons[0] else: it
|
var key = if it.kind in nkPragmaCallKinds and it.len > 1: it.sons[0] else: it
|
||||||
if key.kind == nkBracketExpr:
|
if key.kind == nkBracketExpr:
|
||||||
|
|
@ -783,7 +784,7 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int,
|
||||||
if sym.kind in {skTemplate, skMacro}:
|
if sym.kind in {skTemplate, skMacro}:
|
||||||
incl(sym.flags, sfImmediate)
|
incl(sym.flags, sfImmediate)
|
||||||
incl(sym.flags, sfAllUntyped)
|
incl(sym.flags, sfAllUntyped)
|
||||||
message(c.config, n.info, warnDeprecated, "use 'untyped' parameters instead; immediate")
|
message(c.config, n.info, warnDeprecated, "use 'untyped' parameters instead; immediate is deprecated")
|
||||||
else: invalidPragma(c, it)
|
else: invalidPragma(c, it)
|
||||||
of wDirty:
|
of wDirty:
|
||||||
if sym.kind == skTemplate: incl(sym.flags, sfDirty)
|
if sym.kind == skTemplate: incl(sym.flags, sfDirty)
|
||||||
|
|
@ -880,7 +881,7 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int,
|
||||||
of wExplain:
|
of wExplain:
|
||||||
sym.flags.incl sfExplain
|
sym.flags.incl sfExplain
|
||||||
of wDeprecated:
|
of wDeprecated:
|
||||||
if sym != nil and sym.kind in routineKinds + {skType}:
|
if sym != nil and sym.kind in routineKinds + {skType, skVar, skLet}:
|
||||||
if it.kind in nkPragmaCallKinds: discard getStrLitNode(c, it)
|
if it.kind in nkPragmaCallKinds: discard getStrLitNode(c, it)
|
||||||
incl(sym.flags, sfDeprecated)
|
incl(sym.flags, sfDeprecated)
|
||||||
elif sym != nil and sym.kind != skModule:
|
elif sym != nil and sym.kind != skModule:
|
||||||
|
|
@ -1090,10 +1091,10 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int,
|
||||||
of wThis:
|
of wThis:
|
||||||
if it.kind in nkPragmaCallKinds and it.len == 2:
|
if it.kind in nkPragmaCallKinds and it.len == 2:
|
||||||
c.selfName = considerQuotedIdent(c, it[1])
|
c.selfName = considerQuotedIdent(c, it[1])
|
||||||
message(c.config, n.info, warnDeprecated, "the '.this' pragma")
|
message(c.config, n.info, warnDeprecated, "the '.this' pragma is deprecated")
|
||||||
elif it.kind == nkIdent or it.len == 1:
|
elif it.kind == nkIdent or it.len == 1:
|
||||||
c.selfName = getIdent(c.cache, "self")
|
c.selfName = getIdent(c.cache, "self")
|
||||||
message(c.config, n.info, warnDeprecated, "the '.this' pragma")
|
message(c.config, n.info, warnDeprecated, "the '.this' pragma is deprecated")
|
||||||
else:
|
else:
|
||||||
localError(c.config, it.info, "'this' pragma is allowed to have zero or one arguments")
|
localError(c.config, it.info, "'this' pragma is allowed to have zero or one arguments")
|
||||||
of wNoRewrite:
|
of wNoRewrite:
|
||||||
|
|
@ -1111,7 +1112,10 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int,
|
||||||
else: sym.flags.incl sfUsed
|
else: sym.flags.incl sfUsed
|
||||||
of wLiftLocals: discard
|
of wLiftLocals: discard
|
||||||
else: invalidPragma(c, it)
|
else: invalidPragma(c, it)
|
||||||
elif sym == nil or (sym != nil and sym.kind in {skVar, skLet, skParam,
|
elif comesFromPush and whichKeyword(ident) in {wTags, wRaises}:
|
||||||
|
discard "ignore the .push pragma; it doesn't apply"
|
||||||
|
else:
|
||||||
|
if sym == nil or (sym != nil and sym.kind in {skVar, skLet, skParam,
|
||||||
skField, skProc, skFunc, skConverter, skMethod, skType}):
|
skField, skProc, skFunc, skConverter, skMethod, skType}):
|
||||||
n.sons[i] = semCustomPragma(c, it)
|
n.sons[i] = semCustomPragma(c, it)
|
||||||
elif sym != nil:
|
elif sym != nil:
|
||||||
|
|
@ -1119,7 +1123,14 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int,
|
||||||
else:
|
else:
|
||||||
invalidPragma(c, it)
|
invalidPragma(c, it)
|
||||||
|
|
||||||
|
proc overwriteLineInfo(n: PNode; info: TLineInfo) =
|
||||||
|
n.info = info
|
||||||
|
for i in 0..<safeLen(n):
|
||||||
|
overwriteLineInfo(n[i], info)
|
||||||
|
|
||||||
proc mergePragmas(n, pragmas: PNode) =
|
proc mergePragmas(n, pragmas: PNode) =
|
||||||
|
var pragmas = copyTree(pragmas)
|
||||||
|
overwriteLineInfo pragmas, n.info
|
||||||
if n[pragmasPos].kind == nkEmpty:
|
if n[pragmasPos].kind == nkEmpty:
|
||||||
n[pragmasPos] = pragmas
|
n[pragmasPos] = pragmas
|
||||||
else:
|
else:
|
||||||
|
|
@ -1134,7 +1145,7 @@ proc implicitPragmas*(c: PContext, sym: PSym, n: PNode,
|
||||||
pushInfoContext(c.config, n.info)
|
pushInfoContext(c.config, n.info)
|
||||||
var i = 0
|
var i = 0
|
||||||
while i < o.len:
|
while i < o.len:
|
||||||
if singlePragma(c, sym, o, i, validPragmas):
|
if singlePragma(c, sym, o, i, validPragmas, true):
|
||||||
internalError(c.config, n.info, "implicitPragmas")
|
internalError(c.config, n.info, "implicitPragmas")
|
||||||
inc i
|
inc i
|
||||||
popInfoContext(c.config)
|
popInfoContext(c.config)
|
||||||
|
|
@ -1163,7 +1174,7 @@ proc pragmaRec(c: PContext, sym: PSym, n: PNode, validPragmas: TSpecialWords) =
|
||||||
if n == nil: return
|
if n == nil: return
|
||||||
var i = 0
|
var i = 0
|
||||||
while i < n.len:
|
while i < n.len:
|
||||||
if singlePragma(c, sym, n, i, validPragmas): break
|
if singlePragma(c, sym, n, i, validPragmas, false): break
|
||||||
inc i
|
inc i
|
||||||
|
|
||||||
proc pragma(c: PContext, sym: PSym, n: PNode, validPragmas: TSpecialWords) =
|
proc pragma(c: PContext, sym: PSym, n: PNode, validPragmas: TSpecialWords) =
|
||||||
|
|
|
||||||
|
|
@ -42,15 +42,18 @@ proc setupVM*(module: PSym; cache: IdentCache; scriptName: string;
|
||||||
proc (a: VmArgs) =
|
proc (a: VmArgs) =
|
||||||
body
|
body
|
||||||
|
|
||||||
template cbos(name, body) {.dirty.} =
|
template cbexc(name, exc, body) {.dirty.} =
|
||||||
result.registerCallback "stdlib.system." & astToStr(name),
|
result.registerCallback "stdlib.system." & astToStr(name),
|
||||||
proc (a: VmArgs) =
|
proc (a: VmArgs) =
|
||||||
errorMsg = ""
|
errorMsg = ""
|
||||||
try:
|
try:
|
||||||
body
|
body
|
||||||
except OSError:
|
except exc:
|
||||||
errorMsg = getCurrentExceptionMsg()
|
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
|
# Idea: Treat link to file as a file, but ignore link to directory to prevent
|
||||||
# endless recursions out of the box.
|
# endless recursions out of the box.
|
||||||
cbos listFiles:
|
cbos listFiles:
|
||||||
|
|
@ -63,8 +66,10 @@ proc setupVM*(module: PSym; cache: IdentCache; scriptName: string;
|
||||||
os.removeFile getString(a, 0)
|
os.removeFile getString(a, 0)
|
||||||
cbos createDir:
|
cbos createDir:
|
||||||
os.createDir getString(a, 0)
|
os.createDir getString(a, 0)
|
||||||
cbos getOsError:
|
|
||||||
setResult(a, errorMsg)
|
result.registerCallback "stdlib.system.getError",
|
||||||
|
proc (a: VmArgs) = setResult(a, errorMsg)
|
||||||
|
|
||||||
cbos setCurrentDir:
|
cbos setCurrentDir:
|
||||||
os.setCurrentDir getString(a, 0)
|
os.setCurrentDir getString(a, 0)
|
||||||
cbos getCurrentDir:
|
cbos getCurrentDir:
|
||||||
|
|
@ -155,6 +160,12 @@ proc setupVM*(module: PSym; cache: IdentCache; scriptName: string;
|
||||||
setResult(a, os.getAppFilename())
|
setResult(a, os.getAppFilename())
|
||||||
cbconf cppDefine:
|
cbconf cppDefine:
|
||||||
options.cppDefine(conf, a.getString(0))
|
options.cppDefine(conf, a.getString(0))
|
||||||
|
cbexc stdinReadLine, EOFError:
|
||||||
|
setResult(a, "")
|
||||||
|
setResult(a, stdin.readLine())
|
||||||
|
cbexc stdinReadAll, EOFError:
|
||||||
|
setResult(a, "")
|
||||||
|
setResult(a, stdin.readAll())
|
||||||
|
|
||||||
proc runNimScript*(cache: IdentCache; scriptName: AbsoluteFile;
|
proc runNimScript*(cache: IdentCache; scriptName: AbsoluteFile;
|
||||||
freshDefines=true; conf: ConfigRef) =
|
freshDefines=true; conf: ConfigRef) =
|
||||||
|
|
|
||||||
|
|
@ -556,8 +556,6 @@ proc isEmptyTree(n: PNode): bool =
|
||||||
else: result = false
|
else: result = false
|
||||||
|
|
||||||
proc semStmtAndGenerateGenerics(c: PContext, n: PNode): PNode =
|
proc semStmtAndGenerateGenerics(c: PContext, n: PNode): PNode =
|
||||||
if n.kind == nkDefer:
|
|
||||||
localError(c.config, n.info, "defer statement not supported at top level")
|
|
||||||
if c.topStmts == 0 and not isImportSystemStmt(c.graph, n):
|
if c.topStmts == 0 and not isImportSystemStmt(c.graph, n):
|
||||||
if sfSystemModule notin c.module.flags and not isEmptyTree(n):
|
if sfSystemModule notin c.module.flags and not isEmptyTree(n):
|
||||||
c.importTable.addSym c.graph.systemModule # import the "System" identifier
|
c.importTable.addSym c.graph.systemModule # import the "System" identifier
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,7 @@
|
||||||
|
|
||||||
type
|
type
|
||||||
TLiftCtx = object
|
TLiftCtx = object
|
||||||
c: PContext
|
graph: ModuleGraph
|
||||||
info: TLineInfo # for construction
|
info: TLineInfo # for construction
|
||||||
kind: TTypeAttachedOp
|
kind: TTypeAttachedOp
|
||||||
fn: PSym
|
fn: PSym
|
||||||
|
|
@ -22,7 +22,7 @@ type
|
||||||
recurse: bool
|
recurse: bool
|
||||||
|
|
||||||
proc liftBodyAux(c: var TLiftCtx; t: PType; body, x, y: PNode)
|
proc liftBodyAux(c: var TLiftCtx; t: PType; body, x, y: PNode)
|
||||||
proc liftBody(c: PContext; typ: PType; kind: TTypeAttachedOp;
|
proc liftBody(g: ModuleGraph; typ: PType; kind: TTypeAttachedOp;
|
||||||
info: TLineInfo): PSym {.discardable.}
|
info: TLineInfo): PSym {.discardable.}
|
||||||
|
|
||||||
proc at(a, i: PNode, elemType: PType): PNode =
|
proc at(a, i: PNode, elemType: PType): PNode =
|
||||||
|
|
@ -33,7 +33,7 @@ proc at(a, i: PNode, elemType: PType): PNode =
|
||||||
|
|
||||||
proc liftBodyTup(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
proc liftBodyTup(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||||
for i in 0 ..< t.len:
|
for i in 0 ..< t.len:
|
||||||
let lit = lowerings.newIntLit(c.c.graph, x.info, i)
|
let lit = lowerings.newIntLit(c.graph, x.info, i)
|
||||||
liftBodyAux(c, t.sons[i], body, x.at(lit, t.sons[i]), y.at(lit, t.sons[i]))
|
liftBodyAux(c, t.sons[i], body, x.at(lit, t.sons[i]), y.at(lit, t.sons[i]))
|
||||||
|
|
||||||
proc dotField(x: PNode, f: PSym): PNode =
|
proc dotField(x: PNode, f: PSym): PNode =
|
||||||
|
|
@ -49,6 +49,14 @@ proc liftBodyObj(c: var TLiftCtx; n, body, x, y: PNode) =
|
||||||
liftBodyAux(c, f.typ, body, x.dotField(f), y.dotField(f))
|
liftBodyAux(c, f.typ, body, x.dotField(f), y.dotField(f))
|
||||||
of nkNilLit: discard
|
of nkNilLit: discard
|
||||||
of nkRecCase:
|
of nkRecCase:
|
||||||
|
if c.kind in {attachedSink, attachedAsgn, attachedDeepCopy}:
|
||||||
|
## the value needs to be destroyed before we assign the selector
|
||||||
|
## or the value is lost
|
||||||
|
let prevKind = c.kind
|
||||||
|
c.kind = attachedDestructor
|
||||||
|
liftBodyObj(c, n, body, x, y)
|
||||||
|
c.kind = prevKind
|
||||||
|
|
||||||
# copy the selector:
|
# copy the selector:
|
||||||
liftBodyObj(c, n[0], body, x, y)
|
liftBodyObj(c, n[0], body, x, y)
|
||||||
# we need to generate a case statement:
|
# we need to generate a case statement:
|
||||||
|
|
@ -66,26 +74,25 @@ proc liftBodyObj(c: var TLiftCtx; n, body, x, y: PNode) =
|
||||||
liftBodyObj(c, n[i].lastSon, branch.sons[L-1], x, y)
|
liftBodyObj(c, n[i].lastSon, branch.sons[L-1], x, y)
|
||||||
caseStmt.add(branch)
|
caseStmt.add(branch)
|
||||||
body.add(caseStmt)
|
body.add(caseStmt)
|
||||||
localError(c.c.config, c.info, "cannot lift assignment operator to 'case' object")
|
|
||||||
of nkRecList:
|
of nkRecList:
|
||||||
for t in items(n): liftBodyObj(c, t, body, x, y)
|
for t in items(n): liftBodyObj(c, t, body, x, y)
|
||||||
else:
|
else:
|
||||||
illFormedAstLocal(n, c.c.config)
|
illFormedAstLocal(n, c.graph.config)
|
||||||
|
|
||||||
proc genAddr(c: PContext; x: PNode): PNode =
|
proc genAddr(g: ModuleGraph; x: PNode): PNode =
|
||||||
if x.kind == nkHiddenDeref:
|
if x.kind == nkHiddenDeref:
|
||||||
checkSonsLen(x, 1, c.config)
|
checkSonsLen(x, 1, g.config)
|
||||||
result = x.sons[0]
|
result = x.sons[0]
|
||||||
else:
|
else:
|
||||||
result = newNodeIT(nkHiddenAddr, x.info, makeVarType(c, x.typ))
|
result = newNodeIT(nkHiddenAddr, x.info, makeVarType(x.typ.owner, x.typ))
|
||||||
addSon(result, x)
|
addSon(result, x)
|
||||||
|
|
||||||
proc newAsgnCall(c: PContext; op: PSym; x, y: PNode): PNode =
|
proc newAsgnCall(g: ModuleGraph; op: PSym; x, y: PNode): PNode =
|
||||||
#if sfError in op.flags:
|
#if sfError in op.flags:
|
||||||
# localError(c.config, x.info, "usage of '$1' is a user-defined error" % op.name.s)
|
# localError(c.config, x.info, "usage of '$1' is a user-defined error" % op.name.s)
|
||||||
result = newNodeI(nkCall, x.info)
|
result = newNodeI(nkCall, x.info)
|
||||||
result.add newSymNode(op)
|
result.add newSymNode(op)
|
||||||
result.add genAddr(c, x)
|
result.add genAddr(g, x)
|
||||||
result.add y
|
result.add y
|
||||||
|
|
||||||
proc newAsgnStmt(le, ri: PNode): PNode =
|
proc newAsgnStmt(le, ri: PNode): PNode =
|
||||||
|
|
@ -98,10 +105,10 @@ proc newOpCall(op: PSym; x: PNode): PNode =
|
||||||
result.add(newSymNode(op))
|
result.add(newSymNode(op))
|
||||||
result.add x
|
result.add x
|
||||||
|
|
||||||
proc destructorCall(c: PContext; op: PSym; x: PNode): PNode =
|
proc destructorCall(g: ModuleGraph; op: PSym; x: PNode): PNode =
|
||||||
result = newNodeIT(nkCall, x.info, op.typ.sons[0])
|
result = newNodeIT(nkCall, x.info, op.typ.sons[0])
|
||||||
result.add(newSymNode(op))
|
result.add(newSymNode(op))
|
||||||
result.add genAddr(c, x)
|
result.add genAddr(g, x)
|
||||||
|
|
||||||
proc newDeepCopyCall(op: PSym; x, y: PNode): PNode =
|
proc newDeepCopyCall(op: PSym; x, y: PNode): PNode =
|
||||||
result = newAsgnStmt(x, newOpCall(op, y))
|
result = newAsgnStmt(x, newOpCall(op, y))
|
||||||
|
|
@ -120,13 +127,13 @@ proc considerAsgnOrSink(c: var TLiftCtx; t: PType; body, x, y: PNode;
|
||||||
else:
|
else:
|
||||||
op = field
|
op = field
|
||||||
if op == nil:
|
if op == nil:
|
||||||
op = liftBody(c.c, t, c.kind, c.info)
|
op = liftBody(c.graph, t, c.kind, c.info)
|
||||||
if sfError in op.flags:
|
if sfError in op.flags:
|
||||||
incl c.fn.flags, sfError
|
incl c.fn.flags, sfError
|
||||||
else:
|
else:
|
||||||
markUsed(c.c.config, c.info, op, c.c.graph.usageSym)
|
markUsed(c.graph.config, c.info, op, c.graph.usageSym)
|
||||||
onUse(c.info, op)
|
onUse(c.info, op)
|
||||||
body.add newAsgnCall(c.c, op, x, y)
|
body.add newAsgnCall(c.graph, op, x, y)
|
||||||
result = true
|
result = true
|
||||||
|
|
||||||
proc considerOverloadedOp(c: var TLiftCtx; t: PType; body, x, y: PNode): bool =
|
proc considerOverloadedOp(c: var TLiftCtx; t: PType; body, x, y: PNode): bool =
|
||||||
|
|
@ -134,9 +141,9 @@ proc considerOverloadedOp(c: var TLiftCtx; t: PType; body, x, y: PNode): bool =
|
||||||
of attachedDestructor:
|
of attachedDestructor:
|
||||||
let op = t.destructor
|
let op = t.destructor
|
||||||
if op != nil:
|
if op != nil:
|
||||||
markUsed(c.c.config, c.info, op, c.c.graph.usageSym)
|
markUsed(c.graph.config, c.info, op, c.graph.usageSym)
|
||||||
onUse(c.info, op)
|
onUse(c.info, op)
|
||||||
body.add destructorCall(c.c, op, x)
|
body.add destructorCall(c.graph, op, x)
|
||||||
result = true
|
result = true
|
||||||
of attachedAsgn:
|
of attachedAsgn:
|
||||||
result = considerAsgnOrSink(c, t, body, x, y, t.assignment)
|
result = considerAsgnOrSink(c, t, body, x, y, t.assignment)
|
||||||
|
|
@ -145,7 +152,7 @@ proc considerOverloadedOp(c: var TLiftCtx; t: PType; body, x, y: PNode): bool =
|
||||||
of attachedDeepCopy:
|
of attachedDeepCopy:
|
||||||
let op = t.deepCopy
|
let op = t.deepCopy
|
||||||
if op != nil:
|
if op != nil:
|
||||||
markUsed(c.c.config, c.info, op, c.c.graph.usageSym)
|
markUsed(c.graph.config, c.info, op, c.graph.usageSym)
|
||||||
onUse(c.info, op)
|
onUse(c.info, op)
|
||||||
body.add newDeepCopyCall(op, x, y)
|
body.add newDeepCopyCall(op, x, y)
|
||||||
result = true
|
result = true
|
||||||
|
|
@ -162,13 +169,13 @@ proc addVar(father, v, value: PNode) =
|
||||||
addSon(father, vpart)
|
addSon(father, vpart)
|
||||||
|
|
||||||
proc declareCounter(c: var TLiftCtx; body: PNode; first: BiggestInt): PNode =
|
proc declareCounter(c: var TLiftCtx; body: PNode; first: BiggestInt): PNode =
|
||||||
var temp = newSym(skTemp, getIdent(c.c.cache, lowerings.genPrefix), c.fn, c.info)
|
var temp = newSym(skTemp, getIdent(c.graph.cache, lowerings.genPrefix), c.fn, c.info)
|
||||||
temp.typ = getSysType(c.c.graph, body.info, tyInt)
|
temp.typ = getSysType(c.graph, body.info, tyInt)
|
||||||
incl(temp.flags, sfFromGeneric)
|
incl(temp.flags, sfFromGeneric)
|
||||||
|
|
||||||
var v = newNodeI(nkVarSection, c.info)
|
var v = newNodeI(nkVarSection, c.info)
|
||||||
result = newSymNode(temp)
|
result = newSymNode(temp)
|
||||||
v.addVar(result, lowerings.newIntLit(c.c.graph, body.info, first))
|
v.addVar(result, lowerings.newIntLit(c.graph, body.info, first))
|
||||||
body.add v
|
body.add v
|
||||||
|
|
||||||
proc genBuiltin(g: ModuleGraph; magic: TMagic; name: string; i: PNode): PNode =
|
proc genBuiltin(g: ModuleGraph; magic: TMagic; name: string; i: PNode): PNode =
|
||||||
|
|
@ -178,22 +185,22 @@ proc genBuiltin(g: ModuleGraph; magic: TMagic; name: string; i: PNode): PNode =
|
||||||
|
|
||||||
proc genWhileLoop(c: var TLiftCtx; i, dest: PNode): PNode =
|
proc genWhileLoop(c: var TLiftCtx; i, dest: PNode): PNode =
|
||||||
result = newNodeI(nkWhileStmt, c.info, 2)
|
result = newNodeI(nkWhileStmt, c.info, 2)
|
||||||
let cmp = genBuiltin(c.c.graph, mLeI, "<=", i)
|
let cmp = genBuiltin(c.graph, mLeI, "<=", i)
|
||||||
cmp.add genHigh(c.c.graph, dest)
|
cmp.add genHigh(c.graph, dest)
|
||||||
cmp.typ = getSysType(c.c.graph, c.info, tyBool)
|
cmp.typ = getSysType(c.graph, c.info, tyBool)
|
||||||
result.sons[0] = cmp
|
result.sons[0] = cmp
|
||||||
result.sons[1] = newNodeI(nkStmtList, c.info)
|
result.sons[1] = newNodeI(nkStmtList, c.info)
|
||||||
|
|
||||||
proc addIncStmt(c: var TLiftCtx; body, i: PNode) =
|
proc addIncStmt(c: var TLiftCtx; body, i: PNode) =
|
||||||
let incCall = genBuiltin(c.c.graph, mInc, "inc", i)
|
let incCall = genBuiltin(c.graph, mInc, "inc", i)
|
||||||
incCall.add lowerings.newIntLit(c.c.graph, c.info, 1)
|
incCall.add lowerings.newIntLit(c.graph, c.info, 1)
|
||||||
body.add incCall
|
body.add incCall
|
||||||
|
|
||||||
proc newSeqCall(c: PContext; x, y: PNode): PNode =
|
proc newSeqCall(g: ModuleGraph; x, y: PNode): PNode =
|
||||||
# don't call genAddr(c, x) here:
|
# don't call genAddr(c, x) here:
|
||||||
result = genBuiltin(c.graph, mNewSeq, "newSeq", x)
|
result = genBuiltin(g, mNewSeq, "newSeq", x)
|
||||||
let lenCall = genBuiltin(c.graph, mLengthSeq, "len", y)
|
let lenCall = genBuiltin(g, mLengthSeq, "len", y)
|
||||||
lenCall.typ = getSysType(c.graph, x.info, tyInt)
|
lenCall.typ = getSysType(g, x.info, tyInt)
|
||||||
result.add lenCall
|
result.add lenCall
|
||||||
|
|
||||||
proc liftBodyAux(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
proc liftBodyAux(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||||
|
|
@ -204,7 +211,7 @@ proc liftBodyAux(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||||
defaultOp(c, t, body, x, y)
|
defaultOp(c, t, body, x, y)
|
||||||
of tyArray:
|
of tyArray:
|
||||||
if tfHasAsgn in t.flags:
|
if tfHasAsgn in t.flags:
|
||||||
let i = declareCounter(c, body, firstOrd(c.c.config, t))
|
let i = declareCounter(c, body, firstOrd(c.graph.config, t))
|
||||||
let whileLoop = genWhileLoop(c, i, x)
|
let whileLoop = genWhileLoop(c, i, x)
|
||||||
let elemType = t.lastSon
|
let elemType = t.lastSon
|
||||||
liftBodyAux(c, elemType, whileLoop.sons[1], x.at(i, elemType),
|
liftBodyAux(c, elemType, whileLoop.sons[1], x.at(i, elemType),
|
||||||
|
|
@ -216,12 +223,12 @@ proc liftBodyAux(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||||
of tySequence:
|
of tySequence:
|
||||||
# note that tfHasAsgn is propagated so we need the check on
|
# note that tfHasAsgn is propagated so we need the check on
|
||||||
# 'selectedGC' here to determine if we have the new runtime.
|
# 'selectedGC' here to determine if we have the new runtime.
|
||||||
if c.c.config.selectedGC == gcDestructors:
|
if c.graph.config.selectedGC == gcDestructors:
|
||||||
discard considerOverloadedOp(c, t, body, x, y)
|
discard considerOverloadedOp(c, t, body, x, y)
|
||||||
elif tfHasAsgn in t.flags:
|
elif tfHasAsgn in t.flags:
|
||||||
if c.kind != attachedDestructor:
|
if c.kind != attachedDestructor:
|
||||||
body.add newSeqCall(c.c, x, y)
|
body.add newSeqCall(c.graph, x, y)
|
||||||
let i = declareCounter(c, body, firstOrd(c.c.config, t))
|
let i = declareCounter(c, body, firstOrd(c.graph.config, t))
|
||||||
let whileLoop = genWhileLoop(c, i, x)
|
let whileLoop = genWhileLoop(c, i, x)
|
||||||
let elemType = t.lastSon
|
let elemType = t.lastSon
|
||||||
liftBodyAux(c, elemType, whileLoop.sons[1], x.at(i, elemType),
|
liftBodyAux(c, elemType, whileLoop.sons[1], x.at(i, elemType),
|
||||||
|
|
@ -235,11 +242,12 @@ proc liftBodyAux(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||||
discard considerOverloadedOp(c, t, body, x, y)
|
discard considerOverloadedOp(c, t, body, x, y)
|
||||||
else:
|
else:
|
||||||
defaultOp(c, t, body, x, y)
|
defaultOp(c, t, body, x, y)
|
||||||
of tyObject, tyDistinct:
|
of tyObject:
|
||||||
|
if not considerOverloadedOp(c, t, body, x, y):
|
||||||
|
liftBodyObj(c, t.n, body, x, y)
|
||||||
|
of tyDistinct:
|
||||||
if not considerOverloadedOp(c, t, body, x, y):
|
if not considerOverloadedOp(c, t, body, x, y):
|
||||||
if t.sons[0] != nil:
|
|
||||||
liftBodyAux(c, t.sons[0].skipTypes(skipPtrs), body, x, y)
|
liftBodyAux(c, t.sons[0].skipTypes(skipPtrs), body, x, y)
|
||||||
if t.kind == tyObject: liftBodyObj(c, t.n, body, x, y)
|
|
||||||
of tyTuple:
|
of tyTuple:
|
||||||
liftBodyTup(c, t, body, x, y)
|
liftBodyTup(c, t, body, x, y)
|
||||||
of tyProc:
|
of tyProc:
|
||||||
|
|
@ -250,20 +258,20 @@ proc liftBodyAux(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||||
# have to go through some indirection; we delegate this to the codegen:
|
# have to go through some indirection; we delegate this to the codegen:
|
||||||
let call = newNodeI(nkCall, c.info, 2)
|
let call = newNodeI(nkCall, c.info, 2)
|
||||||
call.typ = t
|
call.typ = t
|
||||||
call.sons[0] = newSymNode(createMagic(c.c.graph, "deepCopy", mDeepCopy))
|
call.sons[0] = newSymNode(createMagic(c.graph, "deepCopy", mDeepCopy))
|
||||||
call.sons[1] = y
|
call.sons[1] = y
|
||||||
body.add newAsgnStmt(x, call)
|
body.add newAsgnStmt(x, call)
|
||||||
of tyVarargs, tyOpenArray:
|
of tyVarargs, tyOpenArray:
|
||||||
localError(c.c.config, c.info, "cannot copy openArray")
|
localError(c.graph.config, c.info, "cannot copy openArray")
|
||||||
of tyFromExpr, tyProxy, tyBuiltInTypeClass, tyUserTypeClass,
|
of tyFromExpr, tyProxy, tyBuiltInTypeClass, tyUserTypeClass,
|
||||||
tyUserTypeClassInst, tyCompositeTypeClass, tyAnd, tyOr, tyNot, tyAnything,
|
tyUserTypeClassInst, tyCompositeTypeClass, tyAnd, tyOr, tyNot, tyAnything,
|
||||||
tyGenericParam, tyGenericBody, tyNil, tyExpr, tyStmt,
|
tyGenericParam, tyGenericBody, tyNil, tyExpr, tyStmt,
|
||||||
tyTypeDesc, tyGenericInvocation, tyForward:
|
tyTypeDesc, tyGenericInvocation, tyForward:
|
||||||
internalError(c.c.config, c.info, "assignment requested for type: " & typeToString(t))
|
internalError(c.graph.config, c.info, "assignment requested for type: " & typeToString(t))
|
||||||
of tyOrdinal, tyRange, tyInferred,
|
of tyOrdinal, tyRange, tyInferred,
|
||||||
tyGenericInst, tyStatic, tyVar, tyLent, tyAlias, tySink:
|
tyGenericInst, tyStatic, tyVar, tyLent, tyAlias, tySink:
|
||||||
liftBodyAux(c, lastSon(t), body, x, y)
|
liftBodyAux(c, lastSon(t), body, x, y)
|
||||||
of tyOptAsRef: internalError(c.c.config, "liftBodyAux")
|
of tyOptAsRef: internalError(c.graph.config, "liftBodyAux")
|
||||||
|
|
||||||
proc newProcType(info: TLineInfo; owner: PSym): PType =
|
proc newProcType(info: TLineInfo; owner: PSym): PType =
|
||||||
result = newType(tyProc, owner)
|
result = newType(tyProc, owner)
|
||||||
|
|
@ -279,26 +287,54 @@ proc addParam(procType: PType; param: PSym) =
|
||||||
addSon(procType.n, newSymNode(param))
|
addSon(procType.n, newSymNode(param))
|
||||||
rawAddSon(procType, param.typ)
|
rawAddSon(procType, param.typ)
|
||||||
|
|
||||||
proc liftBody(c: PContext; typ: PType; kind: TTypeAttachedOp;
|
proc liftBodyDistinctType(g: ModuleGraph; typ: PType; kind: TTypeAttachedOp; info: TLineInfo): PSym =
|
||||||
|
assert typ.kind == tyDistinct
|
||||||
|
let baseType = typ[0]
|
||||||
|
case kind
|
||||||
|
of attachedAsgn:
|
||||||
|
if baseType.assignment == nil:
|
||||||
|
discard liftBody(g, baseType, kind, info)
|
||||||
|
typ.assignment = baseType.assignment
|
||||||
|
result = typ.assignment
|
||||||
|
of attachedSink:
|
||||||
|
if baseType.sink == nil:
|
||||||
|
discard liftBody(g, baseType, kind, info)
|
||||||
|
typ.sink = baseType.sink
|
||||||
|
result = typ.sink
|
||||||
|
of attachedDeepCopy:
|
||||||
|
if baseType.deepCopy == nil:
|
||||||
|
discard liftBody(g, baseType, kind, info)
|
||||||
|
typ.deepCopy = baseType.deepCopy
|
||||||
|
result = typ.deepCopy
|
||||||
|
of attachedDestructor:
|
||||||
|
if baseType.destructor == nil:
|
||||||
|
discard liftBody(g, baseType, kind, info)
|
||||||
|
typ.destructor = baseType.destructor
|
||||||
|
result = typ.destructor
|
||||||
|
|
||||||
|
proc liftBody(g: ModuleGraph; typ: PType; kind: TTypeAttachedOp;
|
||||||
info: TLineInfo): PSym =
|
info: TLineInfo): PSym =
|
||||||
|
if typ.kind == tyDistinct:
|
||||||
|
return liftBodyDistinctType(g, typ, kind, info)
|
||||||
|
|
||||||
var a: TLiftCtx
|
var a: TLiftCtx
|
||||||
a.info = info
|
a.info = info
|
||||||
a.c = c
|
a.graph = g
|
||||||
a.kind = kind
|
a.kind = kind
|
||||||
let body = newNodeI(nkStmtList, info)
|
let body = newNodeI(nkStmtList, info)
|
||||||
let procname = case kind
|
let procname = case kind
|
||||||
of attachedAsgn: getIdent(c.cache, "=")
|
of attachedAsgn: getIdent(g.cache, "=")
|
||||||
of attachedSink: getIdent(c.cache, "=sink")
|
of attachedSink: getIdent(g.cache, "=sink")
|
||||||
of attachedDeepCopy: getIdent(c.cache, "=deepcopy")
|
of attachedDeepCopy: getIdent(g.cache, "=deepcopy")
|
||||||
of attachedDestructor: getIdent(c.cache, "=destroy")
|
of attachedDestructor: getIdent(g.cache, "=destroy")
|
||||||
|
|
||||||
result = newSym(skProc, procname, typ.owner, info)
|
result = newSym(skProc, procname, typ.owner, info)
|
||||||
a.fn = result
|
a.fn = result
|
||||||
a.asgnForType = typ
|
a.asgnForType = typ
|
||||||
|
|
||||||
let dest = newSym(skParam, getIdent(c.cache, "dest"), result, info)
|
let dest = newSym(skParam, getIdent(g.cache, "dest"), result, info)
|
||||||
let src = newSym(skParam, getIdent(c.cache, "src"), result, info)
|
let src = newSym(skParam, getIdent(g.cache, "src"), result, info)
|
||||||
dest.typ = makeVarType(c, typ)
|
dest.typ = makeVarType(typ.owner, typ)
|
||||||
src.typ = typ
|
src.typ = typ
|
||||||
|
|
||||||
result.typ = newProcType(info, typ.owner)
|
result.typ = newProcType(info, typ.owner)
|
||||||
|
|
@ -309,7 +345,7 @@ proc liftBody(c: PContext; typ: PType; kind: TTypeAttachedOp;
|
||||||
liftBodyAux(a, typ, body, newSymNode(dest).newDeref, newSymNode(src))
|
liftBodyAux(a, typ, body, newSymNode(dest).newDeref, newSymNode(src))
|
||||||
# recursion is handled explicitly, do not register the type based operation
|
# recursion is handled explicitly, do not register the type based operation
|
||||||
# before 'liftBodyAux':
|
# before 'liftBodyAux':
|
||||||
if c.config.selectedGC == gcDestructors and
|
if g.config.selectedGC == gcDestructors and
|
||||||
typ.kind in {tySequence, tyString} and body.len == 0:
|
typ.kind in {tySequence, tyString} and body.len == 0:
|
||||||
discard "do not cache it yet"
|
discard "do not cache it yet"
|
||||||
else:
|
else:
|
||||||
|
|
@ -328,17 +364,17 @@ proc liftBody(c: PContext; typ: PType; kind: TTypeAttachedOp;
|
||||||
incl result.flags, sfFromGeneric
|
incl result.flags, sfFromGeneric
|
||||||
|
|
||||||
|
|
||||||
proc getAsgnOrLiftBody(c: PContext; typ: PType; info: TLineInfo): PSym =
|
proc getAsgnOrLiftBody(g: ModuleGraph; typ: PType; info: TLineInfo): PSym =
|
||||||
let t = typ.skipTypes({tyGenericInst, tyVar, tyLent, tyAlias, tySink})
|
let t = typ.skipTypes({tyGenericInst, tyVar, tyLent, tyAlias, tySink})
|
||||||
result = t.assignment
|
result = t.assignment
|
||||||
if result.isNil:
|
if result.isNil:
|
||||||
result = liftBody(c, t, attachedAsgn, info)
|
result = liftBody(g, t, attachedAsgn, info)
|
||||||
|
|
||||||
proc overloadedAsgn(c: PContext; dest, src: PNode): PNode =
|
proc overloadedAsgn(g: ModuleGraph; dest, src: PNode): PNode =
|
||||||
let a = getAsgnOrLiftBody(c, dest.typ, dest.info)
|
let a = getAsgnOrLiftBody(g, dest.typ, dest.info)
|
||||||
result = newAsgnCall(c, a, dest, src)
|
result = newAsgnCall(g, a, dest, src)
|
||||||
|
|
||||||
proc liftTypeBoundOps*(c: PContext; typ: PType; info: TLineInfo) =
|
proc liftTypeBoundOps*(g: ModuleGraph; typ: PType; info: TLineInfo) =
|
||||||
## In the semantic pass this is called in strategic places
|
## In the semantic pass this is called in strategic places
|
||||||
## to ensure we lift assignment, destructors and moves properly.
|
## to ensure we lift assignment, destructors and moves properly.
|
||||||
## The later 'destroyer' pass depends on it.
|
## The later 'destroyer' pass depends on it.
|
||||||
|
|
@ -350,11 +386,11 @@ proc liftTypeBoundOps*(c: PContext; typ: PType; info: TLineInfo) =
|
||||||
let typ = typ.skipTypes({tyGenericInst, tyAlias})
|
let typ = typ.skipTypes({tyGenericInst, tyAlias})
|
||||||
# we generate the destructor first so that other operators can depend on it:
|
# we generate the destructor first so that other operators can depend on it:
|
||||||
if typ.destructor == nil:
|
if typ.destructor == nil:
|
||||||
liftBody(c, typ, attachedDestructor, info)
|
liftBody(g, typ, attachedDestructor, info)
|
||||||
if typ.assignment == nil:
|
if typ.assignment == nil:
|
||||||
liftBody(c, typ, attachedAsgn, info)
|
liftBody(g, typ, attachedAsgn, info)
|
||||||
if typ.sink == nil:
|
if typ.sink == nil:
|
||||||
liftBody(c, typ, attachedSink, info)
|
liftBody(g, typ, attachedSink, info)
|
||||||
|
|
||||||
#proc patchResolvedTypeBoundOp*(c: PContext; n: PNode): PNode =
|
#proc patchResolvedTypeBoundOp*(g: ModuleGraph; n: PNode): PNode =
|
||||||
# if n.kind == nkCall and
|
# if n.kind == nkCall and
|
||||||
|
|
|
||||||
|
|
@ -287,7 +287,18 @@ proc getMsgDiagnostic(c: PContext, flags: TExprFlags, n, f: PNode): string =
|
||||||
|
|
||||||
let ident = considerQuotedIdent(c, f, n).s
|
let ident = considerQuotedIdent(c, f, n).s
|
||||||
if nfDotField in n.flags and nfExplicitCall notin n.flags:
|
if nfDotField in n.flags and nfExplicitCall notin n.flags:
|
||||||
result = errUndeclaredField % ident & result
|
let sym = n.sons[1].typ.sym
|
||||||
|
var typeHint = ""
|
||||||
|
if sym == nil:
|
||||||
|
# Perhaps we're in a `compiles(foo.bar)` expression, or
|
||||||
|
# in a concept, eg:
|
||||||
|
# ExplainedConcept {.explain.} = concept x
|
||||||
|
# x.foo is int
|
||||||
|
# We coudl use: `(c.config $ n.sons[1].info)` to get more context.
|
||||||
|
discard
|
||||||
|
else:
|
||||||
|
typeHint = " for type " & getProcHeader(c.config, sym)
|
||||||
|
result = errUndeclaredField % ident & typeHint & " " & result
|
||||||
else:
|
else:
|
||||||
if result.len == 0: result = errUndeclaredRoutine % ident
|
if result.len == 0: result = errUndeclaredRoutine % ident
|
||||||
else: result = errBadRoutine % [ident, result]
|
else: result = errBadRoutine % [ident, result]
|
||||||
|
|
|
||||||
|
|
@ -286,6 +286,13 @@ proc makeVarType*(c: PContext, baseType: PType; kind = tyVar): PType =
|
||||||
result = newTypeS(kind, c)
|
result = newTypeS(kind, c)
|
||||||
addSonSkipIntLit(result, baseType)
|
addSonSkipIntLit(result, baseType)
|
||||||
|
|
||||||
|
proc makeVarType*(owner: PSym, baseType: PType; kind = tyVar): PType =
|
||||||
|
if baseType.kind == kind:
|
||||||
|
result = baseType
|
||||||
|
else:
|
||||||
|
result = newType(kind, owner)
|
||||||
|
addSonSkipIntLit(result, baseType)
|
||||||
|
|
||||||
proc makeTypeDesc*(c: PContext, typ: PType): PType =
|
proc makeTypeDesc*(c: PContext, typ: PType): PType =
|
||||||
if typ.kind == tyTypeDesc:
|
if typ.kind == tyTypeDesc:
|
||||||
result = typ
|
result = typ
|
||||||
|
|
|
||||||
|
|
@ -108,6 +108,8 @@ const
|
||||||
|
|
||||||
proc checkConvertible(c: PContext, castDest, src: PType): TConvStatus =
|
proc checkConvertible(c: PContext, castDest, src: PType): TConvStatus =
|
||||||
result = convOK
|
result = convOK
|
||||||
|
# We're interested in the inner type and not in the static tag
|
||||||
|
var src = src.skipTypes({tyStatic})
|
||||||
if sameType(castDest, src) and castDest.sym == src.sym:
|
if sameType(castDest, src) and castDest.sym == src.sym:
|
||||||
# don't annoy conversions that may be needed on another processor:
|
# don't annoy conversions that may be needed on another processor:
|
||||||
if castDest.kind notin IntegralTypes+{tyRange}:
|
if castDest.kind notin IntegralTypes+{tyRange}:
|
||||||
|
|
@ -230,7 +232,7 @@ proc semConv(c: PContext, n: PNode): PNode =
|
||||||
# special case to make MyObject(x = 3) produce a nicer error message:
|
# special case to make MyObject(x = 3) produce a nicer error message:
|
||||||
if n[1].kind == nkExprEqExpr and
|
if n[1].kind == nkExprEqExpr and
|
||||||
targetType.skipTypes(abstractPtrs).kind == tyObject:
|
targetType.skipTypes(abstractPtrs).kind == tyObject:
|
||||||
localError(c.config, n.info, "object contruction uses ':', not '='")
|
localError(c.config, n.info, "object construction uses ':', not '='")
|
||||||
var op = semExprWithType(c, n.sons[1])
|
var op = semExprWithType(c, n.sons[1])
|
||||||
if targetType.isMetaType:
|
if targetType.isMetaType:
|
||||||
let final = inferWithMetatype(c, targetType, op, true)
|
let final = inferWithMetatype(c, targetType, op, true)
|
||||||
|
|
@ -403,8 +405,8 @@ proc semIs(c: PContext, n: PNode, flags: TExprFlags): PNode =
|
||||||
n[1] = makeTypeSymNode(c, lhsType, n[1].info)
|
n[1] = makeTypeSymNode(c, lhsType, n[1].info)
|
||||||
lhsType = n[1].typ
|
lhsType = n[1].typ
|
||||||
else:
|
else:
|
||||||
internalAssert c.config, lhsType.base.kind != tyNone
|
if lhsType.base.kind == tyNone or
|
||||||
if c.inGenericContext > 0 and lhsType.base.containsGenericType:
|
(c.inGenericContext > 0 and lhsType.base.containsGenericType):
|
||||||
# BUGFIX: don't evaluate this too early: ``T is void``
|
# BUGFIX: don't evaluate this too early: ``T is void``
|
||||||
return
|
return
|
||||||
|
|
||||||
|
|
@ -710,7 +712,9 @@ proc evalAtCompileTime(c: PContext, n: PNode): PNode =
|
||||||
let a = getConstExpr(c.module, n.sons[i], c.graph)
|
let a = getConstExpr(c.module, n.sons[i], c.graph)
|
||||||
if a == nil: return n
|
if a == nil: return n
|
||||||
call.add(a)
|
call.add(a)
|
||||||
|
|
||||||
#echo "NOW evaluating at compile time: ", call.renderTree
|
#echo "NOW evaluating at compile time: ", call.renderTree
|
||||||
|
if c.inStaticContext == 0 or sfNoSideEffect in callee.flags:
|
||||||
if sfCompileTime in callee.flags:
|
if sfCompileTime in callee.flags:
|
||||||
result = evalStaticExpr(c.module, c.graph, call, c.p.owner)
|
result = evalStaticExpr(c.module, c.graph, call, c.p.owner)
|
||||||
if result.isNil:
|
if result.isNil:
|
||||||
|
|
@ -720,6 +724,8 @@ proc evalAtCompileTime(c: PContext, n: PNode): PNode =
|
||||||
result = evalConstExpr(c.module, c.graph, call)
|
result = evalConstExpr(c.module, c.graph, call)
|
||||||
if result.isNil: result = n
|
if result.isNil: result = n
|
||||||
else: result = fixupTypeAfterEval(c, result, n)
|
else: result = fixupTypeAfterEval(c, result, n)
|
||||||
|
else:
|
||||||
|
result = n
|
||||||
#if result != n:
|
#if result != n:
|
||||||
# echo "SUCCESS evaluated at compile time: ", call.renderTree
|
# echo "SUCCESS evaluated at compile time: ", call.renderTree
|
||||||
|
|
||||||
|
|
@ -798,7 +804,7 @@ proc afterCallActions(c: PContext; n, orig: PNode, flags: TExprFlags): PNode =
|
||||||
result = magicsAfterOverloadResolution(c, result, flags)
|
result = magicsAfterOverloadResolution(c, result, flags)
|
||||||
if result.typ != nil and
|
if result.typ != nil and
|
||||||
not (result.typ.kind == tySequence and result.typ.sons[0].kind == tyEmpty):
|
not (result.typ.kind == tySequence and result.typ.sons[0].kind == tyEmpty):
|
||||||
liftTypeBoundOps(c, result.typ, n.info)
|
liftTypeBoundOps(c.graph, result.typ, n.info)
|
||||||
#result = patchResolvedTypeBoundOp(c, result)
|
#result = patchResolvedTypeBoundOp(c, result)
|
||||||
if c.matchedConcept == nil:
|
if c.matchedConcept == nil:
|
||||||
result = evalAtCompileTime(c, result)
|
result = evalAtCompileTime(c, result)
|
||||||
|
|
@ -913,7 +919,7 @@ proc semExprNoType(c: PContext, n: PNode): PNode =
|
||||||
let isPush = hintExtendedContext in c.config.notes
|
let isPush = hintExtendedContext in c.config.notes
|
||||||
if isPush: pushInfoContext(c.config, n.info)
|
if isPush: pushInfoContext(c.config, n.info)
|
||||||
result = semExpr(c, n, {efWantStmt})
|
result = semExpr(c, n, {efWantStmt})
|
||||||
discardCheck(c, result, {})
|
result = discardCheck(c, result, {})
|
||||||
if isPush: popInfoContext(c.config)
|
if isPush: popInfoContext(c.config)
|
||||||
|
|
||||||
proc isTypeExpr(n: PNode): bool =
|
proc isTypeExpr(n: PNode): bool =
|
||||||
|
|
@ -1039,7 +1045,8 @@ proc semSym(c: PContext, n: PNode, sym: PSym, flags: TExprFlags): PNode =
|
||||||
of skConst:
|
of skConst:
|
||||||
markUsed(c.config, n.info, s, c.graph.usageSym)
|
markUsed(c.config, n.info, s, c.graph.usageSym)
|
||||||
onUse(n.info, s)
|
onUse(n.info, s)
|
||||||
case skipTypes(s.typ, abstractInst-{tyTypeDesc}).kind
|
let typ = skipTypes(s.typ, abstractInst-{tyTypeDesc})
|
||||||
|
case typ.kind
|
||||||
of tyNil, tyChar, tyInt..tyInt64, tyFloat..tyFloat128,
|
of tyNil, tyChar, tyInt..tyInt64, tyFloat..tyFloat128,
|
||||||
tyTuple, tySet, tyUInt..tyUInt64:
|
tyTuple, tySet, tyUInt..tyUInt64:
|
||||||
if s.magic == mNone: result = inlineConst(c, n, s)
|
if s.magic == mNone: result = inlineConst(c, n, s)
|
||||||
|
|
@ -1057,6 +1064,12 @@ proc semSym(c: PContext, n: PNode, sym: PSym, flags: TExprFlags): PNode =
|
||||||
# deal with two different ``[]``.
|
# deal with two different ``[]``.
|
||||||
if s.ast.len == 0: result = inlineConst(c, n, s)
|
if s.ast.len == 0: result = inlineConst(c, n, s)
|
||||||
else: result = newSymNode(s, n.info)
|
else: result = newSymNode(s, n.info)
|
||||||
|
of tyStatic:
|
||||||
|
if typ.n != nil:
|
||||||
|
result = typ.n
|
||||||
|
result.typ = typ.base
|
||||||
|
else:
|
||||||
|
result = newSymNode(s, n.info)
|
||||||
else:
|
else:
|
||||||
result = newSymNode(s, n.info)
|
result = newSymNode(s, n.info)
|
||||||
of skMacro:
|
of skMacro:
|
||||||
|
|
@ -1581,7 +1594,7 @@ proc semAsgn(c: PContext, n: PNode; mode=asgnNormal): PNode =
|
||||||
typeMismatch(c.config, n.info, lhs.typ, rhsTyp)
|
typeMismatch(c.config, n.info, lhs.typ, rhsTyp)
|
||||||
|
|
||||||
n.sons[1] = fitNode(c, le, rhs, goodLineInfo(n[1]))
|
n.sons[1] = fitNode(c, le, rhs, goodLineInfo(n[1]))
|
||||||
liftTypeBoundOps(c, lhs.typ, lhs.info)
|
liftTypeBoundOps(c.graph, lhs.typ, lhs.info)
|
||||||
#liftTypeBoundOps(c, n.sons[0].typ, n.sons[0].info)
|
#liftTypeBoundOps(c, n.sons[0].typ, n.sons[0].info)
|
||||||
|
|
||||||
fixAbstractType(c, n)
|
fixAbstractType(c, n)
|
||||||
|
|
@ -1628,7 +1641,7 @@ proc semProcBody(c: PContext, n: PNode): PNode =
|
||||||
a.sons[1] = result
|
a.sons[1] = result
|
||||||
result = semAsgn(c, a)
|
result = semAsgn(c, a)
|
||||||
else:
|
else:
|
||||||
discardCheck(c, result, {})
|
result = discardCheck(c, result, {})
|
||||||
|
|
||||||
if c.p.owner.kind notin {skMacro, skTemplate} and
|
if c.p.owner.kind notin {skMacro, skTemplate} and
|
||||||
c.p.resultSym != nil and c.p.resultSym.typ.isMetaType:
|
c.p.resultSym != nil and c.p.resultSym.typ.isMetaType:
|
||||||
|
|
@ -2324,7 +2337,6 @@ proc semExportExcept(c: PContext, n: PNode): PNode =
|
||||||
|
|
||||||
proc semExport(c: PContext, n: PNode): PNode =
|
proc semExport(c: PContext, n: PNode): PNode =
|
||||||
result = newNodeI(nkExportStmt, n.info)
|
result = newNodeI(nkExportStmt, n.info)
|
||||||
|
|
||||||
for i in 0..<n.len:
|
for i in 0..<n.len:
|
||||||
let a = n.sons[i]
|
let a = n.sons[i]
|
||||||
var o: TOverloadIter
|
var o: TOverloadIter
|
||||||
|
|
@ -2343,6 +2355,9 @@ proc semExport(c: PContext, n: PNode): PNode =
|
||||||
it = nextIter(ti, s.tab)
|
it = nextIter(ti, s.tab)
|
||||||
else:
|
else:
|
||||||
while s != nil:
|
while s != nil:
|
||||||
|
if s.kind == skEnumField:
|
||||||
|
localError(c.config, a.info, errGenerated, "cannot export: " & renderTree(a) &
|
||||||
|
"; enum field cannot be exported individually")
|
||||||
if s.kind in ExportableSymKinds+{skModule}:
|
if s.kind in ExportableSymKinds+{skModule}:
|
||||||
result.add(newSymNode(s, a.info))
|
result.add(newSymNode(s, a.info))
|
||||||
strTableAdd(c.module.tab, s)
|
strTableAdd(c.module.tab, s)
|
||||||
|
|
@ -2430,7 +2445,7 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
|
||||||
result.kind = nkCall
|
result.kind = nkCall
|
||||||
result = semExpr(c, result, flags)
|
result = semExpr(c, result, flags)
|
||||||
of nkBind:
|
of nkBind:
|
||||||
message(c.config, n.info, warnDeprecated, "bind")
|
message(c.config, n.info, warnDeprecated, "bind is deprecated")
|
||||||
result = semExpr(c, n.sons[0], flags)
|
result = semExpr(c, n.sons[0], flags)
|
||||||
of nkTypeOfExpr, nkTupleTy, nkTupleClassTy, nkRefTy..nkEnumTy, nkStaticTy:
|
of nkTypeOfExpr, nkTupleTy, nkTupleClassTy, nkRefTy..nkEnumTy, nkStaticTy:
|
||||||
if c.matchedConcept != nil and n.len == 1:
|
if c.matchedConcept != nil and n.len == 1:
|
||||||
|
|
@ -2620,6 +2635,8 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
|
||||||
of nkStaticStmt:
|
of nkStaticStmt:
|
||||||
result = semStaticStmt(c, n)
|
result = semStaticStmt(c, n)
|
||||||
of nkDefer:
|
of nkDefer:
|
||||||
|
if c.currentScope == c.topLevelScope:
|
||||||
|
localError(c.config, n.info, "defer statement not supported at top level")
|
||||||
n.sons[0] = semExpr(c, n.sons[0])
|
n.sons[0] = semExpr(c, n.sons[0])
|
||||||
if not n.sons[0].typ.isEmptyType and not implicitlyDiscardable(n.sons[0]):
|
if not n.sons[0].typ.isEmptyType and not implicitlyDiscardable(n.sons[0]):
|
||||||
localError(c.config, n.info, "'defer' takes a 'void' expression")
|
localError(c.config, n.info, "'defer' takes a 'void' expression")
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,9 @@ type
|
||||||
c: PContext
|
c: PContext
|
||||||
|
|
||||||
proc instFieldLoopBody(c: TFieldInstCtx, n: PNode, forLoop: PNode): PNode =
|
proc instFieldLoopBody(c: TFieldInstCtx, n: PNode, forLoop: PNode): PNode =
|
||||||
|
if c.field != nil and isEmptyType(c.field.typ):
|
||||||
|
result = newNode(nkEmpty)
|
||||||
|
return
|
||||||
case n.kind
|
case n.kind
|
||||||
of nkEmpty..pred(nkIdent), succ(nkSym)..nkNilLit: result = n
|
of nkEmpty..pred(nkIdent), succ(nkSym)..nkNilLit: result = n
|
||||||
of nkIdent, nkSym:
|
of nkIdent, nkSym:
|
||||||
|
|
|
||||||
|
|
@ -132,7 +132,9 @@ proc ordinalValToString*(a: PNode; g: ModuleGraph): string =
|
||||||
return field.name.s
|
return field.name.s
|
||||||
else:
|
else:
|
||||||
return field.ast.strVal
|
return field.ast.strVal
|
||||||
internalError(g.config, a.info, "no symbol for ordinal value: " & $x)
|
localError(g.config, a.info,
|
||||||
|
"Cannot convert int literal to $1. The value is invalid." %
|
||||||
|
[typeToString(t)])
|
||||||
else:
|
else:
|
||||||
result = $x
|
result = $x
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -129,14 +129,15 @@ proc evalTypeTrait(c: PContext; traitCall: PNode, operand: PType, context: PSym)
|
||||||
template typeWithSonsResult(kind, sons): PNode =
|
template typeWithSonsResult(kind, sons): PNode =
|
||||||
newTypeWithSons(context, kind, sons).toNode(traitCall.info)
|
newTypeWithSons(context, kind, sons).toNode(traitCall.info)
|
||||||
|
|
||||||
case trait.sym.name.s
|
let s = trait.sym.name.s
|
||||||
|
case s
|
||||||
of "or", "|":
|
of "or", "|":
|
||||||
return typeWithSonsResult(tyOr, @[operand, operand2])
|
return typeWithSonsResult(tyOr, @[operand, operand2])
|
||||||
of "and":
|
of "and":
|
||||||
return typeWithSonsResult(tyAnd, @[operand, operand2])
|
return typeWithSonsResult(tyAnd, @[operand, operand2])
|
||||||
of "not":
|
of "not":
|
||||||
return typeWithSonsResult(tyNot, @[operand])
|
return typeWithSonsResult(tyNot, @[operand])
|
||||||
of "name":
|
of "name", "$":
|
||||||
result = newStrNode(nkStrLit, operand.typeToString(preferTypeName))
|
result = newStrNode(nkStrLit, operand.typeToString(preferTypeName))
|
||||||
result.typ = newType(tyString, context)
|
result.typ = newType(tyString, context)
|
||||||
result.info = traitCall.info
|
result.info = traitCall.info
|
||||||
|
|
@ -160,7 +161,7 @@ proc evalTypeTrait(c: PContext; traitCall: PNode, operand: PType, context: PSym)
|
||||||
hasDestructor(t)
|
hasDestructor(t)
|
||||||
result = newIntNodeT(ord(not complexObj), traitCall, c.graph)
|
result = newIntNodeT(ord(not complexObj), traitCall, c.graph)
|
||||||
else:
|
else:
|
||||||
localError(c.config, traitCall.info, "unknown trait")
|
localError(c.config, traitCall.info, "unknown trait: " & s)
|
||||||
result = newNodeI(nkEmpty, traitCall.info)
|
result = newNodeI(nkEmpty, traitCall.info)
|
||||||
|
|
||||||
proc semTypeTraits(c: PContext, n: PNode): PNode =
|
proc semTypeTraits(c: PContext, n: PNode): PNode =
|
||||||
|
|
|
||||||
|
|
@ -95,13 +95,6 @@ proc semWhile(c: PContext, n: PNode; flags: TExprFlags): PNode =
|
||||||
elif efInTypeof in flags:
|
elif efInTypeof in flags:
|
||||||
result.typ = n[1].typ
|
result.typ = n[1].typ
|
||||||
|
|
||||||
proc toCover(c: PContext, t: PType): BiggestInt =
|
|
||||||
let t2 = skipTypes(t, abstractVarRange-{tyTypeDesc})
|
|
||||||
if t2.kind == tyEnum and enumHasHoles(t2):
|
|
||||||
result = sonsLen(t2.n)
|
|
||||||
else:
|
|
||||||
result = lengthOrd(c.config, skipTypes(t, abstractVar-{tyTypeDesc}))
|
|
||||||
|
|
||||||
proc semProc(c: PContext, n: PNode): PNode
|
proc semProc(c: PContext, n: PNode): PNode
|
||||||
|
|
||||||
proc semExprBranch(c: PContext, n: PNode; flags: TExprFlags = {}): PNode =
|
proc semExprBranch(c: PContext, n: PNode; flags: TExprFlags = {}): PNode =
|
||||||
|
|
@ -137,13 +130,13 @@ proc fixNilType(c: PContext; n: PNode) =
|
||||||
for it in n: fixNilType(c, it)
|
for it in n: fixNilType(c, it)
|
||||||
n.typ = nil
|
n.typ = nil
|
||||||
|
|
||||||
proc discardCheck(c: PContext, result: PNode, flags: TExprFlags) =
|
proc discardCheck(c: PContext, expr: PNode, flags: TExprFlags): PNode =
|
||||||
|
result = expr
|
||||||
if c.matchedConcept != nil or efInTypeof in flags: return
|
if c.matchedConcept != nil or efInTypeof in flags: return
|
||||||
|
|
||||||
if result.typ != nil and result.typ.kind notin {tyStmt, tyVoid}:
|
if result.typ != nil and result.typ.kind notin {tyStmt, tyVoid}:
|
||||||
if implicitlyDiscardable(result):
|
if implicitlyDiscardable(result):
|
||||||
var n = newNodeI(nkDiscardStmt, result.info, 1)
|
result = newNode(nkDiscardStmt, result.info, @[result])
|
||||||
n[0] = result
|
|
||||||
elif result.typ.kind != tyError and c.config.cmd != cmdInteractive:
|
elif result.typ.kind != tyError and c.config.cmd != cmdInteractive:
|
||||||
var n = result
|
var n = result
|
||||||
while n.kind in skipForDiscardable: n = n.lastSon
|
while n.kind in skipForDiscardable: n = n.lastSon
|
||||||
|
|
@ -175,7 +168,8 @@ proc semIf(c: PContext, n: PNode; flags: TExprFlags): PNode =
|
||||||
else: illFormedAst(it, c.config)
|
else: illFormedAst(it, c.config)
|
||||||
if isEmptyType(typ) or typ.kind in {tyNil, tyExpr} or
|
if isEmptyType(typ) or typ.kind in {tyNil, tyExpr} or
|
||||||
(not hasElse and efInTypeof notin flags):
|
(not hasElse and efInTypeof notin flags):
|
||||||
for it in n: discardCheck(c, it.lastSon, flags)
|
for it in n:
|
||||||
|
it.sons[^1] = discardCheck(c, it.sons[^1], flags)
|
||||||
result.kind = nkIfStmt
|
result.kind = nkIfStmt
|
||||||
# propagate any enforced VoidContext:
|
# propagate any enforced VoidContext:
|
||||||
if typ == c.enforceVoidContext: result.typ = c.enforceVoidContext
|
if typ == c.enforceVoidContext: result.typ = c.enforceVoidContext
|
||||||
|
|
@ -273,12 +267,14 @@ proc semTry(c: PContext, n: PNode; flags: TExprFlags): PNode =
|
||||||
|
|
||||||
dec c.p.inTryStmt
|
dec c.p.inTryStmt
|
||||||
if isEmptyType(typ) or typ.kind in {tyNil, tyExpr}:
|
if isEmptyType(typ) or typ.kind in {tyNil, tyExpr}:
|
||||||
discardCheck(c, n.sons[0], flags)
|
n.sons[0] = discardCheck(c, n.sons[0], flags)
|
||||||
for i in 1..n.len-1: discardCheck(c, n.sons[i].lastSon, flags)
|
for i in 1..n.len-1:
|
||||||
|
n.sons[i].sons[^1] = discardCheck(c, n.sons[i].sons[^1], flags)
|
||||||
if typ == c.enforceVoidContext:
|
if typ == c.enforceVoidContext:
|
||||||
result.typ = c.enforceVoidContext
|
result.typ = c.enforceVoidContext
|
||||||
else:
|
else:
|
||||||
if n.lastSon.kind == nkFinally: discardCheck(c, n.lastSon.lastSon, flags)
|
if n.lastSon.kind == nkFinally:
|
||||||
|
n.sons[^1].sons[^1] = discardCheck(c, n.sons[^1].sons[^1], flags)
|
||||||
n.sons[0] = fitNode(c, typ, n.sons[0], n.sons[0].info)
|
n.sons[0] = fitNode(c, typ, n.sons[0], n.sons[0].info)
|
||||||
for i in 1..last:
|
for i in 1..last:
|
||||||
var it = n.sons[i]
|
var it = n.sons[i]
|
||||||
|
|
@ -322,14 +318,24 @@ proc semIdentDef(c: PContext, n: PNode, kind: TSymKind): PSym =
|
||||||
result = semIdentWithPragma(c, kind, n, {})
|
result = semIdentWithPragma(c, kind, n, {})
|
||||||
if result.owner.kind == skModule:
|
if result.owner.kind == skModule:
|
||||||
incl(result.flags, sfGlobal)
|
incl(result.flags, sfGlobal)
|
||||||
suggestSym(c.config, n.info, result, c.graph.usageSym)
|
|
||||||
|
proc getLineInfo(n: PNode): TLineInfo =
|
||||||
|
case n.kind
|
||||||
|
of nkPostfix:
|
||||||
|
getLineInfo(n.sons[1])
|
||||||
|
of nkAccQuoted, nkPragmaExpr:
|
||||||
|
getLineInfo(n.sons[0])
|
||||||
|
else:
|
||||||
|
n.info
|
||||||
|
let info = getLineInfo(n)
|
||||||
|
suggestSym(c.config, info, result, c.graph.usageSym)
|
||||||
|
|
||||||
proc checkNilable(c: PContext; v: PSym) =
|
proc checkNilable(c: PContext; v: PSym) =
|
||||||
if {sfGlobal, sfImportC} * v.flags == {sfGlobal} and
|
if {sfGlobal, sfImportC} * v.flags == {sfGlobal} and
|
||||||
{tfNotNil, tfNeedsInit} * v.typ.flags != {}:
|
{tfNotNil, tfNeedsInit} * v.typ.flags != {}:
|
||||||
if v.ast.isNil:
|
if v.astdef.isNil:
|
||||||
message(c.config, v.info, warnProveInit, v.name.s)
|
message(c.config, v.info, warnProveInit, v.name.s)
|
||||||
elif tfNotNil in v.typ.flags and tfNotNil notin v.ast.typ.flags:
|
elif tfNotNil in v.typ.flags and tfNotNil notin v.astdef.typ.flags:
|
||||||
message(c.config, v.info, warnProveInit, v.name.s)
|
message(c.config, v.info, warnProveInit, v.name.s)
|
||||||
|
|
||||||
include semasgn
|
include semasgn
|
||||||
|
|
@ -470,7 +476,7 @@ proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode =
|
||||||
# this can only happen for errornous var statements:
|
# this can only happen for errornous var statements:
|
||||||
if typ == nil: continue
|
if typ == nil: continue
|
||||||
typeAllowedCheck(c.config, a.info, typ, symkind, if c.matchedConcept != nil: {taConcept} else: {})
|
typeAllowedCheck(c.config, a.info, typ, symkind, if c.matchedConcept != nil: {taConcept} else: {})
|
||||||
liftTypeBoundOps(c, typ, a.info)
|
liftTypeBoundOps(c.graph, typ, a.info)
|
||||||
var tup = skipTypes(typ, {tyGenericInst, tyAlias, tySink})
|
var tup = skipTypes(typ, {tyGenericInst, tyAlias, tySink})
|
||||||
if a.kind == nkVarTuple:
|
if a.kind == nkVarTuple:
|
||||||
if tup.kind != tyTuple:
|
if tup.kind != tyTuple:
|
||||||
|
|
@ -515,8 +521,6 @@ proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode =
|
||||||
message(c.config, a.info, warnShadowIdent, v.name.s)
|
message(c.config, a.info, warnShadowIdent, v.name.s)
|
||||||
if a.kind != nkVarTuple:
|
if a.kind != nkVarTuple:
|
||||||
if def.kind != nkEmpty:
|
if def.kind != nkEmpty:
|
||||||
# this is needed for the evaluation pass and for the guard checking:
|
|
||||||
v.ast = def
|
|
||||||
if sfThread in v.flags: localError(c.config, def.info, errThreadvarCannotInit)
|
if sfThread in v.flags: localError(c.config, def.info, errThreadvarCannotInit)
|
||||||
setVarType(c, v, typ)
|
setVarType(c, v, typ)
|
||||||
b = newNodeI(nkIdentDefs, a.info)
|
b = newNodeI(nkIdentDefs, a.info)
|
||||||
|
|
@ -528,6 +532,23 @@ proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode =
|
||||||
addSon(b, a.sons[length-2])
|
addSon(b, a.sons[length-2])
|
||||||
addSon(b, copyTree(def))
|
addSon(b, copyTree(def))
|
||||||
addToVarSection(c, result, n, b)
|
addToVarSection(c, result, n, b)
|
||||||
|
if optOldAst in c.config.options:
|
||||||
|
if def.kind != nkEmpty:
|
||||||
|
v.ast = def
|
||||||
|
else:
|
||||||
|
# this is needed for the evaluation pass, guard checking
|
||||||
|
# and custom pragmas:
|
||||||
|
var ast = newNodeI(nkIdentDefs, a.info)
|
||||||
|
if a[j].kind == nkPragmaExpr:
|
||||||
|
var p = newNodeI(nkPragmaExpr, a.info)
|
||||||
|
p.add newSymNode(v)
|
||||||
|
p.add a[j][1].copyTree
|
||||||
|
ast.add p
|
||||||
|
else:
|
||||||
|
ast.add newSymNode(v)
|
||||||
|
ast.add a.sons[length-2].copyTree
|
||||||
|
ast.add def
|
||||||
|
v.ast = ast
|
||||||
else:
|
else:
|
||||||
if def.kind in {nkPar, nkTupleConstr}: v.ast = def[j]
|
if def.kind in {nkPar, nkTupleConstr}: v.ast = def[j]
|
||||||
# bug #7663, for 'nim check' this can be a non-tuple:
|
# bug #7663, for 'nim check' this can be a non-tuple:
|
||||||
|
|
@ -545,21 +566,22 @@ proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode =
|
||||||
|
|
||||||
proc semConst(c: PContext, n: PNode): PNode =
|
proc semConst(c: PContext, n: PNode): PNode =
|
||||||
result = copyNode(n)
|
result = copyNode(n)
|
||||||
|
inc c.inStaticContext
|
||||||
for i in countup(0, sonsLen(n) - 1):
|
for i in countup(0, sonsLen(n) - 1):
|
||||||
var a = n.sons[i]
|
var a = n.sons[i]
|
||||||
if c.config.cmd == cmdIdeTools: suggestStmt(c, a)
|
if c.config.cmd == cmdIdeTools: suggestStmt(c, a)
|
||||||
if a.kind == nkCommentStmt: continue
|
if a.kind == nkCommentStmt: continue
|
||||||
if a.kind != nkConstDef: illFormedAst(a, c.config)
|
if a.kind notin {nkConstDef, nkVarTuple}: illFormedAst(a, c.config)
|
||||||
checkSonsLen(a, 3, c.config)
|
checkMinSonsLen(a, 3, c.config)
|
||||||
var v = semIdentDef(c, a.sons[0], skConst)
|
var length = sonsLen(a)
|
||||||
styleCheckDef(c.config, v)
|
|
||||||
onDef(a[0].info, v)
|
|
||||||
var typ: PType = nil
|
|
||||||
if a.sons[1].kind != nkEmpty: typ = semTypeNode(c, a.sons[1], nil)
|
|
||||||
|
|
||||||
var def = semConstExpr(c, a.sons[2])
|
var typ: PType = nil
|
||||||
|
if a.sons[length-2].kind != nkEmpty:
|
||||||
|
typ = semTypeNode(c, a.sons[length-2], nil)
|
||||||
|
|
||||||
|
var def = semConstExpr(c, a.sons[length-1])
|
||||||
if def == nil:
|
if def == nil:
|
||||||
localError(c.config, a.sons[2].info, errConstExprExpected)
|
localError(c.config, a.sons[length-1].info, errConstExprExpected)
|
||||||
continue
|
continue
|
||||||
# check type compatibility between def.typ and typ:
|
# check type compatibility between def.typ and typ:
|
||||||
if typ != nil:
|
if typ != nil:
|
||||||
|
|
@ -567,21 +589,44 @@ proc semConst(c: PContext, n: PNode): PNode =
|
||||||
else:
|
else:
|
||||||
typ = def.typ
|
typ = def.typ
|
||||||
if typ == nil:
|
if typ == nil:
|
||||||
localError(c.config, a.sons[2].info, errConstExprExpected)
|
localError(c.config, a.sons[length-1].info, errConstExprExpected)
|
||||||
continue
|
continue
|
||||||
if typeAllowed(typ, skConst) != nil and def.kind != nkNilLit:
|
if typeAllowed(typ, skConst) != nil and def.kind != nkNilLit:
|
||||||
localError(c.config, a.info, "invalid type for const: " & typeToString(typ))
|
localError(c.config, a.info, "invalid type for const: " & typeToString(typ))
|
||||||
continue
|
continue
|
||||||
setVarType(c, v, typ)
|
|
||||||
v.ast = def # no need to copy
|
var b: PNode
|
||||||
|
if a.kind == nkVarTuple:
|
||||||
|
if typ.kind != tyTuple:
|
||||||
|
localError(c.config, a.info, errXExpected, "tuple")
|
||||||
|
elif length-2 != sonsLen(typ):
|
||||||
|
localError(c.config, a.info, errWrongNumberOfVariables)
|
||||||
|
b = newNodeI(nkVarTuple, a.info)
|
||||||
|
newSons(b, length)
|
||||||
|
b.sons[length-2] = a.sons[length-2]
|
||||||
|
b.sons[length-1] = def
|
||||||
|
|
||||||
|
for j in countup(0, length-3):
|
||||||
|
var v = semIdentDef(c, a.sons[j], skConst)
|
||||||
if sfGenSym notin v.flags: addInterfaceDecl(c, v)
|
if sfGenSym notin v.flags: addInterfaceDecl(c, v)
|
||||||
elif v.owner == nil: v.owner = getCurrOwner(c)
|
elif v.owner == nil: v.owner = getCurrOwner(c)
|
||||||
var b = newNodeI(nkConstDef, a.info)
|
styleCheckDef(c.config, v)
|
||||||
|
onDef(a[j].info, v)
|
||||||
|
|
||||||
|
if a.kind != nkVarTuple:
|
||||||
|
setVarType(c, v, typ)
|
||||||
|
v.ast = def # no need to copy
|
||||||
|
b = newNodeI(nkConstDef, a.info)
|
||||||
if importantComments(c.config): b.comment = a.comment
|
if importantComments(c.config): b.comment = a.comment
|
||||||
addSon(b, newSymNode(v))
|
addSon(b, newSymNode(v))
|
||||||
addSon(b, a.sons[1])
|
addSon(b, a.sons[1])
|
||||||
addSon(b, copyTree(def))
|
addSon(b, copyTree(def))
|
||||||
|
else:
|
||||||
|
setVarType(c, v, typ.sons[j])
|
||||||
|
v.ast = def[j]
|
||||||
|
b.sons[j] = newSymNode(v)
|
||||||
addSon(result,b)
|
addSon(result,b)
|
||||||
|
dec c.inStaticContext
|
||||||
|
|
||||||
include semfields
|
include semfields
|
||||||
|
|
||||||
|
|
@ -637,7 +682,7 @@ proc semForVars(c: PContext, n: PNode; flags: TExprFlags): PNode =
|
||||||
openScope(c)
|
openScope(c)
|
||||||
n.sons[length-1] = semExprBranch(c, n.sons[length-1], flags)
|
n.sons[length-1] = semExprBranch(c, n.sons[length-1], flags)
|
||||||
if efInTypeof notin flags:
|
if efInTypeof notin flags:
|
||||||
discardCheck(c, n.sons[length-1], flags)
|
n.sons[^1] = discardCheck(c, n.sons[^1], flags)
|
||||||
closeScope(c)
|
closeScope(c)
|
||||||
dec(c.p.nestedLoopCounter)
|
dec(c.p.nestedLoopCounter)
|
||||||
|
|
||||||
|
|
@ -824,7 +869,8 @@ proc semCase(c: PContext, n: PNode; flags: TExprFlags): PNode =
|
||||||
closeScope(c)
|
closeScope(c)
|
||||||
if isEmptyType(typ) or typ.kind in {tyNil, tyExpr} or
|
if isEmptyType(typ) or typ.kind in {tyNil, tyExpr} or
|
||||||
(not hasElse and efInTypeof notin flags):
|
(not hasElse and efInTypeof notin flags):
|
||||||
for i in 1..n.len-1: discardCheck(c, n.sons[i].lastSon, flags)
|
for i in 1..n.len-1:
|
||||||
|
n.sons[i].sons[^1] = discardCheck(c, n.sons[i].sons[^1], flags)
|
||||||
# propagate any enforced VoidContext:
|
# propagate any enforced VoidContext:
|
||||||
if typ == c.enforceVoidContext:
|
if typ == c.enforceVoidContext:
|
||||||
result.typ = c.enforceVoidContext
|
result.typ = c.enforceVoidContext
|
||||||
|
|
@ -1074,6 +1120,8 @@ proc typeSectionRightSidePass(c: PContext, n: PNode) =
|
||||||
#debug s.typ
|
#debug s.typ
|
||||||
s.ast = a
|
s.ast = a
|
||||||
popOwner(c)
|
popOwner(c)
|
||||||
|
if sfExportc in s.flags and s.typ.kind == tyAlias:
|
||||||
|
localError(c.config, name.info, "{.exportc.} not allowed for type aliases")
|
||||||
let aa = a.sons[2]
|
let aa = a.sons[2]
|
||||||
if aa.kind in {nkRefTy, nkPtrTy} and aa.len == 1 and
|
if aa.kind in {nkRefTy, nkPtrTy} and aa.len == 1 and
|
||||||
aa.sons[0].kind == nkObjectTy:
|
aa.sons[0].kind == nkObjectTy:
|
||||||
|
|
@ -1090,8 +1138,12 @@ proc typeSectionRightSidePass(c: PContext, n: PNode) =
|
||||||
|
|
||||||
|
|
||||||
proc checkForMetaFields(c: PContext; n: PNode) =
|
proc checkForMetaFields(c: PContext; n: PNode) =
|
||||||
template checkMeta(t) =
|
proc checkMeta(c: PContext; n: PNode; t: PType) =
|
||||||
if t != nil and t.isMetaType and tfGenericTypeParam notin t.flags:
|
if t != nil and t.isMetaType and tfGenericTypeParam notin t.flags:
|
||||||
|
if t.kind == tyBuiltInTypeClass and t.len == 1 and t.sons[0].kind == tyProc:
|
||||||
|
localError(c.config, n.info, ("'$1' is not a concrete type; " &
|
||||||
|
"for a callback without parameters use 'proc()'") % t.typeToString)
|
||||||
|
else:
|
||||||
localError(c.config, n.info, errTIsNotAConcreteType % t.typeToString)
|
localError(c.config, n.info, errTIsNotAConcreteType % t.typeToString)
|
||||||
|
|
||||||
if n.isNil: return
|
if n.isNil: return
|
||||||
|
|
@ -1107,9 +1159,9 @@ proc checkForMetaFields(c: PContext; n: PNode) =
|
||||||
tyProc, tyGenericInvocation, tyGenericInst, tyAlias, tySink:
|
tyProc, tyGenericInvocation, tyGenericInst, tyAlias, tySink:
|
||||||
let start = ord(t.kind in {tyGenericInvocation, tyGenericInst})
|
let start = ord(t.kind in {tyGenericInvocation, tyGenericInst})
|
||||||
for i in start ..< t.len:
|
for i in start ..< t.len:
|
||||||
checkMeta(t.sons[i])
|
checkMeta(c, n, t.sons[i])
|
||||||
else:
|
else:
|
||||||
checkMeta(t)
|
checkMeta(c, n, t)
|
||||||
else:
|
else:
|
||||||
internalAssert c.config, false
|
internalAssert c.config, false
|
||||||
|
|
||||||
|
|
@ -1205,9 +1257,6 @@ proc semTypeSection(c: PContext, n: PNode): PNode =
|
||||||
|
|
||||||
proc semParamList(c: PContext, n, genericParams: PNode, s: PSym) =
|
proc semParamList(c: PContext, n, genericParams: PNode, s: PSym) =
|
||||||
s.typ = semProcTypeNode(c, n, genericParams, nil, s.kind)
|
s.typ = semProcTypeNode(c, n, genericParams, nil, s.kind)
|
||||||
if s.kind notin {skMacro, skTemplate}:
|
|
||||||
if s.typ.sons[0] != nil and s.typ.sons[0].kind == tyStmt:
|
|
||||||
localError(c.config, n.info, "invalid return type: 'stmt'")
|
|
||||||
|
|
||||||
proc addParams(c: PContext, n: PNode, kind: TSymKind) =
|
proc addParams(c: PContext, n: PNode, kind: TSymKind) =
|
||||||
for i in countup(1, sonsLen(n)-1):
|
for i in countup(1, sonsLen(n)-1):
|
||||||
|
|
@ -1423,8 +1472,15 @@ proc maybeAddResult(c: PContext, s: PSym, n: PNode) =
|
||||||
addResult(c, s.typ.sons[0], n.info, s.kind)
|
addResult(c, s.typ.sons[0], n.info, s.kind)
|
||||||
addResultNode(c, n)
|
addResultNode(c, n)
|
||||||
|
|
||||||
|
proc canonType(c: PContext, t: PType): PType =
|
||||||
|
if t.kind == tySequence:
|
||||||
|
result = c.graph.sysTypes[tySequence]
|
||||||
|
else:
|
||||||
|
result = t
|
||||||
|
|
||||||
proc semOverride(c: PContext, s: PSym, n: PNode) =
|
proc semOverride(c: PContext, s: PSym, n: PNode) =
|
||||||
case s.name.s.normalize
|
let name = s.name.s.normalize
|
||||||
|
case name
|
||||||
of "=destroy":
|
of "=destroy":
|
||||||
let t = s.typ
|
let t = s.typ
|
||||||
var noError = false
|
var noError = false
|
||||||
|
|
@ -1436,12 +1492,16 @@ proc semOverride(c: PContext, s: PSym, n: PNode) =
|
||||||
elif obj.kind == tyGenericInvocation: obj = obj.sons[0]
|
elif obj.kind == tyGenericInvocation: obj = obj.sons[0]
|
||||||
else: break
|
else: break
|
||||||
if obj.kind in {tyObject, tyDistinct, tySequence, tyString}:
|
if obj.kind in {tyObject, tyDistinct, tySequence, tyString}:
|
||||||
|
obj = canonType(c, obj)
|
||||||
if obj.destructor.isNil:
|
if obj.destructor.isNil:
|
||||||
obj.destructor = s
|
obj.destructor = s
|
||||||
else:
|
else:
|
||||||
localError(c.config, n.info, errGenerated,
|
localError(c.config, n.info, errGenerated,
|
||||||
"cannot bind another '" & s.name.s & "' to: " & typeToString(obj))
|
"cannot bind another '" & s.name.s & "' to: " & typeToString(obj))
|
||||||
noError = true
|
noError = true
|
||||||
|
if obj.owner.getModule != s.getModule:
|
||||||
|
localError(c.config, n.info, errGenerated,
|
||||||
|
"type bound operation `=destroy` can be defined only in the same module with its type (" & obj.typeToString() & ")")
|
||||||
if not noError and sfSystemModule notin s.owner.flags:
|
if not noError and sfSystemModule notin s.owner.flags:
|
||||||
localError(c.config, n.info, errGenerated,
|
localError(c.config, n.info, errGenerated,
|
||||||
"signature for '" & s.name.s & "' must be proc[T: object](x: var T)")
|
"signature for '" & s.name.s & "' must be proc[T: object](x: var T)")
|
||||||
|
|
@ -1465,6 +1525,11 @@ proc semOverride(c: PContext, s: PSym, n: PNode) =
|
||||||
else:
|
else:
|
||||||
localError(c.config, n.info, errGenerated,
|
localError(c.config, n.info, errGenerated,
|
||||||
"cannot bind 'deepCopy' to: " & typeToString(t))
|
"cannot bind 'deepCopy' to: " & typeToString(t))
|
||||||
|
|
||||||
|
if t.owner.getModule != s.getModule:
|
||||||
|
localError(c.config, n.info, errGenerated,
|
||||||
|
"type bound operation `" & name & "` can be defined only in the same module with its type (" & t.typeToString() & ")")
|
||||||
|
|
||||||
else:
|
else:
|
||||||
localError(c.config, n.info, errGenerated,
|
localError(c.config, n.info, errGenerated,
|
||||||
"signature for 'deepCopy' must be proc[T: ptr|ref](x: T): T")
|
"signature for 'deepCopy' must be proc[T: ptr|ref](x: T): T")
|
||||||
|
|
@ -1487,12 +1552,18 @@ proc semOverride(c: PContext, s: PSym, n: PNode) =
|
||||||
objB = objB.sons[0]
|
objB = objB.sons[0]
|
||||||
else: break
|
else: break
|
||||||
if obj.kind in {tyObject, tyDistinct, tySequence, tyString} and sameType(obj, objB):
|
if obj.kind in {tyObject, tyDistinct, tySequence, tyString} and sameType(obj, objB):
|
||||||
|
# attach these ops to the canonical tySequence
|
||||||
|
obj = canonType(c, obj)
|
||||||
let opr = if s.name.s == "=": addr(obj.assignment) else: addr(obj.sink)
|
let opr = if s.name.s == "=": addr(obj.assignment) else: addr(obj.sink)
|
||||||
if opr[].isNil:
|
if opr[].isNil:
|
||||||
opr[] = s
|
opr[] = s
|
||||||
else:
|
else:
|
||||||
localError(c.config, n.info, errGenerated,
|
localError(c.config, n.info, errGenerated,
|
||||||
"cannot bind another '" & s.name.s & "' to: " & typeToString(obj))
|
"cannot bind another '" & s.name.s & "' to: " & typeToString(obj))
|
||||||
|
if obj.owner.getModule != s.getModule:
|
||||||
|
localError(c.config, n.info, errGenerated,
|
||||||
|
"type bound operation `" & name & "` can be defined only in the same module with its type (" & obj.typeToString() & ")")
|
||||||
|
|
||||||
return
|
return
|
||||||
if sfSystemModule notin s.owner.flags:
|
if sfSystemModule notin s.owner.flags:
|
||||||
localError(c.config, n.info, errGenerated,
|
localError(c.config, n.info, errGenerated,
|
||||||
|
|
@ -1542,7 +1613,7 @@ proc semMethodPrototype(c: PContext; s: PSym; n: PNode) =
|
||||||
foundObj = true
|
foundObj = true
|
||||||
x.methods.add((col,s))
|
x.methods.add((col,s))
|
||||||
if not foundObj:
|
if not foundObj:
|
||||||
message(c.config, n.info, warnDeprecated, "generic method not attachable to object type")
|
message(c.config, n.info, warnDeprecated, "generic method not attachable to object type is deprecated")
|
||||||
else:
|
else:
|
||||||
# why check for the body? bug #2400 has none. Checking for sfForward makes
|
# why check for the body? bug #2400 has none. Checking for sfForward makes
|
||||||
# no sense either.
|
# no sense either.
|
||||||
|
|
@ -1975,7 +2046,7 @@ proc semStmtList(c: PContext, n: PNode, flags: TExprFlags): PNode =
|
||||||
n.typ = n.sons[i].typ
|
n.typ = n.sons[i].typ
|
||||||
if not isEmptyType(n.typ): n.kind = nkStmtListExpr
|
if not isEmptyType(n.typ): n.kind = nkStmtListExpr
|
||||||
elif i != last or voidContext:
|
elif i != last or voidContext:
|
||||||
discardCheck(c, n.sons[i], flags)
|
n.sons[i] = discardCheck(c, n.sons[i], flags)
|
||||||
else:
|
else:
|
||||||
n.typ = n.sons[i].typ
|
n.typ = n.sons[i].typ
|
||||||
if not isEmptyType(n.typ): n.kind = nkStmtListExpr
|
if not isEmptyType(n.typ): n.kind = nkStmtListExpr
|
||||||
|
|
|
||||||
|
|
@ -80,8 +80,13 @@ proc semEnum(c: PContext, n: PNode, prev: PType): PType =
|
||||||
if isPure: initStrTable(symbols)
|
if isPure: initStrTable(symbols)
|
||||||
var hasNull = false
|
var hasNull = false
|
||||||
for i in countup(1, sonsLen(n) - 1):
|
for i in countup(1, sonsLen(n) - 1):
|
||||||
|
if n.sons[i].kind == nkEmpty: continue
|
||||||
case n.sons[i].kind
|
case n.sons[i].kind
|
||||||
of nkEnumFieldDef:
|
of nkEnumFieldDef:
|
||||||
|
if n.sons[i].sons[0].kind == nkPragmaExpr:
|
||||||
|
e = newSymS(skEnumField, n.sons[i].sons[0].sons[0], c)
|
||||||
|
pragma(c, e, n.sons[i].sons[0].sons[1], enumFieldPragmas)
|
||||||
|
else:
|
||||||
e = newSymS(skEnumField, n.sons[i].sons[0], c)
|
e = newSymS(skEnumField, n.sons[i].sons[0], c)
|
||||||
var v = semConstExpr(c, n.sons[i].sons[1])
|
var v = semConstExpr(c, n.sons[i].sons[1])
|
||||||
var strVal: PNode = nil
|
var strVal: PNode = nil
|
||||||
|
|
@ -91,7 +96,7 @@ proc semEnum(c: PContext, n: PNode, prev: PType): PType =
|
||||||
strVal = v.sons[1] # second tuple part is the string value
|
strVal = v.sons[1] # second tuple part is the string value
|
||||||
if skipTypes(strVal.typ, abstractInst).kind in {tyString, tyCString}:
|
if skipTypes(strVal.typ, abstractInst).kind in {tyString, tyCString}:
|
||||||
if not isOrdinalType(v.sons[0].typ, allowEnumWithHoles=true):
|
if not isOrdinalType(v.sons[0].typ, allowEnumWithHoles=true):
|
||||||
localError(c.config, v.sons[0].info, errOrdinalTypeExpected)
|
localError(c.config, v.sons[0].info, errOrdinalTypeExpected & "; given: " & typeToString(v.sons[0].typ, preferDesc))
|
||||||
x = getOrdValue(v.sons[0]) # first tuple part is the ordinal
|
x = getOrdValue(v.sons[0]) # first tuple part is the ordinal
|
||||||
else:
|
else:
|
||||||
localError(c.config, strVal.info, errStringLiteralExpected)
|
localError(c.config, strVal.info, errStringLiteralExpected)
|
||||||
|
|
@ -102,7 +107,7 @@ proc semEnum(c: PContext, n: PNode, prev: PType): PType =
|
||||||
x = counter
|
x = counter
|
||||||
else:
|
else:
|
||||||
if not isOrdinalType(v.typ, allowEnumWithHoles=true):
|
if not isOrdinalType(v.typ, allowEnumWithHoles=true):
|
||||||
localError(c.config, v.info, errOrdinalTypeExpected)
|
localError(c.config, v.info, errOrdinalTypeExpected & "; given: " & typeToString(v.typ, preferDesc))
|
||||||
x = getOrdValue(v)
|
x = getOrdValue(v)
|
||||||
if i != 1:
|
if i != 1:
|
||||||
if x != counter: incl(result.flags, tfEnumHasHoles)
|
if x != counter: incl(result.flags, tfEnumHasHoles)
|
||||||
|
|
@ -115,6 +120,9 @@ proc semEnum(c: PContext, n: PNode, prev: PType): PType =
|
||||||
e = n.sons[i].sym
|
e = n.sons[i].sym
|
||||||
of nkIdent, nkAccQuoted:
|
of nkIdent, nkAccQuoted:
|
||||||
e = newSymS(skEnumField, n.sons[i], c)
|
e = newSymS(skEnumField, n.sons[i], c)
|
||||||
|
of nkPragmaExpr:
|
||||||
|
e = newSymS(skEnumField, n.sons[i].sons[0], c)
|
||||||
|
pragma(c, e, n.sons[i].sons[1], enumFieldPragmas)
|
||||||
else:
|
else:
|
||||||
illFormedAst(n[i], c.config)
|
illFormedAst(n[i], c.config)
|
||||||
e.typ = result
|
e.typ = result
|
||||||
|
|
@ -133,7 +141,7 @@ proc semEnum(c: PContext, n: PNode, prev: PType): PType =
|
||||||
if isPure and (let conflict = strTableInclReportConflict(symbols, e); conflict != nil):
|
if isPure and (let conflict = strTableInclReportConflict(symbols, e); conflict != nil):
|
||||||
wrongRedefinition(c, e.info, e.name.s, conflict.info)
|
wrongRedefinition(c, e.info, e.name.s, conflict.info)
|
||||||
inc(counter)
|
inc(counter)
|
||||||
if not hasNull: incl(result.flags, tfNeedsInit)
|
if tfNotNil in e.typ.flags and not hasNull: incl(result.flags, tfNeedsInit)
|
||||||
|
|
||||||
proc semSet(c: PContext, n: PNode, prev: PType): PType =
|
proc semSet(c: PContext, n: PNode, prev: PType): PType =
|
||||||
result = newOrPrevType(tySet, prev, c)
|
result = newOrPrevType(tySet, prev, c)
|
||||||
|
|
@ -202,7 +210,7 @@ proc semAnyRef(c: PContext; n: PNode; kind: TTypeKind; prev: PType): PType =
|
||||||
tyError, tyObject}:
|
tyError, tyObject}:
|
||||||
message c.config, n[i].info, errGenerated, "region needs to be an object type"
|
message c.config, n[i].info, errGenerated, "region needs to be an object type"
|
||||||
else:
|
else:
|
||||||
message(c.config, n.info, warnDeprecated, "region for pointer types")
|
message(c.config, n.info, warnDeprecated, "region for pointer types is deprecated")
|
||||||
addSonSkipIntLit(result, region)
|
addSonSkipIntLit(result, region)
|
||||||
addSonSkipIntLit(result, t)
|
addSonSkipIntLit(result, t)
|
||||||
if tfPartial in result.flags:
|
if tfPartial in result.flags:
|
||||||
|
|
@ -591,6 +599,13 @@ proc semCaseBranch(c: PContext, t, branch: PNode, branchIndex: int,
|
||||||
for i in lastIndex.succ..(sonsLen(branch) - 2):
|
for i in lastIndex.succ..(sonsLen(branch) - 2):
|
||||||
checkForOverlap(c, t, i, branchIndex)
|
checkForOverlap(c, t, i, branchIndex)
|
||||||
|
|
||||||
|
proc toCover(c: PContext, t: PType): BiggestInt =
|
||||||
|
let t2 = skipTypes(t, abstractVarRange-{tyTypeDesc})
|
||||||
|
if t2.kind == tyEnum and enumHasHoles(t2):
|
||||||
|
result = sonsLen(t2.n)
|
||||||
|
else:
|
||||||
|
result = lengthOrd(c.config, skipTypes(t, abstractVar-{tyTypeDesc}))
|
||||||
|
|
||||||
proc semRecordNodeAux(c: PContext, n: PNode, check: var IntSet, pos: var int,
|
proc semRecordNodeAux(c: PContext, n: PNode, check: var IntSet, pos: var int,
|
||||||
father: PNode, rectype: PType, hasCaseFields = false)
|
father: PNode, rectype: PType, hasCaseFields = false)
|
||||||
proc semRecordCase(c: PContext, n: PNode, check: var IntSet, pos: var int,
|
proc semRecordCase(c: PContext, n: PNode, check: var IntSet, pos: var int,
|
||||||
|
|
@ -603,15 +618,16 @@ proc semRecordCase(c: PContext, n: PNode, check: var IntSet, pos: var int,
|
||||||
return
|
return
|
||||||
incl(a.sons[0].sym.flags, sfDiscriminant)
|
incl(a.sons[0].sym.flags, sfDiscriminant)
|
||||||
var covered: BiggestInt = 0
|
var covered: BiggestInt = 0
|
||||||
|
var chckCovered = false
|
||||||
var typ = skipTypes(a.sons[0].typ, abstractVar-{tyTypeDesc})
|
var typ = skipTypes(a.sons[0].typ, abstractVar-{tyTypeDesc})
|
||||||
|
case typ.kind
|
||||||
|
of tyInt..tyInt64, tyChar, tyEnum, tyUInt..tyUInt32, tyBool, tyRange:
|
||||||
|
chckCovered = true
|
||||||
|
of tyFloat..tyFloat128, tyString, tyError:
|
||||||
|
discard
|
||||||
|
else:
|
||||||
if not isOrdinalType(typ):
|
if not isOrdinalType(typ):
|
||||||
localError(c.config, n.info, "selector must be of an ordinal type")
|
localError(c.config, n.info, "selector must be of an ordinal type, float or string")
|
||||||
elif firstOrd(c.config, typ) != 0:
|
|
||||||
localError(c.config, n.info, "low(" & $a.sons[0].sym.name.s &
|
|
||||||
") must be 0 for discriminant")
|
|
||||||
elif lengthOrd(c.config, typ) > 0x00007FFF:
|
|
||||||
localError(c.config, n.info, "len($1) must be less than 32768" % a.sons[0].sym.name.s)
|
|
||||||
var chckCovered = true
|
|
||||||
for i in countup(1, sonsLen(n) - 1):
|
for i in countup(1, sonsLen(n) - 1):
|
||||||
var b = copyTree(n.sons[i])
|
var b = copyTree(n.sons[i])
|
||||||
addSon(a, b)
|
addSon(a, b)
|
||||||
|
|
@ -620,12 +636,14 @@ proc semRecordCase(c: PContext, n: PNode, check: var IntSet, pos: var int,
|
||||||
checkMinSonsLen(b, 2, c.config)
|
checkMinSonsLen(b, 2, c.config)
|
||||||
semCaseBranch(c, a, b, i, covered)
|
semCaseBranch(c, a, b, i, covered)
|
||||||
of nkElse:
|
of nkElse:
|
||||||
chckCovered = false
|
|
||||||
checkSonsLen(b, 1, c.config)
|
checkSonsLen(b, 1, c.config)
|
||||||
|
if chckCovered and covered == toCover(c, a.sons[0].typ):
|
||||||
|
localError(c.config, b.info, "invalid else, all cases are already covered")
|
||||||
|
chckCovered = false
|
||||||
else: illFormedAst(n, c.config)
|
else: illFormedAst(n, c.config)
|
||||||
delSon(b, sonsLen(b) - 1)
|
delSon(b, sonsLen(b) - 1)
|
||||||
semRecordNodeAux(c, lastSon(n.sons[i]), check, pos, b, rectype, hasCaseFields = true)
|
semRecordNodeAux(c, lastSon(n.sons[i]), check, pos, b, rectype, hasCaseFields = true)
|
||||||
if chckCovered and covered != lengthOrd(c.config, a.sons[0].typ):
|
if chckCovered and covered != toCover(c, a.sons[0].typ):
|
||||||
localError(c.config, a.info, "not all cases are covered")
|
localError(c.config, a.info, "not all cases are covered")
|
||||||
addSon(father, a)
|
addSon(father, a)
|
||||||
|
|
||||||
|
|
@ -1010,7 +1028,12 @@ proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode,
|
||||||
proc semParamType(c: PContext, n: PNode, constraint: var PNode): PType =
|
proc semParamType(c: PContext, n: PNode, constraint: var PNode): PType =
|
||||||
if n.kind == nkCurlyExpr:
|
if n.kind == nkCurlyExpr:
|
||||||
result = semTypeNode(c, n.sons[0], nil)
|
result = semTypeNode(c, n.sons[0], nil)
|
||||||
constraint = semNodeKindConstraints(n, c.config)
|
constraint = semNodeKindConstraints(n, c.config, 1)
|
||||||
|
elif n.kind == nkCall and
|
||||||
|
n[0].kind in {nkIdent, nkSym, nkOpenSymChoice, nkClosedSymChoice} and
|
||||||
|
considerQuotedIdent(c, n[0]).s == "{}":
|
||||||
|
result = semTypeNode(c, n[1], nil)
|
||||||
|
constraint = semNodeKindConstraints(n, c.config, 2)
|
||||||
else:
|
else:
|
||||||
result = semTypeNode(c, n, nil)
|
result = semTypeNode(c, n, nil)
|
||||||
|
|
||||||
|
|
@ -1054,6 +1077,11 @@ proc semProcTypeNode(c: PContext, n, genericParams: PNode,
|
||||||
|
|
||||||
if hasType:
|
if hasType:
|
||||||
typ = semParamType(c, a.sons[length-2], constraint)
|
typ = semParamType(c, a.sons[length-2], constraint)
|
||||||
|
var owner = getCurrOwner(c).owner
|
||||||
|
# TODO: Disallow typed/untyped in procs in the compiler/stdlib
|
||||||
|
if (owner.kind != skModule or owner.owner.name.s != "stdlib") and
|
||||||
|
kind == skProc and (typ.kind == tyStmt or typ.kind == tyExpr):
|
||||||
|
localError(c.config, a.sons[length-2].info, "'" & typ.sym.name.s & "' is only allowed in templates and macros")
|
||||||
|
|
||||||
if hasDefault:
|
if hasDefault:
|
||||||
def = a[^1]
|
def = a[^1]
|
||||||
|
|
@ -1130,12 +1158,15 @@ proc semProcTypeNode(c: PContext, n, genericParams: PNode,
|
||||||
# turn explicit 'void' return type into 'nil' because the rest of the
|
# turn explicit 'void' return type into 'nil' because the rest of the
|
||||||
# compiler only checks for 'nil':
|
# compiler only checks for 'nil':
|
||||||
if skipTypes(r, {tyGenericInst, tyAlias, tySink}).kind != tyVoid:
|
if skipTypes(r, {tyGenericInst, tyAlias, tySink}).kind != tyVoid:
|
||||||
|
if kind notin {skMacro, skTemplate} and r.kind in {tyStmt, tyExpr}:
|
||||||
|
localError(c.config, n.sons[0].info, "return type '" & typeToString(r) &
|
||||||
|
"' is only valid for macros and templates")
|
||||||
# 'auto' as a return type does not imply a generic:
|
# 'auto' as a return type does not imply a generic:
|
||||||
if r.kind == tyAnything:
|
elif r.kind == tyAnything:
|
||||||
# 'p(): auto' and 'p(): expr' are equivalent, but the rest of the
|
# 'p(): auto' and 'p(): expr' are equivalent, but the rest of the
|
||||||
# compiler is hardly aware of 'auto':
|
# compiler is hardly aware of 'auto':
|
||||||
r = newTypeS(tyExpr, c)
|
r = newTypeS(tyExpr, c)
|
||||||
elif r.kind != tyExpr:
|
else:
|
||||||
if r.sym == nil or sfAnon notin r.sym.flags:
|
if r.sym == nil or sfAnon notin r.sym.flags:
|
||||||
let lifted = liftParamType(c, kind, genericParams, r, "result",
|
let lifted = liftParamType(c, kind, genericParams, r, "result",
|
||||||
n.sons[0].info)
|
n.sons[0].info)
|
||||||
|
|
@ -1287,7 +1318,19 @@ proc semGeneric(c: PContext, n: PNode, s: PSym, prev: PType): PType =
|
||||||
if tx != result and tx.kind == tyObject and tx.sons[0] != nil:
|
if tx != result and tx.kind == tyObject and tx.sons[0] != nil:
|
||||||
semObjectTypeForInheritedGenericInst(c, n, tx)
|
semObjectTypeForInheritedGenericInst(c, n, tx)
|
||||||
|
|
||||||
proc maybeAliasType(c: PContext; typeExpr, prev: PType): PType
|
proc maybeAliasType(c: PContext; typeExpr, prev: PType): PType =
|
||||||
|
if typeExpr.kind in {tyObject, tyEnum, tyDistinct, tyForward} and prev != nil:
|
||||||
|
result = newTypeS(tyAlias, c)
|
||||||
|
result.rawAddSon typeExpr
|
||||||
|
result.sym = prev.sym
|
||||||
|
assignType(prev, result)
|
||||||
|
|
||||||
|
proc fixupTypeOf(c: PContext, prev: PType, typExpr: PNode) =
|
||||||
|
if prev != nil:
|
||||||
|
let result = newTypeS(tyAlias, c)
|
||||||
|
result.rawAddSon typExpr.typ
|
||||||
|
result.sym = prev.sym
|
||||||
|
assignType(prev, result)
|
||||||
|
|
||||||
proc semTypeExpr(c: PContext, n: PNode; prev: PType): PType =
|
proc semTypeExpr(c: PContext, n: PNode; prev: PType): PType =
|
||||||
var n = semExprWithType(c, n, {efDetermineType})
|
var n = semExprWithType(c, n, {efDetermineType})
|
||||||
|
|
@ -1391,20 +1434,6 @@ proc semProcTypeWithScope(c: PContext, n: PNode,
|
||||||
when useEffectSystem: setEffectsForProcType(c.graph, result, n.sons[1])
|
when useEffectSystem: setEffectsForProcType(c.graph, result, n.sons[1])
|
||||||
closeScope(c)
|
closeScope(c)
|
||||||
|
|
||||||
proc maybeAliasType(c: PContext; typeExpr, prev: PType): PType =
|
|
||||||
if typeExpr.kind in {tyObject, tyEnum, tyDistinct} and prev != nil:
|
|
||||||
result = newTypeS(tyAlias, c)
|
|
||||||
result.rawAddSon typeExpr
|
|
||||||
result.sym = prev.sym
|
|
||||||
assignType(prev, result)
|
|
||||||
|
|
||||||
proc fixupTypeOf(c: PContext, prev: PType, typExpr: PNode) =
|
|
||||||
if prev != nil:
|
|
||||||
let result = newTypeS(tyAlias, c)
|
|
||||||
result.rawAddSon typExpr.typ
|
|
||||||
result.sym = prev.sym
|
|
||||||
assignType(prev, result)
|
|
||||||
|
|
||||||
proc symFromExpectedTypeNode(c: PContext, n: PNode): PSym =
|
proc symFromExpectedTypeNode(c: PContext, n: PNode): PSym =
|
||||||
if n.kind == nkType:
|
if n.kind == nkType:
|
||||||
result = symFromType(c, n.typ, n.info)
|
result = symFromType(c, n.typ, n.info)
|
||||||
|
|
@ -1551,10 +1580,7 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType =
|
||||||
# XXX figure out why this has children already...
|
# XXX figure out why this has children already...
|
||||||
result.sons.setLen 0
|
result.sons.setLen 0
|
||||||
result.n = nil
|
result.n = nil
|
||||||
if c.config.selectedGc == gcDestructors:
|
|
||||||
result.flags = {tfHasAsgn}
|
result.flags = {tfHasAsgn}
|
||||||
else:
|
|
||||||
result.flags = {}
|
|
||||||
semContainerArg(c, n, "seq", result)
|
semContainerArg(c, n, "seq", result)
|
||||||
else:
|
else:
|
||||||
result = semContainer(c, n, tySequence, "seq", prev)
|
result = semContainer(c, n, tySequence, "seq", prev)
|
||||||
|
|
|
||||||
|
|
@ -2071,6 +2071,7 @@ proc paramTypesMatchAux(m: var TCandidate, f, a: PType,
|
||||||
# constructor in a call:
|
# constructor in a call:
|
||||||
if result == nil and f.kind == tyVarargs:
|
if result == nil and f.kind == tyVarargs:
|
||||||
if f.n != nil:
|
if f.n != nil:
|
||||||
|
# Forward to the varargs converter
|
||||||
result = localConvMatch(c, m, f, a, arg)
|
result = localConvMatch(c, m, f, a, arg)
|
||||||
else:
|
else:
|
||||||
r = typeRel(m, base(f), a)
|
r = typeRel(m, base(f), a)
|
||||||
|
|
@ -2083,10 +2084,10 @@ proc paramTypesMatchAux(m: var TCandidate, f, a: PType,
|
||||||
# bug #4799, varargs accepting subtype relation object
|
# bug #4799, varargs accepting subtype relation object
|
||||||
elif r == isSubtype:
|
elif r == isSubtype:
|
||||||
inc(m.subtypeMatches)
|
inc(m.subtypeMatches)
|
||||||
if f.kind == tyTypeDesc:
|
if base(f).kind == tyTypeDesc:
|
||||||
result = arg
|
result = arg
|
||||||
else:
|
else:
|
||||||
result = implicitConv(nkHiddenSubConv, f, arg, m, c)
|
result = implicitConv(nkHiddenSubConv, base(f), arg, m, c)
|
||||||
m.baseTypeMatch = true
|
m.baseTypeMatch = true
|
||||||
else:
|
else:
|
||||||
result = userConvMatch(c, m, base(f), a, arg)
|
result = userConvMatch(c, m, base(f), a, arg)
|
||||||
|
|
@ -2233,9 +2234,9 @@ proc matchesAux(c: PContext, n, nOrig: PNode,
|
||||||
return
|
return
|
||||||
|
|
||||||
if formal.typ.kind == tyVar:
|
if formal.typ.kind == tyVar:
|
||||||
let arg_converter = if arg.kind == nkHiddenDeref: arg[0] else: arg
|
let argConverter = if arg.kind == nkHiddenDeref: arg[0] else: arg
|
||||||
if arg_converter.kind == nkHiddenCallConv:
|
if argConverter.kind == nkHiddenCallConv:
|
||||||
if arg_converter.typ.kind != tyVar:
|
if argConverter.typ.kind != tyVar:
|
||||||
m.state = csNoMatch
|
m.state = csNoMatch
|
||||||
m.mutabilityProblem = uint8(f-1)
|
m.mutabilityProblem = uint8(f-1)
|
||||||
return
|
return
|
||||||
|
|
@ -2267,8 +2268,7 @@ proc matchesAux(c: PContext, n, nOrig: PNode,
|
||||||
|
|
||||||
if n.sons[a].kind == nkHiddenStdConv:
|
if n.sons[a].kind == nkHiddenStdConv:
|
||||||
doAssert n.sons[a].sons[0].kind == nkEmpty and
|
doAssert n.sons[a].sons[0].kind == nkEmpty and
|
||||||
n.sons[a].sons[1].kind == nkArgList and
|
n.sons[a].sons[1].kind in {nkBracket, nkArgList}
|
||||||
n.sons[a].sons[1].len == 0
|
|
||||||
# Steal the container and pass it along
|
# Steal the container and pass it along
|
||||||
setSon(m.call, formal.position + 1, n.sons[a].sons[1])
|
setSon(m.call, formal.position + 1, n.sons[a].sons[1])
|
||||||
else:
|
else:
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,7 @@ const
|
||||||
szIllegalRecursion* = -2
|
szIllegalRecursion* = -2
|
||||||
szUncomputedSize* = -1
|
szUncomputedSize* = -1
|
||||||
|
|
||||||
proc computeSizeAlign(conf: ConfigRef; typ: PType): void
|
proc computeSizeAlign(conf: ConfigRef; typ: PType)
|
||||||
|
|
||||||
proc computeSubObjectAlign(conf: ConfigRef; n: PNode): BiggestInt =
|
proc computeSubObjectAlign(conf: ConfigRef; n: PNode): BiggestInt =
|
||||||
## returns object alignment
|
## returns object alignment
|
||||||
|
|
@ -49,7 +49,8 @@ proc computeSubObjectAlign(conf: ConfigRef; n: PNode): BiggestInt =
|
||||||
else:
|
else:
|
||||||
result = 1
|
result = 1
|
||||||
|
|
||||||
proc computeObjectOffsetsFoldFunction(conf: ConfigRef; n: PNode, initialOffset: BiggestInt): tuple[offset, align: BiggestInt] =
|
proc computeObjectOffsetsFoldFunction(conf: ConfigRef; n: PNode,
|
||||||
|
initialOffset: BiggestInt): tuple[offset, align: BiggestInt] =
|
||||||
## ``offset`` is the offset within the object, after the node has been written, no padding bytes added
|
## ``offset`` is the offset within the object, after the node has been written, no padding bytes added
|
||||||
## ``align`` maximum alignment from all sub nodes
|
## ``align`` maximum alignment from all sub nodes
|
||||||
assert n != nil
|
assert n != nil
|
||||||
|
|
@ -71,66 +72,52 @@ proc computeObjectOffsetsFoldFunction(conf: ConfigRef; n: PNode, initialOffset:
|
||||||
of nkOfBranch, nkElse:
|
of nkOfBranch, nkElse:
|
||||||
# offset parameter cannot be known yet, it needs to know the alignment first
|
# offset parameter cannot be known yet, it needs to know the alignment first
|
||||||
let align = computeSubObjectAlign(conf, n.sons[i].lastSon)
|
let align = computeSubObjectAlign(conf, n.sons[i].lastSon)
|
||||||
|
|
||||||
if align == szIllegalRecursion:
|
if align == szIllegalRecursion:
|
||||||
result.offset = szIllegalRecursion
|
result.offset = szIllegalRecursion
|
||||||
result.align = szIllegalRecursion
|
result.align = szIllegalRecursion
|
||||||
return
|
return
|
||||||
|
|
||||||
if align == szUnknownSize or maxChildAlign == szUnknownSize:
|
if align == szUnknownSize or maxChildAlign == szUnknownSize:
|
||||||
maxChildAlign = szUnknownSize
|
maxChildAlign = szUnknownSize
|
||||||
else:
|
else:
|
||||||
maxChildAlign = max(maxChildAlign, align)
|
maxChildAlign = max(maxChildAlign, align)
|
||||||
else:
|
else:
|
||||||
internalError(conf, "computeObjectOffsetsFoldFunction(record case branch)")
|
internalError(conf, "computeObjectOffsetsFoldFunction(record case branch)")
|
||||||
|
|
||||||
if maxChildAlign == szUnknownSize:
|
if maxChildAlign == szUnknownSize:
|
||||||
result.align = szUnknownSize
|
result.align = szUnknownSize
|
||||||
result.offset = szUnknownSize
|
result.offset = szUnknownSize
|
||||||
else:
|
else:
|
||||||
# the union neds to be aligned first, before the offsets can be assigned
|
# the union neds to be aligned first, before the offsets can be assigned
|
||||||
let kindUnionOffset = align(kindOffset, maxChildAlign)
|
let kindUnionOffset = align(kindOffset, maxChildAlign)
|
||||||
|
|
||||||
var maxChildOffset: BiggestInt = 0
|
var maxChildOffset: BiggestInt = 0
|
||||||
for i in 1 ..< sonsLen(n):
|
for i in 1 ..< sonsLen(n):
|
||||||
let (offset, align) = computeObjectOffsetsFoldFunction(conf, n.sons[i].lastSon, kindUnionOffset)
|
let (offset, align) = computeObjectOffsetsFoldFunction(conf, n.sons[i].lastSon, kindUnionOffset)
|
||||||
maxChildOffset = max(maxChildOffset, offset)
|
maxChildOffset = max(maxChildOffset, offset)
|
||||||
|
|
||||||
result.align = max(kindAlign, maxChildAlign)
|
result.align = max(kindAlign, maxChildAlign)
|
||||||
result.offset = maxChildOffset
|
result.offset = maxChildOffset
|
||||||
|
|
||||||
|
|
||||||
of nkRecList:
|
of nkRecList:
|
||||||
result.align = 1 # maximum of all member alignments
|
result.align = 1 # maximum of all member alignments
|
||||||
var offset = initialOffset
|
var offset = initialOffset
|
||||||
|
|
||||||
for i, child in n.sons:
|
for i, child in n.sons:
|
||||||
let (new_offset, align) = computeObjectOffsetsFoldFunction(conf, child, offset)
|
let (new_offset, align) = computeObjectOffsetsFoldFunction(conf, child, offset)
|
||||||
|
|
||||||
if new_offset == szIllegalRecursion:
|
if new_offset == szIllegalRecursion:
|
||||||
result.offset = szIllegalRecursion
|
result.offset = szIllegalRecursion
|
||||||
result.align = szIllegalRecursion
|
result.align = szIllegalRecursion
|
||||||
return
|
return
|
||||||
|
|
||||||
elif new_offset == szUnknownSize or offset == szUnknownSize:
|
elif new_offset == szUnknownSize or offset == szUnknownSize:
|
||||||
# if anything is unknown, the rest becomes unknown as well
|
# if anything is unknown, the rest becomes unknown as well
|
||||||
offset = szUnknownSize
|
offset = szUnknownSize
|
||||||
result.align = szUnknownSize
|
result.align = szUnknownSize
|
||||||
|
|
||||||
else:
|
else:
|
||||||
offset = new_offset
|
offset = new_offset
|
||||||
result.align = max(result.align, align)
|
result.align = max(result.align, align)
|
||||||
|
|
||||||
# final alignment
|
# final alignment
|
||||||
if offset == szUnknownSize:
|
if offset == szUnknownSize:
|
||||||
result.offset = szUnknownSize
|
result.offset = szUnknownSize
|
||||||
else:
|
else:
|
||||||
result.offset = align(offset, result.align)
|
result.offset = align(offset, result.align)
|
||||||
|
|
||||||
of nkSym:
|
of nkSym:
|
||||||
var size = szUnknownSize
|
var size = szUnknownSize
|
||||||
var align = szUnknownSize
|
var align = szUnknownSize
|
||||||
|
|
||||||
if n.sym.bitsize == 0: # 0 represents bitsize not set
|
if n.sym.bitsize == 0: # 0 represents bitsize not set
|
||||||
computeSizeAlign(conf, n.sym.typ)
|
computeSizeAlign(conf, n.sym.typ)
|
||||||
size = n.sym.typ.size.int
|
size = n.sym.typ.size.int
|
||||||
|
|
@ -155,7 +142,6 @@ proc computePackedObjectOffsetsFoldFunction(conf: ConfigRef; n: PNode, initialOf
|
||||||
let kindOffset = computePackedObjectOffsetsFoldFunction(conf, n.sons[0], initialOffset, debug)
|
let kindOffset = computePackedObjectOffsetsFoldFunction(conf, n.sons[0], initialOffset, debug)
|
||||||
# the union neds to be aligned first, before the offsets can be assigned
|
# the union neds to be aligned first, before the offsets can be assigned
|
||||||
let kindUnionOffset = kindOffset
|
let kindUnionOffset = kindOffset
|
||||||
|
|
||||||
var maxChildOffset: BiggestInt = kindUnionOffset
|
var maxChildOffset: BiggestInt = kindUnionOffset
|
||||||
for i in 1 ..< sonsLen(n):
|
for i in 1 ..< sonsLen(n):
|
||||||
let offset = computePackedObjectOffsetsFoldFunction(conf, n.sons[i].lastSon, kindUnionOffset, debug)
|
let offset = computePackedObjectOffsetsFoldFunction(conf, n.sons[i].lastSon, kindUnionOffset, debug)
|
||||||
|
|
@ -168,9 +154,17 @@ proc computePackedObjectOffsetsFoldFunction(conf: ConfigRef; n: PNode, initialOf
|
||||||
if result == szIllegalRecursion:
|
if result == szIllegalRecursion:
|
||||||
break
|
break
|
||||||
of nkSym:
|
of nkSym:
|
||||||
|
var size = szUnknownSize
|
||||||
|
if n.sym.bitsize == 0:
|
||||||
computeSizeAlign(conf, n.sym.typ)
|
computeSizeAlign(conf, n.sym.typ)
|
||||||
n.sym.offset = initialOffset.int
|
size = n.sym.typ.size.int
|
||||||
result = n.sym.offset + n.sym.typ.size
|
|
||||||
|
if initialOffset == szUnknownSize or size == szUnknownSize:
|
||||||
|
n.sym.offset = szUnknownSize
|
||||||
|
result = szUnknownSize
|
||||||
|
else:
|
||||||
|
n.sym.offset = int(initialOffset)
|
||||||
|
result = initialOffset + n.sym.typ.size
|
||||||
else:
|
else:
|
||||||
result = szUnknownSize
|
result = szUnknownSize
|
||||||
|
|
||||||
|
|
@ -324,7 +318,6 @@ proc computeSizeAlign(conf: ConfigRef; typ: PType) =
|
||||||
var headerAlign: int16
|
var headerAlign: int16
|
||||||
if typ.sons[0] != nil:
|
if typ.sons[0] != nil:
|
||||||
# compute header size
|
# compute header size
|
||||||
|
|
||||||
if conf.cmd == cmdCompileToCpp:
|
if conf.cmd == cmdCompileToCpp:
|
||||||
# if the target is C++ the members of this type are written
|
# if the target is C++ the members of this type are written
|
||||||
# into the padding byets at the end of the parent type. At the
|
# into the padding byets at the end of the parent type. At the
|
||||||
|
|
@ -364,7 +357,6 @@ proc computeSizeAlign(conf: ConfigRef; typ: PType) =
|
||||||
typ.size = szUnknownSize
|
typ.size = szUnknownSize
|
||||||
typ.align = szUnknownSize
|
typ.align = szUnknownSize
|
||||||
return
|
return
|
||||||
|
|
||||||
# header size is already in size from computeObjectOffsetsFoldFunction
|
# header size is already in size from computeObjectOffsetsFoldFunction
|
||||||
# maxAlign is probably not changed at all from headerAlign
|
# maxAlign is probably not changed at all from headerAlign
|
||||||
if tfPacked in typ.flags:
|
if tfPacked in typ.flags:
|
||||||
|
|
@ -373,7 +365,6 @@ proc computeSizeAlign(conf: ConfigRef; typ: PType) =
|
||||||
else:
|
else:
|
||||||
typ.align = int16(max(align, headerAlign))
|
typ.align = int16(max(align, headerAlign))
|
||||||
typ.size = align(offset, typ.align)
|
typ.size = align(offset, typ.align)
|
||||||
|
|
||||||
of tyInferred:
|
of tyInferred:
|
||||||
if typ.len > 1:
|
if typ.len > 1:
|
||||||
computeSizeAlign(conf, typ.lastSon)
|
computeSizeAlign(conf, typ.lastSon)
|
||||||
|
|
|
||||||
|
|
@ -456,20 +456,27 @@ proc suggestSym*(conf: ConfigRef; info: TLineInfo; s: PSym; usageSym: var PSym;
|
||||||
proc extractPragma(s: PSym): PNode =
|
proc extractPragma(s: PSym): PNode =
|
||||||
if s.kind in routineKinds:
|
if s.kind in routineKinds:
|
||||||
result = s.ast[pragmasPos]
|
result = s.ast[pragmasPos]
|
||||||
elif s.kind in {skType}:
|
elif s.kind in {skType, skVar, skLet}:
|
||||||
# s.ast = nkTypedef / nkPragmaExpr / [nkSym, nkPragma]
|
# s.ast = nkTypedef / nkPragmaExpr / [nkSym, nkPragma]
|
||||||
result = s.ast[0][1]
|
result = s.ast[0][1]
|
||||||
doAssert result == nil or result.kind == nkPragma
|
doAssert result == nil or result.kind == nkPragma
|
||||||
|
|
||||||
proc warnAboutDeprecated(conf: ConfigRef; info: TLineInfo; s: PSym) =
|
proc warnAboutDeprecated(conf: ConfigRef; info: TLineInfo; s: PSym) =
|
||||||
let pragmaNode = extractPragma(s)
|
var pragmaNode: PNode
|
||||||
|
if optOldAst in conf.options and s.kind in {skVar, skLet}:
|
||||||
|
pragmaNode = nil
|
||||||
|
else:
|
||||||
|
pragmaNode = if s.kind == skEnumField: extractPragma(s.owner) else: extractPragma(s)
|
||||||
|
let name =
|
||||||
|
if s.kind == skEnumField and sfDeprecated notin s.flags: "enum '" & s.owner.name.s & "' which contains field '" & s.name.s & "'"
|
||||||
|
else: s.name.s
|
||||||
if pragmaNode != nil:
|
if pragmaNode != nil:
|
||||||
for it in pragmaNode:
|
for it in pragmaNode:
|
||||||
if whichPragma(it) == wDeprecated and it.safeLen == 2 and
|
if whichPragma(it) == wDeprecated and it.safeLen == 2 and
|
||||||
it[1].kind in {nkStrLit..nkTripleStrLit}:
|
it[1].kind in {nkStrLit..nkTripleStrLit}:
|
||||||
message(conf, info, warnDeprecated, it[1].strVal & "; " & s.name.s)
|
message(conf, info, warnDeprecated, it[1].strVal & "; " & name & " is deprecated")
|
||||||
return
|
return
|
||||||
message(conf, info, warnDeprecated, s.name.s)
|
message(conf, info, warnDeprecated, name & " is deprecated")
|
||||||
|
|
||||||
proc userError(conf: ConfigRef; info: TLineInfo; s: PSym) =
|
proc userError(conf: ConfigRef; info: TLineInfo; s: PSym) =
|
||||||
let pragmaNode = extractPragma(s)
|
let pragmaNode = extractPragma(s)
|
||||||
|
|
@ -486,6 +493,8 @@ proc markUsed(conf: ConfigRef; info: TLineInfo; s: PSym; usageSym: var PSym) =
|
||||||
incl(s.flags, sfUsed)
|
incl(s.flags, sfUsed)
|
||||||
if s.kind == skEnumField and s.owner != nil:
|
if s.kind == skEnumField and s.owner != nil:
|
||||||
incl(s.owner.flags, sfUsed)
|
incl(s.owner.flags, sfUsed)
|
||||||
|
if sfDeprecated in s.owner.flags:
|
||||||
|
warnAboutDeprecated(conf, info, s)
|
||||||
if {sfDeprecated, sfError} * s.flags != {}:
|
if {sfDeprecated, sfError} * s.flags != {}:
|
||||||
if sfDeprecated in s.flags: warnAboutDeprecated(conf, info, s)
|
if sfDeprecated in s.flags: warnAboutDeprecated(conf, info, s)
|
||||||
if sfError in s.flags: userError(conf, info, s)
|
if sfError in s.flags: userError(conf, info, s)
|
||||||
|
|
|
||||||
|
|
@ -107,7 +107,10 @@ proc isFloatLit*(t: PType): bool {.inline.} =
|
||||||
result = t.kind == tyFloat and t.n != nil and t.n.kind == nkFloatLit
|
result = t.kind == tyFloat and t.n != nil and t.n.kind == nkFloatLit
|
||||||
|
|
||||||
proc getProcHeader*(conf: ConfigRef; sym: PSym; prefer: TPreferedDesc = preferName): string =
|
proc getProcHeader*(conf: ConfigRef; sym: PSym; prefer: TPreferedDesc = preferName): string =
|
||||||
result = sym.owner.name.s & '.' & sym.name.s & '('
|
assert sym != nil
|
||||||
|
result = sym.owner.name.s & '.' & sym.name.s
|
||||||
|
if sym.kind in routineKinds:
|
||||||
|
result.add '('
|
||||||
var n = sym.typ.n
|
var n = sym.typ.n
|
||||||
for i in countup(1, sonsLen(n) - 1):
|
for i in countup(1, sonsLen(n) - 1):
|
||||||
let p = n.sons[i]
|
let p = n.sons[i]
|
||||||
|
|
@ -429,7 +432,7 @@ proc typeToString(typ: PType, prefer: TPreferedDesc = preferName): string =
|
||||||
sfAnon notin t.sym.flags:
|
sfAnon notin t.sym.flags:
|
||||||
if t.kind == tyInt and isIntLit(t):
|
if t.kind == tyInt and isIntLit(t):
|
||||||
result = t.sym.name.s & " literal(" & $t.n.intVal & ")"
|
result = t.sym.name.s & " literal(" & $t.n.intVal & ")"
|
||||||
elif t.kind == tyAlias:
|
elif t.kind == tyAlias and t.sons[0].kind != tyAlias:
|
||||||
result = typeToString(t.sons[0])
|
result = typeToString(t.sons[0])
|
||||||
elif prefer in {preferName, preferTypeName} or t.sym.owner.isNil:
|
elif prefer in {preferName, preferTypeName} or t.sym.owner.isNil:
|
||||||
result = t.sym.name.s
|
result = t.sym.name.s
|
||||||
|
|
@ -756,7 +759,7 @@ type
|
||||||
|
|
||||||
TTypeCmpFlags* = set[TTypeCmpFlag]
|
TTypeCmpFlags* = set[TTypeCmpFlag]
|
||||||
|
|
||||||
TSameTypeClosure = object {.pure.}
|
TSameTypeClosure = object
|
||||||
cmp: TDistinctCompare
|
cmp: TDistinctCompare
|
||||||
recCheck: int
|
recCheck: int
|
||||||
flags: TTypeCmpFlags
|
flags: TTypeCmpFlags
|
||||||
|
|
|
||||||
38
compiler/unittest_light.nim
Normal file
38
compiler/unittest_light.nim
Normal file
|
|
@ -0,0 +1,38 @@
|
||||||
|
# note: consider merging tests/assert/testhelper.nim here.
|
||||||
|
|
||||||
|
proc mismatch*[T](lhs: T, rhs: T): string =
|
||||||
|
## Simplified version of `unittest.require` that satisfies a common use case,
|
||||||
|
## while avoiding pulling too many dependencies. On failure, diagnostic
|
||||||
|
## information is provided that in particular makes it easy to spot
|
||||||
|
## whitespace mismatches and where the mismatch is.
|
||||||
|
proc replaceInvisible(s: string): string =
|
||||||
|
for a in s:
|
||||||
|
case a
|
||||||
|
of '\n': result.add "\\n\n"
|
||||||
|
else: result.add a
|
||||||
|
|
||||||
|
proc quoted(s: string): string = result.addQuoted s
|
||||||
|
|
||||||
|
result.add "\n"
|
||||||
|
result.add "lhs:{" & replaceInvisible(
|
||||||
|
$lhs) & "}\nrhs:{" & replaceInvisible($rhs) & "}\n"
|
||||||
|
when compiles(lhs.len):
|
||||||
|
if lhs.len != rhs.len:
|
||||||
|
result.add "lhs.len: " & $lhs.len & " rhs.len: " & $rhs.len & "\n"
|
||||||
|
when compiles(lhs[0]):
|
||||||
|
var i = 0
|
||||||
|
while i < lhs.len and i < rhs.len:
|
||||||
|
if lhs[i] != rhs[i]: break
|
||||||
|
i.inc
|
||||||
|
result.add "first mismatch index: " & $i & "\n"
|
||||||
|
if i < lhs.len and i < rhs.len:
|
||||||
|
result.add "lhs[i]: {" & quoted($lhs[i]) & "}\nrhs[i]: {" & quoted(
|
||||||
|
$rhs[i]) & "}\n"
|
||||||
|
result.add "lhs[0..<i]:{" & replaceInvisible($lhs[
|
||||||
|
0..<i]) & "}"
|
||||||
|
|
||||||
|
proc assertEquals*[T](lhs: T, rhs: T) =
|
||||||
|
when false: # can be useful for debugging to see all that's fed to this.
|
||||||
|
echo "----" & $lhs
|
||||||
|
if lhs!=rhs:
|
||||||
|
doAssert false, mismatch(lhs, rhs)
|
||||||
|
|
@ -10,10 +10,6 @@
|
||||||
## This file implements the new evaluation engine for Nim code.
|
## This file implements the new evaluation engine for Nim code.
|
||||||
## An instruction is 1-3 int32s in memory, it is a register based VM.
|
## An instruction is 1-3 int32s in memory, it is a register based VM.
|
||||||
|
|
||||||
const
|
|
||||||
debugEchoCode = false
|
|
||||||
traceCode = debugEchoCode
|
|
||||||
|
|
||||||
import ast except getstr
|
import ast except getstr
|
||||||
|
|
||||||
import
|
import
|
||||||
|
|
@ -26,6 +22,9 @@ from evaltempl import evalTemplate
|
||||||
|
|
||||||
from modulegraphs import ModuleGraph, PPassContext
|
from modulegraphs import ModuleGraph, PPassContext
|
||||||
|
|
||||||
|
const
|
||||||
|
traceCode = debugEchoCode
|
||||||
|
|
||||||
when hasFFI:
|
when hasFFI:
|
||||||
import evalffi
|
import evalffi
|
||||||
|
|
||||||
|
|
@ -65,22 +64,27 @@ proc stackTraceAux(c: PCtx; x: PStackFrame; pc: int; recursionLimit=100) =
|
||||||
return
|
return
|
||||||
stackTraceAux(c, x.next, x.comesFrom, recursionLimit-1)
|
stackTraceAux(c, x.next, x.comesFrom, recursionLimit-1)
|
||||||
var info = c.debug[pc]
|
var info = c.debug[pc]
|
||||||
# we now use the same format as in system/except.nim
|
# we now use a format similar to the one in lib/system/excpt.nim
|
||||||
var s = substr(toFilename(c.config, info), 0)
|
var s = ""
|
||||||
# this 'substr' prevents a strange corruption. XXX This needs to be
|
# todo: factor with quotedFilename
|
||||||
# investigated eventually but first attempts to fix it broke everything
|
if optExcessiveStackTrace in c.config.globalOptions:
|
||||||
# see the araq-wip-fixed-writebarrier branch.
|
s = toFullPath(c.config, info)
|
||||||
|
else:
|
||||||
|
s = toFilename(c.config, info)
|
||||||
var line = toLinenumber(info)
|
var line = toLinenumber(info)
|
||||||
|
var col = toColumn(info)
|
||||||
if line > 0:
|
if line > 0:
|
||||||
add(s, '(')
|
add(s, '(')
|
||||||
add(s, $line)
|
add(s, $line)
|
||||||
|
add(s, ", ")
|
||||||
|
add(s, $(col + ColOffset))
|
||||||
add(s, ')')
|
add(s, ')')
|
||||||
if x.prc != nil:
|
if x.prc != nil:
|
||||||
for k in 1..max(1, 25-s.len): add(s, ' ')
|
for k in 1..max(1, 25-s.len): add(s, ' ')
|
||||||
add(s, x.prc.name.s)
|
add(s, x.prc.name.s)
|
||||||
msgWriteln(c.config, s)
|
msgWriteln(c.config, s)
|
||||||
|
|
||||||
proc stackTrace(c: PCtx, tos: PStackFrame, pc: int,
|
proc stackTraceImpl(c: PCtx, tos: PStackFrame, pc: int,
|
||||||
msg: string, lineInfo: TLineInfo) =
|
msg: string, lineInfo: TLineInfo) =
|
||||||
msgWriteln(c.config, "stack trace: (most recent call last)")
|
msgWriteln(c.config, "stack trace: (most recent call last)")
|
||||||
stackTraceAux(c, tos, pc)
|
stackTraceAux(c, tos, pc)
|
||||||
|
|
@ -88,8 +92,14 @@ proc stackTrace(c: PCtx, tos: PStackFrame, pc: int,
|
||||||
if c.mode == emRepl: globalError(c.config, lineInfo, msg)
|
if c.mode == emRepl: globalError(c.config, lineInfo, msg)
|
||||||
else: localError(c.config, lineInfo, msg)
|
else: localError(c.config, lineInfo, msg)
|
||||||
|
|
||||||
proc stackTrace(c: PCtx, tos: PStackFrame, pc: int, msg: string) =
|
template stackTrace(c: PCtx, tos: PStackFrame, pc: int,
|
||||||
stackTrace(c, tos, pc, msg, c.debug[pc])
|
msg: string, lineInfo: TLineInfo) =
|
||||||
|
stackTraceImpl(c, tos, pc, msg, lineInfo)
|
||||||
|
return
|
||||||
|
|
||||||
|
template stackTrace(c: PCtx, tos: PStackFrame, pc: int, msg: string) =
|
||||||
|
stackTraceImpl(c, tos, pc, msg, c.debug[pc])
|
||||||
|
return
|
||||||
|
|
||||||
proc bailOut(c: PCtx; tos: PStackFrame) =
|
proc bailOut(c: PCtx; tos: PStackFrame) =
|
||||||
stackTrace(c, tos, c.exceptionInstr, "unhandled exception: " &
|
stackTrace(c, tos, c.exceptionInstr, "unhandled exception: " &
|
||||||
|
|
@ -1239,8 +1249,6 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
|
||||||
let newLen = regs[rb].intVal.int
|
let newLen = regs[rb].intVal.int
|
||||||
if regs[ra].node.isNil: stackTrace(c, tos, pc, errNilAccess)
|
if regs[ra].node.isNil: stackTrace(c, tos, pc, errNilAccess)
|
||||||
else: c.setLenSeq(regs[ra].node, newLen, c.debug[pc])
|
else: c.setLenSeq(regs[ra].node, newLen, c.debug[pc])
|
||||||
of opcReset:
|
|
||||||
internalError(c.config, c.debug[pc], "too implement")
|
|
||||||
of opcNarrowS:
|
of opcNarrowS:
|
||||||
decodeB(rkInt)
|
decodeB(rkInt)
|
||||||
let min = -(1.BiggestInt shl (rb-1))
|
let min = -(1.BiggestInt shl (rb-1))
|
||||||
|
|
|
||||||
|
|
@ -73,7 +73,7 @@ type
|
||||||
opcContainsSet, opcRepr, opcSetLenStr, opcSetLenSeq,
|
opcContainsSet, opcRepr, opcSetLenStr, opcSetLenSeq,
|
||||||
opcIsNil, opcOf, opcIs,
|
opcIsNil, opcOf, opcIs,
|
||||||
opcSubStr, opcParseFloat, opcConv, opcCast,
|
opcSubStr, opcParseFloat, opcConv, opcCast,
|
||||||
opcQuit, opcReset,
|
opcQuit,
|
||||||
opcNarrowS, opcNarrowU,
|
opcNarrowS, opcNarrowU,
|
||||||
|
|
||||||
opcAddStrCh,
|
opcAddStrCh,
|
||||||
|
|
|
||||||
|
|
@ -33,6 +33,11 @@ import
|
||||||
import platform
|
import platform
|
||||||
from os import splitFile
|
from os import splitFile
|
||||||
|
|
||||||
|
const
|
||||||
|
debugEchoCode* = defined(nimVMDebug)
|
||||||
|
|
||||||
|
when debugEchoCode:
|
||||||
|
import asciitables
|
||||||
when hasFFI:
|
when hasFFI:
|
||||||
import evalffi
|
import evalffi
|
||||||
|
|
||||||
|
|
@ -43,9 +48,10 @@ type
|
||||||
TGenFlags = set[TGenFlag]
|
TGenFlags = set[TGenFlag]
|
||||||
|
|
||||||
proc debugInfo(c: PCtx; info: TLineInfo): string =
|
proc debugInfo(c: PCtx; info: TLineInfo): string =
|
||||||
result = toFilename(c.config, info).splitFile.name & ":" & $info.line
|
result = toFileLineCol(c.config, info)
|
||||||
|
|
||||||
proc codeListing(c: PCtx, result: var string, start=0; last = -1) =
|
proc codeListing(c: PCtx, result: var string, start=0; last = -1) =
|
||||||
|
## for debugging purposes
|
||||||
# first iteration: compute all necessary labels:
|
# first iteration: compute all necessary labels:
|
||||||
var jumpTargets = initIntSet()
|
var jumpTargets = initIntSet()
|
||||||
let last = if last < 0: c.code.len-1 else: min(last, c.code.len-1)
|
let last = if last < 0: c.code.len-1 else: min(last, c.code.len-1)
|
||||||
|
|
@ -54,7 +60,9 @@ proc codeListing(c: PCtx, result: var string, start=0; last = -1) =
|
||||||
if x.opcode in relativeJumps:
|
if x.opcode in relativeJumps:
|
||||||
jumpTargets.incl(i+x.regBx-wordExcess)
|
jumpTargets.incl(i+x.regBx-wordExcess)
|
||||||
|
|
||||||
# for debugging purposes
|
template toStr(opc: TOpcode): string = ($opc).substr(3)
|
||||||
|
|
||||||
|
result.add "code listing:\n"
|
||||||
var i = start
|
var i = start
|
||||||
while i <= last:
|
while i <= last:
|
||||||
if i in jumpTargets: result.addf("L$1:\n", i)
|
if i in jumpTargets: result.addf("L$1:\n", i)
|
||||||
|
|
@ -62,34 +70,39 @@ proc codeListing(c: PCtx, result: var string, start=0; last = -1) =
|
||||||
|
|
||||||
result.add($i)
|
result.add($i)
|
||||||
let opc = opcode(x)
|
let opc = opcode(x)
|
||||||
if opc in {opcConv, opcCast}:
|
if opc in {opcIndCall, opcIndCallAsgn}:
|
||||||
|
result.addf("\t$#\tr$#, r$#, nargs:$#", opc.toStr, x.regA,
|
||||||
|
x.regB, x.regC)
|
||||||
|
elif opc in {opcConv, opcCast}:
|
||||||
let y = c.code[i+1]
|
let y = c.code[i+1]
|
||||||
let z = c.code[i+2]
|
let z = c.code[i+2]
|
||||||
result.addf("\t$#\tr$#, r$#, $#, $#", ($opc).substr(3), x.regA, x.regB,
|
result.addf("\t$#\tr$#, r$#, $#, $#", opc.toStr, x.regA, x.regB,
|
||||||
c.types[y.regBx-wordExcess].typeToString,
|
c.types[y.regBx-wordExcess].typeToString,
|
||||||
c.types[z.regBx-wordExcess].typeToString)
|
c.types[z.regBx-wordExcess].typeToString)
|
||||||
inc i, 2
|
inc i, 2
|
||||||
elif opc < firstABxInstr:
|
elif opc < firstABxInstr:
|
||||||
result.addf("\t$#\tr$#, r$#, r$#", ($opc).substr(3), x.regA,
|
result.addf("\t$#\tr$#, r$#, r$#", opc.toStr, x.regA,
|
||||||
x.regB, x.regC)
|
x.regB, x.regC)
|
||||||
elif opc in relativeJumps:
|
elif opc in relativeJumps:
|
||||||
result.addf("\t$#\tr$#, L$#", ($opc).substr(3), x.regA,
|
result.addf("\t$#\tr$#, L$#", opc.toStr, x.regA,
|
||||||
i+x.regBx-wordExcess)
|
i+x.regBx-wordExcess)
|
||||||
elif opc in {opcLdConst, opcAsgnConst}:
|
elif opc in {opcLdConst, opcAsgnConst}:
|
||||||
let idx = x.regBx-wordExcess
|
let idx = x.regBx-wordExcess
|
||||||
result.addf("\t$#\tr$#, $# ($#)", ($opc).substr(3), x.regA,
|
result.addf("\t$#\tr$#, $# ($#)", opc.toStr, x.regA,
|
||||||
c.constants[idx].renderTree, $idx)
|
c.constants[idx].renderTree, $idx)
|
||||||
elif opc in {opcMarshalLoad, opcMarshalStore}:
|
elif opc in {opcMarshalLoad, opcMarshalStore}:
|
||||||
let y = c.code[i+1]
|
let y = c.code[i+1]
|
||||||
result.addf("\t$#\tr$#, r$#, $#", ($opc).substr(3), x.regA, x.regB,
|
result.addf("\t$#\tr$#, r$#, $#", opc.toStr, x.regA, x.regB,
|
||||||
c.types[y.regBx-wordExcess].typeToString)
|
c.types[y.regBx-wordExcess].typeToString)
|
||||||
inc i
|
inc i
|
||||||
else:
|
else:
|
||||||
result.addf("\t$#\tr$#, $#", ($opc).substr(3), x.regA, x.regBx-wordExcess)
|
result.addf("\t$#\tr$#, $#", opc.toStr, x.regA, x.regBx-wordExcess)
|
||||||
result.add("\t#")
|
result.add("\t#")
|
||||||
result.add(debugInfo(c, c.debug[i]))
|
result.add(debugInfo(c, c.debug[i]))
|
||||||
result.add("\n")
|
result.add("\n")
|
||||||
inc i
|
inc i
|
||||||
|
when debugEchoCode:
|
||||||
|
result = result.alignTable
|
||||||
|
|
||||||
proc echoCode*(c: PCtx; start=0; last = -1) {.deprecated.} =
|
proc echoCode*(c: PCtx; start=0; last = -1) {.deprecated.} =
|
||||||
var buf = ""
|
var buf = ""
|
||||||
|
|
@ -1092,7 +1105,9 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest; m: TMagic) =
|
||||||
of mReset:
|
of mReset:
|
||||||
unused(c, n, dest)
|
unused(c, n, dest)
|
||||||
var d = c.genx(n.sons[1])
|
var d = c.genx(n.sons[1])
|
||||||
c.gABC(n, opcReset, d)
|
c.gABx(n, opcLdNull, d, c.genType(n.sons[1].typ))
|
||||||
|
c.gABx(n, opcNodeToReg, d, d)
|
||||||
|
c.genAsgnPatch(n.sons[1], d)
|
||||||
of mOf, mIs:
|
of mOf, mIs:
|
||||||
if dest < 0: dest = c.getTemp(n.typ)
|
if dest < 0: dest = c.getTemp(n.typ)
|
||||||
var tmp = c.genx(n.sons[1])
|
var tmp = c.genx(n.sons[1])
|
||||||
|
|
@ -1736,6 +1751,7 @@ proc genVarSection(c: PCtx; n: PNode) =
|
||||||
#assert(a.sons[0].kind == nkSym) can happen for transformed vars
|
#assert(a.sons[0].kind == nkSym) can happen for transformed vars
|
||||||
if a.kind == nkVarTuple:
|
if a.kind == nkVarTuple:
|
||||||
for i in 0 .. a.len-3:
|
for i in 0 .. a.len-3:
|
||||||
|
if a[i].kind == nkSym:
|
||||||
if not a[i].sym.isGlobal: setSlot(c, a[i].sym)
|
if not a[i].sym.isGlobal: setSlot(c, a[i].sym)
|
||||||
checkCanEval(c, a[i])
|
checkCanEval(c, a[i])
|
||||||
c.gen(lowerTupleUnpacking(c.graph, a, c.getOwner))
|
c.gen(lowerTupleUnpacking(c.graph, a, c.getOwner))
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,7 @@ from math import sqrt, ln, log10, log2, exp, round, arccos, arcsin,
|
||||||
arctan, arctan2, cos, cosh, hypot, sinh, sin, tan, tanh, pow, trunc,
|
arctan, arctan2, cos, cosh, hypot, sinh, sin, tan, tanh, pow, trunc,
|
||||||
floor, ceil, `mod`
|
floor, ceil, `mod`
|
||||||
|
|
||||||
from os import getEnv, existsEnv, dirExists, fileExists, putEnv, walkDir
|
from os import getEnv, existsEnv, dirExists, fileExists, putEnv, walkDir, getAppFilename
|
||||||
|
|
||||||
template mathop(op) {.dirty.} =
|
template mathop(op) {.dirty.} =
|
||||||
registerCallback(c, "stdlib.math." & astToStr(op), `op Wrapper`)
|
registerCallback(c, "stdlib.math." & astToStr(op), `op Wrapper`)
|
||||||
|
|
@ -120,3 +120,6 @@ proc registerAdditionalOps*(c: PCtx) =
|
||||||
setResult(a, staticWalkDirImpl(getString(a, 0), getBool(a, 1)))
|
setResult(a, staticWalkDirImpl(getString(a, 0), getBool(a, 1)))
|
||||||
systemop gorgeEx
|
systemop gorgeEx
|
||||||
macrosop getProjectPath
|
macrosop getProjectPath
|
||||||
|
|
||||||
|
registerCallback c, "stdlib.os.getCurrentCompilerExe", proc (a: VmArgs) {.nimcall.} =
|
||||||
|
setResult(a, getAppFilename())
|
||||||
|
|
|
||||||
1
config/config.nims
Normal file
1
config/config.nims
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
# empty config.nims to prevent future regressions, see #9990
|
||||||
|
|
@ -102,9 +102,6 @@ path="$lib/pure"
|
||||||
tlsEmulation:on
|
tlsEmulation:on
|
||||||
@end
|
@end
|
||||||
@if haiku:
|
@if haiku:
|
||||||
# Haiku currently have problems with TLS
|
|
||||||
# https://dev.haiku-os.org/ticket/14342
|
|
||||||
tlsEmulation:on
|
|
||||||
gcc.options.linker = "-Wl,--as-needed -lnetwork"
|
gcc.options.linker = "-Wl,--as-needed -lnetwork"
|
||||||
gcc.cpp.options.linker = "-Wl,--as-needed -lnetwork"
|
gcc.cpp.options.linker = "-Wl,--as-needed -lnetwork"
|
||||||
clang.options.linker = "-Wl,--as-needed -lnetwork"
|
clang.options.linker = "-Wl,--as-needed -lnetwork"
|
||||||
|
|
@ -220,10 +217,25 @@ llvm_gcc.options.size = "-Os"
|
||||||
|
|
||||||
# Configuration for the LLVM CLang compiler:
|
# Configuration for the LLVM CLang compiler:
|
||||||
clang.options.debug = "-g"
|
clang.options.debug = "-g"
|
||||||
|
clang.cpp.options.debug = "-g"
|
||||||
clang.options.always = "-w"
|
clang.options.always = "-w"
|
||||||
clang.options.speed = "-O3"
|
clang.options.speed = "-O3"
|
||||||
clang.options.size = "-Os"
|
clang.options.size = "-Os"
|
||||||
|
|
||||||
|
@if windows:
|
||||||
|
clang_cl.cpp.options.always %= "${clang_cl.options.always} /EHsc"
|
||||||
|
@if not release:
|
||||||
|
clang_cl.options.linker = "/Z7"
|
||||||
|
clang_cl.cpp.options.linker = "/Z7"
|
||||||
|
@end
|
||||||
|
clang.options.debug = "-g -gcodeview"
|
||||||
|
clang.cpp.options.debug = "-g -gcodeview"
|
||||||
|
@if not release:
|
||||||
|
clang.options.linker = "-g"
|
||||||
|
clang.cpp.options.linker = "-g"
|
||||||
|
@end
|
||||||
|
@end
|
||||||
|
|
||||||
# Configuration for the Visual C/C++ compiler:
|
# Configuration for the Visual C/C++ compiler:
|
||||||
# VCCEXE is a tool that invokes the Visual Studio Developer Command Prompt
|
# VCCEXE is a tool that invokes the Visual Studio Developer Command Prompt
|
||||||
# before calling the compiler.
|
# before calling the compiler.
|
||||||
|
|
|
||||||
|
|
@ -18,9 +18,9 @@ Advanced commands:
|
||||||
|
|
||||||
Advanced options:
|
Advanced options:
|
||||||
-o:FILE, --out:FILE set the output filename
|
-o:FILE, --out:FILE set the output filename
|
||||||
--stdout output to stdout
|
--stdout:on|off output to stdout
|
||||||
--colors:on|off turn compiler messages coloring on|off
|
--colors:on|off turn compiler messages coloring on|off
|
||||||
--listFullPaths list full paths in messages
|
--listFullPaths:on|off list full paths in messages
|
||||||
-w:on|off|list, --warnings:on|off|list
|
-w:on|off|list, --warnings:on|off|list
|
||||||
turn all warnings on|off or list all available
|
turn all warnings on|off or list all available
|
||||||
--warning[X]:on|off turn specific warning X on|off
|
--warning[X]:on|off turn specific warning X on|off
|
||||||
|
|
@ -39,29 +39,27 @@ Advanced options:
|
||||||
--nimcache:PATH set the path used for generated files
|
--nimcache:PATH set the path used for generated files
|
||||||
--header:FILE the compiler should produce a .h file (FILE
|
--header:FILE the compiler should produce a .h file (FILE
|
||||||
is optional)
|
is optional)
|
||||||
-c, --compileOnly compile Nim files only; do not assemble or link
|
-c, --compileOnly:on|off compile Nim files only; do not assemble or link
|
||||||
--noLinking compile Nim and generated files but do not link
|
--noLinking:on|off compile Nim and generated files but do not link
|
||||||
--noMain do not generate a main procedure
|
--noMain:on|off do not generate a main procedure
|
||||||
--genScript generate a compile script (in the 'nimcache'
|
--genScript:on|off generate a compile script (in the 'nimcache'
|
||||||
subdirectory named 'compile_$$project$$scriptext'),
|
subdirectory named 'compile_$$project$$scriptext'),
|
||||||
implies --compileOnly
|
implies --compileOnly
|
||||||
--genDeps generate a '.deps' file containing the dependencies
|
--genDeps:on|off generate a '.deps' file containing the dependencies
|
||||||
--os:SYMBOL set the target operating system (cross-compilation)
|
--os:SYMBOL set the target operating system (cross-compilation)
|
||||||
--cpu:SYMBOL set the target processor (cross-compilation)
|
--cpu:SYMBOL set the target processor (cross-compilation)
|
||||||
--debuginfo enables debug information
|
--debuginfo:on|off enables debug information
|
||||||
-t, --passC:OPTION pass an option to the C compiler
|
-t, --passC:OPTION pass an option to the C compiler
|
||||||
-l, --passL:OPTION pass an option to the linker
|
-l, --passL:OPTION pass an option to the linker
|
||||||
--cincludes:DIR modify the C compiler header search path
|
--cincludes:DIR modify the C compiler header search path
|
||||||
--clibdir:DIR modify the linker library search path
|
--clibdir:DIR modify the linker library search path
|
||||||
--clib:LIBNAME link an additional C library
|
--clib:LIBNAME link an additional C library
|
||||||
(you should omit platform-specific extensions)
|
(you should omit platform-specific extensions)
|
||||||
--genMapping generate a mapping file containing
|
|
||||||
(Nim, mangled) identifier pairs
|
|
||||||
--project document the whole project (doc2)
|
--project document the whole project (doc2)
|
||||||
--docSeeSrcUrl:url activate 'see source' for doc and doc2 commands
|
--docSeeSrcUrl:url activate 'see source' for doc and doc2 commands
|
||||||
(see doc.item.seesrc in config/nimdoc.cfg)
|
(see doc.item.seesrc in config/nimdoc.cfg)
|
||||||
--lineDir:on|off generation of #line directive on|off
|
--lineDir:on|off generation of #line directive on|off
|
||||||
--embedsrc embeds the original source code as comments
|
--embedsrc:on|off embeds the original source code as comments
|
||||||
in the generated output
|
in the generated output
|
||||||
--threadanalysis:on|off turn thread analysis on|off
|
--threadanalysis:on|off turn thread analysis on|off
|
||||||
--tlsEmulation:on|off turn thread local storage emulation on|off
|
--tlsEmulation:on|off turn thread local storage emulation on|off
|
||||||
|
|
@ -78,11 +76,12 @@ Advanced options:
|
||||||
strings is allowed; only for backwards compatibility
|
strings is allowed; only for backwards compatibility
|
||||||
--nilseqs:on|off allow 'nil' for strings/seqs for
|
--nilseqs:on|off allow 'nil' for strings/seqs for
|
||||||
backwards compatibility
|
backwards compatibility
|
||||||
--skipCfg do not read the general configuration file
|
--oldast:on|off use old AST for backwards compatibility
|
||||||
--skipUserCfg do not read the user's configuration file
|
--skipCfg:on|off do not read the nim installation's configuration file
|
||||||
--skipParentCfg do not read the parent dirs' configuration files
|
--skipUserCfg:on|off do not read the user's configuration file
|
||||||
--skipProjCfg do not read the project's configuration file
|
--skipParentCfg:on|off do not read the parent dirs' configuration files
|
||||||
--gc:refc|v2|markAndSweep|boehm|go|none|regions
|
--skipProjCfg:on|off do not read the project's configuration file
|
||||||
|
--gc:refc|markAndSweep|boehm|go|none|regions
|
||||||
select the GC to use; default is 'refc'
|
select the GC to use; default is 'refc'
|
||||||
--index:on|off turn index file generation on|off
|
--index:on|off turn index file generation on|off
|
||||||
--putenv:key=value set an environment variable
|
--putenv:key=value set an environment variable
|
||||||
|
|
@ -98,8 +97,8 @@ Advanced options:
|
||||||
symbol matching is fuzzy so
|
symbol matching is fuzzy so
|
||||||
that --dynlibOverride:lua matches
|
that --dynlibOverride:lua matches
|
||||||
dynlib: "liblua.so.3"
|
dynlib: "liblua.so.3"
|
||||||
--dynlibOverrideAll makes the dynlib pragma have no effect
|
--dynlibOverrideAll:on|off makes the dynlib pragma have no effect
|
||||||
--listCmd list the commands used to execute external programs
|
--listCmd:on|off list the commands used to execute external programs
|
||||||
--parallelBuild:0|1|... perform a parallel build
|
--parallelBuild:0|1|... perform a parallel build
|
||||||
value = number of processors (0 for auto-detect)
|
value = number of processors (0 for auto-detect)
|
||||||
--incremental:on|off only recompile the changed modules (experimental!)
|
--incremental:on|off only recompile the changed modules (experimental!)
|
||||||
|
|
@ -108,3 +107,6 @@ Advanced options:
|
||||||
--experimental:$1
|
--experimental:$1
|
||||||
enable experimental language feature
|
enable experimental language feature
|
||||||
-v, --version show detailed version information
|
-v, --version show detailed version information
|
||||||
|
--profiler:on|off Enable profiling; requires `import nimprof`, and
|
||||||
|
works better with `--stackTrace:on`
|
||||||
|
see also https://nim-lang.github.io/Nim/estp.html
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,7 @@ Options:
|
||||||
(Optionally: Define the value for that symbol,
|
(Optionally: Define the value for that symbol,
|
||||||
see: "compile time define pragmas")
|
see: "compile time define pragmas")
|
||||||
-u, --undef:SYMBOL undefine a conditional symbol
|
-u, --undef:SYMBOL undefine a conditional symbol
|
||||||
-f, --forceBuild force rebuilding of all modules
|
-f, --forceBuild:on|off force rebuilding of all modules
|
||||||
--stackTrace:on|off turn stack tracing on|off
|
--stackTrace:on|off turn stack tracing on|off
|
||||||
--lineTrace:on|off turn line tracing on|off
|
--lineTrace:on|off turn line tracing on|off
|
||||||
--threads:on|off turn support for multi-threading on|off
|
--threads:on|off turn support for multi-threading on|off
|
||||||
|
|
@ -35,7 +35,7 @@ Options:
|
||||||
--debugger:native|endb use native debugger (gdb) | ENDB (experimental)
|
--debugger:native|endb use native debugger (gdb) | ENDB (experimental)
|
||||||
--app:console|gui|lib|staticlib
|
--app:console|gui|lib|staticlib
|
||||||
generate a console app|GUI app|DLL|static library
|
generate a console app|GUI app|DLL|static library
|
||||||
-r, --run run the compiled program with given arguments
|
-r, --run:on|off run the compiled program with given arguments
|
||||||
--fullhelp show all command line switches
|
--fullhelp show all command line switches
|
||||||
-h, --help show this help
|
-h, --help show this help
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,9 @@ The documentation consists of several documents:
|
||||||
- | `Tutorial (part II) <tut2.html>`_
|
- | `Tutorial (part II) <tut2.html>`_
|
||||||
| The Nim tutorial part two deals with the advanced language constructs.
|
| The Nim tutorial part two deals with the advanced language constructs.
|
||||||
|
|
||||||
|
- | `Tutorial (part III) <tut3.html>`_
|
||||||
|
| The Nim tutorial part three about Nim's macro system.
|
||||||
|
|
||||||
- | `Language Manual <manual.html>`_
|
- | `Language Manual <manual.html>`_
|
||||||
| The Nim manual is a draft that will evolve into a proper specification.
|
| The Nim manual is a draft that will evolve into a proper specification.
|
||||||
|
|
||||||
|
|
|
||||||
24
doc/lib.rst
24
doc/lib.rst
|
|
@ -107,8 +107,8 @@ String handling
|
||||||
substrings, replacing substrings.
|
substrings, replacing substrings.
|
||||||
|
|
||||||
* `strformat <strformat.html>`_
|
* `strformat <strformat.html>`_
|
||||||
Macro based standard string interpolation / formatting. Inpired by
|
Macro based standard string interpolation / formatting. Inspired by
|
||||||
Python's ```f``-strings.
|
Python's ``f``-strings.
|
||||||
|
|
||||||
* `strmisc <strmisc.html>`_
|
* `strmisc <strmisc.html>`_
|
||||||
This module contains uncommon string handling operations that do not
|
This module contains uncommon string handling operations that do not
|
||||||
|
|
@ -141,12 +141,6 @@ String handling
|
||||||
Ropes can represent very long strings efficiently; especially concatenation
|
Ropes can represent very long strings efficiently; especially concatenation
|
||||||
is done in O(1) instead of O(n).
|
is done in O(1) instead of O(n).
|
||||||
|
|
||||||
* `matchers <matchers.html>`_
|
|
||||||
This module contains various string matchers for email addresses, etc.
|
|
||||||
|
|
||||||
* `subexes <subexes.html>`_
|
|
||||||
This module implements advanced string substitution operations.
|
|
||||||
|
|
||||||
* `std/editdistance <editdistance.html>`_
|
* `std/editdistance <editdistance.html>`_
|
||||||
This module contains an algorithm to compute the edit distance between two
|
This module contains an algorithm to compute the edit distance between two
|
||||||
Unicode strings.
|
Unicode strings.
|
||||||
|
|
@ -236,9 +230,6 @@ Internet Protocols and Support
|
||||||
* `cgi <cgi.html>`_
|
* `cgi <cgi.html>`_
|
||||||
This module implements helpers for CGI applications.
|
This module implements helpers for CGI applications.
|
||||||
|
|
||||||
* `scgi <scgi.html>`_
|
|
||||||
This module implements helpers for SCGI applications.
|
|
||||||
|
|
||||||
* `browsers <browsers.html>`_
|
* `browsers <browsers.html>`_
|
||||||
This module implements procs for opening URLs with the user's default
|
This module implements procs for opening URLs with the user's default
|
||||||
browser.
|
browser.
|
||||||
|
|
@ -275,8 +266,8 @@ Internet Protocols and Support
|
||||||
module.
|
module.
|
||||||
|
|
||||||
* `net <net.html>`_
|
* `net <net.html>`_
|
||||||
This module implements a high-level sockets API. It will replace the
|
This module implements a high-level sockets API. It replaces the
|
||||||
``sockets`` module in the future.
|
``sockets`` module.
|
||||||
|
|
||||||
* `nativesockets <nativesockets.html>`_
|
* `nativesockets <nativesockets.html>`_
|
||||||
This module implements a low-level sockets API.
|
This module implements a low-level sockets API.
|
||||||
|
|
@ -456,13 +447,6 @@ Database support
|
||||||
for other databases too.
|
for other databases too.
|
||||||
|
|
||||||
|
|
||||||
Other
|
|
||||||
-----
|
|
||||||
|
|
||||||
* `ssl <ssl.html>`_
|
|
||||||
This module provides an easy to use sockets-style
|
|
||||||
Nim interface to the OpenSSL library.
|
|
||||||
|
|
||||||
|
|
||||||
Wrappers
|
Wrappers
|
||||||
========
|
========
|
||||||
|
|
|
||||||
|
|
@ -480,7 +480,6 @@ The type suffixes are:
|
||||||
``'d`` float64
|
``'d`` float64
|
||||||
``'f32`` float32
|
``'f32`` float32
|
||||||
``'f64`` float64
|
``'f64`` float64
|
||||||
``'f128`` float128
|
|
||||||
================= =========================
|
================= =========================
|
||||||
|
|
||||||
Floating point literals may also be in binary, octal or hexadecimal
|
Floating point literals may also be in binary, octal or hexadecimal
|
||||||
|
|
@ -1587,7 +1586,7 @@ details like this when mixing garbage collected data with unmanaged memory.
|
||||||
Not nil annotation
|
Not nil annotation
|
||||||
------------------
|
------------------
|
||||||
|
|
||||||
All types for that ``nil`` is a valid value can be annotated to
|
All types for which ``nil`` is a valid value can be annotated to
|
||||||
exclude ``nil`` as a valid value with the ``not nil`` annotation:
|
exclude ``nil`` as a valid value with the ``not nil`` annotation:
|
||||||
|
|
||||||
.. code-block:: nim
|
.. code-block:: nim
|
||||||
|
|
@ -1664,12 +1663,12 @@ Nim supports these `calling conventions`:idx:\:
|
||||||
and another one for the pointer to implicitly passed environment.
|
and another one for the pointer to implicitly passed environment.
|
||||||
|
|
||||||
`stdcall`:idx:
|
`stdcall`:idx:
|
||||||
This the stdcall convention as specified by Microsoft. The generated C
|
This is the stdcall convention as specified by Microsoft. The generated C
|
||||||
procedure is declared with the ``__stdcall`` keyword.
|
procedure is declared with the ``__stdcall`` keyword.
|
||||||
|
|
||||||
`cdecl`:idx:
|
`cdecl`:idx:
|
||||||
The cdecl convention means that a procedure shall use the same convention
|
The cdecl convention means that a procedure shall use the same convention
|
||||||
as the C compiler. Under windows the generated C procedure is declared with
|
as the C compiler. Under Windows the generated C procedure is declared with
|
||||||
the ``__cdecl`` keyword.
|
the ``__cdecl`` keyword.
|
||||||
|
|
||||||
`safecall`:idx:
|
`safecall`:idx:
|
||||||
|
|
@ -7365,6 +7364,17 @@ Example:
|
||||||
|
|
||||||
embedsC()
|
embedsC()
|
||||||
|
|
||||||
|
``nimbase.h`` defines ``NIM_EXTERNC`` C macro that can be used for
|
||||||
|
``extern "C"`` code to work with both ``nim c`` and ``nim cpp``, eg:
|
||||||
|
|
||||||
|
.. code-block:: Nim
|
||||||
|
proc foobar() {.importc:"$1".}
|
||||||
|
{.emit: """
|
||||||
|
#include <stdio.h>
|
||||||
|
NIM_EXTERNC
|
||||||
|
void fun(){}
|
||||||
|
""".}
|
||||||
|
|
||||||
For backwards compatibility, if the argument to the ``emit`` statement
|
For backwards compatibility, if the argument to the ``emit`` statement
|
||||||
is a single string literal, Nim symbols can be referred to via backticks.
|
is a single string literal, Nim symbols can be referred to via backticks.
|
||||||
This usage is however deprecated.
|
This usage is however deprecated.
|
||||||
|
|
|
||||||
|
|
@ -347,13 +347,16 @@ complete list.
|
||||||
Define Effect
|
Define Effect
|
||||||
====================== =========================================================
|
====================== =========================================================
|
||||||
``release`` Turns off runtime checks and turns on the optimizer.
|
``release`` Turns off runtime checks and turns on the optimizer.
|
||||||
|
More aggressive optimizations are possible, eg:
|
||||||
|
``--passC:-ffast-math`` (but see issue #10305)
|
||||||
|
``--stacktrace:off``
|
||||||
``useWinAnsi`` Modules like ``os`` and ``osproc`` use the Ansi versions
|
``useWinAnsi`` Modules like ``os`` and ``osproc`` use the Ansi versions
|
||||||
of the Windows API. The default build uses the Unicode
|
of the Windows API. The default build uses the Unicode
|
||||||
version.
|
version.
|
||||||
``useFork`` Makes ``osproc`` use ``fork`` instead of ``posix_spawn``.
|
``useFork`` Makes ``osproc`` use ``fork`` instead of ``posix_spawn``.
|
||||||
``useNimRtl`` Compile and link against ``nimrtl.dll``.
|
``useNimRtl`` Compile and link against ``nimrtl.dll``.
|
||||||
``useMalloc`` Makes Nim use C's `malloc`:idx: instead of Nim's
|
``useMalloc`` Makes Nim use C's `malloc`:idx: instead of Nim's
|
||||||
own memory manager, ableit prefixing each allocation with
|
own memory manager, albeit prefixing each allocation with
|
||||||
its size to support clearing memory on reallocation.
|
its size to support clearing memory on reallocation.
|
||||||
This only works with ``gc:none``.
|
This only works with ``gc:none``.
|
||||||
``useRealtimeGC`` Enables support of Nim's GC for *soft* realtime
|
``useRealtimeGC`` Enables support of Nim's GC for *soft* realtime
|
||||||
|
|
|
||||||
36
doc/nims.rst
36
doc/nims.rst
|
|
@ -26,9 +26,28 @@ previous settings):
|
||||||
``$project.nim``. This file can be skipped with the same
|
``$project.nim``. This file can be skipped with the same
|
||||||
``--skipProjCfg`` command line option.
|
``--skipProjCfg`` command line option.
|
||||||
|
|
||||||
The VM cannot deal with ``importc`` because the FFI is not
|
Limitations
|
||||||
available. So the stdlib modules using ``importc`` cannot be used with
|
=================================
|
||||||
Nim's VM. However, at least the following modules are available:
|
|
||||||
|
NimScript is subject to some limitations caused by the implementation of the VM
|
||||||
|
(virtual machine):
|
||||||
|
|
||||||
|
* Nim's FFI (foreign function interface) is not available in NimScript. This
|
||||||
|
means that any stdlib module which relies on ``importc`` can not be used in
|
||||||
|
the VM.
|
||||||
|
|
||||||
|
* ``ptr`` operations are are hard to emulate with the symbolic representation
|
||||||
|
the VM uses. They are available and tested extensively but there are bugs left.
|
||||||
|
|
||||||
|
* ``var T`` function arguments rely on ``ptr`` operations internally and might
|
||||||
|
also be problematic in some cases.
|
||||||
|
|
||||||
|
* More than one level of `ref` is generally not supported (for example, the type
|
||||||
|
`ref ref int`).
|
||||||
|
|
||||||
|
* multimethods are not available.
|
||||||
|
|
||||||
|
Given the above restrictions, at least the following modules are available:
|
||||||
|
|
||||||
* `macros <macros.html>`_
|
* `macros <macros.html>`_
|
||||||
* `os <os.html>`_
|
* `os <os.html>`_
|
||||||
|
|
@ -98,17 +117,6 @@ Task Description
|
||||||
========= ===================================================
|
========= ===================================================
|
||||||
|
|
||||||
|
|
||||||
If the task runs an external command via ``exec`` it should afterwards call
|
|
||||||
``setCommand "nop"`` to tell the Nim compiler that nothing else needs to be
|
|
||||||
done:
|
|
||||||
|
|
||||||
.. code-block:: nim
|
|
||||||
|
|
||||||
task tests, "test regular expressions":
|
|
||||||
exec "nim c -r tests"
|
|
||||||
setCommand "nop"
|
|
||||||
|
|
||||||
|
|
||||||
Look at the module `distros <distros.html>`_ for some support of the
|
Look at the module `distros <distros.html>`_ for some support of the
|
||||||
OS's native package managers.
|
OS's native package managers.
|
||||||
|
|
||||||
|
|
|
||||||
104
koch.nim
104
koch.nim
|
|
@ -46,6 +46,8 @@ Possible Commands:
|
||||||
boot [options] bootstraps with given command line options
|
boot [options] bootstraps with given command line options
|
||||||
distrohelper [bindir] helper for distro packagers
|
distrohelper [bindir] helper for distro packagers
|
||||||
tools builds Nim related tools
|
tools builds Nim related tools
|
||||||
|
toolsNoNimble builds Nim related tools (except nimble)
|
||||||
|
doesn't require network connectivity
|
||||||
nimble builds the Nimble tool
|
nimble builds the Nimble tool
|
||||||
Boot options:
|
Boot options:
|
||||||
-d:release produce a release version of the compiler
|
-d:release produce a release version of the compiler
|
||||||
|
|
@ -56,6 +58,7 @@ Boot options:
|
||||||
for bootstrapping
|
for bootstrapping
|
||||||
|
|
||||||
Commands for core developers:
|
Commands for core developers:
|
||||||
|
runCI runs continuous integration (CI), eg from travis
|
||||||
docs [options] generates the full documentation
|
docs [options] generates the full documentation
|
||||||
csource -d:release builds the C sources for installation
|
csource -d:release builds the C sources for installation
|
||||||
pdf builds the PDF documentation
|
pdf builds the PDF documentation
|
||||||
|
|
@ -71,6 +74,12 @@ Web options:
|
||||||
build the official docs, use UA-48159761-1
|
build the official docs, use UA-48159761-1
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
let kochExe* = when isMainModule: os.getAppFilename() # always correct when koch is main program, even if `koch` exe renamed eg: `nim c -o:koch_debug koch.nim`
|
||||||
|
else: getAppDir() / "koch".exe # works for winrelease
|
||||||
|
|
||||||
|
proc kochExec*(cmd: string) =
|
||||||
|
exec kochExe.quoteShell & " " & cmd
|
||||||
|
|
||||||
template withDir(dir, body) =
|
template withDir(dir, body) =
|
||||||
let old = getCurrentDir()
|
let old = getCurrentDir()
|
||||||
try:
|
try:
|
||||||
|
|
@ -121,9 +130,6 @@ proc bundleNimbleExe(latest: bool) =
|
||||||
# installer.ini expects it under $nim/bin
|
# installer.ini expects it under $nim/bin
|
||||||
nimCompile("dist/nimble/src/nimble.nim", options = "-d:release --nilseqs:on")
|
nimCompile("dist/nimble/src/nimble.nim", options = "-d:release --nilseqs:on")
|
||||||
|
|
||||||
proc buildNimfind() =
|
|
||||||
nimCompile("tools/nimfind.nim", options = "-d:release")
|
|
||||||
|
|
||||||
proc buildNimble(latest: bool) =
|
proc buildNimble(latest: bool) =
|
||||||
# old installations created nim/nimblepkg/*.nim files. We remove these
|
# old installations created nim/nimblepkg/*.nim files. We remove these
|
||||||
# here so that it cannot cause problems (nimble bug #306):
|
# here so that it cannot cause problems (nimble bug #306):
|
||||||
|
|
@ -197,13 +203,12 @@ proc buildTool(toolname, args: string) =
|
||||||
nimexec("cc $# $#" % [args, toolname])
|
nimexec("cc $# $#" % [args, toolname])
|
||||||
copyFile(dest="bin" / splitFile(toolname).name.exe, source=toolname.exe)
|
copyFile(dest="bin" / splitFile(toolname).name.exe, source=toolname.exe)
|
||||||
|
|
||||||
proc buildTools(latest: bool) =
|
proc buildTools() =
|
||||||
bundleNimsuggest()
|
bundleNimsuggest()
|
||||||
nimCompile("tools/nimgrep.nim", options = "-d:release")
|
nimCompile("tools/nimgrep.nim", options = "-d:release")
|
||||||
when defined(windows): buildVccTool()
|
when defined(windows): buildVccTool()
|
||||||
nimCompile("nimpretty/nimpretty.nim", options = "-d:release")
|
nimCompile("nimpretty/nimpretty.nim", options = "-d:release")
|
||||||
buildNimble(latest)
|
nimCompile("tools/nimfind.nim", options = "-d:release")
|
||||||
buildNimfind()
|
|
||||||
|
|
||||||
proc nsis(latest: bool; args: string) =
|
proc nsis(latest: bool; args: string) =
|
||||||
bundleNimbleExe(latest)
|
bundleNimbleExe(latest)
|
||||||
|
|
@ -272,16 +277,30 @@ proc boot(args: string) =
|
||||||
var output = "compiler" / "nim".exe
|
var output = "compiler" / "nim".exe
|
||||||
var finalDest = "bin" / "nim".exe
|
var finalDest = "bin" / "nim".exe
|
||||||
# default to use the 'c' command:
|
# default to use the 'c' command:
|
||||||
let defaultCommand = if getEnv("NIM_COMPILE_TO_CPP", "false") == "true": "cpp" else: "c"
|
let useCpp = getEnv("NIM_COMPILE_TO_CPP", "false") == "true"
|
||||||
let bootOptions = if args.len == 0 or args.startsWith("-"): defaultCommand else: ""
|
|
||||||
let smartNimcache = (if "release" in args: "nimcache/r_" else: "nimcache/d_") &
|
let smartNimcache = (if "release" in args: "nimcache/r_" else: "nimcache/d_") &
|
||||||
hostOs & "_" & hostCpu
|
hostOs & "_" & hostCpu
|
||||||
|
|
||||||
copyExe(findStartNim(), 0.thVersion)
|
let nimStart = findStartNim()
|
||||||
|
copyExe(nimStart, 0.thVersion)
|
||||||
for i in 0..2:
|
for i in 0..2:
|
||||||
|
let defaultCommand = if useCpp: "cpp" else: "c"
|
||||||
|
let bootOptions = if args.len == 0 or args.startsWith("-"): defaultCommand else: ""
|
||||||
echo "iteration: ", i+1
|
echo "iteration: ", i+1
|
||||||
exec i.thVersion & " $# $# --nimcache:$# compiler" / "nim.nim" % [bootOptions, args,
|
var extraOption = ""
|
||||||
smartNimcache]
|
if i == 0:
|
||||||
|
extraOption.add " --skipUserCfg --skipParentCfg"
|
||||||
|
# Note(D20190115T162028:here): the configs are skipped for bootstrap
|
||||||
|
# (1st iteration) to prevent newer flags from breaking bootstrap phase.
|
||||||
|
# fixes #10030.
|
||||||
|
let ret = execCmdEx(nimStart & " --version")
|
||||||
|
doAssert ret.exitCode == 0
|
||||||
|
let version = ret.output.splitLines[0]
|
||||||
|
if version.startsWith "Nim Compiler Version 0.19.0":
|
||||||
|
extraOption.add " -d:nimBoostrapCsources0_19_0"
|
||||||
|
# remove this when csources get updated
|
||||||
|
exec i.thVersion & " $# $# $# --nimcache:$# compiler" / "nim.nim" %
|
||||||
|
[bootOptions, extraOption, args, smartNimcache]
|
||||||
if sameFileContent(output, i.thVersion):
|
if sameFileContent(output, i.thVersion):
|
||||||
copyExe(output, finalDest)
|
copyExe(output, finalDest)
|
||||||
echo "executables are equal: SUCCESS!"
|
echo "executables are equal: SUCCESS!"
|
||||||
|
|
@ -352,8 +371,8 @@ proc winReleaseArch(arch: string) =
|
||||||
# Rebuilding koch is necessary because it uses its pointer size to
|
# Rebuilding koch is necessary because it uses its pointer size to
|
||||||
# determine which mingw link to put in the NSIS installer.
|
# determine which mingw link to put in the NSIS installer.
|
||||||
nimexec "c --cpu:$# koch" % cpu
|
nimexec "c --cpu:$# koch" % cpu
|
||||||
exec "koch boot -d:release --cpu:$#" % cpu
|
kochExec "boot -d:release --cpu:$#" % cpu
|
||||||
exec "koch --latest zip -d:release"
|
kochExec "--latest zip -d:release"
|
||||||
overwriteFile r"build\nim-$#.zip" % VersionAsString,
|
overwriteFile r"build\nim-$#.zip" % VersionAsString,
|
||||||
r"web\upload\download\nim-$#_x$#.zip" % [VersionAsString, arch]
|
r"web\upload\download\nim-$#_x$#.zip" % [VersionAsString, arch]
|
||||||
|
|
||||||
|
|
@ -428,6 +447,47 @@ proc xtemp(cmd: string) =
|
||||||
finally:
|
finally:
|
||||||
copyExe(d / "bin" / "nim_backup".exe, d / "bin" / "nim".exe)
|
copyExe(d / "bin" / "nim_backup".exe, d / "bin" / "nim".exe)
|
||||||
|
|
||||||
|
proc runCI(cmd: string) =
|
||||||
|
doAssert cmd.len == 0, cmd # avoid silently ignoring
|
||||||
|
echo "runCI:", cmd
|
||||||
|
# note(@araq): Do not replace these commands with direct calls (eg boot())
|
||||||
|
# as that would weaken our testing efforts.
|
||||||
|
when defined(posix): # appveyor (on windows) didn't run this
|
||||||
|
kochExec "boot"
|
||||||
|
kochExec "boot -d:release"
|
||||||
|
|
||||||
|
## build nimble early on to enable remainder to depend on it if needed
|
||||||
|
kochExec "nimble"
|
||||||
|
|
||||||
|
when false:
|
||||||
|
for pkg in "zip opengl sdl1 jester@#head niminst".split:
|
||||||
|
exec "nimble install -y" & pkg
|
||||||
|
|
||||||
|
buildTools() # altenatively, kochExec "tools --toolsNoNimble"
|
||||||
|
|
||||||
|
## run tests
|
||||||
|
exec "nim e tests/test_nimscript.nims"
|
||||||
|
when defined(windows):
|
||||||
|
# note: will be over-written below
|
||||||
|
exec "nim c -d:nimCoroutines --os:genode -d:posix --compileOnly testament/tester"
|
||||||
|
|
||||||
|
# main bottleneck here
|
||||||
|
exec "nim c -r -d:nimCoroutines testament/tester --pedantic all -d:nimCoroutines"
|
||||||
|
|
||||||
|
exec "nim c -r nimdoc/tester"
|
||||||
|
exec "nim c -r nimpretty/tester.nim"
|
||||||
|
when defined(posix):
|
||||||
|
exec "nim c -r nimsuggest/tester"
|
||||||
|
|
||||||
|
## remaining actions
|
||||||
|
when defined(posix):
|
||||||
|
kochExec "docs --git.commit:devel"
|
||||||
|
kochExec "csource"
|
||||||
|
elif defined(windows):
|
||||||
|
when false:
|
||||||
|
kochExec "csource"
|
||||||
|
kochExec "zip"
|
||||||
|
|
||||||
proc pushCsources() =
|
proc pushCsources() =
|
||||||
if not dirExists("../csources/.git"):
|
if not dirExists("../csources/.git"):
|
||||||
quit "[Error] no csources git repository found"
|
quit "[Error] no csources git repository found"
|
||||||
|
|
@ -475,8 +535,7 @@ proc testUnixInstall(cmdLineRest: string) =
|
||||||
execCleanPath("./koch --latest tools")
|
execCleanPath("./koch --latest tools")
|
||||||
# check the tests work:
|
# check the tests work:
|
||||||
putEnv("NIM_EXE_NOT_IN_PATH", "NOT_IN_PATH")
|
putEnv("NIM_EXE_NOT_IN_PATH", "NOT_IN_PATH")
|
||||||
execCleanPath("./koch tests", destDir / "bin")
|
execCleanPath("./koch tests cat megatest", destDir / "bin")
|
||||||
#execCleanPath("./koch tests cat newconfig", destDir / "bin")
|
|
||||||
else:
|
else:
|
||||||
echo "Version check: failure"
|
echo "Version check: failure"
|
||||||
finally:
|
finally:
|
||||||
|
|
@ -512,6 +571,10 @@ when isMainModule:
|
||||||
var op = initOptParser()
|
var op = initOptParser()
|
||||||
var latest = false
|
var latest = false
|
||||||
var stable = false
|
var stable = false
|
||||||
|
template isLatest(): bool =
|
||||||
|
if stable: false
|
||||||
|
else:
|
||||||
|
existsDir(".git") or latest
|
||||||
while true:
|
while true:
|
||||||
op.next()
|
op.next()
|
||||||
case op.kind
|
case op.kind
|
||||||
|
|
@ -537,17 +600,18 @@ when isMainModule:
|
||||||
of "distrohelper": geninstall()
|
of "distrohelper": geninstall()
|
||||||
of "install": install(op.cmdLineRest)
|
of "install": install(op.cmdLineRest)
|
||||||
of "testinstall": testUnixInstall(op.cmdLineRest)
|
of "testinstall": testUnixInstall(op.cmdLineRest)
|
||||||
|
of "runci": runCI(op.cmdLineRest)
|
||||||
of "test", "tests": tests(op.cmdLineRest)
|
of "test", "tests": tests(op.cmdLineRest)
|
||||||
of "temp": temp(op.cmdLineRest)
|
of "temp": temp(op.cmdLineRest)
|
||||||
of "xtemp": xtemp(op.cmdLineRest)
|
of "xtemp": xtemp(op.cmdLineRest)
|
||||||
of "wintools": bundleWinTools()
|
of "wintools": bundleWinTools()
|
||||||
of "nimble":
|
of "nimble": buildNimble(isLatest())
|
||||||
if stable: buildNimble(false)
|
|
||||||
else: buildNimble(existsDir(".git") or latest)
|
|
||||||
of "nimsuggest": bundleNimsuggest()
|
of "nimsuggest": bundleNimsuggest()
|
||||||
|
of "toolsnonimble":
|
||||||
|
buildTools()
|
||||||
of "tools":
|
of "tools":
|
||||||
if stable: buildTools(false)
|
buildTools()
|
||||||
else: buildTools(existsDir(".git") or latest)
|
buildNimble(isLatest())
|
||||||
of "pushcsource", "pushcsources": pushCsources()
|
of "pushcsource", "pushcsources": pushCsources()
|
||||||
of "valgrind": valgrind(op.cmdLineRest)
|
of "valgrind": valgrind(op.cmdLineRest)
|
||||||
else: showHelp()
|
else: showHelp()
|
||||||
|
|
|
||||||
|
|
@ -151,7 +151,7 @@ proc `==`*(a, b: NimNode): bool {.magic: "EqNimrodNode", noSideEffect.}
|
||||||
|
|
||||||
proc `==`*(a, b: NimSym): bool {.magic: "EqNimrodNode", noSideEffect, deprecated.}
|
proc `==`*(a, b: NimSym): bool {.magic: "EqNimrodNode", noSideEffect, deprecated.}
|
||||||
## compares two Nim symbols
|
## compares two Nim symbols
|
||||||
## **Deprecated since version 0.18.1**; Use ```==`(NimNode,NimNode)`` instead.
|
## **Deprecated since version 0.18.1**; Use ``==(NimNode, NimNode)`` instead.
|
||||||
|
|
||||||
|
|
||||||
proc sameType*(a, b: NimNode): bool {.magic: "SameNodeType", noSideEffect.} =
|
proc sameType*(a, b: NimNode): bool {.magic: "SameNodeType", noSideEffect.} =
|
||||||
|
|
@ -377,7 +377,9 @@ proc copyNimNode*(n: NimNode): NimNode {.magic: "NCopyNimNode", noSideEffect.}
|
||||||
proc copyNimTree*(n: NimNode): NimNode {.magic: "NCopyNimTree", noSideEffect.}
|
proc copyNimTree*(n: NimNode): NimNode {.magic: "NCopyNimTree", noSideEffect.}
|
||||||
|
|
||||||
proc error*(msg: string, n: NimNode = nil) {.magic: "NError", benign.}
|
proc error*(msg: string, n: NimNode = nil) {.magic: "NError", benign.}
|
||||||
## writes an error message at compile time
|
## writes an error message at compile time. The optional ``n: NimNode``
|
||||||
|
## parameter is used as the source for file and line number information in
|
||||||
|
## the compilation error message.
|
||||||
|
|
||||||
proc warning*(msg: string, n: NimNode = nil) {.magic: "NWarning", benign.}
|
proc warning*(msg: string, n: NimNode = nil) {.magic: "NWarning", benign.}
|
||||||
## writes a warning message at compile time
|
## writes a warning message at compile time
|
||||||
|
|
@ -1402,8 +1404,14 @@ proc customPragmaNode(n: NimNode): NimNode =
|
||||||
let impl = n.getImpl()
|
let impl = n.getImpl()
|
||||||
if impl.kind in RoutineNodes:
|
if impl.kind in RoutineNodes:
|
||||||
return impl.pragma
|
return impl.pragma
|
||||||
|
elif impl.kind == nnkIdentDefs and impl[0].kind == nnkPragmaExpr:
|
||||||
|
return impl[0][1]
|
||||||
else:
|
else:
|
||||||
return typ.getImpl()[0][1]
|
let timpl = typ.getImpl()
|
||||||
|
if timpl.len>0 and timpl[0].len>1:
|
||||||
|
return timpl[0][1]
|
||||||
|
else:
|
||||||
|
return timpl
|
||||||
|
|
||||||
if n.kind in {nnkDotExpr, nnkCheckedFieldExpr}:
|
if n.kind in {nnkDotExpr, nnkCheckedFieldExpr}:
|
||||||
let name = $(if n.kind == nnkCheckedFieldExpr: n[0][1] else: n[1])
|
let name = $(if n.kind == nnkCheckedFieldExpr: n[0][1] else: n[1])
|
||||||
|
|
@ -1492,8 +1500,17 @@ macro getCustomPragmaVal*(n: typed, cp: typed{nkSym}): untyped =
|
||||||
let pragmaNode = customPragmaNode(n)
|
let pragmaNode = customPragmaNode(n)
|
||||||
for p in pragmaNode:
|
for p in pragmaNode:
|
||||||
if p.kind in nnkPragmaCallKinds and p.len > 0 and p[0].kind == nnkSym and p[0] == cp:
|
if p.kind in nnkPragmaCallKinds and p.len > 0 and p[0].kind == nnkSym and p[0] == cp:
|
||||||
return p[1]
|
if p.len == 2:
|
||||||
|
result = p[1]
|
||||||
|
else:
|
||||||
|
let def = p[0].getImpl[3]
|
||||||
|
result = newTree(nnkPar)
|
||||||
|
for i in 1..<p.len:
|
||||||
|
let key = def[i][0]
|
||||||
|
let val = p[i]
|
||||||
|
result.add newTree(nnkExprColonExpr, key, val)
|
||||||
|
break
|
||||||
|
if result.kind == nnkEmpty:
|
||||||
error(n.repr & " doesn't have a pragma named " & cp.repr()) # returning an empty node results in most cases in a cryptic error,
|
error(n.repr & " doesn't have a pragma named " & cp.repr()) # returning an empty node results in most cases in a cryptic error,
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -70,12 +70,6 @@ proc `=sink`[T](x: var seq[T]; y: seq[T]) =
|
||||||
a.len = b.len
|
a.len = b.len
|
||||||
a.p = b.p
|
a.p = b.p
|
||||||
|
|
||||||
when false:
|
|
||||||
proc incrSeqV3(s: PGenericSeq, typ: PNimType): PGenericSeq {.compilerProc.}
|
|
||||||
proc setLengthSeqV2(s: PGenericSeq, typ: PNimType, newLen: int): PGenericSeq {.
|
|
||||||
compilerRtl.}
|
|
||||||
proc newSeq(typ: PNimType, len: int): pointer {.compilerRtl.}
|
|
||||||
|
|
||||||
|
|
||||||
type
|
type
|
||||||
PayloadBase = object
|
PayloadBase = object
|
||||||
|
|
|
||||||
|
|
@ -1,18 +0,0 @@
|
||||||
#
|
|
||||||
#
|
|
||||||
# Nim's Runtime Library
|
|
||||||
# (c) Copyright 2012 Andreas Rumpf
|
|
||||||
#
|
|
||||||
# See the file "copying.txt", included in this
|
|
||||||
# distribution, for details about the copyright.
|
|
||||||
#
|
|
||||||
|
|
||||||
## **Warning:** Since version 0.11.4 this module is deprecated.
|
|
||||||
##
|
|
||||||
## This module implemented basic arithmetic operators for unsigned integers.
|
|
||||||
## These operators are now available in the ``system`` module directly.
|
|
||||||
|
|
||||||
{.deprecated.}
|
|
||||||
|
|
||||||
export `shr`, `shl`, `and`, `or`, `xor`, `==`, `+`, `-`, `*`, `div`, `mod`,
|
|
||||||
`<=`, `<`
|
|
||||||
|
|
@ -1,239 +0,0 @@
|
||||||
#
|
|
||||||
#
|
|
||||||
# Nim's Runtime Library
|
|
||||||
# (c) Copyright 2012 Andreas Rumpf
|
|
||||||
#
|
|
||||||
# See the file "copying.txt", included in this
|
|
||||||
# distribution, for details about the copyright.
|
|
||||||
#
|
|
||||||
|
|
||||||
## `Actor`:idx: support for Nim. An actor is implemented as a thread with
|
|
||||||
## a channel as its inbox. This module requires the ``--threads:on``
|
|
||||||
## command line switch.
|
|
||||||
##
|
|
||||||
## Example:
|
|
||||||
##
|
|
||||||
## .. code-block:: nim
|
|
||||||
##
|
|
||||||
## var
|
|
||||||
## a: ActorPool[int, void]
|
|
||||||
## createActorPool(a)
|
|
||||||
## for i in 0 ..< 300:
|
|
||||||
## a.spawn(i, proc (x: int) {.thread.} = echo x)
|
|
||||||
## a.join()
|
|
||||||
##
|
|
||||||
## **Note**: This whole module is deprecated. Use `threadpool` and ``spawn``
|
|
||||||
## instead.
|
|
||||||
|
|
||||||
{.deprecated.}
|
|
||||||
|
|
||||||
from os import sleep
|
|
||||||
|
|
||||||
type
|
|
||||||
Task*[In, Out] = object{.pure, final.} ## a task
|
|
||||||
when Out isnot void:
|
|
||||||
receiver*: ptr Channel[Out] ## the receiver channel of the response
|
|
||||||
action*: proc (x: In): Out {.thread.} ## action to execute;
|
|
||||||
## sometimes useful
|
|
||||||
shutDown*: bool ## set to tell an actor to shut-down
|
|
||||||
data*: In ## the data to process
|
|
||||||
|
|
||||||
Actor[In, Out] = object{.pure, final.}
|
|
||||||
i: Channel[Task[In, Out]]
|
|
||||||
t: Thread[ptr Actor[In, Out]]
|
|
||||||
|
|
||||||
PActor*[In, Out] = ptr Actor[In, Out] ## an actor
|
|
||||||
|
|
||||||
proc spawn*[In, Out](action: proc(
|
|
||||||
self: PActor[In, Out]){.thread.}): PActor[In, Out] =
|
|
||||||
## creates an actor; that is a thread with an inbox. The caller MUST call
|
|
||||||
## ``join`` because that also frees the actor's associated resources.
|
|
||||||
result = cast[PActor[In, Out]](allocShared0(sizeof(result[])))
|
|
||||||
open(result.i)
|
|
||||||
createThread(result.t, action, result)
|
|
||||||
|
|
||||||
proc inbox*[In, Out](self: PActor[In, Out]): ptr Channel[In] =
|
|
||||||
## gets a pointer to the associated inbox of the actor `self`.
|
|
||||||
result = addr(self.i)
|
|
||||||
|
|
||||||
proc running*[In, Out](a: PActor[In, Out]): bool =
|
|
||||||
## returns true if the actor `a` is running.
|
|
||||||
result = running(a.t)
|
|
||||||
|
|
||||||
proc ready*[In, Out](a: PActor[In, Out]): bool =
|
|
||||||
## returns true if the actor `a` is ready to process new messages.
|
|
||||||
result = ready(a.i)
|
|
||||||
|
|
||||||
proc join*[In, Out](a: PActor[In, Out]) =
|
|
||||||
## joins an actor.
|
|
||||||
joinThread(a.t)
|
|
||||||
close(a.i)
|
|
||||||
deallocShared(a)
|
|
||||||
|
|
||||||
proc recv*[In, Out](a: PActor[In, Out]): Task[In, Out] =
|
|
||||||
## receives a task from `a`'s inbox.
|
|
||||||
result = recv(a.i)
|
|
||||||
|
|
||||||
proc send*[In, Out, X, Y](receiver: PActor[In, Out], msg: In,
|
|
||||||
sender: PActor[X, Y]) =
|
|
||||||
## sends a message to `a`'s inbox.
|
|
||||||
var t: Task[In, Out]
|
|
||||||
t.receiver = addr(sender.i)
|
|
||||||
shallowCopy(t.data, msg)
|
|
||||||
send(receiver.i, t)
|
|
||||||
|
|
||||||
proc send*[In, Out](receiver: PActor[In, Out], msg: In,
|
|
||||||
sender: ptr Channel[Out] = nil) =
|
|
||||||
## sends a message to `receiver`'s inbox.
|
|
||||||
var t: Task[In, Out]
|
|
||||||
t.receiver = sender
|
|
||||||
shallowCopy(t.data, msg)
|
|
||||||
send(receiver.i, t)
|
|
||||||
|
|
||||||
proc sendShutdown*[In, Out](receiver: PActor[In, Out]) =
|
|
||||||
## send a shutdown message to `receiver`.
|
|
||||||
var t: Task[In, Out]
|
|
||||||
t.shutdown = true
|
|
||||||
send(receiver.i, t)
|
|
||||||
|
|
||||||
proc reply*[In, Out](t: Task[In, Out], m: Out) =
|
|
||||||
## sends a message to io's output message box.
|
|
||||||
when Out is void:
|
|
||||||
{.error: "you cannot reply to a void outbox".}
|
|
||||||
assert t.receiver != nil
|
|
||||||
send(t.receiver[], m)
|
|
||||||
|
|
||||||
|
|
||||||
# ----------------- actor pools ----------------------------------------------
|
|
||||||
|
|
||||||
type
|
|
||||||
ActorPool*[In, Out] = object{.pure, final.} ## an actor pool
|
|
||||||
actors: seq[PActor[In, Out]]
|
|
||||||
when Out isnot void:
|
|
||||||
outputs: Channel[Out]
|
|
||||||
|
|
||||||
proc `^`*[T](f: ptr Channel[T]): T =
|
|
||||||
## alias for 'recv'.
|
|
||||||
result = recv(f[])
|
|
||||||
|
|
||||||
proc poolWorker[In, Out](self: PActor[In, Out]) {.thread.} =
|
|
||||||
while true:
|
|
||||||
var m = self.recv
|
|
||||||
if m.shutDown: break
|
|
||||||
when Out is void:
|
|
||||||
m.action(m.data)
|
|
||||||
else:
|
|
||||||
send(m.receiver[], m.action(m.data))
|
|
||||||
#self.reply()
|
|
||||||
|
|
||||||
proc createActorPool*[In, Out](a: var ActorPool[In, Out], poolSize = 4) =
|
|
||||||
## creates an actor pool.
|
|
||||||
newSeq(a.actors, poolSize)
|
|
||||||
when Out isnot void:
|
|
||||||
open(a.outputs)
|
|
||||||
for i in 0 ..< a.actors.len:
|
|
||||||
a.actors[i] = spawn(poolWorker[In, Out])
|
|
||||||
|
|
||||||
proc sync*[In, Out](a: var ActorPool[In, Out], polling=50) =
|
|
||||||
## waits for every actor of `a` to finish with its work. Currently this is
|
|
||||||
## implemented as polling every `polling` ms and has a slight chance
|
|
||||||
## of failing since we check for every actor to be in `ready` state and not
|
|
||||||
## for messages still in ether. This will change in a later
|
|
||||||
## version, however.
|
|
||||||
var allReadyCount = 0
|
|
||||||
while true:
|
|
||||||
var wait = false
|
|
||||||
for i in 0..high(a.actors):
|
|
||||||
if not a.actors[i].i.ready:
|
|
||||||
wait = true
|
|
||||||
allReadyCount = 0
|
|
||||||
break
|
|
||||||
if not wait:
|
|
||||||
# it's possible that some actor sent a message to some other actor but
|
|
||||||
# both appeared to be non-working as the message takes some time to
|
|
||||||
# arrive. We assume that this won't take longer than `polling` and
|
|
||||||
# simply attempt a second time and declare victory then. ;-)
|
|
||||||
inc allReadyCount
|
|
||||||
if allReadyCount > 1: break
|
|
||||||
sleep(polling)
|
|
||||||
|
|
||||||
proc terminate*[In, Out](a: var ActorPool[In, Out]) =
|
|
||||||
## terminates each actor in the actor pool `a` and frees the
|
|
||||||
## resources attached to `a`.
|
|
||||||
var t: Task[In, Out]
|
|
||||||
t.shutdown = true
|
|
||||||
for i in 0..<a.actors.len: send(a.actors[i].i, t)
|
|
||||||
for i in 0..<a.actors.len: join(a.actors[i])
|
|
||||||
when Out isnot void:
|
|
||||||
close(a.outputs)
|
|
||||||
a.actors = @[]
|
|
||||||
|
|
||||||
proc join*[In, Out](a: var ActorPool[In, Out]) =
|
|
||||||
## short-cut for `sync` and then `terminate`.
|
|
||||||
sync(a)
|
|
||||||
terminate(a)
|
|
||||||
|
|
||||||
template setupTask =
|
|
||||||
t.action = action
|
|
||||||
shallowCopy(t.data, input)
|
|
||||||
|
|
||||||
template schedule =
|
|
||||||
# extremely simple scheduler: We always try the first thread first, so that
|
|
||||||
# it remains 'hot' ;-). Round-robin hurts for keeping threads hot.
|
|
||||||
for i in 0..high(p.actors):
|
|
||||||
if p.actors[i].i.ready:
|
|
||||||
p.actors[i].i.send(t)
|
|
||||||
return
|
|
||||||
# no thread ready :-( --> send message to the thread which has the least
|
|
||||||
# messages pending:
|
|
||||||
var minIdx = -1
|
|
||||||
var minVal = high(int)
|
|
||||||
for i in 0..high(p.actors):
|
|
||||||
var curr = p.actors[i].i.peek
|
|
||||||
if curr == 0:
|
|
||||||
# ok, is ready now:
|
|
||||||
p.actors[i].i.send(t)
|
|
||||||
return
|
|
||||||
if curr < minVal and curr >= 0:
|
|
||||||
minVal = curr
|
|
||||||
minIdx = i
|
|
||||||
if minIdx >= 0:
|
|
||||||
p.actors[minIdx].i.send(t)
|
|
||||||
else:
|
|
||||||
raise newException(DeadThreadError, "cannot send message; thread died")
|
|
||||||
|
|
||||||
proc spawn*[In, Out](p: var ActorPool[In, Out], input: In,
|
|
||||||
action: proc (input: In): Out {.thread.}
|
|
||||||
): ptr Channel[Out] =
|
|
||||||
## uses the actor pool to run ``action(input)`` concurrently.
|
|
||||||
## `spawn` is guaranteed to not block.
|
|
||||||
var t: Task[In, Out]
|
|
||||||
setupTask()
|
|
||||||
result = addr(p.outputs)
|
|
||||||
t.receiver = result
|
|
||||||
schedule()
|
|
||||||
|
|
||||||
proc spawn*[In](p: var ActorPool[In, void], input: In,
|
|
||||||
action: proc (input: In) {.thread.}) =
|
|
||||||
## uses the actor pool to run ``action(input)`` concurrently.
|
|
||||||
## `spawn` is guaranteed to not block.
|
|
||||||
var t: Task[In, void]
|
|
||||||
setupTask()
|
|
||||||
schedule()
|
|
||||||
|
|
||||||
when not defined(testing) and isMainModule:
|
|
||||||
var
|
|
||||||
a: ActorPool[int, void]
|
|
||||||
createActorPool(a)
|
|
||||||
for i in 0 ..< 300:
|
|
||||||
a.spawn(i, proc (x: int) {.thread.} = echo x)
|
|
||||||
|
|
||||||
when false:
|
|
||||||
proc treeDepth(n: PNode): int {.thread.} =
|
|
||||||
var x = a.spawn(treeDepth, n.le)
|
|
||||||
var y = a.spawn(treeDepth, n.ri)
|
|
||||||
result = max(^x, ^y) + 1
|
|
||||||
|
|
||||||
a.join()
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -1,3 +0,0 @@
|
||||||
# to shut up the tester:
|
|
||||||
--threads:on
|
|
||||||
|
|
||||||
|
|
@ -81,7 +81,6 @@ type
|
||||||
oneSecond: BiggestInt # Bytes transferred in one second.
|
oneSecond: BiggestInt # Bytes transferred in one second.
|
||||||
lastProgressReport: float # Time
|
lastProgressReport: float # Time
|
||||||
toStore: string # Data left to upload (Only used with async)
|
toStore: string # Data left to upload (Only used with async)
|
||||||
else: nil
|
|
||||||
|
|
||||||
FtpClientObj* = FtpBaseObj[Socket]
|
FtpClientObj* = FtpBaseObj[Socket]
|
||||||
FtpClient* = ref FtpClientObj
|
FtpClient* = ref FtpClientObj
|
||||||
|
|
|
||||||
|
|
@ -1,112 +0,0 @@
|
||||||
#
|
|
||||||
#
|
|
||||||
# Nim's Runtime Library
|
|
||||||
# (c) Copyright 2015 Dominik Picheta
|
|
||||||
#
|
|
||||||
# See the file "copying.txt", included in this
|
|
||||||
# distribution, for details about the copyright.
|
|
||||||
#
|
|
||||||
|
|
||||||
## **Warnings:** This module is deprecated since version 0.10.2.
|
|
||||||
## Use the `uri <uri.html>`_ module instead.
|
|
||||||
##
|
|
||||||
## Parses & constructs URLs.
|
|
||||||
|
|
||||||
{.deprecated.}
|
|
||||||
|
|
||||||
import strutils
|
|
||||||
|
|
||||||
type
|
|
||||||
Url* = tuple[ ## represents a *Uniform Resource Locator* (URL)
|
|
||||||
## any optional component is "" if it does not exist
|
|
||||||
scheme, username, password,
|
|
||||||
hostname, port, path, query, anchor: string]
|
|
||||||
|
|
||||||
proc parseUrl*(url: string): Url {.deprecated.} =
|
|
||||||
var i = 0
|
|
||||||
|
|
||||||
var scheme, username, password: string = ""
|
|
||||||
var hostname, port, path, query, anchor: string = ""
|
|
||||||
|
|
||||||
var temp = ""
|
|
||||||
|
|
||||||
if url[i] != '/': # url isn't a relative path
|
|
||||||
while true:
|
|
||||||
# Scheme
|
|
||||||
if url[i] == ':':
|
|
||||||
if url[i+1] == '/' and url[i+2] == '/':
|
|
||||||
scheme = temp
|
|
||||||
temp.setLen(0)
|
|
||||||
inc(i, 3) # Skip the //
|
|
||||||
# Authority(username, password)
|
|
||||||
if url[i] == '@':
|
|
||||||
username = temp
|
|
||||||
let colon = username.find(':')
|
|
||||||
if colon >= 0:
|
|
||||||
password = username.substr(colon+1)
|
|
||||||
username = username.substr(0, colon-1)
|
|
||||||
temp.setLen(0)
|
|
||||||
inc(i) #Skip the @
|
|
||||||
# hostname(subdomain, domain, port)
|
|
||||||
if url[i] == '/' or url[i] == '\0':
|
|
||||||
hostname = temp
|
|
||||||
let colon = hostname.find(':')
|
|
||||||
if colon >= 0:
|
|
||||||
port = hostname.substr(colon+1)
|
|
||||||
hostname = hostname.substr(0, colon-1)
|
|
||||||
|
|
||||||
temp.setLen(0)
|
|
||||||
break
|
|
||||||
|
|
||||||
temp.add(url[i])
|
|
||||||
inc(i)
|
|
||||||
|
|
||||||
if url[i] == '/': inc(i) # Skip the '/'
|
|
||||||
# Path
|
|
||||||
while true:
|
|
||||||
if url[i] == '?':
|
|
||||||
path = temp
|
|
||||||
temp.setLen(0)
|
|
||||||
if url[i] == '#':
|
|
||||||
if temp[0] == '?':
|
|
||||||
query = temp
|
|
||||||
else:
|
|
||||||
path = temp
|
|
||||||
temp.setLen(0)
|
|
||||||
|
|
||||||
if url[i] == '\0':
|
|
||||||
if temp[0] == '?':
|
|
||||||
query = temp
|
|
||||||
elif temp[0] == '#':
|
|
||||||
anchor = temp
|
|
||||||
else:
|
|
||||||
path = temp
|
|
||||||
break
|
|
||||||
|
|
||||||
temp.add(url[i])
|
|
||||||
inc(i)
|
|
||||||
|
|
||||||
return (scheme, username, password, hostname, port, path, query, anchor)
|
|
||||||
|
|
||||||
proc `$`*(u: Url): string {.deprecated.} =
|
|
||||||
## turns the URL `u` into its string representation.
|
|
||||||
result = ""
|
|
||||||
if u.scheme.len > 0:
|
|
||||||
result.add(u.scheme)
|
|
||||||
result.add("://")
|
|
||||||
if u.username.len > 0:
|
|
||||||
result.add(u.username)
|
|
||||||
if u.password.len > 0:
|
|
||||||
result.add(":")
|
|
||||||
result.add(u.password)
|
|
||||||
result.add("@")
|
|
||||||
result.add(u.hostname)
|
|
||||||
if u.port.len > 0:
|
|
||||||
result.add(":")
|
|
||||||
result.add(u.port)
|
|
||||||
if u.path.len > 0:
|
|
||||||
result.add("/")
|
|
||||||
result.add(u.path)
|
|
||||||
result.add(u.query)
|
|
||||||
result.add(u.anchor)
|
|
||||||
|
|
||||||
|
|
@ -1,14 +0,0 @@
|
||||||
import nativesockets
|
|
||||||
export nativesockets
|
|
||||||
|
|
||||||
{.warning: "rawsockets module is deprecated, use nativesockets instead".}
|
|
||||||
|
|
||||||
template newRawSocket*(domain, sockType, protocol: cint): untyped =
|
|
||||||
{.warning: "newRawSocket is deprecated, use newNativeSocket instead".}
|
|
||||||
newNativeSocket(domain, sockType, protocol)
|
|
||||||
|
|
||||||
template newRawSocket*(domain: Domain = AF_INET,
|
|
||||||
sockType: SockType = SOCK_STREAM,
|
|
||||||
protocol: Protocol = IPPROTO_TCP): untyped =
|
|
||||||
{.warning: "newRawSocket is deprecated, use newNativeSocket instead".}
|
|
||||||
newNativeSocket(domain, sockType, protocol)
|
|
||||||
|
|
@ -252,8 +252,11 @@ proc createDiffs(dataA, dataB: DiffData): seq[Item] =
|
||||||
|
|
||||||
proc diffInt*(arrayA, arrayB: openArray[int]): seq[Item] =
|
proc diffInt*(arrayA, arrayB: openArray[int]): seq[Item] =
|
||||||
## Find the difference in 2 arrays of integers.
|
## Find the difference in 2 arrays of integers.
|
||||||
|
##
|
||||||
## ``arrayA`` A-version of the numbers (usualy the old one)
|
## ``arrayA`` A-version of the numbers (usualy the old one)
|
||||||
|
##
|
||||||
## ``arrayB`` B-version of the numbers (usualy the new one)
|
## ``arrayB`` B-version of the numbers (usualy the new one)
|
||||||
|
##
|
||||||
## Returns a array of Items that describe the differences.
|
## Returns a array of Items that describe the differences.
|
||||||
|
|
||||||
# The A-Version of the data (original data) to be compared.
|
# The A-Version of the data (original data) to be compared.
|
||||||
|
|
@ -273,15 +276,16 @@ proc diffInt*(arrayA, arrayB: openArray[int]): seq[Item] =
|
||||||
|
|
||||||
proc diffText*(textA, textB: string): seq[Item] =
|
proc diffText*(textA, textB: string): seq[Item] =
|
||||||
## Find the difference in 2 text documents, comparing by textlines.
|
## Find the difference in 2 text documents, comparing by textlines.
|
||||||
|
##
|
||||||
## The algorithm itself is comparing 2 arrays of numbers so when comparing 2 text documents
|
## The algorithm itself is comparing 2 arrays of numbers so when comparing 2 text documents
|
||||||
## each line is converted into a (hash) number. This hash-value is computed by storing all
|
## each line is converted into a (hash) number. This hash-value is computed by storing all
|
||||||
## textlines into a common hashtable so i can find dublicates in there, and generating a
|
## textlines into a common hashtable so i can find dublicates in there, and generating a
|
||||||
## new number each time a new textline is inserted.
|
## new number each time a new textline is inserted.
|
||||||
## ``TextA`` A-version of the text (usualy the old one)
|
##
|
||||||
## ``TextB`` B-version of the text (usualy the new one)
|
## ``textA`` A-version of the text (usually the old one)
|
||||||
## ``trimSpace`` When set to true, all leading and trailing whitespace characters are stripped out before the comparation is done.
|
##
|
||||||
## ``ignoreSpace`` When set to true, all whitespace characters are converted to a single space character before the comparation is done.
|
## ``textB`` B-version of the text (usually the new one)
|
||||||
## ``ignoreCase`` When set to true, all characters are converted to their lowercase equivivalence before the comparation is done.
|
##
|
||||||
## Returns a seq of Items that describe the differences.
|
## Returns a seq of Items that describe the differences.
|
||||||
|
|
||||||
# prepare the input-text and convert to comparable numbers.
|
# prepare the input-text and convert to comparable numbers.
|
||||||
|
|
|
||||||
|
|
@ -131,7 +131,7 @@ proc getErrInfo(db: var DbConn): tuple[res: int, ss, ne, msg: string] {.
|
||||||
511.TSqlSmallInt, retSz.addr.PSQLSMALLINT)
|
511.TSqlSmallInt, retSz.addr.PSQLSMALLINT)
|
||||||
except:
|
except:
|
||||||
discard
|
discard
|
||||||
return (res.int, $sqlState, $nativeErr, $errMsg)
|
return (res.int, $(addr sqlState), $(addr nativeErr), $(addr errMsg))
|
||||||
|
|
||||||
proc dbError*(db: var DbConn) {.
|
proc dbError*(db: var DbConn) {.
|
||||||
tags: [ReadDbEffect, WriteDbEffect], raises: [DbError] .} =
|
tags: [ReadDbEffect, WriteDbEffect], raises: [DbError] .} =
|
||||||
|
|
|
||||||
|
|
@ -166,9 +166,11 @@ iterator fastRows*(db: DbConn, query: SqlQuery,
|
||||||
var stmt = setupQuery(db, query, args)
|
var stmt = setupQuery(db, query, args)
|
||||||
var L = (column_count(stmt))
|
var L = (column_count(stmt))
|
||||||
var result = newRow(L)
|
var result = newRow(L)
|
||||||
|
try:
|
||||||
while step(stmt) == SQLITE_ROW:
|
while step(stmt) == SQLITE_ROW:
|
||||||
setRow(stmt, result, L)
|
setRow(stmt, result, L)
|
||||||
yield result
|
yield result
|
||||||
|
finally:
|
||||||
if finalize(stmt) != SQLITE_OK: dbError(db)
|
if finalize(stmt) != SQLITE_OK: dbError(db)
|
||||||
|
|
||||||
iterator instantRows*(db: DbConn, query: SqlQuery,
|
iterator instantRows*(db: DbConn, query: SqlQuery,
|
||||||
|
|
@ -177,8 +179,10 @@ iterator instantRows*(db: DbConn, query: SqlQuery,
|
||||||
## same as fastRows but returns a handle that can be used to get column text
|
## same as fastRows but returns a handle that can be used to get column text
|
||||||
## on demand using []. Returned handle is valid only within the iterator body.
|
## on demand using []. Returned handle is valid only within the iterator body.
|
||||||
var stmt = setupQuery(db, query, args)
|
var stmt = setupQuery(db, query, args)
|
||||||
|
try:
|
||||||
while step(stmt) == SQLITE_ROW:
|
while step(stmt) == SQLITE_ROW:
|
||||||
yield stmt
|
yield stmt
|
||||||
|
finally:
|
||||||
if finalize(stmt) != SQLITE_OK: dbError(db)
|
if finalize(stmt) != SQLITE_OK: dbError(db)
|
||||||
|
|
||||||
proc toTypeKind(t: var DbType; x: int32) =
|
proc toTypeKind(t: var DbType; x: int32) =
|
||||||
|
|
@ -210,8 +214,10 @@ iterator instantRows*(db: DbConn; columns: var DbColumns; query: SqlQuery,
|
||||||
## on demand using []. Returned handle is valid only within the iterator body.
|
## on demand using []. Returned handle is valid only within the iterator body.
|
||||||
var stmt = setupQuery(db, query, args)
|
var stmt = setupQuery(db, query, args)
|
||||||
setColumns(columns, stmt)
|
setColumns(columns, stmt)
|
||||||
|
try:
|
||||||
while step(stmt) == SQLITE_ROW:
|
while step(stmt) == SQLITE_ROW:
|
||||||
yield stmt
|
yield stmt
|
||||||
|
finally:
|
||||||
if finalize(stmt) != SQLITE_OK: dbError(db)
|
if finalize(stmt) != SQLITE_OK: dbError(db)
|
||||||
|
|
||||||
proc `[]`*(row: InstantRow, col: int32): string {.inline.} =
|
proc `[]`*(row: InstantRow, col: int32): string {.inline.} =
|
||||||
|
|
|
||||||
|
|
@ -561,31 +561,6 @@ proc escapeRe*(s: string): string =
|
||||||
result.add("\\x")
|
result.add("\\x")
|
||||||
result.add(toHex(ord(c), 2))
|
result.add(toHex(ord(c), 2))
|
||||||
|
|
||||||
const ## common regular expressions
|
|
||||||
reIdentifier* {.deprecated.} = r"\b[a-zA-Z_]+[a-zA-Z_0-9]*\b"
|
|
||||||
## describes an identifier
|
|
||||||
reNatural* {.deprecated.} = r"\b\d+\b"
|
|
||||||
## describes a natural number
|
|
||||||
reInteger* {.deprecated.} = r"\b[-+]?\d+\b"
|
|
||||||
## describes an integer
|
|
||||||
reHex* {.deprecated.} = r"\b0[xX][0-9a-fA-F]+\b"
|
|
||||||
## describes a hexadecimal number
|
|
||||||
reBinary* {.deprecated.} = r"\b0[bB][01]+\b"
|
|
||||||
## describes a binary number (example: 0b11101)
|
|
||||||
reOctal* {.deprecated.} = r"\b0[oO][0-7]+\b"
|
|
||||||
## describes an octal number (example: 0o777)
|
|
||||||
reFloat* {.deprecated.} = r"\b[-+]?[0-9]*\.?[0-9]+([eE][-+]?[0-9]+)?\b"
|
|
||||||
## describes a floating point number
|
|
||||||
reEmail* {.deprecated.} = r"\b[a-zA-Z0-9!#$%&'*+/=?^_`{|}~\-]+(?:\. &" &
|
|
||||||
r"[a-zA-Z0-9!#$%&'*+/=?^_`{|}~-]+)*@" &
|
|
||||||
r"(?:[a-zA-Z0-9](?:[a-zA-Z0-9-]*[a-zA-Z0-9])?\.)+" &
|
|
||||||
r"(?:[a-zA-Z]{2}|com|org|net|gov|mil|biz|" &
|
|
||||||
r"info|mobi|name|aero|jobs|museum)\b"
|
|
||||||
## describes a common email address
|
|
||||||
reURL* {.deprecated.} = r"\b(http(s)?|ftp|gopher|telnet|file|notes|ms-help)" &
|
|
||||||
r":((//)|(\\\\))+[\w\d:#@%/;$()~_?\+\-\=\\\.\&]*\b"
|
|
||||||
## describes an URL
|
|
||||||
|
|
||||||
when isMainModule:
|
when isMainModule:
|
||||||
doAssert match("(a b c)", rex"\( .* \)")
|
doAssert match("(a b c)", rex"\( .* \)")
|
||||||
doAssert match("WHiLe", re("while", {reIgnoreCase}))
|
doAssert match("WHiLe", re("while", {reIgnoreCase}))
|
||||||
|
|
@ -595,7 +570,7 @@ when isMainModule:
|
||||||
doAssert "ABC".match(rex"\d+ | \w+")
|
doAssert "ABC".match(rex"\d+ | \w+")
|
||||||
|
|
||||||
{.push warnings:off.}
|
{.push warnings:off.}
|
||||||
doAssert matchLen("key", re(reIdentifier)) == 3
|
doAssert matchLen("key", re"\b[a-zA-Z_]+[a-zA-Z_0-9]*\b") == 3
|
||||||
{.pop.}
|
{.pop.}
|
||||||
|
|
||||||
var pattern = re"[a-z0-9]+\s*=\s*[a-z0-9]+"
|
var pattern = re"[a-z0-9]+\s*=\s*[a-z0-9]+"
|
||||||
|
|
|
||||||
|
|
@ -1,99 +0,0 @@
|
||||||
#
|
|
||||||
#
|
|
||||||
# Nim's Runtime Library
|
|
||||||
# (c) Copyright 2012 Dominik Picheta
|
|
||||||
#
|
|
||||||
# See the file "copying.txt", included in this
|
|
||||||
# distribution, for details about the copyright.
|
|
||||||
#
|
|
||||||
|
|
||||||
## This module provides an easy to use sockets-style
|
|
||||||
## nim interface to the OpenSSL library.
|
|
||||||
##
|
|
||||||
## **Warning:** This module is deprecated, use the SSL procedures defined in
|
|
||||||
## the ``net`` module instead.
|
|
||||||
|
|
||||||
{.deprecated.}
|
|
||||||
|
|
||||||
import openssl, strutils, os
|
|
||||||
|
|
||||||
type
|
|
||||||
SecureSocket* = object
|
|
||||||
ssl: SslPtr
|
|
||||||
bio: BIO
|
|
||||||
|
|
||||||
proc connect*(sock: var SecureSocket, address: string,
|
|
||||||
port: int): int =
|
|
||||||
## Connects to the specified `address` on the specified `port`.
|
|
||||||
## Returns the result of the certificate validation.
|
|
||||||
SslLoadErrorStrings()
|
|
||||||
ERR_load_BIO_strings()
|
|
||||||
|
|
||||||
if SSL_library_init() != 1:
|
|
||||||
raiseOSError(osLastError())
|
|
||||||
|
|
||||||
var ctx = SSL_CTX_new(SSLv23_client_method())
|
|
||||||
if ctx == nil:
|
|
||||||
ERR_print_errors_fp(stderr)
|
|
||||||
raiseOSError(osLastError())
|
|
||||||
|
|
||||||
#if SSL_CTX_load_verify_locations(ctx,
|
|
||||||
# "/tmp/openssl-0.9.8e/certs/vsign1.pem", NIL) == 0:
|
|
||||||
# echo("Failed load verify locations")
|
|
||||||
# ERR_print_errors_fp(stderr)
|
|
||||||
|
|
||||||
sock.bio = BIO_new_ssl_connect(ctx)
|
|
||||||
if BIO_get_ssl(sock.bio, addr(sock.ssl)) == 0:
|
|
||||||
raiseOSError(osLastError())
|
|
||||||
|
|
||||||
if BIO_set_conn_hostname(sock.bio, address & ":" & $port) != 1:
|
|
||||||
raiseOSError(osLastError())
|
|
||||||
|
|
||||||
if BIO_do_connect(sock.bio) <= 0:
|
|
||||||
ERR_print_errors_fp(stderr)
|
|
||||||
raiseOSError(osLastError())
|
|
||||||
|
|
||||||
result = SSL_get_verify_result(sock.ssl)
|
|
||||||
|
|
||||||
proc recvLine*(sock: SecureSocket, line: var TaintedString): bool =
|
|
||||||
## Acts in a similar fashion to the `recvLine` in the sockets module.
|
|
||||||
## Returns false when no data is available to be read.
|
|
||||||
## `Line` must be initialized and not nil!
|
|
||||||
setLen(line.string, 0)
|
|
||||||
while true:
|
|
||||||
var c: array[0..0, char]
|
|
||||||
var n = BIO_read(sock.bio, addr c, c.len.cint)
|
|
||||||
if n <= 0: return false
|
|
||||||
if c[0] == '\r':
|
|
||||||
n = BIO_read(sock.bio, addr c, c.len.cint)
|
|
||||||
if n > 0 and c[0] == '\L':
|
|
||||||
return true
|
|
||||||
elif n <= 0:
|
|
||||||
return false
|
|
||||||
elif c[0] == '\L': return true
|
|
||||||
add(line.string, c[0])
|
|
||||||
|
|
||||||
|
|
||||||
proc send*(sock: SecureSocket, data: string) =
|
|
||||||
## Writes `data` to the socket.
|
|
||||||
if BIO_write(sock.bio, data, data.len.cint) <= 0:
|
|
||||||
raiseOSError(osLastError())
|
|
||||||
|
|
||||||
proc close*(sock: SecureSocket) =
|
|
||||||
## Closes the socket
|
|
||||||
if BIO_free(sock.bio) <= 0:
|
|
||||||
ERR_print_errors_fp(stderr)
|
|
||||||
raiseOSError(osLastError())
|
|
||||||
|
|
||||||
when not defined(testing) and isMainModule:
|
|
||||||
var s: SecureSocket
|
|
||||||
echo connect(s, "smtp.gmail.com", 465)
|
|
||||||
|
|
||||||
#var buffer: array[0..255, char]
|
|
||||||
#echo BIO_read(bio, buffer, buffer.len)
|
|
||||||
var buffer: string = ""
|
|
||||||
|
|
||||||
echo s.recvLine(buffer)
|
|
||||||
echo buffer
|
|
||||||
echo buffer.len
|
|
||||||
|
|
||||||
|
|
@ -459,7 +459,6 @@ proc dispatchEvent*(et: EventTarget, ev: Event)
|
||||||
proc alert*(w: Window, msg: cstring)
|
proc alert*(w: Window, msg: cstring)
|
||||||
proc back*(w: Window)
|
proc back*(w: Window)
|
||||||
proc blur*(w: Window)
|
proc blur*(w: Window)
|
||||||
proc captureEvents*(w: Window, eventMask: int) {.deprecated.}
|
|
||||||
proc clearInterval*(w: Window, interval: ref TInterval)
|
proc clearInterval*(w: Window, interval: ref TInterval)
|
||||||
proc clearTimeout*(w: Window, timeout: ref TTimeOut)
|
proc clearTimeout*(w: Window, timeout: ref TTimeOut)
|
||||||
proc close*(w: Window)
|
proc close*(w: Window)
|
||||||
|
|
@ -478,7 +477,6 @@ proc open*(w: Window, uri, windowname: cstring,
|
||||||
properties: cstring = nil): Window
|
properties: cstring = nil): Window
|
||||||
proc print*(w: Window)
|
proc print*(w: Window)
|
||||||
proc prompt*(w: Window, text, default: cstring): cstring
|
proc prompt*(w: Window, text, default: cstring): cstring
|
||||||
proc releaseEvents*(w: Window, eventMask: int) {.deprecated.}
|
|
||||||
proc resizeBy*(w: Window, x, y: int)
|
proc resizeBy*(w: Window, x, y: int)
|
||||||
proc resizeTo*(w: Window, x, y: int)
|
proc resizeTo*(w: Window, x, y: int)
|
||||||
proc routeEvent*(w: Window, event: Event)
|
proc routeEvent*(w: Window, event: Event)
|
||||||
|
|
@ -513,7 +511,6 @@ proc setAttribute*(n: Node, name, value: cstring)
|
||||||
proc setAttributeNode*(n: Node, attr: Node)
|
proc setAttributeNode*(n: Node, attr: Node)
|
||||||
|
|
||||||
# Document "methods"
|
# Document "methods"
|
||||||
proc captureEvents*(d: Document, eventMask: int) {.deprecated.}
|
|
||||||
proc createAttribute*(d: Document, identifier: cstring): Node
|
proc createAttribute*(d: Document, identifier: cstring): Node
|
||||||
proc createElement*(d: Document, identifier: cstring): Element
|
proc createElement*(d: Document, identifier: cstring): Element
|
||||||
proc createTextNode*(d: Document, identifier: cstring): Node
|
proc createTextNode*(d: Document, identifier: cstring): Node
|
||||||
|
|
@ -524,7 +521,6 @@ proc getElementsByClassName*(d: Document, name: cstring): seq[Element]
|
||||||
proc getSelection*(d: Document): cstring
|
proc getSelection*(d: Document): cstring
|
||||||
proc handleEvent*(d: Document, event: Event)
|
proc handleEvent*(d: Document, event: Event)
|
||||||
proc open*(d: Document)
|
proc open*(d: Document)
|
||||||
proc releaseEvents*(d: Document, eventMask: int) {.deprecated.}
|
|
||||||
proc routeEvent*(d: Document, event: Event)
|
proc routeEvent*(d: Document, event: Event)
|
||||||
proc write*(d: Document, text: cstring)
|
proc write*(d: Document, text: cstring)
|
||||||
proc writeln*(d: Document, text: cstring)
|
proc writeln*(d: Document, text: cstring)
|
||||||
|
|
@ -605,25 +601,3 @@ proc parseInt*(s: cstring): int {.importc, nodecl.}
|
||||||
proc parseInt*(s: cstring, radix: int):int {.importc, nodecl.}
|
proc parseInt*(s: cstring, radix: int):int {.importc, nodecl.}
|
||||||
|
|
||||||
proc newEvent*(name: cstring): Event {.importcpp: "new Event(@)", constructor.}
|
proc newEvent*(name: cstring): Event {.importcpp: "new Event(@)", constructor.}
|
||||||
|
|
||||||
type
|
|
||||||
TEventHandlers* {.deprecated.} = EventTargetObj
|
|
||||||
TWindow* {.deprecated.} = WindowObj
|
|
||||||
TFrame* {.deprecated.} = FrameObj
|
|
||||||
TNode* {.deprecated.} = NodeObj
|
|
||||||
TDocument* {.deprecated.} = DocumentObj
|
|
||||||
TElement* {.deprecated.} = ElementObj
|
|
||||||
TLink* {.deprecated.} = LinkObj
|
|
||||||
TEmbed* {.deprecated.} = EmbedObj
|
|
||||||
TAnchor* {.deprecated.} = AnchorObj
|
|
||||||
TOption* {.deprecated.} = OptionObj
|
|
||||||
TForm* {.deprecated.} = FormObj
|
|
||||||
TImage* {.deprecated.} = ImageObj
|
|
||||||
TNodeType* {.deprecated.} = NodeType
|
|
||||||
TEvent* {.deprecated.} = EventObj
|
|
||||||
TLocation* {.deprecated.} = LocationObj
|
|
||||||
THistory* {.deprecated.} = HistoryObj
|
|
||||||
TNavigator* {.deprecated.} = NavigatorObj
|
|
||||||
TStyle* {.deprecated.} = StyleObj
|
|
||||||
TScreen* {.deprecated.} = ScreenObj
|
|
||||||
TApplet* {.importc, deprecated.} = object of RootObj
|
|
||||||
|
|
|
||||||
|
|
@ -73,7 +73,7 @@ proc parse*(d: DateLib, s: cstring): int {.importcpp.}
|
||||||
proc newDate*(): DateTime {.
|
proc newDate*(): DateTime {.
|
||||||
importcpp: "new Date()".}
|
importcpp: "new Date()".}
|
||||||
|
|
||||||
proc newDate*(date: int|string): DateTime {.
|
proc newDate*(date: int|int64|string): DateTime {.
|
||||||
importcpp: "new Date(#)".}
|
importcpp: "new Date(#)".}
|
||||||
|
|
||||||
proc newDate*(year, month, day, hours, minutes,
|
proc newDate*(year, month, day, hours, minutes,
|
||||||
|
|
@ -90,6 +90,16 @@ proc getSeconds*(d: DateTime): int {.importcpp.}
|
||||||
proc getYear*(d: DateTime): int {.importcpp.}
|
proc getYear*(d: DateTime): int {.importcpp.}
|
||||||
proc getTime*(d: DateTime): int {.importcpp.}
|
proc getTime*(d: DateTime): int {.importcpp.}
|
||||||
proc toString*(d: DateTime): cstring {.importcpp.}
|
proc toString*(d: DateTime): cstring {.importcpp.}
|
||||||
|
proc getUTCDate*(d: DateTime): int {.importcpp.}
|
||||||
|
proc getUTCFullYear*(d: DateTime): int {.importcpp.}
|
||||||
|
proc getUTCHours*(d: DateTime): int {.importcpp.}
|
||||||
|
proc getUTCMilliseconds*(d: DateTime): int {.importcpp.}
|
||||||
|
proc getUTCMinutes*(d: DateTime): int {.importcpp.}
|
||||||
|
proc getUTCMonth*(d: DateTime): int {.importcpp.}
|
||||||
|
proc getUTCSeconds*(d: DateTime): int {.importcpp.}
|
||||||
|
proc getUTCDay*(d: DateTime): int {.importcpp.}
|
||||||
|
proc getTimezoneOffset*(d: DateTime): int {.importcpp.}
|
||||||
|
proc setFullYear*(d: DateTime, year: int) {.importcpp.}
|
||||||
|
|
||||||
#JSON library
|
#JSON library
|
||||||
proc stringify*(l: JsonLib, s: JsRoot): cstring {.importcpp.}
|
proc stringify*(l: JsonLib, s: JsRoot): cstring {.importcpp.}
|
||||||
|
|
|
||||||
|
|
@ -104,6 +104,12 @@ var
|
||||||
jsFilename* {.importc: "__filename", nodecl.}: cstring
|
jsFilename* {.importc: "__filename", nodecl.}: cstring
|
||||||
## JavaScript's __filename pseudo-variable
|
## JavaScript's __filename pseudo-variable
|
||||||
|
|
||||||
|
proc isNull*[T](x: T): bool {.noSideEffect, importcpp: "(# === null)".}
|
||||||
|
## check if a value is exactly null
|
||||||
|
|
||||||
|
proc isUndefined*[T](x: T): bool {.noSideEffect, importcpp: "(# === undefined)".}
|
||||||
|
## check if a value is exactly undefined
|
||||||
|
|
||||||
# Exceptions
|
# Exceptions
|
||||||
type
|
type
|
||||||
JsError* {.importc: "Error".} = object of JsRoot
|
JsError* {.importc: "Error".} = object of JsRoot
|
||||||
|
|
|
||||||
|
|
@ -166,13 +166,13 @@ __clang__
|
||||||
# define N_STDCALL(rettype, name) rettype __stdcall name
|
# define N_STDCALL(rettype, name) rettype __stdcall name
|
||||||
# define N_SYSCALL(rettype, name) rettype __syscall name
|
# define N_SYSCALL(rettype, name) rettype __syscall name
|
||||||
# define N_FASTCALL(rettype, name) rettype __fastcall name
|
# define N_FASTCALL(rettype, name) rettype __fastcall name
|
||||||
# define N_SAFECALL(rettype, name) rettype __safecall name
|
# define N_SAFECALL(rettype, name) rettype __stdcall name
|
||||||
/* function pointers with calling convention: */
|
/* function pointers with calling convention: */
|
||||||
# define N_CDECL_PTR(rettype, name) rettype (__cdecl *name)
|
# define N_CDECL_PTR(rettype, name) rettype (__cdecl *name)
|
||||||
# define N_STDCALL_PTR(rettype, name) rettype (__stdcall *name)
|
# define N_STDCALL_PTR(rettype, name) rettype (__stdcall *name)
|
||||||
# define N_SYSCALL_PTR(rettype, name) rettype (__syscall *name)
|
# define N_SYSCALL_PTR(rettype, name) rettype (__syscall *name)
|
||||||
# define N_FASTCALL_PTR(rettype, name) rettype (__fastcall *name)
|
# define N_FASTCALL_PTR(rettype, name) rettype (__fastcall *name)
|
||||||
# define N_SAFECALL_PTR(rettype, name) rettype (__safecall *name)
|
# define N_SAFECALL_PTR(rettype, name) rettype (__stdcall *name)
|
||||||
|
|
||||||
# ifdef __cplusplus
|
# ifdef __cplusplus
|
||||||
# define N_LIB_EXPORT extern "C" __declspec(dllexport)
|
# define N_LIB_EXPORT extern "C" __declspec(dllexport)
|
||||||
|
|
|
||||||
|
|
@ -560,9 +560,9 @@ proc match(p: RstParser, start: int, expr: string): bool =
|
||||||
result = (p.tok[j].kind == tkWord) or (p.tok[j].symbol == "#")
|
result = (p.tok[j].kind == tkWord) or (p.tok[j].symbol == "#")
|
||||||
if result:
|
if result:
|
||||||
case p.tok[j].symbol[0]
|
case p.tok[j].symbol[0]
|
||||||
of 'a'..'z', 'A'..'Z': result = len(p.tok[j].symbol) == 1
|
of 'a'..'z', 'A'..'Z', '#': result = len(p.tok[j].symbol) == 1
|
||||||
of '0'..'9': result = allCharsInSet(p.tok[j].symbol, {'0'..'9'})
|
of '0'..'9': result = allCharsInSet(p.tok[j].symbol, {'0'..'9'})
|
||||||
else: discard
|
else: result = false
|
||||||
else:
|
else:
|
||||||
var c = expr[i]
|
var c = expr[i]
|
||||||
var length = 0
|
var length = 0
|
||||||
|
|
@ -780,6 +780,31 @@ proc parseMarkdownCodeblock(p: var RstParser): PRstNode =
|
||||||
add(result, nil)
|
add(result, nil)
|
||||||
add(result, lb)
|
add(result, lb)
|
||||||
|
|
||||||
|
proc parseMarkdownLink(p: var RstParser; father: PRstNode): bool =
|
||||||
|
result = true
|
||||||
|
var desc, link = ""
|
||||||
|
var i = p.idx
|
||||||
|
|
||||||
|
template parse(endToken, dest) =
|
||||||
|
inc i # skip begin token
|
||||||
|
while true:
|
||||||
|
if p.tok[i].kind in {tkEof, tkIndent}: return false
|
||||||
|
if p.tok[i].symbol == endToken: break
|
||||||
|
dest.add p.tok[i].symbol
|
||||||
|
inc i
|
||||||
|
inc i # skip end token
|
||||||
|
|
||||||
|
parse("]", desc)
|
||||||
|
if p.tok[i].symbol != "(": return false
|
||||||
|
parse(")", link)
|
||||||
|
let child = newRstNode(rnHyperlink)
|
||||||
|
child.add desc
|
||||||
|
child.add link
|
||||||
|
# only commit if we detected no syntax error:
|
||||||
|
father.add child
|
||||||
|
p.idx = i
|
||||||
|
result = true
|
||||||
|
|
||||||
proc parseInline(p: var RstParser, father: PRstNode) =
|
proc parseInline(p: var RstParser, father: PRstNode) =
|
||||||
case p.tok[p.idx].kind
|
case p.tok[p.idx].kind
|
||||||
of tkPunct:
|
of tkPunct:
|
||||||
|
|
@ -811,6 +836,9 @@ proc parseInline(p: var RstParser, father: PRstNode) =
|
||||||
var n = newRstNode(rnSubstitutionReferences)
|
var n = newRstNode(rnSubstitutionReferences)
|
||||||
parseUntil(p, n, "|", false)
|
parseUntil(p, n, "|", false)
|
||||||
add(father, n)
|
add(father, n)
|
||||||
|
elif roSupportMarkdown in p.s.options and p.tok[p.idx].symbol == "[" and
|
||||||
|
parseMarkdownLink(p, father):
|
||||||
|
discard "parseMarkdownLink already processed it"
|
||||||
else:
|
else:
|
||||||
if roSupportSmilies in p.s.options:
|
if roSupportSmilies in p.s.options:
|
||||||
let n = parseSmiley(p)
|
let n = parseSmiley(p)
|
||||||
|
|
@ -1037,15 +1065,32 @@ proc isOptionList(p: RstParser): bool =
|
||||||
result = match(p, p.idx, "-w") or match(p, p.idx, "--w") or
|
result = match(p, p.idx, "-w") or match(p, p.idx, "--w") or
|
||||||
match(p, p.idx, "/w") or match(p, p.idx, "//w")
|
match(p, p.idx, "/w") or match(p, p.idx, "//w")
|
||||||
|
|
||||||
|
proc isMarkdownHeadlinePattern(s: string): bool =
|
||||||
|
if s.len >= 1 and s.len <= 6:
|
||||||
|
for c in s:
|
||||||
|
if c != '#': return false
|
||||||
|
result = true
|
||||||
|
|
||||||
|
proc isMarkdownHeadline(p: RstParser): bool =
|
||||||
|
if roSupportMarkdown in p.s.options:
|
||||||
|
if isMarkdownHeadlinePattern(p.tok[p.idx].symbol) and p.tok[p.idx+1].kind == tkWhite:
|
||||||
|
if p.tok[p.idx+2].kind in {tkWord, tkOther, tkPunct}:
|
||||||
|
result = true
|
||||||
|
|
||||||
proc whichSection(p: RstParser): RstNodeKind =
|
proc whichSection(p: RstParser): RstNodeKind =
|
||||||
case p.tok[p.idx].kind
|
case p.tok[p.idx].kind
|
||||||
of tkAdornment:
|
of tkAdornment:
|
||||||
if match(p, p.idx + 1, "ii"): result = rnTransition
|
if match(p, p.idx + 1, "ii"): result = rnTransition
|
||||||
elif match(p, p.idx + 1, " a"): result = rnTable
|
elif match(p, p.idx + 1, " a"): result = rnTable
|
||||||
elif match(p, p.idx + 1, "i"): result = rnOverline
|
elif match(p, p.idx + 1, "i"): result = rnOverline
|
||||||
else: result = rnLeaf
|
elif isMarkdownHeadline(p):
|
||||||
|
result = rnHeadline
|
||||||
|
else:
|
||||||
|
result = rnLeaf
|
||||||
of tkPunct:
|
of tkPunct:
|
||||||
if match(p, tokenAfterNewline(p), "ai"):
|
if isMarkdownHeadline(p):
|
||||||
|
result = rnHeadline
|
||||||
|
elif match(p, tokenAfterNewline(p), "ai"):
|
||||||
result = rnHeadline
|
result = rnHeadline
|
||||||
elif p.tok[p.idx].symbol == "::":
|
elif p.tok[p.idx].symbol == "::":
|
||||||
result = rnLiteralBlock
|
result = rnLiteralBlock
|
||||||
|
|
@ -1060,7 +1105,7 @@ proc whichSection(p: RstParser): RstNodeKind =
|
||||||
elif match(p, p.idx, ":w:") and predNL(p):
|
elif match(p, p.idx, ":w:") and predNL(p):
|
||||||
# (p.tok[p.idx].symbol == ":")
|
# (p.tok[p.idx].symbol == ":")
|
||||||
result = rnFieldList
|
result = rnFieldList
|
||||||
elif match(p, p.idx, "(e) "):
|
elif match(p, p.idx, "(e) ") or match(p, p.idx, "e. "):
|
||||||
result = rnEnumList
|
result = rnEnumList
|
||||||
elif match(p, p.idx, "+a+"):
|
elif match(p, p.idx, "+a+"):
|
||||||
result = rnGridTable
|
result = rnGridTable
|
||||||
|
|
@ -1130,6 +1175,12 @@ proc parseParagraph(p: var RstParser, result: PRstNode) =
|
||||||
|
|
||||||
proc parseHeadline(p: var RstParser): PRstNode =
|
proc parseHeadline(p: var RstParser): PRstNode =
|
||||||
result = newRstNode(rnHeadline)
|
result = newRstNode(rnHeadline)
|
||||||
|
if isMarkdownHeadline(p):
|
||||||
|
result.level = p.tok[p.idx].symbol.len
|
||||||
|
assert(p.tok[p.idx+1].kind == tkWhite)
|
||||||
|
inc p.idx, 2
|
||||||
|
parseUntilNewline(p, result)
|
||||||
|
else:
|
||||||
parseUntilNewline(p, result)
|
parseUntilNewline(p, result)
|
||||||
assert(p.tok[p.idx].kind == tkIndent)
|
assert(p.tok[p.idx].kind == tkIndent)
|
||||||
assert(p.tok[p.idx + 1].kind == tkAdornment)
|
assert(p.tok[p.idx + 1].kind == tkAdornment)
|
||||||
|
|
|
||||||
|
|
@ -89,6 +89,9 @@ proc lastSon*(n: PRstNode): PRstNode =
|
||||||
proc add*(father, son: PRstNode) =
|
proc add*(father, son: PRstNode) =
|
||||||
add(father.sons, son)
|
add(father.sons, son)
|
||||||
|
|
||||||
|
proc add*(father: PRstNode; s: string) =
|
||||||
|
add(father.sons, newRstNode(rnLeaf, s))
|
||||||
|
|
||||||
proc addIfNotNil*(father, son: PRstNode) =
|
proc addIfNotNil*(father, son: PRstNode) =
|
||||||
if son != nil: add(father, son)
|
if son != nil: add(father, son)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1134,11 +1134,9 @@ proc renderRstToOut(d: PDoc, n: PRstNode, result: var string) =
|
||||||
of rnTripleEmphasis:
|
of rnTripleEmphasis:
|
||||||
renderAux(d, n, "<strong><em>$1</em></strong>",
|
renderAux(d, n, "<strong><em>$1</em></strong>",
|
||||||
"\\textbf{emph{$1}}", result)
|
"\\textbf{emph{$1}}", result)
|
||||||
of rnInterpretedText:
|
|
||||||
renderAux(d, n, "<cite>$1</cite>", "\\emph{$1}", result)
|
|
||||||
of rnIdx:
|
of rnIdx:
|
||||||
renderIndexTerm(d, n, result)
|
renderIndexTerm(d, n, result)
|
||||||
of rnInlineLiteral:
|
of rnInlineLiteral, rnInterpretedText:
|
||||||
renderAux(d, n,
|
renderAux(d, n,
|
||||||
"<tt class=\"docutils literal\"><span class=\"pre\">$1</span></tt>",
|
"<tt class=\"docutils literal\"><span class=\"pre\">$1</span></tt>",
|
||||||
"\\texttt{$1}", result)
|
"\\texttt{$1}", result)
|
||||||
|
|
|
||||||
|
|
@ -295,6 +295,7 @@ const IF_NAMESIZE* = cint(16)
|
||||||
const IPPROTO_IP* = cint(0)
|
const IPPROTO_IP* = cint(0)
|
||||||
const IPPROTO_IPV6* = cint(41)
|
const IPPROTO_IPV6* = cint(41)
|
||||||
const IPPROTO_ICMP* = cint(1)
|
const IPPROTO_ICMP* = cint(1)
|
||||||
|
const IPPROTO_ICMPV6* = cint(58)
|
||||||
const IPPROTO_RAW* = cint(255)
|
const IPPROTO_RAW* = cint(255)
|
||||||
const IPPROTO_TCP* = cint(6)
|
const IPPROTO_TCP* = cint(6)
|
||||||
const IPPROTO_UDP* = cint(17)
|
const IPPROTO_UDP* = cint(17)
|
||||||
|
|
|
||||||
|
|
@ -237,6 +237,7 @@ const IF_NAMESIZE* = cint(16)
|
||||||
const IPPROTO_IP* = cint(0)
|
const IPPROTO_IP* = cint(0)
|
||||||
const IPPROTO_IPV6* = cint(41)
|
const IPPROTO_IPV6* = cint(41)
|
||||||
const IPPROTO_ICMP* = cint(1)
|
const IPPROTO_ICMP* = cint(1)
|
||||||
|
const IPPROTO_ICMPV6* = cint(58)
|
||||||
const IPPROTO_RAW* = cint(255)
|
const IPPROTO_RAW* = cint(255)
|
||||||
const IPPROTO_TCP* = cint(6)
|
const IPPROTO_TCP* = cint(6)
|
||||||
const IPPROTO_UDP* = cint(17)
|
const IPPROTO_UDP* = cint(17)
|
||||||
|
|
|
||||||
|
|
@ -302,6 +302,7 @@ var IF_NAMESIZE* {.importc: "IF_NAMESIZE", header: "<net/if.h>".}: cint
|
||||||
var IPPROTO_IP* {.importc: "IPPROTO_IP", header: "<netinet/in.h>".}: cint
|
var IPPROTO_IP* {.importc: "IPPROTO_IP", header: "<netinet/in.h>".}: cint
|
||||||
var IPPROTO_IPV6* {.importc: "IPPROTO_IPV6", header: "<netinet/in.h>".}: cint
|
var IPPROTO_IPV6* {.importc: "IPPROTO_IPV6", header: "<netinet/in.h>".}: cint
|
||||||
var IPPROTO_ICMP* {.importc: "IPPROTO_ICMP", header: "<netinet/in.h>".}: cint
|
var IPPROTO_ICMP* {.importc: "IPPROTO_ICMP", header: "<netinet/in.h>".}: cint
|
||||||
|
var IPPROTO_ICMPV6* {.importc: "IPPROTO_ICMPV6", header: "<netinet/in.h>".}: cint
|
||||||
var IPPROTO_RAW* {.importc: "IPPROTO_RAW", header: "<netinet/in.h>".}: cint
|
var IPPROTO_RAW* {.importc: "IPPROTO_RAW", header: "<netinet/in.h>".}: cint
|
||||||
var IPPROTO_TCP* {.importc: "IPPROTO_TCP", header: "<netinet/in.h>".}: cint
|
var IPPROTO_TCP* {.importc: "IPPROTO_TCP", header: "<netinet/in.h>".}: cint
|
||||||
var IPPROTO_UDP* {.importc: "IPPROTO_UDP", header: "<netinet/in.h>".}: cint
|
var IPPROTO_UDP* {.importc: "IPPROTO_UDP", header: "<netinet/in.h>".}: cint
|
||||||
|
|
|
||||||
|
|
@ -143,11 +143,14 @@ proc parseUppercaseMethod(name: string): HttpMethod =
|
||||||
of "TRACE": HttpTrace
|
of "TRACE": HttpTrace
|
||||||
else: raise newException(ValueError, "Invalid HTTP method " & name)
|
else: raise newException(ValueError, "Invalid HTTP method " & name)
|
||||||
|
|
||||||
proc processRequest(server: AsyncHttpServer, req: FutureVar[Request],
|
proc processRequest(
|
||||||
|
server: AsyncHttpServer,
|
||||||
|
req: FutureVar[Request],
|
||||||
client: AsyncSocket,
|
client: AsyncSocket,
|
||||||
address: string, lineFut: FutureVar[string],
|
address: string,
|
||||||
callback: proc (request: Request):
|
lineFut: FutureVar[string],
|
||||||
Future[void] {.closure, gcsafe.}) {.async.} =
|
callback: proc (request: Request): Future[void] {.closure, gcsafe.},
|
||||||
|
): Future[bool] {.async.} =
|
||||||
|
|
||||||
# Alias `request` to `req.mget()` so we don't have to write `mget` everywhere.
|
# Alias `request` to `req.mget()` so we don't have to write `mget` everywhere.
|
||||||
template request(): Request =
|
template request(): Request =
|
||||||
|
|
@ -171,12 +174,12 @@ proc processRequest(server: AsyncHttpServer, req: FutureVar[Request],
|
||||||
|
|
||||||
if lineFut.mget == "":
|
if lineFut.mget == "":
|
||||||
client.close()
|
client.close()
|
||||||
return
|
return false
|
||||||
|
|
||||||
if lineFut.mget.len > maxLine:
|
if lineFut.mget.len > maxLine:
|
||||||
await request.respondError(Http413)
|
await request.respondError(Http413)
|
||||||
client.close()
|
client.close()
|
||||||
return
|
return false
|
||||||
if lineFut.mget != "\c\L":
|
if lineFut.mget != "\c\L":
|
||||||
break
|
break
|
||||||
|
|
||||||
|
|
@ -189,22 +192,22 @@ proc processRequest(server: AsyncHttpServer, req: FutureVar[Request],
|
||||||
request.reqMethod = parseUppercaseMethod(linePart)
|
request.reqMethod = parseUppercaseMethod(linePart)
|
||||||
except ValueError:
|
except ValueError:
|
||||||
asyncCheck request.respondError(Http400)
|
asyncCheck request.respondError(Http400)
|
||||||
return
|
return true # Retry processing of request
|
||||||
of 1:
|
of 1:
|
||||||
try:
|
try:
|
||||||
parseUri(linePart, request.url)
|
parseUri(linePart, request.url)
|
||||||
except ValueError:
|
except ValueError:
|
||||||
asyncCheck request.respondError(Http400)
|
asyncCheck request.respondError(Http400)
|
||||||
return
|
return true
|
||||||
of 2:
|
of 2:
|
||||||
try:
|
try:
|
||||||
request.protocol = parseProtocol(linePart)
|
request.protocol = parseProtocol(linePart)
|
||||||
except ValueError:
|
except ValueError:
|
||||||
asyncCheck request.respondError(Http400)
|
asyncCheck request.respondError(Http400)
|
||||||
return
|
return true
|
||||||
else:
|
else:
|
||||||
await request.respondError(Http400)
|
await request.respondError(Http400)
|
||||||
return
|
return true
|
||||||
inc i
|
inc i
|
||||||
|
|
||||||
# Headers
|
# Headers
|
||||||
|
|
@ -215,10 +218,10 @@ proc processRequest(server: AsyncHttpServer, req: FutureVar[Request],
|
||||||
await client.recvLineInto(lineFut, maxLength=maxLine)
|
await client.recvLineInto(lineFut, maxLength=maxLine)
|
||||||
|
|
||||||
if lineFut.mget == "":
|
if lineFut.mget == "":
|
||||||
client.close(); return
|
client.close(); return false
|
||||||
if lineFut.mget.len > maxLine:
|
if lineFut.mget.len > maxLine:
|
||||||
await request.respondError(Http413)
|
await request.respondError(Http413)
|
||||||
client.close(); return
|
client.close(); return false
|
||||||
if lineFut.mget == "\c\L": break
|
if lineFut.mget == "\c\L": break
|
||||||
let (key, value) = parseHeader(lineFut.mget)
|
let (key, value) = parseHeader(lineFut.mget)
|
||||||
request.headers[key] = value
|
request.headers[key] = value
|
||||||
|
|
@ -226,7 +229,7 @@ proc processRequest(server: AsyncHttpServer, req: FutureVar[Request],
|
||||||
if request.headers.len > headerLimit:
|
if request.headers.len > headerLimit:
|
||||||
await client.sendStatus("400 Bad Request")
|
await client.sendStatus("400 Bad Request")
|
||||||
request.client.close()
|
request.client.close()
|
||||||
return
|
return false
|
||||||
|
|
||||||
if request.reqMethod == HttpPost:
|
if request.reqMethod == HttpPost:
|
||||||
# Check for Expect header
|
# Check for Expect header
|
||||||
|
|
@ -242,24 +245,24 @@ proc processRequest(server: AsyncHttpServer, req: FutureVar[Request],
|
||||||
var contentLength = 0
|
var contentLength = 0
|
||||||
if parseSaturatedNatural(request.headers["Content-Length"], contentLength) == 0:
|
if parseSaturatedNatural(request.headers["Content-Length"], contentLength) == 0:
|
||||||
await request.respond(Http400, "Bad Request. Invalid Content-Length.")
|
await request.respond(Http400, "Bad Request. Invalid Content-Length.")
|
||||||
return
|
return true
|
||||||
else:
|
else:
|
||||||
if contentLength > server.maxBody:
|
if contentLength > server.maxBody:
|
||||||
await request.respondError(Http413)
|
await request.respondError(Http413)
|
||||||
return
|
return false
|
||||||
request.body = await client.recv(contentLength)
|
request.body = await client.recv(contentLength)
|
||||||
if request.body.len != contentLength:
|
if request.body.len != contentLength:
|
||||||
await request.respond(Http400, "Bad Request. Content-Length does not match actual.")
|
await request.respond(Http400, "Bad Request. Content-Length does not match actual.")
|
||||||
return
|
return true
|
||||||
elif request.reqMethod == HttpPost:
|
elif request.reqMethod == HttpPost:
|
||||||
await request.respond(Http411, "Content-Length required.")
|
await request.respond(Http411, "Content-Length required.")
|
||||||
return
|
return true
|
||||||
|
|
||||||
# Call the user's callback.
|
# Call the user's callback.
|
||||||
await callback(request)
|
await callback(request)
|
||||||
|
|
||||||
if "upgrade" in request.headers.getOrDefault("connection"):
|
if "upgrade" in request.headers.getOrDefault("connection"):
|
||||||
return
|
return false
|
||||||
|
|
||||||
# Persistent connections
|
# Persistent connections
|
||||||
if (request.protocol == HttpVer11 and
|
if (request.protocol == HttpVer11 and
|
||||||
|
|
@ -273,7 +276,7 @@ proc processRequest(server: AsyncHttpServer, req: FutureVar[Request],
|
||||||
discard
|
discard
|
||||||
else:
|
else:
|
||||||
request.client.close()
|
request.client.close()
|
||||||
return
|
return false
|
||||||
|
|
||||||
proc processClient(server: AsyncHttpServer, client: AsyncSocket, address: string,
|
proc processClient(server: AsyncHttpServer, client: AsyncSocket, address: string,
|
||||||
callback: proc (request: Request):
|
callback: proc (request: Request):
|
||||||
|
|
@ -285,7 +288,10 @@ proc processClient(server: AsyncHttpServer, client: AsyncSocket, address: string
|
||||||
lineFut.mget() = newStringOfCap(80)
|
lineFut.mget() = newStringOfCap(80)
|
||||||
|
|
||||||
while not client.isClosed:
|
while not client.isClosed:
|
||||||
await processRequest(server, request, client, address, lineFut, callback)
|
let retry = await processRequest(
|
||||||
|
server, request, client, address, lineFut, callback
|
||||||
|
)
|
||||||
|
if not retry: break
|
||||||
|
|
||||||
proc serve*(server: AsyncHttpServer, port: Port,
|
proc serve*(server: AsyncHttpServer, port: Port,
|
||||||
callback: proc (request: Request): Future[void] {.closure,gcsafe.},
|
callback: proc (request: Request): Future[void] {.closure,gcsafe.},
|
||||||
|
|
|
||||||
|
|
@ -9,37 +9,49 @@
|
||||||
|
|
||||||
## This module implements a base64 encoder and decoder.
|
## This module implements a base64 encoder and decoder.
|
||||||
##
|
##
|
||||||
|
## Base64 is an encoding and decoding technique used to convert binary
|
||||||
|
## data to an ASCII string format.
|
||||||
|
## Each Base64 digit represents exactly 6 bits of data. Three 8-bit
|
||||||
|
## bytes (i.e., a total of 24 bits) can therefore be represented by
|
||||||
|
## four 6-bit Base64 digits.
|
||||||
|
##
|
||||||
|
##
|
||||||
|
## Basic usage
|
||||||
|
## ===========
|
||||||
|
##
|
||||||
## Encoding data
|
## Encoding data
|
||||||
## -------------
|
## -------------
|
||||||
##
|
##
|
||||||
## In order to encode some text simply call the ``encode`` procedure:
|
|
||||||
##
|
|
||||||
## .. code-block::nim
|
## .. code-block::nim
|
||||||
## import base64
|
## import base64
|
||||||
## let encoded = encode("Hello World")
|
## let encoded = encode("Hello World")
|
||||||
## echo(encoded) # SGVsbG8gV29ybGQ=
|
## assert encoded == "SGVsbG8gV29ybGQ="
|
||||||
##
|
##
|
||||||
## Apart from strings you can also encode lists of integers or characters:
|
## Apart from strings you can also encode lists of integers or characters:
|
||||||
##
|
##
|
||||||
## .. code-block::nim
|
## .. code-block::nim
|
||||||
## import base64
|
## import base64
|
||||||
## let encodedInts = encode([1,2,3])
|
## let encodedInts = encode([1,2,3])
|
||||||
## echo(encodedInts) # AQID
|
## assert encodedInts == "AQID"
|
||||||
## let encodedChars = encode(['h','e','y'])
|
## let encodedChars = encode(['h','e','y'])
|
||||||
## echo(encodedChars) # aGV5
|
## assert encodedChars == "aGV5"
|
||||||
##
|
##
|
||||||
## The ``encode`` procedure takes an ``openarray`` so both arrays and sequences
|
|
||||||
## can be passed as parameters.
|
|
||||||
##
|
##
|
||||||
## Decoding data
|
## Decoding data
|
||||||
## -------------
|
## -------------
|
||||||
##
|
##
|
||||||
## To decode a base64 encoded data string simply call the ``decode``
|
|
||||||
## procedure:
|
|
||||||
##
|
|
||||||
## .. code-block::nim
|
## .. code-block::nim
|
||||||
## import base64
|
## import base64
|
||||||
## echo(decode("SGVsbG8gV29ybGQ=")) # Hello World
|
## let decoded = decode("SGVsbG8gV29ybGQ=")
|
||||||
|
## assert decoded == "Hello World"
|
||||||
|
##
|
||||||
|
##
|
||||||
|
## See also
|
||||||
|
## ========
|
||||||
|
##
|
||||||
|
## * `hashes module<hashes.html>`_ for efficient computations of hash values for diverse Nim types
|
||||||
|
## * `md5 module<md5.html>`_ implements the MD5 checksum algorithm
|
||||||
|
## * `sha1 module<sha1.html>`_ implements a sha1 encoder and decoder
|
||||||
|
|
||||||
const
|
const
|
||||||
cb64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
|
cb64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
|
||||||
|
|
@ -100,18 +112,33 @@ template encodeInternal(s: typed, lineLen: int, newLine: string): untyped =
|
||||||
discard
|
discard
|
||||||
|
|
||||||
proc encode*[T:SomeInteger|char](s: openarray[T], lineLen = 75, newLine="\13\10"): string =
|
proc encode*[T:SomeInteger|char](s: openarray[T], lineLen = 75, newLine="\13\10"): string =
|
||||||
## encodes `s` into base64 representation. After `lineLen` characters, a
|
## Encodes ``s`` into base64 representation. After ``lineLen`` characters, a
|
||||||
## `newline` is added.
|
## ``newline`` is added.
|
||||||
##
|
##
|
||||||
## This procedure encodes an openarray (array or sequence) of either integers
|
## This procedure encodes an openarray (array or sequence) of either integers
|
||||||
## or characters.
|
## or characters.
|
||||||
|
##
|
||||||
|
## **See also:**
|
||||||
|
## * `encode proc<#encode,string,int,string>`_ for encoding a string
|
||||||
|
## * `decode proc<#decode,string>`_ for decoding a string
|
||||||
|
runnableExamples:
|
||||||
|
assert encode(['n', 'i', 'm']) == "bmlt"
|
||||||
|
assert encode(@['n', 'i', 'm']) == "bmlt"
|
||||||
|
assert encode([1, 2, 3, 4, 5]) == "AQIDBAU="
|
||||||
encodeInternal(s, lineLen, newLine)
|
encodeInternal(s, lineLen, newLine)
|
||||||
|
|
||||||
proc encode*(s: string, lineLen = 75, newLine="\13\10"): string =
|
proc encode*(s: string, lineLen = 75, newLine="\13\10"): string =
|
||||||
## encodes `s` into base64 representation. After `lineLen` characters, a
|
## Encodes ``s`` into base64 representation. After ``lineLen`` characters, a
|
||||||
## `newline` is added.
|
## ``newline`` is added.
|
||||||
##
|
##
|
||||||
## This procedure encodes a string.
|
## This procedure encodes a string.
|
||||||
|
##
|
||||||
|
## **See also:**
|
||||||
|
## * `encode proc<#encode,openArray[T],int,string>`_ for encoding an openarray
|
||||||
|
## * `decode proc<#decode,string>`_ for decoding a string
|
||||||
|
runnableExamples:
|
||||||
|
assert encode("Hello World") == "SGVsbG8gV29ybGQ="
|
||||||
|
assert encode("Hello World", 3, "\n") == "SGVs\nbG8g\nV29ybGQ="
|
||||||
encodeInternal(s, lineLen, newLine)
|
encodeInternal(s, lineLen, newLine)
|
||||||
|
|
||||||
proc decodeByte(b: char): int {.inline.} =
|
proc decodeByte(b: char): int {.inline.} =
|
||||||
|
|
@ -123,8 +150,15 @@ proc decodeByte(b: char): int {.inline.} =
|
||||||
else: result = 63
|
else: result = 63
|
||||||
|
|
||||||
proc decode*(s: string): string =
|
proc decode*(s: string): string =
|
||||||
## decodes a string in base64 representation back into its original form.
|
## Decodes string ``s`` in base64 representation back into its original form.
|
||||||
## Whitespace is skipped.
|
## The initial whitespace is skipped.
|
||||||
|
##
|
||||||
|
## **See also:**
|
||||||
|
## * `encode proc<#encode,openArray[T],int,string>`_ for encoding an openarray
|
||||||
|
## * `encode proc<#encode,string,int,string>`_ for encoding a string
|
||||||
|
runnableExamples:
|
||||||
|
assert decode("SGVsbG8gV29ybGQ=") == "Hello World"
|
||||||
|
assert decode(" SGVsbG8gV29ybGQ=") == "Hello World"
|
||||||
const Whitespace = {' ', '\t', '\v', '\r', '\l', '\f'}
|
const Whitespace = {' ', '\t', '\v', '\r', '\l', '\f'}
|
||||||
var total = ((len(s) + 3) div 4) * 3
|
var total = ((len(s) + 3) div 4) * 3
|
||||||
# total is an upper bound, as we will skip arbitrary whitespace:
|
# total is an upper bound, as we will skip arbitrary whitespace:
|
||||||
|
|
|
||||||
|
|
@ -337,11 +337,6 @@ proc setStackTraceStdout*() =
|
||||||
## Makes Nim output stacktraces to stdout, instead of server log.
|
## Makes Nim output stacktraces to stdout, instead of server log.
|
||||||
errorMessageWriter = writeErrorMessage
|
errorMessageWriter = writeErrorMessage
|
||||||
|
|
||||||
proc setStackTraceNewLine*() {.deprecated.} =
|
|
||||||
## Makes Nim output stacktraces to stdout, instead of server log.
|
|
||||||
## Depracated alias for setStackTraceStdout.
|
|
||||||
setStackTraceStdout()
|
|
||||||
|
|
||||||
proc setCookie*(name, value: string) =
|
proc setCookie*(name, value: string) =
|
||||||
## Sets a cookie.
|
## Sets a cookie.
|
||||||
write(stdout, "Set-Cookie: ", name, "=", value, "\n")
|
write(stdout, "Set-Cookie: ", name, "=", value, "\n")
|
||||||
|
|
|
||||||
|
|
@ -202,12 +202,6 @@ proc `[]`*[T](c: var CritBitTree[T], key: string): var T {.inline,
|
||||||
## If `key` is not in `t`, the ``KeyError`` exception is raised.
|
## If `key` is not in `t`, the ``KeyError`` exception is raised.
|
||||||
get(c, key)
|
get(c, key)
|
||||||
|
|
||||||
proc mget*[T](c: var CritBitTree[T], key: string): var T {.inline, deprecated.} =
|
|
||||||
## retrieves the value at ``c[key]``. The value can be modified.
|
|
||||||
## If `key` is not in `t`, the ``KeyError`` exception is raised.
|
|
||||||
## Use ```[]``` instead.
|
|
||||||
get(c, key)
|
|
||||||
|
|
||||||
iterator leaves[T](n: Node[T]): Node[T] =
|
iterator leaves[T](n: Node[T]): Node[T] =
|
||||||
if n != nil:
|
if n != nil:
|
||||||
# XXX actually we could compute the necessary stack size in advance:
|
# XXX actually we could compute the necessary stack size in advance:
|
||||||
|
|
|
||||||
|
|
@ -20,41 +20,59 @@
|
||||||
## access, unless your program logic guarantees it indirectly.
|
## access, unless your program logic guarantees it indirectly.
|
||||||
##
|
##
|
||||||
## .. code-block:: Nim
|
## .. code-block:: Nim
|
||||||
## proc foo(a, b: Positive) = # assume random positive values for `a` and `b`
|
## import deques
|
||||||
## var deq = initDeque[int]() # initializes the object
|
|
||||||
## for i in 1 ..< a: deq.addLast i # populates the deque
|
|
||||||
##
|
##
|
||||||
## if b < deq.len: # checking before indexed access
|
## var a = initDeque[int]()
|
||||||
## echo "The element at index position ", b, " is ", deq[b]
|
|
||||||
##
|
##
|
||||||
## # The following two lines don't need any checking on access due to the
|
## doAssertRaises(IndexError, echo a[0])
|
||||||
## # logic of the program, but that would not be the case if `a` could be 0.
|
|
||||||
## assert deq.peekFirst == 1
|
|
||||||
## assert deq.peekLast == a
|
|
||||||
##
|
##
|
||||||
## while deq.len > 0: # checking if the deque is empty
|
## for i in 1 .. 5:
|
||||||
## echo deq.popLast()
|
## a.addLast(10*i)
|
||||||
|
## assert $a == "[10, 20, 30, 40, 50]"
|
||||||
##
|
##
|
||||||
## Note: For inter thread communication use
|
## assert a.peekFirst == 10
|
||||||
## a `Channel <channels.html>`_ instead.
|
## assert a.peekLast == 50
|
||||||
|
## assert len(a) == 5
|
||||||
|
##
|
||||||
|
## assert a.popFirst == 10
|
||||||
|
## assert a.popLast == 50
|
||||||
|
## assert len(a) == 3
|
||||||
|
##
|
||||||
|
## a.addFirst(11)
|
||||||
|
## a.addFirst(22)
|
||||||
|
## a.addFirst(33)
|
||||||
|
## assert $a == "[33, 22, 11, 20, 30, 40]"
|
||||||
|
##
|
||||||
|
## a.shrink(fromFirst = 1, fromLast = 2)
|
||||||
|
## assert $a == "[22, 11, 20]"
|
||||||
|
##
|
||||||
|
##
|
||||||
|
## **See also:**
|
||||||
|
## * `lists module <lists.html>`_ for singly and doubly linked lists and rings
|
||||||
|
## * `channels module <channels.html>`_ for inter-thread communication
|
||||||
|
|
||||||
|
|
||||||
import math, typetraits
|
import math, typetraits
|
||||||
|
|
||||||
type
|
type
|
||||||
Deque*[T] = object
|
Deque*[T] = object
|
||||||
## A double-ended queue backed with a ringed seq buffer.
|
## A double-ended queue backed with a ringed seq buffer.
|
||||||
|
##
|
||||||
|
## To initialize an empty deque use `initDeque proc <#initDeque,int>`_.
|
||||||
data: seq[T]
|
data: seq[T]
|
||||||
head, tail, count, mask: int
|
head, tail, count, mask: int
|
||||||
|
|
||||||
proc initDeque*[T](initialSize: int = 4): Deque[T] =
|
proc initDeque*[T](initialSize: int = 4): Deque[T] =
|
||||||
## Create a new deque.
|
## Create a new empty deque.
|
||||||
## Optionally, the initial capacity can be reserved via `initialSize` as a
|
|
||||||
## performance optimization. The length of a newly created deque will still
|
|
||||||
## be 0.
|
|
||||||
##
|
##
|
||||||
## `initialSize` needs to be a power of two. If you need to accept runtime
|
## Optionally, the initial capacity can be reserved via `initialSize`
|
||||||
## values for this you could use the ``nextPowerOfTwo`` proc from the
|
## as a performance optimization.
|
||||||
## `math <math.html>`_ module.
|
## The length of a newly created deque will still be 0.
|
||||||
|
##
|
||||||
|
## ``initialSize`` must be a power of two (default: 4).
|
||||||
|
## If you need to accept runtime values for this you could use the
|
||||||
|
## `nextPowerOfTwo proc<math.html#nextPowerOfTwo,int>`_ from the
|
||||||
|
## `math module<math.html>`_.
|
||||||
assert isPowerOfTwo(initialSize)
|
assert isPowerOfTwo(initialSize)
|
||||||
result.mask = initialSize-1
|
result.mask = initialSize-1
|
||||||
newSeq(result.data, initialSize)
|
newSeq(result.data, initialSize)
|
||||||
|
|
@ -75,33 +93,128 @@ template xBoundsCheck(deq, i) =
|
||||||
if unlikely(i >= deq.count): # x < deq.low is taken care by the Natural parameter
|
if unlikely(i >= deq.count): # x < deq.low is taken care by the Natural parameter
|
||||||
raise newException(IndexError,
|
raise newException(IndexError,
|
||||||
"Out of bounds: " & $i & " > " & $(deq.count - 1))
|
"Out of bounds: " & $i & " > " & $(deq.count - 1))
|
||||||
|
if unlikely(i < 0): # when used with BackwardsIndex
|
||||||
|
raise newException(IndexError,
|
||||||
|
"Out of bounds: " & $i & " < 0")
|
||||||
|
|
||||||
proc `[]`*[T](deq: Deque[T], i: Natural) : T {.inline.} =
|
proc `[]`*[T](deq: Deque[T], i: Natural) : T {.inline.} =
|
||||||
## Access the i-th element of `deq` by order from first to last.
|
## Access the i-th element of `deq`.
|
||||||
## deq[0] is the first, deq[^1] is the last.
|
runnableExamples:
|
||||||
|
var a = initDeque[int]()
|
||||||
|
for i in 1 .. 5:
|
||||||
|
a.addLast(10*i)
|
||||||
|
assert a[0] == 10
|
||||||
|
assert a[3] == 40
|
||||||
|
doAssertRaises(IndexError, echo a[8])
|
||||||
|
|
||||||
xBoundsCheck(deq, i)
|
xBoundsCheck(deq, i)
|
||||||
return deq.data[(deq.head + i) and deq.mask]
|
return deq.data[(deq.head + i) and deq.mask]
|
||||||
|
|
||||||
proc `[]`*[T](deq: var Deque[T], i: Natural): var T {.inline.} =
|
proc `[]`*[T](deq: var Deque[T], i: Natural): var T {.inline.} =
|
||||||
## Access the i-th element of `deq` and returns a mutable
|
## Access the i-th element of `deq` and return a mutable
|
||||||
## reference to it.
|
## reference to it.
|
||||||
|
runnableExamples:
|
||||||
|
var a = initDeque[int]()
|
||||||
|
for i in 1 .. 5:
|
||||||
|
a.addLast(10*i)
|
||||||
|
assert a[0] == 10
|
||||||
|
assert a[3] == 40
|
||||||
|
doAssertRaises(IndexError, echo a[8])
|
||||||
|
|
||||||
xBoundsCheck(deq, i)
|
xBoundsCheck(deq, i)
|
||||||
return deq.data[(deq.head + i) and deq.mask]
|
return deq.data[(deq.head + i) and deq.mask]
|
||||||
|
|
||||||
proc `[]=`*[T](deq: var Deque[T], i: Natural, val : T) {.inline.} =
|
proc `[]=`*[T](deq: var Deque[T], i: Natural, val : T) {.inline.} =
|
||||||
## Change the i-th element of `deq`.
|
## Change the i-th element of `deq`.
|
||||||
|
runnableExamples:
|
||||||
|
var a = initDeque[int]()
|
||||||
|
for i in 1 .. 5:
|
||||||
|
a.addLast(10*i)
|
||||||
|
a[0] = 99
|
||||||
|
a[3] = 66
|
||||||
|
assert $a == "[99, 20, 30, 66, 50]"
|
||||||
|
|
||||||
xBoundsCheck(deq, i)
|
xBoundsCheck(deq, i)
|
||||||
deq.data[(deq.head + i) and deq.mask] = val
|
deq.data[(deq.head + i) and deq.mask] = val
|
||||||
|
|
||||||
|
proc `[]`*[T](deq: Deque[T], i: BackwardsIndex): T {.inline.} =
|
||||||
|
## Access the backwards indexed i-th element.
|
||||||
|
##
|
||||||
|
## `deq[^1]` is the last element.
|
||||||
|
runnableExamples:
|
||||||
|
var a = initDeque[int]()
|
||||||
|
for i in 1 .. 5:
|
||||||
|
a.addLast(10*i)
|
||||||
|
assert a[^1] == 50
|
||||||
|
assert a[^4] == 20
|
||||||
|
doAssertRaises(IndexError, echo a[^9])
|
||||||
|
|
||||||
|
xBoundsCheck(deq, deq.len - int(i))
|
||||||
|
return deq[deq.len - int(i)]
|
||||||
|
|
||||||
|
proc `[]`*[T](deq: var Deque[T], i: BackwardsIndex): var T {.inline.} =
|
||||||
|
## Access the backwards indexed i-th element.
|
||||||
|
##
|
||||||
|
## `deq[^1]` is the last element.
|
||||||
|
runnableExamples:
|
||||||
|
var a = initDeque[int]()
|
||||||
|
for i in 1 .. 5:
|
||||||
|
a.addLast(10*i)
|
||||||
|
assert a[^1] == 50
|
||||||
|
assert a[^4] == 20
|
||||||
|
doAssertRaises(IndexError, echo a[^9])
|
||||||
|
|
||||||
|
xBoundsCheck(deq, deq.len - int(i))
|
||||||
|
return deq[deq.len - int(i)]
|
||||||
|
|
||||||
|
proc `[]=`*[T](deq: var Deque[T], i: BackwardsIndex, x: T) {.inline.} =
|
||||||
|
## Change the backwards indexed i-th element.
|
||||||
|
##
|
||||||
|
## `deq[^1]` is the last element.
|
||||||
|
runnableExamples:
|
||||||
|
var a = initDeque[int]()
|
||||||
|
for i in 1 .. 5:
|
||||||
|
a.addLast(10*i)
|
||||||
|
a[^1] = 99
|
||||||
|
a[^3] = 77
|
||||||
|
assert $a == "[10, 20, 77, 40, 99]"
|
||||||
|
|
||||||
|
xBoundsCheck(deq, deq.len - int(i))
|
||||||
|
deq[deq.len - int(i)] = x
|
||||||
|
|
||||||
iterator items*[T](deq: Deque[T]): T =
|
iterator items*[T](deq: Deque[T]): T =
|
||||||
## Yield every element of `deq`.
|
## Yield every element of `deq`.
|
||||||
|
##
|
||||||
|
## **Examples:**
|
||||||
|
##
|
||||||
|
## .. code-block::
|
||||||
|
## var a = initDeque[int]()
|
||||||
|
## for i in 1 .. 3:
|
||||||
|
## a.addLast(10*i)
|
||||||
|
##
|
||||||
|
## for x in a: # the same as: for x in items(a):
|
||||||
|
## echo x
|
||||||
|
##
|
||||||
|
## # 10
|
||||||
|
## # 20
|
||||||
|
## # 30
|
||||||
|
##
|
||||||
var i = deq.head
|
var i = deq.head
|
||||||
for c in 0 ..< deq.count:
|
for c in 0 ..< deq.count:
|
||||||
yield deq.data[i]
|
yield deq.data[i]
|
||||||
i = (i + 1) and deq.mask
|
i = (i + 1) and deq.mask
|
||||||
|
|
||||||
iterator mitems*[T](deq: var Deque[T]): var T =
|
iterator mitems*[T](deq: var Deque[T]): var T =
|
||||||
## Yield every element of `deq`.
|
## Yield every element of `deq`, which can be modified.
|
||||||
|
runnableExamples:
|
||||||
|
var a = initDeque[int]()
|
||||||
|
for i in 1 .. 5:
|
||||||
|
a.addLast(10*i)
|
||||||
|
assert $a == "[10, 20, 30, 40, 50]"
|
||||||
|
for x in mitems(a):
|
||||||
|
x = 5*x - 1
|
||||||
|
assert $a == "[49, 99, 149, 199, 249]"
|
||||||
|
|
||||||
var i = deq.head
|
var i = deq.head
|
||||||
for c in 0 ..< deq.count:
|
for c in 0 ..< deq.count:
|
||||||
yield deq.data[i]
|
yield deq.data[i]
|
||||||
|
|
@ -109,18 +222,35 @@ iterator mitems*[T](deq: var Deque[T]): var T =
|
||||||
|
|
||||||
iterator pairs*[T](deq: Deque[T]): tuple[key: int, val: T] =
|
iterator pairs*[T](deq: Deque[T]): tuple[key: int, val: T] =
|
||||||
## Yield every (position, value) of `deq`.
|
## Yield every (position, value) of `deq`.
|
||||||
|
##
|
||||||
|
## **Examples:**
|
||||||
|
##
|
||||||
|
## .. code-block::
|
||||||
|
## var a = initDeque[int]()
|
||||||
|
## for i in 1 .. 3:
|
||||||
|
## a.addLast(10*i)
|
||||||
|
##
|
||||||
|
## for k, v in pairs(a):
|
||||||
|
## echo "key: ", k, ", value: ", v
|
||||||
|
##
|
||||||
|
## # key: 0, value: 10
|
||||||
|
## # key: 1, value: 20
|
||||||
|
## # key: 2, value: 30
|
||||||
|
##
|
||||||
var i = deq.head
|
var i = deq.head
|
||||||
for c in 0 ..< deq.count:
|
for c in 0 ..< deq.count:
|
||||||
yield (c, deq.data[i])
|
yield (c, deq.data[i])
|
||||||
i = (i + 1) and deq.mask
|
i = (i + 1) and deq.mask
|
||||||
|
|
||||||
proc contains*[T](deq: Deque[T], item: T): bool {.inline.} =
|
proc contains*[T](deq: Deque[T], item: T): bool {.inline.} =
|
||||||
## Return true if `item` is in `deq` or false if not found. Usually used
|
## Return true if `item` is in `deq` or false if not found.
|
||||||
## via the ``in`` operator. It is the equivalent of ``deq.find(item) >= 0``.
|
##
|
||||||
|
## Usually used via the ``in`` operator.
|
||||||
|
## It is the equivalent of ``deq.find(item) >= 0``.
|
||||||
##
|
##
|
||||||
## .. code-block:: Nim
|
## .. code-block:: Nim
|
||||||
## if x in q:
|
## if x in q:
|
||||||
## assert q.contains x
|
## assert q.contains(x)
|
||||||
for e in deq:
|
for e in deq:
|
||||||
if e == item: return true
|
if e == item: return true
|
||||||
return false
|
return false
|
||||||
|
|
@ -138,6 +268,19 @@ proc expandIfNeeded[T](deq: var Deque[T]) =
|
||||||
|
|
||||||
proc addFirst*[T](deq: var Deque[T], item: T) =
|
proc addFirst*[T](deq: var Deque[T], item: T) =
|
||||||
## Add an `item` to the beginning of the `deq`.
|
## Add an `item` to the beginning of the `deq`.
|
||||||
|
##
|
||||||
|
## See also:
|
||||||
|
## * `addLast proc <#addLast,Deque[T],T>`_
|
||||||
|
## * `peekFirst proc <#peekFirst,Deque[T]>`_
|
||||||
|
## * `peekLast proc <#peekLast,Deque[T]>`_
|
||||||
|
## * `popFirst proc <#popFirst,Deque[T]>`_
|
||||||
|
## * `popLast proc <#popLast,Deque[T]>`_
|
||||||
|
runnableExamples:
|
||||||
|
var a = initDeque[int]()
|
||||||
|
for i in 1 .. 5:
|
||||||
|
a.addFirst(10*i)
|
||||||
|
assert $a == "[50, 40, 30, 20, 10]"
|
||||||
|
|
||||||
expandIfNeeded(deq)
|
expandIfNeeded(deq)
|
||||||
inc deq.count
|
inc deq.count
|
||||||
deq.head = (deq.head - 1) and deq.mask
|
deq.head = (deq.head - 1) and deq.mask
|
||||||
|
|
@ -145,6 +288,19 @@ proc addFirst*[T](deq: var Deque[T], item: T) =
|
||||||
|
|
||||||
proc addLast*[T](deq: var Deque[T], item: T) =
|
proc addLast*[T](deq: var Deque[T], item: T) =
|
||||||
## Add an `item` to the end of the `deq`.
|
## Add an `item` to the end of the `deq`.
|
||||||
|
##
|
||||||
|
## See also:
|
||||||
|
## * `addFirst proc <#addFirst,Deque[T],T>`_
|
||||||
|
## * `peekFirst proc <#peekFirst,Deque[T]>`_
|
||||||
|
## * `peekLast proc <#peekLast,Deque[T]>`_
|
||||||
|
## * `popFirst proc <#popFirst,Deque[T]>`_
|
||||||
|
## * `popLast proc <#popLast,Deque[T]>`_
|
||||||
|
runnableExamples:
|
||||||
|
var a = initDeque[int]()
|
||||||
|
for i in 1 .. 5:
|
||||||
|
a.addLast(10*i)
|
||||||
|
assert $a == "[10, 20, 30, 40, 50]"
|
||||||
|
|
||||||
expandIfNeeded(deq)
|
expandIfNeeded(deq)
|
||||||
inc deq.count
|
inc deq.count
|
||||||
deq.data[deq.tail] = item
|
deq.data[deq.tail] = item
|
||||||
|
|
@ -152,11 +308,41 @@ proc addLast*[T](deq: var Deque[T], item: T) =
|
||||||
|
|
||||||
proc peekFirst*[T](deq: Deque[T]): T {.inline.}=
|
proc peekFirst*[T](deq: Deque[T]): T {.inline.}=
|
||||||
## Returns the first element of `deq`, but does not remove it from the deque.
|
## Returns the first element of `deq`, but does not remove it from the deque.
|
||||||
|
##
|
||||||
|
## See also:
|
||||||
|
## * `addFirst proc <#addFirst,Deque[T],T>`_
|
||||||
|
## * `addLast proc <#addLast,Deque[T],T>`_
|
||||||
|
## * `peekLast proc <#peekLast,Deque[T]>`_
|
||||||
|
## * `popFirst proc <#popFirst,Deque[T]>`_
|
||||||
|
## * `popLast proc <#popLast,Deque[T]>`_
|
||||||
|
runnableExamples:
|
||||||
|
var a = initDeque[int]()
|
||||||
|
for i in 1 .. 5:
|
||||||
|
a.addLast(10*i)
|
||||||
|
assert $a == "[10, 20, 30, 40, 50]"
|
||||||
|
assert a.peekFirst == 10
|
||||||
|
assert len(a) == 5
|
||||||
|
|
||||||
emptyCheck(deq)
|
emptyCheck(deq)
|
||||||
result = deq.data[deq.head]
|
result = deq.data[deq.head]
|
||||||
|
|
||||||
proc peekLast*[T](deq: Deque[T]): T {.inline.} =
|
proc peekLast*[T](deq: Deque[T]): T {.inline.} =
|
||||||
## Returns the last element of `deq`, but does not remove it from the deque.
|
## Returns the last element of `deq`, but does not remove it from the deque.
|
||||||
|
##
|
||||||
|
## See also:
|
||||||
|
## * `addFirst proc <#addFirst,Deque[T],T>`_
|
||||||
|
## * `addLast proc <#addLast,Deque[T],T>`_
|
||||||
|
## * `peekFirst proc <#peekFirst,Deque[T]>`_
|
||||||
|
## * `popFirst proc <#popFirst,Deque[T]>`_
|
||||||
|
## * `popLast proc <#popLast,Deque[T]>`_
|
||||||
|
runnableExamples:
|
||||||
|
var a = initDeque[int]()
|
||||||
|
for i in 1 .. 5:
|
||||||
|
a.addLast(10*i)
|
||||||
|
assert $a == "[10, 20, 30, 40, 50]"
|
||||||
|
assert a.peekLast == 50
|
||||||
|
assert len(a) == 5
|
||||||
|
|
||||||
emptyCheck(deq)
|
emptyCheck(deq)
|
||||||
result = deq.data[(deq.tail - 1) and deq.mask]
|
result = deq.data[(deq.tail - 1) and deq.mask]
|
||||||
|
|
||||||
|
|
@ -165,6 +351,23 @@ template destroy(x: untyped) =
|
||||||
|
|
||||||
proc popFirst*[T](deq: var Deque[T]): T {.inline, discardable.} =
|
proc popFirst*[T](deq: var Deque[T]): T {.inline, discardable.} =
|
||||||
## Remove and returns the first element of the `deq`.
|
## Remove and returns the first element of the `deq`.
|
||||||
|
##
|
||||||
|
## See also:
|
||||||
|
## * `addFirst proc <#addFirst,Deque[T],T>`_
|
||||||
|
## * `addLast proc <#addLast,Deque[T],T>`_
|
||||||
|
## * `peekFirst proc <#peekFirst,Deque[T]>`_
|
||||||
|
## * `peekLast proc <#peekLast,Deque[T]>`_
|
||||||
|
## * `popLast proc <#popLast,Deque[T]>`_
|
||||||
|
## * `clear proc <#clear,Deque[T]>`_
|
||||||
|
## * `shrink proc <#shrink,Deque[T],int,int>`_
|
||||||
|
runnableExamples:
|
||||||
|
var a = initDeque[int]()
|
||||||
|
for i in 1 .. 5:
|
||||||
|
a.addLast(10*i)
|
||||||
|
assert $a == "[10, 20, 30, 40, 50]"
|
||||||
|
assert a.popFirst == 10
|
||||||
|
assert $a == "[20, 30, 40, 50]"
|
||||||
|
|
||||||
emptyCheck(deq)
|
emptyCheck(deq)
|
||||||
dec deq.count
|
dec deq.count
|
||||||
result = deq.data[deq.head]
|
result = deq.data[deq.head]
|
||||||
|
|
@ -173,6 +376,23 @@ proc popFirst*[T](deq: var Deque[T]): T {.inline, discardable.} =
|
||||||
|
|
||||||
proc popLast*[T](deq: var Deque[T]): T {.inline, discardable.} =
|
proc popLast*[T](deq: var Deque[T]): T {.inline, discardable.} =
|
||||||
## Remove and returns the last element of the `deq`.
|
## Remove and returns the last element of the `deq`.
|
||||||
|
##
|
||||||
|
## See also:
|
||||||
|
## * `addFirst proc <#addFirst,Deque[T],T>`_
|
||||||
|
## * `addLast proc <#addLast,Deque[T],T>`_
|
||||||
|
## * `peekFirst proc <#peekFirst,Deque[T]>`_
|
||||||
|
## * `peekLast proc <#peekLast,Deque[T]>`_
|
||||||
|
## * `popFirst proc <#popFirst,Deque[T]>`_
|
||||||
|
## * `clear proc <#clear,Deque[T]>`_
|
||||||
|
## * `shrink proc <#shrink,Deque[T],int,int>`_
|
||||||
|
runnableExamples:
|
||||||
|
var a = initDeque[int]()
|
||||||
|
for i in 1 .. 5:
|
||||||
|
a.addLast(10*i)
|
||||||
|
assert $a == "[10, 20, 30, 40, 50]"
|
||||||
|
assert a.popLast == 50
|
||||||
|
assert $a == "[10, 20, 30, 40]"
|
||||||
|
|
||||||
emptyCheck(deq)
|
emptyCheck(deq)
|
||||||
dec deq.count
|
dec deq.count
|
||||||
deq.tail = (deq.tail - 1) and deq.mask
|
deq.tail = (deq.tail - 1) and deq.mask
|
||||||
|
|
@ -181,17 +401,39 @@ proc popLast*[T](deq: var Deque[T]): T {.inline, discardable.} =
|
||||||
|
|
||||||
proc clear*[T](deq: var Deque[T]) {.inline.} =
|
proc clear*[T](deq: var Deque[T]) {.inline.} =
|
||||||
## Resets the deque so that it is empty.
|
## Resets the deque so that it is empty.
|
||||||
|
##
|
||||||
|
## See also:
|
||||||
|
## * `clear proc <#clear,Deque[T]>`_
|
||||||
|
## * `shrink proc <#shrink,Deque[T],int,int>`_
|
||||||
|
runnableExamples:
|
||||||
|
var a = initDeque[int]()
|
||||||
|
for i in 1 .. 5:
|
||||||
|
a.addFirst(10*i)
|
||||||
|
assert $a == "[50, 40, 30, 20, 10]"
|
||||||
|
clear(a)
|
||||||
|
assert len(a) == 0
|
||||||
|
|
||||||
for el in mitems(deq): destroy(el)
|
for el in mitems(deq): destroy(el)
|
||||||
deq.count = 0
|
deq.count = 0
|
||||||
deq.tail = deq.head
|
deq.tail = deq.head
|
||||||
|
|
||||||
proc shrink*[T](deq: var Deque[T], fromFirst = 0, fromLast = 0) =
|
proc shrink*[T](deq: var Deque[T], fromFirst = 0, fromLast = 0) =
|
||||||
## Remove `fromFirst` elements from the front of the deque and
|
## Remove `fromFirst` elements from the front of the deque and
|
||||||
## `fromLast` elements from the back. If the supplied number of
|
## `fromLast` elements from the back.
|
||||||
## elements exceeds the total number of elements in the deque,
|
|
||||||
## the deque will remain empty.
|
|
||||||
##
|
##
|
||||||
## Any user defined destructors
|
## If the supplied number of elements exceeds the total number of elements
|
||||||
|
## in the deque, the deque will remain empty.
|
||||||
|
##
|
||||||
|
## See also:
|
||||||
|
## * `clear proc <#clear,Deque[T]>`_
|
||||||
|
runnableExamples:
|
||||||
|
var a = initDeque[int]()
|
||||||
|
for i in 1 .. 5:
|
||||||
|
a.addFirst(10*i)
|
||||||
|
assert $a == "[50, 40, 30, 20, 10]"
|
||||||
|
a.shrink(fromFirst = 2, fromLast = 1)
|
||||||
|
assert $a == "[30, 20]"
|
||||||
|
|
||||||
if fromFirst + fromLast > deq.count:
|
if fromFirst + fromLast > deq.count:
|
||||||
clear(deq)
|
clear(deq)
|
||||||
return
|
return
|
||||||
|
|
@ -214,6 +456,8 @@ proc `$`*[T](deq: Deque[T]): string =
|
||||||
result.addQuoted(x)
|
result.addQuoted(x)
|
||||||
result.add("]")
|
result.add("]")
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
when isMainModule:
|
when isMainModule:
|
||||||
var deq = initDeque[int](1)
|
var deq = initDeque[int](1)
|
||||||
deq.addLast(4)
|
deq.addLast(4)
|
||||||
|
|
|
||||||
|
|
@ -9,9 +9,12 @@
|
||||||
|
|
||||||
## The ``intsets`` module implements an efficient int set implemented as a
|
## The ``intsets`` module implements an efficient int set implemented as a
|
||||||
## `sparse bit set`:idx:.
|
## `sparse bit set`:idx:.
|
||||||
## **Note**: Since Nim currently does not allow the assignment operator to
|
|
||||||
## be overloaded, ``=`` for int sets performs some rather meaningless shallow
|
## **Note**: Currently the assignment operator ``=`` for ``intsets``
|
||||||
## copy; use ``assign`` to get a deep copy.
|
## performs some rather meaningless shallow copy. Since Nim currently does
|
||||||
|
## not allow the assignment operator to be overloaded, use ``assign`` to
|
||||||
|
## get a deep copy.
|
||||||
|
|
||||||
|
|
||||||
import
|
import
|
||||||
hashes, math
|
hashes, math
|
||||||
|
|
@ -94,7 +97,7 @@ proc intSetPut(t: var IntSet, key: int): PTrunk =
|
||||||
t.data[h] = result
|
t.data[h] = result
|
||||||
|
|
||||||
proc contains*(s: IntSet, key: int): bool =
|
proc contains*(s: IntSet, key: int): bool =
|
||||||
## returns true iff `key` is in `s`.
|
## Returns true iff `key` is in `s`.
|
||||||
if s.elems <= s.a.len:
|
if s.elems <= s.a.len:
|
||||||
for i in 0..<s.elems:
|
for i in 0..<s.elems:
|
||||||
if s.a[i] == key: return true
|
if s.a[i] == key: return true
|
||||||
|
|
@ -107,7 +110,7 @@ proc contains*(s: IntSet, key: int): bool =
|
||||||
result = false
|
result = false
|
||||||
|
|
||||||
iterator items*(s: IntSet): int {.inline.} =
|
iterator items*(s: IntSet): int {.inline.} =
|
||||||
## iterates over any included element of `s`.
|
## Iterates over any included element of `s`.
|
||||||
if s.elems <= s.a.len:
|
if s.elems <= s.a.len:
|
||||||
for i in 0..<s.elems:
|
for i in 0..<s.elems:
|
||||||
yield s.a[i]
|
yield s.a[i]
|
||||||
|
|
@ -135,7 +138,7 @@ proc bitincl(s: var IntSet, key: int) {.inline.} =
|
||||||
`shl`(1, u and IntMask)
|
`shl`(1, u and IntMask)
|
||||||
|
|
||||||
proc incl*(s: var IntSet, key: int) =
|
proc incl*(s: var IntSet, key: int) =
|
||||||
## includes an element `key` in `s`.
|
## Includes an element `key` in `s`.
|
||||||
if s.elems <= s.a.len:
|
if s.elems <= s.a.len:
|
||||||
for i in 0..<s.elems:
|
for i in 0..<s.elems:
|
||||||
if s.a[i] == key: return
|
if s.a[i] == key: return
|
||||||
|
|
@ -170,7 +173,7 @@ proc exclImpl(s: var IntSet, key: int) =
|
||||||
not `shl`(1, u and IntMask)
|
not `shl`(1, u and IntMask)
|
||||||
|
|
||||||
proc excl*(s: var IntSet, key: int) =
|
proc excl*(s: var IntSet, key: int) =
|
||||||
## excludes `key` from the set `s`.
|
## Excludes `key` from the set `s`.
|
||||||
exclImpl(s, key)
|
exclImpl(s, key)
|
||||||
|
|
||||||
proc excl*(s: var IntSet, other: IntSet) =
|
proc excl*(s: var IntSet, other: IntSet) =
|
||||||
|
|
@ -178,14 +181,14 @@ proc excl*(s: var IntSet, other: IntSet) =
|
||||||
for item in other: excl(s, item)
|
for item in other: excl(s, item)
|
||||||
|
|
||||||
proc missingOrExcl*(s: var IntSet, key: int) : bool =
|
proc missingOrExcl*(s: var IntSet, key: int) : bool =
|
||||||
## returns true if `s` does not contain `key`, otherwise
|
## Returns true if `s` does not contain `key`, otherwise
|
||||||
## `key` is removed from `s` and false is returned.
|
## `key` is removed from `s` and false is returned.
|
||||||
var count = s.elems
|
var count = s.elems
|
||||||
exclImpl(s, key)
|
exclImpl(s, key)
|
||||||
result = count == s.elems
|
result = count == s.elems
|
||||||
|
|
||||||
proc containsOrIncl*(s: var IntSet, key: int): bool =
|
proc containsOrIncl*(s: var IntSet, key: int): bool =
|
||||||
## returns true if `s` contains `key`, otherwise `key` is included in `s`
|
## Returns true if `s` contains `key`, otherwise `key` is included in `s`
|
||||||
## and false is returned.
|
## and false is returned.
|
||||||
if s.elems <= s.a.len:
|
if s.elems <= s.a.len:
|
||||||
for i in 0..<s.elems:
|
for i in 0..<s.elems:
|
||||||
|
|
@ -206,20 +209,25 @@ proc containsOrIncl*(s: var IntSet, key: int): bool =
|
||||||
result = false
|
result = false
|
||||||
|
|
||||||
proc initIntSet*: IntSet =
|
proc initIntSet*: IntSet =
|
||||||
## creates a new int set that is empty.
|
## Returns an empty IntSet. Example:
|
||||||
|
##
|
||||||
|
## .. code-block ::
|
||||||
|
## var a = initIntSet()
|
||||||
|
## a.incl(2)
|
||||||
|
|
||||||
# newSeq(result.data, InitIntSetSize)
|
# newSeq(result.data, InitIntSetSize)
|
||||||
# result.max = InitIntSetSize-1
|
# result.max = InitIntSetSize-1
|
||||||
when defined(nimNoNilSeqs):
|
result = IntSet(
|
||||||
result.data = @[]
|
elems: 0,
|
||||||
else:
|
counter: 0,
|
||||||
result.data = nil
|
max: 0,
|
||||||
result.max = 0
|
head: nil,
|
||||||
result.counter = 0
|
data: when defined(nimNoNilSeqs): @[] else: nil)
|
||||||
result.head = nil
|
# a: array[0..33, int] # profiling shows that 34 elements are enough
|
||||||
result.elems = 0
|
|
||||||
|
|
||||||
proc clear*(result: var IntSet) =
|
proc clear*(result: var IntSet) =
|
||||||
|
## Clears the IntSet back to an empty state.
|
||||||
|
|
||||||
# setLen(result.data, InitIntSetSize)
|
# setLen(result.data, InitIntSetSize)
|
||||||
# for i in 0..InitIntSetSize-1: result.data[i] = nil
|
# for i in 0..InitIntSetSize-1: result.data[i] = nil
|
||||||
# result.max = InitIntSetSize-1
|
# result.max = InitIntSetSize-1
|
||||||
|
|
@ -304,7 +312,7 @@ proc `-`*(s1, s2: IntSet): IntSet {.inline.} =
|
||||||
result = difference(s1, s2)
|
result = difference(s1, s2)
|
||||||
|
|
||||||
proc disjoint*(s1, s2: IntSet): bool =
|
proc disjoint*(s1, s2: IntSet): bool =
|
||||||
## Returns true iff the sets `s1` and `s2` have no items in common.
|
## Returns true if the sets `s1` and `s2` have no items in common.
|
||||||
for item in s1:
|
for item in s1:
|
||||||
if contains(s2, item):
|
if contains(s2, item):
|
||||||
return false
|
return false
|
||||||
|
|
@ -320,7 +328,7 @@ proc len*(s: IntSet): int {.inline.} =
|
||||||
inc(result)
|
inc(result)
|
||||||
|
|
||||||
proc card*(s: IntSet): int {.inline.} =
|
proc card*(s: IntSet): int {.inline.} =
|
||||||
## alias for `len() <#len>` _.
|
## Alias for `len() <#len>` _.
|
||||||
result = s.len()
|
result = s.len()
|
||||||
|
|
||||||
proc `<=`*(s1, s2: IntSet): bool =
|
proc `<=`*(s1, s2: IntSet): bool =
|
||||||
|
|
@ -349,12 +357,6 @@ proc `$`*(s: IntSet): string =
|
||||||
## The `$` operator for int sets.
|
## The `$` operator for int sets.
|
||||||
dollarImpl()
|
dollarImpl()
|
||||||
|
|
||||||
proc empty*(s: IntSet): bool {.inline, deprecated.} =
|
|
||||||
## returns true if `s` is empty. This is safe to call even before
|
|
||||||
## the set has been initialized with `initIntSet`. Note this never
|
|
||||||
## worked reliably and so is deprecated.
|
|
||||||
result = s.counter == 0
|
|
||||||
|
|
||||||
when isMainModule:
|
when isMainModule:
|
||||||
import sequtils, algorithm
|
import sequtils, algorithm
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -7,34 +7,112 @@
|
||||||
# distribution, for details about the copyright.
|
# distribution, for details about the copyright.
|
||||||
#
|
#
|
||||||
|
|
||||||
## Implementation of singly and doubly linked lists. Because it makes no sense
|
## Implementation of:
|
||||||
## to do so, the 'next' and 'prev' pointers are not hidden from you and can
|
## * `singly linked lists <#SinglyLinkedList>`_
|
||||||
## be manipulated directly for efficiency.
|
## * `doubly linked lists <#DoublyLinkedList>`_
|
||||||
|
## * `singly linked rings <#SinglyLinkedRing>`_ (circular lists)
|
||||||
|
## * `doubly linked rings <#DoublyLinkedRing>`_ (circular lists)
|
||||||
|
##
|
||||||
|
##
|
||||||
|
## Basic Usage
|
||||||
|
## ===========
|
||||||
|
##
|
||||||
|
## Because it makes no sense to do otherwise, the `next` and `prev` pointers
|
||||||
|
## are not hidden from you and can be manipulated directly for efficiency.
|
||||||
|
##
|
||||||
|
## Lists
|
||||||
|
## -----
|
||||||
|
##
|
||||||
|
## .. code-block::
|
||||||
|
## import lists
|
||||||
|
##
|
||||||
|
## var
|
||||||
|
## l = initDoublyLinkedList[int]()
|
||||||
|
## a = newDoublyLinkedNode[int](3)
|
||||||
|
## b = newDoublyLinkedNode[int](7)
|
||||||
|
## c = newDoublyLinkedNode[int](9)
|
||||||
|
##
|
||||||
|
## l.append(a)
|
||||||
|
## l.append(b)
|
||||||
|
## l.prepend(c)
|
||||||
|
##
|
||||||
|
## assert a.next == b
|
||||||
|
## assert a.prev == c
|
||||||
|
## assert c.next == a
|
||||||
|
## assert c.next.next == b
|
||||||
|
## assert c.prev == nil
|
||||||
|
## assert b.next == nil
|
||||||
|
##
|
||||||
|
##
|
||||||
|
## Rings
|
||||||
|
## -----
|
||||||
|
##
|
||||||
|
## .. code-block::
|
||||||
|
## import lists
|
||||||
|
##
|
||||||
|
## var
|
||||||
|
## l = initSinglyLinkedRing[int]()
|
||||||
|
## a = newSinglyLinkedNode[int](3)
|
||||||
|
## b = newSinglyLinkedNode[int](7)
|
||||||
|
## c = newSinglyLinkedNode[int](9)
|
||||||
|
##
|
||||||
|
## l.append(a)
|
||||||
|
## l.append(b)
|
||||||
|
## l.prepend(c)
|
||||||
|
##
|
||||||
|
## assert c.next == a
|
||||||
|
## assert a.next == b
|
||||||
|
## assert c.next.next == b
|
||||||
|
## assert b.next == c
|
||||||
|
## assert c.next.next.next == c
|
||||||
|
##
|
||||||
|
## See also
|
||||||
|
## ========
|
||||||
|
##
|
||||||
|
## * `deques module <#deques.html>`_ for double-ended queues
|
||||||
|
## * `sharedlist module <#sharedlist.html>`_ for shared singly-linked lists
|
||||||
|
|
||||||
|
|
||||||
when not defined(nimhygiene):
|
when not defined(nimhygiene):
|
||||||
{.pragma: dirty.}
|
{.pragma: dirty.}
|
||||||
|
|
||||||
type
|
type
|
||||||
DoublyLinkedNodeObj*[T] = object ## a node a doubly linked list consists of
|
DoublyLinkedNodeObj*[T] = object ## A node a doubly linked list consists of.
|
||||||
|
##
|
||||||
|
## It consists of a `value` field, and pointers to `next` and `prev`.
|
||||||
next*, prev*: ref DoublyLinkedNodeObj[T]
|
next*, prev*: ref DoublyLinkedNodeObj[T]
|
||||||
value*: T
|
value*: T
|
||||||
DoublyLinkedNode*[T] = ref DoublyLinkedNodeObj[T]
|
DoublyLinkedNode*[T] = ref DoublyLinkedNodeObj[T]
|
||||||
|
|
||||||
SinglyLinkedNodeObj*[T] = object ## a node a singly linked list consists of
|
SinglyLinkedNodeObj*[T] = object ## A node a singly linked list consists of.
|
||||||
|
##
|
||||||
|
## It consists of a `value` field, and a pointer to `next`.
|
||||||
next*: ref SinglyLinkedNodeObj[T]
|
next*: ref SinglyLinkedNodeObj[T]
|
||||||
value*: T
|
value*: T
|
||||||
SinglyLinkedNode*[T] = ref SinglyLinkedNodeObj[T]
|
SinglyLinkedNode*[T] = ref SinglyLinkedNodeObj[T]
|
||||||
|
|
||||||
SinglyLinkedList*[T] = object ## a singly linked list
|
SinglyLinkedList*[T] = object ## A singly linked list.
|
||||||
|
##
|
||||||
|
## Use `initSinglyLinkedList proc <#initSinglyLinkedList,>`_ to create
|
||||||
|
## a new empty list.
|
||||||
head*, tail*: SinglyLinkedNode[T]
|
head*, tail*: SinglyLinkedNode[T]
|
||||||
|
|
||||||
DoublyLinkedList*[T] = object ## a doubly linked list
|
DoublyLinkedList*[T] = object ## A doubly linked list.
|
||||||
|
##
|
||||||
|
## Use `initDoublyLinkedList proc <#initDoublyLinkedList,>`_ to create
|
||||||
|
## a new empty list.
|
||||||
head*, tail*: DoublyLinkedNode[T]
|
head*, tail*: DoublyLinkedNode[T]
|
||||||
|
|
||||||
SinglyLinkedRing*[T] = object ## a singly linked ring
|
SinglyLinkedRing*[T] = object ## A singly linked ring.
|
||||||
|
##
|
||||||
|
## Use `initSinglyLinkedRing proc <#initSinglyLinkedRing,>`_ to create
|
||||||
|
## a new empty ring.
|
||||||
head*, tail*: SinglyLinkedNode[T]
|
head*, tail*: SinglyLinkedNode[T]
|
||||||
|
|
||||||
DoublyLinkedRing*[T] = object ## a doubly linked ring
|
DoublyLinkedRing*[T] = object ## A doubly linked ring.
|
||||||
|
##
|
||||||
|
## Use `initDoublyLinkedRing proc <#initDoublyLinkedRing,>`_ to create
|
||||||
|
## a new empty ring.
|
||||||
head*: DoublyLinkedNode[T]
|
head*: DoublyLinkedNode[T]
|
||||||
|
|
||||||
SomeLinkedList*[T] = SinglyLinkedList[T] | DoublyLinkedList[T]
|
SomeLinkedList*[T] = SinglyLinkedList[T] | DoublyLinkedList[T]
|
||||||
|
|
@ -46,28 +124,44 @@ type
|
||||||
SomeLinkedNode*[T] = SinglyLinkedNode[T] | DoublyLinkedNode[T]
|
SomeLinkedNode*[T] = SinglyLinkedNode[T] | DoublyLinkedNode[T]
|
||||||
|
|
||||||
proc initSinglyLinkedList*[T](): SinglyLinkedList[T] =
|
proc initSinglyLinkedList*[T](): SinglyLinkedList[T] =
|
||||||
## creates a new singly linked list that is empty.
|
## Creates a new singly linked list that is empty.
|
||||||
|
runnableExamples:
|
||||||
|
var a = initSinglyLinkedList[int]()
|
||||||
discard
|
discard
|
||||||
|
|
||||||
proc initDoublyLinkedList*[T](): DoublyLinkedList[T] =
|
proc initDoublyLinkedList*[T](): DoublyLinkedList[T] =
|
||||||
## creates a new doubly linked list that is empty.
|
## Creates a new doubly linked list that is empty.
|
||||||
|
runnableExamples:
|
||||||
|
var a = initDoublyLinkedList[int]()
|
||||||
discard
|
discard
|
||||||
|
|
||||||
proc initSinglyLinkedRing*[T](): SinglyLinkedRing[T] =
|
proc initSinglyLinkedRing*[T](): SinglyLinkedRing[T] =
|
||||||
## creates a new singly linked ring that is empty.
|
## Creates a new singly linked ring that is empty.
|
||||||
|
runnableExamples:
|
||||||
|
var a = initSinglyLinkedRing[int]()
|
||||||
discard
|
discard
|
||||||
|
|
||||||
proc initDoublyLinkedRing*[T](): DoublyLinkedRing[T] =
|
proc initDoublyLinkedRing*[T](): DoublyLinkedRing[T] =
|
||||||
## creates a new doubly linked ring that is empty.
|
## Creates a new doubly linked ring that is empty.
|
||||||
|
runnableExamples:
|
||||||
|
var a = initDoublyLinkedRing[int]()
|
||||||
discard
|
discard
|
||||||
|
|
||||||
proc newDoublyLinkedNode*[T](value: T): DoublyLinkedNode[T] =
|
proc newDoublyLinkedNode*[T](value: T): DoublyLinkedNode[T] =
|
||||||
## creates a new doubly linked node with the given `value`.
|
## Creates a new doubly linked node with the given `value`.
|
||||||
|
runnableExamples:
|
||||||
|
var n = newDoublyLinkedNode[int](5)
|
||||||
|
assert n.value == 5
|
||||||
|
|
||||||
new(result)
|
new(result)
|
||||||
result.value = value
|
result.value = value
|
||||||
|
|
||||||
proc newSinglyLinkedNode*[T](value: T): SinglyLinkedNode[T] =
|
proc newSinglyLinkedNode*[T](value: T): SinglyLinkedNode[T] =
|
||||||
## creates a new singly linked node with the given `value`.
|
## Creates a new singly linked node with the given `value`.
|
||||||
|
runnableExamples:
|
||||||
|
var n = newSinglyLinkedNode[int](5)
|
||||||
|
assert n.value == 5
|
||||||
|
|
||||||
new(result)
|
new(result)
|
||||||
result.value = value
|
result.value = value
|
||||||
|
|
||||||
|
|
@ -86,24 +180,100 @@ template itemsRingImpl() {.dirty.} =
|
||||||
if it == L.head: break
|
if it == L.head: break
|
||||||
|
|
||||||
iterator items*[T](L: SomeLinkedList[T]): T =
|
iterator items*[T](L: SomeLinkedList[T]): T =
|
||||||
## yields every value of `L`.
|
## Yields every value of `L`.
|
||||||
|
##
|
||||||
|
## See also:
|
||||||
|
## * `mitems iterator <#mitems.i,SomeLinkedList[T]>`_
|
||||||
|
## * `nodes iterator <#nodes.i,SomeLinkedList[T]>`_
|
||||||
|
##
|
||||||
|
## **Examples:**
|
||||||
|
##
|
||||||
|
## .. code-block::
|
||||||
|
## var a = initSinglyLinkedList[int]()
|
||||||
|
## for i in 1 .. 3:
|
||||||
|
## a.append(10*i)
|
||||||
|
##
|
||||||
|
## for x in a: # the same as: for x in items(a):
|
||||||
|
## echo x
|
||||||
|
##
|
||||||
|
## # 10
|
||||||
|
## # 20
|
||||||
|
## # 30
|
||||||
itemsListImpl()
|
itemsListImpl()
|
||||||
|
|
||||||
iterator items*[T](L: SomeLinkedRing[T]): T =
|
iterator items*[T](L: SomeLinkedRing[T]): T =
|
||||||
## yields every value of `L`.
|
## Yields every value of `L`.
|
||||||
|
##
|
||||||
|
## See also:
|
||||||
|
## * `mitems iterator <#mitems.i,SomeLinkedRing[T]>`_
|
||||||
|
## * `nodes iterator <#nodes.i,SomeLinkedRing[T]>`_
|
||||||
|
##
|
||||||
|
## **Examples:**
|
||||||
|
##
|
||||||
|
## .. code-block::
|
||||||
|
## var a = initSinglyLinkedRing[int]()
|
||||||
|
## for i in 1 .. 3:
|
||||||
|
## a.append(10*i)
|
||||||
|
##
|
||||||
|
## for x in a: # the same as: for x in items(a):
|
||||||
|
## echo x
|
||||||
|
##
|
||||||
|
## # 10
|
||||||
|
## # 20
|
||||||
|
## # 30
|
||||||
itemsRingImpl()
|
itemsRingImpl()
|
||||||
|
|
||||||
iterator mitems*[T](L: var SomeLinkedList[T]): var T =
|
iterator mitems*[T](L: var SomeLinkedList[T]): var T =
|
||||||
## yields every value of `L` so that you can modify it.
|
## Yields every value of `L` so that you can modify it.
|
||||||
|
##
|
||||||
|
## See also:
|
||||||
|
## * `items iterator <#items.i,SomeLinkedList[T]>`_
|
||||||
|
## * `nodes iterator <#nodes.i,SomeLinkedList[T]>`_
|
||||||
|
runnableExamples:
|
||||||
|
var a = initSinglyLinkedList[int]()
|
||||||
|
for i in 1 .. 5:
|
||||||
|
a.append(10*i)
|
||||||
|
assert $a == "[10, 20, 30, 40, 50]"
|
||||||
|
for x in mitems(a):
|
||||||
|
x = 5*x - 1
|
||||||
|
assert $a == "[49, 99, 149, 199, 249]"
|
||||||
itemsListImpl()
|
itemsListImpl()
|
||||||
|
|
||||||
iterator mitems*[T](L: var SomeLinkedRing[T]): var T =
|
iterator mitems*[T](L: var SomeLinkedRing[T]): var T =
|
||||||
## yields every value of `L` so that you can modify it.
|
## Yields every value of `L` so that you can modify it.
|
||||||
|
##
|
||||||
|
## See also:
|
||||||
|
## * `items iterator <#items.i,SomeLinkedRing[T]>`_
|
||||||
|
## * `nodes iterator <#nodes.i,SomeLinkedRing[T]>`_
|
||||||
|
runnableExamples:
|
||||||
|
var a = initSinglyLinkedRing[int]()
|
||||||
|
for i in 1 .. 5:
|
||||||
|
a.append(10*i)
|
||||||
|
assert $a == "[10, 20, 30, 40, 50]"
|
||||||
|
for x in mitems(a):
|
||||||
|
x = 5*x - 1
|
||||||
|
assert $a == "[49, 99, 149, 199, 249]"
|
||||||
itemsRingImpl()
|
itemsRingImpl()
|
||||||
|
|
||||||
iterator nodes*[T](L: SomeLinkedList[T]): SomeLinkedNode[T] =
|
iterator nodes*[T](L: SomeLinkedList[T]): SomeLinkedNode[T] =
|
||||||
## iterates over every node of `x`. Removing the current node from the
|
## Iterates over every node of `x`. Removing the current node from the
|
||||||
## list during traversal is supported.
|
## list during traversal is supported.
|
||||||
|
##
|
||||||
|
## See also:
|
||||||
|
## * `items iterator <#items.i,SomeLinkedList[T]>`_
|
||||||
|
## * `mitems iterator <#mitems.i,SomeLinkedList[T]>`_
|
||||||
|
runnableExamples:
|
||||||
|
var a = initDoublyLinkedList[int]()
|
||||||
|
for i in 1 .. 5:
|
||||||
|
a.append(10*i)
|
||||||
|
assert $a == "[10, 20, 30, 40, 50]"
|
||||||
|
for x in nodes(a):
|
||||||
|
if x.value == 30:
|
||||||
|
a.remove(x)
|
||||||
|
else:
|
||||||
|
x.value = 5*x.value - 1
|
||||||
|
assert $a == "[49, 99, 199, 249]"
|
||||||
|
|
||||||
var it = L.head
|
var it = L.head
|
||||||
while it != nil:
|
while it != nil:
|
||||||
var nxt = it.next
|
var nxt = it.next
|
||||||
|
|
@ -111,8 +281,24 @@ iterator nodes*[T](L: SomeLinkedList[T]): SomeLinkedNode[T] =
|
||||||
it = nxt
|
it = nxt
|
||||||
|
|
||||||
iterator nodes*[T](L: SomeLinkedRing[T]): SomeLinkedNode[T] =
|
iterator nodes*[T](L: SomeLinkedRing[T]): SomeLinkedNode[T] =
|
||||||
## iterates over every node of `x`. Removing the current node from the
|
## Iterates over every node of `x`. Removing the current node from the
|
||||||
## list during traversal is supported.
|
## list during traversal is supported.
|
||||||
|
##
|
||||||
|
## See also:
|
||||||
|
## * `items iterator <#items.i,SomeLinkedRing[T]>`_
|
||||||
|
## * `mitems iterator <#mitems.i,SomeLinkedRing[T]>`_
|
||||||
|
runnableExamples:
|
||||||
|
var a = initDoublyLinkedRing[int]()
|
||||||
|
for i in 1 .. 5:
|
||||||
|
a.append(10*i)
|
||||||
|
assert $a == "[10, 20, 30, 40, 50]"
|
||||||
|
for x in nodes(a):
|
||||||
|
if x.value == 30:
|
||||||
|
a.remove(x)
|
||||||
|
else:
|
||||||
|
x.value = 5*x.value - 1
|
||||||
|
assert $a == "[49, 99, 199, 249]"
|
||||||
|
|
||||||
var it = L.head
|
var it = L.head
|
||||||
if it != nil:
|
if it != nil:
|
||||||
while true:
|
while true:
|
||||||
|
|
@ -122,7 +308,7 @@ iterator nodes*[T](L: SomeLinkedRing[T]): SomeLinkedNode[T] =
|
||||||
if it == L.head: break
|
if it == L.head: break
|
||||||
|
|
||||||
proc `$`*[T](L: SomeLinkedCollection[T]): string =
|
proc `$`*[T](L: SomeLinkedCollection[T]): string =
|
||||||
## turns a list into its string representation.
|
## Turns a list into its string representation for logging and printing.
|
||||||
result = "["
|
result = "["
|
||||||
for x in nodes(L):
|
for x in nodes(L):
|
||||||
if result.len > 1: result.add(", ")
|
if result.len > 1: result.add(", ")
|
||||||
|
|
@ -130,19 +316,54 @@ proc `$`*[T](L: SomeLinkedCollection[T]): string =
|
||||||
result.add("]")
|
result.add("]")
|
||||||
|
|
||||||
proc find*[T](L: SomeLinkedCollection[T], value: T): SomeLinkedNode[T] =
|
proc find*[T](L: SomeLinkedCollection[T], value: T): SomeLinkedNode[T] =
|
||||||
## searches in the list for a value. Returns nil if the value does not
|
## Searches in the list for a value. Returns `nil` if the value does not
|
||||||
## exist.
|
## exist.
|
||||||
|
##
|
||||||
|
## See also:
|
||||||
|
## * `contains proc <#contains,SomeLinkedCollection[T],T>`_
|
||||||
|
runnableExamples:
|
||||||
|
var a = initSinglyLinkedList[int]()
|
||||||
|
a.append(9)
|
||||||
|
a.append(8)
|
||||||
|
assert a.find(9).value == 9
|
||||||
|
assert a.find(1) == nil
|
||||||
|
|
||||||
for x in nodes(L):
|
for x in nodes(L):
|
||||||
if x.value == value: return x
|
if x.value == value: return x
|
||||||
|
|
||||||
proc contains*[T](L: SomeLinkedCollection[T], value: T): bool {.inline.} =
|
proc contains*[T](L: SomeLinkedCollection[T], value: T): bool {.inline.} =
|
||||||
## searches in the list for a value. Returns false if the value does not
|
## Searches in the list for a value. Returns `false` if the value does not
|
||||||
## exist, true otherwise.
|
## exist, `true` otherwise.
|
||||||
|
##
|
||||||
|
## See also:
|
||||||
|
## * `find proc <#find,SomeLinkedCollection[T],T>`_
|
||||||
|
runnableExamples:
|
||||||
|
var a = initSinglyLinkedList[int]()
|
||||||
|
a.append(9)
|
||||||
|
a.append(8)
|
||||||
|
assert a.contains(9)
|
||||||
|
assert 8 in a
|
||||||
|
assert(not a.contains(1))
|
||||||
|
assert 2 notin a
|
||||||
|
|
||||||
result = find(L, value) != nil
|
result = find(L, value) != nil
|
||||||
|
|
||||||
proc append*[T](L: var SinglyLinkedList[T],
|
proc append*[T](L: var SinglyLinkedList[T],
|
||||||
n: SinglyLinkedNode[T]) {.inline.} =
|
n: SinglyLinkedNode[T]) {.inline.} =
|
||||||
## appends a node `n` to `L`. Efficiency: O(1).
|
## Appends (adds to the end) a node `n` to `L`. Efficiency: O(1).
|
||||||
|
##
|
||||||
|
## See also:
|
||||||
|
## * `append proc <#append,SinglyLinkedList[T],T>`_ for appending a value
|
||||||
|
## * `prepend proc <#prepend,SinglyLinkedList[T],SinglyLinkedNode[T]>`_
|
||||||
|
## for prepending a node
|
||||||
|
## * `prepend proc <#prepend,SinglyLinkedList[T],T>`_ for prepending a value
|
||||||
|
runnableExamples:
|
||||||
|
var
|
||||||
|
a = initSinglyLinkedList[int]()
|
||||||
|
n = newSinglyLinkedNode[int](9)
|
||||||
|
a.append(n)
|
||||||
|
assert a.contains(9)
|
||||||
|
|
||||||
n.next = nil
|
n.next = nil
|
||||||
if L.tail != nil:
|
if L.tail != nil:
|
||||||
assert(L.tail.next == nil)
|
assert(L.tail.next == nil)
|
||||||
|
|
@ -151,22 +372,75 @@ proc append*[T](L: var SinglyLinkedList[T],
|
||||||
if L.head == nil: L.head = n
|
if L.head == nil: L.head = n
|
||||||
|
|
||||||
proc append*[T](L: var SinglyLinkedList[T], value: T) {.inline.} =
|
proc append*[T](L: var SinglyLinkedList[T], value: T) {.inline.} =
|
||||||
## appends a value to `L`. Efficiency: O(1).
|
## Appends (adds to the end) a value to `L`. Efficiency: O(1).
|
||||||
|
##
|
||||||
|
## See also:
|
||||||
|
## * `append proc <#append,SinglyLinkedList[T],T>`_ for appending a value
|
||||||
|
## * `prepend proc <#prepend,SinglyLinkedList[T],SinglyLinkedNode[T]>`_
|
||||||
|
## for prepending a node
|
||||||
|
## * `prepend proc <#prepend,SinglyLinkedList[T],T>`_ for prepending a value
|
||||||
|
runnableExamples:
|
||||||
|
var a = initSinglyLinkedList[int]()
|
||||||
|
a.append(9)
|
||||||
|
a.append(8)
|
||||||
|
assert a.contains(9)
|
||||||
append(L, newSinglyLinkedNode(value))
|
append(L, newSinglyLinkedNode(value))
|
||||||
|
|
||||||
proc prepend*[T](L: var SinglyLinkedList[T],
|
proc prepend*[T](L: var SinglyLinkedList[T],
|
||||||
n: SinglyLinkedNode[T]) {.inline.} =
|
n: SinglyLinkedNode[T]) {.inline.} =
|
||||||
## prepends a node to `L`. Efficiency: O(1).
|
## Prepends (adds to the beginning) a node to `L`. Efficiency: O(1).
|
||||||
|
##
|
||||||
|
## See also:
|
||||||
|
## * `append proc <#append,SinglyLinkedList[T],SinglyLinkedNode[T]>`_
|
||||||
|
## for appending a node
|
||||||
|
## * `append proc <#append,SinglyLinkedList[T],T>`_ for appending a value
|
||||||
|
## * `prepend proc <#prepend,SinglyLinkedList[T],T>`_ for prepending a value
|
||||||
|
runnableExamples:
|
||||||
|
var
|
||||||
|
a = initSinglyLinkedList[int]()
|
||||||
|
n = newSinglyLinkedNode[int](9)
|
||||||
|
a.prepend(n)
|
||||||
|
assert a.contains(9)
|
||||||
|
|
||||||
n.next = L.head
|
n.next = L.head
|
||||||
L.head = n
|
L.head = n
|
||||||
if L.tail == nil: L.tail = n
|
if L.tail == nil: L.tail = n
|
||||||
|
|
||||||
proc prepend*[T](L: var SinglyLinkedList[T], value: T) {.inline.} =
|
proc prepend*[T](L: var SinglyLinkedList[T], value: T) {.inline.} =
|
||||||
## prepends a node to `L`. Efficiency: O(1).
|
## Prepends (adds to the beginning) a node to `L`. Efficiency: O(1).
|
||||||
|
##
|
||||||
|
## See also:
|
||||||
|
## * `append proc <#append,SinglyLinkedList[T],SinglyLinkedNode[T]>`_
|
||||||
|
## for appending a node
|
||||||
|
## * `append proc <#append,SinglyLinkedList[T],T>`_ for appending a value
|
||||||
|
## * `prepend proc <#prepend,SinglyLinkedList[T],SinglyLinkedNode[T]>`_
|
||||||
|
## for prepending a node
|
||||||
|
runnableExamples:
|
||||||
|
var a = initSinglyLinkedList[int]()
|
||||||
|
a.prepend(9)
|
||||||
|
a.prepend(8)
|
||||||
|
assert a.contains(9)
|
||||||
prepend(L, newSinglyLinkedNode(value))
|
prepend(L, newSinglyLinkedNode(value))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
proc append*[T](L: var DoublyLinkedList[T], n: DoublyLinkedNode[T]) =
|
proc append*[T](L: var DoublyLinkedList[T], n: DoublyLinkedNode[T]) =
|
||||||
## appends a node `n` to `L`. Efficiency: O(1).
|
## Appends (adds to the end) a node `n` to `L`. Efficiency: O(1).
|
||||||
|
##
|
||||||
|
## See also:
|
||||||
|
## * `append proc <#append,DoublyLinkedList[T],T>`_ for appending a value
|
||||||
|
## * `prepend proc <#prepend,DoublyLinkedList[T],DoublyLinkedNode[T]>`_
|
||||||
|
## for prepending a node
|
||||||
|
## * `prepend proc <#prepend,DoublyLinkedList[T],T>`_ for prepending a value
|
||||||
|
## * `remove proc <#remove,DoublyLinkedList[T],DoublyLinkedNode[T]>`_
|
||||||
|
## for removing a node
|
||||||
|
runnableExamples:
|
||||||
|
var
|
||||||
|
a = initDoublyLinkedList[int]()
|
||||||
|
n = newDoublyLinkedNode[int](9)
|
||||||
|
a.append(n)
|
||||||
|
assert a.contains(9)
|
||||||
|
|
||||||
n.next = nil
|
n.next = nil
|
||||||
n.prev = L.tail
|
n.prev = L.tail
|
||||||
if L.tail != nil:
|
if L.tail != nil:
|
||||||
|
|
@ -176,11 +450,40 @@ proc append*[T](L: var DoublyLinkedList[T], n: DoublyLinkedNode[T]) =
|
||||||
if L.head == nil: L.head = n
|
if L.head == nil: L.head = n
|
||||||
|
|
||||||
proc append*[T](L: var DoublyLinkedList[T], value: T) =
|
proc append*[T](L: var DoublyLinkedList[T], value: T) =
|
||||||
## appends a value to `L`. Efficiency: O(1).
|
## Appends (adds to the end) a value to `L`. Efficiency: O(1).
|
||||||
|
##
|
||||||
|
## See also:
|
||||||
|
## * `append proc <#append,DoublyLinkedList[T],DoublyLinkedNode[T]>`_
|
||||||
|
## for appending a node
|
||||||
|
## * `prepend proc <#prepend,DoublyLinkedList[T],DoublyLinkedNode[T]>`_
|
||||||
|
## for prepending a node
|
||||||
|
## * `prepend proc <#prepend,DoublyLinkedList[T],T>`_ for prepending a value
|
||||||
|
## * `remove proc <#remove,DoublyLinkedList[T],DoublyLinkedNode[T]>`_
|
||||||
|
## for removing a node
|
||||||
|
runnableExamples:
|
||||||
|
var a = initDoublyLinkedList[int]()
|
||||||
|
a.append(9)
|
||||||
|
a.append(8)
|
||||||
|
assert a.contains(9)
|
||||||
append(L, newDoublyLinkedNode(value))
|
append(L, newDoublyLinkedNode(value))
|
||||||
|
|
||||||
proc prepend*[T](L: var DoublyLinkedList[T], n: DoublyLinkedNode[T]) =
|
proc prepend*[T](L: var DoublyLinkedList[T], n: DoublyLinkedNode[T]) =
|
||||||
## prepends a node `n` to `L`. Efficiency: O(1).
|
## Prepends (adds to the beginning) a node `n` to `L`. Efficiency: O(1).
|
||||||
|
##
|
||||||
|
## See also:
|
||||||
|
## * `append proc <#append,DoublyLinkedList[T],DoublyLinkedNode[T]>`_
|
||||||
|
## for appending a node
|
||||||
|
## * `append proc <#append,DoublyLinkedList[T],T>`_ for appending a value
|
||||||
|
## * `prepend proc <#prepend,DoublyLinkedList[T],T>`_ for prepending a value
|
||||||
|
## * `remove proc <#remove,DoublyLinkedList[T],DoublyLinkedNode[T]>`_
|
||||||
|
## for removing a node
|
||||||
|
runnableExamples:
|
||||||
|
var
|
||||||
|
a = initDoublyLinkedList[int]()
|
||||||
|
n = newDoublyLinkedNode[int](9)
|
||||||
|
a.prepend(n)
|
||||||
|
assert a.contains(9)
|
||||||
|
|
||||||
n.prev = nil
|
n.prev = nil
|
||||||
n.next = L.head
|
n.next = L.head
|
||||||
if L.head != nil:
|
if L.head != nil:
|
||||||
|
|
@ -190,18 +493,56 @@ proc prepend*[T](L: var DoublyLinkedList[T], n: DoublyLinkedNode[T]) =
|
||||||
if L.tail == nil: L.tail = n
|
if L.tail == nil: L.tail = n
|
||||||
|
|
||||||
proc prepend*[T](L: var DoublyLinkedList[T], value: T) =
|
proc prepend*[T](L: var DoublyLinkedList[T], value: T) =
|
||||||
## prepends a value to `L`. Efficiency: O(1).
|
## Prepends (adds to the beginning) a value to `L`. Efficiency: O(1).
|
||||||
|
##
|
||||||
|
## See also:
|
||||||
|
## * `append proc <#append,DoublyLinkedList[T],DoublyLinkedNode[T]>`_
|
||||||
|
## for appending a node
|
||||||
|
## * `append proc <#append,DoublyLinkedList[T],T>`_ for appending a value
|
||||||
|
## * `prepend proc <#prepend,DoublyLinkedList[T],DoublyLinkedNode[T]>`_
|
||||||
|
## for prepending a node
|
||||||
|
## * `remove proc <#remove,DoublyLinkedList[T],DoublyLinkedNode[T]>`_
|
||||||
|
## for removing a node
|
||||||
|
runnableExamples:
|
||||||
|
var a = initDoublyLinkedList[int]()
|
||||||
|
a.prepend(9)
|
||||||
|
a.prepend(8)
|
||||||
|
assert a.contains(9)
|
||||||
prepend(L, newDoublyLinkedNode(value))
|
prepend(L, newDoublyLinkedNode(value))
|
||||||
|
|
||||||
proc remove*[T](L: var DoublyLinkedList[T], n: DoublyLinkedNode[T]) =
|
proc remove*[T](L: var DoublyLinkedList[T], n: DoublyLinkedNode[T]) =
|
||||||
## removes `n` from `L`. Efficiency: O(1).
|
## Removes a node `n` from `L`. Efficiency: O(1).
|
||||||
|
runnableExamples:
|
||||||
|
var
|
||||||
|
a = initDoublyLinkedList[int]()
|
||||||
|
n = newDoublyLinkedNode[int](5)
|
||||||
|
a.append(n)
|
||||||
|
assert 5 in a
|
||||||
|
a.remove(n)
|
||||||
|
assert 5 notin a
|
||||||
|
|
||||||
if n == L.tail: L.tail = n.prev
|
if n == L.tail: L.tail = n.prev
|
||||||
if n == L.head: L.head = n.next
|
if n == L.head: L.head = n.next
|
||||||
if n.next != nil: n.next.prev = n.prev
|
if n.next != nil: n.next.prev = n.prev
|
||||||
if n.prev != nil: n.prev.next = n.next
|
if n.prev != nil: n.prev.next = n.next
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
proc append*[T](L: var SinglyLinkedRing[T], n: SinglyLinkedNode[T]) =
|
proc append*[T](L: var SinglyLinkedRing[T], n: SinglyLinkedNode[T]) =
|
||||||
## appends a node `n` to `L`. Efficiency: O(1).
|
## Appends (adds to the end) a node `n` to `L`. Efficiency: O(1).
|
||||||
|
##
|
||||||
|
## See also:
|
||||||
|
## * `append proc <#append,SinglyLinkedRing[T],T>`_ for appending a value
|
||||||
|
## * `prepend proc <#prepend,SinglyLinkedRing[T],SinglyLinkedNode[T]>`_
|
||||||
|
## for prepending a node
|
||||||
|
## * `prepend proc <#prepend,SinglyLinkedRing[T],T>`_ for prepending a value
|
||||||
|
runnableExamples:
|
||||||
|
var
|
||||||
|
a = initSinglyLinkedRing[int]()
|
||||||
|
n = newSinglyLinkedNode[int](9)
|
||||||
|
a.append(n)
|
||||||
|
assert a.contains(9)
|
||||||
|
|
||||||
if L.head != nil:
|
if L.head != nil:
|
||||||
n.next = L.head
|
n.next = L.head
|
||||||
assert(L.tail != nil)
|
assert(L.tail != nil)
|
||||||
|
|
@ -213,11 +554,36 @@ proc append*[T](L: var SinglyLinkedRing[T], n: SinglyLinkedNode[T]) =
|
||||||
L.tail = n
|
L.tail = n
|
||||||
|
|
||||||
proc append*[T](L: var SinglyLinkedRing[T], value: T) =
|
proc append*[T](L: var SinglyLinkedRing[T], value: T) =
|
||||||
## appends a value to `L`. Efficiency: O(1).
|
## Appends (adds to the end) a value to `L`. Efficiency: O(1).
|
||||||
|
##
|
||||||
|
## See also:
|
||||||
|
## * `append proc <#append,SinglyLinkedRing[T],SinglyLinkedNode[T]>`_
|
||||||
|
## for appending a node
|
||||||
|
## * `prepend proc <#prepend,SinglyLinkedRing[T],SinglyLinkedNode[T]>`_
|
||||||
|
## for prepending a node
|
||||||
|
## * `prepend proc <#prepend,SinglyLinkedRing[T],T>`_ for prepending a value
|
||||||
|
runnableExamples:
|
||||||
|
var a = initSinglyLinkedRing[int]()
|
||||||
|
a.append(9)
|
||||||
|
a.append(8)
|
||||||
|
assert a.contains(9)
|
||||||
append(L, newSinglyLinkedNode(value))
|
append(L, newSinglyLinkedNode(value))
|
||||||
|
|
||||||
proc prepend*[T](L: var SinglyLinkedRing[T], n: SinglyLinkedNode[T]) =
|
proc prepend*[T](L: var SinglyLinkedRing[T], n: SinglyLinkedNode[T]) =
|
||||||
## prepends a node `n` to `L`. Efficiency: O(1).
|
## Prepends (adds to the beginning) a node `n` to `L`. Efficiency: O(1).
|
||||||
|
##
|
||||||
|
## See also:
|
||||||
|
## * `append proc <#append,SinglyLinkedRing[T],SinglyLinkedNode[T]>`_
|
||||||
|
## for appending a node
|
||||||
|
## * `append proc <#append,SinglyLinkedRing[T],T>`_ for appending a value
|
||||||
|
## * `prepend proc <#prepend,SinglyLinkedRing[T],T>`_ for prepending a value
|
||||||
|
runnableExamples:
|
||||||
|
var
|
||||||
|
a = initSinglyLinkedRing[int]()
|
||||||
|
n = newSinglyLinkedNode[int](9)
|
||||||
|
a.prepend(n)
|
||||||
|
assert a.contains(9)
|
||||||
|
|
||||||
if L.head != nil:
|
if L.head != nil:
|
||||||
n.next = L.head
|
n.next = L.head
|
||||||
assert(L.tail != nil)
|
assert(L.tail != nil)
|
||||||
|
|
@ -228,11 +594,40 @@ proc prepend*[T](L: var SinglyLinkedRing[T], n: SinglyLinkedNode[T]) =
|
||||||
L.head = n
|
L.head = n
|
||||||
|
|
||||||
proc prepend*[T](L: var SinglyLinkedRing[T], value: T) =
|
proc prepend*[T](L: var SinglyLinkedRing[T], value: T) =
|
||||||
## prepends a value to `L`. Efficiency: O(1).
|
## Prepends (adds to the beginning) a value to `L`. Efficiency: O(1).
|
||||||
|
##
|
||||||
|
## See also:
|
||||||
|
## * `append proc <#append,SinglyLinkedRing[T],SinglyLinkedNode[T]>`_
|
||||||
|
## for appending a node
|
||||||
|
## * `append proc <#append,SinglyLinkedRing[T],T>`_ for appending a value
|
||||||
|
## * `prepend proc <#prepend,SinglyLinkedRing[T],SinglyLinkedNode[T]>`_
|
||||||
|
## for prepending a node
|
||||||
|
runnableExamples:
|
||||||
|
var a = initSinglyLinkedRing[int]()
|
||||||
|
a.prepend(9)
|
||||||
|
a.prepend(8)
|
||||||
|
assert a.contains(9)
|
||||||
prepend(L, newSinglyLinkedNode(value))
|
prepend(L, newSinglyLinkedNode(value))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
proc append*[T](L: var DoublyLinkedRing[T], n: DoublyLinkedNode[T]) =
|
proc append*[T](L: var DoublyLinkedRing[T], n: DoublyLinkedNode[T]) =
|
||||||
## appends a node `n` to `L`. Efficiency: O(1).
|
## Appends (adds to the end) a node `n` to `L`. Efficiency: O(1).
|
||||||
|
##
|
||||||
|
## See also:
|
||||||
|
## * `append proc <#append,DoublyLinkedRing[T],T>`_ for appending a value
|
||||||
|
## * `prepend proc <#prepend,DoublyLinkedRing[T],DoublyLinkedNode[T]>`_
|
||||||
|
## for prepending a node
|
||||||
|
## * `prepend proc <#prepend,DoublyLinkedRing[T],T>`_ for prepending a value
|
||||||
|
## * `remove proc <#remove,DoublyLinkedRing[T],DoublyLinkedNode[T]>`_
|
||||||
|
## for removing a node
|
||||||
|
runnableExamples:
|
||||||
|
var
|
||||||
|
a = initDoublyLinkedRing[int]()
|
||||||
|
n = newDoublyLinkedNode[int](9)
|
||||||
|
a.append(n)
|
||||||
|
assert a.contains(9)
|
||||||
|
|
||||||
if L.head != nil:
|
if L.head != nil:
|
||||||
n.next = L.head
|
n.next = L.head
|
||||||
n.prev = L.head.prev
|
n.prev = L.head.prev
|
||||||
|
|
@ -244,11 +639,40 @@ proc append*[T](L: var DoublyLinkedRing[T], n: DoublyLinkedNode[T]) =
|
||||||
L.head = n
|
L.head = n
|
||||||
|
|
||||||
proc append*[T](L: var DoublyLinkedRing[T], value: T) =
|
proc append*[T](L: var DoublyLinkedRing[T], value: T) =
|
||||||
## appends a value to `L`. Efficiency: O(1).
|
## Appends (adds to the end) a value to `L`. Efficiency: O(1).
|
||||||
|
##
|
||||||
|
## See also:
|
||||||
|
## * `append proc <#append,DoublyLinkedRing[T],DoublyLinkedNode[T]>`_
|
||||||
|
## for appending a node
|
||||||
|
## * `prepend proc <#prepend,DoublyLinkedRing[T],DoublyLinkedNode[T]>`_
|
||||||
|
## for prepending a node
|
||||||
|
## * `prepend proc <#prepend,DoublyLinkedRing[T],T>`_ for prepending a value
|
||||||
|
## * `remove proc <#remove,DoublyLinkedRing[T],DoublyLinkedNode[T]>`_
|
||||||
|
## for removing a node
|
||||||
|
runnableExamples:
|
||||||
|
var a = initDoublyLinkedRing[int]()
|
||||||
|
a.append(9)
|
||||||
|
a.append(8)
|
||||||
|
assert a.contains(9)
|
||||||
append(L, newDoublyLinkedNode(value))
|
append(L, newDoublyLinkedNode(value))
|
||||||
|
|
||||||
proc prepend*[T](L: var DoublyLinkedRing[T], n: DoublyLinkedNode[T]) =
|
proc prepend*[T](L: var DoublyLinkedRing[T], n: DoublyLinkedNode[T]) =
|
||||||
## prepends a node `n` to `L`. Efficiency: O(1).
|
## Prepends (adds to the beginning) a node `n` to `L`. Efficiency: O(1).
|
||||||
|
##
|
||||||
|
## See also:
|
||||||
|
## * `append proc <#append,DoublyLinkedRing[T],DoublyLinkedNode[T]>`_
|
||||||
|
## for appending a node
|
||||||
|
## * `append proc <#append,DoublyLinkedRing[T],T>`_ for appending a value
|
||||||
|
## * `prepend proc <#prepend,DoublyLinkedRing[T],T>`_ for prepending a value
|
||||||
|
## * `remove proc <#remove,DoublyLinkedRing[T],DoublyLinkedNode[T]>`_
|
||||||
|
## for removing a node
|
||||||
|
runnableExamples:
|
||||||
|
var
|
||||||
|
a = initDoublyLinkedRing[int]()
|
||||||
|
n = newDoublyLinkedNode[int](9)
|
||||||
|
a.prepend(n)
|
||||||
|
assert a.contains(9)
|
||||||
|
|
||||||
if L.head != nil:
|
if L.head != nil:
|
||||||
n.next = L.head
|
n.next = L.head
|
||||||
n.prev = L.head.prev
|
n.prev = L.head.prev
|
||||||
|
|
@ -260,11 +684,34 @@ proc prepend*[T](L: var DoublyLinkedRing[T], n: DoublyLinkedNode[T]) =
|
||||||
L.head = n
|
L.head = n
|
||||||
|
|
||||||
proc prepend*[T](L: var DoublyLinkedRing[T], value: T) =
|
proc prepend*[T](L: var DoublyLinkedRing[T], value: T) =
|
||||||
## prepends a value to `L`. Efficiency: O(1).
|
## Prepends (adds to the beginning) a value to `L`. Efficiency: O(1).
|
||||||
|
##
|
||||||
|
## See also:
|
||||||
|
## * `append proc <#append,DoublyLinkedRing[T],DoublyLinkedNode[T]>`_
|
||||||
|
## for appending a node
|
||||||
|
## * `append proc <#append,DoublyLinkedRing[T],T>`_ for appending a value
|
||||||
|
## * `prepend proc <#prepend,DoublyLinkedRing[T],DoublyLinkedNode[T]>`_
|
||||||
|
## for prepending a node
|
||||||
|
## * `remove proc <#remove,DoublyLinkedRing[T],DoublyLinkedNode[T]>`_
|
||||||
|
## for removing a node
|
||||||
|
runnableExamples:
|
||||||
|
var a = initDoublyLinkedRing[int]()
|
||||||
|
a.prepend(9)
|
||||||
|
a.prepend(8)
|
||||||
|
assert a.contains(9)
|
||||||
prepend(L, newDoublyLinkedNode(value))
|
prepend(L, newDoublyLinkedNode(value))
|
||||||
|
|
||||||
proc remove*[T](L: var DoublyLinkedRing[T], n: DoublyLinkedNode[T]) =
|
proc remove*[T](L: var DoublyLinkedRing[T], n: DoublyLinkedNode[T]) =
|
||||||
## removes `n` from `L`. Efficiency: O(1).
|
## Removes `n` from `L`. Efficiency: O(1).
|
||||||
|
runnableExamples:
|
||||||
|
var
|
||||||
|
a = initDoublyLinkedRing[int]()
|
||||||
|
n = newDoublyLinkedNode[int](5)
|
||||||
|
a.append(n)
|
||||||
|
assert 5 in a
|
||||||
|
a.remove(n)
|
||||||
|
assert 5 notin a
|
||||||
|
|
||||||
n.next.prev = n.prev
|
n.next.prev = n.prev
|
||||||
n.prev.next = n.next
|
n.prev.next = n.next
|
||||||
if n == L.head:
|
if n == L.head:
|
||||||
|
|
|
||||||
|
|
@ -1,257 +0,0 @@
|
||||||
#
|
|
||||||
#
|
|
||||||
# Nim's Runtime Library
|
|
||||||
# (c) Copyright 2012 Andreas Rumpf
|
|
||||||
#
|
|
||||||
# See the file "copying.txt", included in this
|
|
||||||
# distribution, for details about the copyright.
|
|
||||||
#
|
|
||||||
|
|
||||||
## Implementation of a `queue`:idx:. The underlying implementation uses a ``seq``.
|
|
||||||
##
|
|
||||||
## None of the procs that get an individual value from the queue can be used
|
|
||||||
## on an empty queue.
|
|
||||||
## If compiled with `boundChecks` option, those procs will raise an `IndexError`
|
|
||||||
## on such access. This should not be relied upon, as `-d:release` will
|
|
||||||
## disable those checks and may return garbage or crash the program.
|
|
||||||
##
|
|
||||||
## As such, a check to see if the queue is empty is needed before any
|
|
||||||
## access, unless your program logic guarantees it indirectly.
|
|
||||||
##
|
|
||||||
## .. code-block:: Nim
|
|
||||||
## proc foo(a, b: Positive) = # assume random positive values for `a` and `b`
|
|
||||||
## var q = initQueue[int]() # initializes the object
|
|
||||||
## for i in 1 ..< a: q.add i # populates the queue
|
|
||||||
##
|
|
||||||
## if b < q.len: # checking before indexed access
|
|
||||||
## echo "The element at index position ", b, " is ", q[b]
|
|
||||||
##
|
|
||||||
## # The following two lines don't need any checking on access due to the
|
|
||||||
## # logic of the program, but that would not be the case if `a` could be 0.
|
|
||||||
## assert q.front == 1
|
|
||||||
## assert q.back == a
|
|
||||||
##
|
|
||||||
## while q.len > 0: # checking if the queue is empty
|
|
||||||
## echo q.pop()
|
|
||||||
##
|
|
||||||
## Note: For inter thread communication use
|
|
||||||
## a `Channel <channels.html>`_ instead.
|
|
||||||
|
|
||||||
import math
|
|
||||||
|
|
||||||
{.warning: "`queues` module is deprecated - use `deques` instead".}
|
|
||||||
|
|
||||||
type
|
|
||||||
Queue* {.deprecated.} [T] = object ## A queue.
|
|
||||||
data: seq[T]
|
|
||||||
rd, wr, count, mask: int
|
|
||||||
|
|
||||||
proc initQueue*[T](initialSize: int = 4): Queue[T] =
|
|
||||||
## Create a new queue.
|
|
||||||
## Optionally, the initial capacity can be reserved via `initialSize` as a
|
|
||||||
## performance optimization. The length of a newly created queue will still
|
|
||||||
## be 0.
|
|
||||||
##
|
|
||||||
## `initialSize` needs to be a power of two. If you need to accept runtime
|
|
||||||
## values for this you could use the ``nextPowerOfTwo`` proc from the
|
|
||||||
## `math <math.html>`_ module.
|
|
||||||
assert isPowerOfTwo(initialSize)
|
|
||||||
result.mask = initialSize-1
|
|
||||||
newSeq(result.data, initialSize)
|
|
||||||
|
|
||||||
proc len*[T](q: Queue[T]): int {.inline.}=
|
|
||||||
## Return the number of elements of `q`.
|
|
||||||
result = q.count
|
|
||||||
|
|
||||||
template emptyCheck(q) =
|
|
||||||
# Bounds check for the regular queue access.
|
|
||||||
when compileOption("boundChecks"):
|
|
||||||
if unlikely(q.count < 1):
|
|
||||||
raise newException(IndexError, "Empty queue.")
|
|
||||||
|
|
||||||
template xBoundsCheck(q, i) =
|
|
||||||
# Bounds check for the array like accesses.
|
|
||||||
when compileOption("boundChecks"): # d:release should disable this.
|
|
||||||
if unlikely(i >= q.count): # x < q.low is taken care by the Natural parameter
|
|
||||||
raise newException(IndexError,
|
|
||||||
"Out of bounds: " & $i & " > " & $(q.count - 1))
|
|
||||||
|
|
||||||
proc front*[T](q: Queue[T]): T {.inline.}=
|
|
||||||
## Return the oldest element of `q`. Equivalent to `q.pop()` but does not
|
|
||||||
## remove it from the queue.
|
|
||||||
emptyCheck(q)
|
|
||||||
result = q.data[q.rd]
|
|
||||||
|
|
||||||
proc back*[T](q: Queue[T]): T {.inline.} =
|
|
||||||
## Return the newest element of `q` but does not remove it from the queue.
|
|
||||||
emptyCheck(q)
|
|
||||||
result = q.data[q.wr - 1 and q.mask]
|
|
||||||
|
|
||||||
proc `[]`*[T](q: Queue[T], i: Natural) : T {.inline.} =
|
|
||||||
## Access the i-th element of `q` by order of insertion.
|
|
||||||
## q[0] is the oldest (the next one q.pop() will extract),
|
|
||||||
## q[^1] is the newest (last one added to the queue).
|
|
||||||
xBoundsCheck(q, i)
|
|
||||||
return q.data[q.rd + i and q.mask]
|
|
||||||
|
|
||||||
proc `[]`*[T](q: var Queue[T], i: Natural): var T {.inline.} =
|
|
||||||
## Access the i-th element of `q` and returns a mutable
|
|
||||||
## reference to it.
|
|
||||||
xBoundsCheck(q, i)
|
|
||||||
return q.data[q.rd + i and q.mask]
|
|
||||||
|
|
||||||
proc `[]=`* [T] (q: var Queue[T], i: Natural, val : T) {.inline.} =
|
|
||||||
## Change the i-th element of `q`.
|
|
||||||
xBoundsCheck(q, i)
|
|
||||||
q.data[q.rd + i and q.mask] = val
|
|
||||||
|
|
||||||
iterator items*[T](q: Queue[T]): T =
|
|
||||||
## Yield every element of `q`.
|
|
||||||
var i = q.rd
|
|
||||||
for c in 0 ..< q.count:
|
|
||||||
yield q.data[i]
|
|
||||||
i = (i + 1) and q.mask
|
|
||||||
|
|
||||||
iterator mitems*[T](q: var Queue[T]): var T =
|
|
||||||
## Yield every element of `q`.
|
|
||||||
var i = q.rd
|
|
||||||
for c in 0 ..< q.count:
|
|
||||||
yield q.data[i]
|
|
||||||
i = (i + 1) and q.mask
|
|
||||||
|
|
||||||
iterator pairs*[T](q: Queue[T]): tuple[key: int, val: T] =
|
|
||||||
## Yield every (position, value) of `q`.
|
|
||||||
var i = q.rd
|
|
||||||
for c in 0 ..< q.count:
|
|
||||||
yield (c, q.data[i])
|
|
||||||
i = (i + 1) and q.mask
|
|
||||||
|
|
||||||
proc contains*[T](q: Queue[T], item: T): bool {.inline.} =
|
|
||||||
## Return true if `item` is in `q` or false if not found. Usually used
|
|
||||||
## via the ``in`` operator. It is the equivalent of ``q.find(item) >= 0``.
|
|
||||||
##
|
|
||||||
## .. code-block:: Nim
|
|
||||||
## if x in q:
|
|
||||||
## assert q.contains x
|
|
||||||
for e in q:
|
|
||||||
if e == item: return true
|
|
||||||
return false
|
|
||||||
|
|
||||||
proc add*[T](q: var Queue[T], item: T) =
|
|
||||||
## Add an `item` to the end of the queue `q`.
|
|
||||||
var cap = q.mask+1
|
|
||||||
if unlikely(q.count >= cap):
|
|
||||||
var n = newSeq[T](cap*2)
|
|
||||||
for i, x in pairs(q): # don't use copyMem because the GC and because it's slower.
|
|
||||||
shallowCopy(n[i], x)
|
|
||||||
shallowCopy(q.data, n)
|
|
||||||
q.mask = cap*2 - 1
|
|
||||||
q.wr = q.count
|
|
||||||
q.rd = 0
|
|
||||||
inc q.count
|
|
||||||
q.data[q.wr] = item
|
|
||||||
q.wr = (q.wr + 1) and q.mask
|
|
||||||
|
|
||||||
template default[T](t: typedesc[T]): T =
|
|
||||||
var v: T
|
|
||||||
v
|
|
||||||
|
|
||||||
proc pop*[T](q: var Queue[T]): T {.inline, discardable.} =
|
|
||||||
## Remove and returns the first (oldest) element of the queue `q`.
|
|
||||||
emptyCheck(q)
|
|
||||||
dec q.count
|
|
||||||
result = q.data[q.rd]
|
|
||||||
q.data[q.rd] = default(type(result))
|
|
||||||
q.rd = (q.rd + 1) and q.mask
|
|
||||||
|
|
||||||
proc enqueue*[T](q: var Queue[T], item: T) =
|
|
||||||
## Alias for the ``add`` operation.
|
|
||||||
q.add(item)
|
|
||||||
|
|
||||||
proc dequeue*[T](q: var Queue[T]): T =
|
|
||||||
## Alias for the ``pop`` operation.
|
|
||||||
q.pop()
|
|
||||||
|
|
||||||
proc `$`*[T](q: Queue[T]): string =
|
|
||||||
## Turn a queue into its string representation.
|
|
||||||
result = "["
|
|
||||||
for x in items(q): # Don't remove the items here for reasons that don't fit in this margin.
|
|
||||||
if result.len > 1: result.add(", ")
|
|
||||||
result.add($x)
|
|
||||||
result.add("]")
|
|
||||||
|
|
||||||
when isMainModule:
|
|
||||||
var q = initQueue[int](1)
|
|
||||||
q.add(123)
|
|
||||||
q.add(9)
|
|
||||||
q.enqueue(4)
|
|
||||||
var first = q.dequeue()
|
|
||||||
q.add(56)
|
|
||||||
q.add(6)
|
|
||||||
var second = q.pop()
|
|
||||||
q.add(789)
|
|
||||||
|
|
||||||
assert first == 123
|
|
||||||
assert second == 9
|
|
||||||
assert($q == "[4, 56, 6, 789]")
|
|
||||||
|
|
||||||
assert q[0] == q.front and q.front == 4
|
|
||||||
q[0] = 42
|
|
||||||
q[q.len - 1] = 7
|
|
||||||
|
|
||||||
assert 6 in q and 789 notin q
|
|
||||||
assert q.find(6) >= 0
|
|
||||||
assert q.find(789) < 0
|
|
||||||
|
|
||||||
for i in -2 .. 10:
|
|
||||||
if i in q:
|
|
||||||
assert q.contains(i) and q.find(i) >= 0
|
|
||||||
else:
|
|
||||||
assert(not q.contains(i) and q.find(i) < 0)
|
|
||||||
|
|
||||||
when compileOption("boundChecks"):
|
|
||||||
try:
|
|
||||||
echo q[99]
|
|
||||||
assert false
|
|
||||||
except IndexError:
|
|
||||||
discard
|
|
||||||
|
|
||||||
try:
|
|
||||||
assert q.len == 4
|
|
||||||
for i in 0 ..< 5: q.pop()
|
|
||||||
assert false
|
|
||||||
except IndexError:
|
|
||||||
discard
|
|
||||||
|
|
||||||
# grabs some types of resize error.
|
|
||||||
q = initQueue[int]()
|
|
||||||
for i in 1 .. 4: q.add i
|
|
||||||
q.pop()
|
|
||||||
q.pop()
|
|
||||||
for i in 5 .. 8: q.add i
|
|
||||||
assert $q == "[3, 4, 5, 6, 7, 8]"
|
|
||||||
|
|
||||||
# Similar to proc from the documentation example
|
|
||||||
proc foo(a, b: Positive) = # assume random positive values for `a` and `b`.
|
|
||||||
var q = initQueue[int]()
|
|
||||||
assert q.len == 0
|
|
||||||
for i in 1 .. a: q.add i
|
|
||||||
|
|
||||||
if b < q.len: # checking before indexed access.
|
|
||||||
assert q[b] == b + 1
|
|
||||||
|
|
||||||
# The following two lines don't need any checking on access due to the logic
|
|
||||||
# of the program, but that would not be the case if `a` could be 0.
|
|
||||||
assert q.front == 1
|
|
||||||
assert q.back == a
|
|
||||||
|
|
||||||
while q.len > 0: # checking if the queue is empty
|
|
||||||
assert q.pop() > 0
|
|
||||||
|
|
||||||
#foo(0,0)
|
|
||||||
foo(8,5)
|
|
||||||
foo(10,9)
|
|
||||||
foo(1,1)
|
|
||||||
foo(2,1)
|
|
||||||
foo(1,5)
|
|
||||||
foo(3,2)
|
|
||||||
File diff suppressed because it is too large
Load diff
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue