mirror of
https://github.com/nim-lang/Nim.git
synced 2026-06-03 02:18:00 +00:00
Merge pull request #1 from nim-lang/devel
Getting the latest from nim-lang
This commit is contained in:
24
.github/ISSUE_TEMPLATE/bug_report.md
vendored
24
.github/ISSUE_TEMPLATE/bug_report.md
vendored
@@ -1,37 +1,32 @@
|
||||
---
|
||||
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. -->
|
||||
|
||||
<!-- Summarize the Problem here, keep it simple. -->
|
||||
<!-- e.g. `echo` outputs the wrong string. -->
|
||||
<!-- Summarize the problem here, keep it short and simple. -->
|
||||
Function `echo` outputs the wrong string.
|
||||
|
||||
|
||||
### Example
|
||||
<!-- This should be a source code block.
|
||||
<!-- Paste your example in the code-block below. -->
|
||||
```nim
|
||||
echo "Hello World!"
|
||||
```
|
||||
-->
|
||||
|
||||
|
||||
### Current Output
|
||||
<!--
|
||||
```
|
||||
Hola mundo!
|
||||
```
|
||||
-->
|
||||
|
||||
|
||||
### Expected Output
|
||||
<!--
|
||||
<!-- What should be the correct output? -->
|
||||
```
|
||||
Hello World!
|
||||
```
|
||||
-->
|
||||
|
||||
|
||||
### Possible Solution
|
||||
@@ -40,6 +35,11 @@ Hello World!
|
||||
|
||||
### Additional Information
|
||||
<!--- For Example:
|
||||
A link to a project where the issue is relevant.
|
||||
A link to a related issue or discussion.
|
||||
* Your Nim version (output of `nim -v`).
|
||||
* Was it working in the previous Nim releases?
|
||||
* A link to a related issue or discussion.
|
||||
-->
|
||||
```
|
||||
$ nim -v
|
||||
Nim Compiler Version 0.1.2
|
||||
```
|
||||
|
||||
17
.github/ISSUE_TEMPLATE/feature_request.md
vendored
17
.github/ISSUE_TEMPLATE/feature_request.md
vendored
@@ -1,9 +1,18 @@
|
||||
---
|
||||
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
|
||||
<!--- Short summary of your proposed feature -->
|
||||
|
||||
@@ -18,6 +27,6 @@ about: You want to suggest a new feature? Use this template.
|
||||
|
||||
### Additional Information
|
||||
<!--- For Example:
|
||||
A link to a project where the issue is relevant.
|
||||
A link to a related issue or discussion.
|
||||
-->
|
||||
* A link to a project where the issue is relevant.
|
||||
* A link to a related issue or discussion.
|
||||
-->
|
||||
|
||||
6
.gitignore
vendored
6
.gitignore
vendored
@@ -32,7 +32,7 @@ doc/*.html
|
||||
doc/*.pdf
|
||||
doc/*.idx
|
||||
/web/upload
|
||||
build/*
|
||||
/build/*
|
||||
bin/*
|
||||
|
||||
# iOS specific wildcards.
|
||||
@@ -71,3 +71,7 @@ test.txt
|
||||
tweeter.db
|
||||
tweeter_test.db
|
||||
megatest.nim
|
||||
|
||||
/outputExpected.txt
|
||||
/outputGotten.txt
|
||||
|
||||
|
||||
26
.travis.yml
26
.travis.yml
@@ -14,8 +14,9 @@ matrix:
|
||||
- os: osx
|
||||
env: NIM_COMPILE_TO_CPP=true
|
||||
|
||||
allow_failures:
|
||||
- env: NIM_COMPILE_TO_CPP=true
|
||||
# To allow failures for a failing configuration, use something like:
|
||||
# allow_failures:
|
||||
# - env: NIM_COMPILE_TO_CPP=true
|
||||
# - os: osx
|
||||
|
||||
addons:
|
||||
@@ -40,26 +41,11 @@ before_script:
|
||||
- sh build.sh
|
||||
- cd ..
|
||||
- export PATH=$(pwd)/bin${PATH:+:$PATH}
|
||||
- echo PATH:${PATH}
|
||||
|
||||
script:
|
||||
- nim c koch
|
||||
- env NIM_COMPILE_TO_CPP=false ./koch boot
|
||||
- ./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
|
||||
- ./koch runCI
|
||||
|
||||
before_deploy:
|
||||
# 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:
|
||||
- bin\nim c koch
|
||||
- koch boot -d:release
|
||||
- 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
|
||||
- koch runCI
|
||||
|
||||
deploy: off
|
||||
|
||||
@@ -26,6 +26,9 @@ 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 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
|
||||
|
||||
@@ -23,13 +24,19 @@
|
||||
- The undocumented ``#? strongSpaces`` parsing mode has been removed.
|
||||
- The `not` operator is now always a unary operator, this means that code like
|
||||
``assert not isFalse(3)`` compiles.
|
||||
- `getImpl` on a `var` or `let` symbol will now return the full `IdentDefs`
|
||||
tree from the symbol declaration instead of just the initializer portion.
|
||||
|
||||
|
||||
#### Breaking changes in the standard library
|
||||
|
||||
- `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
|
||||
`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
|
||||
`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
|
||||
|
||||
- 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.
|
||||
- Implicit imports via ``--import: module`` in a config file are now restricted
|
||||
to the main package.
|
||||
|
||||
|
||||
### Library additions
|
||||
|
||||
@@ -87,11 +109,18 @@ proc enumToString*(enums: openArray[enum]): string =
|
||||
is instantiation of generic proc symbol.
|
||||
|
||||
- Added the parameter ``isSorted`` for the ``sequtils.deduplicate`` proc.
|
||||
|
||||
- There is a new stdlib module `std/diff` to compute the famous "diff"
|
||||
of two texts by line.
|
||||
|
||||
- Added `os.relativePath`.
|
||||
|
||||
- Added `parseopt.remainingArgs`.
|
||||
|
||||
- Added `os.getCurrentCompilerExe` (implmented as `getAppFilename` at CT),
|
||||
can be used to retrieve the currently executing compiler.
|
||||
|
||||
|
||||
### Library changes
|
||||
|
||||
- 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
|
||||
interoperability. (Bug #9619.)
|
||||
|
||||
- `os.joinPath` and `os.normalizePath` handle edge cases like ``"a/b/../../.."``
|
||||
differently.
|
||||
|
||||
- `securehash` is moved to `lib/deprecated`
|
||||
|
||||
|
||||
### 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
|
||||
the `gcsafe` pragma block.
|
||||
- added os.getCurrentProcessId()
|
||||
- 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
|
||||
- Custom pragmas are now supported for `var` and `let` symbols.
|
||||
|
||||
|
||||
### 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,
|
||||
see [#9647](https://github.com/nim-lang/Nim/issues/9647).
|
||||
The previous extension will continue to work.
|
||||
- Pragma syntax is now consistent. Previous syntax where type pragmas did not
|
||||
follow the type name is now deprecated. Also pragma before generic parameter
|
||||
list is deprecated to be consistent with how pragmas are used with a proc. See
|
||||
[#8514](https://github.com/nim-lang/Nim/issues/8514) and
|
||||
[#1872](https://github.com/nim-lang/Nim/issues/1872) for further details.
|
||||
|
||||
|
||||
### Tool changes
|
||||
- `jsondoc` now include a `moduleDescription` field with the module
|
||||
description. `jsondoc0` shows comments as it's own objects as shown in the
|
||||
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
|
||||
- The deprecated `fmod` proc is now unavailable on the VM'.
|
||||
|
||||
|
||||
### Bugfixes
|
||||
|
||||
@@ -4,6 +4,6 @@ author = "Andreas Rumpf"
|
||||
description = "Compiler package providing the compiler sources as a library."
|
||||
license = "MIT"
|
||||
|
||||
installDirs = @["compiler"]
|
||||
installDirs = @["compiler", "nimsuggest"]
|
||||
|
||||
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:
|
||||
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 =
|
||||
return t.kind in tyMetaTypes 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)
|
||||
of tyOpenArray, tyVarargs:
|
||||
var b: TLoc
|
||||
case a.t.kind
|
||||
case skipTypes(a.t, abstractVarRange).kind
|
||||
of tyOpenArray, tyVarargs:
|
||||
putIntoDest(p, b, e, "$1, $1Len_0" % [rdLoc(a)], a.storage)
|
||||
of tyString, tySequence:
|
||||
|
||||
@@ -31,6 +31,7 @@ const
|
||||
cfsData: "NIM_merge_DATA",
|
||||
cfsProcs: "NIM_merge_PROCS",
|
||||
cfsInitProc: "NIM_merge_INIT_PROC",
|
||||
cfsDatInitProc: "NIM_merge_DATINIT_PROC",
|
||||
cfsTypeInit1: "NIM_merge_TYPE_INIT1",
|
||||
cfsTypeInit2: "NIM_merge_TYPE_INIT2",
|
||||
cfsTypeInit3: "NIM_merge_TYPE_INIT3",
|
||||
|
||||
@@ -436,30 +436,25 @@ proc mangleRecFieldName(m: BModule; field: PSym): Rope =
|
||||
if result == nil: internalError(m.config, field.info, "mangleRecFieldName")
|
||||
|
||||
proc genRecordFieldsAux(m: BModule, n: PNode,
|
||||
accessExpr: Rope, rectype: PType,
|
||||
rectype: PType,
|
||||
check: var IntSet): Rope =
|
||||
result = nil
|
||||
case n.kind
|
||||
of nkRecList:
|
||||
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:
|
||||
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,
|
||||
# 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
|
||||
for i in countup(1, sonsLen(n) - 1):
|
||||
case n.sons[i].kind
|
||||
of nkOfBranch, nkElse:
|
||||
let k = lastSon(n.sons[i])
|
||||
if k.kind != nkSym:
|
||||
let sname = "S" & rope(i)
|
||||
let a = genRecordFieldsAux(m, k, "$1.$2" % [ae, sname], rectype,
|
||||
check)
|
||||
let a = genRecordFieldsAux(m, k, rectype, check)
|
||||
if a != nil:
|
||||
if tfPacked notin rectype.flags:
|
||||
add(unionBody, "struct {")
|
||||
@@ -469,22 +464,20 @@ proc genRecordFieldsAux(m: BModule, n: PNode,
|
||||
else:
|
||||
addf(unionBody, "#pragma pack(push, 1)$nstruct{", [])
|
||||
add(unionBody, a)
|
||||
addf(unionBody, "} $1;$n", [sname])
|
||||
addf(unionBody, "};$n", [])
|
||||
if tfPacked in rectype.flags and hasAttribute notin CC[m.config.cCompiler].props:
|
||||
addf(unionBody, "#pragma pack(pop)$n", [])
|
||||
else:
|
||||
add(unionBody, genRecordFieldsAux(m, k, ae, rectype, check))
|
||||
add(unionBody, genRecordFieldsAux(m, k, rectype, check))
|
||||
else: internalError(m.config, "genRecordFieldsAux(record case branch)")
|
||||
if unionBody != nil:
|
||||
addf(result, "union{$n$1} $2;$n", [unionBody, uname])
|
||||
addf(result, "union{$n$1};$n", [unionBody])
|
||||
of nkSym:
|
||||
let field = n.sym
|
||||
if field.typ.kind == tyVoid: return
|
||||
#assert(field.ast == nil)
|
||||
let sname = mangleRecFieldName(m, field)
|
||||
let ae = if accessExpr != nil: "$1.$2" % [accessExpr, sname]
|
||||
else: sname
|
||||
fillLoc(field.loc, locField, n, ae, OnUnknown)
|
||||
fillLoc(field.loc, locField, n, sname, OnUnknown)
|
||||
# for importcpp'ed objects, we only need to set field.loc, but don't
|
||||
# have to recurse via 'getTypeDescAux'. And not doing so prevents problems
|
||||
# with heavily templatized C++ code:
|
||||
@@ -505,7 +498,7 @@ proc genRecordFieldsAux(m: BModule, n: PNode,
|
||||
else: internalError(m.config, n.info, "genRecordFieldsAux()")
|
||||
|
||||
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) =
|
||||
# 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
|
||||
if typ.sym.magic == mException:
|
||||
# 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,
|
||||
# proper request to generate popCurrentExceptionEx not possible for 2 reasons:
|
||||
# generated function will be below declared Exception type and circular dependency
|
||||
# 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
|
||||
else:
|
||||
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 =
|
||||
# bugfix: we need to search the type that contains the discriminator:
|
||||
var objtype = objtype
|
||||
var objtype = objtype.skipTypes(abstractPtrs)
|
||||
while lookupInRecord(objtype.n, d.name) == nil:
|
||||
objtype = objtype.sons[0]
|
||||
objtype = objtype.sons[0].skipTypes(abstractPtrs)
|
||||
if objtype.sym == nil:
|
||||
internalError(m.config, d.info, "anonymous obj with discriminator")
|
||||
result = "NimDT_$1_$2" % [rope($hashType(objtype)), rope(d.name.s.mangle)]
|
||||
@@ -1042,6 +1041,8 @@ proc genObjectFields(m: BModule, typ, origType: PType, n: PNode, expr: Rope;
|
||||
else: internalError(m.config, n.info, "genObjectFields(nkRecCase)")
|
||||
of nkSym:
|
||||
var field = n.sym
|
||||
# Do not produce code for void types
|
||||
if isEmptyType(field.typ): return
|
||||
if field.bitsize == 0:
|
||||
if field.loc.r == nil: fillObjectFields(m, typ)
|
||||
if field.loc.t == nil:
|
||||
|
||||
@@ -1087,30 +1087,24 @@ proc getFileHeader(conf: ConfigRef; cfile: Cfile): Rope =
|
||||
result = getCopyright(conf, cfile)
|
||||
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) =
|
||||
## 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
|
||||
# The use of a volatile function pointer to call Pre/NimMainInner
|
||||
# prevents inlining of the NimMainInner function and dependent
|
||||
# functions, which might otherwise merge their stack frames.
|
||||
PreMainBody =
|
||||
"void PreMainInner(void) {$N" &
|
||||
"\tsystemInit000();$N" &
|
||||
"$1" &
|
||||
"$2" &
|
||||
"$3" &
|
||||
"}$N$N" &
|
||||
"void PreMain(void) {$N" &
|
||||
"\tvoid (*volatile inner)(void);$N" &
|
||||
"\tsystemDatInit000();$N" &
|
||||
"\tinner = PreMainInner;$N" &
|
||||
"$4$5" &
|
||||
"$1" &
|
||||
"\t(*inner)();$N" &
|
||||
"}$N$N"
|
||||
|
||||
@@ -1217,21 +1211,17 @@ proc genMainProc(m: BModule) =
|
||||
else:
|
||||
nimMain = PosixNimMain
|
||||
otherMain = PosixCMain
|
||||
if m.g.breakpoints != nil: discard cgsym(m, "dbgRegisterBreakpoint")
|
||||
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 =
|
||||
if m.config.target.targetOS == osStandalone or m.config.selectedGC == gcNone: "".rope
|
||||
else: ropecg(m, "\t#initStackBottomWith((void *)&inner);$N")
|
||||
inc(m.labels)
|
||||
appcg(m, m.s[cfsProcs], PreMainBody, [
|
||||
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])
|
||||
m.g.mainDatInit, m.g.breakpoints, m.g.otherModsInit])
|
||||
|
||||
appcg(m, m.s[cfsProcs], nimMain,
|
||||
[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 registerModuleToMain(g: BModuleList; m: PSym) =
|
||||
var
|
||||
init = m.getInitName
|
||||
datInit = m.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])
|
||||
if sfSystemModule notin m.flags:
|
||||
|
||||
proc registerModuleToMain(g: BModuleList; m: BModule) =
|
||||
if m.s[cfsDatInitProc].len > 0:
|
||||
let datInit = m.module.getDatInitName
|
||||
addf(g.mainModProcs, "N_LIB_PRIVATE N_NIMCALL(void, $1)(void);$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]
|
||||
if sfMainModule in m.flags:
|
||||
if sfMainModule in m.module.flags:
|
||||
add(g.mainModInit, initCall)
|
||||
elif sfSystemModule in m.module.flags:
|
||||
add(g.mainDatInit, initCall) # systemInit must called right after systemDatInit if any
|
||||
else:
|
||||
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) =
|
||||
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]
|
||||
if m.typeNodes > 0:
|
||||
appcg(m, m.s[cfsTypeInit1], "static #TNimNode $1[$2];$n",
|
||||
@@ -1290,82 +1319,115 @@ proc genInitCode(m: BModule) =
|
||||
# Keep a bogus frame in case the code needs one
|
||||
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, m.preInitProc.s(cpsLocals))
|
||||
add(prc, genSectionEnd(cpsLocals, m.config))
|
||||
|
||||
if m.preInitProc.s(cpsInit).len > 0:
|
||||
moduleInitRequired = true
|
||||
add(prc, genSectionStart(cpsInit, m.config))
|
||||
add(prc, m.preInitProc.s(cpsInit))
|
||||
add(prc, genSectionEnd(cpsInit, m.config))
|
||||
|
||||
if m.preInitProc.s(cpsStmts).len > 0:
|
||||
moduleInitRequired = true
|
||||
add(prc, genSectionStart(cpsStmts, m.config))
|
||||
add(prc, m.preInitProc.s(cpsStmts))
|
||||
add(prc, genSectionEnd(cpsStmts, m.config))
|
||||
addf(prc, "}$N", [])
|
||||
|
||||
if m.initProc.gcFrameId > 0:
|
||||
moduleInitRequired = true
|
||||
add(prc, initGCFrame(m.initProc))
|
||||
|
||||
if m.initProc.s(cpsLocals).len > 0:
|
||||
moduleInitRequired = true
|
||||
add(prc, genSectionStart(cpsLocals, m.config))
|
||||
add(prc, m.preInitProc.s(cpsLocals))
|
||||
add(prc, m.initProc.s(cpsLocals))
|
||||
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:
|
||||
# BUT: the generated init code might depend on a current frame, so
|
||||
# declare it nevertheless:
|
||||
incl m.flags, frameDeclared
|
||||
if preventStackTrace notin m.flags:
|
||||
var procname = makeCString(m.module.name.s)
|
||||
add(prc, initFrame(m.initProc, procname, quotedFilename(m.config, m.module.info)))
|
||||
else:
|
||||
add(prc, ~"\tTFrame FR_; FR_.len = 0;$N")
|
||||
|
||||
add(prc, genSectionStart(cpsInit, m.config))
|
||||
add(prc, m.preInitProc.s(cpsInit))
|
||||
add(prc, m.initProc.s(cpsInit))
|
||||
add(prc, genSectionEnd(cpsInit, m.config))
|
||||
|
||||
add(prc, genSectionStart(cpsStmts, m.config))
|
||||
add(prc, m.preInitProc.s(cpsStmts))
|
||||
add(prc, m.initProc.s(cpsStmts))
|
||||
add(prc, genSectionEnd(cpsStmts, m.config))
|
||||
addf(prc, "}$N", [])
|
||||
|
||||
add(prc, initGCFrame(m.initProc))
|
||||
if optStackTrace in m.initProc.options and preventStackTrace notin m.flags:
|
||||
add(prc, deinitFrame(m.initProc))
|
||||
|
||||
add(prc, genSectionStart(cpsLocals, m.config))
|
||||
add(prc, m.initProc.s(cpsLocals))
|
||||
add(prc, genSectionEnd(cpsLocals, m.config))
|
||||
|
||||
if optStackTrace in m.initProc.options and frameDeclared notin m.flags:
|
||||
# BUT: the generated init code might depend on a current frame, so
|
||||
# declare it nevertheless:
|
||||
incl m.flags, frameDeclared
|
||||
if preventStackTrace notin m.flags:
|
||||
var procname = makeCString(m.module.name.s)
|
||||
add(prc, initFrame(m.initProc, procname, quotedFilename(m.config, m.module.info)))
|
||||
else:
|
||||
add(prc, ~"\tTFrame FR_; FR_.len = 0;$N")
|
||||
|
||||
add(prc, genSectionStart(cpsInit, m.config))
|
||||
add(prc, m.initProc.s(cpsInit))
|
||||
add(prc, genSectionEnd(cpsInit, m.config))
|
||||
|
||||
add(prc, genSectionStart(cpsStmts, m.config))
|
||||
add(prc, m.initProc.s(cpsStmts))
|
||||
add(prc, genSectionEnd(cpsStmts, m.config))
|
||||
|
||||
if optStackTrace in m.initProc.options and preventStackTrace notin m.flags:
|
||||
add(prc, deinitFrame(m.initProc))
|
||||
add(prc, deinitGCFrame(m.initProc))
|
||||
addf(prc, "}$N$N", [])
|
||||
|
||||
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))
|
||||
if m.initProc.gcFrameId > 0:
|
||||
moduleInitRequired = true
|
||||
add(prc, deinitGCFrame(m.initProc))
|
||||
|
||||
addf(prc, "}$N$N", [])
|
||||
|
||||
# we cannot simply add the init proc to ``m.s[cfsProcs]`` anymore because
|
||||
# that would lead to a *nesting* of merge sections which the merger does
|
||||
# not support. So we add it to another special section: ``cfsInitProc``
|
||||
add(m.s[cfsInitProc], prc)
|
||||
|
||||
for i, el in pairs(m.extensionLoaders):
|
||||
if el != nil:
|
||||
let ex = "NIM_EXTERNC N_NIMCALL(void, nimLoadProcs$1)(void) {$2}$N$N" %
|
||||
[(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 =
|
||||
var moduleIsEmpty = true
|
||||
|
||||
result = getFileHeader(m.config, cfile)
|
||||
result.add(genMergeInfo(m))
|
||||
|
||||
if m.config.cppCustomNamespace.len > 0:
|
||||
result.add openNamespaceNim(m.config.cppCustomNamespace)
|
||||
|
||||
generateThreadLocalStorage(m)
|
||||
generateHeaders(m)
|
||||
for i in countup(cfsHeaders, cfsProcs):
|
||||
add(result, genSectionStart(i, m.config))
|
||||
add(result, m.s[i])
|
||||
add(result, genSectionEnd(i, m.config))
|
||||
if m.config.cppCustomNamespace.len > 0 and i == cfsHeaders:
|
||||
result.add openNamespaceNim(m.config.cppCustomNamespace)
|
||||
add(result, m.s[cfsInitProc])
|
||||
if m.config.cppCustomNamespace.len > 0: result.add closeNamespaceNim()
|
||||
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, m.s[i])
|
||||
add(result, genSectionEnd(i, m.config))
|
||||
|
||||
if m.s[cfsInitProc].len > 0:
|
||||
moduleIsEmpty = false
|
||||
add(result, m.s[cfsInitProc])
|
||||
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 =
|
||||
result = newProc(nil, m)
|
||||
@@ -1522,27 +1584,30 @@ proc writeModule(m: BModule, pending: bool) =
|
||||
finishTypeDescriptions(m)
|
||||
if sfMainModule in m.module.flags:
|
||||
# generate main file:
|
||||
genMainProc(m)
|
||||
add(m.s[cfsProcHeaders], m.g.mainModProcs)
|
||||
generateThreadVarsSize(m)
|
||||
|
||||
var cf = Cfile(cname: cfile, obj: completeCFilePath(m.config, toObjFile(m.config, cfile)), flags: {})
|
||||
var code = genModule(m, cf)
|
||||
when hasTinyCBackend:
|
||||
if conf.cmd == cmdRun:
|
||||
tccgen.compileCCode($code)
|
||||
return
|
||||
if code != nil:
|
||||
when hasTinyCBackend:
|
||||
if conf.cmd == cmdRun:
|
||||
tccgen.compileCCode($code)
|
||||
return
|
||||
|
||||
if not shouldRecompile(m, code, cf): cf.flags = {CfileFlag.Cached}
|
||||
addFileToCompile(m.config, cf)
|
||||
if not shouldRecompile(m, code, cf): cf.flags = {CfileFlag.Cached}
|
||||
addFileToCompile(m.config, cf)
|
||||
elif pending and mergeRequired(m) and sfMainModule notin m.module.flags:
|
||||
let cf = Cfile(cname: cfile, obj: completeCFilePath(m.config, toObjFile(m.config, cfile)), flags: {})
|
||||
mergeFiles(cfile, m)
|
||||
genInitCode(m)
|
||||
finishTypeDescriptions(m)
|
||||
var code = genModule(m, cf)
|
||||
if not writeRope(code, cfile):
|
||||
rawMessage(m.config, errCannotOpenFile, cfile.string)
|
||||
addFileToCompile(m.config, cf)
|
||||
if code != nil:
|
||||
if not writeRope(code, cfile):
|
||||
rawMessage(m.config, errCannotOpenFile, cfile.string)
|
||||
addFileToCompile(m.config, cf)
|
||||
else:
|
||||
# Consider: first compilation compiles ``system.nim`` and produces
|
||||
# ``system.c`` but then compilation fails due to an error. This means
|
||||
@@ -1560,13 +1625,16 @@ proc updateCachedModule(m: BModule) =
|
||||
mergeFiles(cfile, m)
|
||||
genInitCode(m)
|
||||
finishTypeDescriptions(m)
|
||||
|
||||
var code = genModule(m, cf)
|
||||
if not writeRope(code, cfile):
|
||||
rawMessage(m.config, errCannotOpenFile, cfile.string)
|
||||
if code != nil:
|
||||
if not writeRope(code, cfile):
|
||||
rawMessage(m.config, errCannotOpenFile, cfile.string)
|
||||
addFileToCompile(m.config, cf)
|
||||
else:
|
||||
if sfMainModule notin m.module.flags:
|
||||
genMainProc(m)
|
||||
cf.flags = {CfileFlag.Cached}
|
||||
addFileToCompile(m.config, cf)
|
||||
addFileToCompile(m.config, cf)
|
||||
|
||||
proc myClose(graph: ModuleGraph; b: PPassContext, n: PNode): PNode =
|
||||
result = n
|
||||
@@ -1579,15 +1647,25 @@ proc myClose(graph: ModuleGraph; b: PPassContext, n: PNode): PNode =
|
||||
if n != nil:
|
||||
m.initProc.options = initProcOptions(m)
|
||||
genStmts(m.initProc, n)
|
||||
# cached modules need to registered too:
|
||||
registerModuleToMain(m.g, m.module)
|
||||
|
||||
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:
|
||||
incl m.flags, objHasKidsValid
|
||||
let disp = generateMethodDispatchers(graph)
|
||||
for x in disp: genProcAux(m, x.sym)
|
||||
genMainProc(m)
|
||||
|
||||
m.g.modules_closed.add m
|
||||
|
||||
proc genForwardedProcs(g: BModuleList) =
|
||||
# Forward declared proc:s lack bodies when first encountered, so they're given
|
||||
|
||||
@@ -32,6 +32,7 @@ type
|
||||
cfsData, # section for C constant data
|
||||
cfsProcs, # section for C procs that are not inline
|
||||
cfsInitProc, # section for the C init proc
|
||||
cfsDatInitProc, # section for the C datInit proc
|
||||
cfsTypeInit1, # section 1 for declarations of type information
|
||||
cfsTypeInit2, # section 2 for init of type information
|
||||
cfsTypeInit3, # section 3 for init of type information
|
||||
@@ -112,6 +113,7 @@ type
|
||||
mainModProcs*, mainModInit*, otherModsInit*, mainDatInit*: Rope
|
||||
mapping*: Rope # the generated mapping file (if requested)
|
||||
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
|
||||
generatedHeader*: BModule
|
||||
breakPointId*: int
|
||||
@@ -191,8 +193,6 @@ proc newModuleList*(g: ModuleGraph): BModuleList =
|
||||
graph: g, nimtvDeclared: initIntSet())
|
||||
|
||||
iterator cgenModules*(g: BModuleList): BModule =
|
||||
for m in g.modules:
|
||||
# ultimately, we are iterating over the file ids here.
|
||||
# some "files" won't have an associated cgen module (like stdin)
|
||||
# and we must skip over them.
|
||||
if m != nil: yield m
|
||||
for m in g.modules_closed:
|
||||
# iterate modules in the order they were closed
|
||||
yield m
|
||||
|
||||
@@ -48,18 +48,10 @@ proc loadConfigsAndRunMainCommand*(self: NimProg, cache: IdentCache; conf: Confi
|
||||
if self.suggestMode:
|
||||
conf.command = "nimsuggest"
|
||||
|
||||
# These defines/options should not be enabled while processing nimscript
|
||||
# bug #4446, #9420, #8991, #9589, #9153
|
||||
undefSymbol(conf.symbols, "profiler")
|
||||
undefSymbol(conf.symbols, "memProfiler")
|
||||
undefSymbol(conf.symbols, "nodejs")
|
||||
|
||||
# bug #9120
|
||||
conf.globalOptions.excl(optTaintMode)
|
||||
|
||||
proc runNimScriptIfExists(path: AbsoluteFile)=
|
||||
if fileExists(path):
|
||||
runNimScript(cache, path, freshDefines = false, conf)
|
||||
template runNimScriptIfExists(path: AbsoluteFile) =
|
||||
let p = path # eval once
|
||||
if fileExists(p):
|
||||
runNimScript(cache, p, freshDefines = false, conf)
|
||||
|
||||
# Caution: make sure this stays in sync with `loadConfigs`
|
||||
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
|
||||
discard
|
||||
|
||||
# Reload configuration from .cfg file
|
||||
loadConfigs(DefaultConfig, cache, conf)
|
||||
|
||||
# now process command line arguments again, because some options in the
|
||||
# command line can overwite the config file's settings
|
||||
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,
|
||||
info: TLineInfo) =
|
||||
case arg.normalize
|
||||
of "on": conf.options = conf.options + op
|
||||
of "","on": conf.options = conf.options + op
|
||||
of "off": conf.options = conf.options - op
|
||||
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,
|
||||
info: TLineInfo) =
|
||||
case arg.normalize
|
||||
of "on": conf.globalOptions = conf.globalOptions + op
|
||||
of "", "on": conf.globalOptions = conf.globalOptions + op
|
||||
of "off": conf.globalOptions = conf.globalOptions - op
|
||||
else: localError(conf, info, errOnOrOffExpectedButXFound % arg)
|
||||
|
||||
@@ -224,7 +224,7 @@ proc testCompileOptionArg*(conf: ConfigRef; switch, arg: string, info: TLineInfo
|
||||
case arg.normalize
|
||||
of "boehm": result = conf.selectedGC == gcBoehm
|
||||
of "refc": result = conf.selectedGC == gcRefc
|
||||
of "v2": result = conf.selectedGC == gcV2
|
||||
of "v2": result = false
|
||||
of "markandsweep": result = conf.selectedGC == gcMarkAndSweep
|
||||
of "generational": result = false
|
||||
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 "excessivestacktrace": result = contains(conf.globalOptions, optExcessiveStackTrace)
|
||||
of "nilseqs": result = contains(conf.options, optNilSeqs)
|
||||
of "oldast": result = contains(conf.options, optOldAst)
|
||||
else: invalidCmdLineOption(conf, passCmd1, switch, info)
|
||||
|
||||
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}:
|
||||
addExternalFileToLink(conf, AbsoluteFile arg)
|
||||
of "debuginfo":
|
||||
expectNoArg(conf, switch, arg, pass, info)
|
||||
incl(conf.globalOptions, optCDebug)
|
||||
processOnOffSwitchG(conf, {optCDebug}, arg, pass, info)
|
||||
of "embedsrc":
|
||||
expectNoArg(conf, switch, arg, pass, info)
|
||||
incl(conf.globalOptions, optEmbedOrigSrc)
|
||||
processOnOffSwitchG(conf, {optEmbedOrigSrc}, arg, pass, info)
|
||||
of "compileonly", "c":
|
||||
expectNoArg(conf, switch, arg, pass, info)
|
||||
incl(conf.globalOptions, optCompileOnly)
|
||||
processOnOffSwitchG(conf, {optCompileOnly}, arg, pass, info)
|
||||
of "nolinking":
|
||||
expectNoArg(conf, switch, arg, pass, info)
|
||||
incl(conf.globalOptions, optNoLinking)
|
||||
processOnOffSwitchG(conf, {optNoLinking}, arg, pass, info)
|
||||
of "nomain":
|
||||
expectNoArg(conf, switch, arg, pass, info)
|
||||
incl(conf.globalOptions, optNoMain)
|
||||
processOnOffSwitchG(conf, {optNoMain}, arg, pass, info)
|
||||
of "forcebuild", "f":
|
||||
expectNoArg(conf, switch, arg, pass, info)
|
||||
incl(conf.globalOptions, optForceFullMake)
|
||||
processOnOffSwitchG(conf, {optForceFullMake}, arg, pass, info)
|
||||
of "project":
|
||||
expectNoArg(conf, switch, arg, pass, info)
|
||||
incl conf.globalOptions, optWholeProject
|
||||
processOnOffSwitchG(conf, {optWholeProject}, arg, pass, info)
|
||||
of "gc":
|
||||
expectArg(conf, switch, arg, pass, info)
|
||||
case arg.normalize
|
||||
@@ -442,7 +436,7 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
|
||||
of "refc":
|
||||
conf.selectedGC = gcRefc
|
||||
of "v2":
|
||||
conf.selectedGC = gcV2
|
||||
message(conf, info, warnDeprecated, "--gc:v2 is deprecated; using default gc")
|
||||
of "markandsweep":
|
||||
conf.selectedGC = gcMarkAndSweep
|
||||
defineSymbol(conf.symbols, "gcmarkandsweep")
|
||||
@@ -498,7 +492,7 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
|
||||
else: undefSymbol(conf.symbols, "hotcodereloading")
|
||||
of "oldnewlines":
|
||||
case arg.normalize
|
||||
of "on":
|
||||
of "","on":
|
||||
conf.oldNewlines = true
|
||||
defineSymbol(conf.symbols, "nimOldNewlines")
|
||||
of "off":
|
||||
@@ -508,6 +502,7 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
|
||||
localError(conf, info, errOnOrOffExpectedButXFound % arg)
|
||||
of "laxstrings": processOnOffSwitch(conf, {optLaxStrings}, 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 "floatchecks":
|
||||
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)
|
||||
of "import":
|
||||
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":
|
||||
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":
|
||||
expectNoArg(conf, switch, arg, pass, info)
|
||||
incl(conf.globalOptions, optListCmd)
|
||||
processOnOffSwitchG(conf, {optListCmd}, arg, pass, info)
|
||||
of "genmapping":
|
||||
expectNoArg(conf, switch, arg, pass, info)
|
||||
incl(conf.globalOptions, optGenMapping)
|
||||
processOnOffSwitchG(conf, {optGenMapping}, arg, pass, info)
|
||||
of "os":
|
||||
expectArg(conf, switch, arg, pass, info)
|
||||
if pass in {passCmd1, passPP}:
|
||||
@@ -615,8 +610,7 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
|
||||
elif cpu != conf.target.hostCPU:
|
||||
setTarget(conf.target, conf.target.targetOS, cpu)
|
||||
of "run", "r":
|
||||
expectNoArg(conf, switch, arg, pass, info)
|
||||
incl(conf.globalOptions, optRun)
|
||||
processOnOffSwitchG(conf, {optRun}, arg, pass, info)
|
||||
of "errormax":
|
||||
expectArg(conf, switch, arg, pass, info)
|
||||
# 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
|
||||
else: localError(conf, info, "invalid option for --incremental: " & arg)
|
||||
of "skipcfg":
|
||||
expectNoArg(conf, switch, arg, pass, info)
|
||||
incl(conf.globalOptions, optSkipSystemConfigFile)
|
||||
processOnOffSwitchG(conf, {optSkipSystemConfigFile}, arg, pass, info)
|
||||
of "skipprojcfg":
|
||||
expectNoArg(conf, switch, arg, pass, info)
|
||||
incl(conf.globalOptions, optSkipProjConfigFile)
|
||||
processOnOffSwitchG(conf, {optSkipProjConfigFile}, arg, pass, info)
|
||||
of "skipusercfg":
|
||||
expectNoArg(conf, switch, arg, pass, info)
|
||||
incl(conf.globalOptions, optSkipUserConfigFile)
|
||||
processOnOffSwitchG(conf, {optSkipUserConfigFile}, arg, pass, info)
|
||||
of "skipparentcfg":
|
||||
expectNoArg(conf, switch, arg, pass, info)
|
||||
incl(conf.globalOptions, optSkipParentConfigFiles)
|
||||
processOnOffSwitchG(conf, {optSkipParentConfigFiles}, arg, pass, info)
|
||||
of "genscript", "gendeps":
|
||||
expectNoArg(conf, switch, arg, pass, info)
|
||||
incl(conf.globalOptions, optGenScript)
|
||||
incl(conf.globalOptions, optCompileOnly)
|
||||
processOnOffSwitchG(conf, {optGenScript}, arg, pass, info)
|
||||
processOnOffSwitchG(conf, {optCompileOnly}, arg, pass, info)
|
||||
of "colors": processOnOffSwitchG(conf, {optUseColors}, arg, pass, info)
|
||||
of "lib":
|
||||
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)
|
||||
conf.ideCmd = ideUse
|
||||
of "stdout":
|
||||
expectNoArg(conf, switch, arg, pass, info)
|
||||
incl(conf.globalOptions, optStdout)
|
||||
processOnOffSwitchG(conf, {optStdout}, arg, pass, info)
|
||||
of "listfullpaths":
|
||||
expectNoArg(conf, switch, arg, pass, info)
|
||||
incl conf.globalOptions, optListFullPaths
|
||||
processOnOffSwitchG(conf, {optListFullPaths}, arg, pass, info)
|
||||
of "dynliboverride":
|
||||
dynlibOverride(conf, switch, arg, pass, info)
|
||||
of "dynliboverrideall":
|
||||
expectNoArg(conf, switch, arg, pass, info)
|
||||
incl conf.globalOptions, optDynlibOverrideAll
|
||||
processOnOffSwitchG(conf, {optDynlibOverrideAll}, arg, pass, info)
|
||||
of "cs":
|
||||
# only supported for compatibility. Does nothing.
|
||||
expectArg(conf, switch, arg, pass, info)
|
||||
|
||||
@@ -296,7 +296,8 @@ proc makePtrType(c: Con, baseType: PType): PType =
|
||||
template genOp(opr, opname, ri) =
|
||||
let op = opr
|
||||
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:
|
||||
globalError(c.graph.config, dest.info, "internal error: '" & opname & "' operator is generic")
|
||||
patchHead op
|
||||
@@ -365,7 +366,7 @@ proc destructiveMoveVar(n: PNode; c: var Con): PNode =
|
||||
result.add genWasMoved(n, c)
|
||||
result.add tempAsNode
|
||||
|
||||
proc sinkParamIsLastReadCheck(c: var Con, s: PNode) =
|
||||
proc sinkParamIsLastReadCheck(c: var Con, s: PNode) =
|
||||
assert s.kind == nkSym and s.sym.kind == skParam
|
||||
if not isLastRead(s, c):
|
||||
localError(c.graph.config, c.otherRead.info, "sink parameter `" & $s.sym.name.s &
|
||||
@@ -427,7 +428,7 @@ proc pArg(arg: PNode; c: var Con; isSink: bool): PNode =
|
||||
result = copyNode(arg)
|
||||
for i in 0..<arg.len:
|
||||
var branch = copyNode(arg[i])
|
||||
if arg[i].kind in {nkElifBranch, nkElifExpr}:
|
||||
if arg[i].kind in {nkElifBranch, nkElifExpr}:
|
||||
branch.add p(arg[i][0], c)
|
||||
branch.add pArgIfTyped(arg[i][1])
|
||||
else:
|
||||
@@ -442,13 +443,13 @@ proc pArg(arg: PNode; c: var Con; isSink: bool): PNode =
|
||||
branch = arg[i] # of branch conditions are constants
|
||||
branch[^1] = pArgIfTyped(arg[i][^1])
|
||||
elif arg[i].kind in {nkElifBranch, nkElifExpr}:
|
||||
branch = copyNode(arg[i])
|
||||
branch = copyNode(arg[i])
|
||||
branch.add p(arg[i][0], c)
|
||||
branch.add pArgIfTyped(arg[i][1])
|
||||
else:
|
||||
branch = copyNode(arg[i])
|
||||
branch = copyNode(arg[i])
|
||||
branch.add pArgIfTyped(arg[i][0])
|
||||
result.add branch
|
||||
result.add branch
|
||||
else:
|
||||
# an object that is not temporary but passed to a 'sink' parameter
|
||||
# results in a copy.
|
||||
@@ -476,7 +477,7 @@ proc moveOrCopy(dest, ri: PNode; c: var Con): PNode =
|
||||
result.add ri2
|
||||
of nkBracketExpr:
|
||||
if ri[0].kind == nkSym and isUnpackedTuple(ri[0].sym):
|
||||
# unpacking of tuple: move out the elements
|
||||
# unpacking of tuple: move out the elements
|
||||
result = genSink(c, dest.typ, dest, ri)
|
||||
else:
|
||||
result = genCopy(c, dest.typ, dest, ri)
|
||||
@@ -509,11 +510,11 @@ proc moveOrCopy(dest, ri: PNode; c: var Con): PNode =
|
||||
branch = ri[i] # of branch conditions are constants
|
||||
branch[^1] = moveOrCopyIfTyped(ri[i][^1])
|
||||
elif ri[i].kind in {nkElifBranch, nkElifExpr}:
|
||||
branch = copyNode(ri[i])
|
||||
branch = copyNode(ri[i])
|
||||
branch.add p(ri[i][0], c)
|
||||
branch.add moveOrCopyIfTyped(ri[i][1])
|
||||
else:
|
||||
branch = copyNode(ri[i])
|
||||
branch = copyNode(ri[i])
|
||||
branch.add moveOrCopyIfTyped(ri[i][0])
|
||||
result.add branch
|
||||
of nkBracket:
|
||||
@@ -550,7 +551,7 @@ proc moveOrCopy(dest, ri: PNode; c: var Con): PNode =
|
||||
sinkParamIsLastReadCheck(c, ri)
|
||||
var snk = genSink(c, dest.typ, dest, ri)
|
||||
snk.add ri
|
||||
result = newTree(nkStmtList, snk, genMagicCall(ri, c, "wasMoved", mWasMoved))
|
||||
result = newTree(nkStmtList, snk, genMagicCall(ri, c, "wasMoved", mWasMoved))
|
||||
elif ri.sym.kind != skParam and isLastRead(ri, c):
|
||||
# Rule 3: `=sink`(x, z); wasMoved(z)
|
||||
var snk = genSink(c, dest.typ, dest, ri)
|
||||
@@ -647,7 +648,7 @@ proc injectDestructorCalls*(g: ModuleGraph; owner: PSym; n: PNode): PNode =
|
||||
let params = owner.typ.n
|
||||
for i in 1 ..< params.len:
|
||||
let param = params[i].sym
|
||||
if param.typ.kind == tySink and hasDestructor(param.typ):
|
||||
if param.typ.kind == tySink and hasDestructor(param.typ):
|
||||
c.destroys.add genDestroy(c, param.typ.skipTypes({tyGenericInst, tyAlias, tySink}), params[i])
|
||||
|
||||
let body = p(n, c)
|
||||
|
||||
@@ -86,7 +86,7 @@ proc codeListing(c: ControlFlowGraph, result: var string, start=0; last = -1) =
|
||||
result.add("\n")
|
||||
inc i
|
||||
if i in jumpTargets: result.add("L" & $i & ": End\n")
|
||||
|
||||
# consider calling `asciitables.alignTable`
|
||||
|
||||
proc echoCfg*(c: ControlFlowGraph; start=0; last = -1) {.deprecated.} =
|
||||
## echos the ControlFlowGraph for debugging purposes.
|
||||
|
||||
@@ -40,6 +40,7 @@ type
|
||||
# already. See bug #3655
|
||||
destFile*: AbsoluteFile
|
||||
thisDir*: AbsoluteDir
|
||||
examples: string
|
||||
|
||||
PDoc* = ref TDocumentor ## Alias to type less.
|
||||
|
||||
@@ -118,7 +119,7 @@ proc newDocumentor*(filename: AbsoluteFile; cache: IdentCache; conf: ConfigRef,
|
||||
result.conf = conf
|
||||
result.cache = cache
|
||||
initRstGenerator(result[], (if conf.cmd != cmdRst2tex: outHtml else: outLatex),
|
||||
conf.configVars, filename.string, {roSupportRawDirective},
|
||||
conf.configVars, filename.string, {roSupportRawDirective, roSupportMarkdown},
|
||||
docgenFindFile, compilerMsgHandler)
|
||||
|
||||
if conf.configVars.hasKey("doc.googleAnalytics"):
|
||||
@@ -378,6 +379,14 @@ proc testExample(d: PDoc; ex: PNode) =
|
||||
"_examples" & $d.exampleCounter & ".nim"))
|
||||
#let nimcache = outp.changeFileExt"" & "_nimcache"
|
||||
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"
|
||||
elif isDefined(d.conf, "cpp"): "cpp"
|
||||
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)
|
||||
|
||||
proc prepareExamples(d: PDoc; n: PNode) =
|
||||
|
||||
var docComment = newTree(nkCommentStmt)
|
||||
let loc = d.conf.toFileLineCol(n.info)
|
||||
docComment.comment = "autogenerated by docgen from " & loc
|
||||
|
||||
var runnableExamples = newTree(nkStmtList,
|
||||
docComment,
|
||||
newTree(nkImportStmt, newStrNode(nkStrLit, d.filename)))
|
||||
@@ -935,6 +942,7 @@ proc generateIndex*(d: PDoc) =
|
||||
writeIndexFile(d[], dest.string)
|
||||
|
||||
proc writeOutput*(d: PDoc, useWarning = false) =
|
||||
runAllExamples(d)
|
||||
var content = genOutFile(d)
|
||||
if optStdout in d.conf.globalOptions:
|
||||
writeRope(stdout, content)
|
||||
@@ -947,6 +955,7 @@ proc writeOutput*(d: PDoc, useWarning = false) =
|
||||
outfile.string)
|
||||
|
||||
proc writeOutputJson*(d: PDoc, useWarning = false) =
|
||||
runAllExamples(d)
|
||||
var modDesc: string
|
||||
for desc in d.modDesc:
|
||||
modDesc &= desc
|
||||
@@ -982,7 +991,7 @@ proc commandRstAux(cache: IdentCache, conf: ConfigRef;
|
||||
|
||||
d.isPureRst = true
|
||||
var rst = parseRst(readFile(filen.string), filen.string, 0, 1, d.hasToc,
|
||||
{roSupportRawDirective}, conf)
|
||||
{roSupportRawDirective, roSupportMarkdown}, conf)
|
||||
var modDesc = newStringOfCap(30_000)
|
||||
renderRstToOut(d[], rst, modDesc)
|
||||
d.modDesc = rope(modDesc)
|
||||
|
||||
@@ -63,8 +63,8 @@ compiler gcc:
|
||||
result = (
|
||||
name: "gcc",
|
||||
objExt: "o",
|
||||
optSpeed: " -O3 -ffast-math ",
|
||||
optSize: " -Os -ffast-math ",
|
||||
optSpeed: " -O3 ",
|
||||
optSize: " -Os ",
|
||||
compilerExe: "gcc",
|
||||
cppCompiler: "g++",
|
||||
compileTmpl: "-c $options $include -o $objfile $file",
|
||||
@@ -88,8 +88,8 @@ compiler nintendoSwitchGCC:
|
||||
result = (
|
||||
name: "switch_gcc",
|
||||
objExt: "o",
|
||||
optSpeed: " -O3 -ffast-math ",
|
||||
optSize: " -Os -ffast-math ",
|
||||
optSpeed: " -O3 ",
|
||||
optSize: " -Os ",
|
||||
compilerExe: "aarch64-none-elf-gcc",
|
||||
cppCompiler: "aarch64-none-elf-g++",
|
||||
compileTmpl: "-w -MMD -MP -MF $dfile -c $options $include -o $objfile $file",
|
||||
@@ -153,6 +153,13 @@ compiler vcc:
|
||||
structStmtFmt: "$3$n$1 $2",
|
||||
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
|
||||
compiler icl:
|
||||
result = vcc()
|
||||
@@ -353,7 +360,8 @@ const
|
||||
pcc(),
|
||||
ucc(),
|
||||
icl(),
|
||||
icc()]
|
||||
icc(),
|
||||
clangcl()]
|
||||
|
||||
hExt* = ".h"
|
||||
|
||||
@@ -567,6 +575,8 @@ proc getCompileCFileCmd*(conf: ConfigRef; cfile: Cfile): string =
|
||||
includeCmd = ""
|
||||
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)
|
||||
else: cfile.cname
|
||||
|
||||
|
||||
@@ -257,9 +257,9 @@ proc canon*(n: PNode; o: Operators): PNode =
|
||||
for i in 0 ..< n.len:
|
||||
result.sons[i] = canon(n.sons[i], o)
|
||||
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):
|
||||
result = n.sym.ast.copyTree
|
||||
result = n.sym.astdef.copyTree
|
||||
else:
|
||||
result = n
|
||||
case result.getMagic
|
||||
@@ -395,8 +395,8 @@ proc usefulFact(n: PNode; o: Operators): PNode =
|
||||
# if a:
|
||||
# ...
|
||||
# We make can easily replace 'a' by '2 < x' here:
|
||||
if n.sym.ast != nil:
|
||||
result = usefulFact(n.sym.ast, o)
|
||||
if n.sym.astdef != nil:
|
||||
result = usefulFact(n.sym.astdef, o)
|
||||
elif n.kind == nkStmtListExpr:
|
||||
result = usefulFact(n.lastSon, o)
|
||||
|
||||
|
||||
@@ -92,6 +92,7 @@ proc importSymbol(c: PContext, n: PNode, fromMod: PSym) =
|
||||
rawImportSymbol(c, e)
|
||||
e = nextIdentIter(it, fromMod.tab)
|
||||
else: rawImportSymbol(c, s)
|
||||
suggestSym(c.config, n.info, s, c.graph.usageSym, false)
|
||||
|
||||
proc importAllSymbolsExcept(c: PContext, fromMod: PSym, exceptSet: IntSet) =
|
||||
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")
|
||||
if sfDeprecated in result.flags:
|
||||
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:
|
||||
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)
|
||||
importStmtResult.add newSymNode(result, n.info)
|
||||
#newStrNode(toFullPath(c.config, f), n.info)
|
||||
|
||||
@@ -94,6 +94,7 @@ type
|
||||
options: TOptions
|
||||
module: BModule
|
||||
g: PGlobals
|
||||
generatedParamCopies: IntSet
|
||||
beforeRetNeeded: bool
|
||||
unique: int # for temp identifier generation
|
||||
blocks: seq[TBlock]
|
||||
@@ -918,35 +919,15 @@ proc countJsParams(typ: PType): int =
|
||||
const
|
||||
nodeKindsNeedNoCopy = {nkCharLit..nkInt64Lit, nkStrLit..nkTripleStrLit,
|
||||
nkFloatLit..nkFloat64Lit, nkCurly, nkPar, nkStringToCString,
|
||||
nkObjConstr, nkTupleConstr, nkBracket,
|
||||
nkCStringToString, nkCall, nkPrefix, nkPostfix, nkInfix,
|
||||
nkCommand, nkHiddenCallConv, nkCallStrLit}
|
||||
|
||||
proc needsNoCopy(p: PProc; y: PNode): bool =
|
||||
# if the node is a literal object constructor we have to recursively
|
||||
# check the expressions passed into it
|
||||
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
|
||||
{tyRef, tyPtr, tyLent, tyVar, tyCString, tyProc} + IntegralTypes))
|
||||
return true
|
||||
return y.kind in nodeKindsNeedNoCopy or
|
||||
((mapType(y.typ) != etyBaseIndex or (y.kind == nkSym and y.sym.kind == skParam)) and
|
||||
(skipTypes(y.typ, abstractInst).kind in
|
||||
{tyRef, tyPtr, tyLent, tyVar, tyCString, tyProc} + IntegralTypes))
|
||||
|
||||
proc genAsgnAux(p: PProc, x, y: PNode, noCopyNeeded: bool) =
|
||||
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",
|
||||
[a.rdLoc, b.res, genTypeInfo(p, y.typ)])
|
||||
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])
|
||||
else:
|
||||
useMagic(p, "nimCopy")
|
||||
@@ -1252,12 +1233,29 @@ proc genProcForSymIfNeeded(p: PProc, s: PSym) =
|
||||
if owner != nil: add(owner.locals, newp)
|
||||
else: attachProc(p, newp, s)
|
||||
|
||||
proc genCopyForParamIfNeeded(p: PProc, n: PNode) =
|
||||
let s = n.sym
|
||||
if p.prc == s.owner or needsNoCopy(p, n):
|
||||
return
|
||||
var owner = p.up
|
||||
while true:
|
||||
if owner == nil:
|
||||
internalError(p.config, n.info, "couldn't find the owner proc of the closed over param: " & s.name.s)
|
||||
if owner.prc == s.owner:
|
||||
if not owner.generatedParamCopies.containsOrIncl(s.id):
|
||||
let copy = "$1 = nimCopy(null, $1, $2);$n" % [s.loc.r, genTypeInfo(p, s.typ)]
|
||||
add(owner.locals, owner.indentLine(copy))
|
||||
return
|
||||
owner = owner.up
|
||||
|
||||
proc genSym(p: PProc, n: PNode, r: var TCompRes) =
|
||||
var s = n.sym
|
||||
case s.kind
|
||||
of skVar, skLet, skParam, skTemp, skResult, skForVar:
|
||||
if s.loc.r == nil:
|
||||
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)
|
||||
if k == 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):
|
||||
createRecordVarAux(p, lastSon(rec.sons[i]), excludedFieldIDs, output)
|
||||
of nkSym:
|
||||
# Do not produce code for void types
|
||||
if isEmptyType(rec.sym.typ): return
|
||||
if rec.sym.id notin excludedFieldIDs:
|
||||
if output.len > 0: output.add(", ")
|
||||
output.addf("$#: ", [mangleName(p.module, rec.sym)])
|
||||
@@ -1886,12 +1886,13 @@ proc genMagic(p: PProc, n: PNode, r: var TCompRes) =
|
||||
of mLtStr:
|
||||
binaryExpr(p, n, r, "cmpStrings", "(cmpStrings($1, $2) < 0)")
|
||||
of mIsNil:
|
||||
# we want to accept undefined, so we ==
|
||||
if mapType(n[1].typ) != etyBaseIndex:
|
||||
unaryExpr(p, n, r, "", "($1 === null)")
|
||||
unaryExpr(p, n, r, "", "($1 == null)")
|
||||
else:
|
||||
var x: TCompRes
|
||||
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 mNew, mNewFinalize: genNew(p, n)
|
||||
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")
|
||||
else: binaryExpr(p, n, r, "subInt", "$1 = subInt($3, $2)")
|
||||
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:
|
||||
var x, y: TCompRes
|
||||
gen(p, n.sons[1], x)
|
||||
@@ -2005,6 +2006,10 @@ proc genArrayConstr(p: PProc, n: PNode, r: var TCompRes) =
|
||||
if a.typ == etyBaseIndex:
|
||||
addf(r.res, "[$1, $2]", [a.address, a.res])
|
||||
else:
|
||||
if not needsNoCopy(p, n[i]):
|
||||
let typ = n[i].typ.skipTypes(abstractInst)
|
||||
useMagic(p, "nimCopy")
|
||||
a.res = "nimCopy(null, $1, $2)" % [a.rdLoc, genTypeInfo(p, typ)]
|
||||
add(r.res, a.res)
|
||||
add(r.res, "]")
|
||||
|
||||
@@ -2017,9 +2022,13 @@ proc genTupleConstr(p: PProc, n: PNode, r: var TCompRes) =
|
||||
var it = n.sons[i]
|
||||
if it.kind == nkExprColonExpr: it = it.sons[1]
|
||||
gen(p, it, a)
|
||||
let typ = it.typ.skipTypes(abstractInst)
|
||||
if a.typ == etyBaseIndex:
|
||||
addf(r.res, "Field$#: [$#, $#]", [i.rope, a.address, a.res])
|
||||
else:
|
||||
if not needsNoCopy(p, it):
|
||||
useMagic(p, "nimCopy")
|
||||
a.res = "nimCopy(null, $1, $2)" % [a.rdLoc, genTypeInfo(p, typ)]
|
||||
addf(r.res, "Field$#: $#", [i.rope, a.res])
|
||||
r.res.add("}")
|
||||
|
||||
@@ -2039,17 +2048,12 @@ proc genObjConstr(p: PProc, n: PNode, r: var TCompRes) =
|
||||
fieldIDs.incl(f.id)
|
||||
|
||||
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:
|
||||
addf(initList, "$#: [$#, $#]", [f.loc.r, a.address, a.res])
|
||||
else:
|
||||
if not needsNoCopy(p, val):
|
||||
useMagic(p, "nimCopy")
|
||||
a.res = "nimCopy(null, $1, $2)" % [a.rdLoc, genTypeInfo(p, typ)]
|
||||
addf(initList, "$#: $#", [f.loc.r, a.res])
|
||||
let t = skipTypes(n.typ, abstractInst + skipPtrs)
|
||||
createObjInitList(p, t, fieldIDs, initList)
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
|
||||
import
|
||||
hashes, options, msgs, strutils, platform, idents, nimlexbase, llstream,
|
||||
wordrecg, lineinfos, pathutils
|
||||
wordrecg, lineinfos, pathutils, parseutils
|
||||
|
||||
const
|
||||
MaxLineLength* = 80 # lines longer than this lead to a warning
|
||||
@@ -307,20 +307,6 @@ template tokenEndPrevious(tok, pos) =
|
||||
when defined(nimpretty):
|
||||
tok.offsetB = L.offsetBase + pos
|
||||
|
||||
{.push overflowChecks: off.}
|
||||
# We need to parse the largest uint literal without overflow checks
|
||||
proc unsafeParseUInt(s: string, b: var BiggestInt, start = 0): int =
|
||||
var i = start
|
||||
if 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) =
|
||||
add(t.literal, replacementChar)
|
||||
inc(L.bufpos)
|
||||
@@ -586,33 +572,43 @@ proc getNumber(L: var TLexer, result: var TToken) =
|
||||
of floatTypes:
|
||||
result.fNumber = parseFloat(result.literal)
|
||||
of tkUint64Lit:
|
||||
xi = 0
|
||||
let len = unsafeParseUInt(result.literal, xi)
|
||||
if len != result.literal.len or len == 0:
|
||||
raise newException(ValueError, "invalid integer: " & $xi)
|
||||
result.iNumber = xi
|
||||
var iNumber: uint64
|
||||
var len: int
|
||||
try:
|
||||
len = parseBiggestUInt(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 = cast[int64](iNumber)
|
||||
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 =
|
||||
case result.tokType
|
||||
of tkInt8Lit: (result.iNumber < int8.low or result.iNumber > int8.high)
|
||||
of tkUInt8Lit: (result.iNumber < BiggestInt(uint8.low) or
|
||||
result.iNumber > BiggestInt(uint8.high))
|
||||
of tkInt16Lit: (result.iNumber < int16.low or result.iNumber > int16.high)
|
||||
of tkUInt16Lit: (result.iNumber < BiggestInt(uint16.low) or
|
||||
result.iNumber > BiggestInt(uint16.high))
|
||||
of tkInt32Lit: (result.iNumber < int32.low or result.iNumber > int32.high)
|
||||
of tkUInt32Lit: (result.iNumber < BiggestInt(uint32.low) or
|
||||
result.iNumber > BiggestInt(uint32.high))
|
||||
of tkInt8Lit: result.iNumber > int8.high
|
||||
of tkUInt8Lit: result.iNumber > BiggestInt(uint8.high)
|
||||
of tkInt16Lit: result.iNumber > int16.high
|
||||
of tkUInt16Lit: result.iNumber > BiggestInt(uint16.high)
|
||||
of tkInt32Lit: result.iNumber > int32.high
|
||||
of tkUInt32Lit: result.iNumber > BiggestInt(uint32.high)
|
||||
else: false
|
||||
|
||||
if outOfRange: lexMessageLitNum(L, "number out of range: '$1'", startpos)
|
||||
|
||||
# Promote int literal to int64? Not always necessary, but more consistent
|
||||
if result.tokType == tkIntLit:
|
||||
if (result.iNumber < low(int32)) or (result.iNumber > high(int32)):
|
||||
if result.iNumber > high(int32):
|
||||
result.tokType = tkInt64Lit
|
||||
|
||||
except ValueError:
|
||||
|
||||
@@ -65,7 +65,7 @@ const
|
||||
warnOctalEscape: "octal escape sequences do not exist; leading zero is ignored",
|
||||
warnXIsNeverRead: "'$1' is never read",
|
||||
warnXmightNotBeenInit: "'$1' might not have been initialized",
|
||||
warnDeprecated: "$1 is deprecated",
|
||||
warnDeprecated: "$1",
|
||||
warnConfigDeprecated: "config file '$1' is deprecated",
|
||||
warnSmallLshouldNotBeUsed: "'l' should not be used as an identifier; may look like '1' (one)",
|
||||
warnUnknownMagic: "unknown magic '$1' might crash the compiler",
|
||||
@@ -171,7 +171,7 @@ proc computeNotesVerbosity(): array[0..3, TNoteKinds] =
|
||||
warnGcUnsafe, hintPath, hintDependency, hintCodeBegin, hintCodeEnd,
|
||||
hintSource, hintGlobalVar, hintGCStats}
|
||||
result[0] = result[1] - {hintSuccessX, hintSuccess, hintConf,
|
||||
hintProcessing, hintPattern, hintExecuting, hintLinking}
|
||||
hintProcessing, hintPattern, hintExecuting, hintLinking, hintCC}
|
||||
|
||||
const
|
||||
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)
|
||||
else:
|
||||
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 =
|
||||
result = strTableGet(c.currentScope.symbols, s)
|
||||
@@ -150,7 +150,7 @@ type
|
||||
|
||||
proc getSymRepr*(conf: ConfigRef; s: PSym): string =
|
||||
case s.kind
|
||||
of skProc, skFunc, skMethod, skConverter, skIterator:
|
||||
of routineKinds, skType:
|
||||
result = getProcHeader(conf, s)
|
||||
else:
|
||||
result = s.name.s
|
||||
@@ -174,7 +174,7 @@ proc ensureNoMissingOrUnusedSymbols(c: PContext; scope: PScope) =
|
||||
# maybe they can be made skGenericParam as well.
|
||||
if s.typ != nil and tfImplicitTypeParam notin s.typ.flags and
|
||||
s.typ.kind != tyGenericParam:
|
||||
message(c.config, s.info, hintXDeclaredButNotUsed, getSymRepr(c.config, s))
|
||||
message(c.config, s.info, hintXDeclaredButNotUsed, s.name.s)
|
||||
s = nextIter(it, scope.symbols)
|
||||
|
||||
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)
|
||||
|
||||
proc addDecl*(c: PContext, sym: PSym) =
|
||||
let conflict = c.currentScope.addUniqueSym(sym)
|
||||
let conflict = strTableInclReportConflict(c.currentScope.symbols, sym, true)
|
||||
if conflict != nil:
|
||||
wrongRedefinition(c, sym.info, sym.name.s, conflict.info)
|
||||
|
||||
|
||||
@@ -76,6 +76,8 @@ proc commandCompileToC(graph: ModuleGraph) =
|
||||
registerPass(graph, cgenPass)
|
||||
|
||||
compileProject(graph)
|
||||
if graph.config.errorCounter > 0:
|
||||
return # issue #9933
|
||||
cgenWriteModules(graph.backend, conf)
|
||||
if conf.cmd != cmdRun:
|
||||
let proj = changeFileExt(conf.projectFull, "")
|
||||
@@ -90,6 +92,7 @@ proc commandJsonScript(graph: ModuleGraph) =
|
||||
|
||||
when not defined(leanCompiler):
|
||||
proc commandCompileToJS(graph: ModuleGraph) =
|
||||
let conf = graph.config
|
||||
#incl(gGlobalOptions, optSafeCode)
|
||||
setTarget(graph.config.target, osJS, cpuJS)
|
||||
#initDefines()
|
||||
@@ -98,6 +101,8 @@ when not defined(leanCompiler):
|
||||
semanticPasses(graph)
|
||||
registerPass(graph, JSgenPass)
|
||||
compileProject(graph)
|
||||
if optGenScript in graph.config.globalOptions:
|
||||
writeDepsFile(graph, toGeneratedFile(conf, conf.projectFull, ""))
|
||||
|
||||
proc interactivePasses(graph: ModuleGraph) =
|
||||
initDefines(graph.config.symbols)
|
||||
@@ -165,6 +170,7 @@ proc mainCommand*(graph: ModuleGraph) =
|
||||
of "c", "cc", "compile", "compiletoc":
|
||||
# compile means compileToC currently
|
||||
conf.cmd = cmdCompileToC
|
||||
defineSymbol(graph.config.symbols, "c")
|
||||
commandCompileToC(graph)
|
||||
of "cpp", "compiletocpp":
|
||||
conf.cmd = cmdCompileToCpp
|
||||
@@ -281,6 +287,7 @@ proc mainCommand*(graph: ModuleGraph) =
|
||||
(key: "defined_symbols", val: definedSymbols),
|
||||
(key: "lib_paths", val: %libpaths),
|
||||
(key: "out", val: %conf.outFile.string),
|
||||
(key: "nimcache", val: %getNimcacheDir(conf).string),
|
||||
(key: "hints", val: hints),
|
||||
(key: "warnings", val: warnings),
|
||||
]
|
||||
|
||||
@@ -147,7 +147,7 @@ proc getModuleName*(conf: ConfigRef; n: PNode): string =
|
||||
# hacky way to implement 'x / y /../ z':
|
||||
result = renderTree(n, {renderNoComments}).replace(" ")
|
||||
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(".", "/")
|
||||
of nkImportAs:
|
||||
result = getModuleName(conf, n.sons[0])
|
||||
|
||||
@@ -135,6 +135,10 @@ const
|
||||
WarningColor = fgYellow
|
||||
HintTitle = "Hint: "
|
||||
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 setInfoContextLen*(conf: ConfigRef; L: int) = setLen(conf.m.msgContext, L)
|
||||
@@ -155,7 +159,10 @@ template toFilename*(conf: ConfigRef; fileIdx: FileIndex): string =
|
||||
if fileIdx.int32 < 0 or conf == nil:
|
||||
"???"
|
||||
else:
|
||||
conf.m.fileInfos[fileIdx.int32].projPath.string
|
||||
if optListFullPaths in conf.globalOptions:
|
||||
conf.m.fileInfos[fileIdx.int32].fullPath.string
|
||||
else:
|
||||
conf.m.fileInfos[fileIdx.int32].projPath.string
|
||||
|
||||
proc toFullPath*(conf: ConfigRef; fileIdx: FileIndex): string =
|
||||
if fileIdx.int32 < 0 or conf == nil: result = "???"
|
||||
@@ -192,10 +199,10 @@ proc toMsgFilename*(conf: ConfigRef; info: TLineInfo): string =
|
||||
result = "???"
|
||||
return
|
||||
let absPath = conf.m.fileInfos[info.fileIndex.int32].fullPath.string
|
||||
let relPath = conf.m.fileInfos[info.fileIndex.int32].projPath.string
|
||||
if optListFullPaths in conf.globalOptions:
|
||||
result = absPath
|
||||
else:
|
||||
let relPath = conf.m.fileInfos[info.fileIndex.int32].projPath.string
|
||||
result = if absPath.len < relPath.len: absPath else: relPath
|
||||
|
||||
proc toLinenumber*(info: TLineInfo): int {.inline.} =
|
||||
@@ -208,7 +215,9 @@ proc toFileLine*(conf: ConfigRef; info: TLineInfo): string {.inline.} =
|
||||
result = toFilename(conf, info) & ":" & $info.line
|
||||
|
||||
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)
|
||||
|
||||
@@ -359,7 +368,7 @@ proc writeContext(conf: ConfigRef; lastinfo: TLineInfo) =
|
||||
styledMsgWriteln(styleBright,
|
||||
PosFormat % [toMsgFilename(conf, context.info),
|
||||
coordToStr(context.info.line.int),
|
||||
coordToStr(context.info.col+1)],
|
||||
coordToStr(context.info.col+ColOffset)],
|
||||
resetStyle,
|
||||
message)
|
||||
info = context.info
|
||||
@@ -446,7 +455,7 @@ proc formatMsg*(conf: ConfigRef; info: TLineInfo, msg: TMsgKind, arg: string): s
|
||||
of hintMin..hintMax: HintTitle
|
||||
else: ErrorTitle
|
||||
result = PosFormat % [toMsgFilename(conf, info), coordToStr(info.line.int),
|
||||
coordToStr(info.col+1)] &
|
||||
coordToStr(info.col+ColOffset)] &
|
||||
title &
|
||||
getMessageStr(msg, arg)
|
||||
|
||||
@@ -483,11 +492,8 @@ proc liMessage(conf: ConfigRef; info: TLineInfo, msg: TMsgKind, arg: string,
|
||||
color = HintColor
|
||||
if msg != hintUserRaw: kind = HintsToStr[ord(msg) - ord(hintMin)]
|
||||
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),
|
||||
coordToStr(info.col+1)]
|
||||
coordToStr(info.col+ColOffset)]
|
||||
let s = getMessageStr(msg, arg)
|
||||
|
||||
if not ignoreMsg:
|
||||
|
||||
@@ -40,7 +40,8 @@ type # please make sure we have under 32 options
|
||||
optMemTracker,
|
||||
optHotCodeReloading,
|
||||
optLaxStrings,
|
||||
optNilSeqs
|
||||
optNilSeqs,
|
||||
optOldAst
|
||||
|
||||
TOptions* = set[TOption]
|
||||
TGlobalOption* = enum # **keep binary compatible**
|
||||
@@ -77,7 +78,7 @@ type # please make sure we have under 32 options
|
||||
optShowAllMismatches # show all overloading resolution candidates
|
||||
optWholeProject # for 'doc2': output any dependency
|
||||
optMixedMode # true if some module triggered C++ codegen
|
||||
optListFullPaths
|
||||
optListFullPaths # use full paths in toMsgFilename, toFilename
|
||||
optNoNimblePath
|
||||
optDynlibOverrideAll
|
||||
|
||||
@@ -132,7 +133,7 @@ type
|
||||
|
||||
TSystemCC* = enum
|
||||
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
|
||||
Cached, ## no need to recompile this time
|
||||
@@ -482,16 +483,7 @@ proc setDefaultLibpath*(conf: ConfigRef) =
|
||||
conf.libpath = AbsoluteDir parentNimLibPath
|
||||
|
||||
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 {.
|
||||
deprecated: "use 'relativeTo' instead".} =
|
||||
|
||||
@@ -115,20 +115,19 @@ proc compileConstraints(p: PNode, result: var TPatternCode; conf: ConfigRef) =
|
||||
else:
|
||||
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
|
||||
## efficient internal format.
|
||||
assert p.kind == nkCurlyExpr
|
||||
result = newNodeI(nkStrLit, p.info)
|
||||
result = newNodeI(nkStrLit, n.info)
|
||||
result.strVal = newStringOfCap(10)
|
||||
result.strVal.add(chr(aqNone.ord))
|
||||
if p.len >= 2:
|
||||
for i in 1..<p.len:
|
||||
compileConstraints(p.sons[i], result.strVal, conf)
|
||||
if n.len >= 2:
|
||||
for i in start..<n.len:
|
||||
compileConstraints(n[i], result.strVal, conf)
|
||||
if result.strVal.len > MaxStackSize-1:
|
||||
internalError(conf, p.info, "parameter pattern too complex")
|
||||
internalError(conf, n.info, "parameter pattern too complex")
|
||||
else:
|
||||
patternError(p, conf)
|
||||
patternError(n, conf)
|
||||
result.strVal.add(ppEof)
|
||||
|
||||
type
|
||||
|
||||
@@ -332,12 +332,15 @@ proc parseSymbol(p: var TParser, mode = smNormal): PNode =
|
||||
parMessage(p, errIdentifierExpected, p.tok)
|
||||
break
|
||||
of tkOpr, tkDot, tkDotDot, tkEquals, tkParLe..tkParDotRi:
|
||||
let lineinfo = parLineinfo(p)
|
||||
var accm = ""
|
||||
while p.tok.tokType in {tkOpr, tkDot, tkDotDot, tkEquals,
|
||||
tkParLe..tkParDotRi}:
|
||||
accm.add(tokToStr(p.tok))
|
||||
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:
|
||||
result.add(newIdentNodeP(p.lex.cache.getIdent(tokToStr(p.tok)), p))
|
||||
getTok(p)
|
||||
@@ -1764,23 +1767,8 @@ proc parseSection(p: var TParser, kind: TNodeKind,
|
||||
else:
|
||||
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 =
|
||||
#| enum = 'enum' optInd (symbol optInd ('=' optInd expr COMMENT?)? comma?)+
|
||||
#| enum = 'enum' optInd (symbol optPragmas optInd ('=' optInd expr COMMENT?)? comma?)+
|
||||
result = newNodeP(nkEnumTy, p)
|
||||
getTok(p)
|
||||
addSon(result, p.emptyNode)
|
||||
@@ -1790,25 +1778,35 @@ proc parseEnum(p: var TParser): PNode =
|
||||
while true:
|
||||
var a = parseSymbol(p)
|
||||
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:
|
||||
add(result, a)
|
||||
add(result, symPragma)
|
||||
break
|
||||
|
||||
if p.tok.tokType == tkEquals and p.tok.indent < 0:
|
||||
getTok(p)
|
||||
optInd(p, a)
|
||||
var b = a
|
||||
a = newNodeP(nkEnumFieldDef, p)
|
||||
addSon(a, b)
|
||||
addSon(a, parseExpr(p))
|
||||
optInd(p, symPragma)
|
||||
var b = symPragma
|
||||
symPragma = newNodeP(nkEnumFieldDef, p)
|
||||
addSon(symPragma, b)
|
||||
addSon(symPragma, parseExpr(p))
|
||||
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:
|
||||
getTok(p)
|
||||
rawSkipComment(p, a)
|
||||
rawSkipComment(p, symPragma)
|
||||
else:
|
||||
if p.tok.indent < 0 or p.tok.indent >= p.currInd:
|
||||
rawSkipComment(p, a)
|
||||
addSon(result, a)
|
||||
rawSkipComment(p, symPragma)
|
||||
addSon(result, symPragma)
|
||||
if p.tok.indent >= 0 and p.tok.indent <= p.currInd or
|
||||
p.tok.tokType == tkEof:
|
||||
break
|
||||
@@ -1920,6 +1918,8 @@ proc parseObject(p: var TParser): PNode =
|
||||
result = newNodeP(nkObjectTy, p)
|
||||
getTok(p)
|
||||
if p.tok.tokType == tkCurlyDotLe and p.validInd:
|
||||
# Deprecated since v0.20.0
|
||||
parMessage(p, warnDeprecated, "type pragmas follow the type name; this form of writing pragmas is deprecated")
|
||||
addSon(result, parsePragma(p))
|
||||
else:
|
||||
addSon(result, p.emptyNode)
|
||||
@@ -1992,13 +1992,42 @@ proc parseTypeClass(p: var TParser): PNode =
|
||||
proc parseTypeDef(p: var TParser): PNode =
|
||||
#|
|
||||
#| typeDef = identWithPragmaDot genericParamList? '=' optInd typeDefAux
|
||||
#| indAndComment? / identVisDot genericParamList? pragma '=' optInd typeDefAux
|
||||
#| indAndComment?
|
||||
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:
|
||||
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:
|
||||
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:
|
||||
result.info = parLineInfo(p)
|
||||
getTok(p)
|
||||
@@ -2035,6 +2064,23 @@ proc parseVariable(p: var TParser): PNode =
|
||||
result[^1] = postExprBlocks(p, result[^1])
|
||||
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 =
|
||||
#| bindStmt = 'bind' 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):
|
||||
# implicit imports should not lead to a module importing itself
|
||||
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)
|
||||
str.info = gCmdLineInfo
|
||||
str.info = m.info
|
||||
importStmt.addSon str
|
||||
if not processTopLevelStmt(graph, importStmt, a): break
|
||||
|
||||
@@ -155,7 +155,7 @@ proc processModule*(graph: ModuleGraph; module: PSym, stream: PLLStream): bool {
|
||||
while true:
|
||||
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
|
||||
# modules to include between compilation runs? we'd need to track that
|
||||
# in ROD files. I think we should enable this feature only
|
||||
|
||||
@@ -17,11 +17,9 @@ type
|
||||
AbsoluteDir* = distinct string
|
||||
RelativeFile* = distinct string
|
||||
RelativeDir* = distinct string
|
||||
AnyPath* = AbsoluteFile|AbsoluteDir|RelativeFile|RelativeDir
|
||||
|
||||
proc isEmpty*(x: AbsoluteFile): 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 isEmpty*(x: AnyPath): bool {.inline.} = x.string.len == 0
|
||||
|
||||
proc copyFile*(source, dest: AbsoluteFile) =
|
||||
os.copyFile(source.string, dest.string)
|
||||
@@ -44,14 +42,13 @@ proc cmpPaths*(x, y: AbsoluteDir): int {.borrow.}
|
||||
|
||||
proc createDir*(x: AbsoluteDir) {.borrow.}
|
||||
|
||||
proc `$`*(x: AnyPath): string = x.string
|
||||
|
||||
when true:
|
||||
proc eqImpl(x, y: string): bool {.inline.} =
|
||||
result = cmpPaths(x, y) == 0
|
||||
|
||||
proc `==`*(x, y: AbsoluteFile): 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 `==`*[T: AnyPath](x, y: T): bool = eqImpl(x.string, y.string)
|
||||
|
||||
proc `/`*(base: AbsoluteDir; f: RelativeFile): AbsoluteFile =
|
||||
#assert isAbsolute(base.string)
|
||||
@@ -88,6 +85,12 @@ when true:
|
||||
when isMainModule:
|
||||
doAssert AbsoluteDir"/Users/me///" / RelativeFile"z.nim" == AbsoluteFile"/Users/me/z.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):
|
||||
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}
|
||||
forVarPragmas* = {wInject, wGensym}
|
||||
allRoutinePragmas* = methodPragmas + iteratorPragmas + lambdaPragmas
|
||||
enumFieldPragmas* = {wDeprecated}
|
||||
|
||||
proc getPragmaVal*(procAst: PNode; name: TSpecialWord): PNode =
|
||||
let p = procAst[pragmasPos]
|
||||
@@ -241,7 +242,7 @@ proc pragmaNoForward(c: PContext, n: PNode; flag=sfNoForward) =
|
||||
# deprecated as of 0.18.1
|
||||
message(c.config, n.info, warnDeprecated,
|
||||
"use {.experimental: \"codeReordering.\".} instead; " &
|
||||
(if flag == sfNoForward: "{.noForward.}" else: "{.reorder.}"))
|
||||
(if flag == sfNoForward: "{.noForward.}" else: "{.reorder.}") & " is deprecated")
|
||||
|
||||
proc processCallConv(c: PContext, n: PNode) =
|
||||
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) =
|
||||
if (n.kind in nkPragmaCallKinds and n.len == 2) and (n[1].kind == nkIdent):
|
||||
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:
|
||||
invalidPragma(c, n)
|
||||
|
||||
proc processUndef(c: PContext, n: PNode) =
|
||||
if (n.kind in nkPragmaCallKinds and n.len == 2) and (n[1].kind == nkIdent):
|
||||
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:
|
||||
invalidPragma(c, n)
|
||||
|
||||
@@ -740,7 +741,7 @@ proc semCustomPragma(c: PContext, n: PNode): PNode =
|
||||
result.kind = n.kind # pragma(arg) -> pragma: arg
|
||||
|
||||
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 key = if it.kind in nkPragmaCallKinds and it.len > 1: it.sons[0] else: it
|
||||
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}:
|
||||
incl(sym.flags, sfImmediate)
|
||||
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)
|
||||
of wDirty:
|
||||
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:
|
||||
sym.flags.incl sfExplain
|
||||
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)
|
||||
incl(sym.flags, sfDeprecated)
|
||||
elif sym != nil and sym.kind != skModule:
|
||||
@@ -1090,10 +1091,10 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int,
|
||||
of wThis:
|
||||
if it.kind in nkPragmaCallKinds and it.len == 2:
|
||||
c.selfName = considerQuotedIdent(c, it[1])
|
||||
message(c.config, n.info, warnDeprecated, "the '.this' pragma")
|
||||
message(c.config, n.info, warnDeprecated, "the '.this' pragma is deprecated")
|
||||
elif it.kind == nkIdent or it.len == 1:
|
||||
c.selfName = getIdent(c.cache, "self")
|
||||
message(c.config, n.info, warnDeprecated, "the '.this' pragma")
|
||||
message(c.config, n.info, warnDeprecated, "the '.this' pragma is deprecated")
|
||||
else:
|
||||
localError(c.config, it.info, "'this' pragma is allowed to have zero or one arguments")
|
||||
of wNoRewrite:
|
||||
@@ -1111,15 +1112,25 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int,
|
||||
else: sym.flags.incl sfUsed
|
||||
of wLiftLocals: discard
|
||||
else: invalidPragma(c, it)
|
||||
elif sym == nil or (sym != nil and sym.kind in {skVar, skLet, skParam,
|
||||
skField, skProc, skFunc, skConverter, skMethod, skType}):
|
||||
n.sons[i] = semCustomPragma(c, it)
|
||||
elif sym != nil:
|
||||
illegalCustomPragma(c, it, sym)
|
||||
elif comesFromPush and whichKeyword(ident) in {wTags, wRaises}:
|
||||
discard "ignore the .push pragma; it doesn't apply"
|
||||
else:
|
||||
invalidPragma(c, it)
|
||||
if sym == nil or (sym != nil and sym.kind in {skVar, skLet, skParam,
|
||||
skField, skProc, skFunc, skConverter, skMethod, skType}):
|
||||
n.sons[i] = semCustomPragma(c, it)
|
||||
elif sym != nil:
|
||||
illegalCustomPragma(c, it, sym)
|
||||
else:
|
||||
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) =
|
||||
var pragmas = copyTree(pragmas)
|
||||
overwriteLineInfo pragmas, n.info
|
||||
if n[pragmasPos].kind == nkEmpty:
|
||||
n[pragmasPos] = pragmas
|
||||
else:
|
||||
@@ -1134,7 +1145,7 @@ proc implicitPragmas*(c: PContext, sym: PSym, n: PNode,
|
||||
pushInfoContext(c.config, n.info)
|
||||
var i = 0
|
||||
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")
|
||||
inc i
|
||||
popInfoContext(c.config)
|
||||
@@ -1163,7 +1174,7 @@ proc pragmaRec(c: PContext, sym: PSym, n: PNode, validPragmas: TSpecialWords) =
|
||||
if n == nil: return
|
||||
var i = 0
|
||||
while i < n.len:
|
||||
if singlePragma(c, sym, n, i, validPragmas): break
|
||||
if singlePragma(c, sym, n, i, validPragmas, false): break
|
||||
inc i
|
||||
|
||||
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) =
|
||||
body
|
||||
|
||||
template cbos(name, body) {.dirty.} =
|
||||
template cbexc(name, exc, body) {.dirty.} =
|
||||
result.registerCallback "stdlib.system." & astToStr(name),
|
||||
proc (a: VmArgs) =
|
||||
errorMsg = ""
|
||||
try:
|
||||
body
|
||||
except OSError:
|
||||
except exc:
|
||||
errorMsg = getCurrentExceptionMsg()
|
||||
|
||||
template cbos(name, body) {.dirty.} =
|
||||
cbexc(name, OSError, body)
|
||||
|
||||
# Idea: Treat link to file as a file, but ignore link to directory to prevent
|
||||
# endless recursions out of the box.
|
||||
cbos listFiles:
|
||||
@@ -63,8 +66,10 @@ proc setupVM*(module: PSym; cache: IdentCache; scriptName: string;
|
||||
os.removeFile getString(a, 0)
|
||||
cbos createDir:
|
||||
os.createDir getString(a, 0)
|
||||
cbos getOsError:
|
||||
setResult(a, errorMsg)
|
||||
|
||||
result.registerCallback "stdlib.system.getError",
|
||||
proc (a: VmArgs) = setResult(a, errorMsg)
|
||||
|
||||
cbos setCurrentDir:
|
||||
os.setCurrentDir getString(a, 0)
|
||||
cbos getCurrentDir:
|
||||
@@ -155,6 +160,12 @@ proc setupVM*(module: PSym; cache: IdentCache; scriptName: string;
|
||||
setResult(a, os.getAppFilename())
|
||||
cbconf cppDefine:
|
||||
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;
|
||||
freshDefines=true; conf: ConfigRef) =
|
||||
|
||||
@@ -556,8 +556,6 @@ proc isEmptyTree(n: PNode): bool =
|
||||
else: result = false
|
||||
|
||||
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 sfSystemModule notin c.module.flags and not isEmptyTree(n):
|
||||
c.importTable.addSym c.graph.systemModule # import the "System" identifier
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
|
||||
type
|
||||
TLiftCtx = object
|
||||
c: PContext
|
||||
graph: ModuleGraph
|
||||
info: TLineInfo # for construction
|
||||
kind: TTypeAttachedOp
|
||||
fn: PSym
|
||||
@@ -22,7 +22,7 @@ type
|
||||
recurse: bool
|
||||
|
||||
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.}
|
||||
|
||||
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) =
|
||||
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]))
|
||||
|
||||
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))
|
||||
of nkNilLit: discard
|
||||
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:
|
||||
liftBodyObj(c, n[0], body, x, y)
|
||||
# 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)
|
||||
caseStmt.add(branch)
|
||||
body.add(caseStmt)
|
||||
localError(c.c.config, c.info, "cannot lift assignment operator to 'case' object")
|
||||
of nkRecList:
|
||||
for t in items(n): liftBodyObj(c, t, body, x, y)
|
||||
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:
|
||||
checkSonsLen(x, 1, c.config)
|
||||
checkSonsLen(x, 1, g.config)
|
||||
result = x.sons[0]
|
||||
else:
|
||||
result = newNodeIT(nkHiddenAddr, x.info, makeVarType(c, x.typ))
|
||||
result = newNodeIT(nkHiddenAddr, x.info, makeVarType(x.typ.owner, x.typ))
|
||||
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:
|
||||
# localError(c.config, x.info, "usage of '$1' is a user-defined error" % op.name.s)
|
||||
result = newNodeI(nkCall, x.info)
|
||||
result.add newSymNode(op)
|
||||
result.add genAddr(c, x)
|
||||
result.add genAddr(g, x)
|
||||
result.add y
|
||||
|
||||
proc newAsgnStmt(le, ri: PNode): PNode =
|
||||
@@ -98,10 +105,10 @@ proc newOpCall(op: PSym; x: PNode): PNode =
|
||||
result.add(newSymNode(op))
|
||||
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.add(newSymNode(op))
|
||||
result.add genAddr(c, x)
|
||||
result.add genAddr(g, x)
|
||||
|
||||
proc newDeepCopyCall(op: PSym; x, y: PNode): PNode =
|
||||
result = newAsgnStmt(x, newOpCall(op, y))
|
||||
@@ -120,13 +127,13 @@ proc considerAsgnOrSink(c: var TLiftCtx; t: PType; body, x, y: PNode;
|
||||
else:
|
||||
op = field
|
||||
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:
|
||||
incl c.fn.flags, sfError
|
||||
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)
|
||||
body.add newAsgnCall(c.c, op, x, y)
|
||||
body.add newAsgnCall(c.graph, op, x, y)
|
||||
result = true
|
||||
|
||||
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:
|
||||
let op = t.destructor
|
||||
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)
|
||||
body.add destructorCall(c.c, op, x)
|
||||
body.add destructorCall(c.graph, op, x)
|
||||
result = true
|
||||
of attachedAsgn:
|
||||
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:
|
||||
let op = t.deepCopy
|
||||
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)
|
||||
body.add newDeepCopyCall(op, x, y)
|
||||
result = true
|
||||
@@ -162,13 +169,13 @@ proc addVar(father, v, value: PNode) =
|
||||
addSon(father, vpart)
|
||||
|
||||
proc declareCounter(c: var TLiftCtx; body: PNode; first: BiggestInt): PNode =
|
||||
var temp = newSym(skTemp, getIdent(c.c.cache, lowerings.genPrefix), c.fn, c.info)
|
||||
temp.typ = getSysType(c.c.graph, body.info, tyInt)
|
||||
var temp = newSym(skTemp, getIdent(c.graph.cache, lowerings.genPrefix), c.fn, c.info)
|
||||
temp.typ = getSysType(c.graph, body.info, tyInt)
|
||||
incl(temp.flags, sfFromGeneric)
|
||||
|
||||
var v = newNodeI(nkVarSection, c.info)
|
||||
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
|
||||
|
||||
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 =
|
||||
result = newNodeI(nkWhileStmt, c.info, 2)
|
||||
let cmp = genBuiltin(c.c.graph, mLeI, "<=", i)
|
||||
cmp.add genHigh(c.c.graph, dest)
|
||||
cmp.typ = getSysType(c.c.graph, c.info, tyBool)
|
||||
let cmp = genBuiltin(c.graph, mLeI, "<=", i)
|
||||
cmp.add genHigh(c.graph, dest)
|
||||
cmp.typ = getSysType(c.graph, c.info, tyBool)
|
||||
result.sons[0] = cmp
|
||||
result.sons[1] = newNodeI(nkStmtList, c.info)
|
||||
|
||||
proc addIncStmt(c: var TLiftCtx; body, i: PNode) =
|
||||
let incCall = genBuiltin(c.c.graph, mInc, "inc", i)
|
||||
incCall.add lowerings.newIntLit(c.c.graph, c.info, 1)
|
||||
let incCall = genBuiltin(c.graph, mInc, "inc", i)
|
||||
incCall.add lowerings.newIntLit(c.graph, c.info, 1)
|
||||
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:
|
||||
result = genBuiltin(c.graph, mNewSeq, "newSeq", x)
|
||||
let lenCall = genBuiltin(c.graph, mLengthSeq, "len", y)
|
||||
lenCall.typ = getSysType(c.graph, x.info, tyInt)
|
||||
result = genBuiltin(g, mNewSeq, "newSeq", x)
|
||||
let lenCall = genBuiltin(g, mLengthSeq, "len", y)
|
||||
lenCall.typ = getSysType(g, x.info, tyInt)
|
||||
result.add lenCall
|
||||
|
||||
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)
|
||||
of tyArray:
|
||||
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 elemType = t.lastSon
|
||||
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:
|
||||
# note that tfHasAsgn is propagated so we need the check on
|
||||
# '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)
|
||||
elif tfHasAsgn in t.flags:
|
||||
if c.kind != attachedDestructor:
|
||||
body.add newSeqCall(c.c, x, y)
|
||||
let i = declareCounter(c, body, firstOrd(c.c.config, t))
|
||||
body.add newSeqCall(c.graph, x, y)
|
||||
let i = declareCounter(c, body, firstOrd(c.graph.config, t))
|
||||
let whileLoop = genWhileLoop(c, i, x)
|
||||
let elemType = t.lastSon
|
||||
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)
|
||||
else:
|
||||
defaultOp(c, t, body, x, y)
|
||||
of tyObject, tyDistinct:
|
||||
of tyObject:
|
||||
if not considerOverloadedOp(c, t, body, x, y):
|
||||
if t.sons[0] != nil:
|
||||
liftBodyAux(c, t.sons[0].skipTypes(skipPtrs), body, x, y)
|
||||
if t.kind == tyObject: liftBodyObj(c, t.n, body, x, y)
|
||||
liftBodyObj(c, t.n, body, x, y)
|
||||
of tyDistinct:
|
||||
if not considerOverloadedOp(c, t, body, x, y):
|
||||
liftBodyAux(c, t.sons[0].skipTypes(skipPtrs), body, x, y)
|
||||
of tyTuple:
|
||||
liftBodyTup(c, t, body, x, y)
|
||||
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:
|
||||
let call = newNodeI(nkCall, c.info, 2)
|
||||
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
|
||||
body.add newAsgnStmt(x, call)
|
||||
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,
|
||||
tyUserTypeClassInst, tyCompositeTypeClass, tyAnd, tyOr, tyNot, tyAnything,
|
||||
tyGenericParam, tyGenericBody, tyNil, tyExpr, tyStmt,
|
||||
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,
|
||||
tyGenericInst, tyStatic, tyVar, tyLent, tyAlias, tySink:
|
||||
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 =
|
||||
result = newType(tyProc, owner)
|
||||
@@ -279,26 +287,54 @@ proc addParam(procType: PType; param: PSym) =
|
||||
addSon(procType.n, newSymNode(param))
|
||||
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 =
|
||||
if typ.kind == tyDistinct:
|
||||
return liftBodyDistinctType(g, typ, kind, info)
|
||||
|
||||
var a: TLiftCtx
|
||||
a.info = info
|
||||
a.c = c
|
||||
a.graph = g
|
||||
a.kind = kind
|
||||
let body = newNodeI(nkStmtList, info)
|
||||
let procname = case kind
|
||||
of attachedAsgn: getIdent(c.cache, "=")
|
||||
of attachedSink: getIdent(c.cache, "=sink")
|
||||
of attachedDeepCopy: getIdent(c.cache, "=deepcopy")
|
||||
of attachedDestructor: getIdent(c.cache, "=destroy")
|
||||
of attachedAsgn: getIdent(g.cache, "=")
|
||||
of attachedSink: getIdent(g.cache, "=sink")
|
||||
of attachedDeepCopy: getIdent(g.cache, "=deepcopy")
|
||||
of attachedDestructor: getIdent(g.cache, "=destroy")
|
||||
|
||||
result = newSym(skProc, procname, typ.owner, info)
|
||||
a.fn = result
|
||||
a.asgnForType = typ
|
||||
|
||||
let dest = newSym(skParam, getIdent(c.cache, "dest"), result, info)
|
||||
let src = newSym(skParam, getIdent(c.cache, "src"), result, info)
|
||||
dest.typ = makeVarType(c, typ)
|
||||
let dest = newSym(skParam, getIdent(g.cache, "dest"), result, info)
|
||||
let src = newSym(skParam, getIdent(g.cache, "src"), result, info)
|
||||
dest.typ = makeVarType(typ.owner, typ)
|
||||
src.typ = typ
|
||||
|
||||
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))
|
||||
# recursion is handled explicitly, do not register the type based operation
|
||||
# before 'liftBodyAux':
|
||||
if c.config.selectedGC == gcDestructors and
|
||||
if g.config.selectedGC == gcDestructors and
|
||||
typ.kind in {tySequence, tyString} and body.len == 0:
|
||||
discard "do not cache it yet"
|
||||
else:
|
||||
@@ -328,17 +364,17 @@ proc liftBody(c: PContext; typ: PType; kind: TTypeAttachedOp;
|
||||
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})
|
||||
result = t.assignment
|
||||
if result.isNil:
|
||||
result = liftBody(c, t, attachedAsgn, info)
|
||||
result = liftBody(g, t, attachedAsgn, info)
|
||||
|
||||
proc overloadedAsgn(c: PContext; dest, src: PNode): PNode =
|
||||
let a = getAsgnOrLiftBody(c, dest.typ, dest.info)
|
||||
result = newAsgnCall(c, a, dest, src)
|
||||
proc overloadedAsgn(g: ModuleGraph; dest, src: PNode): PNode =
|
||||
let a = getAsgnOrLiftBody(g, dest.typ, dest.info)
|
||||
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
|
||||
## to ensure we lift assignment, destructors and moves properly.
|
||||
## 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})
|
||||
# we generate the destructor first so that other operators can depend on it:
|
||||
if typ.destructor == nil:
|
||||
liftBody(c, typ, attachedDestructor, info)
|
||||
liftBody(g, typ, attachedDestructor, info)
|
||||
if typ.assignment == nil:
|
||||
liftBody(c, typ, attachedAsgn, info)
|
||||
liftBody(g, typ, attachedAsgn, info)
|
||||
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
|
||||
|
||||
@@ -287,7 +287,18 @@ proc getMsgDiagnostic(c: PContext, flags: TExprFlags, n, f: PNode): string =
|
||||
|
||||
let ident = considerQuotedIdent(c, f, n).s
|
||||
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:
|
||||
if result.len == 0: result = errUndeclaredRoutine % ident
|
||||
else: result = errBadRoutine % [ident, result]
|
||||
|
||||
@@ -286,6 +286,13 @@ proc makeVarType*(c: PContext, baseType: PType; kind = tyVar): PType =
|
||||
result = newTypeS(kind, c)
|
||||
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 =
|
||||
if typ.kind == tyTypeDesc:
|
||||
result = typ
|
||||
|
||||
@@ -108,6 +108,8 @@ const
|
||||
|
||||
proc checkConvertible(c: PContext, castDest, src: PType): TConvStatus =
|
||||
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:
|
||||
# don't annoy conversions that may be needed on another processor:
|
||||
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:
|
||||
if n[1].kind == nkExprEqExpr and
|
||||
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])
|
||||
if targetType.isMetaType:
|
||||
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)
|
||||
lhsType = n[1].typ
|
||||
else:
|
||||
internalAssert c.config, lhsType.base.kind != tyNone
|
||||
if c.inGenericContext > 0 and lhsType.base.containsGenericType:
|
||||
if lhsType.base.kind == tyNone or
|
||||
(c.inGenericContext > 0 and lhsType.base.containsGenericType):
|
||||
# BUGFIX: don't evaluate this too early: ``T is void``
|
||||
return
|
||||
|
||||
@@ -710,16 +712,20 @@ proc evalAtCompileTime(c: PContext, n: PNode): PNode =
|
||||
let a = getConstExpr(c.module, n.sons[i], c.graph)
|
||||
if a == nil: return n
|
||||
call.add(a)
|
||||
|
||||
#echo "NOW evaluating at compile time: ", call.renderTree
|
||||
if sfCompileTime in callee.flags:
|
||||
result = evalStaticExpr(c.module, c.graph, call, c.p.owner)
|
||||
if result.isNil:
|
||||
localError(c.config, n.info, errCannotInterpretNodeX % renderTree(call))
|
||||
else: result = fixupTypeAfterEval(c, result, n)
|
||||
if c.inStaticContext == 0 or sfNoSideEffect in callee.flags:
|
||||
if sfCompileTime in callee.flags:
|
||||
result = evalStaticExpr(c.module, c.graph, call, c.p.owner)
|
||||
if result.isNil:
|
||||
localError(c.config, n.info, errCannotInterpretNodeX % renderTree(call))
|
||||
else: result = fixupTypeAfterEval(c, result, n)
|
||||
else:
|
||||
result = evalConstExpr(c.module, c.graph, call)
|
||||
if result.isNil: result = n
|
||||
else: result = fixupTypeAfterEval(c, result, n)
|
||||
else:
|
||||
result = evalConstExpr(c.module, c.graph, call)
|
||||
if result.isNil: result = n
|
||||
else: result = fixupTypeAfterEval(c, result, n)
|
||||
result = n
|
||||
#if result != n:
|
||||
# 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)
|
||||
if result.typ != nil and
|
||||
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)
|
||||
if c.matchedConcept == nil:
|
||||
result = evalAtCompileTime(c, result)
|
||||
@@ -913,7 +919,7 @@ proc semExprNoType(c: PContext, n: PNode): PNode =
|
||||
let isPush = hintExtendedContext in c.config.notes
|
||||
if isPush: pushInfoContext(c.config, n.info)
|
||||
result = semExpr(c, n, {efWantStmt})
|
||||
discardCheck(c, result, {})
|
||||
result = discardCheck(c, result, {})
|
||||
if isPush: popInfoContext(c.config)
|
||||
|
||||
proc isTypeExpr(n: PNode): bool =
|
||||
@@ -1039,7 +1045,8 @@ proc semSym(c: PContext, n: PNode, sym: PSym, flags: TExprFlags): PNode =
|
||||
of skConst:
|
||||
markUsed(c.config, n.info, s, c.graph.usageSym)
|
||||
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,
|
||||
tyTuple, tySet, tyUInt..tyUInt64:
|
||||
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 ``[]``.
|
||||
if s.ast.len == 0: result = inlineConst(c, n, s)
|
||||
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:
|
||||
result = newSymNode(s, n.info)
|
||||
of skMacro:
|
||||
@@ -1581,7 +1594,7 @@ proc semAsgn(c: PContext, n: PNode; mode=asgnNormal): PNode =
|
||||
typeMismatch(c.config, n.info, lhs.typ, rhsTyp)
|
||||
|
||||
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)
|
||||
|
||||
fixAbstractType(c, n)
|
||||
@@ -1628,7 +1641,7 @@ proc semProcBody(c: PContext, n: PNode): PNode =
|
||||
a.sons[1] = result
|
||||
result = semAsgn(c, a)
|
||||
else:
|
||||
discardCheck(c, result, {})
|
||||
result = discardCheck(c, result, {})
|
||||
|
||||
if c.p.owner.kind notin {skMacro, skTemplate} and
|
||||
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 =
|
||||
result = newNodeI(nkExportStmt, n.info)
|
||||
|
||||
for i in 0..<n.len:
|
||||
let a = n.sons[i]
|
||||
var o: TOverloadIter
|
||||
@@ -2343,6 +2355,9 @@ proc semExport(c: PContext, n: PNode): PNode =
|
||||
it = nextIter(ti, s.tab)
|
||||
else:
|
||||
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}:
|
||||
result.add(newSymNode(s, a.info))
|
||||
strTableAdd(c.module.tab, s)
|
||||
@@ -2430,7 +2445,7 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
|
||||
result.kind = nkCall
|
||||
result = semExpr(c, result, flags)
|
||||
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)
|
||||
of nkTypeOfExpr, nkTupleTy, nkTupleClassTy, nkRefTy..nkEnumTy, nkStaticTy:
|
||||
if c.matchedConcept != nil and n.len == 1:
|
||||
@@ -2620,6 +2635,8 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
|
||||
of nkStaticStmt:
|
||||
result = semStaticStmt(c, n)
|
||||
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])
|
||||
if not n.sons[0].typ.isEmptyType and not implicitlyDiscardable(n.sons[0]):
|
||||
localError(c.config, n.info, "'defer' takes a 'void' expression")
|
||||
|
||||
@@ -19,6 +19,9 @@ type
|
||||
c: PContext
|
||||
|
||||
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
|
||||
of nkEmpty..pred(nkIdent), succ(nkSym)..nkNilLit: result = n
|
||||
of nkIdent, nkSym:
|
||||
|
||||
@@ -132,7 +132,9 @@ proc ordinalValToString*(a: PNode; g: ModuleGraph): string =
|
||||
return field.name.s
|
||||
else:
|
||||
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:
|
||||
result = $x
|
||||
|
||||
|
||||
@@ -129,14 +129,15 @@ proc evalTypeTrait(c: PContext; traitCall: PNode, operand: PType, context: PSym)
|
||||
template typeWithSonsResult(kind, sons): PNode =
|
||||
newTypeWithSons(context, kind, sons).toNode(traitCall.info)
|
||||
|
||||
case trait.sym.name.s
|
||||
let s = trait.sym.name.s
|
||||
case s
|
||||
of "or", "|":
|
||||
return typeWithSonsResult(tyOr, @[operand, operand2])
|
||||
of "and":
|
||||
return typeWithSonsResult(tyAnd, @[operand, operand2])
|
||||
of "not":
|
||||
return typeWithSonsResult(tyNot, @[operand])
|
||||
of "name":
|
||||
of "name", "$":
|
||||
result = newStrNode(nkStrLit, operand.typeToString(preferTypeName))
|
||||
result.typ = newType(tyString, context)
|
||||
result.info = traitCall.info
|
||||
@@ -160,7 +161,7 @@ proc evalTypeTrait(c: PContext; traitCall: PNode, operand: PType, context: PSym)
|
||||
hasDestructor(t)
|
||||
result = newIntNodeT(ord(not complexObj), traitCall, c.graph)
|
||||
else:
|
||||
localError(c.config, traitCall.info, "unknown trait")
|
||||
localError(c.config, traitCall.info, "unknown trait: " & s)
|
||||
result = newNodeI(nkEmpty, traitCall.info)
|
||||
|
||||
proc semTypeTraits(c: PContext, n: PNode): PNode =
|
||||
|
||||
@@ -95,13 +95,6 @@ proc semWhile(c: PContext, n: PNode; flags: TExprFlags): PNode =
|
||||
elif efInTypeof in flags:
|
||||
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 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)
|
||||
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 result.typ != nil and result.typ.kind notin {tyStmt, tyVoid}:
|
||||
if implicitlyDiscardable(result):
|
||||
var n = newNodeI(nkDiscardStmt, result.info, 1)
|
||||
n[0] = result
|
||||
result = newNode(nkDiscardStmt, result.info, @[result])
|
||||
elif result.typ.kind != tyError and c.config.cmd != cmdInteractive:
|
||||
var n = result
|
||||
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)
|
||||
if isEmptyType(typ) or typ.kind in {tyNil, tyExpr} or
|
||||
(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
|
||||
# propagate any enforced VoidContext:
|
||||
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
|
||||
if isEmptyType(typ) or typ.kind in {tyNil, tyExpr}:
|
||||
discardCheck(c, n.sons[0], flags)
|
||||
for i in 1..n.len-1: discardCheck(c, n.sons[i].lastSon, flags)
|
||||
n.sons[0] = discardCheck(c, n.sons[0], 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:
|
||||
result.typ = c.enforceVoidContext
|
||||
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)
|
||||
for i in 1..last:
|
||||
var it = n.sons[i]
|
||||
@@ -322,14 +318,24 @@ proc semIdentDef(c: PContext, n: PNode, kind: TSymKind): PSym =
|
||||
result = semIdentWithPragma(c, kind, n, {})
|
||||
if result.owner.kind == skModule:
|
||||
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) =
|
||||
if {sfGlobal, sfImportC} * v.flags == {sfGlobal} and
|
||||
{tfNotNil, tfNeedsInit} * v.typ.flags != {}:
|
||||
if v.ast.isNil:
|
||||
if v.astdef.isNil:
|
||||
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)
|
||||
|
||||
include semasgn
|
||||
@@ -470,7 +476,7 @@ proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode =
|
||||
# this can only happen for errornous var statements:
|
||||
if typ == nil: continue
|
||||
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})
|
||||
if a.kind == nkVarTuple:
|
||||
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)
|
||||
if a.kind != nkVarTuple:
|
||||
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)
|
||||
setVarType(c, v, typ)
|
||||
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, copyTree(def))
|
||||
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:
|
||||
if def.kind in {nkPar, nkTupleConstr}: v.ast = def[j]
|
||||
# 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 =
|
||||
result = copyNode(n)
|
||||
inc c.inStaticContext
|
||||
for i in countup(0, sonsLen(n) - 1):
|
||||
var a = n.sons[i]
|
||||
if c.config.cmd == cmdIdeTools: suggestStmt(c, a)
|
||||
if a.kind == nkCommentStmt: continue
|
||||
if a.kind != nkConstDef: illFormedAst(a, c.config)
|
||||
checkSonsLen(a, 3, c.config)
|
||||
var v = semIdentDef(c, a.sons[0], skConst)
|
||||
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)
|
||||
if a.kind notin {nkConstDef, nkVarTuple}: illFormedAst(a, c.config)
|
||||
checkMinSonsLen(a, 3, c.config)
|
||||
var length = sonsLen(a)
|
||||
|
||||
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:
|
||||
localError(c.config, a.sons[2].info, errConstExprExpected)
|
||||
localError(c.config, a.sons[length-1].info, errConstExprExpected)
|
||||
continue
|
||||
# check type compatibility between def.typ and typ:
|
||||
if typ != nil:
|
||||
@@ -567,21 +589,44 @@ proc semConst(c: PContext, n: PNode): PNode =
|
||||
else:
|
||||
typ = def.typ
|
||||
if typ == nil:
|
||||
localError(c.config, a.sons[2].info, errConstExprExpected)
|
||||
localError(c.config, a.sons[length-1].info, errConstExprExpected)
|
||||
continue
|
||||
if typeAllowed(typ, skConst) != nil and def.kind != nkNilLit:
|
||||
localError(c.config, a.info, "invalid type for const: " & typeToString(typ))
|
||||
continue
|
||||
setVarType(c, v, typ)
|
||||
v.ast = def # no need to copy
|
||||
if sfGenSym notin v.flags: addInterfaceDecl(c, v)
|
||||
elif v.owner == nil: v.owner = getCurrOwner(c)
|
||||
var b = newNodeI(nkConstDef, a.info)
|
||||
if importantComments(c.config): b.comment = a.comment
|
||||
addSon(b, newSymNode(v))
|
||||
addSon(b, a.sons[1])
|
||||
addSon(b, copyTree(def))
|
||||
addSon(result, b)
|
||||
|
||||
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)
|
||||
elif v.owner == nil: v.owner = getCurrOwner(c)
|
||||
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
|
||||
addSon(b, newSymNode(v))
|
||||
addSon(b, a.sons[1])
|
||||
addSon(b, copyTree(def))
|
||||
else:
|
||||
setVarType(c, v, typ.sons[j])
|
||||
v.ast = def[j]
|
||||
b.sons[j] = newSymNode(v)
|
||||
addSon(result,b)
|
||||
dec c.inStaticContext
|
||||
|
||||
include semfields
|
||||
|
||||
@@ -637,7 +682,7 @@ proc semForVars(c: PContext, n: PNode; flags: TExprFlags): PNode =
|
||||
openScope(c)
|
||||
n.sons[length-1] = semExprBranch(c, n.sons[length-1], flags)
|
||||
if efInTypeof notin flags:
|
||||
discardCheck(c, n.sons[length-1], flags)
|
||||
n.sons[^1] = discardCheck(c, n.sons[^1], flags)
|
||||
closeScope(c)
|
||||
dec(c.p.nestedLoopCounter)
|
||||
|
||||
@@ -824,7 +869,8 @@ proc semCase(c: PContext, n: PNode; flags: TExprFlags): PNode =
|
||||
closeScope(c)
|
||||
if isEmptyType(typ) or typ.kind in {tyNil, tyExpr} or
|
||||
(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:
|
||||
if typ == c.enforceVoidContext:
|
||||
result.typ = c.enforceVoidContext
|
||||
@@ -1074,6 +1120,8 @@ proc typeSectionRightSidePass(c: PContext, n: PNode) =
|
||||
#debug s.typ
|
||||
s.ast = a
|
||||
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]
|
||||
if aa.kind in {nkRefTy, nkPtrTy} and aa.len == 1 and
|
||||
aa.sons[0].kind == nkObjectTy:
|
||||
@@ -1090,9 +1138,13 @@ proc typeSectionRightSidePass(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:
|
||||
localError(c.config, n.info, errTIsNotAConcreteType % t.typeToString)
|
||||
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)
|
||||
|
||||
if n.isNil: return
|
||||
case n.kind
|
||||
@@ -1107,9 +1159,9 @@ proc checkForMetaFields(c: PContext; n: PNode) =
|
||||
tyProc, tyGenericInvocation, tyGenericInst, tyAlias, tySink:
|
||||
let start = ord(t.kind in {tyGenericInvocation, tyGenericInst})
|
||||
for i in start ..< t.len:
|
||||
checkMeta(t.sons[i])
|
||||
checkMeta(c, n, t.sons[i])
|
||||
else:
|
||||
checkMeta(t)
|
||||
checkMeta(c, n, t)
|
||||
else:
|
||||
internalAssert c.config, false
|
||||
|
||||
@@ -1205,9 +1257,6 @@ proc semTypeSection(c: PContext, n: PNode): PNode =
|
||||
|
||||
proc semParamList(c: PContext, n, genericParams: PNode, s: PSym) =
|
||||
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) =
|
||||
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)
|
||||
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) =
|
||||
case s.name.s.normalize
|
||||
let name = s.name.s.normalize
|
||||
case name
|
||||
of "=destroy":
|
||||
let t = s.typ
|
||||
var noError = false
|
||||
@@ -1436,12 +1492,16 @@ proc semOverride(c: PContext, s: PSym, n: PNode) =
|
||||
elif obj.kind == tyGenericInvocation: obj = obj.sons[0]
|
||||
else: break
|
||||
if obj.kind in {tyObject, tyDistinct, tySequence, tyString}:
|
||||
obj = canonType(c, obj)
|
||||
if obj.destructor.isNil:
|
||||
obj.destructor = s
|
||||
else:
|
||||
localError(c.config, n.info, errGenerated,
|
||||
"cannot bind another '" & s.name.s & "' to: " & typeToString(obj))
|
||||
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:
|
||||
localError(c.config, n.info, errGenerated,
|
||||
"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:
|
||||
localError(c.config, n.info, errGenerated,
|
||||
"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:
|
||||
localError(c.config, n.info, errGenerated,
|
||||
"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]
|
||||
else: break
|
||||
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)
|
||||
if opr[].isNil:
|
||||
opr[] = s
|
||||
else:
|
||||
localError(c.config, n.info, errGenerated,
|
||||
"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
|
||||
if sfSystemModule notin s.owner.flags:
|
||||
localError(c.config, n.info, errGenerated,
|
||||
@@ -1542,7 +1613,7 @@ proc semMethodPrototype(c: PContext; s: PSym; n: PNode) =
|
||||
foundObj = true
|
||||
x.methods.add((col,s))
|
||||
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:
|
||||
# why check for the body? bug #2400 has none. Checking for sfForward makes
|
||||
# no sense either.
|
||||
@@ -1975,7 +2046,7 @@ proc semStmtList(c: PContext, n: PNode, flags: TExprFlags): PNode =
|
||||
n.typ = n.sons[i].typ
|
||||
if not isEmptyType(n.typ): n.kind = nkStmtListExpr
|
||||
elif i != last or voidContext:
|
||||
discardCheck(c, n.sons[i], flags)
|
||||
n.sons[i] = discardCheck(c, n.sons[i], flags)
|
||||
else:
|
||||
n.typ = n.sons[i].typ
|
||||
if not isEmptyType(n.typ): n.kind = nkStmtListExpr
|
||||
|
||||
@@ -80,9 +80,14 @@ proc semEnum(c: PContext, n: PNode, prev: PType): PType =
|
||||
if isPure: initStrTable(symbols)
|
||||
var hasNull = false
|
||||
for i in countup(1, sonsLen(n) - 1):
|
||||
if n.sons[i].kind == nkEmpty: continue
|
||||
case n.sons[i].kind
|
||||
of nkEnumFieldDef:
|
||||
e = newSymS(skEnumField, n.sons[i].sons[0], c)
|
||||
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)
|
||||
var v = semConstExpr(c, n.sons[i].sons[1])
|
||||
var strVal: PNode = nil
|
||||
case skipTypes(v.typ, abstractInst-{tyTypeDesc}).kind
|
||||
@@ -91,7 +96,7 @@ proc semEnum(c: PContext, n: PNode, prev: PType): PType =
|
||||
strVal = v.sons[1] # second tuple part is the string value
|
||||
if skipTypes(strVal.typ, abstractInst).kind in {tyString, tyCString}:
|
||||
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
|
||||
else:
|
||||
localError(c.config, strVal.info, errStringLiteralExpected)
|
||||
@@ -102,7 +107,7 @@ proc semEnum(c: PContext, n: PNode, prev: PType): PType =
|
||||
x = counter
|
||||
else:
|
||||
if not isOrdinalType(v.typ, allowEnumWithHoles=true):
|
||||
localError(c.config, v.info, errOrdinalTypeExpected)
|
||||
localError(c.config, v.info, errOrdinalTypeExpected & "; given: " & typeToString(v.typ, preferDesc))
|
||||
x = getOrdValue(v)
|
||||
if i != 1:
|
||||
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
|
||||
of nkIdent, nkAccQuoted:
|
||||
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:
|
||||
illFormedAst(n[i], c.config)
|
||||
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):
|
||||
wrongRedefinition(c, e.info, e.name.s, conflict.info)
|
||||
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 =
|
||||
result = newOrPrevType(tySet, prev, c)
|
||||
@@ -202,7 +210,7 @@ proc semAnyRef(c: PContext; n: PNode; kind: TTypeKind; prev: PType): PType =
|
||||
tyError, tyObject}:
|
||||
message c.config, n[i].info, errGenerated, "region needs to be an object type"
|
||||
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, t)
|
||||
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):
|
||||
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,
|
||||
father: PNode, rectype: PType, hasCaseFields = false)
|
||||
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
|
||||
incl(a.sons[0].sym.flags, sfDiscriminant)
|
||||
var covered: BiggestInt = 0
|
||||
var chckCovered = false
|
||||
var typ = skipTypes(a.sons[0].typ, abstractVar-{tyTypeDesc})
|
||||
if not isOrdinalType(typ):
|
||||
localError(c.config, n.info, "selector must be of an ordinal type")
|
||||
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
|
||||
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):
|
||||
localError(c.config, n.info, "selector must be of an ordinal type, float or string")
|
||||
for i in countup(1, sonsLen(n) - 1):
|
||||
var b = copyTree(n.sons[i])
|
||||
addSon(a, b)
|
||||
@@ -620,12 +636,14 @@ proc semRecordCase(c: PContext, n: PNode, check: var IntSet, pos: var int,
|
||||
checkMinSonsLen(b, 2, c.config)
|
||||
semCaseBranch(c, a, b, i, covered)
|
||||
of nkElse:
|
||||
chckCovered = false
|
||||
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)
|
||||
delSon(b, sonsLen(b) - 1)
|
||||
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")
|
||||
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 =
|
||||
if n.kind == nkCurlyExpr:
|
||||
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:
|
||||
result = semTypeNode(c, n, nil)
|
||||
|
||||
@@ -1054,6 +1077,11 @@ proc semProcTypeNode(c: PContext, n, genericParams: PNode,
|
||||
|
||||
if hasType:
|
||||
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:
|
||||
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
|
||||
# compiler only checks for 'nil':
|
||||
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:
|
||||
if r.kind == tyAnything:
|
||||
elif r.kind == tyAnything:
|
||||
# 'p(): auto' and 'p(): expr' are equivalent, but the rest of the
|
||||
# compiler is hardly aware of 'auto':
|
||||
r = newTypeS(tyExpr, c)
|
||||
elif r.kind != tyExpr:
|
||||
else:
|
||||
if r.sym == nil or sfAnon notin r.sym.flags:
|
||||
let lifted = liftParamType(c, kind, genericParams, r, "result",
|
||||
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:
|
||||
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 =
|
||||
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])
|
||||
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 =
|
||||
if n.kind == nkType:
|
||||
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...
|
||||
result.sons.setLen 0
|
||||
result.n = nil
|
||||
if c.config.selectedGc == gcDestructors:
|
||||
result.flags = {tfHasAsgn}
|
||||
else:
|
||||
result.flags = {}
|
||||
result.flags = {tfHasAsgn}
|
||||
semContainerArg(c, n, "seq", result)
|
||||
else:
|
||||
result = semContainer(c, n, tySequence, "seq", prev)
|
||||
|
||||
@@ -2071,6 +2071,7 @@ proc paramTypesMatchAux(m: var TCandidate, f, a: PType,
|
||||
# constructor in a call:
|
||||
if result == nil and f.kind == tyVarargs:
|
||||
if f.n != nil:
|
||||
# Forward to the varargs converter
|
||||
result = localConvMatch(c, m, f, a, arg)
|
||||
else:
|
||||
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
|
||||
elif r == isSubtype:
|
||||
inc(m.subtypeMatches)
|
||||
if f.kind == tyTypeDesc:
|
||||
if base(f).kind == tyTypeDesc:
|
||||
result = arg
|
||||
else:
|
||||
result = implicitConv(nkHiddenSubConv, f, arg, m, c)
|
||||
result = implicitConv(nkHiddenSubConv, base(f), arg, m, c)
|
||||
m.baseTypeMatch = true
|
||||
else:
|
||||
result = userConvMatch(c, m, base(f), a, arg)
|
||||
@@ -2231,14 +2232,14 @@ proc matchesAux(c: PContext, n, nOrig: PNode,
|
||||
else:
|
||||
m.state = csNoMatch
|
||||
return
|
||||
|
||||
|
||||
if formal.typ.kind == tyVar:
|
||||
let arg_converter = if arg.kind == nkHiddenDeref: arg[0] else: arg
|
||||
if arg_converter.kind == nkHiddenCallConv:
|
||||
if arg_converter.typ.kind != tyVar:
|
||||
let argConverter = if arg.kind == nkHiddenDeref: arg[0] else: arg
|
||||
if argConverter.kind == nkHiddenCallConv:
|
||||
if argConverter.typ.kind != tyVar:
|
||||
m.state = csNoMatch
|
||||
m.mutabilityProblem = uint8(f-1)
|
||||
return
|
||||
return
|
||||
elif not n.isLValue:
|
||||
m.state = csNoMatch
|
||||
m.mutabilityProblem = uint8(f-1)
|
||||
@@ -2267,8 +2268,7 @@ proc matchesAux(c: PContext, n, nOrig: PNode,
|
||||
|
||||
if n.sons[a].kind == nkHiddenStdConv:
|
||||
doAssert n.sons[a].sons[0].kind == nkEmpty and
|
||||
n.sons[a].sons[1].kind == nkArgList and
|
||||
n.sons[a].sons[1].len == 0
|
||||
n.sons[a].sons[1].kind in {nkBracket, nkArgList}
|
||||
# Steal the container and pass it along
|
||||
setSon(m.call, formal.position + 1, n.sons[a].sons[1])
|
||||
else:
|
||||
|
||||
@@ -18,7 +18,7 @@ const
|
||||
szIllegalRecursion* = -2
|
||||
szUncomputedSize* = -1
|
||||
|
||||
proc computeSizeAlign(conf: ConfigRef; typ: PType): void
|
||||
proc computeSizeAlign(conf: ConfigRef; typ: PType)
|
||||
|
||||
proc computeSubObjectAlign(conf: ConfigRef; n: PNode): BiggestInt =
|
||||
## returns object alignment
|
||||
@@ -49,13 +49,14 @@ proc computeSubObjectAlign(conf: ConfigRef; n: PNode): BiggestInt =
|
||||
else:
|
||||
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
|
||||
## ``align`` maximum alignment from all sub nodes
|
||||
assert n != nil
|
||||
if n.typ != nil and n.typ.size == szIllegalRecursion:
|
||||
result.offset = szIllegalRecursion
|
||||
result.align = szIllegalRecursion
|
||||
result.align = szIllegalRecursion
|
||||
return
|
||||
|
||||
result.align = 1
|
||||
@@ -71,66 +72,52 @@ proc computeObjectOffsetsFoldFunction(conf: ConfigRef; n: PNode, initialOffset:
|
||||
of nkOfBranch, nkElse:
|
||||
# offset parameter cannot be known yet, it needs to know the alignment first
|
||||
let align = computeSubObjectAlign(conf, n.sons[i].lastSon)
|
||||
|
||||
if align == szIllegalRecursion:
|
||||
result.offset = szIllegalRecursion
|
||||
result.offset = szIllegalRecursion
|
||||
result.align = szIllegalRecursion
|
||||
return
|
||||
|
||||
if align == szUnknownSize or maxChildAlign == szUnknownSize:
|
||||
maxChildAlign = szUnknownSize
|
||||
else:
|
||||
maxChildAlign = max(maxChildAlign, align)
|
||||
else:
|
||||
internalError(conf, "computeObjectOffsetsFoldFunction(record case branch)")
|
||||
|
||||
if maxChildAlign == szUnknownSize:
|
||||
result.align = szUnknownSize
|
||||
result.offset = szUnknownSize
|
||||
else:
|
||||
# the union neds to be aligned first, before the offsets can be assigned
|
||||
let kindUnionOffset = align(kindOffset, maxChildAlign)
|
||||
|
||||
var maxChildOffset: BiggestInt = 0
|
||||
for i in 1 ..< sonsLen(n):
|
||||
let (offset, align) = computeObjectOffsetsFoldFunction(conf, n.sons[i].lastSon, kindUnionOffset)
|
||||
maxChildOffset = max(maxChildOffset, offset)
|
||||
|
||||
result.align = max(kindAlign, maxChildAlign)
|
||||
result.offset = maxChildOffset
|
||||
|
||||
|
||||
of nkRecList:
|
||||
result.align = 1 # maximum of all member alignments
|
||||
var offset = initialOffset
|
||||
|
||||
for i, child in n.sons:
|
||||
let (new_offset, align) = computeObjectOffsetsFoldFunction(conf, child, offset)
|
||||
|
||||
if new_offset == szIllegalRecursion:
|
||||
result.offset = szIllegalRecursion
|
||||
result.align = szIllegalRecursion
|
||||
return
|
||||
|
||||
elif new_offset == szUnknownSize or offset == szUnknownSize:
|
||||
# if anything is unknown, the rest becomes unknown as well
|
||||
offset = szUnknownSize
|
||||
result.align = szUnknownSize
|
||||
|
||||
else:
|
||||
offset = new_offset
|
||||
result.align = max(result.align, align)
|
||||
|
||||
# final alignment
|
||||
if offset == szUnknownSize:
|
||||
result.offset = szUnknownSize
|
||||
else:
|
||||
result.offset = align(offset, result.align)
|
||||
|
||||
of nkSym:
|
||||
var size = szUnknownSize
|
||||
var align = szUnknownSize
|
||||
|
||||
if n.sym.bitsize == 0: # 0 represents bitsize not set
|
||||
computeSizeAlign(conf, n.sym.typ)
|
||||
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)
|
||||
# the union neds to be aligned first, before the offsets can be assigned
|
||||
let kindUnionOffset = kindOffset
|
||||
|
||||
var maxChildOffset: BiggestInt = kindUnionOffset
|
||||
for i in 1 ..< sonsLen(n):
|
||||
let offset = computePackedObjectOffsetsFoldFunction(conf, n.sons[i].lastSon, kindUnionOffset, debug)
|
||||
@@ -168,9 +154,17 @@ proc computePackedObjectOffsetsFoldFunction(conf: ConfigRef; n: PNode, initialOf
|
||||
if result == szIllegalRecursion:
|
||||
break
|
||||
of nkSym:
|
||||
computeSizeAlign(conf, n.sym.typ)
|
||||
n.sym.offset = initialOffset.int
|
||||
result = n.sym.offset + n.sym.typ.size
|
||||
var size = szUnknownSize
|
||||
if n.sym.bitsize == 0:
|
||||
computeSizeAlign(conf, n.sym.typ)
|
||||
size = n.sym.typ.size.int
|
||||
|
||||
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:
|
||||
result = szUnknownSize
|
||||
|
||||
@@ -324,7 +318,6 @@ proc computeSizeAlign(conf: ConfigRef; typ: PType) =
|
||||
var headerAlign: int16
|
||||
if typ.sons[0] != nil:
|
||||
# compute header size
|
||||
|
||||
if conf.cmd == cmdCompileToCpp:
|
||||
# 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
|
||||
@@ -364,7 +357,6 @@ proc computeSizeAlign(conf: ConfigRef; typ: PType) =
|
||||
typ.size = szUnknownSize
|
||||
typ.align = szUnknownSize
|
||||
return
|
||||
|
||||
# header size is already in size from computeObjectOffsetsFoldFunction
|
||||
# maxAlign is probably not changed at all from headerAlign
|
||||
if tfPacked in typ.flags:
|
||||
@@ -373,7 +365,6 @@ proc computeSizeAlign(conf: ConfigRef; typ: PType) =
|
||||
else:
|
||||
typ.align = int16(max(align, headerAlign))
|
||||
typ.size = align(offset, typ.align)
|
||||
|
||||
of tyInferred:
|
||||
if typ.len > 1:
|
||||
computeSizeAlign(conf, typ.lastSon)
|
||||
|
||||
@@ -456,20 +456,27 @@ proc suggestSym*(conf: ConfigRef; info: TLineInfo; s: PSym; usageSym: var PSym;
|
||||
proc extractPragma(s: PSym): PNode =
|
||||
if s.kind in routineKinds:
|
||||
result = s.ast[pragmasPos]
|
||||
elif s.kind in {skType}:
|
||||
elif s.kind in {skType, skVar, skLet}:
|
||||
# s.ast = nkTypedef / nkPragmaExpr / [nkSym, nkPragma]
|
||||
result = s.ast[0][1]
|
||||
doAssert result == nil or result.kind == nkPragma
|
||||
|
||||
proc warnAboutDeprecated(conf: ConfigRef; info: TLineInfo; s: PSym) =
|
||||
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:
|
||||
for it in pragmaNode:
|
||||
if whichPragma(it) == wDeprecated and it.safeLen == 2 and
|
||||
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
|
||||
message(conf, info, warnDeprecated, s.name.s)
|
||||
message(conf, info, warnDeprecated, name & " is deprecated")
|
||||
|
||||
proc userError(conf: ConfigRef; info: TLineInfo; s: PSym) =
|
||||
let pragmaNode = extractPragma(s)
|
||||
@@ -486,6 +493,8 @@ proc markUsed(conf: ConfigRef; info: TLineInfo; s: PSym; usageSym: var PSym) =
|
||||
incl(s.flags, sfUsed)
|
||||
if s.kind == skEnumField and s.owner != nil:
|
||||
incl(s.owner.flags, sfUsed)
|
||||
if sfDeprecated in s.owner.flags:
|
||||
warnAboutDeprecated(conf, info, s)
|
||||
if {sfDeprecated, sfError} * s.flags != {}:
|
||||
if sfDeprecated in s.flags: warnAboutDeprecated(conf, info, s)
|
||||
if sfError in s.flags: userError(conf, info, s)
|
||||
|
||||
@@ -107,20 +107,23 @@ proc isFloatLit*(t: PType): bool {.inline.} =
|
||||
result = t.kind == tyFloat and t.n != nil and t.n.kind == nkFloatLit
|
||||
|
||||
proc getProcHeader*(conf: ConfigRef; sym: PSym; prefer: TPreferedDesc = preferName): string =
|
||||
result = sym.owner.name.s & '.' & sym.name.s & '('
|
||||
var n = sym.typ.n
|
||||
for i in countup(1, sonsLen(n) - 1):
|
||||
let p = n.sons[i]
|
||||
if p.kind == nkSym:
|
||||
add(result, p.sym.name.s)
|
||||
add(result, ": ")
|
||||
add(result, typeToString(p.sym.typ, prefer))
|
||||
if i != sonsLen(n)-1: add(result, ", ")
|
||||
else:
|
||||
result.add renderTree(p)
|
||||
add(result, ')')
|
||||
if n.sons[0].typ != nil:
|
||||
result.add(": " & typeToString(n.sons[0].typ, prefer))
|
||||
assert sym != nil
|
||||
result = sym.owner.name.s & '.' & sym.name.s
|
||||
if sym.kind in routineKinds:
|
||||
result.add '('
|
||||
var n = sym.typ.n
|
||||
for i in countup(1, sonsLen(n) - 1):
|
||||
let p = n.sons[i]
|
||||
if p.kind == nkSym:
|
||||
add(result, p.sym.name.s)
|
||||
add(result, ": ")
|
||||
add(result, typeToString(p.sym.typ, prefer))
|
||||
if i != sonsLen(n)-1: add(result, ", ")
|
||||
else:
|
||||
result.add renderTree(p)
|
||||
add(result, ')')
|
||||
if n.sons[0].typ != nil:
|
||||
result.add(": " & typeToString(n.sons[0].typ, prefer))
|
||||
result.add "[declared in "
|
||||
result.add(conf$sym.info)
|
||||
result.add "]"
|
||||
@@ -429,7 +432,7 @@ proc typeToString(typ: PType, prefer: TPreferedDesc = preferName): string =
|
||||
sfAnon notin t.sym.flags:
|
||||
if t.kind == tyInt and isIntLit(t):
|
||||
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])
|
||||
elif prefer in {preferName, preferTypeName} or t.sym.owner.isNil:
|
||||
result = t.sym.name.s
|
||||
@@ -756,7 +759,7 @@ type
|
||||
|
||||
TTypeCmpFlags* = set[TTypeCmpFlag]
|
||||
|
||||
TSameTypeClosure = object {.pure.}
|
||||
TSameTypeClosure = object
|
||||
cmp: TDistinctCompare
|
||||
recCheck: int
|
||||
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.
|
||||
## An instruction is 1-3 int32s in memory, it is a register based VM.
|
||||
|
||||
const
|
||||
debugEchoCode = false
|
||||
traceCode = debugEchoCode
|
||||
|
||||
import ast except getstr
|
||||
|
||||
import
|
||||
@@ -26,6 +22,9 @@ from evaltempl import evalTemplate
|
||||
|
||||
from modulegraphs import ModuleGraph, PPassContext
|
||||
|
||||
const
|
||||
traceCode = debugEchoCode
|
||||
|
||||
when hasFFI:
|
||||
import evalffi
|
||||
|
||||
@@ -65,22 +64,27 @@ proc stackTraceAux(c: PCtx; x: PStackFrame; pc: int; recursionLimit=100) =
|
||||
return
|
||||
stackTraceAux(c, x.next, x.comesFrom, recursionLimit-1)
|
||||
var info = c.debug[pc]
|
||||
# we now use the same format as in system/except.nim
|
||||
var s = substr(toFilename(c.config, info), 0)
|
||||
# this 'substr' prevents a strange corruption. XXX This needs to be
|
||||
# investigated eventually but first attempts to fix it broke everything
|
||||
# see the araq-wip-fixed-writebarrier branch.
|
||||
# we now use a format similar to the one in lib/system/excpt.nim
|
||||
var s = ""
|
||||
# todo: factor with quotedFilename
|
||||
if optExcessiveStackTrace in c.config.globalOptions:
|
||||
s = toFullPath(c.config, info)
|
||||
else:
|
||||
s = toFilename(c.config, info)
|
||||
var line = toLinenumber(info)
|
||||
var col = toColumn(info)
|
||||
if line > 0:
|
||||
add(s, '(')
|
||||
add(s, $line)
|
||||
add(s, ", ")
|
||||
add(s, $(col + ColOffset))
|
||||
add(s, ')')
|
||||
if x.prc != nil:
|
||||
for k in 1..max(1, 25-s.len): add(s, ' ')
|
||||
add(s, x.prc.name.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) =
|
||||
msgWriteln(c.config, "stack trace: (most recent call last)")
|
||||
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)
|
||||
else: localError(c.config, lineInfo, msg)
|
||||
|
||||
proc stackTrace(c: PCtx, tos: PStackFrame, pc: int, msg: string) =
|
||||
stackTrace(c, tos, pc, msg, c.debug[pc])
|
||||
template stackTrace(c: PCtx, tos: PStackFrame, pc: int,
|
||||
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) =
|
||||
stackTrace(c, tos, c.exceptionInstr, "unhandled exception: " &
|
||||
@@ -950,13 +960,13 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
|
||||
decodeBC(rkInt)
|
||||
let a = regs[rb].node
|
||||
let b = regs[rc].node
|
||||
if a.kind == nkSym and a.sym.kind in skProcKinds and
|
||||
if a.kind == nkSym and a.sym.kind in skProcKinds and
|
||||
b.kind == nkSym and b.sym.kind in skProcKinds:
|
||||
regs[ra].intVal =
|
||||
if sfFromGeneric in a.sym.flags and a.sym.owner == b.sym: 1
|
||||
else: 0
|
||||
else:
|
||||
stackTrace(c, tos, pc, "node is not a proc symbol")
|
||||
else:
|
||||
stackTrace(c, tos, pc, "node is not a proc symbol")
|
||||
of opcEcho:
|
||||
let rb = instr.regB
|
||||
if rb == 1:
|
||||
@@ -1239,8 +1249,6 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
|
||||
let newLen = regs[rb].intVal.int
|
||||
if regs[ra].node.isNil: stackTrace(c, tos, pc, errNilAccess)
|
||||
else: c.setLenSeq(regs[ra].node, newLen, c.debug[pc])
|
||||
of opcReset:
|
||||
internalError(c.config, c.debug[pc], "too implement")
|
||||
of opcNarrowS:
|
||||
decodeB(rkInt)
|
||||
let min = -(1.BiggestInt shl (rb-1))
|
||||
|
||||
@@ -73,7 +73,7 @@ type
|
||||
opcContainsSet, opcRepr, opcSetLenStr, opcSetLenSeq,
|
||||
opcIsNil, opcOf, opcIs,
|
||||
opcSubStr, opcParseFloat, opcConv, opcCast,
|
||||
opcQuit, opcReset,
|
||||
opcQuit,
|
||||
opcNarrowS, opcNarrowU,
|
||||
|
||||
opcAddStrCh,
|
||||
|
||||
@@ -33,6 +33,11 @@ import
|
||||
import platform
|
||||
from os import splitFile
|
||||
|
||||
const
|
||||
debugEchoCode* = defined(nimVMDebug)
|
||||
|
||||
when debugEchoCode:
|
||||
import asciitables
|
||||
when hasFFI:
|
||||
import evalffi
|
||||
|
||||
@@ -43,9 +48,10 @@ type
|
||||
TGenFlags = set[TGenFlag]
|
||||
|
||||
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) =
|
||||
## for debugging purposes
|
||||
# first iteration: compute all necessary labels:
|
||||
var jumpTargets = initIntSet()
|
||||
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:
|
||||
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
|
||||
while i <= last:
|
||||
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)
|
||||
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 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[z.regBx-wordExcess].typeToString)
|
||||
inc i, 2
|
||||
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)
|
||||
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)
|
||||
elif opc in {opcLdConst, opcAsgnConst}:
|
||||
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)
|
||||
elif opc in {opcMarshalLoad, opcMarshalStore}:
|
||||
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)
|
||||
inc i
|
||||
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(debugInfo(c, c.debug[i]))
|
||||
result.add("\n")
|
||||
inc i
|
||||
when debugEchoCode:
|
||||
result = result.alignTable
|
||||
|
||||
proc echoCode*(c: PCtx; start=0; last = -1) {.deprecated.} =
|
||||
var buf = ""
|
||||
@@ -1092,7 +1105,9 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest; m: TMagic) =
|
||||
of mReset:
|
||||
unused(c, n, dest)
|
||||
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:
|
||||
if dest < 0: dest = c.getTemp(n.typ)
|
||||
var tmp = c.genx(n.sons[1])
|
||||
@@ -1736,8 +1751,9 @@ proc genVarSection(c: PCtx; n: PNode) =
|
||||
#assert(a.sons[0].kind == nkSym) can happen for transformed vars
|
||||
if a.kind == nkVarTuple:
|
||||
for i in 0 .. a.len-3:
|
||||
if not a[i].sym.isGlobal: setSlot(c, a[i].sym)
|
||||
checkCanEval(c, a[i])
|
||||
if a[i].kind == nkSym:
|
||||
if not a[i].sym.isGlobal: setSlot(c, a[i].sym)
|
||||
checkCanEval(c, a[i])
|
||||
c.gen(lowerTupleUnpacking(c.graph, a, c.getOwner))
|
||||
elif a.sons[0].kind == nkSym:
|
||||
let s = a.sons[0].sym
|
||||
|
||||
@@ -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,
|
||||
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.} =
|
||||
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)))
|
||||
systemop gorgeEx
|
||||
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
|
||||
@end
|
||||
@if haiku:
|
||||
# Haiku currently have problems with TLS
|
||||
# https://dev.haiku-os.org/ticket/14342
|
||||
tlsEmulation:on
|
||||
gcc.options.linker = "-Wl,--as-needed -lnetwork"
|
||||
gcc.cpp.options.linker = "-Wl,--as-needed -lnetwork"
|
||||
clang.options.linker = "-Wl,--as-needed -lnetwork"
|
||||
@@ -220,10 +217,25 @@ llvm_gcc.options.size = "-Os"
|
||||
|
||||
# Configuration for the LLVM CLang compiler:
|
||||
clang.options.debug = "-g"
|
||||
clang.cpp.options.debug = "-g"
|
||||
clang.options.always = "-w"
|
||||
clang.options.speed = "-O3"
|
||||
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:
|
||||
# VCCEXE is a tool that invokes the Visual Studio Developer Command Prompt
|
||||
# before calling the compiler.
|
||||
|
||||
@@ -18,9 +18,9 @@ Advanced commands:
|
||||
|
||||
Advanced options:
|
||||
-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
|
||||
--listFullPaths list full paths in messages
|
||||
--listFullPaths:on|off list full paths in messages
|
||||
-w:on|off|list, --warnings:on|off|list
|
||||
turn all warnings on|off or list all available
|
||||
--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
|
||||
--header:FILE the compiler should produce a .h file (FILE
|
||||
is optional)
|
||||
-c, --compileOnly compile Nim files only; do not assemble or link
|
||||
--noLinking compile Nim and generated files but do not link
|
||||
--noMain do not generate a main procedure
|
||||
--genScript generate a compile script (in the 'nimcache'
|
||||
-c, --compileOnly:on|off compile Nim files only; do not assemble or link
|
||||
--noLinking:on|off compile Nim and generated files but do not link
|
||||
--noMain:on|off do not generate a main procedure
|
||||
--genScript:on|off generate a compile script (in the 'nimcache'
|
||||
subdirectory named 'compile_$$project$$scriptext'),
|
||||
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)
|
||||
--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
|
||||
-l, --passL:OPTION pass an option to the linker
|
||||
--cincludes:DIR modify the C compiler header search path
|
||||
--clibdir:DIR modify the linker library search path
|
||||
--clib:LIBNAME link an additional C library
|
||||
(you should omit platform-specific extensions)
|
||||
--genMapping generate a mapping file containing
|
||||
(Nim, mangled) identifier pairs
|
||||
--project document the whole project (doc2)
|
||||
--docSeeSrcUrl:url activate 'see source' for doc and doc2 commands
|
||||
(see doc.item.seesrc in config/nimdoc.cfg)
|
||||
--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
|
||||
--threadanalysis:on|off turn thread analysis 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
|
||||
--nilseqs:on|off allow 'nil' for strings/seqs for
|
||||
backwards compatibility
|
||||
--skipCfg do not read the general configuration file
|
||||
--skipUserCfg do not read the user's configuration file
|
||||
--skipParentCfg do not read the parent dirs' configuration files
|
||||
--skipProjCfg do not read the project's configuration file
|
||||
--gc:refc|v2|markAndSweep|boehm|go|none|regions
|
||||
--oldast:on|off use old AST for backwards compatibility
|
||||
--skipCfg:on|off do not read the nim installation's configuration file
|
||||
--skipUserCfg:on|off do not read the user's configuration file
|
||||
--skipParentCfg:on|off do not read the parent dirs' configuration files
|
||||
--skipProjCfg:on|off do not read the project's configuration file
|
||||
--gc:refc|markAndSweep|boehm|go|none|regions
|
||||
select the GC to use; default is 'refc'
|
||||
--index:on|off turn index file generation on|off
|
||||
--putenv:key=value set an environment variable
|
||||
@@ -98,8 +97,8 @@ Advanced options:
|
||||
symbol matching is fuzzy so
|
||||
that --dynlibOverride:lua matches
|
||||
dynlib: "liblua.so.3"
|
||||
--dynlibOverrideAll makes the dynlib pragma have no effect
|
||||
--listCmd list the commands used to execute external programs
|
||||
--dynlibOverrideAll:on|off makes the dynlib pragma have no effect
|
||||
--listCmd:on|off list the commands used to execute external programs
|
||||
--parallelBuild:0|1|... perform a parallel build
|
||||
value = number of processors (0 for auto-detect)
|
||||
--incremental:on|off only recompile the changed modules (experimental!)
|
||||
@@ -108,3 +107,6 @@ Advanced options:
|
||||
--experimental:$1
|
||||
enable experimental language feature
|
||||
-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,
|
||||
see: "compile time define pragmas")
|
||||
-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
|
||||
--lineTrace:on|off turn line tracing 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)
|
||||
--app:console|gui|lib|staticlib
|
||||
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
|
||||
-h, --help show this help
|
||||
|
||||
|
||||
@@ -6,6 +6,9 @@ The documentation consists of several documents:
|
||||
- | `Tutorial (part II) <tut2.html>`_
|
||||
| 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>`_
|
||||
| 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.
|
||||
|
||||
* `strformat <strformat.html>`_
|
||||
Macro based standard string interpolation / formatting. Inpired by
|
||||
Python's ```f``-strings.
|
||||
Macro based standard string interpolation / formatting. Inspired by
|
||||
Python's ``f``-strings.
|
||||
|
||||
* `strmisc <strmisc.html>`_
|
||||
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
|
||||
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>`_
|
||||
This module contains an algorithm to compute the edit distance between two
|
||||
Unicode strings.
|
||||
@@ -236,9 +230,6 @@ Internet Protocols and Support
|
||||
* `cgi <cgi.html>`_
|
||||
This module implements helpers for CGI applications.
|
||||
|
||||
* `scgi <scgi.html>`_
|
||||
This module implements helpers for SCGI applications.
|
||||
|
||||
* `browsers <browsers.html>`_
|
||||
This module implements procs for opening URLs with the user's default
|
||||
browser.
|
||||
@@ -275,8 +266,8 @@ Internet Protocols and Support
|
||||
module.
|
||||
|
||||
* `net <net.html>`_
|
||||
This module implements a high-level sockets API. It will replace the
|
||||
``sockets`` module in the future.
|
||||
This module implements a high-level sockets API. It replaces the
|
||||
``sockets`` module.
|
||||
|
||||
* `nativesockets <nativesockets.html>`_
|
||||
This module implements a low-level sockets API.
|
||||
@@ -456,13 +447,6 @@ Database support
|
||||
for other databases too.
|
||||
|
||||
|
||||
Other
|
||||
-----
|
||||
|
||||
* `ssl <ssl.html>`_
|
||||
This module provides an easy to use sockets-style
|
||||
Nim interface to the OpenSSL library.
|
||||
|
||||
|
||||
Wrappers
|
||||
========
|
||||
|
||||
@@ -480,7 +480,6 @@ The type suffixes are:
|
||||
``'d`` float64
|
||||
``'f32`` float32
|
||||
``'f64`` float64
|
||||
``'f128`` float128
|
||||
================= =========================
|
||||
|
||||
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
|
||||
------------------
|
||||
|
||||
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:
|
||||
|
||||
.. code-block:: nim
|
||||
@@ -1664,12 +1663,12 @@ Nim supports these `calling conventions`:idx:\:
|
||||
and another one for the pointer to implicitly passed environment.
|
||||
|
||||
`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.
|
||||
|
||||
`cdecl`:idx:
|
||||
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.
|
||||
|
||||
`safecall`:idx:
|
||||
@@ -7365,6 +7364,17 @@ Example:
|
||||
|
||||
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
|
||||
is a single string literal, Nim symbols can be referred to via backticks.
|
||||
This usage is however deprecated.
|
||||
|
||||
@@ -347,13 +347,16 @@ complete list.
|
||||
Define Effect
|
||||
====================== =========================================================
|
||||
``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
|
||||
of the Windows API. The default build uses the Unicode
|
||||
version.
|
||||
``useFork`` Makes ``osproc`` use ``fork`` instead of ``posix_spawn``.
|
||||
``useNimRtl`` Compile and link against ``nimrtl.dll``.
|
||||
``useMalloc`` Makes Nim use C's `malloc`:idx: instead of Nim's
|
||||
own memory manager, ableit prefixing each allocation with
|
||||
own memory manager, albeit prefixing each allocation with
|
||||
its size to support clearing memory on reallocation.
|
||||
This only works with ``gc:none``.
|
||||
``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
|
||||
``--skipProjCfg`` command line option.
|
||||
|
||||
The VM cannot deal with ``importc`` because the FFI is not
|
||||
available. So the stdlib modules using ``importc`` cannot be used with
|
||||
Nim's VM. However, at least the following modules are available:
|
||||
Limitations
|
||||
=================================
|
||||
|
||||
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>`_
|
||||
* `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
|
||||
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
|
||||
distrohelper [bindir] helper for distro packagers
|
||||
tools builds Nim related tools
|
||||
toolsNoNimble builds Nim related tools (except nimble)
|
||||
doesn't require network connectivity
|
||||
nimble builds the Nimble tool
|
||||
Boot options:
|
||||
-d:release produce a release version of the compiler
|
||||
@@ -56,6 +58,7 @@ Boot options:
|
||||
for bootstrapping
|
||||
|
||||
Commands for core developers:
|
||||
runCI runs continuous integration (CI), eg from travis
|
||||
docs [options] generates the full documentation
|
||||
csource -d:release builds the C sources for installation
|
||||
pdf builds the PDF documentation
|
||||
@@ -71,6 +74,12 @@ Web options:
|
||||
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) =
|
||||
let old = getCurrentDir()
|
||||
try:
|
||||
@@ -121,9 +130,6 @@ proc bundleNimbleExe(latest: bool) =
|
||||
# installer.ini expects it under $nim/bin
|
||||
nimCompile("dist/nimble/src/nimble.nim", options = "-d:release --nilseqs:on")
|
||||
|
||||
proc buildNimfind() =
|
||||
nimCompile("tools/nimfind.nim", options = "-d:release")
|
||||
|
||||
proc buildNimble(latest: bool) =
|
||||
# old installations created nim/nimblepkg/*.nim files. We remove these
|
||||
# here so that it cannot cause problems (nimble bug #306):
|
||||
@@ -197,13 +203,12 @@ proc buildTool(toolname, args: string) =
|
||||
nimexec("cc $# $#" % [args, toolname])
|
||||
copyFile(dest="bin" / splitFile(toolname).name.exe, source=toolname.exe)
|
||||
|
||||
proc buildTools(latest: bool) =
|
||||
proc buildTools() =
|
||||
bundleNimsuggest()
|
||||
nimCompile("tools/nimgrep.nim", options = "-d:release")
|
||||
when defined(windows): buildVccTool()
|
||||
nimCompile("nimpretty/nimpretty.nim", options = "-d:release")
|
||||
buildNimble(latest)
|
||||
buildNimfind()
|
||||
nimCompile("tools/nimfind.nim", options = "-d:release")
|
||||
|
||||
proc nsis(latest: bool; args: string) =
|
||||
bundleNimbleExe(latest)
|
||||
@@ -272,16 +277,30 @@ proc boot(args: string) =
|
||||
var output = "compiler" / "nim".exe
|
||||
var finalDest = "bin" / "nim".exe
|
||||
# default to use the 'c' command:
|
||||
let defaultCommand = if getEnv("NIM_COMPILE_TO_CPP", "false") == "true": "cpp" else: "c"
|
||||
let bootOptions = if args.len == 0 or args.startsWith("-"): defaultCommand else: ""
|
||||
let useCpp = getEnv("NIM_COMPILE_TO_CPP", "false") == "true"
|
||||
let smartNimcache = (if "release" in args: "nimcache/r_" else: "nimcache/d_") &
|
||||
hostOs & "_" & hostCpu
|
||||
|
||||
copyExe(findStartNim(), 0.thVersion)
|
||||
let nimStart = findStartNim()
|
||||
copyExe(nimStart, 0.thVersion)
|
||||
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
|
||||
exec i.thVersion & " $# $# --nimcache:$# compiler" / "nim.nim" % [bootOptions, args,
|
||||
smartNimcache]
|
||||
var extraOption = ""
|
||||
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):
|
||||
copyExe(output, finalDest)
|
||||
echo "executables are equal: SUCCESS!"
|
||||
@@ -352,8 +371,8 @@ proc winReleaseArch(arch: string) =
|
||||
# Rebuilding koch is necessary because it uses its pointer size to
|
||||
# determine which mingw link to put in the NSIS installer.
|
||||
nimexec "c --cpu:$# koch" % cpu
|
||||
exec "koch boot -d:release --cpu:$#" % cpu
|
||||
exec "koch --latest zip -d:release"
|
||||
kochExec "boot -d:release --cpu:$#" % cpu
|
||||
kochExec "--latest zip -d:release"
|
||||
overwriteFile r"build\nim-$#.zip" % VersionAsString,
|
||||
r"web\upload\download\nim-$#_x$#.zip" % [VersionAsString, arch]
|
||||
|
||||
@@ -428,6 +447,47 @@ proc xtemp(cmd: string) =
|
||||
finally:
|
||||
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() =
|
||||
if not dirExists("../csources/.git"):
|
||||
quit "[Error] no csources git repository found"
|
||||
@@ -475,8 +535,7 @@ proc testUnixInstall(cmdLineRest: string) =
|
||||
execCleanPath("./koch --latest tools")
|
||||
# check the tests work:
|
||||
putEnv("NIM_EXE_NOT_IN_PATH", "NOT_IN_PATH")
|
||||
execCleanPath("./koch tests", destDir / "bin")
|
||||
#execCleanPath("./koch tests cat newconfig", destDir / "bin")
|
||||
execCleanPath("./koch tests cat megatest", destDir / "bin")
|
||||
else:
|
||||
echo "Version check: failure"
|
||||
finally:
|
||||
@@ -512,6 +571,10 @@ when isMainModule:
|
||||
var op = initOptParser()
|
||||
var latest = false
|
||||
var stable = false
|
||||
template isLatest(): bool =
|
||||
if stable: false
|
||||
else:
|
||||
existsDir(".git") or latest
|
||||
while true:
|
||||
op.next()
|
||||
case op.kind
|
||||
@@ -537,17 +600,18 @@ when isMainModule:
|
||||
of "distrohelper": geninstall()
|
||||
of "install": install(op.cmdLineRest)
|
||||
of "testinstall": testUnixInstall(op.cmdLineRest)
|
||||
of "runci": runCI(op.cmdLineRest)
|
||||
of "test", "tests": tests(op.cmdLineRest)
|
||||
of "temp": temp(op.cmdLineRest)
|
||||
of "xtemp": xtemp(op.cmdLineRest)
|
||||
of "wintools": bundleWinTools()
|
||||
of "nimble":
|
||||
if stable: buildNimble(false)
|
||||
else: buildNimble(existsDir(".git") or latest)
|
||||
of "nimble": buildNimble(isLatest())
|
||||
of "nimsuggest": bundleNimsuggest()
|
||||
of "toolsnonimble":
|
||||
buildTools()
|
||||
of "tools":
|
||||
if stable: buildTools(false)
|
||||
else: buildTools(existsDir(".git") or latest)
|
||||
buildTools()
|
||||
buildNimble(isLatest())
|
||||
of "pushcsource", "pushcsources": pushCsources()
|
||||
of "valgrind": valgrind(op.cmdLineRest)
|
||||
else: showHelp()
|
||||
|
||||
@@ -151,7 +151,7 @@ proc `==`*(a, b: NimNode): bool {.magic: "EqNimrodNode", noSideEffect.}
|
||||
|
||||
proc `==`*(a, b: NimSym): bool {.magic: "EqNimrodNode", noSideEffect, deprecated.}
|
||||
## 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.} =
|
||||
@@ -277,9 +277,9 @@ when defined(nimHasSymOwnerInMacro):
|
||||
when defined(nimHasInstantiationOfInMacro):
|
||||
proc isInstantiationOf*(instanceProcSym, genProcSym: NimNode): bool {.magic: "SymIsInstantiationOf", noSideEffect.}
|
||||
## check if proc symbol is instance of the generic proc symbol
|
||||
## useful to check proc symbols against generic symbols
|
||||
## useful to check proc symbols against generic symbols
|
||||
## returned by `bindSym`
|
||||
|
||||
|
||||
proc getType*(n: NimNode): NimNode {.magic: "NGetType", noSideEffect.}
|
||||
## with 'getType' you can access the node's `type`:idx:. A Nim type is
|
||||
## mapped to a Nim AST too, so it's slightly confusing but it means the same
|
||||
@@ -377,7 +377,9 @@ proc copyNimNode*(n: NimNode): NimNode {.magic: "NCopyNimNode", noSideEffect.}
|
||||
proc copyNimTree*(n: NimNode): NimNode {.magic: "NCopyNimTree", noSideEffect.}
|
||||
|
||||
proc error*(msg: string, n: NimNode = nil) {.magic: "NError", benign.}
|
||||
## writes an error message at compile time
|
||||
## 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.}
|
||||
## writes a warning message at compile time
|
||||
@@ -1402,8 +1404,14 @@ proc customPragmaNode(n: NimNode): NimNode =
|
||||
let impl = n.getImpl()
|
||||
if impl.kind in RoutineNodes:
|
||||
return impl.pragma
|
||||
elif impl.kind == nnkIdentDefs and impl[0].kind == nnkPragmaExpr:
|
||||
return impl[0][1]
|
||||
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}:
|
||||
let name = $(if n.kind == nnkCheckedFieldExpr: n[0][1] else: n[1])
|
||||
@@ -1492,9 +1500,18 @@ macro getCustomPragmaVal*(n: typed, cp: typed{nkSym}): untyped =
|
||||
let pragmaNode = customPragmaNode(n)
|
||||
for p in pragmaNode:
|
||||
if p.kind in nnkPragmaCallKinds and p.len > 0 and p[0].kind == nnkSym and p[0] == cp:
|
||||
return p[1]
|
||||
|
||||
error(n.repr & " doesn't have a pragma named " & cp.repr()) # returning an empty node results in most cases in a cryptic error,
|
||||
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,
|
||||
|
||||
|
||||
when not defined(booting):
|
||||
|
||||
@@ -70,12 +70,6 @@ proc `=sink`[T](x: var seq[T]; y: seq[T]) =
|
||||
a.len = b.len
|
||||
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
|
||||
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.
|
||||
lastProgressReport: float # Time
|
||||
toStore: string # Data left to upload (Only used with async)
|
||||
else: nil
|
||||
|
||||
FtpClientObj* = FtpBaseObj[Socket]
|
||||
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] =
|
||||
## Find the difference in 2 arrays of integers.
|
||||
##
|
||||
## ``arrayA`` A-version of the numbers (usualy the old one)
|
||||
##
|
||||
## ``arrayB`` B-version of the numbers (usualy the new one)
|
||||
##
|
||||
## Returns a array of Items that describe the differences.
|
||||
|
||||
# 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] =
|
||||
## 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
|
||||
## 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
|
||||
## 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)
|
||||
## ``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.
|
||||
## ``ignoreCase`` When set to true, all characters are converted to their lowercase equivivalence before the comparation is done.
|
||||
##
|
||||
## ``textA`` A-version of the text (usually the old one)
|
||||
##
|
||||
## ``textB`` B-version of the text (usually the new one)
|
||||
##
|
||||
## Returns a seq of Items that describe the differences.
|
||||
|
||||
# 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)
|
||||
except:
|
||||
discard
|
||||
return (res.int, $sqlState, $nativeErr, $errMsg)
|
||||
return (res.int, $(addr sqlState), $(addr nativeErr), $(addr errMsg))
|
||||
|
||||
proc dbError*(db: var DbConn) {.
|
||||
tags: [ReadDbEffect, WriteDbEffect], raises: [DbError] .} =
|
||||
|
||||
@@ -166,10 +166,12 @@ iterator fastRows*(db: DbConn, query: SqlQuery,
|
||||
var stmt = setupQuery(db, query, args)
|
||||
var L = (column_count(stmt))
|
||||
var result = newRow(L)
|
||||
while step(stmt) == SQLITE_ROW:
|
||||
setRow(stmt, result, L)
|
||||
yield result
|
||||
if finalize(stmt) != SQLITE_OK: dbError(db)
|
||||
try:
|
||||
while step(stmt) == SQLITE_ROW:
|
||||
setRow(stmt, result, L)
|
||||
yield result
|
||||
finally:
|
||||
if finalize(stmt) != SQLITE_OK: dbError(db)
|
||||
|
||||
iterator instantRows*(db: DbConn, query: SqlQuery,
|
||||
args: varargs[string, `$`]): InstantRow
|
||||
@@ -177,9 +179,11 @@ iterator instantRows*(db: DbConn, query: SqlQuery,
|
||||
## same as fastRows but returns a handle that can be used to get column text
|
||||
## on demand using []. Returned handle is valid only within the iterator body.
|
||||
var stmt = setupQuery(db, query, args)
|
||||
while step(stmt) == SQLITE_ROW:
|
||||
yield stmt
|
||||
if finalize(stmt) != SQLITE_OK: dbError(db)
|
||||
try:
|
||||
while step(stmt) == SQLITE_ROW:
|
||||
yield stmt
|
||||
finally:
|
||||
if finalize(stmt) != SQLITE_OK: dbError(db)
|
||||
|
||||
proc toTypeKind(t: var DbType; x: int32) =
|
||||
case x
|
||||
@@ -210,9 +214,11 @@ iterator instantRows*(db: DbConn; columns: var DbColumns; query: SqlQuery,
|
||||
## on demand using []. Returned handle is valid only within the iterator body.
|
||||
var stmt = setupQuery(db, query, args)
|
||||
setColumns(columns, stmt)
|
||||
while step(stmt) == SQLITE_ROW:
|
||||
yield stmt
|
||||
if finalize(stmt) != SQLITE_OK: dbError(db)
|
||||
try:
|
||||
while step(stmt) == SQLITE_ROW:
|
||||
yield stmt
|
||||
finally:
|
||||
if finalize(stmt) != SQLITE_OK: dbError(db)
|
||||
|
||||
proc `[]`*(row: InstantRow, col: int32): string {.inline.} =
|
||||
## returns text for given column of the row
|
||||
|
||||
@@ -540,7 +540,7 @@ proc matchImpl(str: string, pattern: Regex, start, endpos: int, flags: int): Opt
|
||||
raise RegexInternalError(msg : "Unknown internal error: " & $execRet)
|
||||
|
||||
proc match*(str: string, pattern: Regex, start = 0, endpos = int.high): Option[RegexMatch] =
|
||||
## Like ```find(...)`` <#proc-find>`_, but anchored to the start of the
|
||||
## Like ` ``find(...)`` <#proc-find>`_, but anchored to the start of the
|
||||
## string.
|
||||
##
|
||||
runnableExamples:
|
||||
@@ -550,11 +550,11 @@ proc match*(str: string, pattern: Regex, start = 0, endpos = int.high): Option[R
|
||||
return str.matchImpl(pattern, start, endpos, pcre.ANCHORED)
|
||||
|
||||
iterator findIter*(str: string, pattern: Regex, start = 0, endpos = int.high): RegexMatch =
|
||||
## Works the same as ```find(...)`` <#proc-find>`_, but finds every
|
||||
## Works the same as ` ``find(...)`` <#proc-find>`_, but finds every
|
||||
## non-overlapping match. ``"2222".find(re"22")`` is ``"22", "22"``, not
|
||||
## ``"22", "22", "22"``.
|
||||
##
|
||||
## Arguments are the same as ```find(...)`` <#proc-find>`_
|
||||
## Arguments are the same as ` ``find(...)`` <#proc-find>`_
|
||||
##
|
||||
## Variants:
|
||||
##
|
||||
@@ -633,7 +633,7 @@ proc split*(str: string, pattern: Regex, maxSplit = -1, start = 0): seq[string]
|
||||
## Splits the string with the given regex. This works according to the
|
||||
## rules that Perl and Javascript use.
|
||||
##
|
||||
## ``start`` behaves the same as in ```find(...)`` <#proc-find>`_.
|
||||
## ``start`` behaves the same as in ` ``find(...)`` <#proc-find>`_.
|
||||
##
|
||||
runnableExamples:
|
||||
# - If the match is zero-width, then the string is still split:
|
||||
|
||||
@@ -561,31 +561,6 @@ proc escapeRe*(s: string): string =
|
||||
result.add("\\x")
|
||||
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:
|
||||
doAssert match("(a b c)", rex"\( .* \)")
|
||||
doAssert match("WHiLe", re("while", {reIgnoreCase}))
|
||||
@@ -595,7 +570,7 @@ when isMainModule:
|
||||
doAssert "ABC".match(rex"\d+ | \w+")
|
||||
|
||||
{.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.}
|
||||
|
||||
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
|
||||
|
||||
@@ -416,12 +416,12 @@ type
|
||||
BoundingRect* {.importc.} = ref object
|
||||
top*, bottom*, left*, right*, x*, y*, width*, height*: float
|
||||
|
||||
PerformanceMemory* {.importc.} = ref object
|
||||
PerformanceMemory* {.importc.} = ref object
|
||||
jsHeapSizeLimit*: float
|
||||
totalJSHeapSize*: float
|
||||
usedJSHeapSize*: float
|
||||
|
||||
PerformanceTiming* {.importc.} = ref object
|
||||
PerformanceTiming* {.importc.} = ref object
|
||||
connectStart*: float
|
||||
domComplete*: float
|
||||
domContentLoadedEventEnd*: float
|
||||
@@ -459,7 +459,6 @@ proc dispatchEvent*(et: EventTarget, ev: Event)
|
||||
proc alert*(w: Window, msg: cstring)
|
||||
proc back*(w: Window)
|
||||
proc blur*(w: Window)
|
||||
proc captureEvents*(w: Window, eventMask: int) {.deprecated.}
|
||||
proc clearInterval*(w: Window, interval: ref TInterval)
|
||||
proc clearTimeout*(w: Window, timeout: ref TTimeOut)
|
||||
proc close*(w: Window)
|
||||
@@ -478,7 +477,6 @@ proc open*(w: Window, uri, windowname: cstring,
|
||||
properties: cstring = nil): Window
|
||||
proc print*(w: Window)
|
||||
proc prompt*(w: Window, text, default: cstring): cstring
|
||||
proc releaseEvents*(w: Window, eventMask: int) {.deprecated.}
|
||||
proc resizeBy*(w: Window, x, y: int)
|
||||
proc resizeTo*(w: Window, x, y: int)
|
||||
proc routeEvent*(w: Window, event: Event)
|
||||
@@ -513,7 +511,6 @@ proc setAttribute*(n: Node, name, value: cstring)
|
||||
proc setAttributeNode*(n: Node, attr: Node)
|
||||
|
||||
# Document "methods"
|
||||
proc captureEvents*(d: Document, eventMask: int) {.deprecated.}
|
||||
proc createAttribute*(d: Document, identifier: cstring): Node
|
||||
proc createElement*(d: Document, identifier: cstring): Element
|
||||
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 handleEvent*(d: Document, event: Event)
|
||||
proc open*(d: Document)
|
||||
proc releaseEvents*(d: Document, eventMask: int) {.deprecated.}
|
||||
proc routeEvent*(d: Document, event: Event)
|
||||
proc write*(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 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 {.
|
||||
importcpp: "new Date()".}
|
||||
|
||||
proc newDate*(date: int|string): DateTime {.
|
||||
proc newDate*(date: int|int64|string): DateTime {.
|
||||
importcpp: "new Date(#)".}
|
||||
|
||||
proc newDate*(year, month, day, hours, minutes,
|
||||
@@ -90,6 +90,16 @@ proc getSeconds*(d: DateTime): int {.importcpp.}
|
||||
proc getYear*(d: DateTime): int {.importcpp.}
|
||||
proc getTime*(d: DateTime): int {.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
|
||||
proc stringify*(l: JsonLib, s: JsRoot): cstring {.importcpp.}
|
||||
|
||||
@@ -104,6 +104,12 @@ var
|
||||
jsFilename* {.importc: "__filename", nodecl.}: cstring
|
||||
## 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
|
||||
type
|
||||
JsError* {.importc: "Error".} = object of JsRoot
|
||||
|
||||
@@ -166,13 +166,13 @@ __clang__
|
||||
# define N_STDCALL(rettype, name) rettype __stdcall name
|
||||
# define N_SYSCALL(rettype, name) rettype __syscall 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: */
|
||||
# define N_CDECL_PTR(rettype, name) rettype (__cdecl *name)
|
||||
# define N_STDCALL_PTR(rettype, name) rettype (__stdcall *name)
|
||||
# define N_SYSCALL_PTR(rettype, name) rettype (__syscall *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
|
||||
# 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 == "#")
|
||||
if result:
|
||||
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'})
|
||||
else: discard
|
||||
else: result = false
|
||||
else:
|
||||
var c = expr[i]
|
||||
var length = 0
|
||||
@@ -780,6 +780,31 @@ proc parseMarkdownCodeblock(p: var RstParser): PRstNode =
|
||||
add(result, nil)
|
||||
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) =
|
||||
case p.tok[p.idx].kind
|
||||
of tkPunct:
|
||||
@@ -811,6 +836,9 @@ proc parseInline(p: var RstParser, father: PRstNode) =
|
||||
var n = newRstNode(rnSubstitutionReferences)
|
||||
parseUntil(p, n, "|", false)
|
||||
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:
|
||||
if roSupportSmilies in p.s.options:
|
||||
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
|
||||
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 =
|
||||
case p.tok[p.idx].kind
|
||||
of tkAdornment:
|
||||
if match(p, p.idx + 1, "ii"): result = rnTransition
|
||||
elif match(p, p.idx + 1, " a"): result = rnTable
|
||||
elif match(p, p.idx + 1, "i"): result = rnOverline
|
||||
else: result = rnLeaf
|
||||
elif isMarkdownHeadline(p):
|
||||
result = rnHeadline
|
||||
else:
|
||||
result = rnLeaf
|
||||
of tkPunct:
|
||||
if match(p, tokenAfterNewline(p), "ai"):
|
||||
if isMarkdownHeadline(p):
|
||||
result = rnHeadline
|
||||
elif match(p, tokenAfterNewline(p), "ai"):
|
||||
result = rnHeadline
|
||||
elif p.tok[p.idx].symbol == "::":
|
||||
result = rnLiteralBlock
|
||||
@@ -1060,7 +1105,7 @@ proc whichSection(p: RstParser): RstNodeKind =
|
||||
elif match(p, p.idx, ":w:") and predNL(p):
|
||||
# (p.tok[p.idx].symbol == ":")
|
||||
result = rnFieldList
|
||||
elif match(p, p.idx, "(e) "):
|
||||
elif match(p, p.idx, "(e) ") or match(p, p.idx, "e. "):
|
||||
result = rnEnumList
|
||||
elif match(p, p.idx, "+a+"):
|
||||
result = rnGridTable
|
||||
@@ -1130,12 +1175,18 @@ proc parseParagraph(p: var RstParser, result: PRstNode) =
|
||||
|
||||
proc parseHeadline(p: var RstParser): PRstNode =
|
||||
result = newRstNode(rnHeadline)
|
||||
parseUntilNewline(p, result)
|
||||
assert(p.tok[p.idx].kind == tkIndent)
|
||||
assert(p.tok[p.idx + 1].kind == tkAdornment)
|
||||
var c = p.tok[p.idx + 1].symbol[0]
|
||||
inc(p.idx, 2)
|
||||
result.level = getLevel(p.s.underlineToLevel, p.s.uLevel, c)
|
||||
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)
|
||||
assert(p.tok[p.idx].kind == tkIndent)
|
||||
assert(p.tok[p.idx + 1].kind == tkAdornment)
|
||||
var c = p.tok[p.idx + 1].symbol[0]
|
||||
inc(p.idx, 2)
|
||||
result.level = getLevel(p.s.underlineToLevel, p.s.uLevel, c)
|
||||
|
||||
type
|
||||
IntSeq = seq[int]
|
||||
|
||||
@@ -89,6 +89,9 @@ proc lastSon*(n: PRstNode): PRstNode =
|
||||
proc add*(father, son: PRstNode) =
|
||||
add(father.sons, son)
|
||||
|
||||
proc add*(father: PRstNode; s: string) =
|
||||
add(father.sons, newRstNode(rnLeaf, s))
|
||||
|
||||
proc addIfNotNil*(father, son: PRstNode) =
|
||||
if son != nil: add(father, son)
|
||||
|
||||
|
||||
@@ -1134,11 +1134,9 @@ proc renderRstToOut(d: PDoc, n: PRstNode, result: var string) =
|
||||
of rnTripleEmphasis:
|
||||
renderAux(d, n, "<strong><em>$1</em></strong>",
|
||||
"\\textbf{emph{$1}}", result)
|
||||
of rnInterpretedText:
|
||||
renderAux(d, n, "<cite>$1</cite>", "\\emph{$1}", result)
|
||||
of rnIdx:
|
||||
renderIndexTerm(d, n, result)
|
||||
of rnInlineLiteral:
|
||||
of rnInlineLiteral, rnInterpretedText:
|
||||
renderAux(d, n,
|
||||
"<tt class=\"docutils literal\"><span class=\"pre\">$1</span></tt>",
|
||||
"\\texttt{$1}", result)
|
||||
|
||||
@@ -295,6 +295,7 @@ const IF_NAMESIZE* = cint(16)
|
||||
const IPPROTO_IP* = cint(0)
|
||||
const IPPROTO_IPV6* = cint(41)
|
||||
const IPPROTO_ICMP* = cint(1)
|
||||
const IPPROTO_ICMPV6* = cint(58)
|
||||
const IPPROTO_RAW* = cint(255)
|
||||
const IPPROTO_TCP* = cint(6)
|
||||
const IPPROTO_UDP* = cint(17)
|
||||
|
||||
@@ -237,6 +237,7 @@ const IF_NAMESIZE* = cint(16)
|
||||
const IPPROTO_IP* = cint(0)
|
||||
const IPPROTO_IPV6* = cint(41)
|
||||
const IPPROTO_ICMP* = cint(1)
|
||||
const IPPROTO_ICMPV6* = cint(58)
|
||||
const IPPROTO_RAW* = cint(255)
|
||||
const IPPROTO_TCP* = cint(6)
|
||||
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_IPV6* {.importc: "IPPROTO_IPV6", 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_TCP* {.importc: "IPPROTO_TCP", 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
|
||||
else: raise newException(ValueError, "Invalid HTTP method " & name)
|
||||
|
||||
proc processRequest(server: AsyncHttpServer, req: FutureVar[Request],
|
||||
client: AsyncSocket,
|
||||
address: string, lineFut: FutureVar[string],
|
||||
callback: proc (request: Request):
|
||||
Future[void] {.closure, gcsafe.}) {.async.} =
|
||||
proc processRequest(
|
||||
server: AsyncHttpServer,
|
||||
req: FutureVar[Request],
|
||||
client: AsyncSocket,
|
||||
address: string,
|
||||
lineFut: FutureVar[string],
|
||||
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.
|
||||
template request(): Request =
|
||||
@@ -171,12 +174,12 @@ proc processRequest(server: AsyncHttpServer, req: FutureVar[Request],
|
||||
|
||||
if lineFut.mget == "":
|
||||
client.close()
|
||||
return
|
||||
return false
|
||||
|
||||
if lineFut.mget.len > maxLine:
|
||||
await request.respondError(Http413)
|
||||
client.close()
|
||||
return
|
||||
return false
|
||||
if lineFut.mget != "\c\L":
|
||||
break
|
||||
|
||||
@@ -189,22 +192,22 @@ proc processRequest(server: AsyncHttpServer, req: FutureVar[Request],
|
||||
request.reqMethod = parseUppercaseMethod(linePart)
|
||||
except ValueError:
|
||||
asyncCheck request.respondError(Http400)
|
||||
return
|
||||
return true # Retry processing of request
|
||||
of 1:
|
||||
try:
|
||||
parseUri(linePart, request.url)
|
||||
except ValueError:
|
||||
asyncCheck request.respondError(Http400)
|
||||
return
|
||||
return true
|
||||
of 2:
|
||||
try:
|
||||
request.protocol = parseProtocol(linePart)
|
||||
except ValueError:
|
||||
asyncCheck request.respondError(Http400)
|
||||
return
|
||||
return true
|
||||
else:
|
||||
await request.respondError(Http400)
|
||||
return
|
||||
return true
|
||||
inc i
|
||||
|
||||
# Headers
|
||||
@@ -215,10 +218,10 @@ proc processRequest(server: AsyncHttpServer, req: FutureVar[Request],
|
||||
await client.recvLineInto(lineFut, maxLength=maxLine)
|
||||
|
||||
if lineFut.mget == "":
|
||||
client.close(); return
|
||||
client.close(); return false
|
||||
if lineFut.mget.len > maxLine:
|
||||
await request.respondError(Http413)
|
||||
client.close(); return
|
||||
client.close(); return false
|
||||
if lineFut.mget == "\c\L": break
|
||||
let (key, value) = parseHeader(lineFut.mget)
|
||||
request.headers[key] = value
|
||||
@@ -226,7 +229,7 @@ proc processRequest(server: AsyncHttpServer, req: FutureVar[Request],
|
||||
if request.headers.len > headerLimit:
|
||||
await client.sendStatus("400 Bad Request")
|
||||
request.client.close()
|
||||
return
|
||||
return false
|
||||
|
||||
if request.reqMethod == HttpPost:
|
||||
# Check for Expect header
|
||||
@@ -242,24 +245,24 @@ proc processRequest(server: AsyncHttpServer, req: FutureVar[Request],
|
||||
var contentLength = 0
|
||||
if parseSaturatedNatural(request.headers["Content-Length"], contentLength) == 0:
|
||||
await request.respond(Http400, "Bad Request. Invalid Content-Length.")
|
||||
return
|
||||
return true
|
||||
else:
|
||||
if contentLength > server.maxBody:
|
||||
await request.respondError(Http413)
|
||||
return
|
||||
return false
|
||||
request.body = await client.recv(contentLength)
|
||||
if request.body.len != contentLength:
|
||||
await request.respond(Http400, "Bad Request. Content-Length does not match actual.")
|
||||
return
|
||||
return true
|
||||
elif request.reqMethod == HttpPost:
|
||||
await request.respond(Http411, "Content-Length required.")
|
||||
return
|
||||
return true
|
||||
|
||||
# Call the user's callback.
|
||||
await callback(request)
|
||||
|
||||
if "upgrade" in request.headers.getOrDefault("connection"):
|
||||
return
|
||||
return false
|
||||
|
||||
# Persistent connections
|
||||
if (request.protocol == HttpVer11 and
|
||||
@@ -273,7 +276,7 @@ proc processRequest(server: AsyncHttpServer, req: FutureVar[Request],
|
||||
discard
|
||||
else:
|
||||
request.client.close()
|
||||
return
|
||||
return false
|
||||
|
||||
proc processClient(server: AsyncHttpServer, client: AsyncSocket, address: string,
|
||||
callback: proc (request: Request):
|
||||
@@ -285,7 +288,10 @@ proc processClient(server: AsyncHttpServer, client: AsyncSocket, address: string
|
||||
lineFut.mget() = newStringOfCap(80)
|
||||
|
||||
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,
|
||||
callback: proc (request: Request): Future[void] {.closure,gcsafe.},
|
||||
|
||||
@@ -9,37 +9,49 @@
|
||||
|
||||
## 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
|
||||
## -------------
|
||||
##
|
||||
## In order to encode some text simply call the ``encode`` procedure:
|
||||
##
|
||||
## .. code-block::nim
|
||||
## import base64
|
||||
## let encoded = encode("Hello World")
|
||||
## echo(encoded) # SGVsbG8gV29ybGQ=
|
||||
## .. code-block::nim
|
||||
## import base64
|
||||
## let encoded = encode("Hello World")
|
||||
## assert encoded == "SGVsbG8gV29ybGQ="
|
||||
##
|
||||
## Apart from strings you can also encode lists of integers or characters:
|
||||
##
|
||||
## .. code-block::nim
|
||||
## import base64
|
||||
## let encodedInts = encode([1,2,3])
|
||||
## echo(encodedInts) # AQID
|
||||
## let encodedChars = encode(['h','e','y'])
|
||||
## echo(encodedChars) # aGV5
|
||||
## .. code-block::nim
|
||||
## import base64
|
||||
## let encodedInts = encode([1,2,3])
|
||||
## assert encodedInts == "AQID"
|
||||
## let encodedChars = encode(['h','e','y'])
|
||||
## assert encodedChars == "aGV5"
|
||||
##
|
||||
## The ``encode`` procedure takes an ``openarray`` so both arrays and sequences
|
||||
## can be passed as parameters.
|
||||
##
|
||||
## Decoding data
|
||||
## -------------
|
||||
##
|
||||
## To decode a base64 encoded data string simply call the ``decode``
|
||||
## procedure:
|
||||
## .. code-block::nim
|
||||
## import base64
|
||||
## let decoded = decode("SGVsbG8gV29ybGQ=")
|
||||
## assert decoded == "Hello World"
|
||||
##
|
||||
## .. code-block::nim
|
||||
## import base64
|
||||
## echo(decode("SGVsbG8gV29ybGQ=")) # 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
|
||||
cb64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
|
||||
@@ -100,18 +112,33 @@ template encodeInternal(s: typed, lineLen: int, newLine: string): untyped =
|
||||
discard
|
||||
|
||||
proc encode*[T:SomeInteger|char](s: openarray[T], lineLen = 75, newLine="\13\10"): string =
|
||||
## encodes `s` into base64 representation. After `lineLen` characters, a
|
||||
## `newline` is added.
|
||||
## Encodes ``s`` into base64 representation. After ``lineLen`` characters, a
|
||||
## ``newline`` is added.
|
||||
##
|
||||
## This procedure encodes an openarray (array or sequence) of either integers
|
||||
## 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)
|
||||
|
||||
proc encode*(s: string, lineLen = 75, newLine="\13\10"): string =
|
||||
## encodes `s` into base64 representation. After `lineLen` characters, a
|
||||
## `newline` is added.
|
||||
## Encodes ``s`` into base64 representation. After ``lineLen`` characters, a
|
||||
## ``newline`` is added.
|
||||
##
|
||||
## 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)
|
||||
|
||||
proc decodeByte(b: char): int {.inline.} =
|
||||
@@ -123,8 +150,15 @@ proc decodeByte(b: char): int {.inline.} =
|
||||
else: result = 63
|
||||
|
||||
proc decode*(s: string): string =
|
||||
## decodes a string in base64 representation back into its original form.
|
||||
## Whitespace is skipped.
|
||||
## Decodes string ``s`` in base64 representation back into its original form.
|
||||
## 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'}
|
||||
var total = ((len(s) + 3) div 4) * 3
|
||||
# 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.
|
||||
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) =
|
||||
## Sets a cookie.
|
||||
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.
|
||||
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] =
|
||||
if n != nil:
|
||||
# XXX actually we could compute the necessary stack size in advance:
|
||||
|
||||
@@ -20,41 +20,59 @@
|
||||
## 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 deq = initDeque[int]() # initializes the object
|
||||
## for i in 1 ..< a: deq.addLast i # populates the deque
|
||||
## import deques
|
||||
##
|
||||
## if b < deq.len: # checking before indexed access
|
||||
## echo "The element at index position ", b, " is ", deq[b]
|
||||
## var a = initDeque[int]()
|
||||
##
|
||||
## # 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 deq.peekFirst == 1
|
||||
## assert deq.peekLast == a
|
||||
## doAssertRaises(IndexError, echo a[0])
|
||||
##
|
||||
## while deq.len > 0: # checking if the deque is empty
|
||||
## echo deq.popLast()
|
||||
## for i in 1 .. 5:
|
||||
## a.addLast(10*i)
|
||||
## assert $a == "[10, 20, 30, 40, 50]"
|
||||
##
|
||||
## Note: For inter thread communication use
|
||||
## a `Channel <channels.html>`_ instead.
|
||||
## assert a.peekFirst == 10
|
||||
## 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
|
||||
|
||||
type
|
||||
Deque*[T] = object
|
||||
## A double-ended queue backed with a ringed seq buffer.
|
||||
##
|
||||
## To initialize an empty deque use `initDeque proc <#initDeque,int>`_.
|
||||
data: seq[T]
|
||||
head, tail, count, mask: int
|
||||
|
||||
proc initDeque*[T](initialSize: int = 4): Deque[T] =
|
||||
## Create a new 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.
|
||||
## Create a new empty deque.
|
||||
##
|
||||
## `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.
|
||||
## 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`` 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)
|
||||
result.mask = initialSize-1
|
||||
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
|
||||
raise newException(IndexError,
|
||||
"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.} =
|
||||
## Access the i-th element of `deq` by order from first to last.
|
||||
## deq[0] is the first, deq[^1] is the last.
|
||||
## Access the i-th element of `deq`.
|
||||
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)
|
||||
return deq.data[(deq.head + i) and deq.mask]
|
||||
|
||||
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.
|
||||
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)
|
||||
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`.
|
||||
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)
|
||||
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 =
|
||||
## 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
|
||||
for c in 0 ..< deq.count:
|
||||
yield deq.data[i]
|
||||
i = (i + 1) and deq.mask
|
||||
|
||||
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
|
||||
for c in 0 ..< deq.count:
|
||||
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] =
|
||||
## 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
|
||||
for c in 0 ..< deq.count:
|
||||
yield (c, deq.data[i])
|
||||
i = (i + 1) and deq.mask
|
||||
|
||||
proc contains*[T](deq: Deque[T], item: T): bool {.inline.} =
|
||||
## Return true if `item` is in `deq` or false if not found. Usually used
|
||||
## via the ``in`` operator. It is the equivalent of ``deq.find(item) >= 0``.
|
||||
## Return true if `item` is in `deq` or false if not found.
|
||||
##
|
||||
## Usually used via the ``in`` operator.
|
||||
## It is the equivalent of ``deq.find(item) >= 0``.
|
||||
##
|
||||
## .. code-block:: Nim
|
||||
## if x in q:
|
||||
## assert q.contains x
|
||||
## assert q.contains(x)
|
||||
for e in deq:
|
||||
if e == item: return true
|
||||
return false
|
||||
@@ -138,6 +268,19 @@ proc expandIfNeeded[T](deq: var Deque[T]) =
|
||||
|
||||
proc addFirst*[T](deq: var Deque[T], item: T) =
|
||||
## 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)
|
||||
inc deq.count
|
||||
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) =
|
||||
## 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)
|
||||
inc deq.count
|
||||
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.}=
|
||||
## 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)
|
||||
result = deq.data[deq.head]
|
||||
|
||||
proc peekLast*[T](deq: Deque[T]): T {.inline.} =
|
||||
## 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)
|
||||
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.} =
|
||||
## 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)
|
||||
dec deq.count
|
||||
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.} =
|
||||
## 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)
|
||||
dec deq.count
|
||||
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.} =
|
||||
## 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)
|
||||
deq.count = 0
|
||||
deq.tail = deq.head
|
||||
|
||||
proc shrink*[T](deq: var Deque[T], fromFirst = 0, fromLast = 0) =
|
||||
## Remove `fromFirst` elements from the front of the deque and
|
||||
## `fromLast` elements from the back. If the supplied number of
|
||||
## elements exceeds the total number of elements in the deque,
|
||||
## the deque will remain empty.
|
||||
## `fromLast` elements from the back.
|
||||
##
|
||||
## 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:
|
||||
clear(deq)
|
||||
return
|
||||
@@ -214,6 +456,8 @@ proc `$`*[T](deq: Deque[T]): string =
|
||||
result.addQuoted(x)
|
||||
result.add("]")
|
||||
|
||||
|
||||
|
||||
when isMainModule:
|
||||
var deq = initDeque[int](1)
|
||||
deq.addLast(4)
|
||||
|
||||
@@ -9,9 +9,12 @@
|
||||
|
||||
## The ``intsets`` module implements an efficient int set implemented as a
|
||||
## `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
|
||||
## copy; use ``assign`` to get a deep copy.
|
||||
|
||||
## **Note**: Currently the assignment operator ``=`` for ``intsets``
|
||||
## 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
|
||||
hashes, math
|
||||
@@ -94,7 +97,7 @@ proc intSetPut(t: var IntSet, key: int): PTrunk =
|
||||
t.data[h] = result
|
||||
|
||||
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:
|
||||
for i in 0..<s.elems:
|
||||
if s.a[i] == key: return true
|
||||
@@ -107,7 +110,7 @@ proc contains*(s: IntSet, key: int): bool =
|
||||
result = false
|
||||
|
||||
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:
|
||||
for i in 0..<s.elems:
|
||||
yield s.a[i]
|
||||
@@ -135,7 +138,7 @@ proc bitincl(s: var IntSet, key: int) {.inline.} =
|
||||
`shl`(1, u and IntMask)
|
||||
|
||||
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:
|
||||
for i in 0..<s.elems:
|
||||
if s.a[i] == key: return
|
||||
@@ -170,7 +173,7 @@ proc exclImpl(s: var IntSet, key: int) =
|
||||
not `shl`(1, u and IntMask)
|
||||
|
||||
proc excl*(s: var IntSet, key: int) =
|
||||
## excludes `key` from the set `s`.
|
||||
## Excludes `key` from the set `s`.
|
||||
exclImpl(s, key)
|
||||
|
||||
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)
|
||||
|
||||
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.
|
||||
var count = s.elems
|
||||
exclImpl(s, key)
|
||||
result = count == s.elems
|
||||
|
||||
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.
|
||||
if s.elems <= s.a.len:
|
||||
for i in 0..<s.elems:
|
||||
@@ -206,23 +209,28 @@ proc containsOrIncl*(s: var IntSet, key: int): bool =
|
||||
result = false
|
||||
|
||||
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)
|
||||
#result.max = InitIntSetSize-1
|
||||
when defined(nimNoNilSeqs):
|
||||
result.data = @[]
|
||||
else:
|
||||
result.data = nil
|
||||
result.max = 0
|
||||
result.counter = 0
|
||||
result.head = nil
|
||||
result.elems = 0
|
||||
# newSeq(result.data, InitIntSetSize)
|
||||
# result.max = InitIntSetSize-1
|
||||
result = IntSet(
|
||||
elems: 0,
|
||||
counter: 0,
|
||||
max: 0,
|
||||
head: nil,
|
||||
data: when defined(nimNoNilSeqs): @[] else: nil)
|
||||
# a: array[0..33, int] # profiling shows that 34 elements are enough
|
||||
|
||||
proc clear*(result: var IntSet) =
|
||||
#setLen(result.data, InitIntSetSize)
|
||||
#for i in 0..InitIntSetSize-1: result.data[i] = nil
|
||||
#result.max = InitIntSetSize-1
|
||||
## Clears the IntSet back to an empty state.
|
||||
|
||||
# setLen(result.data, InitIntSetSize)
|
||||
# for i in 0..InitIntSetSize-1: result.data[i] = nil
|
||||
# result.max = InitIntSetSize-1
|
||||
when defined(nimNoNilSeqs):
|
||||
result.data = @[]
|
||||
else:
|
||||
@@ -304,7 +312,7 @@ proc `-`*(s1, s2: IntSet): IntSet {.inline.} =
|
||||
result = difference(s1, s2)
|
||||
|
||||
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:
|
||||
if contains(s2, item):
|
||||
return false
|
||||
@@ -320,7 +328,7 @@ proc len*(s: IntSet): int {.inline.} =
|
||||
inc(result)
|
||||
|
||||
proc card*(s: IntSet): int {.inline.} =
|
||||
## alias for `len() <#len>` _.
|
||||
## Alias for `len() <#len>` _.
|
||||
result = s.len()
|
||||
|
||||
proc `<=`*(s1, s2: IntSet): bool =
|
||||
@@ -349,12 +357,6 @@ proc `$`*(s: IntSet): string =
|
||||
## The `$` operator for int sets.
|
||||
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:
|
||||
import sequtils, algorithm
|
||||
|
||||
|
||||
@@ -7,34 +7,112 @@
|
||||
# distribution, for details about the copyright.
|
||||
#
|
||||
|
||||
## Implementation of singly and doubly linked lists. Because it makes no sense
|
||||
## to do so, the 'next' and 'prev' pointers are not hidden from you and can
|
||||
## be manipulated directly for efficiency.
|
||||
## Implementation of:
|
||||
## * `singly linked lists <#SinglyLinkedList>`_
|
||||
## * `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):
|
||||
{.pragma: dirty.}
|
||||
|
||||
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]
|
||||
value*: 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]
|
||||
value*: 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]
|
||||
|
||||
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]
|
||||
|
||||
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]
|
||||
|
||||
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]
|
||||
|
||||
SomeLinkedList*[T] = SinglyLinkedList[T] | DoublyLinkedList[T]
|
||||
@@ -46,28 +124,44 @@ type
|
||||
SomeLinkedNode*[T] = SinglyLinkedNode[T] | DoublyLinkedNode[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
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
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)
|
||||
result.value = value
|
||||
|
||||
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)
|
||||
result.value = value
|
||||
|
||||
@@ -86,24 +180,100 @@ template itemsRingImpl() {.dirty.} =
|
||||
if it == L.head: break
|
||||
|
||||
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()
|
||||
|
||||
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()
|
||||
|
||||
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()
|
||||
|
||||
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()
|
||||
|
||||
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.
|
||||
##
|
||||
## 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
|
||||
while it != nil:
|
||||
var nxt = it.next
|
||||
@@ -111,8 +281,24 @@ iterator nodes*[T](L: SomeLinkedList[T]): SomeLinkedNode[T] =
|
||||
it = nxt
|
||||
|
||||
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.
|
||||
##
|
||||
## 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
|
||||
if it != nil:
|
||||
while true:
|
||||
@@ -122,7 +308,7 @@ iterator nodes*[T](L: SomeLinkedRing[T]): SomeLinkedNode[T] =
|
||||
if it == L.head: break
|
||||
|
||||
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 = "["
|
||||
for x in nodes(L):
|
||||
if result.len > 1: result.add(", ")
|
||||
@@ -130,19 +316,54 @@ proc `$`*[T](L: SomeLinkedCollection[T]): string =
|
||||
result.add("]")
|
||||
|
||||
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.
|
||||
##
|
||||
## 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):
|
||||
if x.value == value: return x
|
||||
|
||||
proc contains*[T](L: SomeLinkedCollection[T], value: T): bool {.inline.} =
|
||||
## searches in the list for a value. Returns false if the value does not
|
||||
## exist, true otherwise.
|
||||
## Searches in the list for a value. Returns `false` if the value does not
|
||||
## 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
|
||||
|
||||
proc append*[T](L: var SinglyLinkedList[T],
|
||||
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
|
||||
if L.tail != nil:
|
||||
assert(L.tail.next == nil)
|
||||
@@ -151,22 +372,75 @@ proc append*[T](L: var SinglyLinkedList[T],
|
||||
if L.head == nil: L.head = n
|
||||
|
||||
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))
|
||||
|
||||
proc prepend*[T](L: var SinglyLinkedList[T],
|
||||
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
|
||||
L.head = n
|
||||
if L.tail == nil: L.tail = n
|
||||
|
||||
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))
|
||||
|
||||
|
||||
|
||||
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.prev = L.tail
|
||||
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
|
||||
|
||||
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))
|
||||
|
||||
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.next = L.head
|
||||
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
|
||||
|
||||
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))
|
||||
|
||||
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.head: L.head = n.next
|
||||
if n.next != nil: n.next.prev = n.prev
|
||||
if n.prev != nil: n.prev.next = n.next
|
||||
|
||||
|
||||
|
||||
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:
|
||||
n.next = L.head
|
||||
assert(L.tail != nil)
|
||||
@@ -213,11 +554,36 @@ proc append*[T](L: var SinglyLinkedRing[T], n: SinglyLinkedNode[T]) =
|
||||
L.tail = n
|
||||
|
||||
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))
|
||||
|
||||
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:
|
||||
n.next = L.head
|
||||
assert(L.tail != nil)
|
||||
@@ -228,11 +594,40 @@ proc prepend*[T](L: var SinglyLinkedRing[T], n: SinglyLinkedNode[T]) =
|
||||
L.head = n
|
||||
|
||||
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))
|
||||
|
||||
|
||||
|
||||
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:
|
||||
n.next = L.head
|
||||
n.prev = L.head.prev
|
||||
@@ -244,11 +639,40 @@ proc append*[T](L: var DoublyLinkedRing[T], n: DoublyLinkedNode[T]) =
|
||||
L.head = n
|
||||
|
||||
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))
|
||||
|
||||
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:
|
||||
n.next = L.head
|
||||
n.prev = L.head.prev
|
||||
@@ -260,11 +684,34 @@ proc prepend*[T](L: var DoublyLinkedRing[T], n: DoublyLinkedNode[T]) =
|
||||
L.head = n
|
||||
|
||||
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))
|
||||
|
||||
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.prev.next = n.next
|
||||
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)
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user