Merge branch 'devel' into expand-amb-identifier-output
This commit is contained in:
commit
8cadeb9605
99 changed files with 3389 additions and 1421 deletions
1
.github/ISSUE_TEMPLATE/bug_report.md
vendored
1
.github/ISSUE_TEMPLATE/bug_report.md
vendored
|
|
@ -38,6 +38,7 @@ Hello World!
|
||||||
* Your Nim version (output of `nim -v`).
|
* Your Nim version (output of `nim -v`).
|
||||||
* Was it working in the previous Nim releases?
|
* Was it working in the previous Nim releases?
|
||||||
* A link to a related issue or discussion.
|
* A link to a related issue or discussion.
|
||||||
|
* A project reference where (and how) the issue causes problems.
|
||||||
-->
|
-->
|
||||||
```
|
```
|
||||||
$ nim -v
|
$ nim -v
|
||||||
|
|
|
||||||
4
.gitignore
vendored
4
.gitignore
vendored
|
|
@ -52,6 +52,8 @@ xcuserdata/
|
||||||
/compiler/nim.dot
|
/compiler/nim.dot
|
||||||
/reject.json
|
/reject.json
|
||||||
/run.json
|
/run.json
|
||||||
|
*.json
|
||||||
|
/pkgstemp/**/*
|
||||||
# for `nim doc foo.nim`
|
# for `nim doc foo.nim`
|
||||||
/*.html
|
/*.html
|
||||||
lib/**/*.html
|
lib/**/*.html
|
||||||
|
|
@ -59,6 +61,8 @@ lib/**/*.html
|
||||||
|
|
||||||
/testresults.json
|
/testresults.json
|
||||||
testament.db
|
testament.db
|
||||||
|
/tests/**/*.json
|
||||||
|
/tests/**/*.js
|
||||||
/csources
|
/csources
|
||||||
dist/
|
dist/
|
||||||
|
|
||||||
|
|
|
||||||
21
changelog.md
21
changelog.md
|
|
@ -47,11 +47,17 @@
|
||||||
|
|
||||||
- `system.ValueError` now inherits from `system.CatchableError` instead of `system.Defect`.
|
- `system.ValueError` now inherits from `system.CatchableError` instead of `system.Defect`.
|
||||||
|
|
||||||
- The procs `parseutils.parseBiggsetInt`, `parseutils.parseInt`,
|
- The procs `parseutils.parseBiggestInt`, `parseutils.parseInt`,
|
||||||
`parseutils.parseBiggestUInt` and `parseutils.parseUInt` now raise a
|
`parseutils.parseBiggestUInt` and `parseutils.parseUInt` now raise a
|
||||||
`ValueError` when the parsed integer is outside of the valid range.
|
`ValueError` when the parsed integer is outside of the valid range.
|
||||||
Previously they sometimes raised a `OverflowError` and sometimes returned `0`.
|
Previously they sometimes raised a `OverflowError` and sometimes returned `0`.
|
||||||
|
|
||||||
|
- The procs `parseutils.parseBin`, `parseutils.parseOct` and `parseutils.parseHex`
|
||||||
|
were not clearing their `var` parameter `number` and used to push its value to
|
||||||
|
the left when storing the parsed string into it. Now they always set the value
|
||||||
|
of the parameter to `0` before storing the result of the parsing, unless the
|
||||||
|
string to parse is not valid and then the value of `number` is not changed.
|
||||||
|
|
||||||
- `streams.StreamObject` now restricts its fields to only raise `system.Defect`,
|
- `streams.StreamObject` now restricts its fields to only raise `system.Defect`,
|
||||||
`system.IOError` and `system.OSError`.
|
`system.IOError` and `system.OSError`.
|
||||||
This change only affects custom stream implementations.
|
This change only affects custom stream implementations.
|
||||||
|
|
@ -91,6 +97,7 @@
|
||||||
- Custom types that should be supported by `strformat` (&) now need an
|
- Custom types that should be supported by `strformat` (&) now need an
|
||||||
explicit overload of `formatValue`.
|
explicit overload of `formatValue`.
|
||||||
|
|
||||||
|
|
||||||
#### Breaking changes in the compiler
|
#### Breaking changes in the compiler
|
||||||
|
|
||||||
- The compiler now implements the "generic symbol prepass" for `when` statements
|
- The compiler now implements the "generic symbol prepass" for `when` statements
|
||||||
|
|
@ -107,6 +114,7 @@ proc enumToString*(enums: openArray[enum]): string =
|
||||||
```
|
```
|
||||||
|
|
||||||
- ``discard x`` is now illegal when `x` is a function symbol.
|
- ``discard x`` is now illegal when `x` is a function symbol.
|
||||||
|
|
||||||
- Implicit imports via ``--import: module`` in a config file are now restricted
|
- Implicit imports via ``--import: module`` in a config file are now restricted
|
||||||
to the main package.
|
to the main package.
|
||||||
|
|
||||||
|
|
@ -152,6 +160,9 @@ proc enumToString*(enums: openArray[enum]): string =
|
||||||
|
|
||||||
- Added `system.default`.
|
- Added `system.default`.
|
||||||
|
|
||||||
|
- Added `sequtils.items` for closure iterators, allows closure iterators
|
||||||
|
to be used by the the mapIt, filterIt, allIt, anyIt, etc.
|
||||||
|
|
||||||
|
|
||||||
### Library changes
|
### Library changes
|
||||||
|
|
||||||
|
|
@ -179,6 +190,7 @@ proc enumToString*(enums: openArray[enum]): string =
|
||||||
differently.
|
differently.
|
||||||
|
|
||||||
- `securehash` is moved to `lib/deprecated`
|
- `securehash` is moved to `lib/deprecated`
|
||||||
|
|
||||||
- The switch ``-d:useWinAnsi`` is not supported anymore.
|
- The switch ``-d:useWinAnsi`` is not supported anymore.
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -202,14 +214,19 @@ proc enumToString*(enums: openArray[enum]): string =
|
||||||
it's more recognizable and allows tools like github to recognize it as Nim,
|
it's more recognizable and allows tools like github to recognize it as Nim,
|
||||||
see [#9647](https://github.com/nim-lang/Nim/issues/9647).
|
see [#9647](https://github.com/nim-lang/Nim/issues/9647).
|
||||||
The previous extension will continue to work.
|
The previous extension will continue to work.
|
||||||
|
|
||||||
- Pragma syntax is now consistent. Previous syntax where type pragmas did not
|
- Pragma syntax is now consistent. Previous syntax where type pragmas did not
|
||||||
follow the type name is now deprecated. Also pragma before generic parameter
|
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
|
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
|
[#8514](https://github.com/nim-lang/Nim/issues/8514) and
|
||||||
[#1872](https://github.com/nim-lang/Nim/issues/1872) for further details.
|
[#1872](https://github.com/nim-lang/Nim/issues/1872) for further details.
|
||||||
|
|
||||||
|
- Hash sets and tables are initialized by default. The explicit `initHashSet`,
|
||||||
|
`initTable`, etc. are not needed anymore.
|
||||||
|
|
||||||
|
|
||||||
### Tool changes
|
### Tool changes
|
||||||
|
|
||||||
- `jsondoc` now include a `moduleDescription` field with the module
|
- `jsondoc` now include a `moduleDescription` field with the module
|
||||||
description. `jsondoc0` shows comments as it's own objects as shown in the
|
description. `jsondoc0` shows comments as it's own objects as shown in the
|
||||||
documentation.
|
documentation.
|
||||||
|
|
@ -218,6 +235,7 @@ proc enumToString*(enums: openArray[enum]): string =
|
||||||
|
|
||||||
|
|
||||||
### Compiler changes
|
### Compiler changes
|
||||||
|
|
||||||
- The deprecated `fmod` proc is now unavailable on the VM'.
|
- The deprecated `fmod` proc is now unavailable on the VM'.
|
||||||
- A new `--outdir` option was added.
|
- A new `--outdir` option was added.
|
||||||
- The compiled JavaScript file for the project produced by executing `nim js`
|
- The compiled JavaScript file for the project produced by executing `nim js`
|
||||||
|
|
@ -226,4 +244,5 @@ proc enumToString*(enums: openArray[enum]): string =
|
||||||
The compiler also provides a new more flexible API for handling the
|
The compiler also provides a new more flexible API for handling the
|
||||||
hot code reloading events in the code.
|
hot code reloading events in the code.
|
||||||
|
|
||||||
|
|
||||||
### Bugfixes
|
### Bugfixes
|
||||||
|
|
|
||||||
|
|
@ -644,7 +644,7 @@ type
|
||||||
mSwap, mIsNil, mArrToSeq, mCopyStr, mCopyStrLast,
|
mSwap, mIsNil, mArrToSeq, mCopyStr, mCopyStrLast,
|
||||||
mNewString, mNewStringOfCap, mParseBiggestFloat,
|
mNewString, mNewStringOfCap, mParseBiggestFloat,
|
||||||
mMove, mWasMoved, mDestroy,
|
mMove, mWasMoved, mDestroy,
|
||||||
mDefault, mAccessEnv, mReset,
|
mDefault, mUnown, mAccessEnv, mReset,
|
||||||
mArray, mOpenArray, mRange, mSet, mSeq, mOpt, mVarargs,
|
mArray, mOpenArray, mRange, mSet, mSeq, mOpt, mVarargs,
|
||||||
mRef, mPtr, mVar, mDistinct, mVoid, mTuple,
|
mRef, mPtr, mVar, mDistinct, mVoid, mTuple,
|
||||||
mOrdinal,
|
mOrdinal,
|
||||||
|
|
@ -877,6 +877,13 @@ type
|
||||||
|
|
||||||
TTypeSeq* = seq[PType]
|
TTypeSeq* = seq[PType]
|
||||||
TLockLevel* = distinct int16
|
TLockLevel* = distinct int16
|
||||||
|
|
||||||
|
TTypeAttachedOp* = enum ## as usual, order is important here
|
||||||
|
attachedDestructor,
|
||||||
|
attachedAsgn,
|
||||||
|
attachedSink,
|
||||||
|
attachedDeepCopy
|
||||||
|
|
||||||
TType* {.acyclic.} = object of TIdObj # \
|
TType* {.acyclic.} = object of TIdObj # \
|
||||||
# types are identical iff they have the
|
# types are identical iff they have the
|
||||||
# same id; there may be multiple copies of a type
|
# same id; there may be multiple copies of a type
|
||||||
|
|
@ -897,12 +904,7 @@ type
|
||||||
owner*: PSym # the 'owner' of the type
|
owner*: PSym # the 'owner' of the type
|
||||||
sym*: PSym # types have the sym associated with them
|
sym*: PSym # types have the sym associated with them
|
||||||
# it is used for converting types to strings
|
# it is used for converting types to strings
|
||||||
destructor*: PSym # destructor. warning: nil here may not necessary
|
attachedOps*: array[TTypeAttachedOp, PSym] # destructors, etc.
|
||||||
# mean that there is no destructor.
|
|
||||||
# see instantiateDestructor in semdestruct.nim
|
|
||||||
deepCopy*: PSym # overriden 'deepCopy' operation
|
|
||||||
assignment*: PSym # overriden '=' operation
|
|
||||||
sink*: PSym # overriden '=sink' operation
|
|
||||||
methods*: seq[(int,PSym)] # attached methods
|
methods*: seq[(int,PSym)] # attached methods
|
||||||
size*: BiggestInt # the size of the type in bytes
|
size*: BiggestInt # the size of the type in bytes
|
||||||
# -1 means that the size is unkwown
|
# -1 means that the size is unkwown
|
||||||
|
|
@ -1275,6 +1277,7 @@ const
|
||||||
UnspecifiedLockLevel* = TLockLevel(-1'i16)
|
UnspecifiedLockLevel* = TLockLevel(-1'i16)
|
||||||
MaxLockLevel* = 1000'i16
|
MaxLockLevel* = 1000'i16
|
||||||
UnknownLockLevel* = TLockLevel(1001'i16)
|
UnknownLockLevel* = TLockLevel(1001'i16)
|
||||||
|
AttachedOpToStr*: array[TTypeAttachedOp, string] = ["=destroy", "=", "=sink", "=deepcopy"]
|
||||||
|
|
||||||
proc `$`*(x: TLockLevel): string =
|
proc `$`*(x: TLockLevel): string =
|
||||||
if x.ord == UnspecifiedLockLevel.ord: result = "<unspecified>"
|
if x.ord == UnspecifiedLockLevel.ord: result = "<unspecified>"
|
||||||
|
|
@ -1341,10 +1344,7 @@ proc assignType*(dest, src: PType) =
|
||||||
dest.n = src.n
|
dest.n = src.n
|
||||||
dest.size = src.size
|
dest.size = src.size
|
||||||
dest.align = src.align
|
dest.align = src.align
|
||||||
dest.destructor = src.destructor
|
dest.attachedOps = src.attachedOps
|
||||||
dest.deepCopy = src.deepCopy
|
|
||||||
dest.sink = src.sink
|
|
||||||
dest.assignment = src.assignment
|
|
||||||
dest.lockLevel = src.lockLevel
|
dest.lockLevel = src.lockLevel
|
||||||
# this fixes 'type TLock = TSysLock':
|
# this fixes 'type TLock = TSysLock':
|
||||||
if src.sym != nil:
|
if src.sym != nil:
|
||||||
|
|
@ -1831,3 +1831,7 @@ proc addParam*(procType: PType; param: PSym) =
|
||||||
param.position = procType.len-1
|
param.position = procType.len-1
|
||||||
addSon(procType.n, newSymNode(param))
|
addSon(procType.n, newSymNode(param))
|
||||||
rawAddSon(procType, param.typ)
|
rawAddSon(procType, param.typ)
|
||||||
|
|
||||||
|
template destructor*(t: PType): PSym = t.attachedOps[attachedDestructor]
|
||||||
|
template assignment*(t: PType): PSym = t.attachedOps[attachedAsgn]
|
||||||
|
template asink*(t: PType): PSym = t.attachedOps[attachedSink]
|
||||||
|
|
|
||||||
|
|
@ -46,13 +46,23 @@ proc fixupCall(p: BProc, le, ri: PNode, d: var TLoc,
|
||||||
genAssignment(p, d, tmp, {}) # no need for deep copying
|
genAssignment(p, d, tmp, {}) # no need for deep copying
|
||||||
else:
|
else:
|
||||||
add(pl, ~")")
|
add(pl, ~")")
|
||||||
if p.module.compileToCpp and lfSingleUse in d.flags:
|
if p.module.compileToCpp:
|
||||||
# do not generate spurious temporaries for C++! For C we're better off
|
if lfSingleUse in d.flags:
|
||||||
# with them to prevent undefined behaviour and because the codegen
|
# do not generate spurious temporaries for C++! For C we're better off
|
||||||
# is free to emit expressions multiple times!
|
# with them to prevent undefined behaviour and because the codegen
|
||||||
d.k = locCall
|
# is free to emit expressions multiple times!
|
||||||
d.r = pl
|
d.k = locCall
|
||||||
excl d.flags, lfSingleUse
|
d.r = pl
|
||||||
|
excl d.flags, lfSingleUse
|
||||||
|
else:
|
||||||
|
if d.k == locNone and p.splitDecls == 0:
|
||||||
|
getTempCpp(p, typ.sons[0], d, pl)
|
||||||
|
else:
|
||||||
|
if d.k == locNone: getTemp(p, typ.sons[0], d)
|
||||||
|
var list: TLoc
|
||||||
|
initLoc(list, locCall, d.lode, OnUnknown)
|
||||||
|
list.r = pl
|
||||||
|
genAssignment(p, d, list, {}) # no need for deep copying
|
||||||
else:
|
else:
|
||||||
if d.k == locNone: getTemp(p, typ.sons[0], d)
|
if d.k == locNone: getTemp(p, typ.sons[0], d)
|
||||||
assert(d.t != nil) # generate an assignment to d:
|
assert(d.t != nil) # generate an assignment to d:
|
||||||
|
|
@ -218,10 +228,12 @@ proc genClosureCall(p: BProc, le, ri: PNode, d: var TLoc) =
|
||||||
genParamLoop(pl)
|
genParamLoop(pl)
|
||||||
|
|
||||||
template genCallPattern {.dirty.} =
|
template genCallPattern {.dirty.} =
|
||||||
lineF(p, cpsStmts, callPattern & ";$n", [rdLoc(op), pl, pl.addComma, rawProc])
|
if tfIterator in typ.flags:
|
||||||
|
lineF(p, cpsStmts, PatIter & ";$n", [rdLoc(op), pl, pl.addComma, rawProc])
|
||||||
|
else:
|
||||||
|
lineF(p, cpsStmts, PatProc & ";$n", [rdLoc(op), pl, pl.addComma, rawProc])
|
||||||
|
|
||||||
let rawProc = getRawProcType(p, typ)
|
let rawProc = getRawProcType(p, typ)
|
||||||
let callPattern = if tfIterator in typ.flags: PatIter else: PatProc
|
|
||||||
if typ.sons[0] != nil:
|
if typ.sons[0] != nil:
|
||||||
if isInvalidReturnType(p.config, typ.sons[0]):
|
if isInvalidReturnType(p.config, typ.sons[0]):
|
||||||
if sonsLen(ri) > 1: add(pl, ~", ")
|
if sonsLen(ri) > 1: add(pl, ~", ")
|
||||||
|
|
@ -246,7 +258,11 @@ proc genClosureCall(p: BProc, le, ri: PNode, d: var TLoc) =
|
||||||
assert(d.t != nil) # generate an assignment to d:
|
assert(d.t != nil) # generate an assignment to d:
|
||||||
var list: TLoc
|
var list: TLoc
|
||||||
initLoc(list, locCall, d.lode, OnUnknown)
|
initLoc(list, locCall, d.lode, OnUnknown)
|
||||||
list.r = callPattern % [rdLoc(op), pl, pl.addComma, rawProc]
|
if tfIterator in typ.flags:
|
||||||
|
list.r = PatIter % [rdLoc(op), pl, pl.addComma, rawProc]
|
||||||
|
else:
|
||||||
|
list.r = PatProc % [rdLoc(op), pl, pl.addComma, rawProc]
|
||||||
|
|
||||||
genAssignment(p, d, list, {}) # no need for deep copying
|
genAssignment(p, d, list, {}) # no need for deep copying
|
||||||
else:
|
else:
|
||||||
genCallPattern()
|
genCallPattern()
|
||||||
|
|
|
||||||
|
|
@ -99,17 +99,17 @@ proc bitSetToWord(s: TBitSet, size: int): BiggestInt =
|
||||||
if j < len(s): result = result or (ze64(s[j]) shl (j * 8))
|
if j < len(s): result = result or (ze64(s[j]) shl (j * 8))
|
||||||
|
|
||||||
proc genRawSetData(cs: TBitSet, size: int): Rope =
|
proc genRawSetData(cs: TBitSet, size: int): Rope =
|
||||||
var frmt: FormatStr
|
|
||||||
if size > 8:
|
if size > 8:
|
||||||
result = "{$n" % []
|
result = "{$n" % []
|
||||||
for i in countup(0, size - 1):
|
for i in countup(0, size - 1):
|
||||||
if i < size - 1:
|
if i < size - 1:
|
||||||
# not last iteration?
|
# not last iteration?
|
||||||
if (i + 1) mod 8 == 0: frmt = "0x$1,$n"
|
if (i + 1) mod 8 == 0:
|
||||||
else: frmt = "0x$1, "
|
addf(result, "0x$1,$n", [rope(toHex(ze64(cs[i]), 2))])
|
||||||
|
else:
|
||||||
|
addf(result, "0x$1, ", [rope(toHex(ze64(cs[i]), 2))])
|
||||||
else:
|
else:
|
||||||
frmt = "0x$1}$n"
|
addf(result, "0x$1}$n", [rope(toHex(ze64(cs[i]), 2))])
|
||||||
addf(result, frmt, [rope(toHex(ze64(cs[i]), 2))])
|
|
||||||
else:
|
else:
|
||||||
result = intLiteral(bitSetToWord(cs, size))
|
result = intLiteral(bitSetToWord(cs, size))
|
||||||
# result := rope('0x' + ToHex(bitSetToWord(cs, size), size * 2))
|
# result := rope('0x' + ToHex(bitSetToWord(cs, size), size * 2))
|
||||||
|
|
@ -510,10 +510,7 @@ proc binaryArithOverflow(p: BProc, e: PNode, d: var TLoc, m: TMagic) =
|
||||||
"mulInt64", "divInt64", "modInt64",
|
"mulInt64", "divInt64", "modInt64",
|
||||||
"addInt64", "subInt64"
|
"addInt64", "subInt64"
|
||||||
]
|
]
|
||||||
opr: array[mAddI..mPred, string] = [
|
opr: array[mAddI..mPred, string] = ["+", "-", "*", "/", "%", "+", "-"]
|
||||||
"($#)($# + $#)", "($#)($# - $#)", "($#)($# * $#)",
|
|
||||||
"($#)($# / $#)", "($#)($# % $#)",
|
|
||||||
"($#)($# + $#)", "($#)($# - $#)"]
|
|
||||||
var a, b: TLoc
|
var a, b: TLoc
|
||||||
assert(e.sons[1].typ != nil)
|
assert(e.sons[1].typ != nil)
|
||||||
assert(e.sons[2].typ != nil)
|
assert(e.sons[2].typ != nil)
|
||||||
|
|
@ -523,7 +520,7 @@ proc binaryArithOverflow(p: BProc, e: PNode, d: var TLoc, m: TMagic) =
|
||||||
# later via 'chckRange'
|
# later via 'chckRange'
|
||||||
let t = e.typ.skipTypes(abstractRange)
|
let t = e.typ.skipTypes(abstractRange)
|
||||||
if optOverflowCheck notin p.options:
|
if optOverflowCheck notin p.options:
|
||||||
let res = opr[m] % [getTypeDesc(p.module, e.typ), rdLoc(a), rdLoc(b)]
|
let res = "($1)($2 $3 $4)" % [getTypeDesc(p.module, e.typ), rdLoc(a), rope(opr[m]), rdLoc(b)]
|
||||||
putIntoDest(p, d, e, res)
|
putIntoDest(p, d, e, res)
|
||||||
else:
|
else:
|
||||||
let res = binaryArithOverflowRaw(p, t, a, b,
|
let res = binaryArithOverflowRaw(p, t, a, b,
|
||||||
|
|
@ -531,11 +528,6 @@ proc binaryArithOverflow(p: BProc, e: PNode, d: var TLoc, m: TMagic) =
|
||||||
putIntoDest(p, d, e, "($#)($#)" % [getTypeDesc(p.module, e.typ), res])
|
putIntoDest(p, d, e, "($#)($#)" % [getTypeDesc(p.module, e.typ), res])
|
||||||
|
|
||||||
proc unaryArithOverflow(p: BProc, e: PNode, d: var TLoc, m: TMagic) =
|
proc unaryArithOverflow(p: BProc, e: PNode, d: var TLoc, m: TMagic) =
|
||||||
const
|
|
||||||
opr: array[mUnaryMinusI..mAbsI, string] = [
|
|
||||||
mUnaryMinusI: "((NI$2)-($1))",
|
|
||||||
mUnaryMinusI64: "-($1)",
|
|
||||||
mAbsI: "($1 > 0? ($1) : -($1))"]
|
|
||||||
var
|
var
|
||||||
a: TLoc
|
a: TLoc
|
||||||
t: PType
|
t: PType
|
||||||
|
|
@ -545,54 +537,17 @@ proc unaryArithOverflow(p: BProc, e: PNode, d: var TLoc, m: TMagic) =
|
||||||
if optOverflowCheck in p.options:
|
if optOverflowCheck in p.options:
|
||||||
linefmt(p, cpsStmts, "if ($1 == $2) #raiseOverflow();$n",
|
linefmt(p, cpsStmts, "if ($1 == $2) #raiseOverflow();$n",
|
||||||
[rdLoc(a), intLiteral(firstOrd(p.config, t))])
|
[rdLoc(a), intLiteral(firstOrd(p.config, t))])
|
||||||
putIntoDest(p, d, e, opr[m] % [rdLoc(a), rope(getSize(p.config, t) * 8)])
|
case m
|
||||||
|
of mUnaryMinusI:
|
||||||
|
putIntoDest(p, d, e, "((NI$2)-($1))" % [rdLoc(a), rope(getSize(p.config, t) * 8)])
|
||||||
|
of mUnaryMinusI64:
|
||||||
|
putIntoDest(p, d, e, "-($1)" % [rdLoc(a)])
|
||||||
|
of mAbsI:
|
||||||
|
putIntoDest(p, d, e, "($1 > 0? ($1) : -($1))" % [rdLoc(a)])
|
||||||
|
else:
|
||||||
|
assert(false, $m)
|
||||||
|
|
||||||
proc binaryArith(p: BProc, e: PNode, d: var TLoc, op: TMagic) =
|
proc binaryArith(p: BProc, e: PNode, d: var TLoc, op: TMagic) =
|
||||||
const
|
|
||||||
binArithTab: array[mAddF64..mXor, string] = [
|
|
||||||
"(($4)($1) + ($4)($2))", # AddF64
|
|
||||||
"(($4)($1) - ($4)($2))", # SubF64
|
|
||||||
"(($4)($1) * ($4)($2))", # MulF64
|
|
||||||
"(($4)($1) / ($4)($2))", # DivF64
|
|
||||||
"($4)((NU$5)($1) >> (NU$3)($2))", # ShrI
|
|
||||||
"($4)((NU$3)($1) << (NU$3)($2))", # ShlI
|
|
||||||
"($4)((NI$3)($1) >> (NU$3)($2))", # AshrI
|
|
||||||
"($4)($1 & $2)", # BitandI
|
|
||||||
"($4)($1 | $2)", # BitorI
|
|
||||||
"($4)($1 ^ $2)", # BitxorI
|
|
||||||
"(($1 <= $2) ? $1 : $2)", # MinI
|
|
||||||
"(($1 >= $2) ? $1 : $2)", # MaxI
|
|
||||||
"(($1 <= $2) ? $1 : $2)", # MinF64
|
|
||||||
"(($1 >= $2) ? $1 : $2)", # MaxF64
|
|
||||||
"($4)((NU$3)($1) + (NU$3)($2))", # AddU
|
|
||||||
"($4)((NU$3)($1) - (NU$3)($2))", # SubU
|
|
||||||
"($4)((NU$3)($1) * (NU$3)($2))", # MulU
|
|
||||||
"($4)((NU$3)($1) / (NU$3)($2))", # DivU
|
|
||||||
"($4)((NU$3)($1) % (NU$3)($2))", # ModU
|
|
||||||
"($1 == $2)", # EqI
|
|
||||||
"($1 <= $2)", # LeI
|
|
||||||
"($1 < $2)", # LtI
|
|
||||||
"($1 == $2)", # EqF64
|
|
||||||
"($1 <= $2)", # LeF64
|
|
||||||
"($1 < $2)", # LtF64
|
|
||||||
"((NU$3)($1) <= (NU$3)($2))", # LeU
|
|
||||||
"((NU$3)($1) < (NU$3)($2))", # LtU
|
|
||||||
"((NU64)($1) <= (NU64)($2))", # LeU64
|
|
||||||
"((NU64)($1) < (NU64)($2))", # LtU64
|
|
||||||
"($1 == $2)", # EqEnum
|
|
||||||
"($1 <= $2)", # LeEnum
|
|
||||||
"($1 < $2)", # LtEnum
|
|
||||||
"((NU8)($1) == (NU8)($2))", # EqCh
|
|
||||||
"((NU8)($1) <= (NU8)($2))", # LeCh
|
|
||||||
"((NU8)($1) < (NU8)($2))", # LtCh
|
|
||||||
"($1 == $2)", # EqB
|
|
||||||
"($1 <= $2)", # LeB
|
|
||||||
"($1 < $2)", # LtB
|
|
||||||
"($1 == $2)", # EqRef
|
|
||||||
"($1 == $2)", # EqPtr
|
|
||||||
"($1 <= $2)", # LePtr
|
|
||||||
"($1 < $2)", # LtPtr
|
|
||||||
"($1 != $2)"] # Xor
|
|
||||||
var
|
var
|
||||||
a, b: TLoc
|
a, b: TLoc
|
||||||
s, k: BiggestInt
|
s, k: BiggestInt
|
||||||
|
|
@ -603,9 +558,59 @@ proc binaryArith(p: BProc, e: PNode, d: var TLoc, op: TMagic) =
|
||||||
# BUGFIX: cannot use result-type here, as it may be a boolean
|
# BUGFIX: cannot use result-type here, as it may be a boolean
|
||||||
s = max(getSize(p.config, a.t), getSize(p.config, b.t)) * 8
|
s = max(getSize(p.config, a.t), getSize(p.config, b.t)) * 8
|
||||||
k = getSize(p.config, a.t) * 8
|
k = getSize(p.config, a.t) * 8
|
||||||
putIntoDest(p, d, e,
|
|
||||||
binArithTab[op] % [rdLoc(a), rdLoc(b), rope(s),
|
template applyFormat(frmt: untyped) =
|
||||||
getSimpleTypeDesc(p.module, e.typ), rope(k)])
|
putIntoDest(p, d, e, frmt % [
|
||||||
|
rdLoc(a), rdLoc(b), rope(s),
|
||||||
|
getSimpleTypeDesc(p.module, e.typ), rope(k)]
|
||||||
|
)
|
||||||
|
|
||||||
|
case op
|
||||||
|
of mAddF64: applyFormat("(($4)($1) + ($4)($2))")
|
||||||
|
of mSubF64: applyFormat("(($4)($1) - ($4)($2))")
|
||||||
|
of mMulF64: applyFormat("(($4)($1) * ($4)($2))")
|
||||||
|
of mDivF64: applyFormat("(($4)($1) / ($4)($2))")
|
||||||
|
of mShrI: applyFormat("($4)((NU$5)($1) >> (NU$3)($2))")
|
||||||
|
of mShlI: applyFormat("($4)((NU$3)($1) << (NU$3)($2))")
|
||||||
|
of mAshrI: applyFormat("($4)((NI$3)($1) >> (NU$3)($2))")
|
||||||
|
of mBitandI: applyFormat("($4)($1 & $2)")
|
||||||
|
of mBitorI: applyFormat("($4)($1 | $2)")
|
||||||
|
of mBitxorI: applyFormat("($4)($1 ^ $2)")
|
||||||
|
of mMinI: applyFormat("(($1 <= $2) ? $1 : $2)")
|
||||||
|
of mMaxI: applyFormat("(($1 >= $2) ? $1 : $2)")
|
||||||
|
of mMinF64: applyFormat("(($1 <= $2) ? $1 : $2)")
|
||||||
|
of mMaxF64: applyFormat("(($1 >= $2) ? $1 : $2)")
|
||||||
|
of mAddU: applyFormat("($4)((NU$3)($1) + (NU$3)($2))")
|
||||||
|
of mSubU: applyFormat("($4)((NU$3)($1) - (NU$3)($2))")
|
||||||
|
of mMulU: applyFormat("($4)((NU$3)($1) * (NU$3)($2))")
|
||||||
|
of mDivU: applyFormat("($4)((NU$3)($1) / (NU$3)($2))")
|
||||||
|
of mModU: applyFormat("($4)((NU$3)($1) % (NU$3)($2))")
|
||||||
|
of mEqI: applyFormat("($1 == $2)")
|
||||||
|
of mLeI: applyFormat("($1 <= $2)")
|
||||||
|
of mLtI: applyFormat("($1 < $2)")
|
||||||
|
of mEqF64: applyFormat("($1 == $2)")
|
||||||
|
of mLeF64: applyFormat("($1 <= $2)")
|
||||||
|
of mLtF64: applyFormat("($1 < $2)")
|
||||||
|
of mLeU: applyFormat("((NU$3)($1) <= (NU$3)($2))")
|
||||||
|
of mLtU: applyFormat("((NU$3)($1) < (NU$3)($2))")
|
||||||
|
of mLeU64: applyFormat("((NU64)($1) <= (NU64)($2))")
|
||||||
|
of mLtU64: applyFormat("((NU64)($1) < (NU64)($2))")
|
||||||
|
of mEqEnum: applyFormat("($1 == $2)")
|
||||||
|
of mLeEnum: applyFormat("($1 <= $2)")
|
||||||
|
of mLtEnum: applyFormat("($1 < $2)")
|
||||||
|
of mEqCh: applyFormat("((NU8)($1) == (NU8)($2))")
|
||||||
|
of mLeCh: applyFormat("((NU8)($1) <= (NU8)($2))")
|
||||||
|
of mLtCh: applyFormat("((NU8)($1) < (NU8)($2))")
|
||||||
|
of mEqB: applyFormat("($1 == $2)")
|
||||||
|
of mLeB: applyFormat("($1 <= $2)")
|
||||||
|
of mLtB: applyFormat("($1 < $2)")
|
||||||
|
of mEqRef: applyFormat("($1 == $2)")
|
||||||
|
of mEqUntracedRef: applyFormat("($1 == $2)")
|
||||||
|
of mLePtr: applyFormat("($1 <= $2)")
|
||||||
|
of mLtPtr: applyFormat("($1 < $2)")
|
||||||
|
of mXor: applyFormat("($1 != $2)")
|
||||||
|
else:
|
||||||
|
assert(false, $op)
|
||||||
|
|
||||||
proc genEqProc(p: BProc, e: PNode, d: var TLoc) =
|
proc genEqProc(p: BProc, e: PNode, d: var TLoc) =
|
||||||
var a, b: TLoc
|
var a, b: TLoc
|
||||||
|
|
@ -628,7 +633,8 @@ proc genIsNil(p: BProc, e: PNode, d: var TLoc) =
|
||||||
|
|
||||||
proc unaryArith(p: BProc, e: PNode, d: var TLoc, op: TMagic) =
|
proc unaryArith(p: BProc, e: PNode, d: var TLoc, op: TMagic) =
|
||||||
const
|
const
|
||||||
unArithTab: array[mNot..mToBiggestInt, string] = ["!($1)", # Not
|
unArithTab: array[mNot..mToBiggestInt, string] = [
|
||||||
|
"!($1)", # Not
|
||||||
"$1", # UnaryPlusI
|
"$1", # UnaryPlusI
|
||||||
"($3)((NU$2) ~($1))", # BitnotI
|
"($3)((NU$2) ~($1))", # BitnotI
|
||||||
"$1", # UnaryPlusF64
|
"$1", # UnaryPlusF64
|
||||||
|
|
@ -654,10 +660,58 @@ proc unaryArith(p: BProc, e: PNode, d: var TLoc, op: TMagic) =
|
||||||
assert(e.sons[1].typ != nil)
|
assert(e.sons[1].typ != nil)
|
||||||
initLocExpr(p, e.sons[1], a)
|
initLocExpr(p, e.sons[1], a)
|
||||||
t = skipTypes(e.typ, abstractRange)
|
t = skipTypes(e.typ, abstractRange)
|
||||||
putIntoDest(p, d, e,
|
|
||||||
unArithTab[op] % [rdLoc(a), rope(getSize(p.config, t) * 8),
|
|
||||||
|
template applyFormat(frmt: untyped) =
|
||||||
|
putIntoDest(p, d, e, frmt % [rdLoc(a), rope(getSize(p.config, t) * 8),
|
||||||
getSimpleTypeDesc(p.module, e.typ)])
|
getSimpleTypeDesc(p.module, e.typ)])
|
||||||
|
|
||||||
|
|
||||||
|
case op
|
||||||
|
of mNot:
|
||||||
|
applyFormat("!($1)")
|
||||||
|
of mUnaryPlusI:
|
||||||
|
applyFormat("$1")
|
||||||
|
of mBitnotI:
|
||||||
|
applyFormat("($3)((NU$2) ~($1))")
|
||||||
|
of mUnaryPlusF64:
|
||||||
|
applyFormat("$1")
|
||||||
|
of mUnaryMinusF64:
|
||||||
|
applyFormat("-($1)")
|
||||||
|
of mAbsF64:
|
||||||
|
applyFormat("($1 < 0? -($1) : ($1))")
|
||||||
|
# BUGFIX: fabs() makes problems for Tiny C
|
||||||
|
of mZe8ToI:
|
||||||
|
applyFormat("(($3)(NU)(NU8)($1))")
|
||||||
|
of mZe8ToI64:
|
||||||
|
applyFormat("(($3)(NU64)(NU8)($1))")
|
||||||
|
of mZe16ToI:
|
||||||
|
applyFormat("(($3)(NU)(NU16)($1))")
|
||||||
|
of mZe16ToI64:
|
||||||
|
applyFormat("(($3)(NU64)(NU16)($1))")
|
||||||
|
of mZe32ToI64:
|
||||||
|
applyFormat("(($3)(NU64)(NU32)($1))")
|
||||||
|
of mZeIToI64:
|
||||||
|
applyFormat("(($3)(NU64)(NU)($1))")
|
||||||
|
of mToU8:
|
||||||
|
applyFormat("(($3)(NU8)(NU)($1))")
|
||||||
|
of mToU16:
|
||||||
|
applyFormat("(($3)(NU16)(NU)($1))")
|
||||||
|
of mToU32:
|
||||||
|
applyFormat("(($3)(NU32)(NU64)($1))")
|
||||||
|
of mToFloat:
|
||||||
|
applyFormat("((double) ($1))")
|
||||||
|
of mToBiggestFloat:
|
||||||
|
applyFormat("((double) ($1))")
|
||||||
|
of mToInt:
|
||||||
|
applyFormat("float64ToInt32($1)")
|
||||||
|
of mToBiggestInt:
|
||||||
|
applyFormat("float64ToInt64($1)")
|
||||||
|
else:
|
||||||
|
assert false, $op
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
proc isCppRef(p: BProc; typ: PType): bool {.inline.} =
|
proc isCppRef(p: BProc; typ: PType): bool {.inline.} =
|
||||||
result = p.module.compileToCpp and
|
result = p.module.compileToCpp and
|
||||||
skipTypes(typ, abstractInstOwned).kind == tyVar and
|
skipTypes(typ, abstractInstOwned).kind == tyVar and
|
||||||
|
|
@ -942,6 +996,23 @@ proc genBracketExpr(p: BProc; n: PNode; d: var TLoc) =
|
||||||
of tyTuple: genTupleElem(p, n, d)
|
of tyTuple: genTupleElem(p, n, d)
|
||||||
else: internalError(p.config, n.info, "expr(nkBracketExpr, " & $ty.kind & ')')
|
else: internalError(p.config, n.info, "expr(nkBracketExpr, " & $ty.kind & ')')
|
||||||
|
|
||||||
|
proc isSimpleExpr(n: PNode): bool =
|
||||||
|
# calls all the way down --> can stay expression based
|
||||||
|
case n.kind
|
||||||
|
of nkCallKinds, nkDotExpr, nkPar, nkTupleConstr,
|
||||||
|
nkObjConstr, nkBracket, nkCurly, nkHiddenDeref, nkDerefExpr, nkHiddenAddr,
|
||||||
|
nkHiddenStdConv, nkHiddenSubConv, nkConv, nkAddr:
|
||||||
|
for c in n:
|
||||||
|
if not isSimpleExpr(c): return false
|
||||||
|
result = true
|
||||||
|
of nkStmtListExpr:
|
||||||
|
for i in 0..n.len-2:
|
||||||
|
if n[i].kind notin {nkCommentStmt, nkEmpty}: return false
|
||||||
|
result = isSimpleExpr(n.lastSon)
|
||||||
|
else:
|
||||||
|
if n.isAtom:
|
||||||
|
result = true
|
||||||
|
|
||||||
proc genAndOr(p: BProc, e: PNode, d: var TLoc, m: TMagic) =
|
proc genAndOr(p: BProc, e: PNode, d: var TLoc, m: TMagic) =
|
||||||
# how to generate code?
|
# how to generate code?
|
||||||
# 'expr1 and expr2' becomes:
|
# 'expr1 and expr2' becomes:
|
||||||
|
|
@ -963,24 +1034,41 @@ proc genAndOr(p: BProc, e: PNode, d: var TLoc, m: TMagic) =
|
||||||
# tmp = a
|
# tmp = a
|
||||||
# end:
|
# end:
|
||||||
# a = tmp
|
# a = tmp
|
||||||
var
|
when false:
|
||||||
L: TLabel
|
#if isSimpleExpr(e) and p.module.compileToCpp:
|
||||||
tmp: TLoc
|
var tmpA, tmpB: TLoc
|
||||||
getTemp(p, e.typ, tmp) # force it into a temp!
|
#getTemp(p, e.typ, tmpA)
|
||||||
inc p.splitDecls
|
#getTemp(p, e.typ, tmpB)
|
||||||
expr(p, e.sons[1], tmp)
|
initLocExprSingleUse(p, e.sons[1], tmpA)
|
||||||
L = getLabel(p)
|
initLocExprSingleUse(p, e.sons[2], tmpB)
|
||||||
if m == mOr:
|
tmpB.k = locExpr
|
||||||
lineF(p, cpsStmts, "if ($1) goto $2;$n", [rdLoc(tmp), L])
|
if m == mOr:
|
||||||
|
tmpB.r = "((" & rdLoc(tmpA) & ")||(" & rdLoc(tmpB) & "))"
|
||||||
|
else:
|
||||||
|
tmpB.r = "((" & rdLoc(tmpA) & ")&&(" & rdLoc(tmpB) & "))"
|
||||||
|
if d.k == locNone:
|
||||||
|
d = tmpB
|
||||||
|
else:
|
||||||
|
genAssignment(p, d, tmpB, {})
|
||||||
else:
|
else:
|
||||||
lineF(p, cpsStmts, "if (!($1)) goto $2;$n", [rdLoc(tmp), L])
|
var
|
||||||
expr(p, e.sons[2], tmp)
|
L: TLabel
|
||||||
fixLabel(p, L)
|
tmp: TLoc
|
||||||
if d.k == locNone:
|
getTemp(p, e.typ, tmp) # force it into a temp!
|
||||||
d = tmp
|
inc p.splitDecls
|
||||||
else:
|
expr(p, e.sons[1], tmp)
|
||||||
genAssignment(p, d, tmp, {}) # no need for deep copying
|
L = getLabel(p)
|
||||||
dec p.splitDecls
|
if m == mOr:
|
||||||
|
lineF(p, cpsStmts, "if ($1) goto $2;$n", [rdLoc(tmp), L])
|
||||||
|
else:
|
||||||
|
lineF(p, cpsStmts, "if (!($1)) goto $2;$n", [rdLoc(tmp), L])
|
||||||
|
expr(p, e.sons[2], tmp)
|
||||||
|
fixLabel(p, L)
|
||||||
|
if d.k == locNone:
|
||||||
|
d = tmp
|
||||||
|
else:
|
||||||
|
genAssignment(p, d, tmp, {}) # no need for deep copying
|
||||||
|
dec p.splitDecls
|
||||||
|
|
||||||
proc genEcho(p: BProc, n: PNode) =
|
proc genEcho(p: BProc, n: PNode) =
|
||||||
# this unusal way of implementing it ensures that e.g. ``echo("hallo", 45)``
|
# this unusal way of implementing it ensures that e.g. ``echo("hallo", 45)``
|
||||||
|
|
@ -1675,7 +1763,7 @@ proc fewCmps(conf: ConfigRef; s: PNode): bool =
|
||||||
else:
|
else:
|
||||||
result = sonsLen(s) <= 8 # 8 seems to be a good value
|
result = sonsLen(s) <= 8 # 8 seems to be a good value
|
||||||
|
|
||||||
proc binaryExprIn(p: BProc, e: PNode, a, b, d: var TLoc, frmt: string) =
|
template binaryExprIn(p: BProc, e: PNode, a, b, d: var TLoc, frmt: string) =
|
||||||
putIntoDest(p, d, e, frmt % [rdLoc(a), rdSetElemLoc(p.config, b, a.t)])
|
putIntoDest(p, d, e, frmt % [rdLoc(a), rdSetElemLoc(p.config, b, a.t)])
|
||||||
|
|
||||||
proc genInExprAux(p: BProc, e: PNode, a, b, d: var TLoc) =
|
proc genInExprAux(p: BProc, e: PNode, a, b, d: var TLoc) =
|
||||||
|
|
@ -1686,7 +1774,7 @@ proc genInExprAux(p: BProc, e: PNode, a, b, d: var TLoc) =
|
||||||
of 8: binaryExprIn(p, e, a, b, d, "(($1 &((NU64)1<<((NU)($2)&63U)))!=0)")
|
of 8: binaryExprIn(p, e, a, b, d, "(($1 &((NU64)1<<((NU)($2)&63U)))!=0)")
|
||||||
else: binaryExprIn(p, e, a, b, d, "(($1[(NU)($2)>>3] &(1U<<((NU)($2)&7U)))!=0)")
|
else: binaryExprIn(p, e, a, b, d, "(($1[(NU)($2)>>3] &(1U<<((NU)($2)&7U)))!=0)")
|
||||||
|
|
||||||
proc binaryStmtInExcl(p: BProc, e: PNode, d: var TLoc, frmt: string) =
|
template binaryStmtInExcl(p: BProc, e: PNode, d: var TLoc, frmt: string) =
|
||||||
var a, b: TLoc
|
var a, b: TLoc
|
||||||
assert(d.k == locNone)
|
assert(d.k == locNone)
|
||||||
initLocExpr(p, e.sons[1], a)
|
initLocExpr(p, e.sons[1], a)
|
||||||
|
|
@ -1753,13 +1841,19 @@ proc genSetOp(p: BProc, e: PNode, d: var TLoc, op: TMagic) =
|
||||||
of 1, 2, 4, 8:
|
of 1, 2, 4, 8:
|
||||||
case op
|
case op
|
||||||
of mIncl:
|
of mIncl:
|
||||||
var ts = "NU" & $(size * 8)
|
case size
|
||||||
binaryStmtInExcl(p, e, d,
|
of 1: binaryStmtInExcl(p, e, d, "$1 |= ((NU8)1)<<(($2) & 7);$n")
|
||||||
"$1 |= ((" & ts & ")1)<<(($2)%(sizeof(" & ts & ")*8));$n")
|
of 2: binaryStmtInExcl(p, e, d, "$1 |= ((NU16)1)<<(($2) & 15);$n")
|
||||||
|
of 4: binaryStmtInExcl(p, e, d, "$1 |= ((NU32)1)<<(($2) & 31);$n")
|
||||||
|
of 8: binaryStmtInExcl(p, e, d, "$1 |= ((NU64)1)<<(($2) & 63);$n")
|
||||||
|
else: assert(false, $size)
|
||||||
of mExcl:
|
of mExcl:
|
||||||
var ts = "NU" & $(size * 8)
|
case size
|
||||||
binaryStmtInExcl(p, e, d, "$1 &= ~(((" & ts & ")1) << (($2) % (sizeof(" &
|
of 1: binaryStmtInExcl(p, e, d, "$1 &= ~(((NU8)1) << (($2) & 7));$n")
|
||||||
ts & ")*8)));$n")
|
of 2: binaryStmtInExcl(p, e, d, "$1 &= ~(((NU16)1) << (($2) & 15));$n")
|
||||||
|
of 4: binaryStmtInExcl(p, e, d, "$1 &= ~(((NU32)1) << (($2) & 31));$n")
|
||||||
|
of 8: binaryStmtInExcl(p, e, d, "$1 &= ~(((NU64)1) << (($2) & 63));$n")
|
||||||
|
else: assert(false, $size)
|
||||||
of mCard:
|
of mCard:
|
||||||
if size <= 4: unaryExprChar(p, e, d, "#countBits32($1)")
|
if size <= 4: unaryExprChar(p, e, d, "#countBits32($1)")
|
||||||
else: unaryExprChar(p, e, d, "#countBits64($1)")
|
else: unaryExprChar(p, e, d, "#countBits64($1)")
|
||||||
|
|
@ -1824,14 +1918,14 @@ proc genSomeCast(p: BProc, e: PNode, d: var TLoc) =
|
||||||
var a: TLoc
|
var a: TLoc
|
||||||
initLocExpr(p, e.sons[1], a)
|
initLocExpr(p, e.sons[1], a)
|
||||||
let etyp = skipTypes(e.typ, abstractRange+{tyOwned})
|
let etyp = skipTypes(e.typ, abstractRange+{tyOwned})
|
||||||
|
let srcTyp = skipTypes(e.sons[1].typ, abstractRange)
|
||||||
if etyp.kind in ValueTypes and lfIndirect notin a.flags:
|
if etyp.kind in ValueTypes and lfIndirect notin a.flags:
|
||||||
putIntoDest(p, d, e, "(*($1*) ($2))" %
|
putIntoDest(p, d, e, "(*($1*) ($2))" %
|
||||||
[getTypeDesc(p.module, e.typ), addrLoc(p.config, a)], a.storage)
|
[getTypeDesc(p.module, e.typ), addrLoc(p.config, a)], a.storage)
|
||||||
elif etyp.kind == tyProc and etyp.callConv == ccClosure:
|
elif etyp.kind == tyProc and etyp.callConv == ccClosure and srcTyp.callConv != ccClosure:
|
||||||
putIntoDest(p, d, e, "(($1) ($2))" %
|
putIntoDest(p, d, e, "(($1) ($2))" %
|
||||||
[getClosureType(p.module, etyp, clHalfWithEnv), rdCharLoc(a)], a.storage)
|
[getClosureType(p.module, etyp, clHalfWithEnv), rdCharLoc(a)], a.storage)
|
||||||
else:
|
else:
|
||||||
let srcTyp = skipTypes(e.sons[1].typ, abstractRange)
|
|
||||||
# C++ does not like direct casts from pointer to shorter integral types
|
# C++ does not like direct casts from pointer to shorter integral types
|
||||||
if srcTyp.kind in {tyPtr, tyPointer} and etyp.kind in IntegralTypes:
|
if srcTyp.kind in {tyPtr, tyPointer} and etyp.kind in IntegralTypes:
|
||||||
putIntoDest(p, d, e, "(($1) (ptrdiff_t) ($2))" %
|
putIntoDest(p, d, e, "(($1) (ptrdiff_t) ($2))" %
|
||||||
|
|
@ -2085,7 +2179,7 @@ proc genMagicExpr(p: BProc, e: PNode, d: var TLoc, op: TMagic) =
|
||||||
of mCharToStr: genDollar(p, e, d, "#nimCharToStr($1)")
|
of mCharToStr: genDollar(p, e, d, "#nimCharToStr($1)")
|
||||||
of mFloatToStr: genDollar(p, e, d, "#nimFloatToStr($1)")
|
of mFloatToStr: genDollar(p, e, d, "#nimFloatToStr($1)")
|
||||||
of mCStrToStr: genDollar(p, e, d, "#cstrToNimstr($1)")
|
of mCStrToStr: genDollar(p, e, d, "#cstrToNimstr($1)")
|
||||||
of mStrToStr: expr(p, e.sons[1], d)
|
of mStrToStr, mUnown: expr(p, e.sons[1], d)
|
||||||
of mEnumToStr:
|
of mEnumToStr:
|
||||||
if optNimV2 in p.config.globalOptions:
|
if optNimV2 in p.config.globalOptions:
|
||||||
genEnumToStr(p, e, d)
|
genEnumToStr(p, e, d)
|
||||||
|
|
@ -2228,14 +2322,14 @@ proc genSetConstr(p: BProc, e: PNode, d: var TLoc) =
|
||||||
initLocExpr(p, it.sons[0], a)
|
initLocExpr(p, it.sons[0], a)
|
||||||
initLocExpr(p, it.sons[1], b)
|
initLocExpr(p, it.sons[1], b)
|
||||||
lineF(p, cpsStmts, "for ($1 = $3; $1 <= $4; $1++) $n" &
|
lineF(p, cpsStmts, "for ($1 = $3; $1 <= $4; $1++) $n" &
|
||||||
"$2 |=((" & ts & ")(1)<<(($1)%(sizeof(" & ts & ")*8)));$n", [
|
"$2 |=(($5)(1)<<(($1)%(sizeof($5)*8)));$n", [
|
||||||
rdLoc(idx), rdLoc(d), rdSetElemLoc(p.config, a, e.typ),
|
rdLoc(idx), rdLoc(d), rdSetElemLoc(p.config, a, e.typ),
|
||||||
rdSetElemLoc(p.config, b, e.typ)])
|
rdSetElemLoc(p.config, b, e.typ), rope(ts)])
|
||||||
else:
|
else:
|
||||||
initLocExpr(p, it, a)
|
initLocExpr(p, it, a)
|
||||||
lineF(p, cpsStmts,
|
lineF(p, cpsStmts,
|
||||||
"$1 |=((" & ts & ")(1)<<(($2)%(sizeof(" & ts & ")*8)));$n",
|
"$1 |=(($3)(1)<<(($2)%(sizeof($3)*8)));$n",
|
||||||
[rdLoc(d), rdSetElemLoc(p.config, a, e.typ)])
|
[rdLoc(d), rdSetElemLoc(p.config, a, e.typ), rope(ts)])
|
||||||
|
|
||||||
proc genTupleConstr(p: BProc, n: PNode, d: var TLoc) =
|
proc genTupleConstr(p: BProc, n: PNode, d: var TLoc) =
|
||||||
var rec: TLoc
|
var rec: TLoc
|
||||||
|
|
@ -2609,7 +2703,10 @@ proc expr(p: BProc, n: PNode, d: var TLoc) =
|
||||||
genProc(p.module, prc)
|
genProc(p.module, prc)
|
||||||
of nkParForStmt: genParForStmt(p, n)
|
of nkParForStmt: genParForStmt(p, n)
|
||||||
of nkState: genState(p, n)
|
of nkState: genState(p, n)
|
||||||
of nkGotoState: genGotoState(p, n)
|
of nkGotoState:
|
||||||
|
# simply never set it back to 0 here from here on...
|
||||||
|
inc p.splitDecls
|
||||||
|
genGotoState(p, n)
|
||||||
of nkBreakState: genBreakState(p, n, d)
|
of nkBreakState: genBreakState(p, n, d)
|
||||||
else: internalError(p.config, n.info, "expr(" & $n.kind & "); unknown node kind")
|
else: internalError(p.config, n.info, "expr(" & $n.kind & "); unknown node kind")
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -207,7 +207,7 @@ proc blockLeaveActions(p: BProc, howManyTrys, howManyExcepts: int) =
|
||||||
let tryStmt = p.nestedTryStmts.pop
|
let tryStmt = p.nestedTryStmts.pop
|
||||||
if not p.module.compileToCpp or optNoCppExceptions in p.config.globalOptions:
|
if not p.module.compileToCpp or optNoCppExceptions in p.config.globalOptions:
|
||||||
# Pop safe points generated by try
|
# Pop safe points generated by try
|
||||||
if not tryStmt.inExcept and not isDefined(p.config, "nimQuirky"):
|
if not tryStmt.inExcept:
|
||||||
linefmt(p, cpsStmts, "#popSafePoint();$n", [])
|
linefmt(p, cpsStmts, "#popSafePoint();$n", [])
|
||||||
|
|
||||||
# Pop this try-stmt of the list of nested trys
|
# Pop this try-stmt of the list of nested trys
|
||||||
|
|
@ -227,8 +227,9 @@ proc blockLeaveActions(p: BProc, howManyTrys, howManyExcepts: int) =
|
||||||
if not p.module.compileToCpp or optNoCppExceptions in p.config.globalOptions:
|
if not p.module.compileToCpp or optNoCppExceptions in p.config.globalOptions:
|
||||||
# Pop exceptions that was handled by the
|
# Pop exceptions that was handled by the
|
||||||
# except-blocks we are in
|
# except-blocks we are in
|
||||||
for i in countdown(howManyExcepts-1, 0):
|
if not p.noSafePoints:
|
||||||
linefmt(p, cpsStmts, "#popCurrentException();$n", [])
|
for i in countdown(howManyExcepts-1, 0):
|
||||||
|
linefmt(p, cpsStmts, "#popCurrentException();$n", [])
|
||||||
|
|
||||||
proc genGotoState(p: BProc, n: PNode) =
|
proc genGotoState(p: BProc, n: PNode) =
|
||||||
# we resist the temptation to translate it into duff's device as it later
|
# we resist the temptation to translate it into duff's device as it later
|
||||||
|
|
@ -449,7 +450,7 @@ proc genReturnStmt(p: BProc, t: PNode) =
|
||||||
blockLeaveActions(p,
|
blockLeaveActions(p,
|
||||||
howManyTrys = p.nestedTryStmts.len,
|
howManyTrys = p.nestedTryStmts.len,
|
||||||
howManyExcepts = p.inExceptBlockLen)
|
howManyExcepts = p.inExceptBlockLen)
|
||||||
if (p.finallySafePoints.len > 0) and not isDefined(p.config, "nimQuirky"):
|
if (p.finallySafePoints.len > 0) and not p.noSafePoints:
|
||||||
# If we're in a finally block, and we came here by exception
|
# If we're in a finally block, and we came here by exception
|
||||||
# consume it before we return.
|
# consume it before we return.
|
||||||
var safePoint = p.finallySafePoints[p.finallySafePoints.len-1]
|
var safePoint = p.finallySafePoints[p.finallySafePoints.len-1]
|
||||||
|
|
@ -1004,6 +1005,8 @@ proc genTry(p: BProc, t: PNode, d: var TLoc) =
|
||||||
(t.kind == nkHiddenTryStmt and sfSystemModule in p.module.module.flags)
|
(t.kind == nkHiddenTryStmt and sfSystemModule in p.module.module.flags)
|
||||||
if not quirkyExceptions:
|
if not quirkyExceptions:
|
||||||
p.module.includeHeader("<setjmp.h>")
|
p.module.includeHeader("<setjmp.h>")
|
||||||
|
else:
|
||||||
|
p.noSafePoints = true
|
||||||
genLineDir(p, t)
|
genLineDir(p, t)
|
||||||
discard cgsym(p.module, "Exception")
|
discard cgsym(p.module, "Exception")
|
||||||
var safePoint: Rope
|
var safePoint: Rope
|
||||||
|
|
@ -1021,7 +1024,7 @@ proc genTry(p: BProc, t: PNode, d: var TLoc) =
|
||||||
linefmt(p, cpsStmts, "$1.status = setjmp($1.context);$n", [safePoint])
|
linefmt(p, cpsStmts, "$1.status = setjmp($1.context);$n", [safePoint])
|
||||||
startBlock(p, "if ($1.status == 0) {$n", [safePoint])
|
startBlock(p, "if ($1.status == 0) {$n", [safePoint])
|
||||||
var length = sonsLen(t)
|
var length = sonsLen(t)
|
||||||
add(p.nestedTryStmts, (t, false))
|
add(p.nestedTryStmts, (t, quirkyExceptions))
|
||||||
expr(p, t.sons[0], d)
|
expr(p, t.sons[0], d)
|
||||||
if not quirkyExceptions:
|
if not quirkyExceptions:
|
||||||
linefmt(p, cpsStmts, "#popSafePoint();$n", [])
|
linefmt(p, cpsStmts, "#popSafePoint();$n", [])
|
||||||
|
|
@ -1135,9 +1138,9 @@ proc genAsmStmt(p: BProc, t: PNode) =
|
||||||
# work:
|
# work:
|
||||||
if p.prc == nil:
|
if p.prc == nil:
|
||||||
# top level asm statement?
|
# top level asm statement?
|
||||||
addf(p.module.s[cfsProcHeaders], CC[p.config.cCompiler].asmStmtFrmt, [s])
|
add(p.module.s[cfsProcHeaders], runtimeFormat(CC[p.config.cCompiler].asmStmtFrmt, [s]))
|
||||||
else:
|
else:
|
||||||
lineF(p, cpsStmts, CC[p.config.cCompiler].asmStmtFrmt, [s])
|
add(p.s(cpsStmts), indentLine(p, runtimeFormat(CC[p.config.cCompiler].asmStmtFrmt, [s])))
|
||||||
|
|
||||||
proc determineSection(n: PNode): TCFileSection =
|
proc determineSection(n: PNode): TCFileSection =
|
||||||
result = cfsProcHeaders
|
result = cfsProcHeaders
|
||||||
|
|
|
||||||
|
|
@ -338,12 +338,17 @@ proc getTypePre(m: BModule, typ: PType; sig: SigHash): Rope =
|
||||||
if result == nil: result = cacheGetType(m.typeCache, sig)
|
if result == nil: result = cacheGetType(m.typeCache, sig)
|
||||||
|
|
||||||
proc structOrUnion(t: PType): Rope =
|
proc structOrUnion(t: PType): Rope =
|
||||||
|
let cachedUnion {.global.} = rope("union")
|
||||||
|
let cachedStruct {.global.} = rope("struct")
|
||||||
let t = t.skipTypes({tyAlias, tySink})
|
let t = t.skipTypes({tyAlias, tySink})
|
||||||
(if tfUnion in t.flags: rope("union") else: rope("struct"))
|
if tfUnion in t.flags: cachedUnion
|
||||||
|
else: cachedStruct
|
||||||
|
|
||||||
proc getForwardStructFormat(m: BModule): string =
|
proc addForwardStructFormat(m: BModule, structOrUnion: Rope, typename: Rope) =
|
||||||
if m.compileToCpp: result = "$1 $2;$n"
|
if m.compileToCpp:
|
||||||
else: result = "typedef $1 $2 $2;$n"
|
m.s[cfsForwardTypes].addf "$1 $2;$n", [structOrUnion, typename]
|
||||||
|
else:
|
||||||
|
m.s[cfsForwardTypes].addf "typedef $1 $2 $2;$n", [structOrUnion, typename]
|
||||||
|
|
||||||
proc seqStar(m: BModule): string =
|
proc seqStar(m: BModule): string =
|
||||||
if m.config.selectedGC == gcDestructors: result = ""
|
if m.config.selectedGC == gcDestructors: result = ""
|
||||||
|
|
@ -360,8 +365,7 @@ proc getTypeForward(m: BModule, typ: PType; sig: SigHash): Rope =
|
||||||
result = getTypeName(m, typ, sig)
|
result = getTypeName(m, typ, sig)
|
||||||
m.forwTypeCache[sig] = result
|
m.forwTypeCache[sig] = result
|
||||||
if not isImportedType(concrete):
|
if not isImportedType(concrete):
|
||||||
addf(m.s[cfsForwardTypes], getForwardStructFormat(m),
|
addForwardStructFormat(m, structOrUnion(typ), result)
|
||||||
[structOrUnion(typ), result])
|
|
||||||
else:
|
else:
|
||||||
pushType(m, concrete)
|
pushType(m, concrete)
|
||||||
doAssert m.forwTypeCache[sig] == result
|
doAssert m.forwTypeCache[sig] == result
|
||||||
|
|
@ -733,8 +737,7 @@ proc getTypeDescAux(m: BModule, origTyp: PType, check: var IntSet): Rope =
|
||||||
if result == nil:
|
if result == nil:
|
||||||
result = getTypeName(m, origTyp, sig)
|
result = getTypeName(m, origTyp, sig)
|
||||||
if not isImportedType(t):
|
if not isImportedType(t):
|
||||||
addf(m.s[cfsForwardTypes], getForwardStructFormat(m),
|
addForwardStructFormat(m, structOrUnion(t), result)
|
||||||
[structOrUnion(t), result])
|
|
||||||
m.forwTypeCache[sig] = result
|
m.forwTypeCache[sig] = result
|
||||||
assert(cacheGetType(m.typeCache, sig) == nil)
|
assert(cacheGetType(m.typeCache, sig) == nil)
|
||||||
m.typeCache[sig] = result & seqStar(m)
|
m.typeCache[sig] = result & seqStar(m)
|
||||||
|
|
@ -845,8 +848,7 @@ proc getTypeDescAux(m: BModule, origTyp: PType, check: var IntSet): Rope =
|
||||||
result = getTypeName(m, origTyp, sig)
|
result = getTypeName(m, origTyp, sig)
|
||||||
m.forwTypeCache[sig] = result
|
m.forwTypeCache[sig] = result
|
||||||
if not isImportedType(t):
|
if not isImportedType(t):
|
||||||
addf(m.s[cfsForwardTypes], getForwardStructFormat(m),
|
addForwardStructFormat(m, structOrUnion(t), result)
|
||||||
[structOrUnion(t), result])
|
|
||||||
assert m.forwTypeCache[sig] == result
|
assert m.forwTypeCache[sig] == result
|
||||||
m.typeCache[sig] = result # always call for sideeffects:
|
m.typeCache[sig] = result # always call for sideeffects:
|
||||||
if not incompleteType(t):
|
if not incompleteType(t):
|
||||||
|
|
@ -905,7 +907,8 @@ proc finishTypeDescriptions(m: BModule) =
|
||||||
discard getTypeDesc(m, m.typeStack[i])
|
discard getTypeDesc(m, m.typeStack[i])
|
||||||
inc(i)
|
inc(i)
|
||||||
|
|
||||||
template cgDeclFrmt*(s: PSym): string = s.constraint.strVal
|
template cgDeclFrmt*(s: PSym): string =
|
||||||
|
s.constraint.strVal
|
||||||
|
|
||||||
proc isReloadable(m: BModule, prc: PSym): bool =
|
proc isReloadable(m: BModule, prc: PSym): bool =
|
||||||
return m.hcrOn and sfNonReloadable notin prc.flags
|
return m.hcrOn and sfNonReloadable notin prc.flags
|
||||||
|
|
@ -943,7 +946,7 @@ proc genProcHeader(m: BModule, prc: PSym, asPtr: bool = false): Rope =
|
||||||
params])
|
params])
|
||||||
else:
|
else:
|
||||||
let asPtrStr = if asPtr: (rope("(*") & name & ")") else: name
|
let asPtrStr = if asPtr: (rope("(*") & name & ")") else: name
|
||||||
result = prc.cgDeclFrmt % [rettype, asPtrStr, params]
|
result = runtimeFormat(prc.cgDeclFrmt, [rettype, asPtrStr, params])
|
||||||
|
|
||||||
# ------------------ type info generation -------------------------------------
|
# ------------------ type info generation -------------------------------------
|
||||||
|
|
||||||
|
|
@ -1346,10 +1349,10 @@ proc genTypeInfo(m: BModule, t: PType; info: TLineInfo): Rope =
|
||||||
# results are not deterministic!
|
# results are not deterministic!
|
||||||
genTupleInfo(m, t, origType, result, info)
|
genTupleInfo(m, t, origType, result, info)
|
||||||
else: internalError(m.config, "genTypeInfo(" & $t.kind & ')')
|
else: internalError(m.config, "genTypeInfo(" & $t.kind & ')')
|
||||||
if t.deepCopy != nil:
|
if t.attachedOps[attachedDeepCopy] != nil:
|
||||||
genDeepCopyProc(m, t.deepCopy, result)
|
genDeepCopyProc(m, t.attachedOps[attachedDeepCopy], result)
|
||||||
elif origType.deepCopy != nil:
|
elif origType.attachedOps[attachedDeepCopy] != nil:
|
||||||
genDeepCopyProc(m, origType.deepCopy, result)
|
genDeepCopyProc(m, origType.attachedOps[attachedDeepCopy], result)
|
||||||
result = prefixTI.rope & result & ")".rope
|
result = prefixTI.rope & result & ")".rope
|
||||||
|
|
||||||
proc genTypeSection(m: BModule, n: PNode) =
|
proc genTypeSection(m: BModule, n: PNode) =
|
||||||
|
|
|
||||||
|
|
@ -206,15 +206,15 @@ proc indentLine(p: BProc, r: Rope): Rope =
|
||||||
prepend(result, "\t".rope)
|
prepend(result, "\t".rope)
|
||||||
|
|
||||||
template appcg(m: BModule, c: var Rope, frmt: FormatStr,
|
template appcg(m: BModule, c: var Rope, frmt: FormatStr,
|
||||||
args: varargs[untyped]) =
|
args: untyped) =
|
||||||
add(c, ropecg(m, frmt, args))
|
add(c, ropecg(m, frmt, args))
|
||||||
|
|
||||||
template appcg(m: BModule, sec: TCFileSection, frmt: FormatStr,
|
template appcg(m: BModule, sec: TCFileSection, frmt: FormatStr,
|
||||||
args: varargs[untyped]) =
|
args: untyped) =
|
||||||
add(m.s[sec], ropecg(m, frmt, args))
|
add(m.s[sec], ropecg(m, frmt, args))
|
||||||
|
|
||||||
template appcg(p: BProc, sec: TCProcSection, frmt: FormatStr,
|
template appcg(p: BProc, sec: TCProcSection, frmt: FormatStr,
|
||||||
args: varargs[untyped]) =
|
args: untyped) =
|
||||||
add(p.s(sec), ropecg(p.module, frmt, args))
|
add(p.s(sec), ropecg(p.module, frmt, args))
|
||||||
|
|
||||||
template line(p: BProc, sec: TCProcSection, r: Rope) =
|
template line(p: BProc, sec: TCProcSection, r: Rope) =
|
||||||
|
|
@ -224,7 +224,7 @@ template line(p: BProc, sec: TCProcSection, r: string) =
|
||||||
add(p.s(sec), indentLine(p, r.rope))
|
add(p.s(sec), indentLine(p, r.rope))
|
||||||
|
|
||||||
template lineF(p: BProc, sec: TCProcSection, frmt: FormatStr,
|
template lineF(p: BProc, sec: TCProcSection, frmt: FormatStr,
|
||||||
args: openarray[Rope]) =
|
args: untyped) =
|
||||||
add(p.s(sec), indentLine(p, frmt % args))
|
add(p.s(sec), indentLine(p, frmt % args))
|
||||||
|
|
||||||
template lineCg(p: BProc, sec: TCProcSection, frmt: FormatStr,
|
template lineCg(p: BProc, sec: TCProcSection, frmt: FormatStr,
|
||||||
|
|
@ -440,6 +440,15 @@ proc getTemp(p: BProc, t: PType, result: var TLoc; needsInit=false) =
|
||||||
result.flags = {}
|
result.flags = {}
|
||||||
constructLoc(p, result, not needsInit)
|
constructLoc(p, result, not needsInit)
|
||||||
|
|
||||||
|
proc getTempCpp(p: BProc, t: PType, result: var TLoc; value: Rope) =
|
||||||
|
inc(p.labels)
|
||||||
|
result.r = "T" & rope(p.labels) & "_"
|
||||||
|
linefmt(p, cpsStmts, "$1 $2 = $3;$n", [getTypeDesc(p.module, t), result.r, value])
|
||||||
|
result.k = locTemp
|
||||||
|
result.lode = lodeTyp t
|
||||||
|
result.storage = OnStack
|
||||||
|
result.flags = {}
|
||||||
|
|
||||||
proc getIntTemp(p: BProc, result: var TLoc) =
|
proc getIntTemp(p: BProc, result: var TLoc) =
|
||||||
inc(p.labels)
|
inc(p.labels)
|
||||||
result.r = "T" & rope(p.labels) & "_"
|
result.r = "T" & rope(p.labels) & "_"
|
||||||
|
|
@ -484,7 +493,7 @@ proc localVarDecl(p: BProc; n: PNode): Rope =
|
||||||
add(result, " ")
|
add(result, " ")
|
||||||
add(result, s.loc.r)
|
add(result, s.loc.r)
|
||||||
else:
|
else:
|
||||||
result = s.cgDeclFrmt % [result, s.loc.r]
|
result = runtimeFormat(s.cgDeclFrmt, [result, s.loc.r])
|
||||||
|
|
||||||
proc assignLocalVar(p: BProc, n: PNode) =
|
proc assignLocalVar(p: BProc, n: PNode) =
|
||||||
#assert(s.loc.k == locNone) # not yet assigned
|
#assert(s.loc.k == locNone) # not yet assigned
|
||||||
|
|
@ -535,7 +544,7 @@ proc assignGlobalVar(p: BProc, n: PNode) =
|
||||||
if sfVolatile in s.flags: add(decl, " volatile")
|
if sfVolatile in s.flags: add(decl, " volatile")
|
||||||
addf(decl, " $1;$n", [s.loc.r])
|
addf(decl, " $1;$n", [s.loc.r])
|
||||||
else:
|
else:
|
||||||
decl = (s.cgDeclFrmt & ";$n") % [td, s.loc.r]
|
decl = runtimeFormat(s.cgDeclFrmt & ";$n", [td, s.loc.r])
|
||||||
add(p.module.s[cfsVars], decl)
|
add(p.module.s[cfsVars], decl)
|
||||||
if p.withinLoop > 0:
|
if p.withinLoop > 0:
|
||||||
# fixes tests/run/tzeroarray:
|
# fixes tests/run/tzeroarray:
|
||||||
|
|
|
||||||
|
|
@ -70,6 +70,7 @@ type
|
||||||
threadVarAccessed*: bool # true if the proc already accessed some threadvar
|
threadVarAccessed*: bool # true if the proc already accessed some threadvar
|
||||||
hasCurFramePointer*: bool # true if _nimCurFrame var needed to recover after
|
hasCurFramePointer*: bool # true if _nimCurFrame var needed to recover after
|
||||||
# exception is generated
|
# exception is generated
|
||||||
|
noSafePoints*: bool # the proc doesn't use safe points in exception handling
|
||||||
lastLineInfo*: TLineInfo # to avoid generating excessive 'nimln' statements
|
lastLineInfo*: TLineInfo # to avoid generating excessive 'nimln' statements
|
||||||
currLineInfo*: TLineInfo # AST codegen will make this superfluous
|
currLineInfo*: TLineInfo # AST codegen will make this superfluous
|
||||||
nestedTryStmts*: seq[tuple[n: PNode, inExcept: bool]]
|
nestedTryStmts*: seq[tuple[n: PNode, inExcept: bool]]
|
||||||
|
|
|
||||||
|
|
@ -567,19 +567,36 @@ proc genReturn(c: var Con; n: PNode) =
|
||||||
|
|
||||||
const
|
const
|
||||||
InterestingSyms = {skVar, skResult, skLet, skParam, skForVar, skTemp}
|
InterestingSyms = {skVar, skResult, skLet, skParam, skForVar, skTemp}
|
||||||
PathKinds* = {nkDotExpr, nkCheckedFieldExpr,
|
PathKinds0 = {nkDotExpr, nkCheckedFieldExpr,
|
||||||
nkBracketExpr, nkDerefExpr, nkHiddenDeref,
|
nkBracketExpr, nkDerefExpr, nkHiddenDeref,
|
||||||
nkAddr, nkHiddenAddr,
|
nkAddr, nkHiddenAddr,
|
||||||
nkHiddenStdConv, nkHiddenSubConv, nkObjDownConv, nkObjUpConv}
|
nkObjDownConv, nkObjUpConv}
|
||||||
|
PathKinds1 = {nkHiddenStdConv, nkHiddenSubConv}
|
||||||
|
|
||||||
|
proc getRoot(n: PNode): PNode =
|
||||||
|
result = n
|
||||||
|
while true:
|
||||||
|
case result.kind
|
||||||
|
of PathKinds0:
|
||||||
|
result = result[0]
|
||||||
|
of PathKinds1:
|
||||||
|
result = result[1]
|
||||||
|
else: break
|
||||||
|
|
||||||
|
proc skipConvDfa*(n: PNode): PNode =
|
||||||
|
result = n
|
||||||
|
while true:
|
||||||
|
case result.kind
|
||||||
|
of nkObjDownConv, nkObjUpConv:
|
||||||
|
result = result[0]
|
||||||
|
of PathKinds1:
|
||||||
|
result = result[1]
|
||||||
|
else: break
|
||||||
|
|
||||||
proc genUse(c: var Con; orig: PNode) =
|
proc genUse(c: var Con; orig: PNode) =
|
||||||
var n = orig
|
let n = dfa.getRoot(orig)
|
||||||
var iters = 0
|
|
||||||
while n.kind in PathKinds:
|
|
||||||
n = n[0]
|
|
||||||
inc iters
|
|
||||||
if n.kind == nkSym and n.sym.kind in InterestingSyms:
|
if n.kind == nkSym and n.sym.kind in InterestingSyms:
|
||||||
c.code.add Instr(n: orig, kind: use, sym: if iters > 0: nil else: n.sym)
|
c.code.add Instr(n: orig, kind: use, sym: if orig != n: nil else: n.sym)
|
||||||
|
|
||||||
proc aliases(obj, field: PNode): bool =
|
proc aliases(obj, field: PNode): bool =
|
||||||
var n = field
|
var n = field
|
||||||
|
|
@ -590,7 +607,7 @@ proc aliases(obj, field: PNode): bool =
|
||||||
if sameTrees(obj, n): return true
|
if sameTrees(obj, n): return true
|
||||||
case n.kind
|
case n.kind
|
||||||
of nkDotExpr, nkCheckedFieldExpr, nkHiddenSubConv, nkHiddenStdConv,
|
of nkDotExpr, nkCheckedFieldExpr, nkHiddenSubConv, nkHiddenStdConv,
|
||||||
nkObjDownConv, nkObjUpConv, nkHiddenDeref:
|
nkObjDownConv, nkObjUpConv, nkHiddenDeref, nkDerefExpr:
|
||||||
n = n[0]
|
n = n[0]
|
||||||
of nkBracketExpr:
|
of nkBracketExpr:
|
||||||
let x = n[0]
|
let x = n[0]
|
||||||
|
|
@ -616,13 +633,19 @@ proc instrTargets*(ins: Instr; loc: PNode): bool =
|
||||||
# use x; question does it affect 'x.f'? Yes.
|
# use x; question does it affect 'x.f'? Yes.
|
||||||
result = aliases(ins.n, loc) or aliases(loc, ins.n)
|
result = aliases(ins.n, loc) or aliases(loc, ins.n)
|
||||||
|
|
||||||
proc isAnalysableFieldAccess*(n: PNode; owner: PSym): bool =
|
proc isAnalysableFieldAccess*(orig: PNode; owner: PSym): bool =
|
||||||
var n = n
|
var n = orig
|
||||||
while true:
|
while true:
|
||||||
case n.kind
|
case n.kind
|
||||||
of nkDotExpr, nkCheckedFieldExpr, nkHiddenSubConv, nkHiddenStdConv,
|
of nkDotExpr, nkCheckedFieldExpr, nkHiddenSubConv, nkHiddenStdConv,
|
||||||
nkObjDownConv, nkObjUpConv, nkHiddenDeref:
|
nkObjDownConv, nkObjUpConv:
|
||||||
n = n[0]
|
n = n[0]
|
||||||
|
of nkHiddenDeref, nkDerefExpr:
|
||||||
|
# We "own" sinkparam[].loc but not ourVar[].location as it is a nasty
|
||||||
|
# pointer indirection.
|
||||||
|
n = n[0]
|
||||||
|
return n.kind == nkSym and n.sym.owner == owner and (isSinkParam(n.sym) or
|
||||||
|
n.sym.typ.skipTypes(abstractInst-{tyOwned}).kind in {tyOwned, tyVar})
|
||||||
of nkBracketExpr:
|
of nkBracketExpr:
|
||||||
let x = n[0]
|
let x = n[0]
|
||||||
if x.typ != nil and x.typ.skipTypes(abstractInst).kind == tyTuple:
|
if x.typ != nil and x.typ.skipTypes(abstractInst).kind == tyTuple:
|
||||||
|
|
@ -633,11 +656,25 @@ proc isAnalysableFieldAccess*(n: PNode; owner: PSym): bool =
|
||||||
break
|
break
|
||||||
# XXX Allow closure deref operations here if we know
|
# XXX Allow closure deref operations here if we know
|
||||||
# the owner controlled the closure allocation?
|
# the owner controlled the closure allocation?
|
||||||
result = n.kind == nkSym and n.sym.owner == owner and owner.kind != skModule
|
result = n.kind == nkSym and n.sym.owner == owner and
|
||||||
|
owner.kind != skModule and
|
||||||
|
(n.sym.kind != skParam or isSinkParam(n.sym)) # or n.sym.typ.kind == tyVar)
|
||||||
|
# Note: There is a different move analyzer possible that checks for
|
||||||
|
# consume(param.key); param.key = newValue for all paths. Then code like
|
||||||
|
#
|
||||||
|
# let splited = split(move self.root, x)
|
||||||
|
# self.root = merge(splited.lower, splited.greater)
|
||||||
|
#
|
||||||
|
# could be written without the ``move self.root``. However, this would be
|
||||||
|
# wrong! Then the write barrier for the ``self.root`` assignment would
|
||||||
|
# free the old data and all is lost! Lesson: Don't be too smart, trust the
|
||||||
|
# lower level C++ optimizer to specialize this code.
|
||||||
|
|
||||||
proc genDef(c: var Con; n: PNode) =
|
proc genDef(c: var Con; n: PNode) =
|
||||||
if n.kind == nkSym and n.sym.kind in InterestingSyms:
|
if n.kind == nkSym and n.sym.kind in InterestingSyms:
|
||||||
c.code.add Instr(n: n, kind: def, sym: n.sym)
|
c.code.add Instr(n: n, kind: def, sym: n.sym)
|
||||||
|
elif isAnalysableFieldAccess(n, c.owner):
|
||||||
|
c.code.add Instr(n: n, kind: def, sym: nil)
|
||||||
|
|
||||||
proc canRaise(fn: PNode): bool =
|
proc canRaise(fn: PNode): bool =
|
||||||
const magicsThatCanRaise = {
|
const magicsThatCanRaise = {
|
||||||
|
|
@ -715,7 +752,7 @@ proc gen(c: var Con; n: PNode) =
|
||||||
# "uses" 'i'. But we are only talking about builtin array indexing so
|
# "uses" 'i'. But we are only talking about builtin array indexing so
|
||||||
# it doesn't matter and 'x = 34' is NOT a usage of 'x'.
|
# it doesn't matter and 'x = 34' is NOT a usage of 'x'.
|
||||||
genDef(c, n[0])
|
genDef(c, n[0])
|
||||||
of PathKinds:
|
of PathKinds0 - {nkHiddenStdConv, nkHiddenSubConv, nkObjDownConv, nkObjUpConv}:
|
||||||
genUse(c, n)
|
genUse(c, n)
|
||||||
of nkIfStmt, nkIfExpr: genIf(c, n)
|
of nkIfStmt, nkIfExpr: genIf(c, n)
|
||||||
of nkWhenStmt:
|
of nkWhenStmt:
|
||||||
|
|
@ -732,8 +769,8 @@ proc gen(c: var Con; n: PNode) =
|
||||||
nkBracket, nkCurly, nkPar, nkTupleConstr, nkClosure, nkObjConstr:
|
nkBracket, nkCurly, nkPar, nkTupleConstr, nkClosure, nkObjConstr:
|
||||||
for x in n: gen(c, x)
|
for x in n: gen(c, x)
|
||||||
of nkPragmaBlock: gen(c, n.lastSon)
|
of nkPragmaBlock: gen(c, n.lastSon)
|
||||||
of nkDiscardStmt: gen(c, n.sons[0])
|
of nkDiscardStmt, nkObjDownConv, nkObjUpConv: gen(c, n.sons[0])
|
||||||
of nkConv, nkExprColonExpr, nkExprEqExpr, nkCast:
|
of nkConv, nkExprColonExpr, nkExprEqExpr, nkCast, nkHiddenSubConv, nkHiddenStdConv:
|
||||||
gen(c, n.sons[1])
|
gen(c, n.sons[1])
|
||||||
of nkStringToCString, nkCStringToString: gen(c, n.sons[0])
|
of nkStringToCString, nkCStringToString: gen(c, n.sons[0])
|
||||||
of nkVarSection, nkLetSection: genVarSection(c, n)
|
of nkVarSection, nkLetSection: genVarSection(c, n)
|
||||||
|
|
|
||||||
|
|
@ -172,7 +172,7 @@ proc newDocumentor*(filename: AbsoluteFile; cache: IdentCache; conf: ConfigRef,
|
||||||
outExt, RelativeDir"htmldocs", false)
|
outExt, RelativeDir"htmldocs", false)
|
||||||
result.thisDir = result.destFile.splitFile.dir
|
result.thisDir = result.destFile.splitFile.dir
|
||||||
|
|
||||||
proc dispA(conf: ConfigRef; dest: var Rope, xml, tex: string, args: openArray[Rope]) =
|
template dispA(conf: ConfigRef; dest: var Rope, xml, tex: string, args: openArray[Rope]) =
|
||||||
if conf.cmd != cmdRst2tex: addf(dest, xml, args)
|
if conf.cmd != cmdRst2tex: addf(dest, xml, args)
|
||||||
else: addf(dest, tex, args)
|
else: addf(dest, tex, args)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -20,11 +20,12 @@ type
|
||||||
TGen = object of PPassContext
|
TGen = object of PPassContext
|
||||||
doc: PDoc
|
doc: PDoc
|
||||||
module: PSym
|
module: PSym
|
||||||
|
config: ConfigRef
|
||||||
PGen = ref TGen
|
PGen = ref TGen
|
||||||
|
|
||||||
template shouldProcess(g): bool =
|
template shouldProcess(g): bool =
|
||||||
(g.module.owner.id == g.doc.conf.mainPackageId and optWholeProject in g.doc.conf.globalOptions) or
|
(g.module.owner.id == g.doc.conf.mainPackageId and optWholeProject in g.doc.conf.globalOptions) or
|
||||||
sfMainModule in g.module.flags
|
sfMainModule in g.module.flags or g.config.projectMainIdx == g.module.info.fileIndex
|
||||||
|
|
||||||
template closeImpl(body: untyped) {.dirty.} =
|
template closeImpl(body: untyped) {.dirty.} =
|
||||||
var g = PGen(p)
|
var g = PGen(p)
|
||||||
|
|
@ -60,6 +61,7 @@ template myOpenImpl(ext: untyped) {.dirty.} =
|
||||||
var g: PGen
|
var g: PGen
|
||||||
new(g)
|
new(g)
|
||||||
g.module = module
|
g.module = module
|
||||||
|
g.config = graph.config
|
||||||
var d = newDocumentor(AbsoluteFile toFullPath(graph.config, FileIndex module.position),
|
var d = newDocumentor(AbsoluteFile toFullPath(graph.config, FileIndex module.position),
|
||||||
graph.cache, graph.config, ext)
|
graph.cache, graph.config, ext)
|
||||||
d.hasToc = true
|
d.hasToc = true
|
||||||
|
|
|
||||||
|
|
@ -376,7 +376,7 @@ proc nameToCC*(name: string): TSystemCC =
|
||||||
result = ccNone
|
result = ccNone
|
||||||
|
|
||||||
proc isVSCompatible*(conf: ConfigRef): bool =
|
proc isVSCompatible*(conf: ConfigRef): bool =
|
||||||
return conf.cCompiler == ccVcc or
|
return conf.cCompiler == ccVcc or
|
||||||
conf.cCompiler == ccClangCl or
|
conf.cCompiler == ccClangCl or
|
||||||
(conf.cCompiler == ccIcl and conf.target.hostOS in osDos..osWindows)
|
(conf.cCompiler == ccIcl and conf.target.hostOS in osDos..osWindows)
|
||||||
|
|
||||||
|
|
@ -738,7 +738,7 @@ proc getLinkCmd(conf: ConfigRef; output: AbsoluteFile,
|
||||||
# way of being able to debug and rebuild the program at the same time. This
|
# way of being able to debug and rebuild the program at the same time. This
|
||||||
# is accomplished using the /PDB:<filename> flag (there also exists the
|
# is accomplished using the /PDB:<filename> flag (there also exists the
|
||||||
# /PDBALTPATH:<filename> flag). The only downside is that the .pdb files are
|
# /PDBALTPATH:<filename> flag). The only downside is that the .pdb files are
|
||||||
# atleast 300kb big (when linking statically to the runtime - or else 5mb+)
|
# atleast 300kb big (when linking statically to the runtime - or else 5mb+)
|
||||||
# and will quickly accumulate. There is a hacky solution: we could try to
|
# and will quickly accumulate. There is a hacky solution: we could try to
|
||||||
# delete all .pdb files with a pattern and swallow exceptions.
|
# delete all .pdb files with a pattern and swallow exceptions.
|
||||||
#
|
#
|
||||||
|
|
@ -910,7 +910,8 @@ proc callCCompiler*(conf: ConfigRef) =
|
||||||
else: AbsoluteFile(conf.projectName)
|
else: AbsoluteFile(conf.projectName)
|
||||||
linkCmd = getLinkCmd(conf, mainOutput, objfiles)
|
linkCmd = getLinkCmd(conf, mainOutput, objfiles)
|
||||||
if optCompileOnly notin conf.globalOptions:
|
if optCompileOnly notin conf.globalOptions:
|
||||||
if defined(windows) and linkCmd.len > 8_000:
|
const MaxCmdLen = when defined(windows): 8_000 else: 32_000
|
||||||
|
if linkCmd.len > MaxCmdLen:
|
||||||
# Windows's command line limit is about 8K (don't laugh...) so C compilers on
|
# Windows's command line limit is about 8K (don't laugh...) so C compilers on
|
||||||
# Windows support a feature where the command line can be passed via ``@linkcmd``
|
# Windows support a feature where the command line can be passed via ``@linkcmd``
|
||||||
# to them.
|
# to them.
|
||||||
|
|
|
||||||
|
|
@ -136,7 +136,7 @@ to do it.
|
||||||
import
|
import
|
||||||
intsets, ast, astalgo, msgs, renderer, magicsys, types, idents, trees,
|
intsets, ast, astalgo, msgs, renderer, magicsys, types, idents, trees,
|
||||||
strutils, options, dfa, lowerings, tables, modulegraphs, msgs,
|
strutils, options, dfa, lowerings, tables, modulegraphs, msgs,
|
||||||
lineinfos, parampatterns
|
lineinfos, parampatterns, sighashes
|
||||||
|
|
||||||
const
|
const
|
||||||
InterestingSyms = {skVar, skResult, skLet, skForVar, skTemp}
|
InterestingSyms = {skVar, skResult, skLet, skForVar, skTemp}
|
||||||
|
|
@ -195,15 +195,17 @@ proc isLastRead(n: PNode; c: var Con): bool =
|
||||||
# first we need to search for the instruction that belongs to 'n':
|
# first we need to search for the instruction that belongs to 'n':
|
||||||
c.otherRead = nil
|
c.otherRead = nil
|
||||||
var instr = -1
|
var instr = -1
|
||||||
|
let m = dfa.skipConvDfa(n)
|
||||||
|
|
||||||
for i in 0..<c.g.len:
|
for i in 0..<c.g.len:
|
||||||
# This comparison is correct and MUST not be ``instrTargets``:
|
# This comparison is correct and MUST not be ``instrTargets``:
|
||||||
if c.g[i].kind == use and c.g[i].n == n:
|
if c.g[i].kind == use and c.g[i].n == m:
|
||||||
if instr < 0:
|
if instr < 0:
|
||||||
instr = i
|
instr = i
|
||||||
break
|
break
|
||||||
|
|
||||||
dbg:
|
dbg:
|
||||||
echo "starting point for ", n, " is ", instr
|
echo "starting point for ", n, " is ", instr, " ", n.kind
|
||||||
|
|
||||||
if instr < 0: return false
|
if instr < 0: return false
|
||||||
# we go through all paths beginning from 'instr+1' and need to
|
# we go through all paths beginning from 'instr+1' and need to
|
||||||
|
|
@ -314,24 +316,37 @@ proc makePtrType(c: Con, baseType: PType): PType =
|
||||||
result = newType(tyPtr, c.owner)
|
result = newType(tyPtr, c.owner)
|
||||||
addSonSkipIntLit(result, baseType)
|
addSonSkipIntLit(result, baseType)
|
||||||
|
|
||||||
template genOp(opr, opname, ri) =
|
proc addDestroy(c: var Con; n: PNode) =
|
||||||
let op = opr
|
# append to front:
|
||||||
|
c.destroys = newTree(nkStmtList, n, c.destroys)
|
||||||
|
|
||||||
|
proc genOp(c: Con; t: PType; kind: TTypeAttachedOp; dest, ri: PNode): PNode =
|
||||||
|
var op = t.attachedOps[kind]
|
||||||
|
|
||||||
if op == nil:
|
if op == nil:
|
||||||
globalError(c.graph.config, dest.info, "internal error: '" & opname &
|
# give up and find the canonical type instead:
|
||||||
|
let h = sighashes.hashType(t, {CoType, CoConsiderOwned})
|
||||||
|
let canon = c.graph.canonTypes.getOrDefault(h)
|
||||||
|
if canon != nil:
|
||||||
|
op = canon.attachedOps[kind]
|
||||||
|
|
||||||
|
if op == nil:
|
||||||
|
globalError(c.graph.config, dest.info, "internal error: '" & AttachedOpToStr[kind] &
|
||||||
"' operator not found for type " & typeToString(t))
|
"' operator not found for type " & typeToString(t))
|
||||||
elif op.ast[genericParamsPos].kind != nkEmpty:
|
elif op.ast[genericParamsPos].kind != nkEmpty:
|
||||||
globalError(c.graph.config, dest.info, "internal error: '" & opname &
|
globalError(c.graph.config, dest.info, "internal error: '" & AttachedOpToStr[kind] &
|
||||||
"' operator is generic")
|
"' operator is generic")
|
||||||
if sfError in op.flags: checkForErrorPragma(c, t, ri, opname)
|
if sfError in op.flags: checkForErrorPragma(c, t, ri, AttachedOpToStr[kind])
|
||||||
let addrExp = newNodeIT(nkHiddenAddr, dest.info, makePtrType(c, dest.typ))
|
let addrExp = newNodeIT(nkHiddenAddr, dest.info, makePtrType(c, dest.typ))
|
||||||
addrExp.add(dest)
|
addrExp.add(dest)
|
||||||
result = newTree(nkCall, newSymNode(op), addrExp)
|
result = newTree(nkCall, newSymNode(op), addrExp)
|
||||||
|
|
||||||
proc genSink(c: Con; t: PType; dest, ri: PNode): PNode =
|
proc genSink(c: Con; t: PType; dest, ri: PNode): PNode =
|
||||||
let t = t.skipTypes({tyGenericInst, tyAlias, tySink})
|
let t = t.skipTypes({tyGenericInst, tyAlias, tySink})
|
||||||
let op = if t.sink != nil: t.sink else: t.assignment
|
let k = if t.attachedOps[attachedSink] != nil: attachedSink
|
||||||
if op != nil:
|
else: attachedAsgn
|
||||||
genOp(op, "=sink", ri)
|
if t.attachedOps[k] != nil:
|
||||||
|
result = genOp(c, t, k, dest, ri)
|
||||||
else:
|
else:
|
||||||
# in rare cases only =destroy exists but no sink or assignment
|
# in rare cases only =destroy exists but no sink or assignment
|
||||||
# (see Pony object in tmove_objconstr.nim)
|
# (see Pony object in tmove_objconstr.nim)
|
||||||
|
|
@ -342,15 +357,15 @@ proc genCopy(c: Con; t: PType; dest, ri: PNode): PNode =
|
||||||
if tfHasOwned in t.flags:
|
if tfHasOwned in t.flags:
|
||||||
checkForErrorPragma(c, t, ri, "=")
|
checkForErrorPragma(c, t, ri, "=")
|
||||||
let t = t.skipTypes({tyGenericInst, tyAlias, tySink})
|
let t = t.skipTypes({tyGenericInst, tyAlias, tySink})
|
||||||
genOp(t.assignment, "=", ri)
|
result = genOp(c, t, attachedAsgn, dest, ri)
|
||||||
|
|
||||||
proc genCopyNoCheck(c: Con; t: PType; dest, ri: PNode): PNode =
|
proc genCopyNoCheck(c: Con; t: PType; dest, ri: PNode): PNode =
|
||||||
let t = t.skipTypes({tyGenericInst, tyAlias, tySink})
|
let t = t.skipTypes({tyGenericInst, tyAlias, tySink})
|
||||||
genOp(t.assignment, "=", ri)
|
result = genOp(c, t, attachedAsgn, dest, ri)
|
||||||
|
|
||||||
proc genDestroy(c: Con; t: PType; dest: PNode): PNode =
|
proc genDestroy(c: Con; t: PType; dest: PNode): PNode =
|
||||||
let t = t.skipTypes({tyGenericInst, tyAlias, tySink})
|
let t = t.skipTypes({tyGenericInst, tyAlias, tySink})
|
||||||
genOp(t.destructor, "=destroy", nil)
|
result = genOp(c, t, attachedDestructor, dest, nil)
|
||||||
|
|
||||||
proc addTopVar(c: var Con; v: PNode) =
|
proc addTopVar(c: var Con; v: PNode) =
|
||||||
c.topLevelVars.add newTree(nkIdentDefs, v, c.emptyNode, c.emptyNode)
|
c.topLevelVars.add newTree(nkIdentDefs, v, c.emptyNode, c.emptyNode)
|
||||||
|
|
@ -408,6 +423,17 @@ proc sinkParamIsLastReadCheck(c: var Con, s: PNode) =
|
||||||
localError(c.graph.config, c.otherRead.info, "sink parameter `" & $s.sym.name.s &
|
localError(c.graph.config, c.otherRead.info, "sink parameter `" & $s.sym.name.s &
|
||||||
"` is already consumed at " & toFileLineCol(c. graph.config, s.info))
|
"` is already consumed at " & toFileLineCol(c. graph.config, s.info))
|
||||||
|
|
||||||
|
proc isSinkTypeForParam(t: PType): bool =
|
||||||
|
# a parameter like 'seq[owned T]' must not be used only once, but its
|
||||||
|
# elements must, so we detect this case here:
|
||||||
|
result = t.skipTypes({tyGenericInst, tyAlias}).kind in {tySink, tyOwned}
|
||||||
|
when false:
|
||||||
|
if isSinkType(t):
|
||||||
|
if t.skipTypes({tyGenericInst, tyAlias}).kind in {tyArray, tyVarargs, tyOpenArray, tySequence}:
|
||||||
|
result = false
|
||||||
|
else:
|
||||||
|
result = true
|
||||||
|
|
||||||
proc passCopyToSink(n: PNode; c: var Con): PNode =
|
proc passCopyToSink(n: PNode; c: var Con): PNode =
|
||||||
result = newNodeIT(nkStmtListExpr, n.info, n.typ)
|
result = newNodeIT(nkStmtListExpr, n.info, n.typ)
|
||||||
let tmp = getTemp(c, n.typ, n.info)
|
let tmp = getTemp(c, n.typ, n.info)
|
||||||
|
|
@ -441,7 +467,7 @@ proc pArg(arg: PNode; c: var Con; isSink: bool): PNode =
|
||||||
let L = if parameters != nil: parameters.len else: 0
|
let L = if parameters != nil: parameters.len else: 0
|
||||||
result.add arg[0]
|
result.add arg[0]
|
||||||
for i in 1..<arg.len:
|
for i in 1..<arg.len:
|
||||||
result.add pArg(arg[i], c, i < L and isSinkType(parameters[i]))
|
result.add pArg(arg[i], c, i < L and isSinkTypeForParam(parameters[i]))
|
||||||
elif arg.kind in {nkBracket, nkObjConstr, nkTupleConstr, nkBracket, nkCharLit..nkTripleStrLit}:
|
elif arg.kind in {nkBracket, nkObjConstr, nkTupleConstr, nkBracket, nkCharLit..nkTripleStrLit}:
|
||||||
discard "object construction to sink parameter: nothing to do"
|
discard "object construction to sink parameter: nothing to do"
|
||||||
result = arg
|
result = arg
|
||||||
|
|
@ -518,16 +544,22 @@ proc moveOrCopy(dest, ri: PNode; c: var Con): PNode =
|
||||||
let L = if parameters != nil: parameters.len else: 0
|
let L = if parameters != nil: parameters.len else: 0
|
||||||
ri2.add ri[0]
|
ri2.add ri[0]
|
||||||
for i in 1..<ri.len:
|
for i in 1..<ri.len:
|
||||||
ri2.add pArg(ri[i], c, i < L and isSinkType(parameters[i]))
|
ri2.add pArg(ri[i], c, i < L and isSinkTypeForParam(parameters[i]))
|
||||||
#recurse(ri, ri2)
|
#recurse(ri, ri2)
|
||||||
result.add ri2
|
result.add ri2
|
||||||
of nkBracketExpr:
|
of nkBracketExpr:
|
||||||
if ri[0].kind == nkSym and isUnpackedTuple(ri[0].sym):
|
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)
|
result = genSink(c, dest.typ, dest, ri)
|
||||||
|
result.add p(ri, c)
|
||||||
|
elif isAnalysableFieldAccess(ri, c.owner) and isLastRead(ri, c):
|
||||||
|
# Rule 3: `=sink`(x, z); wasMoved(z)
|
||||||
|
var snk = genSink(c, dest.typ, dest, ri)
|
||||||
|
snk.add ri
|
||||||
|
result = newTree(nkStmtList, snk, genWasMoved(ri, c))
|
||||||
else:
|
else:
|
||||||
result = genCopy(c, dest.typ, dest, ri)
|
result = genCopy(c, dest.typ, dest, ri)
|
||||||
result.add p(ri, c)
|
result.add p(ri, c)
|
||||||
of nkStmtListExpr:
|
of nkStmtListExpr:
|
||||||
result = newNodeI(nkStmtList, ri.info)
|
result = newNodeI(nkStmtList, ri.info)
|
||||||
for i in 0..ri.len-2:
|
for i in 0..ri.len-2:
|
||||||
|
|
@ -610,7 +642,9 @@ proc moveOrCopy(dest, ri: PNode; c: var Con): PNode =
|
||||||
result = genCopy(c, dest.typ, dest, ri)
|
result = genCopy(c, dest.typ, dest, ri)
|
||||||
result.add p(ri, c)
|
result.add p(ri, c)
|
||||||
of nkHiddenSubConv, nkHiddenStdConv:
|
of nkHiddenSubConv, nkHiddenStdConv:
|
||||||
if ri[1].kind in movableNodeKinds:
|
if sameType(ri.typ, ri[1].typ):
|
||||||
|
result = moveOrCopy(dest, ri[1], c)
|
||||||
|
elif ri[1].kind in movableNodeKinds:
|
||||||
result = moveOrCopy(dest, ri[1], c)
|
result = moveOrCopy(dest, ri[1], c)
|
||||||
var b = newNodeIT(ri.kind, ri.info, ri.typ)
|
var b = newNodeIT(ri.kind, ri.info, ri.typ)
|
||||||
b.add ri[0] # add empty node
|
b.add ri[0] # add empty node
|
||||||
|
|
@ -673,6 +707,15 @@ proc injectDefaultCalls(n: PNode, c: var Con) =
|
||||||
proc isCursor(n: PNode): bool {.inline.} =
|
proc isCursor(n: PNode): bool {.inline.} =
|
||||||
result = n.kind == nkSym and sfCursor in n.sym.flags
|
result = n.kind == nkSym and sfCursor in n.sym.flags
|
||||||
|
|
||||||
|
proc keepVar(n, it: PNode, c: var Con): PNode =
|
||||||
|
# keep the var but transform 'ri':
|
||||||
|
result = copyNode(n)
|
||||||
|
var itCopy = copyNode(it)
|
||||||
|
for j in 0..it.len-2:
|
||||||
|
itCopy.add it[j]
|
||||||
|
itCopy.add p(it[it.len-1], c)
|
||||||
|
result.add itCopy
|
||||||
|
|
||||||
proc p(n: PNode; c: var Con): PNode =
|
proc p(n: PNode; c: var Con): PNode =
|
||||||
case n.kind
|
case n.kind
|
||||||
of nkVarSection, nkLetSection:
|
of nkVarSection, nkLetSection:
|
||||||
|
|
@ -694,24 +737,17 @@ proc p(n: PNode; c: var Con): PNode =
|
||||||
c.addTopVar v
|
c.addTopVar v
|
||||||
# make sure it's destroyed at the end of the proc:
|
# make sure it's destroyed at the end of the proc:
|
||||||
if not isUnpackedTuple(it[0].sym):
|
if not isUnpackedTuple(it[0].sym):
|
||||||
c.destroys.add genDestroy(c, v.typ, v)
|
c.addDestroy genDestroy(c, v.typ, v)
|
||||||
if ri.kind != nkEmpty:
|
if ri.kind != nkEmpty:
|
||||||
let r = moveOrCopy(v, ri, c)
|
let r = moveOrCopy(v, ri, c)
|
||||||
result.add r
|
result.add r
|
||||||
else:
|
else:
|
||||||
# keep it, but transform 'ri':
|
result.add keepVar(n, it, c)
|
||||||
var varSection = copyNode(n)
|
|
||||||
var itCopy = copyNode(it)
|
|
||||||
for j in 0..L-2:
|
|
||||||
itCopy.add it[j]
|
|
||||||
itCopy.add p(ri, c)
|
|
||||||
varSection.add itCopy
|
|
||||||
result.add varSection
|
|
||||||
of nkCallKinds:
|
of nkCallKinds:
|
||||||
let parameters = n[0].typ
|
let parameters = n[0].typ
|
||||||
let L = if parameters != nil: parameters.len else: 0
|
let L = if parameters != nil: parameters.len else: 0
|
||||||
for i in 1 ..< n.len:
|
for i in 1 ..< n.len:
|
||||||
n.sons[i] = pArg(n[i], c, i < L and isSinkType(parameters[i]))
|
n.sons[i] = pArg(n[i], c, i < L and isSinkTypeForParam(parameters[i]))
|
||||||
if n.typ != nil and hasDestructor(n.typ):
|
if n.typ != nil and hasDestructor(n.typ):
|
||||||
discard "produce temp creation"
|
discard "produce temp creation"
|
||||||
result = newNodeIT(nkStmtListExpr, n.info, n.typ)
|
result = newNodeIT(nkStmtListExpr, n.info, n.typ)
|
||||||
|
|
@ -720,11 +756,11 @@ proc p(n: PNode; c: var Con): PNode =
|
||||||
sinkExpr.add n
|
sinkExpr.add n
|
||||||
result.add sinkExpr
|
result.add sinkExpr
|
||||||
result.add tmp
|
result.add tmp
|
||||||
c.destroys.add genDestroy(c, n.typ, tmp)
|
c.addDestroy genDestroy(c, n.typ, tmp)
|
||||||
else:
|
else:
|
||||||
result = n
|
result = n
|
||||||
of nkAsgn, nkFastAsgn:
|
of nkAsgn, nkFastAsgn:
|
||||||
if hasDestructor(n[0].typ):
|
if hasDestructor(n[0].typ) and n[1].kind notin {nkProcDef, nkDo, nkLambda, nkClosure}:
|
||||||
result = moveOrCopy(n[0], n[1], c)
|
result = moveOrCopy(n[0], n[1], c)
|
||||||
else:
|
else:
|
||||||
result = copyNode(n)
|
result = copyNode(n)
|
||||||
|
|
@ -783,8 +819,8 @@ proc injectDestructorCalls*(g: ModuleGraph; owner: PSym; n: PNode): PNode =
|
||||||
let params = owner.typ.n
|
let params = owner.typ.n
|
||||||
for i in 1 ..< params.len:
|
for i in 1 ..< params.len:
|
||||||
let param = params[i].sym
|
let param = params[i].sym
|
||||||
if isSinkParam(param) and hasDestructor(param.typ.skipTypes({tySink})):
|
if isSinkTypeForParam(param.typ) and hasDestructor(param.typ.skipTypes({tySink})):
|
||||||
c.destroys.add genDestroy(c, param.typ.skipTypes({tyGenericInst, tyAlias, tySink}), params[i])
|
c.addDestroy genDestroy(c, param.typ.skipTypes({tyGenericInst, tyAlias, tySink}), params[i])
|
||||||
|
|
||||||
#if optNimV2 in c.graph.config.globalOptions:
|
#if optNimV2 in c.graph.config.globalOptions:
|
||||||
# injectDefaultCalls(n, c)
|
# injectDefaultCalls(n, c)
|
||||||
|
|
|
||||||
|
|
@ -32,9 +32,9 @@ import
|
||||||
ast, astalgo, strutils, hashes, trees, platform, magicsys, extccomp, options,
|
ast, astalgo, strutils, hashes, trees, platform, magicsys, extccomp, options,
|
||||||
nversion, nimsets, msgs, std / sha1, bitsets, idents, types, os, tables,
|
nversion, nimsets, msgs, std / sha1, bitsets, idents, types, os, tables,
|
||||||
times, ropes, math, passes, ccgutils, wordrecg, renderer,
|
times, ropes, math, passes, ccgutils, wordrecg, renderer,
|
||||||
intsets, cgmeth, lowerings, sighashes, modulegraphs, lineinfos, rodutils,
|
intsets, cgmeth, lowerings, sighashes, modulegraphs, lineinfos, rodutils,
|
||||||
pathutils, transf
|
pathutils, transf
|
||||||
|
|
||||||
|
|
||||||
from modulegraphs import ModuleGraph, PPassContext
|
from modulegraphs import ModuleGraph, PPassContext
|
||||||
|
|
||||||
|
|
@ -365,92 +365,92 @@ proc genOr(p: PProc, a, b: PNode, r: var TCompRes) =
|
||||||
line(p, "}")
|
line(p, "}")
|
||||||
|
|
||||||
type
|
type
|
||||||
TMagicFrmt = array[0..3, string]
|
TMagicFrmt = array[0..1, string]
|
||||||
TMagicOps = array[mAddI..mStrToStr, TMagicFrmt]
|
TMagicOps = array[mAddI..mStrToStr, TMagicFrmt]
|
||||||
|
|
||||||
const # magic checked op; magic unchecked op; checked op; unchecked op
|
const # magic checked op; magic unchecked op;
|
||||||
jsOps: TMagicOps = [
|
jsMagics: TMagicOps = [
|
||||||
["addInt", "", "addInt($1, $2)", "($1 + $2)"], # AddI
|
["addInt", ""], # AddI
|
||||||
["subInt", "", "subInt($1, $2)", "($1 - $2)"], # SubI
|
["subInt", ""], # SubI
|
||||||
["mulInt", "", "mulInt($1, $2)", "($1 * $2)"], # MulI
|
["mulInt", ""], # MulI
|
||||||
["divInt", "", "divInt($1, $2)", "Math.trunc($1 / $2)"], # DivI
|
["divInt", ""], # DivI
|
||||||
["modInt", "", "modInt($1, $2)", "Math.trunc($1 % $2)"], # ModI
|
["modInt", ""], # ModI
|
||||||
["addInt", "", "addInt($1, $2)", "($1 + $2)"], # Succ
|
["addInt", ""], # Succ
|
||||||
["subInt", "", "subInt($1, $2)", "($1 - $2)"], # Pred
|
["subInt", ""], # Pred
|
||||||
["", "", "($1 + $2)", "($1 + $2)"], # AddF64
|
["", ""], # AddF64
|
||||||
["", "", "($1 - $2)", "($1 - $2)"], # SubF64
|
["", ""], # SubF64
|
||||||
["", "", "($1 * $2)", "($1 * $2)"], # MulF64
|
["", ""], # MulF64
|
||||||
["", "", "($1 / $2)", "($1 / $2)"], # DivF64
|
["", ""], # DivF64
|
||||||
["", "", "", ""], # ShrI
|
["", ""], # ShrI
|
||||||
["", "", "($1 << $2)", "($1 << $2)"], # ShlI
|
["", ""], # ShlI
|
||||||
["", "", "($1 >> $2)", "($1 >> $2)"], # AshrI
|
["", ""], # AshrI
|
||||||
["", "", "($1 & $2)", "($1 & $2)"], # BitandI
|
["", ""], # BitandI
|
||||||
["", "", "($1 | $2)", "($1 | $2)"], # BitorI
|
["", ""], # BitorI
|
||||||
["", "", "($1 ^ $2)", "($1 ^ $2)"], # BitxorI
|
["", ""], # BitxorI
|
||||||
["nimMin", "nimMin", "nimMin($1, $2)", "nimMin($1, $2)"], # MinI
|
["nimMin", "nimMin"], # MinI
|
||||||
["nimMax", "nimMax", "nimMax($1, $2)", "nimMax($1, $2)"], # MaxI
|
["nimMax", "nimMax"], # MaxI
|
||||||
["nimMin", "nimMin", "nimMin($1, $2)", "nimMin($1, $2)"], # MinF64
|
["nimMin", "nimMin"], # MinF64
|
||||||
["nimMax", "nimMax", "nimMax($1, $2)", "nimMax($1, $2)"], # MaxF64
|
["nimMax", "nimMax"], # MaxF64
|
||||||
["", "", "", ""], # addU
|
["", ""], # addU
|
||||||
["", "", "", ""], # subU
|
["", ""], # subU
|
||||||
["", "", "", ""], # mulU
|
["", ""], # mulU
|
||||||
["", "", "", ""], # divU
|
["", ""], # divU
|
||||||
["", "", "($1 % $2)", "($1 % $2)"], # modU
|
["", ""], # modU
|
||||||
["", "", "($1 == $2)", "($1 == $2)"], # EqI
|
["", ""], # EqI
|
||||||
["", "", "($1 <= $2)", "($1 <= $2)"], # LeI
|
["", ""], # LeI
|
||||||
["", "", "($1 < $2)", "($1 < $2)"], # LtI
|
["", ""], # LtI
|
||||||
["", "", "($1 == $2)", "($1 == $2)"], # EqF64
|
["", ""], # EqF64
|
||||||
["", "", "($1 <= $2)", "($1 <= $2)"], # LeF64
|
["", ""], # LeF64
|
||||||
["", "", "($1 < $2)", "($1 < $2)"], # LtF64
|
["", ""], # LtF64
|
||||||
["", "", "($1 <= $2)", "($1 <= $2)"], # leU
|
["", ""], # leU
|
||||||
["", "", "($1 < $2)", "($1 < $2)"], # ltU
|
["", ""], # ltU
|
||||||
["", "", "($1 <= $2)", "($1 <= $2)"], # leU64
|
["", ""], # leU64
|
||||||
["", "", "($1 < $2)", "($1 < $2)"], # ltU64
|
["", ""], # ltU64
|
||||||
["", "", "($1 == $2)", "($1 == $2)"], # EqEnum
|
["", ""], # EqEnum
|
||||||
["", "", "($1 <= $2)", "($1 <= $2)"], # LeEnum
|
["", ""], # LeEnum
|
||||||
["", "", "($1 < $2)", "($1 < $2)"], # LtEnum
|
["", ""], # LtEnum
|
||||||
["", "", "($1 == $2)", "($1 == $2)"], # EqCh
|
["", ""], # EqCh
|
||||||
["", "", "($1 <= $2)", "($1 <= $2)"], # LeCh
|
["", ""], # LeCh
|
||||||
["", "", "($1 < $2)", "($1 < $2)"], # LtCh
|
["", ""], # LtCh
|
||||||
["", "", "($1 == $2)", "($1 == $2)"], # EqB
|
["", ""], # EqB
|
||||||
["", "", "($1 <= $2)", "($1 <= $2)"], # LeB
|
["", ""], # LeB
|
||||||
["", "", "($1 < $2)", "($1 < $2)"], # LtB
|
["", ""], # LtB
|
||||||
["", "", "($1 == $2)", "($1 == $2)"], # EqRef
|
["", ""], # EqRef
|
||||||
["", "", "($1 == $2)", "($1 == $2)"], # EqUntracedRef
|
["", ""], # EqUntracedRef
|
||||||
["", "", "($1 <= $2)", "($1 <= $2)"], # LePtr
|
["", ""], # LePtr
|
||||||
["", "", "($1 < $2)", "($1 < $2)"], # LtPtr
|
["", ""], # LtPtr
|
||||||
["", "", "($1 != $2)", "($1 != $2)"], # Xor
|
["", ""], # Xor
|
||||||
["", "", "($1 == $2)", "($1 == $2)"], # EqCString
|
["", ""], # EqCString
|
||||||
["", "", "($1 == $2)", "($1 == $2)"], # EqProc
|
["", ""], # EqProc
|
||||||
["negInt", "", "negInt($1)", "-($1)"], # UnaryMinusI
|
["negInt", ""], # UnaryMinusI
|
||||||
["negInt64", "", "negInt64($1)", "-($1)"], # UnaryMinusI64
|
["negInt64", ""], # UnaryMinusI64
|
||||||
["absInt", "", "absInt($1)", "Math.abs($1)"], # AbsI
|
["absInt", ""], # AbsI
|
||||||
["", "", "!($1)", "!($1)"], # Not
|
["", ""], # Not
|
||||||
["", "", "+($1)", "+($1)"], # UnaryPlusI
|
["", ""], # UnaryPlusI
|
||||||
["", "", "~($1)", "~($1)"], # BitnotI
|
["", ""], # BitnotI
|
||||||
["", "", "+($1)", "+($1)"], # UnaryPlusF64
|
["", ""], # UnaryPlusF64
|
||||||
["", "", "-($1)", "-($1)"], # UnaryMinusF64
|
["", ""], # UnaryMinusF64
|
||||||
["", "", "Math.abs($1)", "Math.abs($1)"], # AbsF64
|
["", ""], # AbsF64
|
||||||
["Ze8ToI", "Ze8ToI", "Ze8ToI($1)", "Ze8ToI($1)"], # mZe8ToI
|
["Ze8ToI", "Ze8ToI"], # mZe8ToI
|
||||||
["Ze8ToI64", "Ze8ToI64", "Ze8ToI64($1)", "Ze8ToI64($1)"], # mZe8ToI64
|
["Ze8ToI64", "Ze8ToI64"], # mZe8ToI64
|
||||||
["Ze16ToI", "Ze16ToI", "Ze16ToI($1)", "Ze16ToI($1)"], # mZe16ToI
|
["Ze16ToI", "Ze16ToI"], # mZe16ToI
|
||||||
["Ze16ToI64", "Ze16ToI64", "Ze16ToI64($1)", "Ze16ToI64($1)"], # mZe16ToI64
|
["Ze16ToI64", "Ze16ToI64"], # mZe16ToI64
|
||||||
["Ze32ToI64", "Ze32ToI64", "Ze32ToI64($1)", "Ze32ToI64($1)"], # mZe32ToI64
|
["Ze32ToI64", "Ze32ToI64"], # mZe32ToI64
|
||||||
["ZeIToI64", "ZeIToI64", "ZeIToI64($1)", "ZeIToI64($1)"], # mZeIToI64
|
["ZeIToI64", "ZeIToI64"], # mZeIToI64
|
||||||
["toU8", "toU8", "toU8($1)", "toU8($1)"], # toU8
|
["toU8", "toU8"], # toU8
|
||||||
["toU16", "toU16", "toU16($1)", "toU16($1)"], # toU16
|
["toU16", "toU16"], # toU16
|
||||||
["toU32", "toU32", "toU32($1)", "toU32($1)"], # toU32
|
["toU32", "toU32"], # toU32
|
||||||
["", "", "$1", "$1"], # ToFloat
|
["", ""], # ToFloat
|
||||||
["", "", "$1", "$1"], # ToBiggestFloat
|
["", ""], # ToBiggestFloat
|
||||||
["", "", "Math.trunc($1)", "Math.trunc($1)"], # ToInt
|
["", ""], # ToInt
|
||||||
["", "", "Math.trunc($1)", "Math.trunc($1)"], # ToBiggestInt
|
["", ""], # ToBiggestInt
|
||||||
["nimCharToStr", "nimCharToStr", "nimCharToStr($1)", "nimCharToStr($1)"],
|
["nimCharToStr", "nimCharToStr"],
|
||||||
["nimBoolToStr", "nimBoolToStr", "nimBoolToStr($1)", "nimBoolToStr($1)"],
|
["nimBoolToStr", "nimBoolToStr"],
|
||||||
["cstrToNimstr", "cstrToNimstr", "cstrToNimstr(($1)+\"\")", "cstrToNimstr(($1)+\"\")"],
|
["cstrToNimstr", "cstrToNimstr"],
|
||||||
["cstrToNimstr", "cstrToNimstr", "cstrToNimstr(($1)+\"\")", "cstrToNimstr(($1)+\"\")"],
|
["cstrToNimstr", "cstrToNimstr"],
|
||||||
["cstrToNimstr", "cstrToNimstr", "cstrToNimstr(($1)+\"\")", "cstrToNimstr(($1)+\"\")"],
|
["cstrToNimstr", "cstrToNimstr"],
|
||||||
["cstrToNimstr", "cstrToNimstr", "cstrToNimstr($1)", "cstrToNimstr($1)"],
|
["cstrToNimstr", "cstrToNimstr"],
|
||||||
["", "", "$1", "$1"]]
|
["", ""]]
|
||||||
|
|
||||||
proc needsTemp(p: PProc; n: PNode): bool =
|
proc needsTemp(p: PProc; n: PNode): bool =
|
||||||
# check if n contains a call to determine
|
# check if n contains a call to determine
|
||||||
|
|
@ -478,7 +478,7 @@ proc maybeMakeTemp(p: PProc, n: PNode; x: TCompRes): tuple[a, tmp: Rope] =
|
||||||
else:
|
else:
|
||||||
(a: a, tmp: b)
|
(a: a, tmp: b)
|
||||||
|
|
||||||
proc binaryExpr(p: PProc, n: PNode, r: var TCompRes, magic, frmt: string) =
|
template binaryExpr(p: PProc, n: PNode, r: var TCompRes, magic, frmt: string) =
|
||||||
# $1 and $2 in the `frmt` string bind to lhs and rhs of the expr,
|
# $1 and $2 in the `frmt` string bind to lhs and rhs of the expr,
|
||||||
# if $3 or $4 are present they will be substituted with temps for
|
# if $3 or $4 are present they will be substituted with temps for
|
||||||
# lhs and rhs respectively
|
# lhs and rhs respectively
|
||||||
|
|
@ -490,8 +490,8 @@ proc binaryExpr(p: PProc, n: PNode, r: var TCompRes, magic, frmt: string) =
|
||||||
var
|
var
|
||||||
a, tmp = x.rdLoc
|
a, tmp = x.rdLoc
|
||||||
b, tmp2 = y.rdLoc
|
b, tmp2 = y.rdLoc
|
||||||
if "$3" in frmt: (a, tmp) = maybeMakeTemp(p, n[1], x)
|
when "$3" in frmt: (a, tmp) = maybeMakeTemp(p, n[1], x)
|
||||||
if "$4" in frmt: (a, tmp) = maybeMakeTemp(p, n[1], x)
|
when "$4" in frmt: (a, tmp) = maybeMakeTemp(p, n[1], x)
|
||||||
|
|
||||||
r.res = frmt % [a, b, tmp, tmp2]
|
r.res = frmt % [a, b, tmp, tmp2]
|
||||||
r.kind = resExpr
|
r.kind = resExpr
|
||||||
|
|
@ -520,7 +520,7 @@ proc binaryUintExpr(p: PProc, n: PNode, r: var TCompRes, op: string,
|
||||||
r.res = "(($1 $2 $3) $4)" % [x.rdLoc, rope op, y.rdLoc, trimmer]
|
r.res = "(($1 $2 $3) $4)" % [x.rdLoc, rope op, y.rdLoc, trimmer]
|
||||||
r.kind = resExpr
|
r.kind = resExpr
|
||||||
|
|
||||||
proc ternaryExpr(p: PProc, n: PNode, r: var TCompRes, magic, frmt: string) =
|
template ternaryExpr(p: PProc, n: PNode, r: var TCompRes, magic, frmt: string) =
|
||||||
var x, y, z: TCompRes
|
var x, y, z: TCompRes
|
||||||
useMagic(p, magic)
|
useMagic(p, magic)
|
||||||
gen(p, n.sons[1], x)
|
gen(p, n.sons[1], x)
|
||||||
|
|
@ -529,7 +529,7 @@ proc ternaryExpr(p: PProc, n: PNode, r: var TCompRes, magic, frmt: string) =
|
||||||
r.res = frmt % [x.rdLoc, y.rdLoc, z.rdLoc]
|
r.res = frmt % [x.rdLoc, y.rdLoc, z.rdLoc]
|
||||||
r.kind = resExpr
|
r.kind = resExpr
|
||||||
|
|
||||||
proc unaryExpr(p: PProc, n: PNode, r: var TCompRes, magic, frmt: string) =
|
template unaryExpr(p: PProc, n: PNode, r: var TCompRes, magic, frmt: string) =
|
||||||
# $1 binds to n[1], if $2 is present it will be substituted to a tmp of $1
|
# $1 binds to n[1], if $2 is present it will be substituted to a tmp of $1
|
||||||
useMagic(p, magic)
|
useMagic(p, magic)
|
||||||
gen(p, n.sons[1], r)
|
gen(p, n.sons[1], r)
|
||||||
|
|
@ -541,15 +541,108 @@ proc unaryExpr(p: PProc, n: PNode, r: var TCompRes, magic, frmt: string) =
|
||||||
proc arithAux(p: PProc, n: PNode, r: var TCompRes, op: TMagic) =
|
proc arithAux(p: PProc, n: PNode, r: var TCompRes, op: TMagic) =
|
||||||
var
|
var
|
||||||
x, y: TCompRes
|
x, y: TCompRes
|
||||||
|
xLoc,yLoc: Rope
|
||||||
let i = ord(optOverflowCheck notin p.options)
|
let i = ord(optOverflowCheck notin p.options)
|
||||||
useMagic(p, jsOps[op][i])
|
useMagic(p, jsMagics[op][i])
|
||||||
if sonsLen(n) > 2:
|
if sonsLen(n) > 2:
|
||||||
gen(p, n.sons[1], x)
|
gen(p, n.sons[1], x)
|
||||||
gen(p, n.sons[2], y)
|
gen(p, n.sons[2], y)
|
||||||
r.res = jsOps[op][i + 2] % [x.rdLoc, y.rdLoc]
|
xLoc = x.rdLoc
|
||||||
|
yLoc = y.rdLoc
|
||||||
else:
|
else:
|
||||||
gen(p, n.sons[1], r)
|
gen(p, n.sons[1], r)
|
||||||
r.res = jsOps[op][i + 2] % [r.rdLoc]
|
xLoc = r.rdLoc
|
||||||
|
|
||||||
|
template applyFormat(frmtA, frmtB: string) =
|
||||||
|
if i == 0:
|
||||||
|
r.res = frmtA % [xLoc, yLoc]
|
||||||
|
else:
|
||||||
|
r.res = frmtB % [xLoc, yLoc]
|
||||||
|
|
||||||
|
case op:
|
||||||
|
of mAddI: applyFormat("addInt($1, $2)", "($1 + $2)")
|
||||||
|
of mSubI: applyFormat("subInt($1, $2)", "($1 - $2)")
|
||||||
|
of mMulI: applyFormat("mulInt($1, $2)", "($1 * $2)")
|
||||||
|
of mDivI: applyFormat("divInt($1, $2)", "Math.trunc($1 / $2)")
|
||||||
|
of mModI: applyFormat("modInt($1, $2)", "Math.trunc($1 % $2)")
|
||||||
|
of mSucc: applyFormat("addInt($1, $2)", "($1 + $2)")
|
||||||
|
of mPred: applyFormat("subInt($1, $2)", "($1 - $2)")
|
||||||
|
of mAddF64: applyFormat("($1 + $2)", "($1 + $2)")
|
||||||
|
of mSubF64: applyFormat("($1 - $2)", "($1 - $2)")
|
||||||
|
of mMulF64: applyFormat("($1 * $2)", "($1 * $2)")
|
||||||
|
of mDivF64: applyFormat("($1 / $2)", "($1 / $2)")
|
||||||
|
of mShrI: applyFormat("", "")
|
||||||
|
of mShlI: applyFormat("($1 << $2)", "($1 << $2)")
|
||||||
|
of mAshrI: applyFormat("($1 >> $2)", "($1 >> $2)")
|
||||||
|
of mBitandI: applyFormat("($1 & $2)", "($1 & $2)")
|
||||||
|
of mBitorI: applyFormat("($1 | $2)", "($1 | $2)")
|
||||||
|
of mBitxorI: applyFormat("($1 ^ $2)", "($1 ^ $2)")
|
||||||
|
of mMinI: applyFormat("nimMin($1, $2)", "nimMin($1, $2)")
|
||||||
|
of mMaxI: applyFormat("nimMax($1, $2)", "nimMax($1, $2)")
|
||||||
|
of mMinF64: applyFormat("nimMin($1, $2)", "nimMin($1, $2)")
|
||||||
|
of mMaxF64: applyFormat("nimMax($1, $2)", "nimMax($1, $2)")
|
||||||
|
of mAddU: applyFormat("", "")
|
||||||
|
of msubU: applyFormat("", "")
|
||||||
|
of mmulU: applyFormat("", "")
|
||||||
|
of mdivU: applyFormat("", "")
|
||||||
|
of mmodU: applyFormat("($1 % $2)", "($1 % $2)")
|
||||||
|
of mEqI: applyFormat("($1 == $2)", "($1 == $2)")
|
||||||
|
of mLeI: applyFormat("($1 <= $2)", "($1 <= $2)")
|
||||||
|
of mLtI: applyFormat("($1 < $2)", "($1 < $2)")
|
||||||
|
of mEqF64: applyFormat("($1 == $2)", "($1 == $2)")
|
||||||
|
of mLeF64: applyFormat("($1 <= $2)", "($1 <= $2)")
|
||||||
|
of mLtF64: applyFormat("($1 < $2)", "($1 < $2)")
|
||||||
|
of mleU: applyFormat("($1 <= $2)", "($1 <= $2)")
|
||||||
|
of mltU: applyFormat("($1 < $2)", "($1 < $2)")
|
||||||
|
of mleU64: applyFormat("($1 <= $2)", "($1 <= $2)")
|
||||||
|
of mltU64: applyFormat("($1 < $2)", "($1 < $2)")
|
||||||
|
of mEqEnum: applyFormat("($1 == $2)", "($1 == $2)")
|
||||||
|
of mLeEnum: applyFormat("($1 <= $2)", "($1 <= $2)")
|
||||||
|
of mLtEnum: applyFormat("($1 < $2)", "($1 < $2)")
|
||||||
|
of mEqCh: applyFormat("($1 == $2)", "($1 == $2)")
|
||||||
|
of mLeCh: applyFormat("($1 <= $2)", "($1 <= $2)")
|
||||||
|
of mLtCh: applyFormat("($1 < $2)", "($1 < $2)")
|
||||||
|
of mEqB: applyFormat("($1 == $2)", "($1 == $2)")
|
||||||
|
of mLeB: applyFormat("($1 <= $2)", "($1 <= $2)")
|
||||||
|
of mLtB: applyFormat("($1 < $2)", "($1 < $2)")
|
||||||
|
of mEqRef: applyFormat("($1 == $2)", "($1 == $2)")
|
||||||
|
of mEqUntracedRef: applyFormat("($1 == $2)", "($1 == $2)")
|
||||||
|
of mLePtr: applyFormat("($1 <= $2)", "($1 <= $2)")
|
||||||
|
of mLtPtr: applyFormat("($1 < $2)", "($1 < $2)")
|
||||||
|
of mXor: applyFormat("($1 != $2)", "($1 != $2)")
|
||||||
|
of mEqCString: applyFormat("($1 == $2)", "($1 == $2)")
|
||||||
|
of mEqProc: applyFormat("($1 == $2)", "($1 == $2)")
|
||||||
|
of mUnaryMinusI: applyFormat("negInt($1)", "-($1)")
|
||||||
|
of mUnaryMinusI64: applyFormat("negInt64($1)", "-($1)")
|
||||||
|
of mAbsI: applyFormat("absInt($1)", "Math.abs($1)")
|
||||||
|
of mNot: applyFormat("!($1)", "!($1)")
|
||||||
|
of mUnaryPlusI: applyFormat("+($1)", "+($1)")
|
||||||
|
of mBitnotI: applyFormat("~($1)", "~($1)")
|
||||||
|
of mUnaryPlusF64: applyFormat("+($1)", "+($1)")
|
||||||
|
of mUnaryMinusF64: applyFormat("-($1)", "-($1)")
|
||||||
|
of mAbsF64: applyFormat("Math.abs($1)", "Math.abs($1)")
|
||||||
|
of mZe8ToI: applyFormat("Ze8ToI($1)", "Ze8ToI($1)")
|
||||||
|
of mZe8ToI64: applyFormat("Ze8ToI64($1)", "Ze8ToI64($1)")
|
||||||
|
of mZe16ToI: applyFormat("Ze16ToI($1)", "Ze16ToI($1)")
|
||||||
|
of mZe16ToI64: applyFormat("Ze16ToI64($1)", "Ze16ToI64($1)")
|
||||||
|
of mZe32ToI64: applyFormat("Ze32ToI64($1)", "Ze32ToI64($1)")
|
||||||
|
of mZeIToI64: applyFormat("ZeIToI64($1)", "ZeIToI64($1)")
|
||||||
|
of mtoU8: applyFormat("toU8($1)", "toU8($1)")
|
||||||
|
of mtoU16: applyFormat("toU16($1)", "toU16($1)")
|
||||||
|
of mtoU32: applyFormat("toU32($1)", "toU32($1)")
|
||||||
|
of mToFloat: applyFormat("$1", "$1")
|
||||||
|
of mToBiggestFloat: applyFormat("$1", "$1")
|
||||||
|
of mToInt: applyFormat("Math.trunc($1)", "Math.trunc($1)")
|
||||||
|
of mToBiggestInt: applyFormat("Math.trunc($1)", "Math.trunc($1)")
|
||||||
|
of mCharToStr: applyFormat("nimCharToStr($1)", "nimCharToStr($1)")
|
||||||
|
of mBoolToStr: applyFormat("nimBoolToStr($1)", "nimBoolToStr($1)")
|
||||||
|
of mIntToStr: applyFormat("cstrToNimstr(($1)+\"\")", "cstrToNimstr(($1)+\"\")")
|
||||||
|
of mInt64ToStr: applyFormat("cstrToNimstr(($1)+\"\")", "cstrToNimstr(($1)+\"\")")
|
||||||
|
of mFloatToStr: applyFormat("cstrToNimstr(($1)+\"\")", "cstrToNimstr(($1)+\"\")")
|
||||||
|
of mCStrToStr: applyFormat("cstrToNimstr($1)", "cstrToNimstr($1)")
|
||||||
|
of mStrToStr, mUnown: applyFormat("$1", "$1")
|
||||||
|
else:
|
||||||
|
assert false, $op
|
||||||
|
|
||||||
proc arith(p: PProc, n: PNode, r: var TCompRes, op: TMagic) =
|
proc arith(p: PProc, n: PNode, r: var TCompRes, op: TMagic) =
|
||||||
case op
|
case op
|
||||||
|
|
@ -1268,6 +1361,9 @@ proc genSym(p: PProc, n: PNode, r: var TCompRes) =
|
||||||
internalError(p.config, n.info, "symbol has no generated name: " & s.name.s)
|
internalError(p.config, n.info, "symbol has no generated name: " & s.name.s)
|
||||||
r.res = s.loc.r
|
r.res = s.loc.r
|
||||||
of skProc, skFunc, skConverter, skMethod:
|
of skProc, skFunc, skConverter, skMethod:
|
||||||
|
if sfCompileTime in s.flags:
|
||||||
|
localError(p.config, n.info, "request to generate code for .compileTime proc: " &
|
||||||
|
s.name.s)
|
||||||
discard mangleName(p.module, s)
|
discard mangleName(p.module, s)
|
||||||
r.res = s.loc.r
|
r.res = s.loc.r
|
||||||
if lfNoDecl in s.loc.flags or s.magic != mNone or
|
if lfNoDecl in s.loc.flags or s.magic != mNone or
|
||||||
|
|
@ -1603,6 +1699,9 @@ proc genVarInit(p: PProc, v: PSym, n: PNode) =
|
||||||
else:
|
else:
|
||||||
varCode = "var $2"
|
varCode = "var $2"
|
||||||
else:
|
else:
|
||||||
|
# Is this really a thought through feature? this basically unused
|
||||||
|
# feature makes it impossible for almost all format strings in
|
||||||
|
# this function to be checked at compile time.
|
||||||
varCode = v.constraint.strVal
|
varCode = v.constraint.strVal
|
||||||
|
|
||||||
if n.kind == nkEmpty:
|
if n.kind == nkEmpty:
|
||||||
|
|
@ -1611,8 +1710,7 @@ proc genVarInit(p: PProc, v: PSym, n: PNode) =
|
||||||
lineF(p, "var $1 = null;$n", [varName])
|
lineF(p, "var $1 = null;$n", [varName])
|
||||||
lineF(p, "var $1_Idx = 0;$n", [varName])
|
lineF(p, "var $1_Idx = 0;$n", [varName])
|
||||||
else:
|
else:
|
||||||
lineF(p, varCode & " = $3;$n",
|
line(p, runtimeFormat(varCode & " = $3;$n", [returnType, varName, createVar(p, v.typ, isIndirect(v))]))
|
||||||
[returnType, varName, createVar(p, v.typ, isIndirect(v))])
|
|
||||||
else:
|
else:
|
||||||
gen(p, n, a)
|
gen(p, n, a)
|
||||||
case mapType(p, v.typ)
|
case mapType(p, v.typ)
|
||||||
|
|
@ -1626,29 +1724,29 @@ proc genVarInit(p: PProc, v: PSym, n: PNode) =
|
||||||
let targetBaseIndex = {sfAddrTaken, sfGlobal} * v.flags == {}
|
let targetBaseIndex = {sfAddrTaken, sfGlobal} * v.flags == {}
|
||||||
if a.typ == etyBaseIndex:
|
if a.typ == etyBaseIndex:
|
||||||
if targetBaseIndex:
|
if targetBaseIndex:
|
||||||
lineF(p, varCode & " = $3, $2_Idx = $4;$n",
|
line(p, runtimeFormat(varCode & " = $3, $2_Idx = $4;$n",
|
||||||
[returnType, v.loc.r, a.address, a.res])
|
[returnType, v.loc.r, a.address, a.res]))
|
||||||
else:
|
else:
|
||||||
if isIndirect(v):
|
if isIndirect(v):
|
||||||
lineF(p, varCode & " = [[$3, $4]];$n",
|
line(p, runtimeFormat(varCode & " = [[$3, $4]];$n",
|
||||||
[returnType, v.loc.r, a.address, a.res])
|
[returnType, v.loc.r, a.address, a.res]))
|
||||||
else:
|
else:
|
||||||
lineF(p, varCode & " = [$3, $4];$n",
|
line(p, runtimeFormat(varCode & " = [$3, $4];$n",
|
||||||
[returnType, v.loc.r, a.address, a.res])
|
[returnType, v.loc.r, a.address, a.res]))
|
||||||
else:
|
else:
|
||||||
if targetBaseIndex:
|
if targetBaseIndex:
|
||||||
let tmp = p.getTemp
|
let tmp = p.getTemp
|
||||||
lineF(p, "var $1 = $2, $3 = $1[0], $3_Idx = $1[1];$n",
|
lineF(p, "var $1 = $2, $3 = $1[0], $3_Idx = $1[1];$n",
|
||||||
[tmp, a.res, v.loc.r])
|
[tmp, a.res, v.loc.r])
|
||||||
else:
|
else:
|
||||||
lineF(p, varCode & " = $3;$n", [returnType, v.loc.r, a.res])
|
line(p, runtimeFormat(varCode & " = $3;$n", [returnType, v.loc.r, a.res]))
|
||||||
return
|
return
|
||||||
else:
|
else:
|
||||||
s = a.res
|
s = a.res
|
||||||
if isIndirect(v):
|
if isIndirect(v):
|
||||||
lineF(p, varCode & " = [$3];$n", [returnType, v.loc.r, s])
|
line(p, runtimeFormat(varCode & " = [$3];$n", [returnType, v.loc.r, s]))
|
||||||
else:
|
else:
|
||||||
lineF(p, varCode & " = $3;$n", [returnType, v.loc.r, s])
|
line(p, runtimeFormat(varCode & " = $3;$n", [returnType, v.loc.r, s]))
|
||||||
|
|
||||||
if useReloadingGuard:
|
if useReloadingGuard:
|
||||||
dec p.extraIndent
|
dec p.extraIndent
|
||||||
|
|
@ -2117,7 +2215,7 @@ proc genReturnStmt(p: PProc, n: PNode) =
|
||||||
lineF(p, "break BeforeRet;$n", [])
|
lineF(p, "break BeforeRet;$n", [])
|
||||||
|
|
||||||
proc frameCreate(p: PProc; procname, filename: Rope): Rope =
|
proc frameCreate(p: PProc; procname, filename: Rope): Rope =
|
||||||
let frameFmt =
|
const frameFmt =
|
||||||
"var F={procname:$1,prev:framePtr,filename:$2,line:0};$n"
|
"var F={procname:$1,prev:framePtr,filename:$2,line:0};$n"
|
||||||
|
|
||||||
result = p.indentLine(frameFmt % [procname, filename])
|
result = p.indentLine(frameFmt % [procname, filename])
|
||||||
|
|
@ -2185,7 +2283,7 @@ proc genProc(oldProc: PProc, prc: PSym): Rope =
|
||||||
|
|
||||||
var def: Rope
|
var def: Rope
|
||||||
if not prc.constraint.isNil:
|
if not prc.constraint.isNil:
|
||||||
def = (prc.constraint.strVal & " {$n$#$#$#$#$#") %
|
def = runtimeFormat(prc.constraint.strVal & " {$n$#$#$#$#$#",
|
||||||
[ returnType,
|
[ returnType,
|
||||||
name,
|
name,
|
||||||
header,
|
header,
|
||||||
|
|
@ -2193,7 +2291,7 @@ proc genProc(oldProc: PProc, prc: PSym): Rope =
|
||||||
optionalLine(p.locals),
|
optionalLine(p.locals),
|
||||||
optionalLine(resultAsgn),
|
optionalLine(resultAsgn),
|
||||||
optionalLine(genProcBody(p, prc)),
|
optionalLine(genProcBody(p, prc)),
|
||||||
optionalLine(p.indentLine(returnStmt))]
|
optionalLine(p.indentLine(returnStmt))])
|
||||||
else:
|
else:
|
||||||
result = ~"\L"
|
result = ~"\L"
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -261,7 +261,7 @@ proc liftIterSym*(g: ModuleGraph; n: PNode; owner: PSym): PNode =
|
||||||
|
|
||||||
proc freshVarForClosureIter*(g: ModuleGraph; s, owner: PSym): PNode =
|
proc freshVarForClosureIter*(g: ModuleGraph; s, owner: PSym): PNode =
|
||||||
let envParam = getHiddenParam(g, owner)
|
let envParam = getHiddenParam(g, owner)
|
||||||
let obj = envParam.typ.lastSon
|
let obj = envParam.typ.skipTypes({tyOwned, tyRef})
|
||||||
addField(obj, s, g.cache)
|
addField(obj, s, g.cache)
|
||||||
|
|
||||||
var access = newSymNode(envParam)
|
var access = newSymNode(envParam)
|
||||||
|
|
@ -320,15 +320,23 @@ proc getEnvTypeForOwner(c: var DetectionPass; owner: PSym;
|
||||||
rawAddSon(result, obj)
|
rawAddSon(result, obj)
|
||||||
c.ownerToType[owner.id] = result
|
c.ownerToType[owner.id] = result
|
||||||
|
|
||||||
|
proc asOwnedRef(c: DetectionPass; t: PType): PType =
|
||||||
|
if optNimV2 in c.graph.config.globalOptions:
|
||||||
|
assert t.kind == tyRef
|
||||||
|
result = newType(tyOwned, t.owner)
|
||||||
|
result.rawAddSon t
|
||||||
|
else:
|
||||||
|
result = t
|
||||||
|
|
||||||
proc getEnvTypeForOwnerUp(c: var DetectionPass; owner: PSym;
|
proc getEnvTypeForOwnerUp(c: var DetectionPass; owner: PSym;
|
||||||
info: TLineInfo): PType =
|
info: TLineInfo): PType =
|
||||||
var r = c.getEnvTypeForOwner(owner, info)
|
var r = c.getEnvTypeForOwner(owner, info)
|
||||||
result = newType(tyPtr, owner)
|
result = newType(tyPtr, owner)
|
||||||
rawAddSon(result, r.base)
|
rawAddSon(result, r.skipTypes({tyOwned, tyRef}))
|
||||||
|
|
||||||
proc createUpField(c: var DetectionPass; dest, dep: PSym; info: TLineInfo) =
|
proc createUpField(c: var DetectionPass; dest, dep: PSym; info: TLineInfo) =
|
||||||
let refObj = c.getEnvTypeForOwner(dest, info) # getHiddenParam(dest).typ
|
let refObj = c.getEnvTypeForOwner(dest, info) # getHiddenParam(dest).typ
|
||||||
let obj = refObj.lastSon
|
let obj = refObj.skipTypes({tyOwned, tyRef})
|
||||||
# The assumption here is that gcDestructors means we cannot deal
|
# The assumption here is that gcDestructors means we cannot deal
|
||||||
# with cycles properly, so it's better to produce a weak ref (=ptr) here.
|
# with cycles properly, so it's better to produce a weak ref (=ptr) here.
|
||||||
# This seems to be generally correct but since it's a bit risky it's only
|
# This seems to be generally correct but since it's a bit risky it's only
|
||||||
|
|
@ -343,7 +351,7 @@ proc createUpField(c: var DetectionPass; dest, dep: PSym; info: TLineInfo) =
|
||||||
let upIdent = getIdent(c.graph.cache, upName)
|
let upIdent = getIdent(c.graph.cache, upName)
|
||||||
let upField = lookupInRecord(obj.n, upIdent)
|
let upField = lookupInRecord(obj.n, upIdent)
|
||||||
if upField != nil:
|
if upField != nil:
|
||||||
if upField.typ.base != fieldType.base:
|
if upField.typ.skipTypes({tyOwned, tyRef}) != fieldType.skipTypes({tyOwned, tyRef}):
|
||||||
localError(c.graph.config, dep.info, "internal error: up references do not agree")
|
localError(c.graph.config, dep.info, "internal error: up references do not agree")
|
||||||
else:
|
else:
|
||||||
let result = newSym(skField, upIdent, obj.owner, obj.owner.info)
|
let result = newSym(skField, upIdent, obj.owner, obj.owner.info)
|
||||||
|
|
@ -414,8 +422,8 @@ proc detectCapturedVars(n: PNode; owner: PSym; c: var DetectionPass) =
|
||||||
addClosureParam(c, owner, n.info)
|
addClosureParam(c, owner, n.info)
|
||||||
if interestingIterVar(s):
|
if interestingIterVar(s):
|
||||||
if not c.capturedVars.containsOrIncl(s.id):
|
if not c.capturedVars.containsOrIncl(s.id):
|
||||||
let obj = getHiddenParam(c.graph, owner).typ.lastSon
|
let obj = getHiddenParam(c.graph, owner).typ.skipTypes({tyOwned, tyRef})
|
||||||
#let obj = c.getEnvTypeForOwner(s.owner).lastSon
|
#let obj = c.getEnvTypeForOwner(s.owner).skipTypes({tyOwned, tyRef})
|
||||||
|
|
||||||
if s.name.id == getIdent(c.graph.cache, ":state").id:
|
if s.name.id == getIdent(c.graph.cache, ":state").id:
|
||||||
obj.n[0].sym.id = -s.id
|
obj.n[0].sym.id = -s.id
|
||||||
|
|
@ -440,8 +448,8 @@ proc detectCapturedVars(n: PNode; owner: PSym; c: var DetectionPass) =
|
||||||
#echo "capturing ", n.info
|
#echo "capturing ", n.info
|
||||||
# variable 's' is actually captured:
|
# variable 's' is actually captured:
|
||||||
if interestingVar(s) and not c.capturedVars.containsOrIncl(s.id):
|
if interestingVar(s) and not c.capturedVars.containsOrIncl(s.id):
|
||||||
let obj = c.getEnvTypeForOwner(ow, n.info).lastSon
|
let obj = c.getEnvTypeForOwner(ow, n.info).skipTypes({tyOwned, tyRef})
|
||||||
#getHiddenParam(owner).typ.lastSon
|
#getHiddenParam(owner).typ.skipTypes({tyOwned, tyRef})
|
||||||
addField(obj, s, c.graph.cache)
|
addField(obj, s, c.graph.cache)
|
||||||
# create required upFields:
|
# create required upFields:
|
||||||
var w = owner.skipGenericOwner
|
var w = owner.skipGenericOwner
|
||||||
|
|
@ -530,14 +538,14 @@ proc setupEnvVar(owner: PSym; d: DetectionPass;
|
||||||
let envVarType = d.ownerToType.getOrDefault(owner.id)
|
let envVarType = d.ownerToType.getOrDefault(owner.id)
|
||||||
if envVarType.isNil:
|
if envVarType.isNil:
|
||||||
localError d.graph.config, owner.info, "internal error: could not determine closure type"
|
localError d.graph.config, owner.info, "internal error: could not determine closure type"
|
||||||
result = newEnvVar(d.graph.cache, owner, envVarType)
|
result = newEnvVar(d.graph.cache, owner, asOwnedRef(d, envVarType))
|
||||||
c.envVars[owner.id] = result
|
c.envVars[owner.id] = result
|
||||||
|
|
||||||
proc getUpViaParam(g: ModuleGraph; owner: PSym): PNode =
|
proc getUpViaParam(g: ModuleGraph; owner: PSym): PNode =
|
||||||
let p = getHiddenParam(g, owner)
|
let p = getHiddenParam(g, owner)
|
||||||
result = p.newSymNode
|
result = p.newSymNode
|
||||||
if owner.isIterator:
|
if owner.isIterator:
|
||||||
let upField = lookupInRecord(p.typ.lastSon.n, getIdent(g.cache, upName))
|
let upField = lookupInRecord(p.typ.skipTypes({tyOwned, tyRef}).n, getIdent(g.cache, upName))
|
||||||
if upField == nil:
|
if upField == nil:
|
||||||
localError(g.config, owner.info, "could not find up reference for closure iter")
|
localError(g.config, owner.info, "could not find up reference for closure iter")
|
||||||
else:
|
else:
|
||||||
|
|
@ -566,10 +574,10 @@ proc rawClosureCreation(owner: PSym;
|
||||||
# add ``env.param = param``
|
# add ``env.param = param``
|
||||||
result.add(newAsgnStmt(fieldAccess, newSymNode(local), env.info))
|
result.add(newAsgnStmt(fieldAccess, newSymNode(local), env.info))
|
||||||
|
|
||||||
let upField = lookupInRecord(env.typ.lastSon.n, getIdent(d.graph.cache, upName))
|
let upField = lookupInRecord(env.typ.skipTypes({tyOwned, tyRef}).n, getIdent(d.graph.cache, upName))
|
||||||
if upField != nil:
|
if upField != nil:
|
||||||
let up = getUpViaParam(d.graph, owner)
|
let up = getUpViaParam(d.graph, owner)
|
||||||
if up != nil and upField.typ.base == up.typ.base:
|
if up != nil and upField.typ.skipTypes({tyOwned, tyRef}) == up.typ.skipTypes({tyOwned, tyRef}):
|
||||||
result.add(newAsgnStmt(rawIndirectAccess(env, upField, env.info),
|
result.add(newAsgnStmt(rawIndirectAccess(env, upField, env.info),
|
||||||
up, env.info))
|
up, env.info))
|
||||||
#elif oldenv != nil and oldenv.typ == upField.typ:
|
#elif oldenv != nil and oldenv.typ == upField.typ:
|
||||||
|
|
@ -584,11 +592,11 @@ proc closureCreationForIter(iter: PNode;
|
||||||
let owner = iter.sym.skipGenericOwner
|
let owner = iter.sym.skipGenericOwner
|
||||||
var v = newSym(skVar, getIdent(d.graph.cache, envName), owner, iter.info)
|
var v = newSym(skVar, getIdent(d.graph.cache, envName), owner, iter.info)
|
||||||
incl(v.flags, sfShadowed)
|
incl(v.flags, sfShadowed)
|
||||||
v.typ = getHiddenParam(d.graph, iter.sym).typ
|
v.typ = asOwnedRef(d, getHiddenParam(d.graph, iter.sym).typ)
|
||||||
var vnode: PNode
|
var vnode: PNode
|
||||||
if owner.isIterator:
|
if owner.isIterator:
|
||||||
let it = getHiddenParam(d.graph, owner)
|
let it = getHiddenParam(d.graph, owner)
|
||||||
addUniqueField(it.typ.sons[0], v, d.graph.cache)
|
addUniqueField(it.typ.skipTypes({tyOwned, tyRef}), v, d.graph.cache)
|
||||||
vnode = indirectAccess(newSymNode(it), v, v.info)
|
vnode = indirectAccess(newSymNode(it), v, v.info)
|
||||||
else:
|
else:
|
||||||
vnode = v.newSymNode
|
vnode = v.newSymNode
|
||||||
|
|
@ -597,10 +605,10 @@ proc closureCreationForIter(iter: PNode;
|
||||||
result.add(vs)
|
result.add(vs)
|
||||||
result.add(newCall(getSysSym(d.graph, iter.info, "internalNew"), vnode))
|
result.add(newCall(getSysSym(d.graph, iter.info, "internalNew"), vnode))
|
||||||
|
|
||||||
let upField = lookupInRecord(v.typ.lastSon.n, getIdent(d.graph.cache, upName))
|
let upField = lookupInRecord(v.typ.skipTypes({tyOwned, tyRef}).n, getIdent(d.graph.cache, upName))
|
||||||
if upField != nil:
|
if upField != nil:
|
||||||
let u = setupEnvVar(owner, d, c)
|
let u = setupEnvVar(owner, d, c)
|
||||||
if u.typ.base == upField.typ.base:
|
if u.typ.skipTypes({tyOwned, tyRef}) == upField.typ.skipTypes({tyOwned, tyRef}):
|
||||||
result.add(newAsgnStmt(rawIndirectAccess(vnode, upField, iter.info),
|
result.add(newAsgnStmt(rawIndirectAccess(vnode, upField, iter.info),
|
||||||
u, iter.info))
|
u, iter.info))
|
||||||
else:
|
else:
|
||||||
|
|
@ -610,7 +618,7 @@ proc closureCreationForIter(iter: PNode;
|
||||||
proc accessViaEnvVar(n: PNode; owner: PSym; d: DetectionPass;
|
proc accessViaEnvVar(n: PNode; owner: PSym; d: DetectionPass;
|
||||||
c: var LiftingPass): PNode =
|
c: var LiftingPass): PNode =
|
||||||
let access = setupEnvVar(owner, d, c)
|
let access = setupEnvVar(owner, d, c)
|
||||||
let obj = access.typ.sons[0]
|
let obj = access.typ.skipTypes({tyOwned, tyRef})
|
||||||
let field = getFieldFromObj(obj, n.sym)
|
let field = getFieldFromObj(obj, n.sym)
|
||||||
if field != nil:
|
if field != nil:
|
||||||
result = rawIndirectAccess(access, field, n.info)
|
result = rawIndirectAccess(access, field, n.info)
|
||||||
|
|
@ -619,7 +627,7 @@ proc accessViaEnvVar(n: PNode; owner: PSym; d: DetectionPass;
|
||||||
result = n
|
result = n
|
||||||
|
|
||||||
proc getStateField*(g: ModuleGraph; owner: PSym): PSym =
|
proc getStateField*(g: ModuleGraph; owner: PSym): PSym =
|
||||||
getHiddenParam(g, owner).typ.sons[0].n.sons[0].sym
|
getHiddenParam(g, owner).typ.skipTypes({tyOwned, tyRef}).n.sons[0].sym
|
||||||
|
|
||||||
proc liftCapturedVars(n: PNode; owner: PSym; d: DetectionPass;
|
proc liftCapturedVars(n: PNode; owner: PSym; d: DetectionPass;
|
||||||
c: var LiftingPass): PNode
|
c: var LiftingPass): PNode
|
||||||
|
|
@ -644,7 +652,7 @@ proc symToClosure(n: PNode; owner: PSym; d: DetectionPass;
|
||||||
while true:
|
while true:
|
||||||
if access.typ == wanted:
|
if access.typ == wanted:
|
||||||
return makeClosure(d.graph, s, access, n.info)
|
return makeClosure(d.graph, s, access, n.info)
|
||||||
let obj = access.typ.sons[0]
|
let obj = access.typ.skipTypes({tyOwned, tyRef})
|
||||||
let upField = lookupInRecord(obj.n, getIdent(d.graph.cache, upName))
|
let upField = lookupInRecord(obj.n, getIdent(d.graph.cache, upName))
|
||||||
if upField == nil:
|
if upField == nil:
|
||||||
localError(d.graph.config, n.info, "internal error: no environment found")
|
localError(d.graph.config, n.info, "internal error: no environment found")
|
||||||
|
|
|
||||||
|
|
@ -126,7 +126,7 @@ proc newDeepCopyCall(op: PSym; x, y: PNode): PNode =
|
||||||
|
|
||||||
proc useNoGc(c: TLiftCtx; t: PType): bool {.inline.} =
|
proc useNoGc(c: TLiftCtx; t: PType): bool {.inline.} =
|
||||||
result = optNimV2 in c.graph.config.globalOptions and
|
result = optNimV2 in c.graph.config.globalOptions and
|
||||||
(tfHasGCedMem in t.flags or t.isGCedMem)
|
({tfHasGCedMem, tfHasOwned} * t.flags != {} or t.isGCedMem)
|
||||||
|
|
||||||
proc considerAsgnOrSink(c: var TLiftCtx; t: PType; body, x, y: PNode;
|
proc considerAsgnOrSink(c: var TLiftCtx; t: PType; body, x, y: PNode;
|
||||||
field: var PSym): bool =
|
field: var PSym): bool =
|
||||||
|
|
@ -171,7 +171,7 @@ proc considerAsgnOrSink(c: var TLiftCtx; t: PType; body, x, y: PNode;
|
||||||
body.add newAsgnCall(c.graph, op, x, y)
|
body.add newAsgnCall(c.graph, op, x, y)
|
||||||
result = true
|
result = true
|
||||||
|
|
||||||
proc addDestructorCall(c: var TLiftCtx; t: PType; body, x: PNode): bool =
|
proc addDestructorCall(c: var TLiftCtx; t: PType; body, x: PNode) =
|
||||||
var op = t.destructor
|
var op = t.destructor
|
||||||
if op == nil and useNoGc(c, t):
|
if op == nil and useNoGc(c, t):
|
||||||
op = produceSym(c.c, t, attachedDestructor, c.info)
|
op = produceSym(c.c, t, attachedDestructor, c.info)
|
||||||
|
|
@ -182,7 +182,6 @@ proc addDestructorCall(c: var TLiftCtx; t: PType; body, x: PNode): bool =
|
||||||
markUsed(c.graph.config, c.info, op, c.graph.usageSym)
|
markUsed(c.graph.config, c.info, op, c.graph.usageSym)
|
||||||
onUse(c.info, op)
|
onUse(c.info, op)
|
||||||
body.add destructorCall(c.graph, op, x)
|
body.add destructorCall(c.graph, op, x)
|
||||||
result = true
|
|
||||||
elif useNoGc(c, t):
|
elif useNoGc(c, t):
|
||||||
internalError(c.graph.config, c.info,
|
internalError(c.graph.config, c.info,
|
||||||
"type-bound operator could not be resolved")
|
"type-bound operator could not be resolved")
|
||||||
|
|
@ -197,7 +196,7 @@ proc considerUserDefinedOp(c: var TLiftCtx; t: PType; body, x, y: PNode): bool =
|
||||||
assert t.typeInst != nil
|
assert t.typeInst != nil
|
||||||
# patch generic destructor:
|
# patch generic destructor:
|
||||||
op = c.c.instTypeBoundOp(c.c, op, t.typeInst, c.info, attachedAsgn, 1)
|
op = c.c.instTypeBoundOp(c.c, op, t.typeInst, c.info, attachedAsgn, 1)
|
||||||
t.destructor = op
|
t.attachedOps[attachedDestructor] = op
|
||||||
|
|
||||||
markUsed(c.graph.config, c.info, op, c.graph.usageSym)
|
markUsed(c.graph.config, c.info, op, c.graph.usageSym)
|
||||||
onUse(c.info, op)
|
onUse(c.info, op)
|
||||||
|
|
@ -207,9 +206,9 @@ proc considerUserDefinedOp(c: var TLiftCtx; t: PType; body, x, y: PNode): bool =
|
||||||
of attachedAsgn:
|
of attachedAsgn:
|
||||||
result = considerAsgnOrSink(c, t, body, x, y, t.assignment)
|
result = considerAsgnOrSink(c, t, body, x, y, t.assignment)
|
||||||
of attachedSink:
|
of attachedSink:
|
||||||
result = considerAsgnOrSink(c, t, body, x, y, t.sink)
|
result = considerAsgnOrSink(c, t, body, x, y, t.asink)
|
||||||
of attachedDeepCopy:
|
of attachedDeepCopy:
|
||||||
let op = t.deepCopy
|
let op = t.attachedOps[attachedDeepCopy]
|
||||||
if op != nil:
|
if op != nil:
|
||||||
markUsed(c.graph.config, c.info, op, c.graph.usageSym)
|
markUsed(c.graph.config, c.info, op, c.graph.usageSym)
|
||||||
onUse(c.info, op)
|
onUse(c.info, op)
|
||||||
|
|
@ -323,8 +322,8 @@ proc useSeqOrStrOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||||
body.add moveCall
|
body.add moveCall
|
||||||
# alternatively we could do this:
|
# alternatively we could do this:
|
||||||
when false:
|
when false:
|
||||||
doAssert t.sink != nil
|
doAssert t.asink != nil
|
||||||
body.add newAsgnCall(c.graph, t.sink, x, y)
|
body.add newAsgnCall(c.graph, t.asink, x, y)
|
||||||
of attachedDestructor:
|
of attachedDestructor:
|
||||||
doAssert t.destructor != nil
|
doAssert t.destructor != nil
|
||||||
body.add destructorCall(c.graph, t.destructor, x)
|
body.add destructorCall(c.graph, t.destructor, x)
|
||||||
|
|
@ -365,10 +364,10 @@ proc ownedRefOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||||
#var disposeCall = genBuiltin(c.graph, mDispose, "dispose", x)
|
#var disposeCall = genBuiltin(c.graph, mDispose, "dispose", x)
|
||||||
|
|
||||||
if isFinal(elemType):
|
if isFinal(elemType):
|
||||||
discard addDestructorCall(c, elemType, actions, genDeref(x))
|
addDestructorCall(c, elemType, actions, genDeref(x))
|
||||||
actions.add callCodegenProc(c.graph, "nimRawDispose", c.info, x)
|
actions.add callCodegenProc(c.graph, "nimRawDispose", c.info, x)
|
||||||
else:
|
else:
|
||||||
discard addDestructorCall(c, elemType, newNodeI(nkStmtList, c.info), genDeref(x))
|
addDestructorCall(c, elemType, newNodeI(nkStmtList, c.info), genDeref(x))
|
||||||
actions.add callCodegenProc(c.graph, "nimDestroyAndDispose", c.info, x)
|
actions.add callCodegenProc(c.graph, "nimDestroyAndDispose", c.info, x)
|
||||||
|
|
||||||
case c.kind
|
case c.kind
|
||||||
|
|
@ -502,27 +501,10 @@ proc fillBody(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||||
proc produceSymDistinctType(c: PContext; typ: PType; kind: TTypeAttachedOp; info: TLineInfo): PSym =
|
proc produceSymDistinctType(c: PContext; typ: PType; kind: TTypeAttachedOp; info: TLineInfo): PSym =
|
||||||
assert typ.kind == tyDistinct
|
assert typ.kind == tyDistinct
|
||||||
let baseType = typ[0]
|
let baseType = typ[0]
|
||||||
case kind
|
if baseType.attachedOps[kind] == nil:
|
||||||
of attachedAsgn:
|
discard produceSym(c, baseType, kind, info)
|
||||||
if baseType.assignment == nil:
|
typ.attachedOps[kind] = baseType.attachedOps[kind]
|
||||||
discard produceSym(c, baseType, kind, info)
|
result = typ.attachedOps[kind]
|
||||||
typ.assignment = baseType.assignment
|
|
||||||
result = typ.assignment
|
|
||||||
of attachedSink:
|
|
||||||
if baseType.sink == nil:
|
|
||||||
discard produceSym(c, baseType, kind, info)
|
|
||||||
typ.sink = baseType.sink
|
|
||||||
result = typ.sink
|
|
||||||
of attachedDeepCopy:
|
|
||||||
if baseType.deepCopy == nil:
|
|
||||||
discard produceSym(c, baseType, kind, info)
|
|
||||||
typ.deepCopy = baseType.deepCopy
|
|
||||||
result = typ.deepCopy
|
|
||||||
of attachedDestructor:
|
|
||||||
if baseType.destructor == nil:
|
|
||||||
discard produceSym(c, baseType, kind, info)
|
|
||||||
typ.destructor = baseType.destructor
|
|
||||||
result = typ.destructor
|
|
||||||
|
|
||||||
proc produceSym(c: PContext; typ: PType; kind: TTypeAttachedOp;
|
proc produceSym(c: PContext; typ: PType; kind: TTypeAttachedOp;
|
||||||
info: TLineInfo): PSym =
|
info: TLineInfo): PSym =
|
||||||
|
|
@ -536,11 +518,7 @@ proc produceSym(c: PContext; typ: PType; kind: TTypeAttachedOp;
|
||||||
a.c = c
|
a.c = c
|
||||||
let g = c.graph
|
let g = c.graph
|
||||||
let body = newNodeI(nkStmtList, info)
|
let body = newNodeI(nkStmtList, info)
|
||||||
let procname = case kind
|
let procname = getIdent(g.cache, AttachedOpToStr[kind])
|
||||||
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)
|
result = newSym(skProc, procname, typ.owner, info)
|
||||||
a.fn = result
|
a.fn = result
|
||||||
|
|
@ -557,11 +535,7 @@ proc produceSym(c: PContext; typ: PType; kind: TTypeAttachedOp;
|
||||||
result.typ.addParam src
|
result.typ.addParam src
|
||||||
|
|
||||||
# register this operation already:
|
# register this operation already:
|
||||||
case kind
|
typ.attachedOps[kind] = result
|
||||||
of attachedAsgn: typ.assignment = result
|
|
||||||
of attachedSink: typ.sink = result
|
|
||||||
of attachedDeepCopy: typ.deepCopy = result
|
|
||||||
of attachedDestructor: typ.destructor = result
|
|
||||||
|
|
||||||
var tk: TTypeKind
|
var tk: TTypeKind
|
||||||
if optNimV2 in c.graph.config.globalOptions:
|
if optNimV2 in c.graph.config.globalOptions:
|
||||||
|
|
@ -636,23 +610,15 @@ proc createTypeBoundOps(c: PContext; orig: PType; info: TLineInfo) =
|
||||||
# 5. We have a (custom) generic destructor.
|
# 5. We have a (custom) generic destructor.
|
||||||
let typ = canon.skipTypes({tyGenericInst, tyAlias})
|
let typ = canon.skipTypes({tyGenericInst, tyAlias})
|
||||||
# we generate the destructor first so that other operators can depend on it:
|
# we generate the destructor first so that other operators can depend on it:
|
||||||
if typ.destructor == nil:
|
for k in attachedDestructor..attachedSink:
|
||||||
discard produceSym(c, typ, attachedDestructor, info)
|
if typ.attachedOps[k] == nil:
|
||||||
else:
|
discard produceSym(c, typ, k, info)
|
||||||
inst(typ.destructor, typ)
|
else:
|
||||||
if typ.assignment == nil:
|
inst(typ.attachedOps[k], typ)
|
||||||
discard produceSym(c, typ, attachedAsgn, info)
|
|
||||||
else:
|
|
||||||
inst(typ.assignment, typ)
|
|
||||||
if typ.sink == nil:
|
|
||||||
discard produceSym(c, typ, attachedSink, info)
|
|
||||||
else:
|
|
||||||
inst(typ.sink, typ)
|
|
||||||
|
|
||||||
if overwrite:
|
if overwrite:
|
||||||
orig.destructor = typ.destructor
|
for k in attachedDestructor..attachedSink:
|
||||||
orig.assignment = typ.assignment
|
orig.attachedOps[k] = typ.attachedOps[k]
|
||||||
orig.sink = typ.sink
|
|
||||||
|
|
||||||
if not isTrival(orig.destructor):
|
if not isTrival(orig.destructor):
|
||||||
#or not isTrival(orig.assignment) or
|
#or not isTrival(orig.assignment) or
|
||||||
|
|
|
||||||
|
|
@ -17,9 +17,9 @@ import
|
||||||
proc resetSystemArtifacts*(g: ModuleGraph) =
|
proc resetSystemArtifacts*(g: ModuleGraph) =
|
||||||
magicsys.resetSysTypes(g)
|
magicsys.resetSysTypes(g)
|
||||||
|
|
||||||
proc partialInitModule(result: PSym; graph: ModuleGraph; fileIdx: FileIndex; filename: string) =
|
proc partialInitModule(result: PSym; graph: ModuleGraph; fileIdx: FileIndex; filename: AbsoluteFile) =
|
||||||
let
|
let
|
||||||
pck = getPackageName(graph.config, filename)
|
pck = getPackageName(graph.config, filename.string)
|
||||||
pck2 = if pck.len > 0: pck else: "unknown"
|
pck2 = if pck.len > 0: pck else: "unknown"
|
||||||
pack = getIdent(graph.cache, pck2)
|
pack = getIdent(graph.cache, pck2)
|
||||||
var packSym = graph.packageSyms.strTableGet(pack)
|
var packSym = graph.packageSyms.strTableGet(pack)
|
||||||
|
|
@ -27,6 +27,22 @@ proc partialInitModule(result: PSym; graph: ModuleGraph; fileIdx: FileIndex; fil
|
||||||
packSym = newSym(skPackage, getIdent(graph.cache, pck2), nil, result.info)
|
packSym = newSym(skPackage, getIdent(graph.cache, pck2), nil, result.info)
|
||||||
initStrTable(packSym.tab)
|
initStrTable(packSym.tab)
|
||||||
graph.packageSyms.strTableAdd(packSym)
|
graph.packageSyms.strTableAdd(packSym)
|
||||||
|
else:
|
||||||
|
let existing = strTableGet(packSym.tab, result.name)
|
||||||
|
if existing != nil and existing.info.fileIndex != result.info.fileIndex:
|
||||||
|
when false:
|
||||||
|
# we used to produce an error:
|
||||||
|
localError(graph.config, result.info,
|
||||||
|
"module names need to be unique per Nimble package; module clashes with " &
|
||||||
|
toFullPath(graph.config, existing.info.fileIndex))
|
||||||
|
else:
|
||||||
|
# but starting with version 0.20 we now produce a fake Nimble package instead
|
||||||
|
# to resolve the conflicts:
|
||||||
|
let pck3 = fakePackageName(graph.config, filename)
|
||||||
|
packSym = newSym(skPackage, getIdent(graph.cache, pck3), nil, result.info)
|
||||||
|
initStrTable(packSym.tab)
|
||||||
|
graph.packageSyms.strTableAdd(packSym)
|
||||||
|
|
||||||
result.owner = packSym
|
result.owner = packSym
|
||||||
result.position = int fileIdx
|
result.position = int fileIdx
|
||||||
|
|
||||||
|
|
@ -37,13 +53,7 @@ proc partialInitModule(result: PSym; graph: ModuleGraph; fileIdx: FileIndex; fil
|
||||||
incl(result.flags, sfUsed)
|
incl(result.flags, sfUsed)
|
||||||
initStrTable(result.tab)
|
initStrTable(result.tab)
|
||||||
strTableAdd(result.tab, result) # a module knows itself
|
strTableAdd(result.tab, result) # a module knows itself
|
||||||
let existing = strTableGet(packSym.tab, result.name)
|
strTableAdd(packSym.tab, result)
|
||||||
if existing != nil and existing.info.fileIndex != result.info.fileIndex:
|
|
||||||
localError(graph.config, result.info,
|
|
||||||
"module names need to be unique per Nimble package; module clashes with " &
|
|
||||||
toFullPath(graph.config, existing.info.fileIndex))
|
|
||||||
# strTableIncl() for error corrections:
|
|
||||||
discard strTableIncl(packSym.tab, result)
|
|
||||||
|
|
||||||
proc newModule(graph: ModuleGraph; fileIdx: FileIndex): PSym =
|
proc newModule(graph: ModuleGraph; fileIdx: FileIndex): PSym =
|
||||||
# We cannot call ``newSym`` here, because we have to circumvent the ID
|
# We cannot call ``newSym`` here, because we have to circumvent the ID
|
||||||
|
|
@ -51,7 +61,7 @@ proc newModule(graph: ModuleGraph; fileIdx: FileIndex): PSym =
|
||||||
new(result)
|
new(result)
|
||||||
result.id = -1 # for better error checking
|
result.id = -1 # for better error checking
|
||||||
result.kind = skModule
|
result.kind = skModule
|
||||||
let filename = toFullPath(graph.config, fileIdx)
|
let filename = AbsoluteFile toFullPath(graph.config, fileIdx)
|
||||||
result.name = getIdent(graph.cache, splitFile(filename).name)
|
result.name = getIdent(graph.cache, splitFile(filename).name)
|
||||||
if not isNimIdentifier(result.name.s):
|
if not isNimIdentifier(result.name.s):
|
||||||
rawMessage(graph.config, errGenerated, "invalid module name: " & result.name.s)
|
rawMessage(graph.config, errGenerated, "invalid module name: " & result.name.s)
|
||||||
|
|
@ -61,8 +71,8 @@ proc newModule(graph: ModuleGraph; fileIdx: FileIndex): PSym =
|
||||||
proc compileModule*(graph: ModuleGraph; fileIdx: FileIndex; flags: TSymFlags): PSym =
|
proc compileModule*(graph: ModuleGraph; fileIdx: FileIndex; flags: TSymFlags): PSym =
|
||||||
result = graph.getModule(fileIdx)
|
result = graph.getModule(fileIdx)
|
||||||
if result == nil:
|
if result == nil:
|
||||||
let filename = toFullPath(graph.config, fileIdx)
|
let filename = AbsoluteFile toFullPath(graph.config, fileIdx)
|
||||||
let (r, id) = loadModuleSym(graph, fileIdx, AbsoluteFile filename)
|
let (r, id) = loadModuleSym(graph, fileIdx, filename)
|
||||||
result = r
|
result = r
|
||||||
if result == nil:
|
if result == nil:
|
||||||
result = newModule(graph, fileIdx)
|
result = newModule(graph, fileIdx)
|
||||||
|
|
|
||||||
|
|
@ -29,9 +29,6 @@ proc getPackageName*(conf: ConfigRef; path: string): string =
|
||||||
for file in walkFiles(d / "*.nimble"):
|
for file in walkFiles(d / "*.nimble"):
|
||||||
result = file.splitFile.name
|
result = file.splitFile.name
|
||||||
break packageSearch
|
break packageSearch
|
||||||
for file in walkFiles(d / "*.babel"):
|
|
||||||
result = file.splitFile.name
|
|
||||||
break packageSearch
|
|
||||||
# we also store if we didn't find anything:
|
# we also store if we didn't find anything:
|
||||||
when not defined(nimNoNilSeqs):
|
when not defined(nimNoNilSeqs):
|
||||||
if result.isNil: result = ""
|
if result.isNil: result = ""
|
||||||
|
|
@ -41,10 +38,19 @@ proc getPackageName*(conf: ConfigRef; path: string): string =
|
||||||
dec parents
|
dec parents
|
||||||
if parents <= 0: break
|
if parents <= 0: break
|
||||||
|
|
||||||
|
proc fakePackageName*(conf: ConfigRef; path: AbsoluteFile): string =
|
||||||
|
# foo/../bar becomes foo7_7bar
|
||||||
|
result = relativeTo(path, conf.projectPath, '/').string.multiReplace(
|
||||||
|
{"/": "7", "..": "_", "7": "77", "_": "__"})
|
||||||
|
|
||||||
proc withPackageName*(conf: ConfigRef; path: AbsoluteFile): AbsoluteFile =
|
proc withPackageName*(conf: ConfigRef; path: AbsoluteFile): AbsoluteFile =
|
||||||
let x = getPackageName(conf, path.string)
|
let x = getPackageName(conf, path.string)
|
||||||
if x.len == 0:
|
if x.len == 0:
|
||||||
result = path
|
result = path
|
||||||
else:
|
else:
|
||||||
let (p, file, ext) = path.splitFile
|
let (p, file, ext) = path.splitFile
|
||||||
result = p / RelativeFile((x & '_' & file) & ext)
|
if x == "stdlib":
|
||||||
|
# Hot code reloading now relies on 'stdlib_system' names etc.
|
||||||
|
result = p / RelativeFile((x & '_' & file) & ext)
|
||||||
|
else:
|
||||||
|
result = p / RelativeFile(fakePackageName(conf, path))
|
||||||
|
|
|
||||||
|
|
@ -211,7 +211,7 @@ proc ropeConcat*(a: varargs[Rope]): Rope =
|
||||||
proc prepend*(a: var Rope, b: Rope) = a = b & a
|
proc prepend*(a: var Rope, b: Rope) = a = b & a
|
||||||
proc prepend*(a: var Rope, b: string) = a = b & a
|
proc prepend*(a: var Rope, b: string) = a = b & a
|
||||||
|
|
||||||
proc `%`*(frmt: FormatStr, args: openArray[Rope]): Rope =
|
proc runtimeFormat*(frmt: FormatStr, args: openArray[Rope]): Rope =
|
||||||
var i = 0
|
var i = 0
|
||||||
var length = len(frmt)
|
var length = len(frmt)
|
||||||
result = nil
|
result = nil
|
||||||
|
|
@ -269,7 +269,10 @@ proc `%`*(frmt: FormatStr, args: openArray[Rope]): Rope =
|
||||||
add(result, substr(frmt, start, i - 1))
|
add(result, substr(frmt, start, i - 1))
|
||||||
assert(ropeInvariant(result))
|
assert(ropeInvariant(result))
|
||||||
|
|
||||||
proc addf*(c: var Rope, frmt: FormatStr, args: openArray[Rope]) =
|
proc `%`*(frmt: static[FormatStr], args: openArray[Rope]): Rope =
|
||||||
|
runtimeFormat(frmt, args)
|
||||||
|
|
||||||
|
template addf*(c: var Rope, frmt: FormatStr, args: openArray[Rope]) =
|
||||||
## shortcut for ``add(c, frmt % args)``.
|
## shortcut for ``add(c, frmt % args)``.
|
||||||
add(c, frmt % args)
|
add(c, frmt % args)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -72,12 +72,6 @@ type
|
||||||
|
|
||||||
TExprFlags* = set[TExprFlag]
|
TExprFlags* = set[TExprFlag]
|
||||||
|
|
||||||
TTypeAttachedOp* = enum
|
|
||||||
attachedAsgn,
|
|
||||||
attachedSink,
|
|
||||||
attachedDeepCopy,
|
|
||||||
attachedDestructor
|
|
||||||
|
|
||||||
PContext* = ref TContext
|
PContext* = ref TContext
|
||||||
TContext* = object of TPassContext # a context represents a module
|
TContext* = object of TPassContext # a context represents a module
|
||||||
enforceVoidContext*: PType
|
enforceVoidContext*: PType
|
||||||
|
|
|
||||||
|
|
@ -937,7 +937,7 @@ proc semExprNoType(c: PContext, n: PNode): PNode =
|
||||||
let isPush = hintExtendedContext in c.config.notes
|
let isPush = hintExtendedContext in c.config.notes
|
||||||
if isPush: pushInfoContext(c.config, n.info)
|
if isPush: pushInfoContext(c.config, n.info)
|
||||||
result = semExpr(c, n, {efWantStmt})
|
result = semExpr(c, n, {efWantStmt})
|
||||||
result = discardCheck(c, result, {})
|
discardCheck(c, result, {})
|
||||||
if isPush: popInfoContext(c.config)
|
if isPush: popInfoContext(c.config)
|
||||||
|
|
||||||
proc isTypeExpr(n: PNode): bool =
|
proc isTypeExpr(n: PNode): bool =
|
||||||
|
|
@ -1526,17 +1526,52 @@ proc asgnToResultVar(c: PContext, n, le, ri: PNode) {.inline.} =
|
||||||
x.typ.flags.incl tfVarIsPtr
|
x.typ.flags.incl tfVarIsPtr
|
||||||
#echo x.info, " setting it for this type ", typeToString(x.typ), " ", n.info
|
#echo x.info, " setting it for this type ", typeToString(x.typ), " ", n.info
|
||||||
|
|
||||||
proc asgnToResult(c: PContext, n, le, ri: PNode) =
|
proc borrowCheck(c: PContext, n, le, ri: PNode) =
|
||||||
|
const
|
||||||
|
PathKinds0 = {nkDotExpr, nkCheckedFieldExpr,
|
||||||
|
nkBracketExpr, nkAddr, nkHiddenAddr,
|
||||||
|
nkObjDownConv, nkObjUpConv}
|
||||||
|
PathKinds1 = {nkHiddenStdConv, nkHiddenSubConv}
|
||||||
|
|
||||||
|
proc getRoot(n: PNode; followDeref: bool): PNode =
|
||||||
|
result = n
|
||||||
|
while true:
|
||||||
|
case result.kind
|
||||||
|
of nkDerefExpr, nkHiddenDeref:
|
||||||
|
if followDeref: result = result[0]
|
||||||
|
else: break
|
||||||
|
of PathKinds0:
|
||||||
|
result = result[0]
|
||||||
|
of PathKinds1:
|
||||||
|
result = result[1]
|
||||||
|
else: break
|
||||||
|
|
||||||
|
proc scopedLifetime(c: PContext; ri: PNode): bool {.inline.} =
|
||||||
|
let n = getRoot(ri, followDeref = false)
|
||||||
|
result = (ri.kind in nkCallKinds+{nkObjConstr}) or
|
||||||
|
(n.kind == nkSym and n.sym.owner == c.p.owner)
|
||||||
|
|
||||||
|
proc escapes(c: PContext; le: PNode): bool {.inline.} =
|
||||||
|
# param[].foo[] = self definitely escapes, we don't need to
|
||||||
|
# care about pointer derefs:
|
||||||
|
let n = getRoot(le, followDeref = true)
|
||||||
|
result = n.kind == nkSym and n.sym.kind == skParam
|
||||||
|
|
||||||
# Special typing rule: do not allow to pass 'owned T' to 'T' in 'result = x':
|
# Special typing rule: do not allow to pass 'owned T' to 'T' in 'result = x':
|
||||||
if ri.typ != nil and ri.typ.skipTypes(abstractInst).kind == tyOwned and
|
const absInst = abstractInst - {tyOwned}
|
||||||
le.typ != nil and le.typ.skipTypes(abstractInst).kind != tyOwned and ri.kind in nkCallKinds:
|
if ri.typ != nil and ri.typ.skipTypes(absInst).kind == tyOwned and
|
||||||
localError(c.config, n.info, "cannot return an owned pointer as an unowned pointer; " &
|
le.typ != nil and le.typ.skipTypes(absInst).kind != tyOwned and
|
||||||
"use 'owned(" & typeToString(le.typ) & ")' as the return type")
|
scopedLifetime(c, ri):
|
||||||
|
if le.kind == nkSym and le.sym.kind == skResult:
|
||||||
|
localError(c.config, n.info, "cannot return an owned pointer as an unowned pointer; " &
|
||||||
|
"use 'owned(" & typeToString(le.typ) & ")' as the return type")
|
||||||
|
elif escapes(c, le):
|
||||||
|
localError(c.config, n.info,
|
||||||
|
"assignment produces a dangling ref: the unowned ref lives longer than the owned ref")
|
||||||
|
|
||||||
template resultTypeIsInferrable(typ: PType): untyped =
|
template resultTypeIsInferrable(typ: PType): untyped =
|
||||||
typ.isMetaType and typ.kind != tyTypeDesc
|
typ.isMetaType and typ.kind != tyTypeDesc
|
||||||
|
|
||||||
|
|
||||||
proc goodLineInfo(arg: PNode): TLineinfo =
|
proc goodLineInfo(arg: PNode): TLineinfo =
|
||||||
if arg.kind == nkStmtListExpr and arg.len > 0:
|
if arg.kind == nkStmtListExpr and arg.len > 0:
|
||||||
goodLineInfo(arg[^1])
|
goodLineInfo(arg[^1])
|
||||||
|
|
@ -1623,7 +1658,7 @@ proc semAsgn(c: PContext, n: PNode; mode=asgnNormal): PNode =
|
||||||
c.p.owner.typ.sons[0] = rhsTyp
|
c.p.owner.typ.sons[0] = rhsTyp
|
||||||
else:
|
else:
|
||||||
typeMismatch(c.config, n.info, lhs.typ, rhsTyp)
|
typeMismatch(c.config, n.info, lhs.typ, rhsTyp)
|
||||||
asgnToResult(c, n, n.sons[0], rhs)
|
borrowCheck(c, n, lhs, rhs)
|
||||||
|
|
||||||
n.sons[1] = fitNode(c, le, rhs, goodLineInfo(n[1]))
|
n.sons[1] = fitNode(c, le, rhs, goodLineInfo(n[1]))
|
||||||
liftTypeBoundOps(c, lhs.typ, lhs.info)
|
liftTypeBoundOps(c, lhs.typ, lhs.info)
|
||||||
|
|
@ -1673,7 +1708,7 @@ proc semProcBody(c: PContext, n: PNode): PNode =
|
||||||
a.sons[1] = result
|
a.sons[1] = result
|
||||||
result = semAsgn(c, a)
|
result = semAsgn(c, a)
|
||||||
else:
|
else:
|
||||||
result = discardCheck(c, result, {})
|
discardCheck(c, result, {})
|
||||||
|
|
||||||
if c.p.owner.kind notin {skMacro, skTemplate} and
|
if c.p.owner.kind notin {skMacro, skTemplate} and
|
||||||
c.p.resultSym != nil and c.p.resultSym.typ.isMetaType:
|
c.p.resultSym != nil and c.p.resultSym.typ.isMetaType:
|
||||||
|
|
|
||||||
|
|
@ -322,6 +322,39 @@ proc semOf(c: PContext, n: PNode): PNode =
|
||||||
n.typ = getSysType(c.graph, n.info, tyBool)
|
n.typ = getSysType(c.graph, n.info, tyBool)
|
||||||
result = n
|
result = n
|
||||||
|
|
||||||
|
proc semUnown(c: PContext; n: PNode): PNode =
|
||||||
|
proc unownedType(c: PContext; t: PType): PType =
|
||||||
|
case t.kind
|
||||||
|
of tyTuple:
|
||||||
|
var elems = newSeq[PType](t.len)
|
||||||
|
var someChange = false
|
||||||
|
for i in 0..<t.len:
|
||||||
|
elems[i] = unownedType(c, t[i])
|
||||||
|
if elems[i] != t[i]: someChange = true
|
||||||
|
if someChange:
|
||||||
|
result = newType(tyTuple, t.owner)
|
||||||
|
# we have to use 'rawAddSon' here so that type flags are
|
||||||
|
# properly computed:
|
||||||
|
for e in elems: result.rawAddSon(e)
|
||||||
|
else:
|
||||||
|
result = t
|
||||||
|
of tyOwned: result = t.sons[0]
|
||||||
|
of tySequence, tyOpenArray, tyArray, tyVarargs, tyVar, tyLent,
|
||||||
|
tyGenericInst, tyAlias:
|
||||||
|
let L = t.len-1
|
||||||
|
let b = unownedType(c, t[L])
|
||||||
|
if b != t[L]:
|
||||||
|
result = copyType(t, t.owner, keepId = false)
|
||||||
|
result[L] = b
|
||||||
|
result.flags.excl tfHasOwned
|
||||||
|
else:
|
||||||
|
result = t
|
||||||
|
else:
|
||||||
|
result = t
|
||||||
|
|
||||||
|
result = copyTree(n[1])
|
||||||
|
result.typ = unownedType(c, result.typ)
|
||||||
|
|
||||||
proc magicsAfterOverloadResolution(c: PContext, n: PNode,
|
proc magicsAfterOverloadResolution(c: PContext, n: PNode,
|
||||||
flags: TExprFlags): PNode =
|
flags: TExprFlags): PNode =
|
||||||
## This is the preferred code point to implement magics.
|
## This is the preferred code point to implement magics.
|
||||||
|
|
@ -433,4 +466,6 @@ proc magicsAfterOverloadResolution(c: PContext, n: PNode,
|
||||||
let t = n[1].typ.skipTypes(abstractVar)
|
let t = n[1].typ.skipTypes(abstractVar)
|
||||||
if t.destructor != nil:
|
if t.destructor != nil:
|
||||||
result.sons[0] = newSymNode(t.destructor)
|
result.sons[0] = newSymNode(t.destructor)
|
||||||
|
of mUnown:
|
||||||
|
result = semUnown(c, n)
|
||||||
else: result = n
|
else: result = n
|
||||||
|
|
|
||||||
|
|
@ -527,7 +527,7 @@ proc isNoEffectList(n: PNode): bool {.inline.} =
|
||||||
n.len == 0 or (n[tagEffects] == nil and n[exceptionEffects] == nil)
|
n.len == 0 or (n[tagEffects] == nil and n[exceptionEffects] == nil)
|
||||||
|
|
||||||
proc isTrival(caller: PNode): bool {.inline.} =
|
proc isTrival(caller: PNode): bool {.inline.} =
|
||||||
result = caller.kind == nkSym and caller.sym.magic in {mEqProc, mIsNil}
|
result = caller.kind == nkSym and caller.sym.magic in {mEqProc, mIsNil, mMove, mWasMoved}
|
||||||
|
|
||||||
proc trackOperand(tracked: PEffects, n: PNode, paramType: PType; caller: PNode) =
|
proc trackOperand(tracked: PEffects, n: PNode, paramType: PType; caller: PNode) =
|
||||||
let a = skipConvAndClosure(n)
|
let a = skipConvAndClosure(n)
|
||||||
|
|
@ -738,7 +738,7 @@ proc track(tracked: PEffects, n: PNode) =
|
||||||
mergeTags(tracked, effectList.sons[tagEffects], n)
|
mergeTags(tracked, effectList.sons[tagEffects], n)
|
||||||
gcsafeAndSideeffectCheck()
|
gcsafeAndSideeffectCheck()
|
||||||
if a.kind != nkSym or a.sym.magic != mNBindSym:
|
if a.kind != nkSym or a.sym.magic != mNBindSym:
|
||||||
for i in 1 ..< len(n): trackOperand(tracked, n.sons[i], paramType(op, i), a)
|
for i in 1 ..< n.len: trackOperand(tracked, n.sons[i], paramType(op, i), a)
|
||||||
if a.kind == nkSym and a.sym.magic in {mNew, mNewFinalize, mNewSeq}:
|
if a.kind == nkSym and a.sym.magic in {mNew, mNewFinalize, mNewSeq}:
|
||||||
# may not look like an assignment, but it is:
|
# may not look like an assignment, but it is:
|
||||||
let arg = n.sons[1]
|
let arg = n.sons[1]
|
||||||
|
|
@ -754,7 +754,7 @@ proc track(tracked: PEffects, n: PNode) =
|
||||||
track(tracked, n.sons[i])
|
track(tracked, n.sons[i])
|
||||||
of nkDotExpr:
|
of nkDotExpr:
|
||||||
guardDotAccess(tracked, n)
|
guardDotAccess(tracked, n)
|
||||||
for i in 0 ..< len(n): track(tracked, n.sons[i])
|
for i in 0 ..< n.len: track(tracked, n.sons[i])
|
||||||
of nkCheckedFieldExpr:
|
of nkCheckedFieldExpr:
|
||||||
track(tracked, n.sons[0])
|
track(tracked, n.sons[0])
|
||||||
if warnProveField in tracked.config.notes:
|
if warnProveField in tracked.config.notes:
|
||||||
|
|
@ -821,7 +821,7 @@ proc track(tracked: PEffects, n: PNode) =
|
||||||
of nkForStmt, nkParForStmt:
|
of nkForStmt, nkParForStmt:
|
||||||
# we are very conservative here and assume the loop is never executed:
|
# we are very conservative here and assume the loop is never executed:
|
||||||
let oldState = tracked.init.len
|
let oldState = tracked.init.len
|
||||||
for i in 0 .. len(n)-3:
|
for i in 0 .. n.len-3:
|
||||||
let it = n[i]
|
let it = n[i]
|
||||||
track(tracked, it)
|
track(tracked, it)
|
||||||
if tracked.owner.kind != skMacro:
|
if tracked.owner.kind != skMacro:
|
||||||
|
|
@ -830,13 +830,19 @@ proc track(tracked: PEffects, n: PNode) =
|
||||||
createTypeBoundOps(tracked.c, x.typ, x.info)
|
createTypeBoundOps(tracked.c, x.typ, x.info)
|
||||||
else:
|
else:
|
||||||
createTypeBoundOps(tracked.c, it.typ, it.info)
|
createTypeBoundOps(tracked.c, it.typ, it.info)
|
||||||
for i in len(n)-2..len(n)-1:
|
let iterCall = n[n.len-2]
|
||||||
track(tracked, n.sons[i])
|
let loopBody = n[n.len-1]
|
||||||
|
if tracked.owner.kind != skMacro and iterCall.safelen > 1:
|
||||||
|
# XXX this is a bit hacky:
|
||||||
|
if iterCall[1].typ != nil and iterCall[1].typ.skipTypes(abstractVar).kind notin {tyVarargs, tyOpenArray}:
|
||||||
|
createTypeBoundOps(tracked.c, iterCall[1].typ, iterCall[1].info)
|
||||||
|
track(tracked, iterCall)
|
||||||
|
track(tracked, loopBody)
|
||||||
setLen(tracked.init, oldState)
|
setLen(tracked.init, oldState)
|
||||||
of nkObjConstr:
|
of nkObjConstr:
|
||||||
when false: track(tracked, n.sons[0])
|
when false: track(tracked, n.sons[0])
|
||||||
let oldFacts = tracked.guards.s.len
|
let oldFacts = tracked.guards.s.len
|
||||||
for i in 1 ..< len(n):
|
for i in 1 ..< n.len:
|
||||||
let x = n.sons[i]
|
let x = n.sons[i]
|
||||||
track(tracked, x)
|
track(tracked, x)
|
||||||
if x.sons[0].kind == nkSym and sfDiscriminant in x.sons[0].sym.flags:
|
if x.sons[0].kind == nkSym and sfDiscriminant in x.sons[0].sym.flags:
|
||||||
|
|
|
||||||
|
|
@ -43,7 +43,7 @@ proc semDiscard(c: PContext, n: PNode): PNode =
|
||||||
n.sons[0] = semExprWithType(c, n.sons[0])
|
n.sons[0] = semExprWithType(c, n.sons[0])
|
||||||
let sonType = n.sons[0].typ
|
let sonType = n.sons[0].typ
|
||||||
let sonKind = n.sons[0].kind
|
let sonKind = n.sons[0].kind
|
||||||
if isEmptyType(sonType) or sonType.kind == tyNone or n.sons[0].kind == nkTypeOfExpr:
|
if isEmptyType(sonType) or sonType.kind in {tyNone, tyTypeDesc} or sonKind == nkTypeOfExpr:
|
||||||
localError(c.config, n.info, errInvalidDiscard)
|
localError(c.config, n.info, errInvalidDiscard)
|
||||||
if sonType.kind == tyProc and sonKind notin nkCallKinds:
|
if sonType.kind == tyProc and sonKind notin nkCallKinds:
|
||||||
# tyProc is disallowed to prevent ``discard foo`` to be valid, when ``discard foo()`` is meant.
|
# tyProc is disallowed to prevent ``discard foo`` to be valid, when ``discard foo()`` is meant.
|
||||||
|
|
@ -130,13 +130,13 @@ proc fixNilType(c: PContext; n: PNode) =
|
||||||
for it in n: fixNilType(c, it)
|
for it in n: fixNilType(c, it)
|
||||||
n.typ = nil
|
n.typ = nil
|
||||||
|
|
||||||
proc discardCheck(c: PContext, expr: PNode, flags: TExprFlags): PNode =
|
proc discardCheck(c: PContext, result: PNode, flags: TExprFlags) =
|
||||||
result = expr
|
|
||||||
if c.matchedConcept != nil or efInTypeof in flags: return
|
if c.matchedConcept != nil or efInTypeof in flags: return
|
||||||
|
|
||||||
if result.typ != nil and result.typ.kind notin {tyStmt, tyVoid}:
|
if result.typ != nil and result.typ.kind notin {tyStmt, tyVoid}:
|
||||||
if implicitlyDiscardable(result):
|
if implicitlyDiscardable(result):
|
||||||
result = newNode(nkDiscardStmt, result.info, @[result])
|
var n = newNodeI(nkDiscardStmt, result.info, 1)
|
||||||
|
n[0] = result
|
||||||
elif result.typ.kind != tyError and c.config.cmd != cmdInteractive:
|
elif result.typ.kind != tyError and c.config.cmd != cmdInteractive:
|
||||||
var n = result
|
var n = result
|
||||||
while n.kind in skipForDiscardable: n = n.lastSon
|
while n.kind in skipForDiscardable: n = n.lastSon
|
||||||
|
|
@ -168,8 +168,7 @@ proc semIf(c: PContext, n: PNode; flags: TExprFlags): PNode =
|
||||||
else: illFormedAst(it, c.config)
|
else: illFormedAst(it, c.config)
|
||||||
if isEmptyType(typ) or typ.kind in {tyNil, tyExpr} or
|
if isEmptyType(typ) or typ.kind in {tyNil, tyExpr} or
|
||||||
(not hasElse and efInTypeof notin flags):
|
(not hasElse and efInTypeof notin flags):
|
||||||
for it in n:
|
for it in n: discardCheck(c, it.lastSon, flags)
|
||||||
it.sons[^1] = discardCheck(c, it.sons[^1], flags)
|
|
||||||
result.kind = nkIfStmt
|
result.kind = nkIfStmt
|
||||||
# propagate any enforced VoidContext:
|
# propagate any enforced VoidContext:
|
||||||
if typ == c.enforceVoidContext: result.typ = c.enforceVoidContext
|
if typ == c.enforceVoidContext: result.typ = c.enforceVoidContext
|
||||||
|
|
@ -267,14 +266,12 @@ proc semTry(c: PContext, n: PNode; flags: TExprFlags): PNode =
|
||||||
|
|
||||||
dec c.p.inTryStmt
|
dec c.p.inTryStmt
|
||||||
if isEmptyType(typ) or typ.kind in {tyNil, tyExpr}:
|
if isEmptyType(typ) or typ.kind in {tyNil, tyExpr}:
|
||||||
n.sons[0] = discardCheck(c, n.sons[0], flags)
|
discardCheck(c, n.sons[0], flags)
|
||||||
for i in 1..n.len-1:
|
for i in 1..n.len-1: discardCheck(c, n.sons[i].lastSon, flags)
|
||||||
n.sons[i].sons[^1] = discardCheck(c, n.sons[i].sons[^1], flags)
|
|
||||||
if typ == c.enforceVoidContext:
|
if typ == c.enforceVoidContext:
|
||||||
result.typ = c.enforceVoidContext
|
result.typ = c.enforceVoidContext
|
||||||
else:
|
else:
|
||||||
if n.lastSon.kind == nkFinally:
|
if n.lastSon.kind == nkFinally: discardCheck(c, n.lastSon.lastSon, flags)
|
||||||
n.sons[^1].sons[^1] = discardCheck(c, n.sons[^1].sons[^1], flags)
|
|
||||||
n.sons[0] = fitNode(c, typ, n.sons[0], n.sons[0].info)
|
n.sons[0] = fitNode(c, typ, n.sons[0], n.sons[0].info)
|
||||||
for i in 1..last:
|
for i in 1..last:
|
||||||
var it = n.sons[i]
|
var it = n.sons[i]
|
||||||
|
|
@ -474,10 +471,14 @@ proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode =
|
||||||
($typ.kind).substr(2).toLowerAscii)
|
($typ.kind).substr(2).toLowerAscii)
|
||||||
elif typ.kind == tyProc and tfUnresolved in typ.flags:
|
elif typ.kind == tyProc and tfUnresolved in typ.flags:
|
||||||
localError(c.config, def.info, errProcHasNoConcreteType % def.renderTree)
|
localError(c.config, def.info, errProcHasNoConcreteType % def.renderTree)
|
||||||
elif symkind == skVar and typ.kind == tyOwned and def.kind notin nkCallKinds:
|
when false:
|
||||||
# special type inference rule: 'var it = ownedPointer' is turned
|
# XXX This typing rule is neither documented nor complete enough to
|
||||||
# into an unowned pointer.
|
# justify it. Instead use the newer 'unowned x' until we figured out
|
||||||
typ = typ.lastSon
|
# a more general solution.
|
||||||
|
if symkind == skVar and typ.kind == tyOwned and def.kind notin nkCallKinds:
|
||||||
|
# special type inference rule: 'var it = ownedPointer' is turned
|
||||||
|
# into an unowned pointer.
|
||||||
|
typ = typ.lastSon
|
||||||
else:
|
else:
|
||||||
if symkind == skLet: localError(c.config, a.info, errLetNeedsInit)
|
if symkind == skLet: localError(c.config, a.info, errLetNeedsInit)
|
||||||
|
|
||||||
|
|
@ -729,7 +730,7 @@ proc semForVars(c: PContext, n: PNode; flags: TExprFlags): PNode =
|
||||||
openScope(c)
|
openScope(c)
|
||||||
n.sons[length-1] = semExprBranch(c, n.sons[length-1], flags)
|
n.sons[length-1] = semExprBranch(c, n.sons[length-1], flags)
|
||||||
if efInTypeof notin flags:
|
if efInTypeof notin flags:
|
||||||
n.sons[^1] = discardCheck(c, n.sons[^1], flags)
|
discardCheck(c, n.sons[length-1], flags)
|
||||||
closeScope(c)
|
closeScope(c)
|
||||||
dec(c.p.nestedLoopCounter)
|
dec(c.p.nestedLoopCounter)
|
||||||
|
|
||||||
|
|
@ -919,8 +920,7 @@ proc semCase(c: PContext, n: PNode; flags: TExprFlags): PNode =
|
||||||
closeScope(c)
|
closeScope(c)
|
||||||
if isEmptyType(typ) or typ.kind in {tyNil, tyExpr} or
|
if isEmptyType(typ) or typ.kind in {tyNil, tyExpr} or
|
||||||
(not hasElse and efInTypeof notin flags):
|
(not hasElse and efInTypeof notin flags):
|
||||||
for i in 1..n.len-1:
|
for i in 1..n.len-1: discardCheck(c, n.sons[i].lastSon, flags)
|
||||||
n.sons[i].sons[^1] = discardCheck(c, n.sons[i].sons[^1], flags)
|
|
||||||
# propagate any enforced VoidContext:
|
# propagate any enforced VoidContext:
|
||||||
if typ == c.enforceVoidContext:
|
if typ == c.enforceVoidContext:
|
||||||
result.typ = c.enforceVoidContext
|
result.typ = c.enforceVoidContext
|
||||||
|
|
@ -1568,7 +1568,7 @@ proc semOverride(c: PContext, s: PSym, n: PNode) =
|
||||||
if obj.kind in {tyObject, tyDistinct, tySequence, tyString}:
|
if obj.kind in {tyObject, tyDistinct, tySequence, tyString}:
|
||||||
obj = canonType(c, obj)
|
obj = canonType(c, obj)
|
||||||
if obj.destructor.isNil:
|
if obj.destructor.isNil:
|
||||||
obj.destructor = s
|
obj.attachedOps[attachedDestructor] = s
|
||||||
else:
|
else:
|
||||||
prevDestructor(c, obj.destructor, obj, n.info)
|
prevDestructor(c, obj.destructor, obj, n.info)
|
||||||
noError = true
|
noError = true
|
||||||
|
|
@ -1592,7 +1592,7 @@ proc semOverride(c: PContext, s: PSym, n: PNode) =
|
||||||
elif t.kind == tyGenericInvocation: t = t.sons[0]
|
elif t.kind == tyGenericInvocation: t = t.sons[0]
|
||||||
else: break
|
else: break
|
||||||
if t.kind in {tyObject, tyDistinct, tyEnum, tySequence, tyString}:
|
if t.kind in {tyObject, tyDistinct, tyEnum, tySequence, tyString}:
|
||||||
if t.deepCopy.isNil: t.deepCopy = s
|
if t.attachedOps[attachedDeepCopy].isNil: t.attachedOps[attachedDeepCopy] = s
|
||||||
else:
|
else:
|
||||||
localError(c.config, n.info, errGenerated,
|
localError(c.config, n.info, errGenerated,
|
||||||
"cannot bind another 'deepCopy' to: " & typeToString(t))
|
"cannot bind another 'deepCopy' to: " & typeToString(t))
|
||||||
|
|
@ -1631,11 +1631,11 @@ proc semOverride(c: PContext, s: PSym, n: PNode) =
|
||||||
# attach these ops to the canonical tySequence
|
# attach these ops to the canonical tySequence
|
||||||
obj = canonType(c, obj)
|
obj = canonType(c, obj)
|
||||||
#echo "ATTACHING TO ", obj.id, " ", s.name.s, " ", cast[int](obj)
|
#echo "ATTACHING TO ", obj.id, " ", s.name.s, " ", cast[int](obj)
|
||||||
let opr = if s.name.s == "=": addr(obj.assignment) else: addr(obj.sink)
|
let k = if name == "=": attachedAsgn else: attachedSink
|
||||||
if opr[].isNil:
|
if obj.attachedOps[k].isNil:
|
||||||
opr[] = s
|
obj.attachedOps[k] = s
|
||||||
else:
|
else:
|
||||||
prevDestructor(c, opr[], obj, n.info)
|
prevDestructor(c, obj.attachedOps[k], obj, n.info)
|
||||||
if obj.owner.getModule != s.getModule:
|
if obj.owner.getModule != s.getModule:
|
||||||
localError(c.config, n.info, errGenerated,
|
localError(c.config, n.info, errGenerated,
|
||||||
"type bound operation `" & name & "` can be defined only in the same module with its type (" & obj.typeToString() & ")")
|
"type bound operation `" & name & "` can be defined only in the same module with its type (" & obj.typeToString() & ")")
|
||||||
|
|
@ -1828,7 +1828,7 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind,
|
||||||
|
|
||||||
if sfOverriden in s.flags or s.name.s[0] == '=': semOverride(c, s, n)
|
if sfOverriden in s.flags or s.name.s[0] == '=': semOverride(c, s, n)
|
||||||
if s.name.s[0] in {'.', '('}:
|
if s.name.s[0] in {'.', '('}:
|
||||||
if s.name.s in [".", ".()", ".="] and {destructor, dotOperators} * c.features == {}:
|
if s.name.s in [".", ".()", ".="] and {Feature.destructor, dotOperators} * c.features == {}:
|
||||||
localError(c.config, n.info, "the overloaded " & s.name.s &
|
localError(c.config, n.info, "the overloaded " & s.name.s &
|
||||||
" operator has to be enabled with {.experimental: \"dotOperators\".}")
|
" operator has to be enabled with {.experimental: \"dotOperators\".}")
|
||||||
elif s.name.s == "()" and callOperator notin c.features:
|
elif s.name.s == "()" and callOperator notin c.features:
|
||||||
|
|
@ -2124,7 +2124,7 @@ proc semStmtList(c: PContext, n: PNode, flags: TExprFlags): PNode =
|
||||||
n.typ = n.sons[i].typ
|
n.typ = n.sons[i].typ
|
||||||
if not isEmptyType(n.typ): n.kind = nkStmtListExpr
|
if not isEmptyType(n.typ): n.kind = nkStmtListExpr
|
||||||
elif i != last or voidContext:
|
elif i != last or voidContext:
|
||||||
n.sons[i] = discardCheck(c, n.sons[i], flags)
|
discardCheck(c, n.sons[i], flags)
|
||||||
else:
|
else:
|
||||||
n.typ = n.sons[i].typ
|
n.typ = n.sons[i].typ
|
||||||
if not isEmptyType(n.typ): n.kind = nkStmtListExpr
|
if not isEmptyType(n.typ): n.kind = nkStmtListExpr
|
||||||
|
|
|
||||||
|
|
@ -559,9 +559,11 @@ proc semTemplateDef(c: PContext, n: PNode): PNode =
|
||||||
else:
|
else:
|
||||||
s = semIdentVis(c, skTemplate, n.sons[0], {})
|
s = semIdentVis(c, skTemplate, n.sons[0], {})
|
||||||
|
|
||||||
if s.owner != nil and sfSystemModule in s.owner.flags and
|
if s.owner != nil:
|
||||||
s.name.s in ["!=", ">=", ">", "incl", "excl", "in", "notin", "isnot"]:
|
const names = ["!=", ">=", ">", "incl", "excl", "in", "notin", "isnot"]
|
||||||
incl(s.flags, sfCallsite)
|
if sfSystemModule in s.owner.flags and s.name.s in names or
|
||||||
|
s.owner.name.s == "vm" and s.name.s == "stackTrace":
|
||||||
|
incl(s.flags, sfCallsite)
|
||||||
|
|
||||||
styleCheckDef(c.config, s)
|
styleCheckDef(c.config, s)
|
||||||
onDef(n[0].info, s)
|
onDef(n[0].info, s)
|
||||||
|
|
|
||||||
|
|
@ -1348,8 +1348,8 @@ proc semGeneric(c: PContext, n: PNode, s: PSym, prev: PType): PType =
|
||||||
# special check for generic object with
|
# special check for generic object with
|
||||||
# generic/partial specialized parent
|
# generic/partial specialized parent
|
||||||
let tx = result.skipTypes(abstractPtrs, 50)
|
let tx = result.skipTypes(abstractPtrs, 50)
|
||||||
if tx.isNil:
|
if tx.isNil or isTupleRecursive(tx):
|
||||||
localError(c.config, n.info, "invalid recursion in type '$1'" % typeToString(result[0]))
|
localError(c.config, n.info, "illegal recursion in type '$1'" % typeToString(result[0]))
|
||||||
return errorType(c)
|
return errorType(c)
|
||||||
if tx != result and tx.kind == tyObject and tx.sons[0] != nil:
|
if tx != result and tx.kind == tyObject and tx.sons[0] != nil:
|
||||||
semObjectTypeForInheritedGenericInst(c, n, tx)
|
semObjectTypeForInheritedGenericInst(c, n, tx)
|
||||||
|
|
|
||||||
|
|
@ -380,13 +380,13 @@ proc handleGenericInvocation(cl: var TReplTypeVars, t: PType): PType =
|
||||||
if newbody.isGenericAlias: newbody = newbody.skipGenericAlias
|
if newbody.isGenericAlias: newbody = newbody.skipGenericAlias
|
||||||
rawAddSon(result, newbody)
|
rawAddSon(result, newbody)
|
||||||
checkPartialConstructedType(cl.c.config, cl.info, newbody)
|
checkPartialConstructedType(cl.c.config, cl.info, newbody)
|
||||||
let dc = newbody.deepCopy
|
let dc = newbody.attachedOps[attachedDeepCopy]
|
||||||
if not cl.allowMetaTypes:
|
if not cl.allowMetaTypes:
|
||||||
if dc != nil and sfFromGeneric notin newbody.deepCopy.flags:
|
if dc != nil and sfFromGeneric notin newbody.attachedOps[attachedDeepCopy].flags:
|
||||||
# 'deepCopy' needs to be instantiated for
|
# 'deepCopy' needs to be instantiated for
|
||||||
# generics *when the type is constructed*:
|
# generics *when the type is constructed*:
|
||||||
newbody.deepCopy = cl.c.instTypeBoundOp(cl.c, dc, result, cl.info,
|
newbody.attachedOps[attachedDeepCopy] = cl.c.instTypeBoundOp(cl.c, dc, result, cl.info,
|
||||||
attachedDeepCopy, 1)
|
attachedDeepCopy, 1)
|
||||||
if bodyIsNew and newbody.typeInst == nil:
|
if bodyIsNew and newbody.typeInst == nil:
|
||||||
#doassert newbody.typeInst == nil
|
#doassert newbody.typeInst == nil
|
||||||
newbody.typeInst = result
|
newbody.typeInst = result
|
||||||
|
|
@ -592,7 +592,8 @@ proc replaceTypeVarsTAux(cl: var TReplTypeVars, t: PType): PType =
|
||||||
skipIntLiteralParams(result)
|
skipIntLiteralParams(result)
|
||||||
|
|
||||||
of tySequence:
|
of tySequence:
|
||||||
if cl.isReturnType and cl.c.config.selectedGc == gcDestructors and result.destructor.isNil and
|
if cl.isReturnType and cl.c.config.selectedGc == gcDestructors and
|
||||||
|
result.attachedOps[attachedDestructor].isNil and
|
||||||
result[0].kind != tyEmpty and optNimV2 notin cl.c.config.globalOptions:
|
result[0].kind != tyEmpty and optNimV2 notin cl.c.config.globalOptions:
|
||||||
let s = cl.c.graph.sysTypes[tySequence]
|
let s = cl.c.graph.sysTypes[tySequence]
|
||||||
var old = copyType(s, s.owner, keepId=false)
|
var old = copyType(s, s.owner, keepId=false)
|
||||||
|
|
@ -601,9 +602,7 @@ proc replaceTypeVarsTAux(cl: var TReplTypeVars, t: PType): PType =
|
||||||
old.n = nil
|
old.n = nil
|
||||||
old.flags = {tfHasAsgn}
|
old.flags = {tfHasAsgn}
|
||||||
old.addSonSkipIntLit result[0]
|
old.addSonSkipIntLit result[0]
|
||||||
result.destructor = old.destructor
|
result.attachedOps = old.attachedOps
|
||||||
result.assignment = old.assignment
|
|
||||||
result.sink = old.sink
|
|
||||||
cl.c.typesWithOps.add((result, old))
|
cl.c.typesWithOps.add((result, old))
|
||||||
|
|
||||||
else: discard
|
else: discard
|
||||||
|
|
@ -619,19 +618,19 @@ proc replaceTypeVarsTAux(cl: var TReplTypeVars, t: PType): PType =
|
||||||
result.n = replaceObjBranches(cl, result.n)
|
result.n = replaceObjBranches(cl, result.n)
|
||||||
|
|
||||||
template typeBound(c, newty, oldty, field, info) =
|
template typeBound(c, newty, oldty, field, info) =
|
||||||
let opr = newty.field
|
let opr = newty.attachedOps[field]
|
||||||
if opr != nil and sfFromGeneric notin opr.flags:
|
if opr != nil and sfFromGeneric notin opr.flags:
|
||||||
# '=' needs to be instantiated for generics when the type is constructed:
|
# '=' needs to be instantiated for generics when the type is constructed:
|
||||||
#echo "DESTROY: instantiating ", astToStr(field), " for ", typeToString(oldty)
|
#echo "DESTROY: instantiating ", astToStr(field), " for ", typeToString(oldty)
|
||||||
newty.field = c.instTypeBoundOp(c, opr, oldty, info, attachedAsgn, 1)
|
newty.attachedOps[field] = c.instTypeBoundOp(c, opr, oldty, info, attachedAsgn, 1)
|
||||||
|
|
||||||
proc instAllTypeBoundOp*(c: PContext, info: TLineInfo) =
|
proc instAllTypeBoundOp*(c: PContext, info: TLineInfo) =
|
||||||
var i = 0
|
var i = 0
|
||||||
while i < c.typesWithOps.len:
|
while i < c.typesWithOps.len:
|
||||||
let (newty, oldty) = c.typesWithOps[i]
|
let (newty, oldty) = c.typesWithOps[i]
|
||||||
typeBound(c, newty, oldty, destructor, info)
|
typeBound(c, newty, oldty, attachedDestructor, info)
|
||||||
typeBound(c, newty, oldty, sink, info)
|
typeBound(c, newty, oldty, attachedSink, info)
|
||||||
typeBound(c, newty, oldty, assignment, info)
|
typeBound(c, newty, oldty, attachedAsgn, info)
|
||||||
inc i
|
inc i
|
||||||
setLen(c.typesWithOps, 0)
|
setLen(c.typesWithOps, 0)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -349,7 +349,7 @@ proc hashBodyTree(graph: ModuleGraph, c: var MD5Context, n: PNode) =
|
||||||
c &= n.strVal
|
c &= n.strVal
|
||||||
else:
|
else:
|
||||||
for i in 0..<n.len:
|
for i in 0..<n.len:
|
||||||
hashTree(c, n.sons[i])
|
hashBodyTree(graph, c, n.sons[i])
|
||||||
|
|
||||||
proc symBodyDigest*(graph: ModuleGraph, sym: PSym): SigHash =
|
proc symBodyDigest*(graph: ModuleGraph, sym: PSym): SigHash =
|
||||||
## compute unique digest of the proc/func/method symbols
|
## compute unique digest of the proc/func/method symbols
|
||||||
|
|
@ -369,7 +369,7 @@ proc symBodyDigest*(graph: ModuleGraph, sym: PSym): SigHash =
|
||||||
if sym.ast != nil:
|
if sym.ast != nil:
|
||||||
md5Init(c)
|
md5Init(c)
|
||||||
c.md5Update(cast[cstring](result.addr), sizeof(result))
|
c.md5Update(cast[cstring](result.addr), sizeof(result))
|
||||||
c.hashTree(sym.ast[bodyPos])
|
hashBodyTree(graph, c, sym.ast[bodyPos])
|
||||||
c.md5Final(result.Md5Digest)
|
c.md5Final(result.Md5Digest)
|
||||||
graph.symBodyHashes[sym.id] = result
|
graph.symBodyHashes[sym.id] = result
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2092,14 +2092,21 @@ proc paramTypesMatchAux(m: var TCandidate, f, a: PType,
|
||||||
result = localConvMatch(c, m, f, a, arg)
|
result = localConvMatch(c, m, f, a, arg)
|
||||||
else:
|
else:
|
||||||
r = typeRel(m, base(f), a)
|
r = typeRel(m, base(f), a)
|
||||||
if r >= isGeneric:
|
case r
|
||||||
|
of isGeneric:
|
||||||
inc(m.convMatches)
|
inc(m.convMatches)
|
||||||
result = copyTree(arg)
|
result = copyTree(arg)
|
||||||
if r == isGeneric:
|
result.typ = getInstantiatedType(c, arg, m, base(f))
|
||||||
result.typ = getInstantiatedType(c, arg, m, base(f))
|
|
||||||
m.baseTypeMatch = true
|
m.baseTypeMatch = true
|
||||||
# bug #4799, varargs accepting subtype relation object
|
of isFromIntLit:
|
||||||
elif r == isSubtype:
|
inc(m.intConvMatches, 256)
|
||||||
|
result = implicitConv(nkHiddenStdConv, f[0], arg, m, c)
|
||||||
|
m.baseTypeMatch = true
|
||||||
|
of isEqual:
|
||||||
|
inc(m.convMatches)
|
||||||
|
result = copyTree(arg)
|
||||||
|
m.baseTypeMatch = true
|
||||||
|
of isSubtype: # bug #4799, varargs accepting subtype relation object
|
||||||
inc(m.subtypeMatches)
|
inc(m.subtypeMatches)
|
||||||
if base(f).kind == tyTypeDesc:
|
if base(f).kind == tyTypeDesc:
|
||||||
result = arg
|
result = arg
|
||||||
|
|
|
||||||
|
|
@ -651,6 +651,8 @@ proc transformFor(c: PTransf, n: PNode): PTransNode =
|
||||||
var t = formal.typ
|
var t = formal.typ
|
||||||
if formal.ast != nil and formal.ast.typ.destructor != nil and t.destructor == nil:
|
if formal.ast != nil and formal.ast.typ.destructor != nil and t.destructor == nil:
|
||||||
t = formal.ast.typ # better use the type that actually has a destructor.
|
t = formal.ast.typ # better use the type that actually has a destructor.
|
||||||
|
elif t.destructor == nil and arg.typ.destructor != nil:
|
||||||
|
t = arg.typ
|
||||||
# generate a temporary and produce an assignment statement:
|
# generate a temporary and produce an assignment statement:
|
||||||
var temp = newTemp(c, t, formal.info)
|
var temp = newTemp(c, t, formal.info)
|
||||||
#temp.sym.flags.incl sfCursor
|
#temp.sym.flags.incl sfCursor
|
||||||
|
|
|
||||||
|
|
@ -101,7 +101,7 @@ proc isDeepConstExpr*(n: PNode): bool =
|
||||||
if not isDeepConstExpr(n.sons[i]): return false
|
if not isDeepConstExpr(n.sons[i]): return false
|
||||||
if n.typ.isNil: result = true
|
if n.typ.isNil: result = true
|
||||||
else:
|
else:
|
||||||
let t = n.typ.skipTypes({tyGenericInst, tyDistinct, tyAlias, tySink})
|
let t = n.typ.skipTypes({tyGenericInst, tyDistinct, tyAlias, tySink, tyOwned})
|
||||||
if t.kind in {tyRef, tyPtr}: return false
|
if t.kind in {tyRef, tyPtr}: return false
|
||||||
if t.kind != tyObject or not isCaseObj(t.n):
|
if t.kind != tyObject or not isCaseObj(t.n):
|
||||||
result = true
|
result = true
|
||||||
|
|
|
||||||
|
|
@ -931,6 +931,21 @@ proc ldNullOpcode(t: PType): TOpcode =
|
||||||
assert t != nil
|
assert t != nil
|
||||||
if fitsRegister(t): opcLdNullReg else: opcLdNull
|
if fitsRegister(t): opcLdNullReg else: opcLdNull
|
||||||
|
|
||||||
|
proc whichAsgnOpc(n: PNode): TOpcode =
|
||||||
|
case n.typ.skipTypes(abstractRange+{tyOwned}-{tyTypeDesc}).kind
|
||||||
|
of tyBool, tyChar, tyEnum, tyOrdinal, tyInt..tyInt64, tyUInt..tyUInt64:
|
||||||
|
opcAsgnInt
|
||||||
|
of tyString, tyCString:
|
||||||
|
opcAsgnStr
|
||||||
|
of tyFloat..tyFloat128:
|
||||||
|
opcAsgnFloat
|
||||||
|
of tyRef, tyNil, tyVar, tyLent, tyPtr:
|
||||||
|
opcAsgnRef
|
||||||
|
else:
|
||||||
|
opcAsgnComplex
|
||||||
|
|
||||||
|
proc whichAsgnOpc(n: PNode; opc: TOpcode): TOpcode = opc
|
||||||
|
|
||||||
proc genMagic(c: PCtx; n: PNode; dest: var TDest; m: TMagic) =
|
proc genMagic(c: PCtx; n: PNode; dest: var TDest; m: TMagic) =
|
||||||
case m
|
case m
|
||||||
of mAnd: c.genAndOr(n, opcFJmp, dest)
|
of mAnd: c.genAndOr(n, opcFJmp, dest)
|
||||||
|
|
@ -957,7 +972,7 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest; m: TMagic) =
|
||||||
c.genNarrow(n.sons[1], d)
|
c.genNarrow(n.sons[1], d)
|
||||||
c.genAsgnPatch(n.sons[1], d)
|
c.genAsgnPatch(n.sons[1], d)
|
||||||
c.freeTemp(d)
|
c.freeTemp(d)
|
||||||
of mOrd, mChr, mArrToSeq: c.gen(n.sons[1], dest)
|
of mOrd, mChr, mArrToSeq, mUnown: c.gen(n.sons[1], dest)
|
||||||
of mNew, mNewFinalize:
|
of mNew, mNewFinalize:
|
||||||
unused(c, n, dest)
|
unused(c, n, dest)
|
||||||
c.genNew(n)
|
c.genNew(n)
|
||||||
|
|
@ -1330,6 +1345,17 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest; m: TMagic) =
|
||||||
of mRunnableExamples:
|
of mRunnableExamples:
|
||||||
discard "just ignore any call to runnableExamples"
|
discard "just ignore any call to runnableExamples"
|
||||||
of mDestroy: discard "ignore calls to the default destructor"
|
of mDestroy: discard "ignore calls to the default destructor"
|
||||||
|
of mMove:
|
||||||
|
let arg = n[1]
|
||||||
|
let a = c.genx(arg)
|
||||||
|
assert dest >= 0
|
||||||
|
if dest < 0: dest = c.getTemp(arg.typ)
|
||||||
|
gABC(c, arg, whichAsgnOpc(arg), dest, a, 1)
|
||||||
|
# XXX use ldNullOpcode() here?
|
||||||
|
c.gABx(n, opcLdNull, a, c.genType(arg.typ))
|
||||||
|
c.gABx(n, opcNodeToReg, a, a)
|
||||||
|
c.genAsgnPatch(arg, a)
|
||||||
|
c.freeTemp(a)
|
||||||
else:
|
else:
|
||||||
# mGCref, mGCunref,
|
# mGCref, mGCunref,
|
||||||
globalError(c.config, n.info, "cannot generate code for: " & $m)
|
globalError(c.config, n.info, "cannot generate code for: " & $m)
|
||||||
|
|
@ -1423,21 +1449,6 @@ proc genDeref(c: PCtx, n: PNode, dest: var TDest, flags: TGenFlags) =
|
||||||
if {gfNodeAddr, gfNode} * flags == {} and fitsRegister(n.typ):
|
if {gfNodeAddr, gfNode} * flags == {} and fitsRegister(n.typ):
|
||||||
c.gABC(n, opcNodeToReg, dest, dest)
|
c.gABC(n, opcNodeToReg, dest, dest)
|
||||||
|
|
||||||
proc whichAsgnOpc(n: PNode): TOpcode =
|
|
||||||
case n.typ.skipTypes(abstractRange+{tyOwned}-{tyTypeDesc}).kind
|
|
||||||
of tyBool, tyChar, tyEnum, tyOrdinal, tyInt..tyInt64, tyUInt..tyUInt64:
|
|
||||||
opcAsgnInt
|
|
||||||
of tyString, tyCString:
|
|
||||||
opcAsgnStr
|
|
||||||
of tyFloat..tyFloat128:
|
|
||||||
opcAsgnFloat
|
|
||||||
of tyRef, tyNil, tyVar, tyLent, tyPtr:
|
|
||||||
opcAsgnRef
|
|
||||||
else:
|
|
||||||
opcAsgnComplex
|
|
||||||
|
|
||||||
proc whichAsgnOpc(n: PNode; opc: TOpcode): TOpcode = opc
|
|
||||||
|
|
||||||
proc genAsgn(c: PCtx; dest: TDest; ri: PNode; requiresCopy: bool) =
|
proc genAsgn(c: PCtx; dest: TDest; ri: PNode; requiresCopy: bool) =
|
||||||
let tmp = c.genx(ri)
|
let tmp = c.genx(ri)
|
||||||
assert dest >= 0
|
assert dest >= 0
|
||||||
|
|
@ -1585,8 +1596,8 @@ proc genGlobalInit(c: PCtx; n: PNode; s: PSym) =
|
||||||
# var decls{.compileTime.}: seq[NimNode] = @[]
|
# var decls{.compileTime.}: seq[NimNode] = @[]
|
||||||
let dest = c.getTemp(s.typ)
|
let dest = c.getTemp(s.typ)
|
||||||
c.gABx(n, opcLdGlobal, dest, s.position)
|
c.gABx(n, opcLdGlobal, dest, s.position)
|
||||||
if s.ast != nil:
|
if s.astdef != nil:
|
||||||
let tmp = c.genx(s.ast)
|
let tmp = c.genx(s.astdef)
|
||||||
c.genAdditionalCopy(n, opcWrDeref, dest, 0, tmp)
|
c.genAdditionalCopy(n, opcWrDeref, dest, 0, tmp)
|
||||||
c.freeTemp(dest)
|
c.freeTemp(dest)
|
||||||
c.freeTemp(tmp)
|
c.freeTemp(tmp)
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,21 @@ from input .nim files and projects, as well as HTML and LaTeX from input RST
|
||||||
dependencies (``import``), any top-level documentation comments (##), and
|
dependencies (``import``), any top-level documentation comments (##), and
|
||||||
exported symbols (*), including procedures, types, and variables.
|
exported symbols (*), including procedures, types, and variables.
|
||||||
|
|
||||||
|
Quick start
|
||||||
|
-----------
|
||||||
|
|
||||||
|
Generate HTML documentation for a file:
|
||||||
|
|
||||||
|
::
|
||||||
|
nim doc <filename>.nim
|
||||||
|
|
||||||
|
Generate HTML documentation for a whole project:
|
||||||
|
|
||||||
|
::
|
||||||
|
# delete any htmldocs/*.idx file before starting
|
||||||
|
nim doc --project --index:on --git.url:<url> --git.commit:<tag> <main_filename>.nim
|
||||||
|
nim buildIndex -o:htmldocs/theindex.html htmldocs
|
||||||
|
|
||||||
|
|
||||||
Documentation Comments
|
Documentation Comments
|
||||||
----------------------
|
----------------------
|
||||||
|
|
@ -186,21 +201,25 @@ file.
|
||||||
See source switch
|
See source switch
|
||||||
-----------------
|
-----------------
|
||||||
|
|
||||||
|
The ``docSeeSrcUrl`` switch is deprecated. Use:
|
||||||
|
|
||||||
::
|
::
|
||||||
nim doc2 --docSeeSrcUrl:txt filename.nim
|
nim doc2 --git.url:<url> filename.nim
|
||||||
|
|
||||||
When you pass the ``docSeeSrcUrl`` switch to docgen, after each documented item
|
With the ``git.url`` switch the *See source* hyperlink will appear below each
|
||||||
in your source code the hyper link *See source* will appear pointing to the
|
documented item in your source code pointing to the implementation of that
|
||||||
implementation of that item on a GitHub repository. You can click the link to
|
item on a GitHub repository.
|
||||||
see the implementation of the item.
|
You can click the link to see the implementation of the item.
|
||||||
|
|
||||||
If you want to reuse this feature in your own documentation you will have to
|
The ``git.commit`` switch overrides the hardcoded `devel` branch in config/nimdoc.cfg.
|
||||||
modify ``config/nimdoc.cfg`` to contain a ``doc.item.seesrc`` value with a
|
This is useful to link to a different branch e.g. `--git.commit:master`,
|
||||||
hyper link to your own code repository. As you will see by the comments in that
|
or to a tag e.g. `--git.commit:1.2.3` or a commit.
|
||||||
file, the value ``txt`` passed on the command line will be used in the HTML
|
|
||||||
template along others like ``$path`` and ``$line``.
|
|
||||||
|
|
||||||
In the case of Nim's own documentation, the ``txt`` value is just a commit
|
Source URLs are generated as `href="${url}/tree/${commit}/${path}#L${line}"` by default and this compatible with GitHub but not with GitLab.
|
||||||
|
|
||||||
|
You can edit ``config/nimdoc.cfg`` and modify the ``doc.item.seesrc`` value with a hyperlink to your own code repository.
|
||||||
|
|
||||||
|
In the case of Nim's own documentation, the ``commit`` value is just a commit
|
||||||
hash to append to a formatted URL to https://github.com/Araq/Nim. The
|
hash to append to a formatted URL to https://github.com/Araq/Nim. The
|
||||||
``tools/nimweb.nim`` helper queries the current git commit hash during doc
|
``tools/nimweb.nim`` helper queries the current git commit hash during doc
|
||||||
generation, but since you might be working on an unpublished repository, it
|
generation, but since you might be working on an unpublished repository, it
|
||||||
|
|
@ -231,7 +250,7 @@ HTML anchor generation
|
||||||
======================
|
======================
|
||||||
|
|
||||||
When you run the ``rst2html`` command, all sections in the RST document will
|
When you run the ``rst2html`` command, all sections in the RST document will
|
||||||
get an anchor you can hyper link to. Usually you can guess the anchor lower
|
get an anchor you can hyperlink to. Usually you can guess the anchor lower
|
||||||
casing the section title and replacing spaces with dashes, and in any case you
|
casing the section title and replacing spaces with dashes, and in any case you
|
||||||
can get it from the table of contents. But when you run the ``doc`` or ``doc2``
|
can get it from the table of contents. But when you run the ``doc`` or ``doc2``
|
||||||
commands to generate API documentation, some symbol get one or two anchors at
|
commands to generate API documentation, some symbol get one or two anchors at
|
||||||
|
|
@ -249,7 +268,7 @@ will be their unquoted value after removing parameters, return types and
|
||||||
pragmas. The plain name allows short and nice linking of symbols which works
|
pragmas. The plain name allows short and nice linking of symbols which works
|
||||||
unless you have a module with collisions due to overloading.
|
unless you have a module with collisions due to overloading.
|
||||||
|
|
||||||
If you hyper link a plain name symbol and there are other matches on the same
|
If you hyperlink a plain name symbol and there are other matches on the same
|
||||||
HTML file, most browsers will go to the first one. To differentiate the rest,
|
HTML file, most browsers will go to the first one. To differentiate the rest,
|
||||||
you will need to use the complex name. A complex name for a callable type is
|
you will need to use the complex name. A complex name for a callable type is
|
||||||
made up from several parts:
|
made up from several parts:
|
||||||
|
|
@ -313,14 +332,14 @@ columns is:
|
||||||
|
|
||||||
1. Mandatory term being indexed. Terms can include quoting according to
|
1. Mandatory term being indexed. Terms can include quoting according to
|
||||||
Nim's rules (eg. \`^\`).
|
Nim's rules (eg. \`^\`).
|
||||||
2. Base filename plus anchor hyper link (eg.
|
2. Base filename plus anchor hyperlink (eg.
|
||||||
``algorithm.html#*,int,SortOrder``).
|
``algorithm.html#*,int,SortOrder``).
|
||||||
3. Optional human readable string to display as hyper link. If the value is not
|
3. Optional human readable string to display as hyperlink. If the value is not
|
||||||
present or is the empty string, the hyper link will be rendered
|
present or is the empty string, the hyperlink will be rendered
|
||||||
using the term. Prefix whitespace indicates that this entry is
|
using the term. Prefix whitespace indicates that this entry is
|
||||||
not for an API symbol but for a TOC entry.
|
not for an API symbol but for a TOC entry.
|
||||||
4. Optional title or description of the hyper link. Browsers usually display
|
4. Optional title or description of the hyperlink. Browsers usually display
|
||||||
this as a tooltip after hovering a moment over the hyper link.
|
this as a tooltip after hovering a moment over the hyperlink.
|
||||||
|
|
||||||
The index generation tools try to differentiate between documentation
|
The index generation tools try to differentiate between documentation
|
||||||
generated from ``.nim`` files and documentation generated from ``.txt`` or
|
generated from ``.nim`` files and documentation generated from ``.txt`` or
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,10 @@ precise wording. This manual is constantly evolving into a proper specification.
|
||||||
**Note**: The experimental features of Nim are
|
**Note**: The experimental features of Nim are
|
||||||
covered `here <manual_experimental.html>`_.
|
covered `here <manual_experimental.html>`_.
|
||||||
|
|
||||||
This document describes the lexis, the syntax, and the semantics of Nim.
|
This document describes the lexis, the syntax, and the semantics of the Nim language.
|
||||||
|
|
||||||
|
To learn how to compile Nim programs and generate documentation see
|
||||||
|
`Compiler User Guide <nimc.html>`_ and `DocGen Tools Guide <docgen.html>`_.
|
||||||
|
|
||||||
The language constructs are explained using an extended BNF, in which ``(a)*``
|
The language constructs are explained using an extended BNF, in which ``(a)*``
|
||||||
means 0 or more ``a``'s, ``a+`` means 1 or more ``a``'s, and ``(a)?`` means an
|
means 0 or more ``a``'s, ``a+`` means 1 or more ``a``'s, and ``(a)?`` means an
|
||||||
|
|
@ -2357,7 +2360,7 @@ argument's resolution:
|
||||||
rem unresolvedExpression(undeclaredIdentifier)
|
rem unresolvedExpression(undeclaredIdentifier)
|
||||||
|
|
||||||
``untyped`` and ``varargs[untyped]`` are the only metatype that are lazy in this sense, the other
|
``untyped`` and ``varargs[untyped]`` are the only metatype that are lazy in this sense, the other
|
||||||
metatypes ``typed`` and ``type`` are not lazy.
|
metatypes ``typed`` and ``typedesc`` are not lazy.
|
||||||
|
|
||||||
|
|
||||||
Varargs matching
|
Varargs matching
|
||||||
|
|
@ -4434,7 +4437,7 @@ templates:
|
||||||
| ``notin`` and ``isnot`` have the obvious meanings.
|
| ``notin`` and ``isnot`` have the obvious meanings.
|
||||||
|
|
||||||
The "types" of templates can be the symbols ``untyped``,
|
The "types" of templates can be the symbols ``untyped``,
|
||||||
``typed`` or ``type``. These are "meta types", they can only be used in certain
|
``typed`` or ``typedesc``. These are "meta types", they can only be used in certain
|
||||||
contexts. Regular types can be used too; this implies that ``typed`` expressions
|
contexts. Regular types can be used too; this implies that ``typed`` expressions
|
||||||
are expected.
|
are expected.
|
||||||
|
|
||||||
|
|
@ -4598,7 +4601,7 @@ In templates identifiers can be constructed with the backticks notation:
|
||||||
.. code-block:: nim
|
.. code-block:: nim
|
||||||
:test: "nim c $1"
|
:test: "nim c $1"
|
||||||
|
|
||||||
template typedef(name: untyped, typ: type) =
|
template typedef(name: untyped, typ: typedesc) =
|
||||||
type
|
type
|
||||||
`T name`* {.inject.} = typ
|
`T name`* {.inject.} = typ
|
||||||
`P name`* {.inject.} = ref `T name`
|
`P name`* {.inject.} = ref `T name`
|
||||||
|
|
@ -4660,7 +4663,7 @@ template cannot be accessed in the instantiation context:
|
||||||
.. code-block:: nim
|
.. code-block:: nim
|
||||||
:test: "nim c $1"
|
:test: "nim c $1"
|
||||||
|
|
||||||
template newException*(exceptn: type, message: string): untyped =
|
template newException*(exceptn: typedesc, message: string): untyped =
|
||||||
var
|
var
|
||||||
e: ref exceptn # e is implicitly gensym'ed here
|
e: ref exceptn # e is implicitly gensym'ed here
|
||||||
new(e)
|
new(e)
|
||||||
|
|
@ -5095,27 +5098,26 @@ expression by coercing it to a corresponding ``static`` type:
|
||||||
The complier will report any failure to evaluate the expression or a
|
The complier will report any failure to evaluate the expression or a
|
||||||
possible type mismatch error.
|
possible type mismatch error.
|
||||||
|
|
||||||
type[T]
|
typedesc[T]
|
||||||
-------
|
-----------
|
||||||
|
|
||||||
In many contexts, Nim allows you to treat the names of types as regular
|
In many contexts, Nim allows you to treat the names of types as regular
|
||||||
values. These values exists only during the compilation phase, but since
|
values. These values exists only during the compilation phase, but since
|
||||||
all values must have a type, ``type`` is considered their special type.
|
all values must have a type, ``typedesc`` is considered their special type.
|
||||||
|
|
||||||
``type`` acts like a generic type. For instance, the type of the symbol
|
``typedesc`` acts like a generic type. For instance, the type of the symbol
|
||||||
``int`` is ``type[int]``. Just like with regular generic types, when the
|
``int`` is ``typedesc[int]``. Just like with regular generic types, when the
|
||||||
generic param is ommited, ``type`` denotes the type class of all types.
|
generic param is ommited, ``typedesc`` denotes the type class of all types.
|
||||||
As a syntactic convenience, you can also use ``type`` as a modifier.
|
As a syntactic convenience, you can also use ``typedesc`` as a modifier.
|
||||||
``type int`` is considered the same as ``type[int]``.
|
|
||||||
|
|
||||||
Procs featuring ``type`` params are considered implicitly generic.
|
Procs featuring ``typedesc`` params are considered implicitly generic.
|
||||||
They will be instantiated for each unique combination of supplied types
|
They will be instantiated for each unique combination of supplied types
|
||||||
and within the body of the proc, the name of each param will refer to
|
and within the body of the proc, the name of each param will refer to
|
||||||
the bound concrete type:
|
the bound concrete type:
|
||||||
|
|
||||||
.. code-block:: nim
|
.. code-block:: nim
|
||||||
|
|
||||||
proc new(T: type): ref T =
|
proc new(T: typedesc): ref T =
|
||||||
echo "allocating ", T.name
|
echo "allocating ", T.name
|
||||||
new(result)
|
new(result)
|
||||||
|
|
||||||
|
|
@ -5126,14 +5128,14 @@ When multiple type params are present, they will bind freely to different
|
||||||
types. To force a bind-once behavior one can use an explicit generic param:
|
types. To force a bind-once behavior one can use an explicit generic param:
|
||||||
|
|
||||||
.. code-block:: nim
|
.. code-block:: nim
|
||||||
proc acceptOnlyTypePairs[T, U](A, B: type[T]; C, D: type[U])
|
proc acceptOnlyTypePairs[T, U](A, B: typedesc[T]; C, D: typedesc[U])
|
||||||
|
|
||||||
Once bound, type params can appear in the rest of the proc signature:
|
Once bound, type params can appear in the rest of the proc signature:
|
||||||
|
|
||||||
.. code-block:: nim
|
.. code-block:: nim
|
||||||
:test: "nim c $1"
|
:test: "nim c $1"
|
||||||
|
|
||||||
template declareVariableWithType(T: type, value: T) =
|
template declareVariableWithType(T: typedesc, value: T) =
|
||||||
var x: T = value
|
var x: T = value
|
||||||
|
|
||||||
declareVariableWithType int, 42
|
declareVariableWithType int, 42
|
||||||
|
|
@ -5145,8 +5147,8 @@ types that will match the type param:
|
||||||
.. code-block:: nim
|
.. code-block:: nim
|
||||||
:test: "nim c $1"
|
:test: "nim c $1"
|
||||||
|
|
||||||
template maxval(T: type int): int = high(int)
|
template maxval(T: typedesc[int]): int = high(int)
|
||||||
template maxval(T: type float): float = Inf
|
template maxval(T: typedesc[float]): float = Inf
|
||||||
|
|
||||||
var i = int.maxval
|
var i = int.maxval
|
||||||
var f = float.maxval
|
var f = float.maxval
|
||||||
|
|
@ -5159,7 +5161,8 @@ The constraint can be a concrete type or a type class.
|
||||||
typeof operator
|
typeof operator
|
||||||
---------------
|
---------------
|
||||||
|
|
||||||
**Note**: ``typeof(x)`` can also be written as ``type(x)``.
|
**Note**: ``typeof(x)`` can also be written as ``type(x)`` but ``type(x)``
|
||||||
|
is discouraged.
|
||||||
|
|
||||||
You can obtain the type of a given expression by constructing a ``typeof``
|
You can obtain the type of a given expression by constructing a ``typeof``
|
||||||
value from it (in many other languages this is known as the `typeof`:idx:
|
value from it (in many other languages this is known as the `typeof`:idx:
|
||||||
|
|
@ -6581,7 +6584,7 @@ Custom pragmas are defined using templates annotated with pragma ``pragma``:
|
||||||
.. code-block:: nim
|
.. code-block:: nim
|
||||||
template dbTable(name: string, table_space: string = "") {.pragma.}
|
template dbTable(name: string, table_space: string = "") {.pragma.}
|
||||||
template dbKey(name: string = "", primary_key: bool = false) {.pragma.}
|
template dbKey(name: string = "", primary_key: bool = false) {.pragma.}
|
||||||
template dbForeignKey(t: type) {.pragma.}
|
template dbForeignKey(t: typedesc) {.pragma.}
|
||||||
template dbIgnore {.pragma.}
|
template dbIgnore {.pragma.}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -486,9 +486,9 @@ The concept types can be parametric just like the regular generic types:
|
||||||
M.data[m * M.N + n] = v
|
M.data[m * M.N + n] = v
|
||||||
|
|
||||||
# Adapt the Matrix type to the concept's requirements
|
# Adapt the Matrix type to the concept's requirements
|
||||||
template Rows*(M: type Matrix): int = M.M
|
template Rows*(M: typedesc[Matrix]): int = M.M
|
||||||
template Cols*(M: type Matrix): int = M.N
|
template Cols*(M: typedesc[Matrix]): int = M.N
|
||||||
template ValueType*(M: type Matrix): type = M.T
|
template ValueType*(M: typedesc[Matrix]): typedesc = M.T
|
||||||
|
|
||||||
-------------
|
-------------
|
||||||
### usage.nim
|
### usage.nim
|
||||||
|
|
|
||||||
|
|
@ -1076,20 +1076,24 @@ proc expectKind*(n: NimNode; k: set[NimNodeKind]) {.compileTime.} =
|
||||||
## macros that check the AST that is passed to them.
|
## macros that check the AST that is passed to them.
|
||||||
if n.kind notin k: error("Expected one of " & $k & ", got " & $n.kind, n)
|
if n.kind notin k: error("Expected one of " & $k & ", got " & $n.kind, n)
|
||||||
|
|
||||||
proc newProc*(name = newEmptyNode(); params: openArray[NimNode] = [newEmptyNode()];
|
proc newProc*(name = newEmptyNode();
|
||||||
body: NimNode = newStmtList(), procType = nnkProcDef): NimNode {.compileTime.} =
|
params: openArray[NimNode] = [newEmptyNode()];
|
||||||
|
body: NimNode = newStmtList();
|
||||||
|
procType = nnkProcDef;
|
||||||
|
pragmas: NimNode = newEmptyNode()): NimNode {.compileTime.} =
|
||||||
## shortcut for creating a new proc
|
## shortcut for creating a new proc
|
||||||
##
|
##
|
||||||
## The ``params`` array must start with the return type of the proc,
|
## The ``params`` array must start with the return type of the proc,
|
||||||
## followed by a list of IdentDefs which specify the params.
|
## followed by a list of IdentDefs which specify the params.
|
||||||
if procType notin RoutineNodes:
|
if procType notin RoutineNodes:
|
||||||
error("Expected one of " & $RoutineNodes & ", got " & $procType)
|
error("Expected one of " & $RoutineNodes & ", got " & $procType)
|
||||||
|
pragmas.expectKind({nnkEmpty, nnkPragma})
|
||||||
result = newNimNode(procType).add(
|
result = newNimNode(procType).add(
|
||||||
name,
|
name,
|
||||||
newEmptyNode(),
|
newEmptyNode(),
|
||||||
newEmptyNode(),
|
newEmptyNode(),
|
||||||
newNimNode(nnkFormalParams).add(params),
|
newNimNode(nnkFormalParams).add(params),
|
||||||
newEmptyNode(), # pragmas
|
pragmas,
|
||||||
newEmptyNode(),
|
newEmptyNode(),
|
||||||
body)
|
body)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -47,8 +47,9 @@ proc nimNewObj(size: int): pointer {.compilerRtl.} =
|
||||||
when defined(nimscript):
|
when defined(nimscript):
|
||||||
discard
|
discard
|
||||||
elif defined(useMalloc):
|
elif defined(useMalloc):
|
||||||
result = c_malloc(s) +! sizeof(RefHeader)
|
var orig = c_malloc(s)
|
||||||
nimZeroMem(result, s)
|
nimZeroMem(orig, s)
|
||||||
|
result = orig +! sizeof(RefHeader)
|
||||||
else:
|
else:
|
||||||
result = alloc0(s) +! sizeof(RefHeader)
|
result = alloc0(s) +! sizeof(RefHeader)
|
||||||
inc allocs
|
inc allocs
|
||||||
|
|
|
||||||
|
|
@ -104,6 +104,10 @@ proc newSeqPayload(cap, elemSize: int): pointer {.compilerRtl, raises: [].} =
|
||||||
proc prepareSeqAdd(len: int; p: pointer; addlen, elemSize: int): pointer {.
|
proc prepareSeqAdd(len: int; p: pointer; addlen, elemSize: int): pointer {.
|
||||||
compilerRtl, noSideEffect, raises: [].} =
|
compilerRtl, noSideEffect, raises: [].} =
|
||||||
{.noSideEffect.}:
|
{.noSideEffect.}:
|
||||||
|
template `+!`(p: pointer, s: int): pointer =
|
||||||
|
cast[pointer](cast[int](p) +% s)
|
||||||
|
|
||||||
|
const headerSize = sizeof(int) + sizeof(Allocator)
|
||||||
if addlen <= 0:
|
if addlen <= 0:
|
||||||
result = p
|
result = p
|
||||||
elif p == nil:
|
elif p == nil:
|
||||||
|
|
@ -112,14 +116,23 @@ proc prepareSeqAdd(len: int; p: pointer; addlen, elemSize: int): pointer {.
|
||||||
# Note: this means we cannot support things that have internal pointers as
|
# Note: this means we cannot support things that have internal pointers as
|
||||||
# they get reallocated here. This needs to be documented clearly.
|
# they get reallocated here. This needs to be documented clearly.
|
||||||
var p = cast[ptr PayloadBase](p)
|
var p = cast[ptr PayloadBase](p)
|
||||||
let allocator = if p.allocator == nil: getLocalAllocator() else: p.allocator
|
|
||||||
let cap = max(resize(p.cap), len+addlen)
|
let cap = max(resize(p.cap), len+addlen)
|
||||||
var q = cast[ptr PayloadBase](allocator.realloc(allocator, p,
|
if p.allocator == nil:
|
||||||
sizeof(int) + sizeof(Allocator) + elemSize * p.cap,
|
let allocator = getLocalAllocator()
|
||||||
sizeof(int) + sizeof(Allocator) + elemSize * cap))
|
var q = cast[ptr PayloadBase](allocator.alloc(allocator,
|
||||||
q.allocator = allocator
|
headerSize + elemSize * cap))
|
||||||
q.cap = cap
|
copyMem(q +! headerSize, p +! headerSize, len * elemSize)
|
||||||
result = q
|
q.allocator = allocator
|
||||||
|
q.cap = cap
|
||||||
|
result = q
|
||||||
|
else:
|
||||||
|
let allocator = p.allocator
|
||||||
|
var q = cast[ptr PayloadBase](allocator.realloc(allocator, p,
|
||||||
|
headerSize + elemSize * p.cap,
|
||||||
|
headerSize + elemSize * cap))
|
||||||
|
q.allocator = allocator
|
||||||
|
q.cap = cap
|
||||||
|
result = q
|
||||||
|
|
||||||
proc shrink*[T](x: var seq[T]; newLen: Natural) =
|
proc shrink*[T](x: var seq[T]; newLen: Natural) =
|
||||||
mixin `=destroy`
|
mixin `=destroy`
|
||||||
|
|
@ -140,6 +153,20 @@ proc grow*[T](x: var seq[T]; newLen: Natural; value: T) =
|
||||||
for i in oldLen .. newLen-1:
|
for i in oldLen .. newLen-1:
|
||||||
xu.p.data[i] = value
|
xu.p.data[i] = value
|
||||||
|
|
||||||
|
proc add*[T](x: var seq[T]; value: sink T) {.magic: "AppendSeqElem", noSideEffect.} =
|
||||||
|
## Generic proc for adding a data item `y` to a container `x`.
|
||||||
|
##
|
||||||
|
## For containers that have an order, `add` means *append*. New generic
|
||||||
|
## containers should also call their adding proc `add` for consistency.
|
||||||
|
## Generic code becomes much easier to write if the Nim naming scheme is
|
||||||
|
## respected.
|
||||||
|
let oldLen = x.len
|
||||||
|
var xu = cast[ptr NimSeqV2[T]](addr x)
|
||||||
|
if xu.p == nil or xu.p.cap < oldLen+1:
|
||||||
|
xu.p = cast[typeof(xu.p)](prepareSeqAdd(oldLen, xu.p, 1, sizeof(T)))
|
||||||
|
xu.len = oldLen+1
|
||||||
|
xu.p.data[oldLen] = value
|
||||||
|
|
||||||
proc setLen[T](s: var seq[T], newlen: Natural) =
|
proc setLen[T](s: var seq[T], newlen: Natural) =
|
||||||
{.noSideEffect.}:
|
{.noSideEffect.}:
|
||||||
if newlen < s.len:
|
if newlen < s.len:
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,7 @@
|
||||||
## `db_postgres <db_postgres.html>`_.
|
## `db_postgres <db_postgres.html>`_.
|
||||||
##
|
##
|
||||||
## Parameter substitution
|
## Parameter substitution
|
||||||
## ----------------------
|
## ======================
|
||||||
##
|
##
|
||||||
## All ``db_*`` modules support the same form of parameter substitution.
|
## All ``db_*`` modules support the same form of parameter substitution.
|
||||||
## That is, using the ``?`` (question mark) to signify the place where a
|
## That is, using the ``?`` (question mark) to signify the place where a
|
||||||
|
|
@ -25,10 +25,10 @@
|
||||||
##
|
##
|
||||||
##
|
##
|
||||||
## Examples
|
## Examples
|
||||||
## --------
|
## ========
|
||||||
##
|
##
|
||||||
## Opening a connection to a database
|
## Opening a connection to a database
|
||||||
## ==================================
|
## ----------------------------------
|
||||||
##
|
##
|
||||||
## .. code-block:: Nim
|
## .. code-block:: Nim
|
||||||
## import db_mysql
|
## import db_mysql
|
||||||
|
|
@ -36,7 +36,7 @@
|
||||||
## db.close()
|
## db.close()
|
||||||
##
|
##
|
||||||
## Creating a table
|
## Creating a table
|
||||||
## ================
|
## ----------------
|
||||||
##
|
##
|
||||||
## .. code-block:: Nim
|
## .. code-block:: Nim
|
||||||
## db.exec(sql"DROP TABLE IF EXISTS myTable")
|
## db.exec(sql"DROP TABLE IF EXISTS myTable")
|
||||||
|
|
@ -45,14 +45,14 @@
|
||||||
## name varchar(50) not null)"""))
|
## name varchar(50) not null)"""))
|
||||||
##
|
##
|
||||||
## Inserting data
|
## Inserting data
|
||||||
## ==============
|
## --------------
|
||||||
##
|
##
|
||||||
## .. code-block:: Nim
|
## .. code-block:: Nim
|
||||||
## db.exec(sql"INSERT INTO myTable (id, name) VALUES (0, ?)",
|
## db.exec(sql"INSERT INTO myTable (id, name) VALUES (0, ?)",
|
||||||
## "Dominik")
|
## "Dominik")
|
||||||
##
|
##
|
||||||
## Larger example
|
## Larger example
|
||||||
## ==============
|
## --------------
|
||||||
##
|
##
|
||||||
## .. code-block:: Nim
|
## .. code-block:: Nim
|
||||||
##
|
##
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,7 @@
|
||||||
## `db_mysql <db_mysql.html>`_.
|
## `db_mysql <db_mysql.html>`_.
|
||||||
##
|
##
|
||||||
## Parameter substitution
|
## Parameter substitution
|
||||||
## ----------------------
|
## ======================
|
||||||
##
|
##
|
||||||
## All ``db_*`` modules support the same form of parameter substitution.
|
## All ``db_*`` modules support the same form of parameter substitution.
|
||||||
## That is, using the ``?`` (question mark) to signify the place where a
|
## That is, using the ``?`` (question mark) to signify the place where a
|
||||||
|
|
@ -31,10 +31,10 @@
|
||||||
##
|
##
|
||||||
##
|
##
|
||||||
## Examples
|
## Examples
|
||||||
## --------
|
## ========
|
||||||
##
|
##
|
||||||
## Opening a connection to a database
|
## Opening a connection to a database
|
||||||
## ==================================
|
## ----------------------------------
|
||||||
##
|
##
|
||||||
## .. code-block:: Nim
|
## .. code-block:: Nim
|
||||||
## import db_odbc
|
## import db_odbc
|
||||||
|
|
@ -42,7 +42,7 @@
|
||||||
## db.close()
|
## db.close()
|
||||||
##
|
##
|
||||||
## Creating a table
|
## Creating a table
|
||||||
## ================
|
## ----------------
|
||||||
##
|
##
|
||||||
## .. code-block:: Nim
|
## .. code-block:: Nim
|
||||||
## db.exec(sql"DROP TABLE IF EXISTS myTable")
|
## db.exec(sql"DROP TABLE IF EXISTS myTable")
|
||||||
|
|
@ -51,14 +51,14 @@
|
||||||
## name varchar(50) not null)"""))
|
## name varchar(50) not null)"""))
|
||||||
##
|
##
|
||||||
## Inserting data
|
## Inserting data
|
||||||
## ==============
|
## --------------
|
||||||
##
|
##
|
||||||
## .. code-block:: Nim
|
## .. code-block:: Nim
|
||||||
## db.exec(sql"INSERT INTO myTable (id, name) VALUES (0, ?)",
|
## db.exec(sql"INSERT INTO myTable (id, name) VALUES (0, ?)",
|
||||||
## "Andreas")
|
## "Andreas")
|
||||||
##
|
##
|
||||||
## Large example
|
## Large example
|
||||||
## =============
|
## -------------
|
||||||
##
|
##
|
||||||
## .. code-block:: Nim
|
## .. code-block:: Nim
|
||||||
##
|
##
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,7 @@
|
||||||
## `db_mysql <db_mysql.html>`_.
|
## `db_mysql <db_mysql.html>`_.
|
||||||
##
|
##
|
||||||
## Parameter substitution
|
## Parameter substitution
|
||||||
## ----------------------
|
## ======================
|
||||||
##
|
##
|
||||||
## All ``db_*`` modules support the same form of parameter substitution.
|
## All ``db_*`` modules support the same form of parameter substitution.
|
||||||
## That is, using the ``?`` (question mark) to signify the place where a
|
## That is, using the ``?`` (question mark) to signify the place where a
|
||||||
|
|
@ -38,10 +38,10 @@
|
||||||
## 3)
|
## 3)
|
||||||
##
|
##
|
||||||
## Examples
|
## Examples
|
||||||
## --------
|
## ========
|
||||||
##
|
##
|
||||||
## Opening a connection to a database
|
## Opening a connection to a database
|
||||||
## ==================================
|
## ----------------------------------
|
||||||
##
|
##
|
||||||
## .. code-block:: Nim
|
## .. code-block:: Nim
|
||||||
## import db_postgres
|
## import db_postgres
|
||||||
|
|
@ -49,7 +49,7 @@
|
||||||
## db.close()
|
## db.close()
|
||||||
##
|
##
|
||||||
## Creating a table
|
## Creating a table
|
||||||
## ================
|
## ----------------
|
||||||
##
|
##
|
||||||
## .. code-block:: Nim
|
## .. code-block:: Nim
|
||||||
## db.exec(sql"DROP TABLE IF EXISTS myTable")
|
## db.exec(sql"DROP TABLE IF EXISTS myTable")
|
||||||
|
|
@ -58,7 +58,7 @@
|
||||||
## name varchar(50) not null)"""))
|
## name varchar(50) not null)"""))
|
||||||
##
|
##
|
||||||
## Inserting data
|
## Inserting data
|
||||||
## ==============
|
## --------------
|
||||||
##
|
##
|
||||||
## .. code-block:: Nim
|
## .. code-block:: Nim
|
||||||
## db.exec(sql"INSERT INTO myTable (id, name) VALUES (0, ?)",
|
## db.exec(sql"INSERT INTO myTable (id, name) VALUES (0, ?)",
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,7 @@
|
||||||
## `db_mysql <db_mysql.html>`_.
|
## `db_mysql <db_mysql.html>`_.
|
||||||
##
|
##
|
||||||
## Parameter substitution
|
## Parameter substitution
|
||||||
## ----------------------
|
## ======================
|
||||||
##
|
##
|
||||||
## All ``db_*`` modules support the same form of parameter substitution.
|
## All ``db_*`` modules support the same form of parameter substitution.
|
||||||
## That is, using the ``?`` (question mark) to signify the place where a
|
## That is, using the ``?`` (question mark) to signify the place where a
|
||||||
|
|
@ -24,10 +24,10 @@
|
||||||
## sql"INSERT INTO myTable (colA, colB, colC) VALUES (?, ?, ?)"
|
## sql"INSERT INTO myTable (colA, colB, colC) VALUES (?, ?, ?)"
|
||||||
##
|
##
|
||||||
## Examples
|
## Examples
|
||||||
## --------
|
## ========
|
||||||
##
|
##
|
||||||
## Opening a connection to a database
|
## Opening a connection to a database
|
||||||
## ==================================
|
## ----------------------------------
|
||||||
##
|
##
|
||||||
## .. code-block:: Nim
|
## .. code-block:: Nim
|
||||||
## import db_sqlite
|
## import db_sqlite
|
||||||
|
|
@ -35,7 +35,7 @@
|
||||||
## db.close()
|
## db.close()
|
||||||
##
|
##
|
||||||
## Creating a table
|
## Creating a table
|
||||||
## ================
|
## ----------------
|
||||||
##
|
##
|
||||||
## .. code-block:: Nim
|
## .. code-block:: Nim
|
||||||
## db.exec(sql"DROP TABLE IF EXISTS myTable")
|
## db.exec(sql"DROP TABLE IF EXISTS myTable")
|
||||||
|
|
@ -44,14 +44,14 @@
|
||||||
## name varchar(50) not null)"""))
|
## name varchar(50) not null)"""))
|
||||||
##
|
##
|
||||||
## Inserting data
|
## Inserting data
|
||||||
## ==============
|
## --------------
|
||||||
##
|
##
|
||||||
## .. code-block:: Nim
|
## .. code-block:: Nim
|
||||||
## db.exec(sql"INSERT INTO myTable (id, name) VALUES (0, ?)",
|
## db.exec(sql"INSERT INTO myTable (id, name) VALUES (0, ?)",
|
||||||
## "Jack")
|
## "Jack")
|
||||||
##
|
##
|
||||||
## Larger example
|
## Larger example
|
||||||
## ==============
|
## --------------
|
||||||
##
|
##
|
||||||
## .. code-block:: nim
|
## .. code-block:: nim
|
||||||
##
|
##
|
||||||
|
|
|
||||||
|
|
@ -6,18 +6,6 @@
|
||||||
# distribution, for details about the copyright.
|
# distribution, for details about the copyright.
|
||||||
#
|
#
|
||||||
|
|
||||||
|
|
||||||
from pcre import nil
|
|
||||||
import nre/private/util
|
|
||||||
import tables
|
|
||||||
from strutils import `%`
|
|
||||||
from math import ceil
|
|
||||||
import options
|
|
||||||
from unicode import runeLenAt
|
|
||||||
|
|
||||||
export options
|
|
||||||
|
|
||||||
|
|
||||||
## What is NRE?
|
## What is NRE?
|
||||||
## ============
|
## ============
|
||||||
##
|
##
|
||||||
|
|
@ -67,6 +55,15 @@ runnableExamples:
|
||||||
let matchBounds = firstVowel.get().captureBounds[-1]
|
let matchBounds = firstVowel.get().captureBounds[-1]
|
||||||
doAssert matchBounds.a == 1
|
doAssert matchBounds.a == 1
|
||||||
|
|
||||||
|
from pcre import nil
|
||||||
|
import nre/private/util
|
||||||
|
import tables
|
||||||
|
from strutils import `%`
|
||||||
|
from math import ceil
|
||||||
|
import options
|
||||||
|
from unicode import runeLenAt
|
||||||
|
|
||||||
|
export options
|
||||||
|
|
||||||
# Type definitions {{{
|
# Type definitions {{{
|
||||||
type
|
type
|
||||||
|
|
|
||||||
|
|
@ -50,7 +50,7 @@
|
||||||
## proc loadGame(name: string): Future[Game] {.async.}
|
## proc loadGame(name: string): Future[Game] {.async.}
|
||||||
##
|
##
|
||||||
## JavaScript compatibility
|
## JavaScript compatibility
|
||||||
## ~~~~~~~~~~~~~~~~~~~~~~~~~
|
## ========================
|
||||||
##
|
##
|
||||||
## Nim currently generates `async/await` JavaScript code which is supported in modern
|
## Nim currently generates `async/await` JavaScript code which is supported in modern
|
||||||
## EcmaScript and most modern versions of browsers, Node.js and Electron.
|
## EcmaScript and most modern versions of browsers, Node.js and Electron.
|
||||||
|
|
|
||||||
|
|
@ -7,23 +7,6 @@
|
||||||
# distribution, for details about the copyright.
|
# distribution, for details about the copyright.
|
||||||
#
|
#
|
||||||
|
|
||||||
include "system/inclrtl"
|
|
||||||
|
|
||||||
import os, tables, strutils, times, heapqueue, lists, options, asyncstreams
|
|
||||||
import options, math
|
|
||||||
import asyncfutures except callSoon
|
|
||||||
|
|
||||||
import nativesockets, net, deques
|
|
||||||
|
|
||||||
export Port, SocketFlag
|
|
||||||
export asyncfutures except callSoon
|
|
||||||
export asyncstreams
|
|
||||||
|
|
||||||
#{.injectStmt: newGcInvariant().}
|
|
||||||
|
|
||||||
## AsyncDispatch
|
|
||||||
## *************
|
|
||||||
##
|
|
||||||
## This module implements asynchronous IO. This includes a dispatcher,
|
## This module implements asynchronous IO. This includes a dispatcher,
|
||||||
## a ``Future`` type implementation, and an ``async`` macro which allows
|
## a ``Future`` type implementation, and an ``async`` macro which allows
|
||||||
## asynchronous code to be written in a synchronous style with the ``await``
|
## asynchronous code to be written in a synchronous style with the ``await``
|
||||||
|
|
@ -82,7 +65,7 @@ export asyncstreams
|
||||||
## error), if there is no error however it returns the value of the future.
|
## error), if there is no error however it returns the value of the future.
|
||||||
##
|
##
|
||||||
## Asynchronous procedures
|
## Asynchronous procedures
|
||||||
## -----------------------
|
## =======================
|
||||||
##
|
##
|
||||||
## Asynchronous procedures remove the pain of working with callbacks. They do
|
## Asynchronous procedures remove the pain of working with callbacks. They do
|
||||||
## this by allowing you to write asynchronous code the same way as you would
|
## this by allowing you to write asynchronous code the same way as you would
|
||||||
|
|
@ -116,7 +99,7 @@ export asyncstreams
|
||||||
## exceptions in async procs.
|
## exceptions in async procs.
|
||||||
##
|
##
|
||||||
## Handling Exceptions
|
## Handling Exceptions
|
||||||
## ~~~~~~~~~~~~~~~~~~~
|
## -------------------
|
||||||
##
|
##
|
||||||
## The most reliable way to handle exceptions is to use ``yield`` on a future
|
## The most reliable way to handle exceptions is to use ``yield`` on a future
|
||||||
## then check the future's ``failed`` property. For example:
|
## then check the future's ``failed`` property. For example:
|
||||||
|
|
@ -142,7 +125,7 @@ export asyncstreams
|
||||||
##
|
##
|
||||||
##
|
##
|
||||||
## Discarding futures
|
## Discarding futures
|
||||||
## ------------------
|
## ==================
|
||||||
##
|
##
|
||||||
## Futures should **never** be discarded. This is because they may contain
|
## Futures should **never** be discarded. This is because they may contain
|
||||||
## errors. If you do not care for the result of a Future then you should
|
## errors. If you do not care for the result of a Future then you should
|
||||||
|
|
@ -151,17 +134,31 @@ export asyncstreams
|
||||||
## ``waitFor`` for that purpose.
|
## ``waitFor`` for that purpose.
|
||||||
##
|
##
|
||||||
## Examples
|
## Examples
|
||||||
## --------
|
## ========
|
||||||
##
|
##
|
||||||
## For examples take a look at the documentation for the modules implementing
|
## For examples take a look at the documentation for the modules implementing
|
||||||
## asynchronous IO. A good place to start is the
|
## asynchronous IO. A good place to start is the
|
||||||
## `asyncnet module <asyncnet.html>`_.
|
## `asyncnet module <asyncnet.html>`_.
|
||||||
##
|
##
|
||||||
## Limitations/Bugs
|
## Limitations/Bugs
|
||||||
## ----------------
|
## ================
|
||||||
##
|
##
|
||||||
## * The effect system (``raises: []``) does not work with async procedures.
|
## * The effect system (``raises: []``) does not work with async procedures.
|
||||||
|
|
||||||
|
include "system/inclrtl"
|
||||||
|
|
||||||
|
import os, tables, strutils, times, heapqueue, lists, options, asyncstreams
|
||||||
|
import options, math
|
||||||
|
import asyncfutures except callSoon
|
||||||
|
|
||||||
|
import nativesockets, net, deques
|
||||||
|
|
||||||
|
export Port, SocketFlag
|
||||||
|
export asyncfutures except callSoon
|
||||||
|
export asyncstreams
|
||||||
|
|
||||||
|
#{.injectStmt: newGcInvariant().}
|
||||||
|
|
||||||
# TODO: Check if yielded future is nil and throw a more meaningful exception
|
# TODO: Check if yielded future is nil and throw a more meaningful exception
|
||||||
|
|
||||||
type
|
type
|
||||||
|
|
@ -1827,4 +1824,4 @@ proc setEvent*(ev: AsyncEvent) {.deprecated.} =
|
||||||
## Set event ``ev`` to signaled state.
|
## Set event ``ev`` to signaled state.
|
||||||
##
|
##
|
||||||
## **Deprecated since v0.18.0:** Use ``trigger`` instead.
|
## **Deprecated since v0.18.0:** Use ``trigger`` instead.
|
||||||
ev.trigger()
|
ev.trigger()
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,7 @@
|
||||||
## * Navigation through the FTP server's directories.
|
## * Navigation through the FTP server's directories.
|
||||||
##
|
##
|
||||||
## Connecting to an FTP server
|
## Connecting to an FTP server
|
||||||
## ------------------------
|
## ===========================
|
||||||
##
|
##
|
||||||
## In order to begin any sort of transfer of files you must first
|
## In order to begin any sort of transfer of files you must first
|
||||||
## connect to an FTP server. You can do so with the ``connect`` procedure.
|
## connect to an FTP server. You can do so with the ``connect`` procedure.
|
||||||
|
|
@ -34,7 +34,7 @@
|
||||||
## client will be connected after the ``await ftp.connect()`` call.
|
## client will be connected after the ``await ftp.connect()`` call.
|
||||||
##
|
##
|
||||||
## Uploading a new file
|
## Uploading a new file
|
||||||
## --------------------
|
## ====================
|
||||||
##
|
##
|
||||||
## After a connection is made you can use the ``store`` procedure to upload
|
## After a connection is made you can use the ``store`` procedure to upload
|
||||||
## a new file to the FTP server. Make sure to check you are in the correct
|
## a new file to the FTP server. Make sure to check you are in the correct
|
||||||
|
|
@ -53,7 +53,7 @@
|
||||||
## waitFor(main())
|
## waitFor(main())
|
||||||
##
|
##
|
||||||
## Checking the progress of a file transfer
|
## Checking the progress of a file transfer
|
||||||
## ----------------------------------------
|
## ========================================
|
||||||
##
|
##
|
||||||
## The progress of either a file upload or a file download can be checked
|
## The progress of either a file upload or a file download can be checked
|
||||||
## by specifying a ``onProgressChanged`` procedure to the ``store`` or
|
## by specifying a ``onProgressChanged`` procedure to the ``store`` or
|
||||||
|
|
|
||||||
|
|
@ -14,9 +14,8 @@
|
||||||
## application you should use a reverse proxy (for example nginx) instead of
|
## application you should use a reverse proxy (for example nginx) instead of
|
||||||
## allowing users to connect directly to this server.
|
## allowing users to connect directly to this server.
|
||||||
##
|
##
|
||||||
##
|
|
||||||
## Basic usage
|
## Basic usage
|
||||||
## -----------
|
## ===========
|
||||||
##
|
##
|
||||||
## This example will create an HTTP server on port 8080. The server will
|
## This example will create an HTTP server on port 8080. The server will
|
||||||
## respond to all requests with a ``200 OK`` response code and "Hello World"
|
## respond to all requests with a ``200 OK`` response code and "Hello World"
|
||||||
|
|
|
||||||
|
|
@ -7,8 +7,6 @@
|
||||||
# distribution, for details about the copyright.
|
# distribution, for details about the copyright.
|
||||||
#
|
#
|
||||||
|
|
||||||
## AsyncMacro
|
|
||||||
## *************
|
|
||||||
## `asyncdispatch` module depends on the `asyncmacro` module to work properly.
|
## `asyncdispatch` module depends on the `asyncmacro` module to work properly.
|
||||||
|
|
||||||
import macros, strutils, asyncfutures
|
import macros, strutils, asyncfutures
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,7 @@
|
||||||
## asynchronous dispatcher defined in the ``asyncdispatch`` module.
|
## asynchronous dispatcher defined in the ``asyncdispatch`` module.
|
||||||
##
|
##
|
||||||
## Asynchronous IO in Nim
|
## Asynchronous IO in Nim
|
||||||
## ----------------------
|
## ======================
|
||||||
##
|
##
|
||||||
## Async IO in Nim consists of multiple layers (from highest to lowest):
|
## Async IO in Nim consists of multiple layers (from highest to lowest):
|
||||||
##
|
##
|
||||||
|
|
@ -49,7 +49,7 @@
|
||||||
## over all the layers, providing some extra features such as buffering.
|
## over all the layers, providing some extra features such as buffering.
|
||||||
##
|
##
|
||||||
## SSL
|
## SSL
|
||||||
## ----
|
## ===
|
||||||
##
|
##
|
||||||
## SSL can be enabled by compiling with the ``-d:ssl`` flag.
|
## SSL can be enabled by compiling with the ``-d:ssl`` flag.
|
||||||
##
|
##
|
||||||
|
|
@ -58,10 +58,10 @@
|
||||||
## the newly created SSL context to get an SSL socket.
|
## the newly created SSL context to get an SSL socket.
|
||||||
##
|
##
|
||||||
## Examples
|
## Examples
|
||||||
## --------
|
## ========
|
||||||
##
|
##
|
||||||
## Chat server
|
## Chat server
|
||||||
## ^^^^^^^^^^^
|
## -----------
|
||||||
##
|
##
|
||||||
## The following example demonstrates a simple chat server.
|
## The following example demonstrates a simple chat server.
|
||||||
##
|
##
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,6 @@
|
||||||
## bytes (i.e., a total of 24 bits) can therefore be represented by
|
## bytes (i.e., a total of 24 bits) can therefore be represented by
|
||||||
## four 6-bit Base64 digits.
|
## four 6-bit Base64 digits.
|
||||||
##
|
##
|
||||||
##
|
|
||||||
## Basic usage
|
## Basic usage
|
||||||
## ===========
|
## ===========
|
||||||
##
|
##
|
||||||
|
|
|
||||||
68
lib/pure/collections/hashcommon.nim
Normal file
68
lib/pure/collections/hashcommon.nim
Normal file
|
|
@ -0,0 +1,68 @@
|
||||||
|
#
|
||||||
|
#
|
||||||
|
# Nim's Runtime Library
|
||||||
|
# (c) Copyright 2019 Andreas Rumpf
|
||||||
|
#
|
||||||
|
# See the file "copying.txt", included in this
|
||||||
|
# distribution, for details about the copyright.
|
||||||
|
#
|
||||||
|
|
||||||
|
# An ``include`` file which contains common code for
|
||||||
|
# hash sets and tables.
|
||||||
|
|
||||||
|
const
|
||||||
|
growthFactor = 2
|
||||||
|
|
||||||
|
when not defined(nimHasDefault):
|
||||||
|
template default[T](t: typedesc[T]): T =
|
||||||
|
var v: T
|
||||||
|
v
|
||||||
|
|
||||||
|
# hcode for real keys cannot be zero. hcode==0 signifies an empty slot. These
|
||||||
|
# two procs retain clarity of that encoding without the space cost of an enum.
|
||||||
|
proc isEmpty(hcode: Hash): bool {.inline.} =
|
||||||
|
result = hcode == 0
|
||||||
|
|
||||||
|
proc isFilled(hcode: Hash): bool {.inline.} =
|
||||||
|
result = hcode != 0
|
||||||
|
|
||||||
|
proc nextTry(h, maxHash: Hash): Hash {.inline.} =
|
||||||
|
result = (h + 1) and maxHash
|
||||||
|
|
||||||
|
proc mustRehash(length, counter: int): bool {.inline.} =
|
||||||
|
assert(length > counter)
|
||||||
|
result = (length * 2 < counter * 3) or (length - counter < 4)
|
||||||
|
|
||||||
|
template rawGetKnownHCImpl() {.dirty.} =
|
||||||
|
if t.dataLen == 0:
|
||||||
|
return -1
|
||||||
|
var h: Hash = hc and maxHash(t) # start with real hash value
|
||||||
|
while isFilled(t.data[h].hcode):
|
||||||
|
# Compare hc THEN key with boolean short circuit. This makes the common case
|
||||||
|
# zero ==key's for missing (e.g.inserts) and exactly one ==key for present.
|
||||||
|
# It does slow down succeeding lookups by one extra Hash cmp&and..usually
|
||||||
|
# just a few clock cycles, generally worth it for any non-integer-like A.
|
||||||
|
if t.data[h].hcode == hc and t.data[h].key == key:
|
||||||
|
return h
|
||||||
|
h = nextTry(h, maxHash(t))
|
||||||
|
result = -1 - h # < 0 => MISSING; insert idx = -1 - result
|
||||||
|
|
||||||
|
proc rawGetKnownHC[X, A](t: X, key: A, hc: Hash): int {.inline.} =
|
||||||
|
rawGetKnownHCImpl()
|
||||||
|
|
||||||
|
template genHashImpl(key, hc: typed) =
|
||||||
|
hc = hash(key)
|
||||||
|
if hc == 0: # This almost never taken branch should be very predictable.
|
||||||
|
hc = 314159265 # Value doesn't matter; Any non-zero favorite is fine.
|
||||||
|
|
||||||
|
template genHash(key: typed): Hash =
|
||||||
|
var res: Hash
|
||||||
|
genHashImpl(key, res)
|
||||||
|
res
|
||||||
|
|
||||||
|
template rawGetImpl() {.dirty.} =
|
||||||
|
genHashImpl(key, hc)
|
||||||
|
rawGetKnownHCImpl()
|
||||||
|
|
||||||
|
proc rawGet[X, A](t: X, key: A, hc: var Hash): int {.inline.} =
|
||||||
|
rawGetImpl()
|
||||||
|
|
@ -80,14 +80,15 @@ 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`.
|
## It consists of a `value` field, and pointers to `next` and `prev`.
|
||||||
next*, prev*: ref DoublyLinkedNodeObj[T]
|
next*: <//>(ref DoublyLinkedNodeObj[T])
|
||||||
|
prev*: ref DoublyLinkedNodeObj[T]
|
||||||
value*: T
|
value*: T
|
||||||
DoublyLinkedNode*[T] = ref DoublyLinkedNodeObj[T]
|
DoublyLinkedNode*[T] = ref DoublyLinkedNodeObj[T]
|
||||||
|
|
||||||
SinglyLinkedNodeObj*[T] = object ## A node a singly linked list consists of.
|
SinglyLinkedNodeObj*[T] = object ## A node a singly linked list consists of.
|
||||||
##
|
##
|
||||||
## It consists of a `value` field, and a pointer to `next`.
|
## It consists of a `value` field, and a pointer to `next`.
|
||||||
next*: ref SinglyLinkedNodeObj[T]
|
next*: <//>(ref SinglyLinkedNodeObj[T])
|
||||||
value*: T
|
value*: T
|
||||||
SinglyLinkedNode*[T] = ref SinglyLinkedNodeObj[T]
|
SinglyLinkedNode*[T] = ref SinglyLinkedNodeObj[T]
|
||||||
|
|
||||||
|
|
@ -95,19 +96,22 @@ type
|
||||||
##
|
##
|
||||||
## Use `initSinglyLinkedList proc <#initSinglyLinkedList>`_ to create
|
## Use `initSinglyLinkedList proc <#initSinglyLinkedList>`_ to create
|
||||||
## a new empty list.
|
## a new empty list.
|
||||||
head*, tail*: SinglyLinkedNode[T]
|
head*: <//>(SinglyLinkedNode[T])
|
||||||
|
tail*: SinglyLinkedNode[T]
|
||||||
|
|
||||||
DoublyLinkedList*[T] = object ## A doubly linked list.
|
DoublyLinkedList*[T] = object ## A doubly linked list.
|
||||||
##
|
##
|
||||||
## Use `initDoublyLinkedList proc <#initDoublyLinkedList>`_ to create
|
## Use `initDoublyLinkedList proc <#initDoublyLinkedList>`_ to create
|
||||||
## a new empty list.
|
## a new empty list.
|
||||||
head*, tail*: DoublyLinkedNode[T]
|
head*: <//>(DoublyLinkedNode[T])
|
||||||
|
tail*: DoublyLinkedNode[T]
|
||||||
|
|
||||||
SinglyLinkedRing*[T] = object ## A singly linked ring.
|
SinglyLinkedRing*[T] = object ## A singly linked ring.
|
||||||
##
|
##
|
||||||
## Use `initSinglyLinkedRing proc <#initSinglyLinkedRing>`_ to create
|
## Use `initSinglyLinkedRing proc <#initSinglyLinkedRing>`_ to create
|
||||||
## a new empty ring.
|
## a new empty ring.
|
||||||
head*, tail*: SinglyLinkedNode[T]
|
head*: <//>(SinglyLinkedNode[T])
|
||||||
|
tail*: SinglyLinkedNode[T]
|
||||||
|
|
||||||
DoublyLinkedRing*[T] = object ## A doubly linked ring.
|
DoublyLinkedRing*[T] = object ## A doubly linked ring.
|
||||||
##
|
##
|
||||||
|
|
@ -147,7 +151,7 @@ proc initDoublyLinkedRing*[T](): DoublyLinkedRing[T] =
|
||||||
var a = initDoublyLinkedRing[int]()
|
var a = initDoublyLinkedRing[int]()
|
||||||
discard
|
discard
|
||||||
|
|
||||||
proc newDoublyLinkedNode*[T](value: T): DoublyLinkedNode[T] =
|
proc newDoublyLinkedNode*[T](value: T): <//>(DoublyLinkedNode[T]) =
|
||||||
## Creates a new doubly linked node with the given `value`.
|
## Creates a new doubly linked node with the given `value`.
|
||||||
runnableExamples:
|
runnableExamples:
|
||||||
var n = newDoublyLinkedNode[int](5)
|
var n = newDoublyLinkedNode[int](5)
|
||||||
|
|
@ -156,7 +160,7 @@ proc newDoublyLinkedNode*[T](value: T): DoublyLinkedNode[T] =
|
||||||
new(result)
|
new(result)
|
||||||
result.value = value
|
result.value = value
|
||||||
|
|
||||||
proc newSinglyLinkedNode*[T](value: T): SinglyLinkedNode[T] =
|
proc newSinglyLinkedNode*[T](value: T): <//>(SinglyLinkedNode[T]) =
|
||||||
## Creates a new singly linked node with the given `value`.
|
## Creates a new singly linked node with the given `value`.
|
||||||
runnableExamples:
|
runnableExamples:
|
||||||
var n = newSinglyLinkedNode[int](5)
|
var n = newSinglyLinkedNode[int](5)
|
||||||
|
|
|
||||||
|
|
@ -949,6 +949,14 @@ macro mapLiterals*(constructor, op: untyped;
|
||||||
## works for nested tuples of arrays of sets etc.
|
## works for nested tuples of arrays of sets etc.
|
||||||
result = mapLitsImpl(constructor, op, nested.boolVal)
|
result = mapLitsImpl(constructor, op, nested.boolVal)
|
||||||
|
|
||||||
|
iterator items*[T](xs: iterator: T): T =
|
||||||
|
## iterates over each element yielded by a closure iterator. This may
|
||||||
|
## not seem particularly useful on its own, but this allows closure
|
||||||
|
## iterators to be used by the the mapIt, filterIt, allIt, anyIt, etc.
|
||||||
|
## templates.
|
||||||
|
for x in xs():
|
||||||
|
yield x
|
||||||
|
|
||||||
when isMainModule:
|
when isMainModule:
|
||||||
import strutils
|
import strutils
|
||||||
from algorithm import sorted
|
from algorithm import sorted
|
||||||
|
|
@ -1382,5 +1390,13 @@ when isMainModule:
|
||||||
doAssert outp == @[@["a", "b"], @["c", "d"]]
|
doAssert outp == @[@["a", "b"], @["c", "d"]]
|
||||||
|
|
||||||
|
|
||||||
|
block:
|
||||||
|
proc iter(len: int): auto =
|
||||||
|
result = iterator(): int =
|
||||||
|
for i in 0..<len:
|
||||||
|
yield i
|
||||||
|
|
||||||
|
doAssert: iter(3).mapIt(2*it).foldl(a + b) == 6
|
||||||
|
|
||||||
when not defined(testing):
|
when not defined(testing):
|
||||||
echo "Finished doc tests"
|
echo "Finished doc tests"
|
||||||
|
|
|
||||||
153
lib/pure/collections/setimpl.nim
Normal file
153
lib/pure/collections/setimpl.nim
Normal file
|
|
@ -0,0 +1,153 @@
|
||||||
|
#
|
||||||
|
#
|
||||||
|
# Nim's Runtime Library
|
||||||
|
# (c) Copyright 2019 Andreas Rumpf
|
||||||
|
#
|
||||||
|
# See the file "copying.txt", included in this
|
||||||
|
# distribution, for details about the copyright.
|
||||||
|
#
|
||||||
|
|
||||||
|
# An ``include`` file for the different hash set implementations.
|
||||||
|
|
||||||
|
|
||||||
|
template maxHash(t): untyped = high(t.data)
|
||||||
|
template dataLen(t): untyped = len(t.data)
|
||||||
|
|
||||||
|
include hashcommon
|
||||||
|
|
||||||
|
template initImpl(s: typed, size: int) =
|
||||||
|
assert isPowerOfTwo(size)
|
||||||
|
when s is OrderedSet:
|
||||||
|
s.first = -1
|
||||||
|
s.last = -1
|
||||||
|
s.counter = 0
|
||||||
|
newSeq(s.data, size)
|
||||||
|
|
||||||
|
template rawInsertImpl() {.dirty.} =
|
||||||
|
if data.len == 0:
|
||||||
|
initImpl(s, defaultInitialSize)
|
||||||
|
data[h].key = key
|
||||||
|
data[h].hcode = hc
|
||||||
|
|
||||||
|
proc rawInsert[A](s: var HashSet[A], data: var KeyValuePairSeq[A], key: A,
|
||||||
|
hc: Hash, h: Hash) =
|
||||||
|
rawInsertImpl()
|
||||||
|
|
||||||
|
proc enlarge[A](s: var HashSet[A]) =
|
||||||
|
var n: KeyValuePairSeq[A]
|
||||||
|
newSeq(n, len(s.data) * growthFactor)
|
||||||
|
swap(s.data, n) # n is now old seq
|
||||||
|
for i in countup(0, high(n)):
|
||||||
|
if isFilled(n[i].hcode):
|
||||||
|
var j = -1 - rawGetKnownHC(s, n[i].key, n[i].hcode)
|
||||||
|
rawInsert(s, s.data, n[i].key, n[i].hcode, j)
|
||||||
|
|
||||||
|
template inclImpl() {.dirty.} =
|
||||||
|
if s.data.len == 0:
|
||||||
|
initImpl(s, defaultInitialSize)
|
||||||
|
var hc: Hash
|
||||||
|
var index = rawGet(s, key, hc)
|
||||||
|
if index < 0:
|
||||||
|
if mustRehash(len(s.data), s.counter):
|
||||||
|
enlarge(s)
|
||||||
|
index = rawGetKnownHC(s, key, hc)
|
||||||
|
rawInsert(s, s.data, key, hc, -1 - index)
|
||||||
|
inc(s.counter)
|
||||||
|
|
||||||
|
template containsOrInclImpl() {.dirty.} =
|
||||||
|
if s.data.len == 0:
|
||||||
|
initImpl(s, defaultInitialSize)
|
||||||
|
var hc: Hash
|
||||||
|
var index = rawGet(s, key, hc)
|
||||||
|
if index >= 0:
|
||||||
|
result = true
|
||||||
|
else:
|
||||||
|
if mustRehash(len(s.data), s.counter):
|
||||||
|
enlarge(s)
|
||||||
|
index = rawGetKnownHC(s, key, hc)
|
||||||
|
rawInsert(s, s.data, key, hc, -1 - index)
|
||||||
|
inc(s.counter)
|
||||||
|
|
||||||
|
template doWhile(a, b) =
|
||||||
|
while true:
|
||||||
|
b
|
||||||
|
if not a: break
|
||||||
|
|
||||||
|
proc exclImpl[A](s: var HashSet[A], key: A) : bool {. inline .} =
|
||||||
|
var hc: Hash
|
||||||
|
var i = rawGet(s, key, hc)
|
||||||
|
var msk = high(s.data)
|
||||||
|
result = true
|
||||||
|
|
||||||
|
if i >= 0:
|
||||||
|
result = false
|
||||||
|
dec(s.counter)
|
||||||
|
while true: # KnuthV3 Algo6.4R adapted for i=i+1 instead of i=i-1
|
||||||
|
var j = i # The correctness of this depends on (h+1) in nextTry,
|
||||||
|
var r = j # though may be adaptable to other simple sequences.
|
||||||
|
s.data[i].hcode = 0 # mark current EMPTY
|
||||||
|
s.data[i].key = default(type(s.data[i].key))
|
||||||
|
doWhile((i >= r and r > j) or (r > j and j > i) or (j > i and i >= r)):
|
||||||
|
i = (i + 1) and msk # increment mod table size
|
||||||
|
if isEmpty(s.data[i].hcode): # end of collision cluster; So all done
|
||||||
|
return
|
||||||
|
r = s.data[i].hcode and msk # "home" location of key@i
|
||||||
|
shallowCopy(s.data[j], s.data[i]) # data[i] will be marked EMPTY next loop
|
||||||
|
|
||||||
|
template dollarImpl() {.dirty.} =
|
||||||
|
result = "{"
|
||||||
|
for key in items(s):
|
||||||
|
if result.len > 1: result.add(", ")
|
||||||
|
result.addQuoted(key)
|
||||||
|
result.add("}")
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# --------------------------- OrderedSet ------------------------------
|
||||||
|
|
||||||
|
proc rawGet[A](t: OrderedSet[A], key: A, hc: var Hash): int {.inline.} =
|
||||||
|
rawGetImpl()
|
||||||
|
|
||||||
|
proc rawInsert[A](s: var OrderedSet[A], data: var OrderedKeyValuePairSeq[A],
|
||||||
|
key: A, hc: Hash, h: Hash) =
|
||||||
|
rawInsertImpl()
|
||||||
|
data[h].next = -1
|
||||||
|
if s.first < 0: s.first = h
|
||||||
|
if s.last >= 0: data[s.last].next = h
|
||||||
|
s.last = h
|
||||||
|
|
||||||
|
proc enlarge[A](s: var OrderedSet[A]) =
|
||||||
|
var n: OrderedKeyValuePairSeq[A]
|
||||||
|
newSeq(n, len(s.data) * growthFactor)
|
||||||
|
var h = s.first
|
||||||
|
s.first = -1
|
||||||
|
s.last = -1
|
||||||
|
swap(s.data, n)
|
||||||
|
while h >= 0:
|
||||||
|
var nxt = n[h].next
|
||||||
|
if isFilled(n[h].hcode):
|
||||||
|
var j = -1 - rawGetKnownHC(s, n[h].key, n[h].hcode)
|
||||||
|
rawInsert(s, s.data, n[h].key, n[h].hcode, j)
|
||||||
|
h = nxt
|
||||||
|
|
||||||
|
proc exclImpl[A](s: var OrderedSet[A], key: A) : bool {.inline.} =
|
||||||
|
if len(s.data) == 0:
|
||||||
|
return true
|
||||||
|
var n: OrderedKeyValuePairSeq[A]
|
||||||
|
newSeq(n, len(s.data))
|
||||||
|
var h = s.first
|
||||||
|
s.first = -1
|
||||||
|
s.last = -1
|
||||||
|
swap(s.data, n)
|
||||||
|
let hc = genHash(key)
|
||||||
|
result = true
|
||||||
|
while h >= 0:
|
||||||
|
var nxt = n[h].next
|
||||||
|
if isFilled(n[h].hcode):
|
||||||
|
if n[h].hcode == hc and n[h].key == key:
|
||||||
|
dec s.counter
|
||||||
|
result = false
|
||||||
|
else:
|
||||||
|
var j = -1 - rawGetKnownHC(s, n[h].key, n[h].hcode)
|
||||||
|
rawInsert(s, s.data, n[h].key, n[h].hcode, j)
|
||||||
|
h = nxt
|
||||||
|
|
@ -26,7 +26,7 @@
|
||||||
## `symmetric difference <#symmetricDifference,HashSet[A],HashSet[A]>`_
|
## `symmetric difference <#symmetricDifference,HashSet[A],HashSet[A]>`_
|
||||||
##
|
##
|
||||||
## .. code-block::
|
## .. code-block::
|
||||||
## echo toHashSet([9, 5, 1]) # {9, 1, 5}
|
## echo toHashSet([9, 5, 1]) # {9, 1, 5}
|
||||||
## echo toOrderedSet([9, 5, 1]) # {9, 5, 1}
|
## echo toOrderedSet([9, 5, 1]) # {9, 5, 1}
|
||||||
##
|
##
|
||||||
## let
|
## let
|
||||||
|
|
@ -69,148 +69,32 @@ type
|
||||||
data: KeyValuePairSeq[A]
|
data: KeyValuePairSeq[A]
|
||||||
counter: int
|
counter: int
|
||||||
|
|
||||||
|
type
|
||||||
|
OrderedKeyValuePair[A] = tuple[
|
||||||
|
hcode: Hash, next: int, key: A]
|
||||||
|
OrderedKeyValuePairSeq[A] = seq[OrderedKeyValuePair[A]]
|
||||||
|
OrderedSet* {.myShallow.} [A] = object ## \
|
||||||
|
## A generic hash set that remembers insertion order.
|
||||||
|
##
|
||||||
|
## Use `init proc <#init,OrderedSet[A],int>`_ or `initOrderedSet proc
|
||||||
|
## <#initOrderedSet,int>`_ before calling other procs on it.
|
||||||
|
data: OrderedKeyValuePairSeq[A]
|
||||||
|
counter, first, last: int
|
||||||
|
|
||||||
# ---------------------- helpers -----------------------------------
|
const
|
||||||
|
defaultInitialSize* = 64
|
||||||
|
|
||||||
const growthFactor = 2
|
include setimpl
|
||||||
|
|
||||||
when not defined(nimHasDefault):
|
|
||||||
template default[T](t: typedesc[T]): T =
|
|
||||||
## Used by clear methods to get a default value.
|
|
||||||
var v: T
|
|
||||||
v
|
|
||||||
|
|
||||||
# hcode for real keys cannot be zero. hcode==0 signifies an empty slot. These
|
|
||||||
# two procs retain clarity of that encoding without the space cost of an enum.
|
|
||||||
proc isEmpty(hcode: Hash): bool {.inline.} =
|
|
||||||
result = hcode == 0
|
|
||||||
|
|
||||||
proc isFilled(hcode: Hash): bool {.inline.} =
|
|
||||||
result = hcode != 0
|
|
||||||
|
|
||||||
proc nextTry(h, maxHash: Hash): Hash {.inline.} =
|
|
||||||
result = (h + 1) and maxHash
|
|
||||||
|
|
||||||
template rawGetKnownHCImpl() {.dirty.} =
|
|
||||||
var h: Hash = hc and high(s.data) # start with real hash value
|
|
||||||
while isFilled(s.data[h].hcode):
|
|
||||||
# Compare hc THEN key with boolean short circuit. This makes the common case
|
|
||||||
# zero ==key's for missing (e.g.inserts) and exactly one ==key for present.
|
|
||||||
# It does slow down succeeding lookups by one extra Hash cmp&and..usually
|
|
||||||
# just a few clock cycles, generally worth it for any non-integer-like A.
|
|
||||||
if s.data[h].hcode == hc and s.data[h].key == key: # compare hc THEN key
|
|
||||||
return h
|
|
||||||
h = nextTry(h, high(s.data))
|
|
||||||
result = -1 - h # < 0 => MISSING; insert idx = -1 - result
|
|
||||||
|
|
||||||
template genHash(key: typed): Hash =
|
|
||||||
var hc = hash(key)
|
|
||||||
if hc == 0: # This almost never taken branch should be very predictable.
|
|
||||||
hc = 314159265 # Value doesn't matter; Any non-zero favorite is fine.
|
|
||||||
hc
|
|
||||||
|
|
||||||
template rawGetImpl() {.dirty.} =
|
|
||||||
hc = genHash(key)
|
|
||||||
rawGetKnownHCImpl()
|
|
||||||
|
|
||||||
template rawInsertImpl() {.dirty.} =
|
|
||||||
data[h].key = key
|
|
||||||
data[h].hcode = hc
|
|
||||||
|
|
||||||
proc rawGetKnownHC[A](s: HashSet[A], key: A, hc: Hash): int {.inline.} =
|
|
||||||
rawGetKnownHCImpl()
|
|
||||||
|
|
||||||
proc rawGet[A](s: HashSet[A], key: A, hc: var Hash): int {.inline.} =
|
|
||||||
rawGetImpl()
|
|
||||||
|
|
||||||
proc rawInsert[A](s: var HashSet[A], data: var KeyValuePairSeq[A], key: A,
|
|
||||||
hc: Hash, h: Hash) =
|
|
||||||
rawInsertImpl()
|
|
||||||
|
|
||||||
proc enlarge[A](s: var HashSet[A]) =
|
|
||||||
var n: KeyValuePairSeq[A]
|
|
||||||
newSeq(n, len(s.data) * growthFactor)
|
|
||||||
swap(s.data, n) # n is now old seq
|
|
||||||
for i in countup(0, high(n)):
|
|
||||||
if isFilled(n[i].hcode):
|
|
||||||
var j = -1 - rawGetKnownHC(s, n[i].key, n[i].hcode)
|
|
||||||
rawInsert(s, s.data, n[i].key, n[i].hcode, j)
|
|
||||||
|
|
||||||
template inclImpl() {.dirty.} =
|
|
||||||
var hc: Hash
|
|
||||||
var index = rawGet(s, key, hc)
|
|
||||||
if index < 0:
|
|
||||||
if mustRehash(len(s.data), s.counter):
|
|
||||||
enlarge(s)
|
|
||||||
index = rawGetKnownHC(s, key, hc)
|
|
||||||
rawInsert(s, s.data, key, hc, -1 - index)
|
|
||||||
inc(s.counter)
|
|
||||||
|
|
||||||
template containsOrInclImpl() {.dirty.} =
|
|
||||||
var hc: Hash
|
|
||||||
var index = rawGet(s, key, hc)
|
|
||||||
if index >= 0:
|
|
||||||
result = true
|
|
||||||
else:
|
|
||||||
if mustRehash(len(s.data), s.counter):
|
|
||||||
enlarge(s)
|
|
||||||
index = rawGetKnownHC(s, key, hc)
|
|
||||||
rawInsert(s, s.data, key, hc, -1 - index)
|
|
||||||
inc(s.counter)
|
|
||||||
|
|
||||||
template doWhile(a, b) =
|
|
||||||
while true:
|
|
||||||
b
|
|
||||||
if not a: break
|
|
||||||
|
|
||||||
proc exclImpl[A](s: var HashSet[A], key: A) : bool {. inline .} =
|
|
||||||
assert s.isValid, "The set needs to be initialized."
|
|
||||||
var hc: Hash
|
|
||||||
var i = rawGet(s, key, hc)
|
|
||||||
var msk = high(s.data)
|
|
||||||
result = true
|
|
||||||
|
|
||||||
if i >= 0:
|
|
||||||
result = false
|
|
||||||
dec(s.counter)
|
|
||||||
while true: # KnuthV3 Algo6.4R adapted for i=i+1 instead of i=i-1
|
|
||||||
var j = i # The correctness of this depends on (h+1) in nextTry,
|
|
||||||
var r = j # though may be adaptable to other simple sequences.
|
|
||||||
s.data[i].hcode = 0 # mark current EMPTY
|
|
||||||
s.data[i].key = default(type(s.data[i].key))
|
|
||||||
doWhile((i >= r and r > j) or (r > j and j > i) or (j > i and i >= r)):
|
|
||||||
i = (i + 1) and msk # increment mod table size
|
|
||||||
if isEmpty(s.data[i].hcode): # end of collision cluster; So all done
|
|
||||||
return
|
|
||||||
r = s.data[i].hcode and msk # "home" location of key@i
|
|
||||||
shallowCopy(s.data[j], s.data[i]) # data[i] will be marked EMPTY next loop
|
|
||||||
|
|
||||||
proc mustRehash(length, counter: int): bool {.inline.} =
|
|
||||||
assert(length > counter)
|
|
||||||
result = (length * 2 < counter * 3) or (length - counter < 4)
|
|
||||||
|
|
||||||
template dollarImpl() {.dirty.} =
|
|
||||||
result = "{"
|
|
||||||
for key in items(s):
|
|
||||||
if result.len > 1: result.add(", ")
|
|
||||||
result.addQuoted(key)
|
|
||||||
result.add("}")
|
|
||||||
|
|
||||||
proc rightSize*(count: Natural): int {.inline.}
|
proc rightSize*(count: Natural): int {.inline.}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# ---------------------------------------------------------------------
|
# ---------------------------------------------------------------------
|
||||||
# ------------------------------ HashSet ------------------------------
|
# ------------------------------ HashSet ------------------------------
|
||||||
# ---------------------------------------------------------------------
|
# ---------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
proc init*[A](s: var HashSet[A], initialSize=64) =
|
proc init*[A](s: var HashSet[A], initialSize = defaultInitialSize) =
|
||||||
## Initializes a hash set.
|
## Initializes a hash set.
|
||||||
##
|
##
|
||||||
## The `initialSize` parameter needs to be a power of two (default: 64).
|
## The `initialSize` parameter needs to be a power of two (default: 64).
|
||||||
|
|
@ -218,9 +102,8 @@ proc init*[A](s: var HashSet[A], initialSize=64) =
|
||||||
## `math.nextPowerOfTwo proc <math.html#nextPowerOfTwo>`_ or `rightSize proc
|
## `math.nextPowerOfTwo proc <math.html#nextPowerOfTwo>`_ or `rightSize proc
|
||||||
## <#rightSize,Natural>`_ from this module.
|
## <#rightSize,Natural>`_ from this module.
|
||||||
##
|
##
|
||||||
## All set variables must be initialized before
|
## Starting from Nim v0.20, sets are initialized by default and it is
|
||||||
## use with other procs from this module, with the exception of `isValid proc
|
## not necessary to call this function explicitly.
|
||||||
## <#isValid,HashSet[A]>`_ and `len() <#len,HashSet[A]>`_.
|
|
||||||
##
|
##
|
||||||
## You can call this proc on a previously initialized hash set, which will
|
## You can call this proc on a previously initialized hash set, which will
|
||||||
## discard all its values. This might be more convenient than iterating over
|
## discard all its values. This might be more convenient than iterating over
|
||||||
|
|
@ -235,42 +118,26 @@ proc init*[A](s: var HashSet[A], initialSize=64) =
|
||||||
init(a)
|
init(a)
|
||||||
assert a.isValid
|
assert a.isValid
|
||||||
|
|
||||||
assert isPowerOfTwo(initialSize)
|
initImpl(s, initialSize)
|
||||||
s.counter = 0
|
|
||||||
newSeq(s.data, initialSize)
|
|
||||||
|
|
||||||
proc initHashSet*[A](initialSize=64): HashSet[A] =
|
proc initHashSet*[A](initialSize = defaultInitialSize): HashSet[A] =
|
||||||
## Wrapper around `init proc <#init,HashSet[A],int>`_ for initialization of
|
## Wrapper around `init proc <#init,HashSet[A],int>`_ for initialization of
|
||||||
## hash sets.
|
## hash sets.
|
||||||
##
|
##
|
||||||
## Returns an empty hash set you can assign directly in ``var`` blocks in a
|
## Returns an empty hash set you can assign directly in ``var`` blocks in a
|
||||||
## single line.
|
## single line.
|
||||||
##
|
##
|
||||||
|
## Starting from Nim v0.20, sets are initialized by default and it is
|
||||||
|
## not necessary to call this function explicitly.
|
||||||
|
##
|
||||||
## See also:
|
## See also:
|
||||||
## * `toHashSet proc <#toHashSet,openArray[A]>`_
|
## * `toHashSet proc <#toHashSet,openArray[A]>`_
|
||||||
runnableExamples:
|
runnableExamples:
|
||||||
var a = initHashSet[int]()
|
var a = initHashSet[int]()
|
||||||
assert a.isValid
|
|
||||||
a.incl(3)
|
a.incl(3)
|
||||||
assert len(a) == 1
|
assert len(a) == 1
|
||||||
result.init(initialSize)
|
|
||||||
|
|
||||||
proc isValid*[A](s: HashSet[A]): bool =
|
result.init(initialSize)
|
||||||
## Returns `true` if the set has been initialized (with `initHashSet proc
|
|
||||||
## <#initHashSet,int>`_ or `init proc <#init,HashSet[A],int>`_).
|
|
||||||
##
|
|
||||||
## Most operations over an uninitialized set will crash at runtime and
|
|
||||||
## `assert <system.html#assert>`_ in debug builds. You can use this proc in
|
|
||||||
## your own procs to verify that sets passed to your procs are correctly
|
|
||||||
## initialized.
|
|
||||||
##
|
|
||||||
## **Examples:**
|
|
||||||
##
|
|
||||||
## .. code-block ::
|
|
||||||
## proc savePreferences(options: HashSet[string]) =
|
|
||||||
## assert options.isValid, "Pass an initialized set!"
|
|
||||||
## # Do stuff here, may crash in release builds!
|
|
||||||
result = s.data.len > 0
|
|
||||||
|
|
||||||
proc `[]`*[A](s: var HashSet[A], key: A): var A =
|
proc `[]`*[A](s: var HashSet[A], key: A): var A =
|
||||||
## Returns the element that is actually stored in `s` which has the same
|
## Returns the element that is actually stored in `s` which has the same
|
||||||
|
|
@ -278,7 +145,6 @@ proc `[]`*[A](s: var HashSet[A], key: A): var A =
|
||||||
##
|
##
|
||||||
## This is useful when one overloaded `hash` and `==` but still needs
|
## This is useful when one overloaded `hash` and `==` but still needs
|
||||||
## reference semantics for sharing.
|
## reference semantics for sharing.
|
||||||
assert s.isValid, "The set needs to be initialized."
|
|
||||||
var hc: Hash
|
var hc: Hash
|
||||||
var index = rawGet(s, key, hc)
|
var index = rawGet(s, key, hc)
|
||||||
if index >= 0: result = s.data[index].key
|
if index >= 0: result = s.data[index].key
|
||||||
|
|
@ -305,7 +171,6 @@ proc contains*[A](s: HashSet[A], key: A): bool =
|
||||||
assert values.contains(2)
|
assert values.contains(2)
|
||||||
assert 2 in values
|
assert 2 in values
|
||||||
|
|
||||||
assert s.isValid, "The set needs to be initialized."
|
|
||||||
var hc: Hash
|
var hc: Hash
|
||||||
var index = rawGet(s, key, hc)
|
var index = rawGet(s, key, hc)
|
||||||
result = index >= 0
|
result = index >= 0
|
||||||
|
|
@ -325,7 +190,6 @@ proc incl*[A](s: var HashSet[A], key: A) =
|
||||||
values.incl(2)
|
values.incl(2)
|
||||||
assert values.len == 1
|
assert values.len == 1
|
||||||
|
|
||||||
assert s.isValid, "The set needs to be initialized."
|
|
||||||
inclImpl()
|
inclImpl()
|
||||||
|
|
||||||
proc incl*[A](s: var HashSet[A], other: HashSet[A]) =
|
proc incl*[A](s: var HashSet[A], other: HashSet[A]) =
|
||||||
|
|
@ -344,8 +208,6 @@ proc incl*[A](s: var HashSet[A], other: HashSet[A]) =
|
||||||
values.incl(others)
|
values.incl(others)
|
||||||
assert values.len == 5
|
assert values.len == 5
|
||||||
|
|
||||||
assert s.isValid, "The set `s` needs to be initialized."
|
|
||||||
assert other.isValid, "The set `other` needs to be initialized."
|
|
||||||
for item in other: incl(s, item)
|
for item in other: incl(s, item)
|
||||||
|
|
||||||
proc toHashSet*[A](keys: openArray[A]): HashSet[A] =
|
proc toHashSet*[A](keys: openArray[A]): HashSet[A] =
|
||||||
|
|
@ -368,14 +230,6 @@ proc toHashSet*[A](keys: openArray[A]): HashSet[A] =
|
||||||
result = initHashSet[A](rightSize(keys.len))
|
result = initHashSet[A](rightSize(keys.len))
|
||||||
for key in items(keys): result.incl(key)
|
for key in items(keys): result.incl(key)
|
||||||
|
|
||||||
proc initSet*[A](initialSize=64): HashSet[A] {.deprecated:
|
|
||||||
"Deprecated since v0.20, use `initHashSet`"} = initHashSet[A](initialSize)
|
|
||||||
## Deprecated since v0.20, use `initHashSet`.
|
|
||||||
|
|
||||||
proc toSet*[A](keys: openArray[A]): HashSet[A] {.deprecated:
|
|
||||||
"Deprecated since v0.20, use `toHashSet`"} = toHashSet[A](keys)
|
|
||||||
## Deprecated since v0.20, use `toHashSet`.
|
|
||||||
|
|
||||||
iterator items*[A](s: HashSet[A]): A =
|
iterator items*[A](s: HashSet[A]): A =
|
||||||
## Iterates over elements of the set `s`.
|
## Iterates over elements of the set `s`.
|
||||||
##
|
##
|
||||||
|
|
@ -395,8 +249,7 @@ iterator items*[A](s: HashSet[A]): A =
|
||||||
## assert a.len == 2
|
## assert a.len == 2
|
||||||
## echo b
|
## echo b
|
||||||
## # --> {(a: 1, b: 3), (a: 0, b: 4)}
|
## # --> {(a: 1, b: 3), (a: 0, b: 4)}
|
||||||
assert s.isValid, "The set needs to be initialized."
|
for h in 0 .. high(s.data):
|
||||||
for h in 0..high(s.data):
|
|
||||||
if isFilled(s.data[h].hcode): yield s.data[h].key
|
if isFilled(s.data[h].hcode): yield s.data[h].key
|
||||||
|
|
||||||
proc containsOrIncl*[A](s: var HashSet[A], key: A): bool =
|
proc containsOrIncl*[A](s: var HashSet[A], key: A): bool =
|
||||||
|
|
@ -417,7 +270,6 @@ proc containsOrIncl*[A](s: var HashSet[A], key: A): bool =
|
||||||
assert values.containsOrIncl(2) == true
|
assert values.containsOrIncl(2) == true
|
||||||
assert values.containsOrIncl(3) == false
|
assert values.containsOrIncl(3) == false
|
||||||
|
|
||||||
assert s.isValid, "The set needs to be initialized."
|
|
||||||
containsOrInclImpl()
|
containsOrInclImpl()
|
||||||
|
|
||||||
proc excl*[A](s: var HashSet[A], key: A) =
|
proc excl*[A](s: var HashSet[A], key: A) =
|
||||||
|
|
@ -434,6 +286,7 @@ proc excl*[A](s: var HashSet[A], key: A) =
|
||||||
s.excl(2)
|
s.excl(2)
|
||||||
s.excl(2)
|
s.excl(2)
|
||||||
assert s.len == 3
|
assert s.len == 3
|
||||||
|
|
||||||
discard exclImpl(s, key)
|
discard exclImpl(s, key)
|
||||||
|
|
||||||
proc excl*[A](s: var HashSet[A], other: HashSet[A]) =
|
proc excl*[A](s: var HashSet[A], other: HashSet[A]) =
|
||||||
|
|
@ -453,8 +306,6 @@ proc excl*[A](s: var HashSet[A], other: HashSet[A]) =
|
||||||
assert len(numbers) == 3
|
assert len(numbers) == 3
|
||||||
## numbers == {1, 3, 5}
|
## numbers == {1, 3, 5}
|
||||||
|
|
||||||
assert s.isValid, "The set `s` needs to be initialized."
|
|
||||||
assert other.isValid, "The set `other` needs to be initialized."
|
|
||||||
for item in other: discard exclImpl(s, item)
|
for item in other: discard exclImpl(s, item)
|
||||||
|
|
||||||
proc missingOrExcl*[A](s: var HashSet[A], key: A): bool =
|
proc missingOrExcl*[A](s: var HashSet[A], key: A): bool =
|
||||||
|
|
@ -474,6 +325,7 @@ proc missingOrExcl*[A](s: var HashSet[A], key: A): bool =
|
||||||
assert s.missingOrExcl(4) == true
|
assert s.missingOrExcl(4) == true
|
||||||
assert s.missingOrExcl(6) == false
|
assert s.missingOrExcl(6) == false
|
||||||
assert s.missingOrExcl(6) == true
|
assert s.missingOrExcl(6) == true
|
||||||
|
|
||||||
exclImpl(s, key)
|
exclImpl(s, key)
|
||||||
|
|
||||||
proc pop*[A](s: var HashSet[A]): A =
|
proc pop*[A](s: var HashSet[A]): A =
|
||||||
|
|
@ -489,7 +341,7 @@ proc pop*[A](s: var HashSet[A]): A =
|
||||||
assert s.pop == 2
|
assert s.pop == 2
|
||||||
doAssertRaises(KeyError, echo s.pop)
|
doAssertRaises(KeyError, echo s.pop)
|
||||||
|
|
||||||
for h in 0..high(s.data):
|
for h in 0 .. high(s.data):
|
||||||
if isFilled(s.data[h].hcode):
|
if isFilled(s.data[h].hcode):
|
||||||
result = s.data[h].key
|
result = s.data[h].key
|
||||||
excl(s, result)
|
excl(s, result)
|
||||||
|
|
@ -510,7 +362,7 @@ proc clear*[A](s: var HashSet[A]) =
|
||||||
assert len(s) == 0
|
assert len(s) == 0
|
||||||
|
|
||||||
s.counter = 0
|
s.counter = 0
|
||||||
for i in 0..<s.data.len:
|
for i in 0 ..< s.data.len:
|
||||||
s.data[i].hcode = 0
|
s.data[i].hcode = 0
|
||||||
s.data[i].key = default(type(s.data[i].key))
|
s.data[i].key = default(type(s.data[i].key))
|
||||||
|
|
||||||
|
|
@ -525,6 +377,7 @@ proc len*[A](s: HashSet[A]): int =
|
||||||
assert len(a) == 0
|
assert len(a) == 0
|
||||||
let s = toHashSet([3, 5, 7])
|
let s = toHashSet([3, 5, 7])
|
||||||
assert len(s) == 3
|
assert len(s) == 3
|
||||||
|
|
||||||
result = s.counter
|
result = s.counter
|
||||||
|
|
||||||
proc card*[A](s: HashSet[A]): int =
|
proc card*[A](s: HashSet[A]): int =
|
||||||
|
|
@ -554,8 +407,6 @@ proc union*[A](s1, s2: HashSet[A]): HashSet[A] =
|
||||||
c = union(a, b)
|
c = union(a, b)
|
||||||
assert c == toHashSet(["a", "b", "c"])
|
assert c == toHashSet(["a", "b", "c"])
|
||||||
|
|
||||||
assert s1.isValid, "The set `s1` needs to be initialized."
|
|
||||||
assert s2.isValid, "The set `s2` needs to be initialized."
|
|
||||||
result = s1
|
result = s1
|
||||||
incl(result, s2)
|
incl(result, s2)
|
||||||
|
|
||||||
|
|
@ -579,9 +430,7 @@ proc intersection*[A](s1, s2: HashSet[A]): HashSet[A] =
|
||||||
c = intersection(a, b)
|
c = intersection(a, b)
|
||||||
assert c == toHashSet(["b"])
|
assert c == toHashSet(["b"])
|
||||||
|
|
||||||
assert s1.isValid, "The set `s1` needs to be initialized."
|
result = initHashSet[A](max(min(s1.data.len, s2.data.len), 2))
|
||||||
assert s2.isValid, "The set `s2` needs to be initialized."
|
|
||||||
result = initHashSet[A](min(s1.data.len, s2.data.len))
|
|
||||||
for item in s1:
|
for item in s1:
|
||||||
if item in s2: incl(result, item)
|
if item in s2: incl(result, item)
|
||||||
|
|
||||||
|
|
@ -604,8 +453,6 @@ proc difference*[A](s1, s2: HashSet[A]): HashSet[A] =
|
||||||
c = difference(a, b)
|
c = difference(a, b)
|
||||||
assert c == toHashSet(["a"])
|
assert c == toHashSet(["a"])
|
||||||
|
|
||||||
assert s1.isValid, "The set `s1` needs to be initialized."
|
|
||||||
assert s2.isValid, "The set `s2` needs to be initialized."
|
|
||||||
result = initHashSet[A]()
|
result = initHashSet[A]()
|
||||||
for item in s1:
|
for item in s1:
|
||||||
if not contains(s2, item):
|
if not contains(s2, item):
|
||||||
|
|
@ -631,8 +478,6 @@ proc symmetricDifference*[A](s1, s2: HashSet[A]): HashSet[A] =
|
||||||
c = symmetricDifference(a, b)
|
c = symmetricDifference(a, b)
|
||||||
assert c == toHashSet(["a", "c"])
|
assert c == toHashSet(["a", "c"])
|
||||||
|
|
||||||
assert s1.isValid, "The set `s1` needs to be initialized."
|
|
||||||
assert s2.isValid, "The set `s2` needs to be initialized."
|
|
||||||
result = s1
|
result = s1
|
||||||
for item in s2:
|
for item in s2:
|
||||||
if containsOrIncl(result, item): excl(result, item)
|
if containsOrIncl(result, item): excl(result, item)
|
||||||
|
|
@ -663,8 +508,6 @@ proc disjoint*[A](s1, s2: HashSet[A]): bool =
|
||||||
assert disjoint(a, b) == false
|
assert disjoint(a, b) == false
|
||||||
assert disjoint(a, b - a) == true
|
assert disjoint(a, b - a) == true
|
||||||
|
|
||||||
assert s1.isValid, "The set `s1` needs to be initialized."
|
|
||||||
assert s2.isValid, "The set `s2` needs to be initialized."
|
|
||||||
for item in s1:
|
for item in s1:
|
||||||
if item in s2: return false
|
if item in s2: return false
|
||||||
return true
|
return true
|
||||||
|
|
@ -681,6 +524,7 @@ proc `<`*[A](s, t: HashSet[A]): bool =
|
||||||
c = intersection(a, b)
|
c = intersection(a, b)
|
||||||
assert c < a and c < b
|
assert c < a and c < b
|
||||||
assert(not (a < a))
|
assert(not (a < a))
|
||||||
|
|
||||||
s.counter != t.counter and s <= t
|
s.counter != t.counter and s <= t
|
||||||
|
|
||||||
proc `<=`*[A](s, t: HashSet[A]): bool =
|
proc `<=`*[A](s, t: HashSet[A]): bool =
|
||||||
|
|
@ -711,6 +555,7 @@ proc `==`*[A](s, t: HashSet[A]): bool =
|
||||||
a = toHashSet([1, 2])
|
a = toHashSet([1, 2])
|
||||||
b = toHashSet([2, 1])
|
b = toHashSet([2, 1])
|
||||||
assert a == b
|
assert a == b
|
||||||
|
|
||||||
s.counter == t.counter and s <= t
|
s.counter == t.counter and s <= t
|
||||||
|
|
||||||
proc map*[A, B](data: HashSet[A], op: proc (x: A): B {.closure.}): HashSet[B] =
|
proc map*[A, B](data: HashSet[A], op: proc (x: A): B {.closure.}): HashSet[B] =
|
||||||
|
|
@ -729,8 +574,7 @@ proc map*[A, B](data: HashSet[A], op: proc (x: A): B {.closure.}): HashSet[B] =
|
||||||
|
|
||||||
proc hash*[A](s: HashSet[A]): Hash =
|
proc hash*[A](s: HashSet[A]): Hash =
|
||||||
## Hashing of HashSet.
|
## Hashing of HashSet.
|
||||||
assert s.isValid, "The set needs to be initialized."
|
for h in 0 .. high(s.data):
|
||||||
for h in 0..high(s.data):
|
|
||||||
result = result xor s.data[h].hcode
|
result = result xor s.data[h].hcode
|
||||||
result = !$result
|
result = !$result
|
||||||
|
|
||||||
|
|
@ -747,7 +591,6 @@ proc `$`*[A](s: HashSet[A]): string =
|
||||||
## # --> {2, 4, 5}
|
## # --> {2, 4, 5}
|
||||||
## echo toHashSet(["no", "esc'aping", "is \" provided"])
|
## echo toHashSet(["no", "esc'aping", "is \" provided"])
|
||||||
## # --> {no, esc'aping, is " provided}
|
## # --> {no, esc'aping, is " provided}
|
||||||
assert s.isValid, "The set needs to be initialized."
|
|
||||||
dollarImpl()
|
dollarImpl()
|
||||||
|
|
||||||
proc rightSize*(count: Natural): int {.inline.} =
|
proc rightSize*(count: Natural): int {.inline.} =
|
||||||
|
|
@ -760,8 +603,28 @@ proc rightSize*(count: Natural): int {.inline.} =
|
||||||
result = nextPowerOfTwo(count * 3 div 2 + 4)
|
result = nextPowerOfTwo(count * 3 div 2 + 4)
|
||||||
|
|
||||||
|
|
||||||
|
proc initSet*[A](initialSize = defaultInitialSize): HashSet[A] {.deprecated:
|
||||||
|
"Deprecated since v0.20, use `initHashSet`"} = initHashSet[A](initialSize)
|
||||||
|
## Deprecated since v0.20, use `initHashSet <#initHashSet,int>`_.
|
||||||
|
|
||||||
|
proc toSet*[A](keys: openArray[A]): HashSet[A] {.deprecated:
|
||||||
|
"Deprecated since v0.20, use `toHashSet`"} = toHashSet[A](keys)
|
||||||
|
## Deprecated since v0.20, use `toHashSet <#toHashSet,openArray[A]>`_.
|
||||||
|
|
||||||
|
proc isValid*[A](s: HashSet[A]): bool {.deprecated:
|
||||||
|
"Deprecated since v0.20; sets are initialized by default"} =
|
||||||
|
## **Deprecated since v0.20; sets are initialized by default**
|
||||||
|
##
|
||||||
|
## Returns `true` if the set has been initialized (with `initHashSet proc
|
||||||
|
## <#initHashSet,int>`_ or `init proc <#init,HashSet[A],int>`_).
|
||||||
|
##
|
||||||
|
## **Examples:**
|
||||||
|
##
|
||||||
|
## .. code-block ::
|
||||||
|
## proc savePreferences(options: HashSet[string]) =
|
||||||
|
## assert options.isValid, "Pass an initialized set!"
|
||||||
|
## # Do stuff here, may crash in release builds!
|
||||||
|
result = s.data.len > 0
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -769,89 +632,19 @@ proc rightSize*(count: Natural): int {.inline.} =
|
||||||
# --------------------------- OrderedSet ------------------------------
|
# --------------------------- OrderedSet ------------------------------
|
||||||
# ---------------------------------------------------------------------
|
# ---------------------------------------------------------------------
|
||||||
|
|
||||||
type
|
|
||||||
OrderedKeyValuePair[A] = tuple[
|
|
||||||
hcode: Hash, next: int, key: A]
|
|
||||||
OrderedKeyValuePairSeq[A] = seq[OrderedKeyValuePair[A]]
|
|
||||||
OrderedSet* {.myShallow.} [A] = object ## \
|
|
||||||
## A generic hash set that remembers insertion order.
|
|
||||||
##
|
|
||||||
## Use `init proc <#init,OrderedSet[A],int>`_ or `initOrderedSet proc
|
|
||||||
## <#initOrderedSet,int>`_ before calling other procs on it.
|
|
||||||
data: OrderedKeyValuePairSeq[A]
|
|
||||||
counter, first, last: int
|
|
||||||
|
|
||||||
|
|
||||||
# ---------------------- helpers -----------------------------------
|
|
||||||
|
|
||||||
template forAllOrderedPairs(yieldStmt: untyped) {.dirty.} =
|
template forAllOrderedPairs(yieldStmt: untyped) {.dirty.} =
|
||||||
var h = s.first
|
if s.data.len > 0:
|
||||||
var idx = 0
|
var h = s.first
|
||||||
while h >= 0:
|
var idx = 0
|
||||||
var nxt = s.data[h].next
|
while h >= 0:
|
||||||
if isFilled(s.data[h].hcode):
|
var nxt = s.data[h].next
|
||||||
yieldStmt
|
if isFilled(s.data[h].hcode):
|
||||||
inc(idx)
|
yieldStmt
|
||||||
h = nxt
|
inc(idx)
|
||||||
|
h = nxt
|
||||||
proc rawGetKnownHC[A](s: OrderedSet[A], key: A, hc: Hash): int {.inline.} =
|
|
||||||
rawGetKnownHCImpl()
|
|
||||||
|
|
||||||
proc rawGet[A](s: OrderedSet[A], key: A, hc: var Hash): int {.inline.} =
|
|
||||||
rawGetImpl()
|
|
||||||
|
|
||||||
proc rawInsert[A](s: var OrderedSet[A], data: var OrderedKeyValuePairSeq[A],
|
|
||||||
key: A, hc: Hash, h: Hash) =
|
|
||||||
rawInsertImpl()
|
|
||||||
data[h].next = -1
|
|
||||||
if s.first < 0: s.first = h
|
|
||||||
if s.last >= 0: data[s.last].next = h
|
|
||||||
s.last = h
|
|
||||||
|
|
||||||
proc enlarge[A](s: var OrderedSet[A]) =
|
|
||||||
var n: OrderedKeyValuePairSeq[A]
|
|
||||||
newSeq(n, len(s.data) * growthFactor)
|
|
||||||
var h = s.first
|
|
||||||
s.first = -1
|
|
||||||
s.last = -1
|
|
||||||
swap(s.data, n)
|
|
||||||
while h >= 0:
|
|
||||||
var nxt = n[h].next
|
|
||||||
if isFilled(n[h].hcode):
|
|
||||||
var j = -1 - rawGetKnownHC(s, n[h].key, n[h].hcode)
|
|
||||||
rawInsert(s, s.data, n[h].key, n[h].hcode, j)
|
|
||||||
h = nxt
|
|
||||||
|
|
||||||
proc isValid*[A](s: OrderedSet[A]): bool
|
|
||||||
|
|
||||||
proc exclImpl[A](s: var OrderedSet[A], key: A) : bool {. inline .} =
|
|
||||||
assert s.isValid, "The set needs to be initialized."
|
|
||||||
var n: OrderedKeyValuePairSeq[A]
|
|
||||||
newSeq(n, len(s.data))
|
|
||||||
var h = s.first
|
|
||||||
s.first = -1
|
|
||||||
s.last = -1
|
|
||||||
swap(s.data, n)
|
|
||||||
let hc = genHash(key)
|
|
||||||
result = true
|
|
||||||
while h >= 0:
|
|
||||||
var nxt = n[h].next
|
|
||||||
if isFilled(n[h].hcode):
|
|
||||||
if n[h].hcode == hc and n[h].key == key:
|
|
||||||
dec s.counter
|
|
||||||
result = false
|
|
||||||
else:
|
|
||||||
var j = -1 - rawGetKnownHC(s, n[h].key, n[h].hcode)
|
|
||||||
rawInsert(s, s.data, n[h].key, n[h].hcode, j)
|
|
||||||
h = nxt
|
|
||||||
|
|
||||||
|
|
||||||
|
proc init*[A](s: var OrderedSet[A], initialSize = defaultInitialSize) =
|
||||||
# -----------------------------------------------------------------------
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
proc init*[A](s: var OrderedSet[A], initialSize=64) =
|
|
||||||
## Initializes an ordered hash set.
|
## Initializes an ordered hash set.
|
||||||
##
|
##
|
||||||
## The `initialSize` parameter needs to be a power of two (default: 64).
|
## The `initialSize` parameter needs to be a power of two (default: 64).
|
||||||
|
|
@ -859,9 +652,8 @@ proc init*[A](s: var OrderedSet[A], initialSize=64) =
|
||||||
## `math.nextPowerOfTwo proc <math.html#nextPowerOfTwo>`_ or `rightSize proc
|
## `math.nextPowerOfTwo proc <math.html#nextPowerOfTwo>`_ or `rightSize proc
|
||||||
## <#rightSize,Natural>`_ from this module.
|
## <#rightSize,Natural>`_ from this module.
|
||||||
##
|
##
|
||||||
## All set variables must be initialized before
|
## Starting from Nim v0.20, sets are initialized by default and it is
|
||||||
## use with other procs from this module, with the exception of `isValid proc
|
## not necessary to call this function explicitly.
|
||||||
## <#isValid,HashSet[A]>`_ and `len() <#len,HashSet[A]>`_.
|
|
||||||
##
|
##
|
||||||
## You can call this proc on a previously initialized hash set, which will
|
## You can call this proc on a previously initialized hash set, which will
|
||||||
## discard all its values. This might be more convenient than iterating over
|
## discard all its values. This might be more convenient than iterating over
|
||||||
|
|
@ -876,26 +668,25 @@ proc init*[A](s: var OrderedSet[A], initialSize=64) =
|
||||||
init(a)
|
init(a)
|
||||||
assert a.isValid
|
assert a.isValid
|
||||||
|
|
||||||
assert isPowerOfTwo(initialSize)
|
initImpl(s, initialSize)
|
||||||
s.counter = 0
|
|
||||||
s.first = -1
|
|
||||||
s.last = -1
|
|
||||||
newSeq(s.data, initialSize)
|
|
||||||
|
|
||||||
proc initOrderedSet*[A](initialSize=64): OrderedSet[A] =
|
proc initOrderedSet*[A](initialSize = defaultInitialSize): OrderedSet[A] =
|
||||||
## Wrapper around `init proc <#init,OrderedSet[A],int>`_ for initialization of
|
## Wrapper around `init proc <#init,OrderedSet[A],int>`_ for initialization of
|
||||||
## ordered hash sets.
|
## ordered hash sets.
|
||||||
##
|
##
|
||||||
## Returns an empty ordered hash set you can assign directly in ``var`` blocks
|
## Returns an empty ordered hash set you can assign directly in ``var`` blocks
|
||||||
## in a single line.
|
## in a single line.
|
||||||
##
|
##
|
||||||
|
## Starting from Nim v0.20, sets are initialized by default and it is
|
||||||
|
## not necessary to call this function explicitly.
|
||||||
|
##
|
||||||
## See also:
|
## See also:
|
||||||
## * `toOrderedSet proc <#toOrderedSet,openArray[A]>`_
|
## * `toOrderedSet proc <#toOrderedSet,openArray[A]>`_
|
||||||
runnableExamples:
|
runnableExamples:
|
||||||
var a = initOrderedSet[int]()
|
var a = initOrderedSet[int]()
|
||||||
assert a.isValid
|
|
||||||
a.incl(3)
|
a.incl(3)
|
||||||
assert len(a) == 1
|
assert len(a) == 1
|
||||||
|
|
||||||
result.init(initialSize)
|
result.init(initialSize)
|
||||||
|
|
||||||
proc toOrderedSet*[A](keys: openArray[A]): OrderedSet[A] =
|
proc toOrderedSet*[A](keys: openArray[A]): OrderedSet[A] =
|
||||||
|
|
@ -918,23 +709,6 @@ proc toOrderedSet*[A](keys: openArray[A]): OrderedSet[A] =
|
||||||
result = initOrderedSet[A](rightSize(keys.len))
|
result = initOrderedSet[A](rightSize(keys.len))
|
||||||
for key in items(keys): result.incl(key)
|
for key in items(keys): result.incl(key)
|
||||||
|
|
||||||
proc isValid*[A](s: OrderedSet[A]): bool =
|
|
||||||
## Returns `true` if the set has been initialized (with `initHashSet proc
|
|
||||||
## <#initOrderedSet,int>`_ or `init proc <#init,OrderedSet[A],int>`_).
|
|
||||||
##
|
|
||||||
## Most operations over an uninitialized set will crash at runtime and
|
|
||||||
## `assert <system.html#assert>`_ in debug builds. You can use this proc in
|
|
||||||
## your own procs to verify that sets passed to your procs are correctly
|
|
||||||
## initialized.
|
|
||||||
##
|
|
||||||
## **Examples:**
|
|
||||||
##
|
|
||||||
## .. code-block ::
|
|
||||||
## proc savePreferences(options: OrderedSet[string]) =
|
|
||||||
## assert options.isValid, "Pass an initialized set!"
|
|
||||||
## # Do stuff here, may crash in release builds!
|
|
||||||
result = s.data.len > 0
|
|
||||||
|
|
||||||
proc contains*[A](s: OrderedSet[A], key: A): bool =
|
proc contains*[A](s: OrderedSet[A], key: A): bool =
|
||||||
## Returns true if `key` is in `s`.
|
## Returns true if `key` is in `s`.
|
||||||
##
|
##
|
||||||
|
|
@ -952,7 +726,6 @@ proc contains*[A](s: OrderedSet[A], key: A): bool =
|
||||||
assert values.contains(2)
|
assert values.contains(2)
|
||||||
assert 2 in values
|
assert 2 in values
|
||||||
|
|
||||||
assert s.isValid, "The set needs to be initialized."
|
|
||||||
var hc: Hash
|
var hc: Hash
|
||||||
var index = rawGet(s, key, hc)
|
var index = rawGet(s, key, hc)
|
||||||
result = index >= 0
|
result = index >= 0
|
||||||
|
|
@ -972,7 +745,6 @@ proc incl*[A](s: var OrderedSet[A], key: A) =
|
||||||
values.incl(2)
|
values.incl(2)
|
||||||
assert values.len == 1
|
assert values.len == 1
|
||||||
|
|
||||||
assert s.isValid, "The set needs to be initialized."
|
|
||||||
inclImpl()
|
inclImpl()
|
||||||
|
|
||||||
proc incl*[A](s: var HashSet[A], other: OrderedSet[A]) =
|
proc incl*[A](s: var HashSet[A], other: OrderedSet[A]) =
|
||||||
|
|
@ -988,8 +760,7 @@ proc incl*[A](s: var HashSet[A], other: OrderedSet[A]) =
|
||||||
others = toOrderedSet([3, 4, 5])
|
others = toOrderedSet([3, 4, 5])
|
||||||
values.incl(others)
|
values.incl(others)
|
||||||
assert values.len == 5
|
assert values.len == 5
|
||||||
assert s.isValid, "The set `s` needs to be initialized."
|
|
||||||
assert other.isValid, "The set `other` needs to be initialized."
|
|
||||||
for item in items(other): incl(s, item)
|
for item in items(other): incl(s, item)
|
||||||
|
|
||||||
proc containsOrIncl*[A](s: var OrderedSet[A], key: A): bool =
|
proc containsOrIncl*[A](s: var OrderedSet[A], key: A): bool =
|
||||||
|
|
@ -1009,7 +780,6 @@ proc containsOrIncl*[A](s: var OrderedSet[A], key: A): bool =
|
||||||
assert values.containsOrIncl(2) == true
|
assert values.containsOrIncl(2) == true
|
||||||
assert values.containsOrIncl(3) == false
|
assert values.containsOrIncl(3) == false
|
||||||
|
|
||||||
assert s.isValid, "The set needs to be initialized."
|
|
||||||
containsOrInclImpl()
|
containsOrInclImpl()
|
||||||
|
|
||||||
proc excl*[A](s: var OrderedSet[A], key: A) =
|
proc excl*[A](s: var OrderedSet[A], key: A) =
|
||||||
|
|
@ -1025,6 +795,7 @@ proc excl*[A](s: var OrderedSet[A], key: A) =
|
||||||
s.excl(2)
|
s.excl(2)
|
||||||
s.excl(2)
|
s.excl(2)
|
||||||
assert s.len == 3
|
assert s.len == 3
|
||||||
|
|
||||||
discard exclImpl(s, key)
|
discard exclImpl(s, key)
|
||||||
|
|
||||||
proc missingOrExcl*[A](s: var OrderedSet[A], key: A): bool =
|
proc missingOrExcl*[A](s: var OrderedSet[A], key: A): bool =
|
||||||
|
|
@ -1044,6 +815,7 @@ proc missingOrExcl*[A](s: var OrderedSet[A], key: A): bool =
|
||||||
assert s.missingOrExcl(4) == true
|
assert s.missingOrExcl(4) == true
|
||||||
assert s.missingOrExcl(6) == false
|
assert s.missingOrExcl(6) == false
|
||||||
assert s.missingOrExcl(6) == true
|
assert s.missingOrExcl(6) == true
|
||||||
|
|
||||||
exclImpl(s, key)
|
exclImpl(s, key)
|
||||||
|
|
||||||
proc clear*[A](s: var OrderedSet[A]) =
|
proc clear*[A](s: var OrderedSet[A]) =
|
||||||
|
|
@ -1059,7 +831,7 @@ proc clear*[A](s: var OrderedSet[A]) =
|
||||||
s.counter = 0
|
s.counter = 0
|
||||||
s.first = -1
|
s.first = -1
|
||||||
s.last = -1
|
s.last = -1
|
||||||
for i in 0..<s.data.len:
|
for i in 0 ..< s.data.len:
|
||||||
s.data[i].hcode = 0
|
s.data[i].hcode = 0
|
||||||
s.data[i].next = 0
|
s.data[i].next = 0
|
||||||
s.data[i].key = default(type(s.data[i].key))
|
s.data[i].key = default(type(s.data[i].key))
|
||||||
|
|
@ -1075,6 +847,7 @@ proc len*[A](s: OrderedSet[A]): int {.inline.} =
|
||||||
assert len(a) == 0
|
assert len(a) == 0
|
||||||
let s = toHashSet([3, 5, 7])
|
let s = toHashSet([3, 5, 7])
|
||||||
assert len(s) == 3
|
assert len(s) == 3
|
||||||
|
|
||||||
result = s.counter
|
result = s.counter
|
||||||
|
|
||||||
proc card*[A](s: OrderedSet[A]): int {.inline.} =
|
proc card*[A](s: OrderedSet[A]): int {.inline.} =
|
||||||
|
|
@ -1110,7 +883,6 @@ proc `==`*[A](s, t: OrderedSet[A]): bool =
|
||||||
|
|
||||||
proc hash*[A](s: OrderedSet[A]): Hash =
|
proc hash*[A](s: OrderedSet[A]): Hash =
|
||||||
## Hashing of OrderedSet.
|
## Hashing of OrderedSet.
|
||||||
assert s.isValid, "The set needs to be initialized."
|
|
||||||
forAllOrderedPairs:
|
forAllOrderedPairs:
|
||||||
result = result !& s.data[h].hcode
|
result = result !& s.data[h].hcode
|
||||||
result = !$result
|
result = !$result
|
||||||
|
|
@ -1129,7 +901,6 @@ proc `$`*[A](s: OrderedSet[A]): string =
|
||||||
## # --> {2, 4, 5}
|
## # --> {2, 4, 5}
|
||||||
## echo toOrderedSet(["no", "esc'aping", "is \" provided"])
|
## echo toOrderedSet(["no", "esc'aping", "is \" provided"])
|
||||||
## # --> {no, esc'aping, is " provided}
|
## # --> {no, esc'aping, is " provided}
|
||||||
assert s.isValid, "The set needs to be initialized."
|
|
||||||
dollarImpl()
|
dollarImpl()
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -1152,11 +923,9 @@ iterator items*[A](s: OrderedSet[A]): A =
|
||||||
## # --> Got 5
|
## # --> Got 5
|
||||||
## # --> Got 8
|
## # --> Got 8
|
||||||
## # --> Got 4
|
## # --> Got 4
|
||||||
assert s.isValid, "The set needs to be initialized."
|
|
||||||
forAllOrderedPairs:
|
forAllOrderedPairs:
|
||||||
yield s.data[h].key
|
yield s.data[h].key
|
||||||
|
|
||||||
|
|
||||||
iterator pairs*[A](s: OrderedSet[A]): tuple[a: int, b: A] =
|
iterator pairs*[A](s: OrderedSet[A]): tuple[a: int, b: A] =
|
||||||
## Iterates through (position, value) tuples of OrderedSet `s`.
|
## Iterates through (position, value) tuples of OrderedSet `s`.
|
||||||
runnableExamples:
|
runnableExamples:
|
||||||
|
|
@ -1166,12 +935,27 @@ iterator pairs*[A](s: OrderedSet[A]): tuple[a: int, b: A] =
|
||||||
p.add(x)
|
p.add(x)
|
||||||
assert p == @[(0, 'a'), (1, 'b'), (2, 'r'), (3, 'c'), (4, 'd')]
|
assert p == @[(0, 'a'), (1, 'b'), (2, 'r'), (3, 'c'), (4, 'd')]
|
||||||
|
|
||||||
assert s.isValid, "The set needs to be initialized"
|
|
||||||
forAllOrderedPairs:
|
forAllOrderedPairs:
|
||||||
yield (idx, s.data[h].key)
|
yield (idx, s.data[h].key)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
proc isValid*[A](s: OrderedSet[A]): bool {.deprecated:
|
||||||
|
"Deprecated since v0.20; sets are initialized by default"} =
|
||||||
|
## **Deprecated since v0.20; sets are initialized by default**
|
||||||
|
##
|
||||||
|
## Returns `true` if the set has been initialized (with `initHashSet proc
|
||||||
|
## <#initOrderedSet,int>`_ or `init proc <#init,OrderedSet[A],int>`_).
|
||||||
|
##
|
||||||
|
## **Examples:**
|
||||||
|
##
|
||||||
|
## .. code-block ::
|
||||||
|
## proc savePreferences(options: OrderedSet[string]) =
|
||||||
|
## assert options.isValid, "Pass an initialized set!"
|
||||||
|
## # Do stuff here, may crash in release builds!
|
||||||
|
result = s.data.len > 0
|
||||||
|
|
||||||
|
|
||||||
# -----------------------------------------------------------------------
|
# -----------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -1179,7 +963,7 @@ iterator pairs*[A](s: OrderedSet[A]): tuple[a: int, b: A] =
|
||||||
when isMainModule and not defined(release):
|
when isMainModule and not defined(release):
|
||||||
proc testModule() =
|
proc testModule() =
|
||||||
## Internal micro test to validate docstrings and such.
|
## Internal micro test to validate docstrings and such.
|
||||||
block isValidTest:
|
block isValidTest: # isValid is deprecated
|
||||||
var options: HashSet[string]
|
var options: HashSet[string]
|
||||||
proc savePreferences(options: HashSet[string]) =
|
proc savePreferences(options: HashSet[string]) =
|
||||||
assert options.isValid, "Pass an initialized set!"
|
assert options.isValid, "Pass an initialized set!"
|
||||||
|
|
@ -1280,7 +1064,7 @@ when isMainModule and not defined(release):
|
||||||
var b = a.map(proc (x: int): string = $x)
|
var b = a.map(proc (x: int): string = $x)
|
||||||
assert b == toHashSet(["1", "2", "3"])
|
assert b == toHashSet(["1", "2", "3"])
|
||||||
|
|
||||||
block isValidTest:
|
block isValidTest: # isValid is deprecated
|
||||||
var cards: OrderedSet[string]
|
var cards: OrderedSet[string]
|
||||||
proc saveTarotCards(cards: OrderedSet[string]) =
|
proc saveTarotCards(cards: OrderedSet[string]) =
|
||||||
assert cards.isValid, "Pass an initialized set!"
|
assert cards.isValid, "Pass an initialized set!"
|
||||||
|
|
@ -1393,6 +1177,69 @@ when isMainModule and not defined(release):
|
||||||
bb.incl(y)
|
bb.incl(y)
|
||||||
assert aa == bb
|
assert aa == bb
|
||||||
|
|
||||||
|
block setsWithoutInit:
|
||||||
|
var
|
||||||
|
a: HashSet[int]
|
||||||
|
b: HashSet[int]
|
||||||
|
c: HashSet[int]
|
||||||
|
d: HashSet[int]
|
||||||
|
e: HashSet[int]
|
||||||
|
|
||||||
|
doAssert a.containsOrIncl(3) == false
|
||||||
|
doAssert a.contains(3)
|
||||||
|
doAssert a.len == 1
|
||||||
|
doAssert a.containsOrIncl(3)
|
||||||
|
a.incl(3)
|
||||||
|
doAssert a.len == 1
|
||||||
|
a.incl(6)
|
||||||
|
doAssert a.len == 2
|
||||||
|
|
||||||
|
b.incl(5)
|
||||||
|
doAssert b.len == 1
|
||||||
|
b.excl(5)
|
||||||
|
b.excl(c)
|
||||||
|
doAssert b.missingOrExcl(5)
|
||||||
|
doAssert b.disjoint(c)
|
||||||
|
|
||||||
|
d = b + c
|
||||||
|
doAssert d.len == 0
|
||||||
|
d = b * c
|
||||||
|
doAssert d.len == 0
|
||||||
|
d = b - c
|
||||||
|
doAssert d.len == 0
|
||||||
|
d = b -+- c
|
||||||
|
doAssert d.len == 0
|
||||||
|
|
||||||
|
doAssert (d < e) == false
|
||||||
|
doAssert d <= e
|
||||||
|
doAssert d == e
|
||||||
|
|
||||||
|
block setsWithoutInit:
|
||||||
|
var
|
||||||
|
a: OrderedSet[int]
|
||||||
|
b: OrderedSet[int]
|
||||||
|
c: OrderedSet[int]
|
||||||
|
d: HashSet[int]
|
||||||
|
|
||||||
|
|
||||||
|
doAssert a.containsOrIncl(3) == false
|
||||||
|
doAssert a.contains(3)
|
||||||
|
doAssert a.len == 1
|
||||||
|
doAssert a.containsOrIncl(3)
|
||||||
|
a.incl(3)
|
||||||
|
doAssert a.len == 1
|
||||||
|
a.incl(6)
|
||||||
|
doAssert a.len == 2
|
||||||
|
|
||||||
|
b.incl(5)
|
||||||
|
doAssert b.len == 1
|
||||||
|
doAssert b.missingOrExcl(5) == false
|
||||||
|
doAssert b.missingOrExcl(5)
|
||||||
|
|
||||||
|
doAssert c.missingOrExcl(9)
|
||||||
|
d.incl(c)
|
||||||
|
doAssert d.len == 0
|
||||||
|
|
||||||
when not defined(testing):
|
when not defined(testing):
|
||||||
echo "Micro tests run successfully."
|
echo "Micro tests run successfully."
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -29,6 +29,14 @@ template maxHash(t): untyped = t.dataLen-1
|
||||||
|
|
||||||
include tableimpl
|
include tableimpl
|
||||||
|
|
||||||
|
template st_maybeRehashPutImpl(enlarge) {.dirty.} =
|
||||||
|
if mustRehash(t.dataLen, t.counter):
|
||||||
|
enlarge(t)
|
||||||
|
index = rawGetKnownHC(t, key, hc)
|
||||||
|
index = -1 - index # important to transform for mgetOrPutImpl
|
||||||
|
rawInsert(t, t.data, key, val, hc, index)
|
||||||
|
inc(t.counter)
|
||||||
|
|
||||||
proc enlarge[A, B](t: var SharedTable[A, B]) =
|
proc enlarge[A, B](t: var SharedTable[A, B]) =
|
||||||
let oldSize = t.dataLen
|
let oldSize = t.dataLen
|
||||||
let size = oldSize * growthFactor
|
let size = oldSize * growthFactor
|
||||||
|
|
@ -176,7 +184,7 @@ proc withKey*[A, B](t: var SharedTable[A, B], key: A,
|
||||||
var val: B
|
var val: B
|
||||||
mapper(key, val, pairExists)
|
mapper(key, val, pairExists)
|
||||||
if pairExists:
|
if pairExists:
|
||||||
maybeRehashPutImpl(enlarge)
|
st_maybeRehashPutImpl(enlarge)
|
||||||
|
|
||||||
proc `[]=`*[A, B](t: var SharedTable[A, B], key: A, val: B) =
|
proc `[]=`*[A, B](t: var SharedTable[A, B], key: A, val: B) =
|
||||||
## puts a (key, value)-pair into `t`.
|
## puts a (key, value)-pair into `t`.
|
||||||
|
|
|
||||||
|
|
@ -9,49 +9,7 @@
|
||||||
|
|
||||||
# An ``include`` file for the different table implementations.
|
# An ``include`` file for the different table implementations.
|
||||||
|
|
||||||
# hcode for real keys cannot be zero. hcode==0 signifies an empty slot. These
|
include hashcommon
|
||||||
# two procs retain clarity of that encoding without the space cost of an enum.
|
|
||||||
proc isEmpty(hcode: Hash): bool {.inline.} =
|
|
||||||
result = hcode == 0
|
|
||||||
|
|
||||||
proc isFilled(hcode: Hash): bool {.inline.} =
|
|
||||||
result = hcode != 0
|
|
||||||
|
|
||||||
const
|
|
||||||
growthFactor = 2
|
|
||||||
|
|
||||||
proc mustRehash(length, counter: int): bool {.inline.} =
|
|
||||||
assert(length > counter)
|
|
||||||
result = (length * 2 < counter * 3) or (length - counter < 4)
|
|
||||||
|
|
||||||
proc nextTry(h, maxHash: Hash): Hash {.inline.} =
|
|
||||||
result = (h + 1) and maxHash
|
|
||||||
|
|
||||||
template rawGetKnownHCImpl() {.dirty.} =
|
|
||||||
var h: Hash = hc and maxHash(t) # start with real hash value
|
|
||||||
while isFilled(t.data[h].hcode):
|
|
||||||
# Compare hc THEN key with boolean short circuit. This makes the common case
|
|
||||||
# zero ==key's for missing (e.g.inserts) and exactly one ==key for present.
|
|
||||||
# It does slow down succeeding lookups by one extra Hash cmp&and..usually
|
|
||||||
# just a few clock cycles, generally worth it for any non-integer-like A.
|
|
||||||
if t.data[h].hcode == hc and t.data[h].key == key:
|
|
||||||
return h
|
|
||||||
h = nextTry(h, maxHash(t))
|
|
||||||
result = -1 - h # < 0 => MISSING; insert idx = -1 - result
|
|
||||||
|
|
||||||
template genHashImpl(key, hc: typed) =
|
|
||||||
hc = hash(key)
|
|
||||||
if hc == 0: # This almost never taken branch should be very predictable.
|
|
||||||
hc = 314159265 # Value doesn't matter; Any non-zero favorite is fine.
|
|
||||||
|
|
||||||
template genHash(key: typed): Hash =
|
|
||||||
var res: Hash
|
|
||||||
genHashImpl(key, res)
|
|
||||||
res
|
|
||||||
|
|
||||||
template rawGetImpl() {.dirty.} =
|
|
||||||
genHashImpl(key, hc)
|
|
||||||
rawGetKnownHCImpl()
|
|
||||||
|
|
||||||
template rawGetDeepImpl() {.dirty.} = # Search algo for unconditional add
|
template rawGetDeepImpl() {.dirty.} = # Search algo for unconditional add
|
||||||
genHashImpl(key, hc)
|
genHashImpl(key, hc)
|
||||||
|
|
@ -65,20 +23,16 @@ template rawInsertImpl() {.dirty.} =
|
||||||
data[h].val = val
|
data[h].val = val
|
||||||
data[h].hcode = hc
|
data[h].hcode = hc
|
||||||
|
|
||||||
proc rawGetKnownHC[X, A](t: X, key: A, hc: Hash): int {.inline.} =
|
|
||||||
rawGetKnownHCImpl()
|
|
||||||
|
|
||||||
proc rawGetDeep[X, A](t: X, key: A, hc: var Hash): int {.inline.} =
|
proc rawGetDeep[X, A](t: X, key: A, hc: var Hash): int {.inline.} =
|
||||||
rawGetDeepImpl()
|
rawGetDeepImpl()
|
||||||
|
|
||||||
proc rawGet[X, A](t: X, key: A, hc: var Hash): int {.inline.} =
|
|
||||||
rawGetImpl()
|
|
||||||
|
|
||||||
proc rawInsert[X, A, B](t: var X, data: var KeyValuePairSeq[A, B],
|
proc rawInsert[X, A, B](t: var X, data: var KeyValuePairSeq[A, B],
|
||||||
key: A, val: B, hc: Hash, h: Hash) =
|
key: A, val: B, hc: Hash, h: Hash) =
|
||||||
rawInsertImpl()
|
rawInsertImpl()
|
||||||
|
|
||||||
template addImpl(enlarge) {.dirty.} =
|
template addImpl(enlarge) {.dirty.} =
|
||||||
|
if t.dataLen == 0:
|
||||||
|
initImpl(t, defaultInitialSize)
|
||||||
if mustRehash(t.dataLen, t.counter): enlarge(t)
|
if mustRehash(t.dataLen, t.counter): enlarge(t)
|
||||||
var hc: Hash
|
var hc: Hash
|
||||||
var j = rawGetDeep(t, key, hc)
|
var j = rawGetDeep(t, key, hc)
|
||||||
|
|
@ -86,6 +40,8 @@ template addImpl(enlarge) {.dirty.} =
|
||||||
inc(t.counter)
|
inc(t.counter)
|
||||||
|
|
||||||
template maybeRehashPutImpl(enlarge) {.dirty.} =
|
template maybeRehashPutImpl(enlarge) {.dirty.} =
|
||||||
|
if t.dataLen == 0:
|
||||||
|
initImpl(t, defaultInitialSize)
|
||||||
if mustRehash(t.dataLen, t.counter):
|
if mustRehash(t.dataLen, t.counter):
|
||||||
enlarge(t)
|
enlarge(t)
|
||||||
index = rawGetKnownHC(t, key, hc)
|
index = rawGetKnownHC(t, key, hc)
|
||||||
|
|
@ -94,12 +50,16 @@ template maybeRehashPutImpl(enlarge) {.dirty.} =
|
||||||
inc(t.counter)
|
inc(t.counter)
|
||||||
|
|
||||||
template putImpl(enlarge) {.dirty.} =
|
template putImpl(enlarge) {.dirty.} =
|
||||||
|
if t.dataLen == 0:
|
||||||
|
initImpl(t, defaultInitialSize)
|
||||||
var hc: Hash
|
var hc: Hash
|
||||||
var index = rawGet(t, key, hc)
|
var index = rawGet(t, key, hc)
|
||||||
if index >= 0: t.data[index].val = val
|
if index >= 0: t.data[index].val = val
|
||||||
else: maybeRehashPutImpl(enlarge)
|
else: maybeRehashPutImpl(enlarge)
|
||||||
|
|
||||||
template mgetOrPutImpl(enlarge) {.dirty.} =
|
template mgetOrPutImpl(enlarge) {.dirty.} =
|
||||||
|
if t.dataLen == 0:
|
||||||
|
initImpl(t, defaultInitialSize)
|
||||||
var hc: Hash
|
var hc: Hash
|
||||||
var index = rawGet(t, key, hc)
|
var index = rawGet(t, key, hc)
|
||||||
if index < 0:
|
if index < 0:
|
||||||
|
|
@ -109,6 +69,8 @@ template mgetOrPutImpl(enlarge) {.dirty.} =
|
||||||
result = t.data[index].val
|
result = t.data[index].val
|
||||||
|
|
||||||
template hasKeyOrPutImpl(enlarge) {.dirty.} =
|
template hasKeyOrPutImpl(enlarge) {.dirty.} =
|
||||||
|
if t.dataLen == 0:
|
||||||
|
initImpl(t, defaultInitialSize)
|
||||||
var hc: Hash
|
var hc: Hash
|
||||||
var index = rawGet(t, key, hc)
|
var index = rawGet(t, key, hc)
|
||||||
if index < 0:
|
if index < 0:
|
||||||
|
|
@ -116,11 +78,6 @@ template hasKeyOrPutImpl(enlarge) {.dirty.} =
|
||||||
maybeRehashPutImpl(enlarge)
|
maybeRehashPutImpl(enlarge)
|
||||||
else: result = true
|
else: result = true
|
||||||
|
|
||||||
when not defined(nimHasDefault):
|
|
||||||
template default[T](t: typedesc[T]): T =
|
|
||||||
var v: T
|
|
||||||
v
|
|
||||||
|
|
||||||
template delImplIdx(t, i) =
|
template delImplIdx(t, i) =
|
||||||
let msk = maxHash(t)
|
let msk = maxHash(t)
|
||||||
if i >= 0:
|
if i >= 0:
|
||||||
|
|
@ -150,9 +107,53 @@ template delImpl() {.dirty.} =
|
||||||
delImplIdx(t, i)
|
delImplIdx(t, i)
|
||||||
|
|
||||||
template clearImpl() {.dirty.} =
|
template clearImpl() {.dirty.} =
|
||||||
for i in 0 ..< t.data.len:
|
for i in 0 ..< t.dataLen:
|
||||||
when compiles(t.data[i].hcode): # CountTable records don't contain a hcode
|
when compiles(t.data[i].hcode): # CountTable records don't contain a hcode
|
||||||
t.data[i].hcode = 0
|
t.data[i].hcode = 0
|
||||||
t.data[i].key = default(type(t.data[i].key))
|
t.data[i].key = default(type(t.data[i].key))
|
||||||
t.data[i].val = default(type(t.data[i].val))
|
t.data[i].val = default(type(t.data[i].val))
|
||||||
t.counter = 0
|
t.counter = 0
|
||||||
|
|
||||||
|
template initImpl(result: typed, size: int) =
|
||||||
|
assert isPowerOfTwo(size)
|
||||||
|
result.counter = 0
|
||||||
|
newSeq(result.data, size)
|
||||||
|
|
||||||
|
template insertImpl() = # for CountTable
|
||||||
|
if t.dataLen == 0: initImpl(t, defaultInitialSize)
|
||||||
|
if mustRehash(len(t.data), t.counter): enlarge(t)
|
||||||
|
ctRawInsert(t, t.data, key, val)
|
||||||
|
inc(t.counter)
|
||||||
|
|
||||||
|
template getOrDefaultImpl(t, key): untyped =
|
||||||
|
mixin rawGet
|
||||||
|
var hc: Hash
|
||||||
|
var index = rawGet(t, key, hc)
|
||||||
|
if index >= 0: result = t.data[index].val
|
||||||
|
|
||||||
|
template getOrDefaultImpl(t, key, default: untyped): untyped =
|
||||||
|
mixin rawGet
|
||||||
|
var hc: Hash
|
||||||
|
var index = rawGet(t, key, hc)
|
||||||
|
result = if index >= 0: t.data[index].val else: default
|
||||||
|
|
||||||
|
template dollarImpl(): untyped {.dirty.} =
|
||||||
|
if t.len == 0:
|
||||||
|
result = "{:}"
|
||||||
|
else:
|
||||||
|
result = "{"
|
||||||
|
for key, val in pairs(t):
|
||||||
|
if result.len > 1: result.add(", ")
|
||||||
|
result.addQuoted(key)
|
||||||
|
result.add(": ")
|
||||||
|
result.addQuoted(val)
|
||||||
|
result.add("}")
|
||||||
|
|
||||||
|
template equalsImpl(s, t: typed): typed =
|
||||||
|
if s.counter == t.counter:
|
||||||
|
# different insertion orders mean different 'data' seqs, so we have
|
||||||
|
# to use the slow route here:
|
||||||
|
for key, val in s:
|
||||||
|
if not t.hasKey(key): return false
|
||||||
|
if t.getOrDefault(key) != val: return false
|
||||||
|
return true
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,7 @@
|
||||||
## For consistency with every other data type in Nim these have **value**
|
## For consistency with every other data type in Nim these have **value**
|
||||||
## semantics, this means that ``=`` performs a copy of the hash table.
|
## semantics, this means that ``=`` performs a copy of the hash table.
|
||||||
##
|
##
|
||||||
## For `ref semantics<manual.html#types-ref-and-pointer-types>`_
|
## For `ref semantics<manual.html#types-reference-and-pointer-types>`_
|
||||||
## use their ``Ref`` variants: `TableRef<#TableRef>`_,
|
## use their ``Ref`` variants: `TableRef<#TableRef>`_,
|
||||||
## `OrderedTableRef<#OrderedTableRef>`_, and `CountTableRef<#CountTableRef>`_.
|
## `OrderedTableRef<#OrderedTableRef>`_, and `CountTableRef<#CountTableRef>`_.
|
||||||
##
|
##
|
||||||
|
|
@ -237,9 +237,13 @@ type
|
||||||
## For creating a new empty TableRef, use `newTable proc
|
## For creating a new empty TableRef, use `newTable proc
|
||||||
## <#newTable,int>`_.
|
## <#newTable,int>`_.
|
||||||
|
|
||||||
|
const
|
||||||
|
defaultInitialSize* = 64
|
||||||
|
|
||||||
# ------------------------------ helpers ---------------------------------
|
# ------------------------------ helpers ---------------------------------
|
||||||
|
|
||||||
|
# Do NOT move these to tableimpl.nim, because sharedtables uses that
|
||||||
|
# file and has its own implementation.
|
||||||
template maxHash(t): untyped = high(t.data)
|
template maxHash(t): untyped = high(t.data)
|
||||||
template dataLen(t): untyped = len(t.data)
|
template dataLen(t): untyped = len(t.data)
|
||||||
|
|
||||||
|
|
@ -260,30 +264,6 @@ template get(t, key): untyped =
|
||||||
else:
|
else:
|
||||||
raise newException(KeyError, "key not found")
|
raise newException(KeyError, "key not found")
|
||||||
|
|
||||||
template getOrDefaultImpl(t, key): untyped =
|
|
||||||
mixin rawGet
|
|
||||||
var hc: Hash
|
|
||||||
var index = rawGet(t, key, hc)
|
|
||||||
if index >= 0: result = t.data[index].val
|
|
||||||
|
|
||||||
template getOrDefaultImpl(t, key, default: untyped): untyped =
|
|
||||||
mixin rawGet
|
|
||||||
var hc: Hash
|
|
||||||
var index = rawGet(t, key, hc)
|
|
||||||
result = if index >= 0: t.data[index].val else: default
|
|
||||||
|
|
||||||
template dollarImpl(): untyped {.dirty.} =
|
|
||||||
if t.len == 0:
|
|
||||||
result = "{:}"
|
|
||||||
else:
|
|
||||||
result = "{"
|
|
||||||
for key, val in pairs(t):
|
|
||||||
if result.len > 1: result.add(", ")
|
|
||||||
result.addQuoted(key)
|
|
||||||
result.add(": ")
|
|
||||||
result.addQuoted(val)
|
|
||||||
result.add("}")
|
|
||||||
|
|
||||||
proc enlarge[A, B](t: var Table[A, B]) =
|
proc enlarge[A, B](t: var Table[A, B]) =
|
||||||
var n: KeyValuePairSeq[A, B]
|
var n: KeyValuePairSeq[A, B]
|
||||||
newSeq(n, len(t.data) * growthFactor)
|
newSeq(n, len(t.data) * growthFactor)
|
||||||
|
|
@ -294,16 +274,8 @@ proc enlarge[A, B](t: var Table[A, B]) =
|
||||||
var j: Hash = eh and maxHash(t)
|
var j: Hash = eh and maxHash(t)
|
||||||
while isFilled(t.data[j].hcode):
|
while isFilled(t.data[j].hcode):
|
||||||
j = nextTry(j, maxHash(t))
|
j = nextTry(j, maxHash(t))
|
||||||
rawInsert(t, t.data, n[i].key, n[i].val, eh, j)
|
rawInsert(t, t.data, move n[i].key, move n[i].val, eh, j)
|
||||||
|
|
||||||
template equalsImpl(s, t: typed): typed =
|
|
||||||
if s.counter == t.counter:
|
|
||||||
# different insertion orders mean different 'data' seqs, so we have
|
|
||||||
# to use the slow route here:
|
|
||||||
for key, val in s:
|
|
||||||
if not t.hasKey(key): return false
|
|
||||||
if t.getOrDefault(key) != val: return false
|
|
||||||
return true
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -311,7 +283,7 @@ template equalsImpl(s, t: typed): typed =
|
||||||
# ------------------------------ Table ------------------------------
|
# ------------------------------ Table ------------------------------
|
||||||
# -------------------------------------------------------------------
|
# -------------------------------------------------------------------
|
||||||
|
|
||||||
proc initTable*[A, B](initialSize=64): Table[A, B] =
|
proc initTable*[A, B](initialsize = defaultInitialSize): Table[A, B] =
|
||||||
## Creates a new hash table that is empty.
|
## Creates a new hash table that is empty.
|
||||||
##
|
##
|
||||||
## ``initialSize`` must be a power of two (default: 64).
|
## ``initialSize`` must be a power of two (default: 64).
|
||||||
|
|
@ -320,6 +292,9 @@ proc initTable*[A, B](initialSize=64): Table[A, B] =
|
||||||
## `math module<math.html>`_ or the `rightSize proc<#rightSize,Natural>`_
|
## `math module<math.html>`_ or the `rightSize proc<#rightSize,Natural>`_
|
||||||
## from this module.
|
## from this module.
|
||||||
##
|
##
|
||||||
|
## Starting from Nim v0.20, tables are initialized by default and it is
|
||||||
|
## not necessary to call this function explicitly.
|
||||||
|
##
|
||||||
## See also:
|
## See also:
|
||||||
## * `toTable proc<#toTable,openArray[]>`_
|
## * `toTable proc<#toTable,openArray[]>`_
|
||||||
## * `newTable proc<#newTable,int>`_ for creating a `TableRef`
|
## * `newTable proc<#newTable,int>`_ for creating a `TableRef`
|
||||||
|
|
@ -327,9 +302,7 @@ proc initTable*[A, B](initialSize=64): Table[A, B] =
|
||||||
let
|
let
|
||||||
a = initTable[int, string]()
|
a = initTable[int, string]()
|
||||||
b = initTable[char, seq[int]]()
|
b = initTable[char, seq[int]]()
|
||||||
assert isPowerOfTwo(initialSize)
|
initImpl(result, initialSize)
|
||||||
result.counter = 0
|
|
||||||
newSeq(result.data, initialSize)
|
|
||||||
|
|
||||||
proc toTable*[A, B](pairs: openArray[(A, B)]): Table[A, B] =
|
proc toTable*[A, B](pairs: openArray[(A, B)]): Table[A, B] =
|
||||||
## Creates a new hash table that contains the given ``pairs``.
|
## Creates a new hash table that contains the given ``pairs``.
|
||||||
|
|
@ -343,6 +316,7 @@ proc toTable*[A, B](pairs: openArray[(A, B)]): Table[A, B] =
|
||||||
let a = [('a', 5), ('b', 9)]
|
let a = [('a', 5), ('b', 9)]
|
||||||
let b = toTable(a)
|
let b = toTable(a)
|
||||||
assert b == {'a': 5, 'b': 9}.toTable
|
assert b == {'a': 5, 'b': 9}.toTable
|
||||||
|
|
||||||
result = initTable[A, B](rightSize(pairs.len))
|
result = initTable[A, B](rightSize(pairs.len))
|
||||||
for key, val in items(pairs): result[key] = val
|
for key, val in items(pairs): result[key] = val
|
||||||
|
|
||||||
|
|
@ -398,6 +372,7 @@ proc `[]=`*[A, B](t: var Table[A, B], key: A, val: B) =
|
||||||
a['x'] = 7
|
a['x'] = 7
|
||||||
a['y'] = 33
|
a['y'] = 33
|
||||||
doAssert a == {'x': 7, 'y': 33}.toTable
|
doAssert a == {'x': 7, 'y': 33}.toTable
|
||||||
|
|
||||||
putImpl(enlarge)
|
putImpl(enlarge)
|
||||||
|
|
||||||
proc hasKey*[A, B](t: Table[A, B], key: A): bool =
|
proc hasKey*[A, B](t: Table[A, B], key: A): bool =
|
||||||
|
|
@ -414,6 +389,7 @@ proc hasKey*[A, B](t: Table[A, B], key: A): bool =
|
||||||
let a = {'a': 5, 'b': 9}.toTable
|
let a = {'a': 5, 'b': 9}.toTable
|
||||||
doAssert a.hasKey('a') == true
|
doAssert a.hasKey('a') == true
|
||||||
doAssert a.hasKey('z') == false
|
doAssert a.hasKey('z') == false
|
||||||
|
|
||||||
var hc: Hash
|
var hc: Hash
|
||||||
result = rawGet(t, key, hc) >= 0
|
result = rawGet(t, key, hc) >= 0
|
||||||
|
|
||||||
|
|
@ -424,6 +400,7 @@ proc contains*[A, B](t: Table[A, B], key: A): bool =
|
||||||
let a = {'a': 5, 'b': 9}.toTable
|
let a = {'a': 5, 'b': 9}.toTable
|
||||||
doAssert 'b' in a == true
|
doAssert 'b' in a == true
|
||||||
doAssert a.contains('z') == false
|
doAssert a.contains('z') == false
|
||||||
|
|
||||||
return hasKey[A, B](t, key)
|
return hasKey[A, B](t, key)
|
||||||
|
|
||||||
proc hasKeyOrPut*[A, B](t: var Table[A, B], key: A, val: B): bool =
|
proc hasKeyOrPut*[A, B](t: var Table[A, B], key: A, val: B): bool =
|
||||||
|
|
@ -443,6 +420,7 @@ proc hasKeyOrPut*[A, B](t: var Table[A, B], key: A, val: B): bool =
|
||||||
if a.hasKeyOrPut('z', 50):
|
if a.hasKeyOrPut('z', 50):
|
||||||
a['z'] = 99
|
a['z'] = 99
|
||||||
doAssert a == {'a': 99, 'b': 9, 'z': 50}.toTable
|
doAssert a == {'a': 99, 'b': 9, 'z': 50}.toTable
|
||||||
|
|
||||||
hasKeyOrPutImpl(enlarge)
|
hasKeyOrPutImpl(enlarge)
|
||||||
|
|
||||||
proc getOrDefault*[A, B](t: Table[A, B], key: A): B =
|
proc getOrDefault*[A, B](t: Table[A, B], key: A): B =
|
||||||
|
|
@ -461,6 +439,7 @@ proc getOrDefault*[A, B](t: Table[A, B], key: A): B =
|
||||||
let a = {'a': 5, 'b': 9}.toTable
|
let a = {'a': 5, 'b': 9}.toTable
|
||||||
doAssert a.getOrDefault('a') == 5
|
doAssert a.getOrDefault('a') == 5
|
||||||
doAssert a.getOrDefault('z') == 0
|
doAssert a.getOrDefault('z') == 0
|
||||||
|
|
||||||
getOrDefaultImpl(t, key)
|
getOrDefaultImpl(t, key)
|
||||||
|
|
||||||
proc getOrDefault*[A, B](t: Table[A, B], key: A, default: B): B =
|
proc getOrDefault*[A, B](t: Table[A, B], key: A, default: B): B =
|
||||||
|
|
@ -478,6 +457,7 @@ proc getOrDefault*[A, B](t: Table[A, B], key: A, default: B): B =
|
||||||
let a = {'a': 5, 'b': 9}.toTable
|
let a = {'a': 5, 'b': 9}.toTable
|
||||||
doAssert a.getOrDefault('a', 99) == 5
|
doAssert a.getOrDefault('a', 99) == 5
|
||||||
doAssert a.getOrDefault('z', 99) == 99
|
doAssert a.getOrDefault('z', 99) == 99
|
||||||
|
|
||||||
getOrDefaultImpl(t, key, default)
|
getOrDefaultImpl(t, key, default)
|
||||||
|
|
||||||
proc mgetOrPut*[A, B](t: var Table[A, B], key: A, val: B): var B =
|
proc mgetOrPut*[A, B](t: var Table[A, B], key: A, val: B): var B =
|
||||||
|
|
@ -497,6 +477,7 @@ proc mgetOrPut*[A, B](t: var Table[A, B], key: A, val: B): var B =
|
||||||
doAssert a.mgetOrPut('a', 99) == 5
|
doAssert a.mgetOrPut('a', 99) == 5
|
||||||
doAssert a.mgetOrPut('z', 99) == 99
|
doAssert a.mgetOrPut('z', 99) == 99
|
||||||
doAssert a == {'a': 5, 'b': 9, 'z': 99}.toTable
|
doAssert a == {'a': 5, 'b': 9, 'z': 99}.toTable
|
||||||
|
|
||||||
mgetOrPutImpl(enlarge)
|
mgetOrPutImpl(enlarge)
|
||||||
|
|
||||||
proc len*[A, B](t: Table[A, B]): int =
|
proc len*[A, B](t: Table[A, B]): int =
|
||||||
|
|
@ -504,6 +485,7 @@ proc len*[A, B](t: Table[A, B]): int =
|
||||||
runnableExamples:
|
runnableExamples:
|
||||||
let a = {'a': 5, 'b': 9}.toTable
|
let a = {'a': 5, 'b': 9}.toTable
|
||||||
doAssert len(a) == 2
|
doAssert len(a) == 2
|
||||||
|
|
||||||
result = t.counter
|
result = t.counter
|
||||||
|
|
||||||
proc add*[A, B](t: var Table[A, B], key: A, val: B) =
|
proc add*[A, B](t: var Table[A, B], key: A, val: B) =
|
||||||
|
|
@ -527,6 +509,7 @@ proc del*[A, B](t: var Table[A, B], key: A) =
|
||||||
doAssert a == {'b': 9, 'c': 13}.toTable
|
doAssert a == {'b': 9, 'c': 13}.toTable
|
||||||
a.del('z')
|
a.del('z')
|
||||||
doAssert a == {'b': 9, 'c': 13}.toTable
|
doAssert a == {'b': 9, 'c': 13}.toTable
|
||||||
|
|
||||||
delImpl()
|
delImpl()
|
||||||
|
|
||||||
proc take*[A, B](t: var Table[A, B], key: A, val: var B): bool =
|
proc take*[A, B](t: var Table[A, B], key: A, val: var B): bool =
|
||||||
|
|
@ -568,6 +551,7 @@ proc clear*[A, B](t: var Table[A, B]) =
|
||||||
doAssert len(a) == 3
|
doAssert len(a) == 3
|
||||||
clear(a)
|
clear(a)
|
||||||
doAssert len(a) == 0
|
doAssert len(a) == 0
|
||||||
|
|
||||||
clearImpl()
|
clearImpl()
|
||||||
|
|
||||||
proc `$`*[A, B](t: Table[A, B]): string =
|
proc `$`*[A, B](t: Table[A, B]): string =
|
||||||
|
|
@ -583,6 +567,7 @@ proc `==`*[A, B](s, t: Table[A, B]): bool =
|
||||||
a = {'a': 5, 'b': 9, 'c': 13}.toTable
|
a = {'a': 5, 'b': 9, 'c': 13}.toTable
|
||||||
b = {'b': 9, 'c': 13, 'a': 5}.toTable
|
b = {'b': 9, 'c': 13, 'a': 5}.toTable
|
||||||
doAssert a == b
|
doAssert a == b
|
||||||
|
|
||||||
equalsImpl(s, t)
|
equalsImpl(s, t)
|
||||||
|
|
||||||
proc rightSize*(count: Natural): int {.inline.} =
|
proc rightSize*(count: Natural): int {.inline.} =
|
||||||
|
|
@ -692,6 +677,7 @@ iterator mpairs*[A, B](t: var Table[A, B]): (A, var B) =
|
||||||
for k, v in a.mpairs:
|
for k, v in a.mpairs:
|
||||||
v.add(v[0] + 10)
|
v.add(v[0] + 10)
|
||||||
doAssert a == {'e': @[2, 4, 6, 8, 12], 'o': @[1, 5, 7, 9, 11]}.toTable
|
doAssert a == {'e': @[2, 4, 6, 8, 12], 'o': @[1, 5, 7, 9, 11]}.toTable
|
||||||
|
|
||||||
for h in 0..high(t.data):
|
for h in 0..high(t.data):
|
||||||
if isFilled(t.data[h].hcode): yield (t.data[h].key, t.data[h].val)
|
if isFilled(t.data[h].hcode): yield (t.data[h].key, t.data[h].val)
|
||||||
|
|
||||||
|
|
@ -709,6 +695,7 @@ iterator keys*[A, B](t: Table[A, B]): A =
|
||||||
for k in a.keys:
|
for k in a.keys:
|
||||||
a[k].add(99)
|
a[k].add(99)
|
||||||
doAssert a == {'e': @[2, 4, 6, 8, 99], 'o': @[1, 5, 7, 9, 99]}.toTable
|
doAssert a == {'e': @[2, 4, 6, 8, 99], 'o': @[1, 5, 7, 9, 99]}.toTable
|
||||||
|
|
||||||
for h in 0..high(t.data):
|
for h in 0..high(t.data):
|
||||||
if isFilled(t.data[h].hcode): yield t.data[h].key
|
if isFilled(t.data[h].hcode): yield t.data[h].key
|
||||||
|
|
||||||
|
|
@ -726,6 +713,7 @@ iterator values*[A, B](t: Table[A, B]): B =
|
||||||
}.toTable
|
}.toTable
|
||||||
for v in a.values:
|
for v in a.values:
|
||||||
doAssert v.len == 4
|
doAssert v.len == 4
|
||||||
|
|
||||||
for h in 0..high(t.data):
|
for h in 0..high(t.data):
|
||||||
if isFilled(t.data[h].hcode): yield t.data[h].val
|
if isFilled(t.data[h].hcode): yield t.data[h].val
|
||||||
|
|
||||||
|
|
@ -744,6 +732,7 @@ iterator mvalues*[A, B](t: var Table[A, B]): var B =
|
||||||
for v in a.mvalues:
|
for v in a.mvalues:
|
||||||
v.add(99)
|
v.add(99)
|
||||||
doAssert a == {'e': @[2, 4, 6, 8, 99], 'o': @[1, 5, 7, 9, 99]}.toTable
|
doAssert a == {'e': @[2, 4, 6, 8, 99], 'o': @[1, 5, 7, 9, 99]}.toTable
|
||||||
|
|
||||||
for h in 0..high(t.data):
|
for h in 0..high(t.data):
|
||||||
if isFilled(t.data[h].hcode): yield t.data[h].val
|
if isFilled(t.data[h].hcode): yield t.data[h].val
|
||||||
|
|
||||||
|
|
@ -779,7 +768,7 @@ iterator allValues*[A, B](t: Table[A, B]; key: A): B =
|
||||||
# -------------------------------------------------------------------
|
# -------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
proc newTable*[A, B](initialSize=64): TableRef[A, B] =
|
proc newTable*[A, B](initialsize = defaultInitialSize): <//>TableRef[A, B] =
|
||||||
## Creates a new ref hash table that is empty.
|
## Creates a new ref hash table that is empty.
|
||||||
##
|
##
|
||||||
## ``initialSize`` must be a power of two (default: 64).
|
## ``initialSize`` must be a power of two (default: 64).
|
||||||
|
|
@ -796,10 +785,11 @@ proc newTable*[A, B](initialSize=64): TableRef[A, B] =
|
||||||
let
|
let
|
||||||
a = newTable[int, string]()
|
a = newTable[int, string]()
|
||||||
b = newTable[char, seq[int]]()
|
b = newTable[char, seq[int]]()
|
||||||
|
|
||||||
new(result)
|
new(result)
|
||||||
result[] = initTable[A, B](initialSize)
|
result[] = initTable[A, B](initialSize)
|
||||||
|
|
||||||
proc newTable*[A, B](pairs: openArray[(A, B)]): TableRef[A, B] =
|
proc newTable*[A, B](pairs: openArray[(A, B)]): <//>TableRef[A, B] =
|
||||||
## Creates a new ref hash table that contains the given ``pairs``.
|
## Creates a new ref hash table that contains the given ``pairs``.
|
||||||
##
|
##
|
||||||
## ``pairs`` is a container consisting of ``(key, value)`` tuples.
|
## ``pairs`` is a container consisting of ``(key, value)`` tuples.
|
||||||
|
|
@ -811,10 +801,11 @@ proc newTable*[A, B](pairs: openArray[(A, B)]): TableRef[A, B] =
|
||||||
let a = [('a', 5), ('b', 9)]
|
let a = [('a', 5), ('b', 9)]
|
||||||
let b = newTable(a)
|
let b = newTable(a)
|
||||||
assert b == {'a': 5, 'b': 9}.newTable
|
assert b == {'a': 5, 'b': 9}.newTable
|
||||||
|
|
||||||
new(result)
|
new(result)
|
||||||
result[] = toTable[A, B](pairs)
|
result[] = toTable[A, B](pairs)
|
||||||
|
|
||||||
proc newTableFrom*[A, B, C](collection: A, index: proc(x: B): C): TableRef[C, B] =
|
proc newTableFrom*[A, B, C](collection: A, index: proc(x: B): C): <//>TableRef[C, B] =
|
||||||
## Index the collection with the proc provided.
|
## Index the collection with the proc provided.
|
||||||
# TODO: As soon as supported, change collection: A to collection: A[B]
|
# TODO: As soon as supported, change collection: A to collection: A[B]
|
||||||
result = newTable[C, B]()
|
result = newTable[C, B]()
|
||||||
|
|
@ -842,6 +833,7 @@ proc `[]`*[A, B](t: TableRef[A, B], key: A): var B =
|
||||||
doAssert a['a'] == 5
|
doAssert a['a'] == 5
|
||||||
doAssertRaises(KeyError):
|
doAssertRaises(KeyError):
|
||||||
echo a['z']
|
echo a['z']
|
||||||
|
|
||||||
result = t[][key]
|
result = t[][key]
|
||||||
|
|
||||||
proc `[]=`*[A, B](t: TableRef[A, B], key: A, val: B) =
|
proc `[]=`*[A, B](t: TableRef[A, B], key: A, val: B) =
|
||||||
|
|
@ -857,6 +849,7 @@ proc `[]=`*[A, B](t: TableRef[A, B], key: A, val: B) =
|
||||||
a['x'] = 7
|
a['x'] = 7
|
||||||
a['y'] = 33
|
a['y'] = 33
|
||||||
doAssert a == {'x': 7, 'y': 33}.newTable
|
doAssert a == {'x': 7, 'y': 33}.newTable
|
||||||
|
|
||||||
t[][key] = val
|
t[][key] = val
|
||||||
|
|
||||||
proc hasKey*[A, B](t: TableRef[A, B], key: A): bool =
|
proc hasKey*[A, B](t: TableRef[A, B], key: A): bool =
|
||||||
|
|
@ -874,6 +867,7 @@ proc hasKey*[A, B](t: TableRef[A, B], key: A): bool =
|
||||||
let a = {'a': 5, 'b': 9}.newTable
|
let a = {'a': 5, 'b': 9}.newTable
|
||||||
doAssert a.hasKey('a') == true
|
doAssert a.hasKey('a') == true
|
||||||
doAssert a.hasKey('z') == false
|
doAssert a.hasKey('z') == false
|
||||||
|
|
||||||
result = t[].hasKey(key)
|
result = t[].hasKey(key)
|
||||||
|
|
||||||
proc contains*[A, B](t: TableRef[A, B], key: A): bool =
|
proc contains*[A, B](t: TableRef[A, B], key: A): bool =
|
||||||
|
|
@ -883,6 +877,7 @@ proc contains*[A, B](t: TableRef[A, B], key: A): bool =
|
||||||
let a = {'a': 5, 'b': 9}.newTable
|
let a = {'a': 5, 'b': 9}.newTable
|
||||||
doAssert 'b' in a == true
|
doAssert 'b' in a == true
|
||||||
doAssert a.contains('z') == false
|
doAssert a.contains('z') == false
|
||||||
|
|
||||||
return hasKey[A, B](t, key)
|
return hasKey[A, B](t, key)
|
||||||
|
|
||||||
proc hasKeyOrPut*[A, B](t: var TableRef[A, B], key: A, val: B): bool =
|
proc hasKeyOrPut*[A, B](t: var TableRef[A, B], key: A, val: B): bool =
|
||||||
|
|
@ -902,6 +897,7 @@ proc hasKeyOrPut*[A, B](t: var TableRef[A, B], key: A, val: B): bool =
|
||||||
if a.hasKeyOrPut('z', 50):
|
if a.hasKeyOrPut('z', 50):
|
||||||
a['z'] = 99
|
a['z'] = 99
|
||||||
doAssert a == {'a': 99, 'b': 9, 'z': 50}.newTable
|
doAssert a == {'a': 99, 'b': 9, 'z': 50}.newTable
|
||||||
|
|
||||||
t[].hasKeyOrPut(key, val)
|
t[].hasKeyOrPut(key, val)
|
||||||
|
|
||||||
proc getOrDefault*[A, B](t: TableRef[A, B], key: A): B =
|
proc getOrDefault*[A, B](t: TableRef[A, B], key: A): B =
|
||||||
|
|
@ -920,6 +916,7 @@ proc getOrDefault*[A, B](t: TableRef[A, B], key: A): B =
|
||||||
let a = {'a': 5, 'b': 9}.newTable
|
let a = {'a': 5, 'b': 9}.newTable
|
||||||
doAssert a.getOrDefault('a') == 5
|
doAssert a.getOrDefault('a') == 5
|
||||||
doAssert a.getOrDefault('z') == 0
|
doAssert a.getOrDefault('z') == 0
|
||||||
|
|
||||||
getOrDefault(t[], key)
|
getOrDefault(t[], key)
|
||||||
|
|
||||||
proc getOrDefault*[A, B](t: TableRef[A, B], key: A, default: B): B =
|
proc getOrDefault*[A, B](t: TableRef[A, B], key: A, default: B): B =
|
||||||
|
|
@ -937,6 +934,7 @@ proc getOrDefault*[A, B](t: TableRef[A, B], key: A, default: B): B =
|
||||||
let a = {'a': 5, 'b': 9}.newTable
|
let a = {'a': 5, 'b': 9}.newTable
|
||||||
doAssert a.getOrDefault('a', 99) == 5
|
doAssert a.getOrDefault('a', 99) == 5
|
||||||
doAssert a.getOrDefault('z', 99) == 99
|
doAssert a.getOrDefault('z', 99) == 99
|
||||||
|
|
||||||
getOrDefault(t[], key, default)
|
getOrDefault(t[], key, default)
|
||||||
|
|
||||||
proc mgetOrPut*[A, B](t: TableRef[A, B], key: A, val: B): var B =
|
proc mgetOrPut*[A, B](t: TableRef[A, B], key: A, val: B): var B =
|
||||||
|
|
@ -956,6 +954,7 @@ proc mgetOrPut*[A, B](t: TableRef[A, B], key: A, val: B): var B =
|
||||||
doAssert a.mgetOrPut('a', 99) == 5
|
doAssert a.mgetOrPut('a', 99) == 5
|
||||||
doAssert a.mgetOrPut('z', 99) == 99
|
doAssert a.mgetOrPut('z', 99) == 99
|
||||||
doAssert a == {'a': 5, 'b': 9, 'z': 99}.newTable
|
doAssert a == {'a': 5, 'b': 9, 'z': 99}.newTable
|
||||||
|
|
||||||
t[].mgetOrPut(key, val)
|
t[].mgetOrPut(key, val)
|
||||||
|
|
||||||
proc len*[A, B](t: TableRef[A, B]): int =
|
proc len*[A, B](t: TableRef[A, B]): int =
|
||||||
|
|
@ -963,6 +962,7 @@ proc len*[A, B](t: TableRef[A, B]): int =
|
||||||
runnableExamples:
|
runnableExamples:
|
||||||
let a = {'a': 5, 'b': 9}.newTable
|
let a = {'a': 5, 'b': 9}.newTable
|
||||||
doAssert len(a) == 2
|
doAssert len(a) == 2
|
||||||
|
|
||||||
result = t.counter
|
result = t.counter
|
||||||
|
|
||||||
proc add*[A, B](t: TableRef[A, B], key: A, val: B) =
|
proc add*[A, B](t: TableRef[A, B], key: A, val: B) =
|
||||||
|
|
@ -988,6 +988,7 @@ proc del*[A, B](t: TableRef[A, B], key: A) =
|
||||||
doAssert a == {'b': 9, 'c': 13}.newTable
|
doAssert a == {'b': 9, 'c': 13}.newTable
|
||||||
a.del('z')
|
a.del('z')
|
||||||
doAssert a == {'b': 9, 'c': 13}.newTable
|
doAssert a == {'b': 9, 'c': 13}.newTable
|
||||||
|
|
||||||
t[].del(key)
|
t[].del(key)
|
||||||
|
|
||||||
proc take*[A, B](t: TableRef[A, B], key: A, val: var B): bool =
|
proc take*[A, B](t: TableRef[A, B], key: A, val: var B): bool =
|
||||||
|
|
@ -1012,6 +1013,7 @@ proc take*[A, B](t: TableRef[A, B], key: A, val: var B): bool =
|
||||||
doAssert a.take('z', i) == false
|
doAssert a.take('z', i) == false
|
||||||
doAssert a == {'a': 5, 'c': 13}.newTable
|
doAssert a == {'a': 5, 'c': 13}.newTable
|
||||||
doAssert i == 0
|
doAssert i == 0
|
||||||
|
|
||||||
result = t[].take(key, val)
|
result = t[].take(key, val)
|
||||||
|
|
||||||
proc clear*[A, B](t: TableRef[A, B]) =
|
proc clear*[A, B](t: TableRef[A, B]) =
|
||||||
|
|
@ -1025,6 +1027,7 @@ proc clear*[A, B](t: TableRef[A, B]) =
|
||||||
doAssert len(a) == 3
|
doAssert len(a) == 3
|
||||||
clear(a)
|
clear(a)
|
||||||
doAssert len(a) == 0
|
doAssert len(a) == 0
|
||||||
|
|
||||||
clearImpl()
|
clearImpl()
|
||||||
|
|
||||||
proc `$`*[A, B](t: TableRef[A, B]): string =
|
proc `$`*[A, B](t: TableRef[A, B]): string =
|
||||||
|
|
@ -1041,6 +1044,7 @@ proc `==`*[A, B](s, t: TableRef[A, B]): bool =
|
||||||
a = {'a': 5, 'b': 9, 'c': 13}.newTable
|
a = {'a': 5, 'b': 9, 'c': 13}.newTable
|
||||||
b = {'b': 9, 'c': 13, 'a': 5}.newTable
|
b = {'b': 9, 'c': 13, 'a': 5}.newTable
|
||||||
doAssert a == b
|
doAssert a == b
|
||||||
|
|
||||||
if isNil(s): result = isNil(t)
|
if isNil(s): result = isNil(t)
|
||||||
elif isNil(t): result = false
|
elif isNil(t): result = false
|
||||||
else: equalsImpl(s[], t[])
|
else: equalsImpl(s[], t[])
|
||||||
|
|
@ -1089,6 +1093,7 @@ iterator mpairs*[A, B](t: TableRef[A, B]): (A, var B) =
|
||||||
for k, v in a.mpairs:
|
for k, v in a.mpairs:
|
||||||
v.add(v[0] + 10)
|
v.add(v[0] + 10)
|
||||||
doAssert a == {'e': @[2, 4, 6, 8, 12], 'o': @[1, 5, 7, 9, 11]}.newTable
|
doAssert a == {'e': @[2, 4, 6, 8, 12], 'o': @[1, 5, 7, 9, 11]}.newTable
|
||||||
|
|
||||||
for h in 0..high(t.data):
|
for h in 0..high(t.data):
|
||||||
if isFilled(t.data[h].hcode): yield (t.data[h].key, t.data[h].val)
|
if isFilled(t.data[h].hcode): yield (t.data[h].key, t.data[h].val)
|
||||||
|
|
||||||
|
|
@ -1106,6 +1111,7 @@ iterator keys*[A, B](t: TableRef[A, B]): A =
|
||||||
for k in a.keys:
|
for k in a.keys:
|
||||||
a[k].add(99)
|
a[k].add(99)
|
||||||
doAssert a == {'e': @[2, 4, 6, 8, 99], 'o': @[1, 5, 7, 9, 99]}.newTable
|
doAssert a == {'e': @[2, 4, 6, 8, 99], 'o': @[1, 5, 7, 9, 99]}.newTable
|
||||||
|
|
||||||
for h in 0..high(t.data):
|
for h in 0..high(t.data):
|
||||||
if isFilled(t.data[h].hcode): yield t.data[h].key
|
if isFilled(t.data[h].hcode): yield t.data[h].key
|
||||||
|
|
||||||
|
|
@ -1123,6 +1129,7 @@ iterator values*[A, B](t: TableRef[A, B]): B =
|
||||||
}.newTable
|
}.newTable
|
||||||
for v in a.values:
|
for v in a.values:
|
||||||
doAssert v.len == 4
|
doAssert v.len == 4
|
||||||
|
|
||||||
for h in 0..high(t.data):
|
for h in 0..high(t.data):
|
||||||
if isFilled(t.data[h].hcode): yield t.data[h].val
|
if isFilled(t.data[h].hcode): yield t.data[h].val
|
||||||
|
|
||||||
|
|
@ -1140,6 +1147,7 @@ iterator mvalues*[A, B](t: TableRef[A, B]): var B =
|
||||||
for v in a.mvalues:
|
for v in a.mvalues:
|
||||||
v.add(99)
|
v.add(99)
|
||||||
doAssert a == {'e': @[2, 4, 6, 8, 99], 'o': @[1, 5, 7, 9, 99]}.newTable
|
doAssert a == {'e': @[2, 4, 6, 8, 99], 'o': @[1, 5, 7, 9, 99]}.newTable
|
||||||
|
|
||||||
for h in 0..high(t.data):
|
for h in 0..high(t.data):
|
||||||
if isFilled(t.data[h].hcode): yield t.data[h].val
|
if isFilled(t.data[h].hcode): yield t.data[h].val
|
||||||
|
|
||||||
|
|
@ -1218,7 +1226,7 @@ template forAllOrderedPairs(yieldStmt: untyped): typed {.dirty.} =
|
||||||
|
|
||||||
# ----------------------------------------------------------------------
|
# ----------------------------------------------------------------------
|
||||||
|
|
||||||
proc initOrderedTable*[A, B](initialSize=64): OrderedTable[A, B] =
|
proc initOrderedTable*[A, B](initialsize = defaultInitialSize): OrderedTable[A, B] =
|
||||||
## Creates a new ordered hash table that is empty.
|
## Creates a new ordered hash table that is empty.
|
||||||
##
|
##
|
||||||
## ``initialSize`` must be a power of two (default: 64).
|
## ``initialSize`` must be a power of two (default: 64).
|
||||||
|
|
@ -1227,6 +1235,9 @@ proc initOrderedTable*[A, B](initialSize=64): OrderedTable[A, B] =
|
||||||
## `math module<math.html>`_ or the `rightSize proc<#rightSize,Natural>`_
|
## `math module<math.html>`_ or the `rightSize proc<#rightSize,Natural>`_
|
||||||
## from this module.
|
## from this module.
|
||||||
##
|
##
|
||||||
|
## Starting from Nim v0.20, tables are initialized by default and it is
|
||||||
|
## not necessary to call this function explicitly.
|
||||||
|
##
|
||||||
## See also:
|
## See also:
|
||||||
## * `toOrderedTable proc<#toOrderedTable,openArray[]>`_
|
## * `toOrderedTable proc<#toOrderedTable,openArray[]>`_
|
||||||
## * `newOrderedTable proc<#newOrderedTable,int>`_ for creating an
|
## * `newOrderedTable proc<#newOrderedTable,int>`_ for creating an
|
||||||
|
|
@ -1235,11 +1246,9 @@ proc initOrderedTable*[A, B](initialSize=64): OrderedTable[A, B] =
|
||||||
let
|
let
|
||||||
a = initOrderedTable[int, string]()
|
a = initOrderedTable[int, string]()
|
||||||
b = initOrderedTable[char, seq[int]]()
|
b = initOrderedTable[char, seq[int]]()
|
||||||
assert isPowerOfTwo(initialSize)
|
initImpl(result, initialSize)
|
||||||
result.counter = 0
|
|
||||||
result.first = -1
|
result.first = -1
|
||||||
result.last = -1
|
result.last = -1
|
||||||
newSeq(result.data, initialSize)
|
|
||||||
|
|
||||||
proc toOrderedTable*[A, B](pairs: openArray[(A, B)]): OrderedTable[A, B] =
|
proc toOrderedTable*[A, B](pairs: openArray[(A, B)]): OrderedTable[A, B] =
|
||||||
## Creates a new ordered hash table that contains the given ``pairs``.
|
## Creates a new ordered hash table that contains the given ``pairs``.
|
||||||
|
|
@ -1254,6 +1263,7 @@ proc toOrderedTable*[A, B](pairs: openArray[(A, B)]): OrderedTable[A, B] =
|
||||||
let a = [('a', 5), ('b', 9)]
|
let a = [('a', 5), ('b', 9)]
|
||||||
let b = toOrderedTable(a)
|
let b = toOrderedTable(a)
|
||||||
assert b == {'a': 5, 'b': 9}.toOrderedTable
|
assert b == {'a': 5, 'b': 9}.toOrderedTable
|
||||||
|
|
||||||
result = initOrderedTable[A, B](rightSize(pairs.len))
|
result = initOrderedTable[A, B](rightSize(pairs.len))
|
||||||
for key, val in items(pairs): result[key] = val
|
for key, val in items(pairs): result[key] = val
|
||||||
|
|
||||||
|
|
@ -1278,6 +1288,7 @@ proc `[]`*[A, B](t: OrderedTable[A, B], key: A): B =
|
||||||
doAssert a['a'] == 5
|
doAssert a['a'] == 5
|
||||||
doAssertRaises(KeyError):
|
doAssertRaises(KeyError):
|
||||||
echo a['z']
|
echo a['z']
|
||||||
|
|
||||||
get(t, key)
|
get(t, key)
|
||||||
|
|
||||||
proc `[]`*[A, B](t: var OrderedTable[A, B], key: A): var B=
|
proc `[]`*[A, B](t: var OrderedTable[A, B], key: A): var B=
|
||||||
|
|
@ -1309,6 +1320,7 @@ proc `[]=`*[A, B](t: var OrderedTable[A, B], key: A, val: B) =
|
||||||
a['x'] = 7
|
a['x'] = 7
|
||||||
a['y'] = 33
|
a['y'] = 33
|
||||||
doAssert a == {'x': 7, 'y': 33}.toOrderedTable
|
doAssert a == {'x': 7, 'y': 33}.toOrderedTable
|
||||||
|
|
||||||
putImpl(enlarge)
|
putImpl(enlarge)
|
||||||
|
|
||||||
proc hasKey*[A, B](t: OrderedTable[A, B], key: A): bool =
|
proc hasKey*[A, B](t: OrderedTable[A, B], key: A): bool =
|
||||||
|
|
@ -1326,6 +1338,7 @@ proc hasKey*[A, B](t: OrderedTable[A, B], key: A): bool =
|
||||||
let a = {'a': 5, 'b': 9}.toOrderedTable
|
let a = {'a': 5, 'b': 9}.toOrderedTable
|
||||||
doAssert a.hasKey('a') == true
|
doAssert a.hasKey('a') == true
|
||||||
doAssert a.hasKey('z') == false
|
doAssert a.hasKey('z') == false
|
||||||
|
|
||||||
var hc: Hash
|
var hc: Hash
|
||||||
result = rawGet(t, key, hc) >= 0
|
result = rawGet(t, key, hc) >= 0
|
||||||
|
|
||||||
|
|
@ -1336,6 +1349,7 @@ proc contains*[A, B](t: OrderedTable[A, B], key: A): bool =
|
||||||
let a = {'a': 5, 'b': 9}.toOrderedTable
|
let a = {'a': 5, 'b': 9}.toOrderedTable
|
||||||
doAssert 'b' in a == true
|
doAssert 'b' in a == true
|
||||||
doAssert a.contains('z') == false
|
doAssert a.contains('z') == false
|
||||||
|
|
||||||
return hasKey[A, B](t, key)
|
return hasKey[A, B](t, key)
|
||||||
|
|
||||||
proc hasKeyOrPut*[A, B](t: var OrderedTable[A, B], key: A, val: B): bool =
|
proc hasKeyOrPut*[A, B](t: var OrderedTable[A, B], key: A, val: B): bool =
|
||||||
|
|
@ -1355,6 +1369,7 @@ proc hasKeyOrPut*[A, B](t: var OrderedTable[A, B], key: A, val: B): bool =
|
||||||
if a.hasKeyOrPut('z', 50):
|
if a.hasKeyOrPut('z', 50):
|
||||||
a['z'] = 99
|
a['z'] = 99
|
||||||
doAssert a == {'a': 99, 'b': 9, 'z': 50}.toOrderedTable
|
doAssert a == {'a': 99, 'b': 9, 'z': 50}.toOrderedTable
|
||||||
|
|
||||||
hasKeyOrPutImpl(enlarge)
|
hasKeyOrPutImpl(enlarge)
|
||||||
|
|
||||||
proc getOrDefault*[A, B](t: OrderedTable[A, B], key: A): B =
|
proc getOrDefault*[A, B](t: OrderedTable[A, B], key: A): B =
|
||||||
|
|
@ -1373,6 +1388,7 @@ proc getOrDefault*[A, B](t: OrderedTable[A, B], key: A): B =
|
||||||
let a = {'a': 5, 'b': 9}.toOrderedTable
|
let a = {'a': 5, 'b': 9}.toOrderedTable
|
||||||
doAssert a.getOrDefault('a') == 5
|
doAssert a.getOrDefault('a') == 5
|
||||||
doAssert a.getOrDefault('z') == 0
|
doAssert a.getOrDefault('z') == 0
|
||||||
|
|
||||||
getOrDefaultImpl(t, key)
|
getOrDefaultImpl(t, key)
|
||||||
|
|
||||||
proc getOrDefault*[A, B](t: OrderedTable[A, B], key: A, default: B): B =
|
proc getOrDefault*[A, B](t: OrderedTable[A, B], key: A, default: B): B =
|
||||||
|
|
@ -1390,6 +1406,7 @@ proc getOrDefault*[A, B](t: OrderedTable[A, B], key: A, default: B): B =
|
||||||
let a = {'a': 5, 'b': 9}.toOrderedTable
|
let a = {'a': 5, 'b': 9}.toOrderedTable
|
||||||
doAssert a.getOrDefault('a', 99) == 5
|
doAssert a.getOrDefault('a', 99) == 5
|
||||||
doAssert a.getOrDefault('z', 99) == 99
|
doAssert a.getOrDefault('z', 99) == 99
|
||||||
|
|
||||||
getOrDefaultImpl(t, key, default)
|
getOrDefaultImpl(t, key, default)
|
||||||
|
|
||||||
proc mgetOrPut*[A, B](t: var OrderedTable[A, B], key: A, val: B): var B =
|
proc mgetOrPut*[A, B](t: var OrderedTable[A, B], key: A, val: B): var B =
|
||||||
|
|
@ -1409,6 +1426,7 @@ proc mgetOrPut*[A, B](t: var OrderedTable[A, B], key: A, val: B): var B =
|
||||||
doAssert a.mgetOrPut('a', 99) == 5
|
doAssert a.mgetOrPut('a', 99) == 5
|
||||||
doAssert a.mgetOrPut('z', 99) == 99
|
doAssert a.mgetOrPut('z', 99) == 99
|
||||||
doAssert a == {'a': 5, 'b': 9, 'z': 99}.toOrderedTable
|
doAssert a == {'a': 5, 'b': 9, 'z': 99}.toOrderedTable
|
||||||
|
|
||||||
mgetOrPutImpl(enlarge)
|
mgetOrPutImpl(enlarge)
|
||||||
|
|
||||||
proc len*[A, B](t: OrderedTable[A, B]): int {.inline.} =
|
proc len*[A, B](t: OrderedTable[A, B]): int {.inline.} =
|
||||||
|
|
@ -1416,6 +1434,7 @@ proc len*[A, B](t: OrderedTable[A, B]): int {.inline.} =
|
||||||
runnableExamples:
|
runnableExamples:
|
||||||
let a = {'a': 5, 'b': 9}.toOrderedTable
|
let a = {'a': 5, 'b': 9}.toOrderedTable
|
||||||
doAssert len(a) == 2
|
doAssert len(a) == 2
|
||||||
|
|
||||||
result = t.counter
|
result = t.counter
|
||||||
|
|
||||||
proc add*[A, B](t: var OrderedTable[A, B], key: A, val: B) =
|
proc add*[A, B](t: var OrderedTable[A, B], key: A, val: B) =
|
||||||
|
|
@ -1440,6 +1459,7 @@ proc del*[A, B](t: var OrderedTable[A, B], key: A) =
|
||||||
doAssert a == {'b': 9, 'c': 13}.toOrderedTable
|
doAssert a == {'b': 9, 'c': 13}.toOrderedTable
|
||||||
a.del('z')
|
a.del('z')
|
||||||
doAssert a == {'b': 9, 'c': 13}.toOrderedTable
|
doAssert a == {'b': 9, 'c': 13}.toOrderedTable
|
||||||
|
|
||||||
var n: OrderedKeyValuePairSeq[A, B]
|
var n: OrderedKeyValuePairSeq[A, B]
|
||||||
newSeq(n, len(t.data))
|
newSeq(n, len(t.data))
|
||||||
var h = t.first
|
var h = t.first
|
||||||
|
|
@ -1467,6 +1487,7 @@ proc clear*[A, B](t: var OrderedTable[A, B]) =
|
||||||
doAssert len(a) == 3
|
doAssert len(a) == 3
|
||||||
clear(a)
|
clear(a)
|
||||||
doAssert len(a) == 0
|
doAssert len(a) == 0
|
||||||
|
|
||||||
clearImpl()
|
clearImpl()
|
||||||
t.first = -1
|
t.first = -1
|
||||||
t.last = -1
|
t.last = -1
|
||||||
|
|
@ -1602,6 +1623,7 @@ iterator mpairs*[A, B](t: var OrderedTable[A, B]): (A, var B) =
|
||||||
for k, v in a.mpairs:
|
for k, v in a.mpairs:
|
||||||
v.add(v[0] + 10)
|
v.add(v[0] + 10)
|
||||||
doAssert a == {'o': @[1, 5, 7, 9, 11], 'e': @[2, 4, 6, 8, 12]}.toOrderedTable
|
doAssert a == {'o': @[1, 5, 7, 9, 11], 'e': @[2, 4, 6, 8, 12]}.toOrderedTable
|
||||||
|
|
||||||
forAllOrderedPairs:
|
forAllOrderedPairs:
|
||||||
yield (t.data[h].key, t.data[h].val)
|
yield (t.data[h].key, t.data[h].val)
|
||||||
|
|
||||||
|
|
@ -1619,6 +1641,7 @@ iterator keys*[A, B](t: OrderedTable[A, B]): A =
|
||||||
for k in a.keys:
|
for k in a.keys:
|
||||||
a[k].add(99)
|
a[k].add(99)
|
||||||
doAssert a == {'o': @[1, 5, 7, 9, 99], 'e': @[2, 4, 6, 8, 99]}.toOrderedTable
|
doAssert a == {'o': @[1, 5, 7, 9, 99], 'e': @[2, 4, 6, 8, 99]}.toOrderedTable
|
||||||
|
|
||||||
forAllOrderedPairs:
|
forAllOrderedPairs:
|
||||||
yield t.data[h].key
|
yield t.data[h].key
|
||||||
|
|
||||||
|
|
@ -1636,6 +1659,7 @@ iterator values*[A, B](t: OrderedTable[A, B]): B =
|
||||||
}.toOrderedTable
|
}.toOrderedTable
|
||||||
for v in a.values:
|
for v in a.values:
|
||||||
doAssert v.len == 4
|
doAssert v.len == 4
|
||||||
|
|
||||||
forAllOrderedPairs:
|
forAllOrderedPairs:
|
||||||
yield t.data[h].val
|
yield t.data[h].val
|
||||||
|
|
||||||
|
|
@ -1666,7 +1690,7 @@ iterator mvalues*[A, B](t: var OrderedTable[A, B]): var B =
|
||||||
# --------------------------- OrderedTableRef -------------------------------
|
# --------------------------- OrderedTableRef -------------------------------
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
proc newOrderedTable*[A, B](initialSize=64): OrderedTableRef[A, B] =
|
proc newOrderedTable*[A, B](initialsize = defaultInitialSize): <//>OrderedTableRef[A, B] =
|
||||||
## Creates a new ordered ref hash table that is empty.
|
## Creates a new ordered ref hash table that is empty.
|
||||||
##
|
##
|
||||||
## ``initialSize`` must be a power of two (default: 64).
|
## ``initialSize`` must be a power of two (default: 64).
|
||||||
|
|
@ -1687,7 +1711,7 @@ proc newOrderedTable*[A, B](initialSize=64): OrderedTableRef[A, B] =
|
||||||
new(result)
|
new(result)
|
||||||
result[] = initOrderedTable[A, B](initialSize)
|
result[] = initOrderedTable[A, B](initialSize)
|
||||||
|
|
||||||
proc newOrderedTable*[A, B](pairs: openArray[(A, B)]): OrderedTableRef[A, B] =
|
proc newOrderedTable*[A, B](pairs: openArray[(A, B)]): <//>OrderedTableRef[A, B] =
|
||||||
## Creates a new ordered ref hash table that contains the given ``pairs``.
|
## Creates a new ordered ref hash table that contains the given ``pairs``.
|
||||||
##
|
##
|
||||||
## ``pairs`` is a container consisting of ``(key, value)`` tuples.
|
## ``pairs`` is a container consisting of ``(key, value)`` tuples.
|
||||||
|
|
@ -1700,6 +1724,7 @@ proc newOrderedTable*[A, B](pairs: openArray[(A, B)]): OrderedTableRef[A, B] =
|
||||||
let a = [('a', 5), ('b', 9)]
|
let a = [('a', 5), ('b', 9)]
|
||||||
let b = newOrderedTable(a)
|
let b = newOrderedTable(a)
|
||||||
assert b == {'a': 5, 'b': 9}.newOrderedTable
|
assert b == {'a': 5, 'b': 9}.newOrderedTable
|
||||||
|
|
||||||
result = newOrderedTable[A, B](rightSize(pairs.len))
|
result = newOrderedTable[A, B](rightSize(pairs.len))
|
||||||
for key, val in items(pairs): result.add(key, val)
|
for key, val in items(pairs): result.add(key, val)
|
||||||
|
|
||||||
|
|
@ -1740,6 +1765,7 @@ proc `[]=`*[A, B](t: OrderedTableRef[A, B], key: A, val: B) =
|
||||||
a['x'] = 7
|
a['x'] = 7
|
||||||
a['y'] = 33
|
a['y'] = 33
|
||||||
doAssert a == {'x': 7, 'y': 33}.newOrderedTable
|
doAssert a == {'x': 7, 'y': 33}.newOrderedTable
|
||||||
|
|
||||||
t[][key] = val
|
t[][key] = val
|
||||||
|
|
||||||
proc hasKey*[A, B](t: OrderedTableRef[A, B], key: A): bool =
|
proc hasKey*[A, B](t: OrderedTableRef[A, B], key: A): bool =
|
||||||
|
|
@ -1757,6 +1783,7 @@ proc hasKey*[A, B](t: OrderedTableRef[A, B], key: A): bool =
|
||||||
let a = {'a': 5, 'b': 9}.newOrderedTable
|
let a = {'a': 5, 'b': 9}.newOrderedTable
|
||||||
doAssert a.hasKey('a') == true
|
doAssert a.hasKey('a') == true
|
||||||
doAssert a.hasKey('z') == false
|
doAssert a.hasKey('z') == false
|
||||||
|
|
||||||
result = t[].hasKey(key)
|
result = t[].hasKey(key)
|
||||||
|
|
||||||
proc contains*[A, B](t: OrderedTableRef[A, B], key: A): bool =
|
proc contains*[A, B](t: OrderedTableRef[A, B], key: A): bool =
|
||||||
|
|
@ -1766,6 +1793,7 @@ proc contains*[A, B](t: OrderedTableRef[A, B], key: A): bool =
|
||||||
let a = {'a': 5, 'b': 9}.newOrderedTable
|
let a = {'a': 5, 'b': 9}.newOrderedTable
|
||||||
doAssert 'b' in a == true
|
doAssert 'b' in a == true
|
||||||
doAssert a.contains('z') == false
|
doAssert a.contains('z') == false
|
||||||
|
|
||||||
return hasKey[A, B](t, key)
|
return hasKey[A, B](t, key)
|
||||||
|
|
||||||
proc hasKeyOrPut*[A, B](t: var OrderedTableRef[A, B], key: A, val: B): bool =
|
proc hasKeyOrPut*[A, B](t: var OrderedTableRef[A, B], key: A, val: B): bool =
|
||||||
|
|
@ -1785,6 +1813,7 @@ proc hasKeyOrPut*[A, B](t: var OrderedTableRef[A, B], key: A, val: B): bool =
|
||||||
if a.hasKeyOrPut('z', 50):
|
if a.hasKeyOrPut('z', 50):
|
||||||
a['z'] = 99
|
a['z'] = 99
|
||||||
doAssert a == {'a': 99, 'b': 9, 'z': 50}.newOrderedTable
|
doAssert a == {'a': 99, 'b': 9, 'z': 50}.newOrderedTable
|
||||||
|
|
||||||
result = t[].hasKeyOrPut(key, val)
|
result = t[].hasKeyOrPut(key, val)
|
||||||
|
|
||||||
proc getOrDefault*[A, B](t: OrderedTableRef[A, B], key: A): B =
|
proc getOrDefault*[A, B](t: OrderedTableRef[A, B], key: A): B =
|
||||||
|
|
@ -1803,6 +1832,7 @@ proc getOrDefault*[A, B](t: OrderedTableRef[A, B], key: A): B =
|
||||||
let a = {'a': 5, 'b': 9}.newOrderedTable
|
let a = {'a': 5, 'b': 9}.newOrderedTable
|
||||||
doAssert a.getOrDefault('a') == 5
|
doAssert a.getOrDefault('a') == 5
|
||||||
doAssert a.getOrDefault('z') == 0
|
doAssert a.getOrDefault('z') == 0
|
||||||
|
|
||||||
getOrDefault(t[], key)
|
getOrDefault(t[], key)
|
||||||
|
|
||||||
proc getOrDefault*[A, B](t: OrderedTableRef[A, B], key: A, default: B): B =
|
proc getOrDefault*[A, B](t: OrderedTableRef[A, B], key: A, default: B): B =
|
||||||
|
|
@ -1820,6 +1850,7 @@ proc getOrDefault*[A, B](t: OrderedTableRef[A, B], key: A, default: B): B =
|
||||||
let a = {'a': 5, 'b': 9}.newOrderedTable
|
let a = {'a': 5, 'b': 9}.newOrderedTable
|
||||||
doAssert a.getOrDefault('a', 99) == 5
|
doAssert a.getOrDefault('a', 99) == 5
|
||||||
doAssert a.getOrDefault('z', 99) == 99
|
doAssert a.getOrDefault('z', 99) == 99
|
||||||
|
|
||||||
getOrDefault(t[], key, default)
|
getOrDefault(t[], key, default)
|
||||||
|
|
||||||
proc mgetOrPut*[A, B](t: OrderedTableRef[A, B], key: A, val: B): var B =
|
proc mgetOrPut*[A, B](t: OrderedTableRef[A, B], key: A, val: B): var B =
|
||||||
|
|
@ -1839,6 +1870,7 @@ proc mgetOrPut*[A, B](t: OrderedTableRef[A, B], key: A, val: B): var B =
|
||||||
doAssert a.mgetOrPut('a', 99) == 5
|
doAssert a.mgetOrPut('a', 99) == 5
|
||||||
doAssert a.mgetOrPut('z', 99) == 99
|
doAssert a.mgetOrPut('z', 99) == 99
|
||||||
doAssert a == {'a': 5, 'b': 9, 'z': 99}.newOrderedTable
|
doAssert a == {'a': 5, 'b': 9, 'z': 99}.newOrderedTable
|
||||||
|
|
||||||
result = t[].mgetOrPut(key, val)
|
result = t[].mgetOrPut(key, val)
|
||||||
|
|
||||||
proc len*[A, B](t: OrderedTableRef[A, B]): int {.inline.} =
|
proc len*[A, B](t: OrderedTableRef[A, B]): int {.inline.} =
|
||||||
|
|
@ -1846,6 +1878,7 @@ proc len*[A, B](t: OrderedTableRef[A, B]): int {.inline.} =
|
||||||
runnableExamples:
|
runnableExamples:
|
||||||
let a = {'a': 5, 'b': 9}.newOrderedTable
|
let a = {'a': 5, 'b': 9}.newOrderedTable
|
||||||
doAssert len(a) == 2
|
doAssert len(a) == 2
|
||||||
|
|
||||||
result = t.counter
|
result = t.counter
|
||||||
|
|
||||||
proc add*[A, B](t: OrderedTableRef[A, B], key: A, val: B) =
|
proc add*[A, B](t: OrderedTableRef[A, B], key: A, val: B) =
|
||||||
|
|
@ -1868,6 +1901,7 @@ proc del*[A, B](t: var OrderedTableRef[A, B], key: A) =
|
||||||
doAssert a == {'b': 9, 'c': 13}.newOrderedTable
|
doAssert a == {'b': 9, 'c': 13}.newOrderedTable
|
||||||
a.del('z')
|
a.del('z')
|
||||||
doAssert a == {'b': 9, 'c': 13}.newOrderedTable
|
doAssert a == {'b': 9, 'c': 13}.newOrderedTable
|
||||||
|
|
||||||
t[].del(key)
|
t[].del(key)
|
||||||
|
|
||||||
proc clear*[A, B](t: var OrderedTableRef[A, B]) =
|
proc clear*[A, B](t: var OrderedTableRef[A, B]) =
|
||||||
|
|
@ -1880,6 +1914,7 @@ proc clear*[A, B](t: var OrderedTableRef[A, B]) =
|
||||||
doAssert len(a) == 3
|
doAssert len(a) == 3
|
||||||
clear(a)
|
clear(a)
|
||||||
doAssert len(a) == 0
|
doAssert len(a) == 0
|
||||||
|
|
||||||
clear(t[])
|
clear(t[])
|
||||||
|
|
||||||
proc sort*[A, B](t: OrderedTableRef[A, B], cmp: proc (x,y: (A, B)): int, order = SortOrder.Ascending) =
|
proc sort*[A, B](t: OrderedTableRef[A, B], cmp: proc (x,y: (A, B)): int, order = SortOrder.Ascending) =
|
||||||
|
|
@ -1899,6 +1934,7 @@ proc sort*[A, B](t: OrderedTableRef[A, B], cmp: proc (x,y: (A, B)): int, order =
|
||||||
doAssert a == {'a': 10, 'b': 20, 'c': 0}.newOrderedTable
|
doAssert a == {'a': 10, 'b': 20, 'c': 0}.newOrderedTable
|
||||||
a.sort(system.cmp, order=SortOrder.Descending)
|
a.sort(system.cmp, order=SortOrder.Descending)
|
||||||
doAssert a == {'c': 0, 'b': 20, 'a': 10}.newOrderedTable
|
doAssert a == {'c': 0, 'b': 20, 'a': 10}.newOrderedTable
|
||||||
|
|
||||||
t[].sort(cmp, order=order)
|
t[].sort(cmp, order=order)
|
||||||
|
|
||||||
proc `$`*[A, B](t: OrderedTableRef[A, B]): string =
|
proc `$`*[A, B](t: OrderedTableRef[A, B]): string =
|
||||||
|
|
@ -1915,6 +1951,7 @@ proc `==`*[A, B](s, t: OrderedTableRef[A, B]): bool =
|
||||||
a = {'a': 5, 'b': 9, 'c': 13}.newOrderedTable
|
a = {'a': 5, 'b': 9, 'c': 13}.newOrderedTable
|
||||||
b = {'b': 9, 'c': 13, 'a': 5}.newOrderedTable
|
b = {'b': 9, 'c': 13, 'a': 5}.newOrderedTable
|
||||||
doAssert a != b
|
doAssert a != b
|
||||||
|
|
||||||
if isNil(s): result = isNil(t)
|
if isNil(s): result = isNil(t)
|
||||||
elif isNil(t): result = false
|
elif isNil(t): result = false
|
||||||
else: result = s[] == t[]
|
else: result = s[] == t[]
|
||||||
|
|
@ -1964,6 +2001,7 @@ iterator mpairs*[A, B](t: OrderedTableRef[A, B]): (A, var B) =
|
||||||
for k, v in a.mpairs:
|
for k, v in a.mpairs:
|
||||||
v.add(v[0] + 10)
|
v.add(v[0] + 10)
|
||||||
doAssert a == {'o': @[1, 5, 7, 9, 11], 'e': @[2, 4, 6, 8, 12]}.newOrderedTable
|
doAssert a == {'o': @[1, 5, 7, 9, 11], 'e': @[2, 4, 6, 8, 12]}.newOrderedTable
|
||||||
|
|
||||||
forAllOrderedPairs:
|
forAllOrderedPairs:
|
||||||
yield (t.data[h].key, t.data[h].val)
|
yield (t.data[h].key, t.data[h].val)
|
||||||
|
|
||||||
|
|
@ -1981,6 +2019,7 @@ iterator keys*[A, B](t: OrderedTableRef[A, B]): A =
|
||||||
for k in a.keys:
|
for k in a.keys:
|
||||||
a[k].add(99)
|
a[k].add(99)
|
||||||
doAssert a == {'o': @[1, 5, 7, 9, 99], 'e': @[2, 4, 6, 8, 99]}.newOrderedTable
|
doAssert a == {'o': @[1, 5, 7, 9, 99], 'e': @[2, 4, 6, 8, 99]}.newOrderedTable
|
||||||
|
|
||||||
forAllOrderedPairs:
|
forAllOrderedPairs:
|
||||||
yield t.data[h].key
|
yield t.data[h].key
|
||||||
|
|
||||||
|
|
@ -1998,6 +2037,7 @@ iterator values*[A, B](t: OrderedTableRef[A, B]): B =
|
||||||
}.newOrderedTable
|
}.newOrderedTable
|
||||||
for v in a.values:
|
for v in a.values:
|
||||||
doAssert v.len == 4
|
doAssert v.len == 4
|
||||||
|
|
||||||
forAllOrderedPairs:
|
forAllOrderedPairs:
|
||||||
yield t.data[h].val
|
yield t.data[h].val
|
||||||
|
|
||||||
|
|
@ -2016,6 +2056,7 @@ iterator mvalues*[A, B](t: OrderedTableRef[A, B]): var B =
|
||||||
for v in a.mvalues:
|
for v in a.mvalues:
|
||||||
v.add(99)
|
v.add(99)
|
||||||
doAssert a == {'o': @[1, 5, 7, 9, 99], 'e': @[2, 4, 6, 8, 99]}.newOrderedTable
|
doAssert a == {'o': @[1, 5, 7, 9, 99], 'e': @[2, 4, 6, 8, 99]}.newOrderedTable
|
||||||
|
|
||||||
forAllOrderedPairs:
|
forAllOrderedPairs:
|
||||||
yield t.data[h].val
|
yield t.data[h].val
|
||||||
|
|
||||||
|
|
@ -2046,7 +2087,7 @@ type
|
||||||
|
|
||||||
# ------------------------------ helpers ---------------------------------
|
# ------------------------------ helpers ---------------------------------
|
||||||
|
|
||||||
proc rawInsert[A](t: CountTable[A], data: var seq[tuple[key: A, val: int]],
|
proc ctRawInsert[A](t: CountTable[A], data: var seq[tuple[key: A, val: int]],
|
||||||
key: A, val: int) =
|
key: A, val: int) =
|
||||||
var h: Hash = hash(key) and high(data)
|
var h: Hash = hash(key) and high(data)
|
||||||
while data[h].val != 0: h = nextTry(h, high(data))
|
while data[h].val != 0: h = nextTry(h, high(data))
|
||||||
|
|
@ -2057,10 +2098,12 @@ proc enlarge[A](t: var CountTable[A]) =
|
||||||
var n: seq[tuple[key: A, val: int]]
|
var n: seq[tuple[key: A, val: int]]
|
||||||
newSeq(n, len(t.data) * growthFactor)
|
newSeq(n, len(t.data) * growthFactor)
|
||||||
for i in countup(0, high(t.data)):
|
for i in countup(0, high(t.data)):
|
||||||
if t.data[i].val != 0: rawInsert(t, n, t.data[i].key, t.data[i].val)
|
if t.data[i].val != 0: ctRawInsert(t, n, t.data[i].key, t.data[i].val)
|
||||||
swap(t.data, n)
|
swap(t.data, n)
|
||||||
|
|
||||||
proc rawGet[A](t: CountTable[A], key: A): int =
|
proc rawGet[A](t: CountTable[A], key: A): int =
|
||||||
|
if t.data.len == 0:
|
||||||
|
return -1
|
||||||
var h: Hash = hash(key) and high(t.data) # start with real hash value
|
var h: Hash = hash(key) and high(t.data) # start with real hash value
|
||||||
while t.data[h].val != 0:
|
while t.data[h].val != 0:
|
||||||
if t.data[h].key == key: return h
|
if t.data[h].key == key: return h
|
||||||
|
|
@ -2075,7 +2118,7 @@ proc inc*[A](t: var CountTable[A], key: A, val = 1)
|
||||||
|
|
||||||
# ----------------------------------------------------------------------
|
# ----------------------------------------------------------------------
|
||||||
|
|
||||||
proc initCountTable*[A](initialSize=64): CountTable[A] =
|
proc initCountTable*[A](initialsize = defaultInitialSize): CountTable[A] =
|
||||||
## Creates a new count table that is empty.
|
## Creates a new count table that is empty.
|
||||||
##
|
##
|
||||||
## ``initialSize`` must be a power of two (default: 64).
|
## ``initialSize`` must be a power of two (default: 64).
|
||||||
|
|
@ -2084,13 +2127,14 @@ proc initCountTable*[A](initialSize=64): CountTable[A] =
|
||||||
## `math module<math.html>`_ or the `rightSize proc<#rightSize,Natural>`_
|
## `math module<math.html>`_ or the `rightSize proc<#rightSize,Natural>`_
|
||||||
## from this module.
|
## from this module.
|
||||||
##
|
##
|
||||||
|
## Starting from Nim v0.20, tables are initialized by default and it is
|
||||||
|
## not necessary to call this function explicitly.
|
||||||
|
##
|
||||||
## See also:
|
## See also:
|
||||||
## * `toCountTable proc<#toCountTable,openArray[A]>`_
|
## * `toCountTable proc<#toCountTable,openArray[A]>`_
|
||||||
## * `newCountTable proc<#newCountTable,int>`_ for creating a
|
## * `newCountTable proc<#newCountTable,int>`_ for creating a
|
||||||
## `CountTableRef`
|
## `CountTableRef`
|
||||||
assert isPowerOfTwo(initialSize)
|
initImpl(result, initialSize)
|
||||||
result.counter = 0
|
|
||||||
newSeq(result.data, initialSize)
|
|
||||||
|
|
||||||
proc toCountTable*[A](keys: openArray[A]): CountTable[A] =
|
proc toCountTable*[A](keys: openArray[A]): CountTable[A] =
|
||||||
## Creates a new count table with every member of a container ``keys``
|
## Creates a new count table with every member of a container ``keys``
|
||||||
|
|
@ -2130,9 +2174,7 @@ proc `[]=`*[A](t: var CountTable[A], key: A, val: int) =
|
||||||
if h >= 0:
|
if h >= 0:
|
||||||
t.data[h].val = val
|
t.data[h].val = val
|
||||||
else:
|
else:
|
||||||
if mustRehash(len(t.data), t.counter): enlarge(t)
|
insertImpl()
|
||||||
rawInsert(t, t.data, key, val)
|
|
||||||
inc(t.counter)
|
|
||||||
|
|
||||||
proc inc*[A](t: var CountTable[A], key: A, val = 1) =
|
proc inc*[A](t: var CountTable[A], key: A, val = 1) =
|
||||||
## Increments ``t[key]`` by ``val`` (default: 1).
|
## Increments ``t[key]`` by ``val`` (default: 1).
|
||||||
|
|
@ -2141,14 +2183,13 @@ proc inc*[A](t: var CountTable[A], key: A, val = 1) =
|
||||||
a.inc('a')
|
a.inc('a')
|
||||||
a.inc('b', 10)
|
a.inc('b', 10)
|
||||||
doAssert a == toCountTable("aaabbbbbbbbbbb")
|
doAssert a == toCountTable("aaabbbbbbbbbbb")
|
||||||
|
|
||||||
var index = rawGet(t, key)
|
var index = rawGet(t, key)
|
||||||
if index >= 0:
|
if index >= 0:
|
||||||
inc(t.data[index].val, val)
|
inc(t.data[index].val, val)
|
||||||
if t.data[index].val == 0: dec(t.counter)
|
if t.data[index].val == 0: dec(t.counter)
|
||||||
else:
|
else:
|
||||||
if mustRehash(len(t.data), t.counter): enlarge(t)
|
insertImpl()
|
||||||
rawInsert(t, t.data, key, val)
|
|
||||||
inc(t.counter)
|
|
||||||
|
|
||||||
proc smallest*[A](t: CountTable[A]): tuple[key: A, val: int] =
|
proc smallest*[A](t: CountTable[A]): tuple[key: A, val: int] =
|
||||||
## Returns the ``(key, value)`` pair with the smallest ``val``. Efficiency: O(n)
|
## Returns the ``(key, value)`` pair with the smallest ``val``. Efficiency: O(n)
|
||||||
|
|
@ -2229,6 +2270,7 @@ proc sort*[A](t: var CountTable[A], order = SortOrder.Descending) =
|
||||||
doAssert toSeq(a.values) == @[5, 2, 2, 1, 1]
|
doAssert toSeq(a.values) == @[5, 2, 2, 1, 1]
|
||||||
a.sort(SortOrder.Ascending)
|
a.sort(SortOrder.Ascending)
|
||||||
doAssert toSeq(a.values) == @[1, 1, 2, 2, 5]
|
doAssert toSeq(a.values) == @[1, 1, 2, 2, 5]
|
||||||
|
|
||||||
t.data.sort(cmp=ctCmp, order=order)
|
t.data.sort(cmp=ctCmp, order=order)
|
||||||
|
|
||||||
proc merge*[A](s: var CountTable[A], t: CountTable[A]) =
|
proc merge*[A](s: var CountTable[A], t: CountTable[A]) =
|
||||||
|
|
@ -2238,6 +2280,7 @@ proc merge*[A](s: var CountTable[A], t: CountTable[A]) =
|
||||||
let b = toCountTable("bcc")
|
let b = toCountTable("bcc")
|
||||||
a.merge(b)
|
a.merge(b)
|
||||||
doAssert a == toCountTable("aaabbbccc")
|
doAssert a == toCountTable("aaabbbccc")
|
||||||
|
|
||||||
for key, value in t:
|
for key, value in t:
|
||||||
s.inc(key, value)
|
s.inc(key, value)
|
||||||
|
|
||||||
|
|
@ -2248,6 +2291,7 @@ proc merge*[A](s, t: CountTable[A]): CountTable[A] =
|
||||||
a = toCountTable("aaabbc")
|
a = toCountTable("aaabbc")
|
||||||
b = toCountTable("bcc")
|
b = toCountTable("bcc")
|
||||||
doAssert merge(a, b) == toCountTable("aaabbbccc")
|
doAssert merge(a, b) == toCountTable("aaabbbccc")
|
||||||
|
|
||||||
result = initCountTable[A](nextPowerOfTwo(max(s.len, t.len)))
|
result = initCountTable[A](nextPowerOfTwo(max(s.len, t.len)))
|
||||||
for table in @[s, t]:
|
for table in @[s, t]:
|
||||||
for key, value in table:
|
for key, value in table:
|
||||||
|
|
@ -2306,6 +2350,7 @@ iterator mpairs*[A](t: var CountTable[A]): (A, var int) =
|
||||||
for k, v in mpairs(a):
|
for k, v in mpairs(a):
|
||||||
v = 2
|
v = 2
|
||||||
doAssert a == toCountTable("aabbccddrr")
|
doAssert a == toCountTable("aabbccddrr")
|
||||||
|
|
||||||
for h in 0..high(t.data):
|
for h in 0..high(t.data):
|
||||||
if t.data[h].val != 0: yield (t.data[h].key, t.data[h].val)
|
if t.data[h].val != 0: yield (t.data[h].key, t.data[h].val)
|
||||||
|
|
||||||
|
|
@ -2320,6 +2365,7 @@ iterator keys*[A](t: CountTable[A]): A =
|
||||||
for k in keys(a):
|
for k in keys(a):
|
||||||
a[k] = 2
|
a[k] = 2
|
||||||
doAssert a == toCountTable("aabbccddrr")
|
doAssert a == toCountTable("aabbccddrr")
|
||||||
|
|
||||||
for h in 0..high(t.data):
|
for h in 0..high(t.data):
|
||||||
if t.data[h].val != 0: yield t.data[h].key
|
if t.data[h].val != 0: yield t.data[h].key
|
||||||
|
|
||||||
|
|
@ -2334,6 +2380,7 @@ iterator values*[A](t: CountTable[A]): int =
|
||||||
let a = toCountTable("abracadabra")
|
let a = toCountTable("abracadabra")
|
||||||
for v in values(a):
|
for v in values(a):
|
||||||
assert v < 10
|
assert v < 10
|
||||||
|
|
||||||
for h in 0..high(t.data):
|
for h in 0..high(t.data):
|
||||||
if t.data[h].val != 0: yield t.data[h].val
|
if t.data[h].val != 0: yield t.data[h].val
|
||||||
|
|
||||||
|
|
@ -2349,6 +2396,7 @@ iterator mvalues*[A](t: var CountTable[A]): var int =
|
||||||
for v in mvalues(a):
|
for v in mvalues(a):
|
||||||
v = 2
|
v = 2
|
||||||
doAssert a == toCountTable("aabbccddrr")
|
doAssert a == toCountTable("aabbccddrr")
|
||||||
|
|
||||||
for h in 0..high(t.data):
|
for h in 0..high(t.data):
|
||||||
if t.data[h].val != 0: yield t.data[h].val
|
if t.data[h].val != 0: yield t.data[h].val
|
||||||
|
|
||||||
|
|
@ -2364,7 +2412,7 @@ iterator mvalues*[A](t: var CountTable[A]): var int =
|
||||||
|
|
||||||
proc inc*[A](t: CountTableRef[A], key: A, val = 1)
|
proc inc*[A](t: CountTableRef[A], key: A, val = 1)
|
||||||
|
|
||||||
proc newCountTable*[A](initialSize=64): CountTableRef[A] =
|
proc newCountTable*[A](initialsize = defaultInitialSize): <//>CountTableRef[A] =
|
||||||
## Creates a new ref count table that is empty.
|
## Creates a new ref count table that is empty.
|
||||||
##
|
##
|
||||||
## ``initialSize`` must be a power of two (default: 64).
|
## ``initialSize`` must be a power of two (default: 64).
|
||||||
|
|
@ -2381,7 +2429,7 @@ proc newCountTable*[A](initialSize=64): CountTableRef[A] =
|
||||||
new(result)
|
new(result)
|
||||||
result[] = initCountTable[A](initialSize)
|
result[] = initCountTable[A](initialSize)
|
||||||
|
|
||||||
proc newCountTable*[A](keys: openArray[A]): CountTableRef[A] =
|
proc newCountTable*[A](keys: openArray[A]): <//>CountTableRef[A] =
|
||||||
## Creates a new ref count table with every member of a container ``keys``
|
## Creates a new ref count table with every member of a container ``keys``
|
||||||
## having a count of how many times it occurs in that container.
|
## having a count of how many times it occurs in that container.
|
||||||
result = newCountTable[A](rightSize(keys.len))
|
result = newCountTable[A](rightSize(keys.len))
|
||||||
|
|
@ -2493,6 +2541,7 @@ proc merge*[A](s, t: CountTableRef[A]) =
|
||||||
b = newCountTable("bcc")
|
b = newCountTable("bcc")
|
||||||
a.merge(b)
|
a.merge(b)
|
||||||
doAssert a == newCountTable("aaabbbccc")
|
doAssert a == newCountTable("aaabbbccc")
|
||||||
|
|
||||||
s[].merge(t[])
|
s[].merge(t[])
|
||||||
|
|
||||||
proc `$`*[A](t: CountTableRef[A]): string =
|
proc `$`*[A](t: CountTableRef[A]): string =
|
||||||
|
|
@ -2551,6 +2600,7 @@ iterator mpairs*[A](t: CountTableRef[A]): (A, var int) =
|
||||||
for k, v in mpairs(a):
|
for k, v in mpairs(a):
|
||||||
v = 2
|
v = 2
|
||||||
doAssert a == newCountTable("aabbccddrr")
|
doAssert a == newCountTable("aabbccddrr")
|
||||||
|
|
||||||
for h in 0..high(t.data):
|
for h in 0..high(t.data):
|
||||||
if t.data[h].val != 0: yield (t.data[h].key, t.data[h].val)
|
if t.data[h].val != 0: yield (t.data[h].key, t.data[h].val)
|
||||||
|
|
||||||
|
|
@ -2565,6 +2615,7 @@ iterator keys*[A](t: CountTableRef[A]): A =
|
||||||
for k in keys(a):
|
for k in keys(a):
|
||||||
a[k] = 2
|
a[k] = 2
|
||||||
doAssert a == newCountTable("aabbccddrr")
|
doAssert a == newCountTable("aabbccddrr")
|
||||||
|
|
||||||
for h in 0..high(t.data):
|
for h in 0..high(t.data):
|
||||||
if t.data[h].val != 0: yield t.data[h].key
|
if t.data[h].val != 0: yield t.data[h].key
|
||||||
|
|
||||||
|
|
@ -2579,6 +2630,7 @@ iterator values*[A](t: CountTableRef[A]): int =
|
||||||
let a = newCountTable("abracadabra")
|
let a = newCountTable("abracadabra")
|
||||||
for v in values(a):
|
for v in values(a):
|
||||||
assert v < 10
|
assert v < 10
|
||||||
|
|
||||||
for h in 0..high(t.data):
|
for h in 0..high(t.data):
|
||||||
if t.data[h].val != 0: yield t.data[h].val
|
if t.data[h].val != 0: yield t.data[h].val
|
||||||
|
|
||||||
|
|
@ -2593,6 +2645,7 @@ iterator mvalues*[A](t: CountTableRef[A]): var int =
|
||||||
for v in mvalues(a):
|
for v in mvalues(a):
|
||||||
v = 2
|
v = 2
|
||||||
doAssert a == newCountTable("aabbccddrr")
|
doAssert a == newCountTable("aabbccddrr")
|
||||||
|
|
||||||
for h in 0..high(t.data):
|
for h in 0..high(t.data):
|
||||||
if t.data[h].val != 0: yield t.data[h].val
|
if t.data[h].val != 0: yield t.data[h].val
|
||||||
|
|
||||||
|
|
@ -2813,3 +2866,99 @@ when isMainModule:
|
||||||
doAssert "test1" == orf.getOrDefault("test1", "test1")
|
doAssert "test1" == orf.getOrDefault("test1", "test1")
|
||||||
orf["test2"] = "test2"
|
orf["test2"] = "test2"
|
||||||
doAssert "test2" == orf.getOrDefault("test2", "test1")
|
doAssert "test2" == orf.getOrDefault("test2", "test1")
|
||||||
|
|
||||||
|
block tableWithoutInit:
|
||||||
|
var
|
||||||
|
a: Table[string, int]
|
||||||
|
b: Table[string, int]
|
||||||
|
c: Table[string, int]
|
||||||
|
d: Table[string, int]
|
||||||
|
e: Table[string, int]
|
||||||
|
|
||||||
|
a["a"] = 7
|
||||||
|
doAssert a.hasKey("a")
|
||||||
|
doAssert a.len == 1
|
||||||
|
doAssert a["a"] == 7
|
||||||
|
a["a"] = 9
|
||||||
|
doAssert a.len == 1
|
||||||
|
doAssert a["a"] == 9
|
||||||
|
|
||||||
|
doAssert b.hasKeyOrPut("b", 5) == false
|
||||||
|
doAssert b.hasKey("b")
|
||||||
|
doAssert b.hasKeyOrPut("b", 8)
|
||||||
|
doAssert b["b"] == 5
|
||||||
|
|
||||||
|
doAssert c.getOrDefault("a") == 0
|
||||||
|
doAssert c.getOrDefault("a", 3) == 3
|
||||||
|
c["a"] = 6
|
||||||
|
doAssert c.getOrDefault("a", 3) == 6
|
||||||
|
|
||||||
|
doAssert d.mgetOrPut("a", 3) == 3
|
||||||
|
doAssert d.mgetOrPut("a", 6) == 3
|
||||||
|
|
||||||
|
var x = 99
|
||||||
|
doAssert e.take("a", x) == false
|
||||||
|
doAssert x == 99
|
||||||
|
e["a"] = 77
|
||||||
|
doAssert e.take("a", x)
|
||||||
|
doAssert x == 77
|
||||||
|
|
||||||
|
block orderedTableWithoutInit:
|
||||||
|
var
|
||||||
|
a: OrderedTable[string, int]
|
||||||
|
b: OrderedTable[string, int]
|
||||||
|
c: OrderedTable[string, int]
|
||||||
|
d: OrderedTable[string, int]
|
||||||
|
|
||||||
|
a["a"] = 7
|
||||||
|
doAssert a.hasKey("a")
|
||||||
|
doAssert a.len == 1
|
||||||
|
doAssert a["a"] == 7
|
||||||
|
a["a"] = 9
|
||||||
|
doAssert a.len == 1
|
||||||
|
doAssert a["a"] == 9
|
||||||
|
|
||||||
|
doAssert b.hasKeyOrPut("b", 5) == false
|
||||||
|
doAssert b.hasKey("b")
|
||||||
|
doAssert b.hasKeyOrPut("b", 8)
|
||||||
|
doAssert b["b"] == 5
|
||||||
|
|
||||||
|
doAssert c.getOrDefault("a") == 0
|
||||||
|
doAssert c.getOrDefault("a", 3) == 3
|
||||||
|
c["a"] = 6
|
||||||
|
doAssert c.getOrDefault("a", 3) == 6
|
||||||
|
|
||||||
|
doAssert d.mgetOrPut("a", 3) == 3
|
||||||
|
doAssert d.mgetOrPut("a", 6) == 3
|
||||||
|
|
||||||
|
block countTableWithoutInit:
|
||||||
|
var
|
||||||
|
a: CountTable[string]
|
||||||
|
b: CountTable[string]
|
||||||
|
c: CountTable[string]
|
||||||
|
d: CountTable[string]
|
||||||
|
e: CountTable[string]
|
||||||
|
|
||||||
|
a["a"] = 7
|
||||||
|
doAssert a.hasKey("a")
|
||||||
|
doAssert a.len == 1
|
||||||
|
doAssert a["a"] == 7
|
||||||
|
a["a"] = 9
|
||||||
|
doAssert a.len == 1
|
||||||
|
doAssert a["a"] == 9
|
||||||
|
|
||||||
|
doAssert b["b"] == 0
|
||||||
|
b.inc("b")
|
||||||
|
doAssert b["b"] == 1
|
||||||
|
|
||||||
|
doAssert c.getOrDefault("a") == 0
|
||||||
|
doAssert c.getOrDefault("a", 3) == 3
|
||||||
|
c["a"] = 6
|
||||||
|
doAssert c.getOrDefault("a", 3) == 6
|
||||||
|
|
||||||
|
e["f"] = 3
|
||||||
|
merge(d, e)
|
||||||
|
doAssert d.hasKey("f")
|
||||||
|
d.inc("f")
|
||||||
|
merge(d, e)
|
||||||
|
doAssert d["f"] == 7
|
||||||
|
|
|
||||||
|
|
@ -12,10 +12,10 @@
|
||||||
## Windows ``LoadLibrary``.
|
## Windows ``LoadLibrary``.
|
||||||
##
|
##
|
||||||
## Examples
|
## Examples
|
||||||
## --------
|
## ========
|
||||||
##
|
##
|
||||||
## Loading a simple C function
|
## Loading a simple C function
|
||||||
## ^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
## ---------------------------
|
||||||
##
|
##
|
||||||
## The following example demonstrates loading a function called 'greet'
|
## The following example demonstrates loading a function called 'greet'
|
||||||
## from a library that is determined at runtime based upon a language choice.
|
## from a library that is determined at runtime based upon a language choice.
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,8 @@
|
||||||
## generator. Each commonly used HTML tag has a corresponding macro
|
## generator. Each commonly used HTML tag has a corresponding macro
|
||||||
## that generates a string with its HTML representation.
|
## that generates a string with its HTML representation.
|
||||||
##
|
##
|
||||||
## Example:
|
## Examples
|
||||||
|
## ========
|
||||||
##
|
##
|
||||||
## .. code-block:: Nim
|
## .. code-block:: Nim
|
||||||
## var nim = "Nim"
|
## var nim = "Nim"
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
# Include file that implements 'getEnv' and friends. Do not import it!
|
# Include file that implements 'getEnv' and friends. Do not import it!
|
||||||
|
|
||||||
when not declared(os):
|
when not declared(os) and not declared(ospaths):
|
||||||
{.error: "This is an include file for os.nim!".}
|
{.error: "This is an include file for os.nim!".}
|
||||||
|
|
||||||
from parseutils import skipIgnoreCase
|
from parseutils import skipIgnoreCase
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
# Include file that implements 'osErrorMsg' and friends. Do not import it!
|
# Include file that implements 'osErrorMsg' and friends. Do not import it!
|
||||||
|
|
||||||
when not declared(os):
|
when not declared(os) and not declared(ospaths):
|
||||||
{.error: "This is an include file for os.nim!".}
|
{.error: "This is an include file for os.nim!".}
|
||||||
|
|
||||||
when not defined(nimscript):
|
when not defined(nimscript):
|
||||||
|
|
|
||||||
|
|
@ -12,48 +12,46 @@
|
||||||
## format, but much more powerful, as it is not a line based parser. String
|
## format, but much more powerful, as it is not a line based parser. String
|
||||||
## literals, raw string literals and triple quoted string literals are supported
|
## literals, raw string literals and triple quoted string literals are supported
|
||||||
## as in the Nim programming language.
|
## as in the Nim programming language.
|
||||||
|
##
|
||||||
## This is an example of how a configuration file may look like:
|
## Example of how a configuration file may look like:
|
||||||
##
|
##
|
||||||
## .. include:: ../../doc/mytest.cfg
|
## .. include:: ../../doc/mytest.cfg
|
||||||
## :literal:
|
## :literal:
|
||||||
##
|
##
|
||||||
|
## Here is an example of how to use the configuration file parser:
|
||||||
##[ Here is an example of how to use the configuration file parser:
|
##
|
||||||
|
## .. code-block:: nim
|
||||||
.. code-block:: nim
|
##
|
||||||
|
## import os, parsecfg, strutils, streams
|
||||||
import
|
##
|
||||||
os, parsecfg, strutils, streams
|
## var f = newFileStream(paramStr(1), fmRead)
|
||||||
|
## if f != nil:
|
||||||
var f = newFileStream(paramStr(1), fmRead)
|
## var p: CfgParser
|
||||||
if f != nil:
|
## open(p, f, paramStr(1))
|
||||||
var p: CfgParser
|
## while true:
|
||||||
open(p, f, paramStr(1))
|
## var e = next(p)
|
||||||
while true:
|
## case e.kind
|
||||||
var e = next(p)
|
## of cfgEof: break
|
||||||
case e.kind
|
## of cfgSectionStart: ## a ``[section]`` has been parsed
|
||||||
of cfgEof: break
|
## echo("new section: " & e.section)
|
||||||
of cfgSectionStart: ## a ``[section]`` has been parsed
|
## of cfgKeyValuePair:
|
||||||
echo("new section: " & e.section)
|
## echo("key-value-pair: " & e.key & ": " & e.value)
|
||||||
of cfgKeyValuePair:
|
## of cfgOption:
|
||||||
echo("key-value-pair: " & e.key & ": " & e.value)
|
## echo("command: " & e.key & ": " & e.value)
|
||||||
of cfgOption:
|
## of cfgError:
|
||||||
echo("command: " & e.key & ": " & e.value)
|
## echo(e.msg)
|
||||||
of cfgError:
|
## close(p)
|
||||||
echo(e.msg)
|
## else:
|
||||||
close(p)
|
## echo("cannot open: " & paramStr(1))
|
||||||
else:
|
##
|
||||||
echo("cannot open: " & paramStr(1))
|
##
|
||||||
|
|
||||||
]##
|
|
||||||
|
|
||||||
## Examples
|
## Examples
|
||||||
## --------
|
## ========
|
||||||
##
|
##
|
||||||
## This is an example of a configuration file.
|
## Configuration file example
|
||||||
|
## --------------------------
|
||||||
##
|
##
|
||||||
## ::
|
## .. code-block:: nim
|
||||||
##
|
##
|
||||||
## charset = "utf-8"
|
## charset = "utf-8"
|
||||||
## [Package]
|
## [Package]
|
||||||
|
|
@ -64,8 +62,8 @@
|
||||||
## qq = "10214028"
|
## qq = "10214028"
|
||||||
## email = "lihaifeng@wxm.com"
|
## email = "lihaifeng@wxm.com"
|
||||||
##
|
##
|
||||||
## Creating a configuration file.
|
## Creating a configuration file
|
||||||
## ==============================
|
## -----------------------------
|
||||||
## .. code-block:: nim
|
## .. code-block:: nim
|
||||||
##
|
##
|
||||||
## import parsecfg
|
## import parsecfg
|
||||||
|
|
@ -78,8 +76,8 @@
|
||||||
## dict.setSectionKey("Author","email","lihaifeng@wxm.com")
|
## dict.setSectionKey("Author","email","lihaifeng@wxm.com")
|
||||||
## dict.writeConfig("config.ini")
|
## dict.writeConfig("config.ini")
|
||||||
##
|
##
|
||||||
## Reading a configuration file.
|
## Reading a configuration file
|
||||||
## =============================
|
## ----------------------------
|
||||||
## .. code-block:: nim
|
## .. code-block:: nim
|
||||||
##
|
##
|
||||||
## import parsecfg
|
## import parsecfg
|
||||||
|
|
@ -92,8 +90,8 @@
|
||||||
## var email = dict.getSectionValue("Author","email")
|
## var email = dict.getSectionValue("Author","email")
|
||||||
## echo pname & "\n" & name & "\n" & qq & "\n" & email
|
## echo pname & "\n" & name & "\n" & qq & "\n" & email
|
||||||
##
|
##
|
||||||
## Modifying a configuration file.
|
## Modifying a configuration file
|
||||||
## ===============================
|
## ------------------------------
|
||||||
## .. code-block:: nim
|
## .. code-block:: nim
|
||||||
##
|
##
|
||||||
## import parsecfg
|
## import parsecfg
|
||||||
|
|
@ -101,8 +99,8 @@
|
||||||
## dict.setSectionKey("Author","name","lhf")
|
## dict.setSectionKey("Author","name","lhf")
|
||||||
## dict.writeConfig("config.ini")
|
## dict.writeConfig("config.ini")
|
||||||
##
|
##
|
||||||
## Deleting a section key in a configuration file.
|
## Deleting a section key in a configuration file
|
||||||
## ===============================================
|
## ----------------------------------------------
|
||||||
## .. code-block:: nim
|
## .. code-block:: nim
|
||||||
##
|
##
|
||||||
## import parsecfg
|
## import parsecfg
|
||||||
|
|
@ -443,17 +441,17 @@ proc next*(c: var CfgParser): CfgEvent {.rtl, extern: "npc$1".} =
|
||||||
|
|
||||||
# ---------------- Configuration file related operations ----------------
|
# ---------------- Configuration file related operations ----------------
|
||||||
type
|
type
|
||||||
Config* = OrderedTableRef[string, OrderedTableRef[string, string]]
|
Config* = OrderedTableRef[string, <//>OrderedTableRef[string, string]]
|
||||||
|
|
||||||
proc newConfig*(): Config =
|
proc newConfig*(): Config =
|
||||||
## Create a new configuration table.
|
## Create a new configuration table.
|
||||||
## Useful when wanting to create a configuration file.
|
## Useful when wanting to create a configuration file.
|
||||||
result = newOrderedTable[string, OrderedTableRef[string, string]]()
|
result = newOrderedTable[string, <//>OrderedTableRef[string, string]]()
|
||||||
|
|
||||||
proc loadConfig*(stream: Stream, filename: string = "[stream]"): Config =
|
proc loadConfig*(stream: Stream, filename: string = "[stream]"): <//>Config =
|
||||||
## Load the specified configuration from stream into a new Config instance.
|
## Load the specified configuration from stream into a new Config instance.
|
||||||
## `filename` parameter is only used for nicer error messages.
|
## `filename` parameter is only used for nicer error messages.
|
||||||
var dict = newOrderedTable[string, OrderedTableRef[string, string]]()
|
var dict = newOrderedTable[string, <//>OrderedTableRef[string, string]]()
|
||||||
var curSection = "" ## Current section,
|
var curSection = "" ## Current section,
|
||||||
## the default value of the current section is "",
|
## the default value of the current section is "",
|
||||||
## which means that the current section is a common
|
## which means that the current section is a common
|
||||||
|
|
@ -483,7 +481,7 @@ proc loadConfig*(stream: Stream, filename: string = "[stream]"): Config =
|
||||||
close(p)
|
close(p)
|
||||||
result = dict
|
result = dict
|
||||||
|
|
||||||
proc loadConfig*(filename: string): Config =
|
proc loadConfig*(filename: string): <//>Config =
|
||||||
## Load the specified configuration file into a new Config instance.
|
## Load the specified configuration file into a new Config instance.
|
||||||
let file = open(filename, fmRead)
|
let file = open(filename, fmRead)
|
||||||
let fileStream = newFileStream(file)
|
let fileStream = newFileStream(file)
|
||||||
|
|
|
||||||
|
|
@ -64,104 +64,154 @@ const
|
||||||
proc toLower(c: char): char {.inline.} =
|
proc toLower(c: char): char {.inline.} =
|
||||||
result = if c in {'A'..'Z'}: chr(ord(c)-ord('A')+ord('a')) else: c
|
result = if c in {'A'..'Z'}: chr(ord(c)-ord('A')+ord('a')) else: c
|
||||||
|
|
||||||
proc parseHex*(s: string, number: var int, start = 0; maxLen = 0): int {.
|
proc parseBin*[T: SomeInteger](s: string, number: var T, start = 0, maxLen = 0): int
|
||||||
rtl, extern: "npuParseHex", noSideEffect.} =
|
{.noSideEffect.} =
|
||||||
|
## Parses a binary number and stores its value in ``number``.
|
||||||
|
##
|
||||||
|
## Returns the number of the parsed characters or 0 in case of an error.
|
||||||
|
## If error, the value of ``number`` is not changed.
|
||||||
|
##
|
||||||
|
## If ``maxLen == 0``, the parsing continues until the first non-bin character
|
||||||
|
## or to the end of the string. Otherwise, no more than ``maxLen`` characters
|
||||||
|
## are parsed starting from the ``start`` position.
|
||||||
|
##
|
||||||
|
## It does not check for overflow. If the value represented by the string is
|
||||||
|
## too big to fit into ``number``, only the value of last fitting characters
|
||||||
|
## will be stored in ``number`` without producing an error.
|
||||||
|
runnableExamples:
|
||||||
|
var num: int
|
||||||
|
doAssert parseBin("0100_1110_0110_1001_1110_1101", num) == 29
|
||||||
|
doAssert num == 5138925
|
||||||
|
doAssert parseBin("3", num) == 0
|
||||||
|
var num8: int8
|
||||||
|
doAssert parseBin("0b_0100_1110_0110_1001_1110_1101", num8) == 32
|
||||||
|
doAssert num8 == 0b1110_1101'i8
|
||||||
|
doAssert parseBin("0b_0100_1110_0110_1001_1110_1101", num8, 3, 9) == 9
|
||||||
|
doAssert num8 == 0b0100_1110'i8
|
||||||
|
var num8u: uint8
|
||||||
|
doAssert parseBin("0b_0100_1110_0110_1001_1110_1101", num8u) == 32
|
||||||
|
doAssert num8u == 237
|
||||||
|
var num64: int64
|
||||||
|
doAssert parseBin("0100111001101001111011010100111001101001", num64) == 40
|
||||||
|
doAssert num64 == 336784608873
|
||||||
|
var i = start
|
||||||
|
var output = T(0)
|
||||||
|
var foundDigit = false
|
||||||
|
let last = min(s.len, if maxLen == 0: s.len else: i + maxLen)
|
||||||
|
if i + 1 < last and s[i] == '0' and (s[i+1] in {'b', 'B'}): inc(i, 2)
|
||||||
|
while i < last:
|
||||||
|
case s[i]
|
||||||
|
of '_': discard
|
||||||
|
of '0'..'1':
|
||||||
|
output = output shl 1 or T(ord(s[i]) - ord('0'))
|
||||||
|
foundDigit = true
|
||||||
|
else: break
|
||||||
|
inc(i)
|
||||||
|
if foundDigit:
|
||||||
|
number = output
|
||||||
|
result = i - start
|
||||||
|
|
||||||
|
proc parseOct*[T: SomeInteger](s: string, number: var T, start = 0, maxLen = 0): int
|
||||||
|
{.noSideEffect.} =
|
||||||
|
## Parses an octal number and stores its value in ``number``.
|
||||||
|
##
|
||||||
|
## Returns the number of the parsed characters or 0 in case of an error.
|
||||||
|
## If error, the value of ``number`` is not changed.
|
||||||
|
##
|
||||||
|
## If ``maxLen == 0``, the parsing continues until the first non-oct character
|
||||||
|
## or to the end of the string. Otherwise, no more than ``maxLen`` characters
|
||||||
|
## are parsed starting from the ``start`` position.
|
||||||
|
##
|
||||||
|
## It does not check for overflow. If the value represented by the string is
|
||||||
|
## too big to fit into ``number``, only the value of last fitting characters
|
||||||
|
## will be stored in ``number`` without producing an error.
|
||||||
|
runnableExamples:
|
||||||
|
var num: int
|
||||||
|
doAssert parseOct("0o23464755", num) == 10
|
||||||
|
doAssert num == 5138925
|
||||||
|
doAssert parseOct("8", num) == 0
|
||||||
|
var num8: int8
|
||||||
|
doAssert parseOct("0o_1464_755", num8) == 11
|
||||||
|
doAssert num8 == -19
|
||||||
|
doAssert parseOct("0o_1464_755", num8, 3, 3) == 3
|
||||||
|
doAssert num8 == 102
|
||||||
|
var num8u: uint8
|
||||||
|
doAssert parseOct("1464755", num8u) == 7
|
||||||
|
doAssert num8u == 237
|
||||||
|
var num64: int64
|
||||||
|
doAssert parseOct("2346475523464755", num64) == 16
|
||||||
|
doAssert num64 == 86216859871725
|
||||||
|
var i = start
|
||||||
|
var output = T(0)
|
||||||
|
var foundDigit = false
|
||||||
|
let last = min(s.len, if maxLen == 0: s.len else: i + maxLen)
|
||||||
|
if i + 1 < last and s[i] == '0' and (s[i+1] in {'o', 'O'}): inc(i, 2)
|
||||||
|
while i < last:
|
||||||
|
case s[i]
|
||||||
|
of '_': discard
|
||||||
|
of '0'..'7':
|
||||||
|
output = output shl 3 or T(ord(s[i]) - ord('0'))
|
||||||
|
foundDigit = true
|
||||||
|
else: break
|
||||||
|
inc(i)
|
||||||
|
if foundDigit:
|
||||||
|
number = output
|
||||||
|
result = i - start
|
||||||
|
|
||||||
|
proc parseHex*[T: SomeInteger](s: string, number: var T, start = 0, maxLen = 0): int
|
||||||
|
{.noSideEffect.} =
|
||||||
## Parses a hexadecimal number and stores its value in ``number``.
|
## Parses a hexadecimal number and stores its value in ``number``.
|
||||||
##
|
##
|
||||||
## Returns the number of the parsed characters or 0 in case of an error. This
|
## Returns the number of the parsed characters or 0 in case of an error.
|
||||||
## proc is sensitive to the already existing value of ``number`` and will
|
## If error, the value of ``number`` is not changed.
|
||||||
## likely not do what you want unless you make sure ``number`` is zero. You
|
|
||||||
## can use this feature to *chain* calls, though the result int will quickly
|
|
||||||
## overflow.
|
|
||||||
##
|
##
|
||||||
## If ``maxLen == 0`` the length of the hexadecimal number has no upper bound.
|
## If ``maxLen == 0``, the parsing continues until the first non-hex character
|
||||||
## Else no more than ``start + maxLen`` characters are parsed, up to the
|
## or to the end of the string. Otherwise, no more than ``maxLen`` characters
|
||||||
## length of the string.
|
## are parsed starting from the ``start`` position.
|
||||||
|
##
|
||||||
|
## It does not check for overflow. If the value represented by the string is
|
||||||
|
## too big to fit into ``number``, only the value of last fitting characters
|
||||||
|
## will be stored in ``number`` without producing an error.
|
||||||
runnableExamples:
|
runnableExamples:
|
||||||
var value = 0
|
var num: int
|
||||||
discard parseHex("0x38", value)
|
doAssert parseHex("4E_69_ED", num) == 8
|
||||||
assert value == 56
|
doAssert num == 5138925
|
||||||
discard parseHex("0x34", value)
|
doAssert parseHex("X", num) == 0
|
||||||
assert value == 56 * 256 + 52
|
doAssert parseHex("#ABC", num) == 4
|
||||||
value = -1
|
var num8: int8
|
||||||
discard parseHex("0x38", value)
|
doAssert parseHex("0x_4E_69_ED", num8) == 11
|
||||||
assert value == -200
|
doAssert num8 == 0xED'i8
|
||||||
|
doAssert parseHex("0x_4E_69_ED", num8, 3, 2) == 2
|
||||||
|
doAssert num8 == 0x4E'i8
|
||||||
|
var num8u: uint8
|
||||||
|
doAssert parseHex("0x_4E_69_ED", num8u) == 11
|
||||||
|
doAssert num8u == 237
|
||||||
|
var num64: int64
|
||||||
|
doAssert parseHex("4E69ED4E69ED", num64) == 12
|
||||||
|
doAssert num64 == 86216859871725
|
||||||
var i = start
|
var i = start
|
||||||
|
var output = T(0)
|
||||||
var foundDigit = false
|
var foundDigit = false
|
||||||
# get last index based on minimum `start + maxLen` or `s.len`
|
let last = min(s.len, if maxLen == 0: s.len else: i + maxLen)
|
||||||
let last = min(s.len, if maxLen == 0: s.len else: i+maxLen)
|
if i + 1 < last and s[i] == '0' and (s[i+1] in {'x', 'X'}): inc(i, 2)
|
||||||
if i+1 < last and s[i] == '0' and (s[i+1] in {'x', 'X'}): inc(i, 2)
|
|
||||||
elif i < last and s[i] == '#': inc(i)
|
elif i < last and s[i] == '#': inc(i)
|
||||||
while i < last:
|
while i < last:
|
||||||
case s[i]
|
case s[i]
|
||||||
of '_': discard
|
of '_': discard
|
||||||
of '0'..'9':
|
of '0'..'9':
|
||||||
number = number shl 4 or (ord(s[i]) - ord('0'))
|
output = output shl 4 or T(ord(s[i]) - ord('0'))
|
||||||
foundDigit = true
|
foundDigit = true
|
||||||
of 'a'..'f':
|
of 'a'..'f':
|
||||||
number = number shl 4 or (ord(s[i]) - ord('a') + 10)
|
output = output shl 4 or T(ord(s[i]) - ord('a') + 10)
|
||||||
foundDigit = true
|
foundDigit = true
|
||||||
of 'A'..'F':
|
of 'A'..'F':
|
||||||
number = number shl 4 or (ord(s[i]) - ord('A') + 10)
|
output = output shl 4 or T(ord(s[i]) - ord('A') + 10)
|
||||||
foundDigit = true
|
foundDigit = true
|
||||||
else: break
|
else: break
|
||||||
inc(i)
|
inc(i)
|
||||||
if foundDigit: result = i-start
|
if foundDigit:
|
||||||
|
number = output
|
||||||
proc parseOct*(s: string, number: var int, start = 0, maxLen = 0): int {.
|
result = i - start
|
||||||
rtl, extern: "npuParseOct", noSideEffect.} =
|
|
||||||
## Parses an octal number and stores its value in ``number``. Returns
|
|
||||||
## the number of the parsed characters or 0 in case of an error.
|
|
||||||
##
|
|
||||||
## If ``maxLen == 0`` the length of the octal number has no upper bound.
|
|
||||||
## Else no more than ``start + maxLen`` characters are parsed, up to the
|
|
||||||
## length of the string.
|
|
||||||
runnableExamples:
|
|
||||||
var res: int
|
|
||||||
doAssert parseOct("12", res) == 2
|
|
||||||
doAssert res == 10
|
|
||||||
doAssert parseOct("9", res) == 0
|
|
||||||
var i = start
|
|
||||||
var foundDigit = false
|
|
||||||
# get last index based on minimum `start + maxLen` or `s.len`
|
|
||||||
let last = min(s.len, if maxLen == 0: s.len else: i+maxLen)
|
|
||||||
if i+1 < last and s[i] == '0' and (s[i+1] in {'o', 'O'}): inc(i, 2)
|
|
||||||
while i < last:
|
|
||||||
case s[i]
|
|
||||||
of '_': discard
|
|
||||||
of '0'..'7':
|
|
||||||
number = number shl 3 or (ord(s[i]) - ord('0'))
|
|
||||||
foundDigit = true
|
|
||||||
else: break
|
|
||||||
inc(i)
|
|
||||||
if foundDigit: result = i-start
|
|
||||||
|
|
||||||
proc parseBin*(s: string, number: var int, start = 0, maxLen = 0): int {.
|
|
||||||
rtl, extern: "npuParseBin", noSideEffect.} =
|
|
||||||
## Parses an binary number and stores its value in ``number``. Returns
|
|
||||||
## the number of the parsed characters or 0 in case of an error.
|
|
||||||
##
|
|
||||||
## If ``maxLen == 0`` the length of the binary number has no upper bound.
|
|
||||||
## Else no more than ``start + maxLen`` characters are parsed, up to the
|
|
||||||
## length of the string.
|
|
||||||
runnableExamples:
|
|
||||||
var res: int
|
|
||||||
doAssert parseBin("010011100110100101101101", res) == 24
|
|
||||||
doAssert parseBin("3", res) == 0
|
|
||||||
var i = start
|
|
||||||
var foundDigit = false
|
|
||||||
# get last index based on minimum `start + maxLen` or `s.len`
|
|
||||||
let last = min(s.len, if maxLen == 0: s.len else: i+maxLen)
|
|
||||||
if i+1 < last and s[i] == '0' and (s[i+1] in {'b', 'B'}): inc(i, 2)
|
|
||||||
while i < last:
|
|
||||||
case s[i]
|
|
||||||
of '_': discard
|
|
||||||
of '0'..'1':
|
|
||||||
number = number shl 1 or (ord(s[i]) - ord('0'))
|
|
||||||
foundDigit = true
|
|
||||||
else: break
|
|
||||||
inc(i)
|
|
||||||
if foundDigit: result = i-start
|
|
||||||
|
|
||||||
proc parseIdent*(s: string, ident: var string, start = 0): int =
|
proc parseIdent*(s: string, ident: var string, start = 0): int =
|
||||||
## Parses an identifier and stores it in ``ident``. Returns
|
## Parses an identifier and stores it in ``ident``. Returns
|
||||||
|
|
@ -605,11 +655,6 @@ when isMainModule:
|
||||||
var value = 0
|
var value = 0
|
||||||
discard parseHex("0x38", value)
|
discard parseHex("0x38", value)
|
||||||
doAssert value == 56
|
doAssert value == 56
|
||||||
discard parseHex("0x34", value)
|
|
||||||
doAssert value == 56 * 256 + 52
|
|
||||||
value = -1
|
|
||||||
discard parseHex("0x38", value)
|
|
||||||
doAssert value == -200
|
|
||||||
|
|
||||||
value = -1
|
value = -1
|
||||||
doAssert(parseSaturatedNatural("848", value) == 3)
|
doAssert(parseSaturatedNatural("848", value) == 3)
|
||||||
|
|
|
||||||
|
|
@ -129,7 +129,7 @@ proc next*(r: var Rand): uint64 =
|
||||||
## a given upper bound
|
## a given upper bound
|
||||||
## * `rand proc<#rand,Rand,range[]>`_ that returns a float
|
## * `rand proc<#rand,Rand,range[]>`_ that returns a float
|
||||||
## * `rand proc<#rand,Rand,HSlice[T,T]>`_ that accepts a slice
|
## * `rand proc<#rand,Rand,HSlice[T,T]>`_ that accepts a slice
|
||||||
## * `rand proc<#rand,typedesc[T]>`_ that accepts an integer type
|
## * `rand proc<#rand,typedesc[T]>`_ that accepts an integer or range type
|
||||||
## * `skipRandomNumbers proc<#skipRandomNumbers,Rand>`_
|
## * `skipRandomNumbers proc<#skipRandomNumbers,Rand>`_
|
||||||
runnableExamples:
|
runnableExamples:
|
||||||
var r = initRand(2019)
|
var r = initRand(2019)
|
||||||
|
|
@ -242,7 +242,7 @@ proc rand*(r: var Rand; max: Natural): int {.benign.} =
|
||||||
## random number generator
|
## random number generator
|
||||||
## * `rand proc<#rand,Rand,range[]>`_ that returns a float
|
## * `rand proc<#rand,Rand,range[]>`_ that returns a float
|
||||||
## * `rand proc<#rand,Rand,HSlice[T,T]>`_ that accepts a slice
|
## * `rand proc<#rand,Rand,HSlice[T,T]>`_ that accepts a slice
|
||||||
## * `rand proc<#rand,typedesc[T]>`_ that accepts an integer type
|
## * `rand proc<#rand,typedesc[T]>`_ that accepts an integer or range type
|
||||||
runnableExamples:
|
runnableExamples:
|
||||||
var r = initRand(123)
|
var r = initRand(123)
|
||||||
doAssert r.rand(100) == 0
|
doAssert r.rand(100) == 0
|
||||||
|
|
@ -268,7 +268,7 @@ proc rand*(max: int): int {.benign.} =
|
||||||
## provided state
|
## provided state
|
||||||
## * `rand proc<#rand,float>`_ that returns a float
|
## * `rand proc<#rand,float>`_ that returns a float
|
||||||
## * `rand proc<#rand,HSlice[T,T]>`_ that accepts a slice
|
## * `rand proc<#rand,HSlice[T,T]>`_ that accepts a slice
|
||||||
## * `rand proc<#rand,typedesc[T]>`_ that accepts an integer type
|
## * `rand proc<#rand,typedesc[T]>`_ that accepts an integer or range type
|
||||||
runnableExamples:
|
runnableExamples:
|
||||||
randomize(123)
|
randomize(123)
|
||||||
doAssert rand(100) == 0
|
doAssert rand(100) == 0
|
||||||
|
|
@ -285,7 +285,7 @@ proc rand*(r: var Rand; max: range[0.0 .. high(float)]): float {.benign.} =
|
||||||
## random number generator
|
## random number generator
|
||||||
## * `rand proc<#rand,Rand,Natural>`_ that returns an integer
|
## * `rand proc<#rand,Rand,Natural>`_ that returns an integer
|
||||||
## * `rand proc<#rand,Rand,HSlice[T,T]>`_ that accepts a slice
|
## * `rand proc<#rand,Rand,HSlice[T,T]>`_ that accepts a slice
|
||||||
## * `rand proc<#rand,typedesc[T]>`_ that accepts an integer type
|
## * `rand proc<#rand,typedesc[T]>`_ that accepts an integer or range type
|
||||||
runnableExamples:
|
runnableExamples:
|
||||||
var r = initRand(234)
|
var r = initRand(234)
|
||||||
let f = r.rand(1.0)
|
let f = r.rand(1.0)
|
||||||
|
|
@ -311,7 +311,7 @@ proc rand*(max: float): float {.benign.} =
|
||||||
## provided state
|
## provided state
|
||||||
## * `rand proc<#rand,int>`_ that returns an integer
|
## * `rand proc<#rand,int>`_ that returns an integer
|
||||||
## * `rand proc<#rand,HSlice[T,T]>`_ that accepts a slice
|
## * `rand proc<#rand,HSlice[T,T]>`_ that accepts a slice
|
||||||
## * `rand proc<#rand,typedesc[T]>`_ that accepts an integer type
|
## * `rand proc<#rand,typedesc[T]>`_ that accepts an integer or range type
|
||||||
runnableExamples:
|
runnableExamples:
|
||||||
randomize(234)
|
randomize(234)
|
||||||
let f = rand(1.0)
|
let f = rand(1.0)
|
||||||
|
|
@ -327,7 +327,7 @@ proc rand*[T: Ordinal](r: var Rand; x: HSlice[T, T]): T =
|
||||||
## default random number generator
|
## default random number generator
|
||||||
## * `rand proc<#rand,Rand,Natural>`_ that returns an integer
|
## * `rand proc<#rand,Rand,Natural>`_ that returns an integer
|
||||||
## * `rand proc<#rand,Rand,range[]>`_ that returns a float
|
## * `rand proc<#rand,Rand,range[]>`_ that returns a float
|
||||||
## * `rand proc<#rand,typedesc[T]>`_ that accepts an integer type
|
## * `rand proc<#rand,typedesc[T]>`_ that accepts an integer or range type
|
||||||
runnableExamples:
|
runnableExamples:
|
||||||
var r = initRand(345)
|
var r = initRand(345)
|
||||||
doAssert r.rand(1..6) == 4
|
doAssert r.rand(1..6) == 4
|
||||||
|
|
@ -349,7 +349,7 @@ proc rand*[T: Ordinal](x: HSlice[T, T]): T =
|
||||||
## a provided state
|
## a provided state
|
||||||
## * `rand proc<#rand,int>`_ that returns an integer
|
## * `rand proc<#rand,int>`_ that returns an integer
|
||||||
## * `rand proc<#rand,float>`_ that returns a floating point number
|
## * `rand proc<#rand,float>`_ that returns a floating point number
|
||||||
## * `rand proc<#rand,typedesc[T]>`_ that accepts an integer type
|
## * `rand proc<#rand,typedesc[T]>`_ that accepts an integer or range type
|
||||||
runnableExamples:
|
runnableExamples:
|
||||||
randomize(345)
|
randomize(345)
|
||||||
doAssert rand(1..6) == 4
|
doAssert rand(1..6) == 4
|
||||||
|
|
@ -383,7 +383,13 @@ proc rand*[T: SomeInteger](t: typedesc[T]): T =
|
||||||
doAssert rand(uint32) == 578980729'u32
|
doAssert rand(uint32) == 578980729'u32
|
||||||
doAssert rand(uint32) == 4052940463'u32
|
doAssert rand(uint32) == 4052940463'u32
|
||||||
doAssert rand(uint32) == 2163872389'u32
|
doAssert rand(uint32) == 2163872389'u32
|
||||||
result = cast[T](state.next)
|
doAssert rand(range[1..16]) == 11
|
||||||
|
doAssert rand(range[1..16]) == 4
|
||||||
|
doAssert rand(range[1..16]) == 16
|
||||||
|
when T is range:
|
||||||
|
result = rand(state, low(T)..high(T))
|
||||||
|
else:
|
||||||
|
result = cast[T](state.next)
|
||||||
|
|
||||||
proc rand*[T](a: openArray[T]): T {.deprecated.} =
|
proc rand*[T](a: openArray[T]): T {.deprecated.} =
|
||||||
## **Deprecated since version 0.20.0:**
|
## **Deprecated since version 0.20.0:**
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load diff
|
|
@ -419,6 +419,9 @@ proc formatValue*(result: var string; value: SomeInteger; specifier: string) =
|
||||||
## Standard format implementation for ``SomeInteger``. It makes little
|
## Standard format implementation for ``SomeInteger``. It makes little
|
||||||
## sense to call this directly, but it is required to exist
|
## sense to call this directly, but it is required to exist
|
||||||
## by the ``&`` macro.
|
## by the ``&`` macro.
|
||||||
|
if specifier.len == 0:
|
||||||
|
result.add $value
|
||||||
|
return
|
||||||
let spec = parseStandardFormatSpecifier(specifier)
|
let spec = parseStandardFormatSpecifier(specifier)
|
||||||
var radix = 10
|
var radix = 10
|
||||||
case spec.typ
|
case spec.typ
|
||||||
|
|
@ -432,10 +435,13 @@ proc formatValue*(result: var string; value: SomeInteger; specifier: string) =
|
||||||
" of 'x', 'X', 'b', 'd', 'o' but got: " & spec.typ)
|
" of 'x', 'X', 'b', 'd', 'o' but got: " & spec.typ)
|
||||||
result.add formatInt(value, radix, spec)
|
result.add formatInt(value, radix, spec)
|
||||||
|
|
||||||
proc formatValue*(result: var string; value: SomeFloat; specifier: string): void =
|
proc formatValue*(result: var string; value: SomeFloat; specifier: string) =
|
||||||
## Standard format implementation for ``SomeFloat``. It makes little
|
## Standard format implementation for ``SomeFloat``. It makes little
|
||||||
## sense to call this directly, but it is required to exist
|
## sense to call this directly, but it is required to exist
|
||||||
## by the ``&`` macro.
|
## by the ``&`` macro.
|
||||||
|
if specifier.len == 0:
|
||||||
|
result.add $value
|
||||||
|
return
|
||||||
let spec = parseStandardFormatSpecifier(specifier)
|
let spec = parseStandardFormatSpecifier(specifier)
|
||||||
|
|
||||||
var fmode = ffDefault
|
var fmode = ffDefault
|
||||||
|
|
@ -457,7 +463,7 @@ proc formatValue*(result: var string; value: SomeFloat; specifier: string): void
|
||||||
if value >= 0.0:
|
if value >= 0.0:
|
||||||
if spec.sign != '-':
|
if spec.sign != '-':
|
||||||
sign = true
|
sign = true
|
||||||
if value == 0.0:
|
if value == 0.0:
|
||||||
if 1.0 / value == Inf:
|
if 1.0 / value == Inf:
|
||||||
# only insert the sign if value != negZero
|
# only insert the sign if value != negZero
|
||||||
f.insert($spec.sign, 0)
|
f.insert($spec.sign, 0)
|
||||||
|
|
@ -467,16 +473,16 @@ proc formatValue*(result: var string; value: SomeFloat; specifier: string): void
|
||||||
sign = true
|
sign = true
|
||||||
|
|
||||||
if spec.padWithZero:
|
if spec.padWithZero:
|
||||||
var sign_str = ""
|
var signStr = ""
|
||||||
if sign:
|
if sign:
|
||||||
sign_str = $f[0]
|
signStr = $f[0]
|
||||||
f = f[1..^1]
|
f = f[1..^1]
|
||||||
|
|
||||||
let toFill = spec.minimumWidth - f.len - ord(sign)
|
let toFill = spec.minimumWidth - f.len - ord(sign)
|
||||||
if toFill > 0:
|
if toFill > 0:
|
||||||
f = repeat('0', toFill) & f
|
f = repeat('0', toFill) & f
|
||||||
if sign:
|
if sign:
|
||||||
f = sign_str & f
|
f = signStr & f
|
||||||
|
|
||||||
# the default for numbers is right-alignment:
|
# the default for numbers is right-alignment:
|
||||||
let align = if spec.align == '\0': '>' else: spec.align
|
let align = if spec.align == '\0': '>' else: spec.align
|
||||||
|
|
@ -712,6 +718,13 @@ when isMainModule:
|
||||||
for s in invalidUtf8:
|
for s in invalidUtf8:
|
||||||
check &"{s:>5}", repeat(" ", 5-s.len) & s
|
check &"{s:>5}", repeat(" ", 5-s.len) & s
|
||||||
|
|
||||||
|
# bug #11089
|
||||||
|
let flfoo: float = 1.0
|
||||||
|
check &"{flfoo}", "1.0"
|
||||||
|
|
||||||
|
# bug #11092
|
||||||
|
check &"{high(int64)}", "9223372036854775807"
|
||||||
|
check &"{low(int64)}", "-9223372036854775808"
|
||||||
|
|
||||||
import json
|
import json
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,8 @@
|
||||||
resolution used by ``getTime()`` depends on the platform and backend
|
resolution used by ``getTime()`` depends on the platform and backend
|
||||||
(JS is limited to millisecond precision).
|
(JS is limited to millisecond precision).
|
||||||
|
|
||||||
Examples:
|
Examples
|
||||||
|
========
|
||||||
|
|
||||||
.. code-block:: nim
|
.. code-block:: nim
|
||||||
import times, os
|
import times, os
|
||||||
|
|
@ -38,7 +39,7 @@
|
||||||
echo "One month from now : ", now() + 1.months
|
echo "One month from now : ", now() + 1.months
|
||||||
|
|
||||||
Parsing and Formatting Dates
|
Parsing and Formatting Dates
|
||||||
----------------------------
|
============================
|
||||||
|
|
||||||
The ``DateTime`` type can be parsed and formatted using the different
|
The ``DateTime`` type can be parsed and formatted using the different
|
||||||
``parse`` and ``format`` procedures.
|
``parse`` and ``format`` procedures.
|
||||||
|
|
@ -128,7 +129,7 @@
|
||||||
only for years in the range 1..9999).
|
only for years in the range 1..9999).
|
||||||
|
|
||||||
Duration vs TimeInterval
|
Duration vs TimeInterval
|
||||||
----------------------------
|
============================
|
||||||
The ``times`` module exports two similiar types that are both used to
|
The ``times`` module exports two similiar types that are both used to
|
||||||
represent some amount of time: `Duration <#Duration>`_ and
|
represent some amount of time: `Duration <#Duration>`_ and
|
||||||
`TimeInterval <#TimeInterval>`_.
|
`TimeInterval <#TimeInterval>`_.
|
||||||
|
|
@ -137,7 +138,7 @@
|
||||||
needed).
|
needed).
|
||||||
|
|
||||||
Duration
|
Duration
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
----------------------------
|
||||||
A ``Duration`` represents a duration of time stored as seconds and
|
A ``Duration`` represents a duration of time stored as seconds and
|
||||||
nanoseconds. A ``Duration`` is always fully normalized, so
|
nanoseconds. A ``Duration`` is always fully normalized, so
|
||||||
``initDuration(hours = 1)`` and ``initDuration(minutes = 60)`` are equivilant.
|
``initDuration(hours = 1)`` and ``initDuration(minutes = 60)`` are equivilant.
|
||||||
|
|
@ -147,7 +148,7 @@
|
||||||
is more performant and easier to understand it should generally prefered.
|
is more performant and easier to understand it should generally prefered.
|
||||||
|
|
||||||
TimeInterval
|
TimeInterval
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
----------------------------
|
||||||
A ``TimeInterval`` represents some amount of time expressed in calendar
|
A ``TimeInterval`` represents some amount of time expressed in calendar
|
||||||
units, for example "1 year and 2 days". Since some units cannot be
|
units, for example "1 year and 2 days". Since some units cannot be
|
||||||
normalized (the length of a year is different for leap years for example),
|
normalized (the length of a year is different for leap years for example),
|
||||||
|
|
@ -164,7 +165,7 @@
|
||||||
``Duration`` doesn't have.
|
``Duration`` doesn't have.
|
||||||
|
|
||||||
How long is a day?
|
How long is a day?
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
----------------------------
|
||||||
It should be especially noted that the handling of days differs between
|
It should be especially noted that the handling of days differs between
|
||||||
``TimeInterval`` and ``Duration``. The ``Duration`` type always treats a day
|
``TimeInterval`` and ``Duration``. The ``Duration`` type always treats a day
|
||||||
as exactly 86400 seconds. For ``TimeInterval``, it's more complex.
|
as exactly 86400 seconds. For ``TimeInterval``, it's more complex.
|
||||||
|
|
@ -2651,7 +2652,7 @@ proc microseconds*(dur: Duration): int {.inline, deprecated.} =
|
||||||
doAssert dur.microseconds == 7008
|
doAssert dur.microseconds == 7008
|
||||||
result = convert(Nanoseconds, Microseconds, dur.nanosecond)
|
result = convert(Nanoseconds, Microseconds, dur.nanosecond)
|
||||||
|
|
||||||
proc nanoseconds*(dur: Duration): NanosecondRange {.inline.} =
|
proc nanoseconds*(dur: Duration): NanosecondRange {.inline, deprecated.} =
|
||||||
## Number of whole microseconds represented by the **fractional**
|
## Number of whole microseconds represented by the **fractional**
|
||||||
## part of the duration.
|
## part of the duration.
|
||||||
##
|
##
|
||||||
|
|
|
||||||
|
|
@ -57,8 +57,8 @@
|
||||||
## # Run suites starting with 'bug #' and standalone tests starting with '#'
|
## # Run suites starting with 'bug #' and standalone tests starting with '#'
|
||||||
## nim c -r test 'bug #*::' '::#*'
|
## nim c -r test 'bug #*::' '::#*'
|
||||||
##
|
##
|
||||||
## Example
|
## Examples
|
||||||
## -------
|
## ========
|
||||||
##
|
##
|
||||||
## .. code:: nim
|
## .. code:: nim
|
||||||
##
|
##
|
||||||
|
|
@ -177,13 +177,13 @@ proc addOutputFormatter*(formatter: OutputFormatter) =
|
||||||
formatters.add(formatter)
|
formatters.add(formatter)
|
||||||
|
|
||||||
proc newConsoleOutputFormatter*(outputLevel: OutputLevel = PRINT_ALL,
|
proc newConsoleOutputFormatter*(outputLevel: OutputLevel = PRINT_ALL,
|
||||||
colorOutput = true): ConsoleOutputFormatter =
|
colorOutput = true): <//>ConsoleOutputFormatter =
|
||||||
ConsoleOutputFormatter(
|
ConsoleOutputFormatter(
|
||||||
outputLevel: outputLevel,
|
outputLevel: outputLevel,
|
||||||
colorOutput: colorOutput
|
colorOutput: colorOutput
|
||||||
)
|
)
|
||||||
|
|
||||||
proc defaultConsoleFormatter*(): ConsoleOutputFormatter =
|
proc defaultConsoleFormatter*(): <//>ConsoleOutputFormatter =
|
||||||
when declared(stdout):
|
when declared(stdout):
|
||||||
# Reading settings
|
# Reading settings
|
||||||
# On a terminal this branch is executed
|
# On a terminal this branch is executed
|
||||||
|
|
@ -263,7 +263,7 @@ proc xmlEscape(s: string): string =
|
||||||
else:
|
else:
|
||||||
result.add(c)
|
result.add(c)
|
||||||
|
|
||||||
proc newJUnitOutputFormatter*(stream: Stream): JUnitOutputFormatter =
|
proc newJUnitOutputFormatter*(stream: Stream): <//>JUnitOutputFormatter =
|
||||||
## Creates a formatter that writes report to the specified stream in
|
## Creates a formatter that writes report to the specified stream in
|
||||||
## JUnit format.
|
## JUnit format.
|
||||||
## The ``stream`` is NOT closed automatically when the test are finished,
|
## The ``stream`` is NOT closed automatically when the test are finished,
|
||||||
|
|
|
||||||
|
|
@ -1619,7 +1619,7 @@ template `isnot`*(x, y: untyped): untyped = not (x is y)
|
||||||
## assert @[1, 2] isnot enum
|
## assert @[1, 2] isnot enum
|
||||||
|
|
||||||
when defined(nimV2) and not defined(nimscript):
|
when defined(nimV2) and not defined(nimscript):
|
||||||
type owned*{.magic: "BuiltinType".}[T]
|
type owned*{.magic: "BuiltinType".}[T] ## type constructor to mark a ref/ptr or a closure as `owned`.
|
||||||
|
|
||||||
proc new*[T](a: var owned(ref T)) {.magic: "New", noSideEffect.}
|
proc new*[T](a: var owned(ref T)) {.magic: "New", noSideEffect.}
|
||||||
## Creates a new object of type ``T`` and returns a safe (traced)
|
## Creates a new object of type ``T`` and returns a safe (traced)
|
||||||
|
|
@ -1637,8 +1637,16 @@ when defined(nimV2) and not defined(nimscript):
|
||||||
var r: owned(ref t)
|
var r: owned(ref t)
|
||||||
new(r)
|
new(r)
|
||||||
return r
|
return r
|
||||||
|
|
||||||
|
proc unown*[T](x: T): T {.magic: "Unown", noSideEffect.}
|
||||||
|
## Use the expression ``x`` ignoring its ownership attribute.
|
||||||
|
|
||||||
|
# This is only required to make 0.20 compile with the 0.19 line.
|
||||||
|
template `<//>`*(t: untyped): untyped = owned(t)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
template owned*(t: typeDesc): typedesc = t
|
template owned*(t: typeDesc): typedesc = t
|
||||||
|
template unown*(x: typed): typed = x
|
||||||
|
|
||||||
proc new*[T](a: var ref T) {.magic: "New", noSideEffect.}
|
proc new*[T](a: var ref T) {.magic: "New", noSideEffect.}
|
||||||
## Creates a new object of type ``T`` and returns a safe (traced)
|
## Creates a new object of type ``T`` and returns a safe (traced)
|
||||||
|
|
@ -1657,6 +1665,9 @@ else:
|
||||||
new(r)
|
new(r)
|
||||||
return r
|
return r
|
||||||
|
|
||||||
|
# This is only required to make 0.20 compile with the 0.19 line.
|
||||||
|
template `<//>`*(t: untyped): untyped = t
|
||||||
|
|
||||||
template disarm*(x: typed) =
|
template disarm*(x: typed) =
|
||||||
## Useful for ``disarming`` dangling pointers explicitly for the
|
## Useful for ``disarming`` dangling pointers explicitly for the
|
||||||
## --newruntime. Regardless of whether --newruntime is used or not
|
## --newruntime. Regardless of whether --newruntime is used or not
|
||||||
|
|
@ -1926,13 +1937,11 @@ const
|
||||||
## failure.
|
## failure.
|
||||||
|
|
||||||
when defined(nodejs) and not defined(nimscript):
|
when defined(nodejs) and not defined(nimscript):
|
||||||
var programResult* {.importc: "process.exitCode".}: int
|
var programResult* {.importc: "process.exitCode", deprecated.}: int
|
||||||
programResult = 0
|
programResult = 0
|
||||||
else:
|
elif hostOS != "standalone":
|
||||||
var programResult* {.compilerproc, exportc: "nim_program_result".}: int
|
var programResult* {.compilerproc, exportc: "nim_program_result", deprecated.}: int
|
||||||
## Modify this variable to specify the exit code of the program
|
## deprecated, prefer ``quit``
|
||||||
## under normal circumstances. When the program is terminated
|
|
||||||
## prematurely using `quit proc <#quit,int>`_, this value is ignored.
|
|
||||||
|
|
||||||
when defined(nimdoc):
|
when defined(nimdoc):
|
||||||
proc quit*(errorcode: int = QuitSuccess) {.magic: "Exit", noreturn.}
|
proc quit*(errorcode: int = QuitSuccess) {.magic: "Exit", noreturn.}
|
||||||
|
|
@ -1994,18 +2003,7 @@ when not defined(JS) and not defined(nimscript) and hostOS != "standalone":
|
||||||
when not defined(JS) and not defined(nimscript) and hasAlloc and not defined(gcDestructors):
|
when not defined(JS) and not defined(nimscript) and hasAlloc and not defined(gcDestructors):
|
||||||
proc addChar(s: NimString, c: char): NimString {.compilerProc, benign.}
|
proc addChar(s: NimString, c: char): NimString {.compilerProc, benign.}
|
||||||
|
|
||||||
when defined(gcDestructors):
|
when not defined(gcDestructors):
|
||||||
proc add*[T](x: var seq[T], y: sink T) {.magic: "AppendSeqElem", noSideEffect.} =
|
|
||||||
## Generic proc for adding a data item `y` to a container `x`.
|
|
||||||
##
|
|
||||||
## For containers that have an order, `add` means *append*. New generic
|
|
||||||
## containers should also call their adding proc `add` for consistency.
|
|
||||||
## Generic code becomes much easier to write if the Nim naming scheme is
|
|
||||||
## respected.
|
|
||||||
let xl = x.len
|
|
||||||
setLen(x, xl + 1)
|
|
||||||
x[xl] = y
|
|
||||||
else:
|
|
||||||
proc add*[T](x: var seq[T], y: T) {.magic: "AppendSeqElem", noSideEffect.}
|
proc add*[T](x: var seq[T], y: T) {.magic: "AppendSeqElem", noSideEffect.}
|
||||||
## Generic proc for adding a data item `y` to a container `x`.
|
## Generic proc for adding a data item `y` to a container `x`.
|
||||||
##
|
##
|
||||||
|
|
@ -2239,14 +2237,15 @@ proc addQuitProc*(quitProc: proc() {.noconv.}) {.
|
||||||
# not be called explicitly! The user may decide to do this manually though.
|
# not be called explicitly! The user may decide to do this manually though.
|
||||||
|
|
||||||
when not defined(nimscript) and not defined(JS):
|
when not defined(nimscript) and not defined(JS):
|
||||||
proc zeroMem*(p: pointer, size: Natural) {.inline, benign.}
|
proc zeroMem*(p: pointer, size: Natural) {.inline, noSideEffect,
|
||||||
|
tags: [], locks: 0, raises: [].}
|
||||||
## Overwrites the contents of the memory at ``p`` with the value 0.
|
## Overwrites the contents of the memory at ``p`` with the value 0.
|
||||||
##
|
##
|
||||||
## Exactly ``size`` bytes will be overwritten. Like any procedure
|
## Exactly ``size`` bytes will be overwritten. Like any procedure
|
||||||
## dealing with raw memory this is **unsafe**.
|
## dealing with raw memory this is **unsafe**.
|
||||||
|
|
||||||
proc copyMem*(dest, source: pointer, size: Natural) {.inline, benign,
|
proc copyMem*(dest, source: pointer, size: Natural) {.inline, benign,
|
||||||
tags: [], locks: 0.}
|
tags: [], locks: 0, raises: [].}
|
||||||
## Copies the contents from the memory at ``source`` to the memory
|
## Copies the contents from the memory at ``source`` to the memory
|
||||||
## at ``dest``.
|
## at ``dest``.
|
||||||
## Exactly ``size`` bytes will be copied. The memory
|
## Exactly ``size`` bytes will be copied. The memory
|
||||||
|
|
@ -2254,7 +2253,7 @@ when not defined(nimscript) and not defined(JS):
|
||||||
## memory this is **unsafe**.
|
## memory this is **unsafe**.
|
||||||
|
|
||||||
proc moveMem*(dest, source: pointer, size: Natural) {.inline, benign,
|
proc moveMem*(dest, source: pointer, size: Natural) {.inline, benign,
|
||||||
tags: [], locks: 0.}
|
tags: [], locks: 0, raises: [].}
|
||||||
## Copies the contents from the memory at ``source`` to the memory
|
## Copies the contents from the memory at ``source`` to the memory
|
||||||
## at ``dest``.
|
## at ``dest``.
|
||||||
##
|
##
|
||||||
|
|
@ -2263,7 +2262,8 @@ when not defined(nimscript) and not defined(JS):
|
||||||
## and is thus somewhat more safe than ``copyMem``. Like any procedure
|
## and is thus somewhat more safe than ``copyMem``. Like any procedure
|
||||||
## dealing with raw memory this is still **unsafe**, though.
|
## dealing with raw memory this is still **unsafe**, though.
|
||||||
|
|
||||||
proc equalMem*(a, b: pointer, size: Natural): bool {.inline, noSideEffect, tags: [], locks: 0.}
|
proc equalMem*(a, b: pointer, size: Natural): bool {.inline, noSideEffect,
|
||||||
|
tags: [], locks: 0, raises: [].}
|
||||||
## Compares the memory blocks ``a`` and ``b``. ``size`` bytes will
|
## Compares the memory blocks ``a`` and ``b``. ``size`` bytes will
|
||||||
## be compared.
|
## be compared.
|
||||||
##
|
##
|
||||||
|
|
|
||||||
|
|
@ -148,4 +148,4 @@ proc rawWrite*(f: CFilePtr, s: cstring) {.compilerproc, nonreloadable, inline.}
|
||||||
# we cannot throw an exception here!
|
# we cannot throw an exception here!
|
||||||
discard c_fwrite(s, 1, s.len, f)
|
discard c_fwrite(s, 1, s.len, f)
|
||||||
|
|
||||||
{.pop}
|
{.pop.}
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,8 @@ when hostOS == "standalone":
|
||||||
panic(arg)
|
panic(arg)
|
||||||
|
|
||||||
elif defined(nimQuirky) and not defined(nimscript):
|
elif defined(nimQuirky) and not defined(nimscript):
|
||||||
|
import ansi_c
|
||||||
|
|
||||||
proc name(t: typedesc): string {.magic: "TypeTrait".}
|
proc name(t: typedesc): string {.magic: "TypeTrait".}
|
||||||
|
|
||||||
proc sysFatal(exceptn: typedesc, message, arg: string) {.inline, noReturn.} =
|
proc sysFatal(exceptn: typedesc, message, arg: string) {.inline, noReturn.} =
|
||||||
|
|
|
||||||
|
|
@ -147,58 +147,58 @@ proc nimParseBiggestFloat(s: string, number: var BiggestFloat,
|
||||||
has_sign = false
|
has_sign = false
|
||||||
|
|
||||||
# Sign?
|
# Sign?
|
||||||
if s[i] == '+' or s[i] == '-':
|
if i < s.len and (s[i] == '+' or s[i] == '-'):
|
||||||
has_sign = true
|
has_sign = true
|
||||||
if s[i] == '-':
|
if s[i] == '-':
|
||||||
sign = -1.0
|
sign = -1.0
|
||||||
inc(i)
|
inc(i)
|
||||||
|
|
||||||
# NaN?
|
# NaN?
|
||||||
if s[i] == 'N' or s[i] == 'n':
|
if i+2 < s.len and (s[i] == 'N' or s[i] == 'n'):
|
||||||
if s[i+1] == 'A' or s[i+1] == 'a':
|
if s[i+1] == 'A' or s[i+1] == 'a':
|
||||||
if s[i+2] == 'N' or s[i+2] == 'n':
|
if s[i+2] == 'N' or s[i+2] == 'n':
|
||||||
if s[i+3] notin IdentChars:
|
if i+3 >= s.len or s[i+3] notin IdentChars:
|
||||||
number = NaN
|
number = NaN
|
||||||
return i+3 - start
|
return i+3 - start
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
# Inf?
|
# Inf?
|
||||||
if s[i] == 'I' or s[i] == 'i':
|
if i+2 < s.len and (s[i] == 'I' or s[i] == 'i'):
|
||||||
if s[i+1] == 'N' or s[i+1] == 'n':
|
if s[i+1] == 'N' or s[i+1] == 'n':
|
||||||
if s[i+2] == 'F' or s[i+2] == 'f':
|
if s[i+2] == 'F' or s[i+2] == 'f':
|
||||||
if s[i+3] notin IdentChars:
|
if i+3 >= s.len or s[i+3] notin IdentChars:
|
||||||
number = Inf*sign
|
number = Inf*sign
|
||||||
return i+3 - start
|
return i+3 - start
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
if s[i] in {'0'..'9'}:
|
if i < s.len and s[i] in {'0'..'9'}:
|
||||||
first_digit = (s[i].ord - '0'.ord)
|
first_digit = (s[i].ord - '0'.ord)
|
||||||
# Integer part?
|
# Integer part?
|
||||||
while s[i] in {'0'..'9'}:
|
while i < s.len and s[i] in {'0'..'9'}:
|
||||||
inc(kdigits)
|
inc(kdigits)
|
||||||
integer = integer * 10'u64 + (s[i].ord - '0'.ord).uint64
|
integer = integer * 10'u64 + (s[i].ord - '0'.ord).uint64
|
||||||
inc(i)
|
inc(i)
|
||||||
while s[i] == '_': inc(i)
|
while i < s.len and s[i] == '_': inc(i)
|
||||||
|
|
||||||
# Fractional part?
|
# Fractional part?
|
||||||
if s[i] == '.':
|
if i < s.len and s[i] == '.':
|
||||||
inc(i)
|
inc(i)
|
||||||
# if no integer part, Skip leading zeros
|
# if no integer part, Skip leading zeros
|
||||||
if kdigits <= 0:
|
if kdigits <= 0:
|
||||||
while s[i] == '0':
|
while i < s.len and s[i] == '0':
|
||||||
inc(frac_exponent)
|
inc(frac_exponent)
|
||||||
inc(i)
|
inc(i)
|
||||||
while s[i] == '_': inc(i)
|
while i < s.len and s[i] == '_': inc(i)
|
||||||
|
|
||||||
if first_digit == -1 and s[i] in {'0'..'9'}:
|
if first_digit == -1 and i < s.len and s[i] in {'0'..'9'}:
|
||||||
first_digit = (s[i].ord - '0'.ord)
|
first_digit = (s[i].ord - '0'.ord)
|
||||||
# get fractional part
|
# get fractional part
|
||||||
while s[i] in {'0'..'9'}:
|
while i < s.len and s[i] in {'0'..'9'}:
|
||||||
inc(fdigits)
|
inc(fdigits)
|
||||||
inc(frac_exponent)
|
inc(frac_exponent)
|
||||||
integer = integer * 10'u64 + (s[i].ord - '0'.ord).uint64
|
integer = integer * 10'u64 + (s[i].ord - '0'.ord).uint64
|
||||||
inc(i)
|
inc(i)
|
||||||
while s[i] == '_': inc(i)
|
while i < s.len and s[i] == '_': inc(i)
|
||||||
|
|
||||||
# if has no digits: return error
|
# if has no digits: return error
|
||||||
if kdigits + fdigits <= 0 and
|
if kdigits + fdigits <= 0 and
|
||||||
|
|
@ -206,7 +206,7 @@ proc nimParseBiggestFloat(s: string, number: var BiggestFloat,
|
||||||
(i == start + 1 and has_sign)): # or only '+' or '-
|
(i == start + 1 and has_sign)): # or only '+' or '-
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
if s[i] in {'e', 'E'}:
|
if i+1 < s.len and s[i] in {'e', 'E'}:
|
||||||
inc(i)
|
inc(i)
|
||||||
if s[i] == '+' or s[i] == '-':
|
if s[i] == '+' or s[i] == '-':
|
||||||
if s[i] == '-':
|
if s[i] == '-':
|
||||||
|
|
@ -215,10 +215,10 @@ proc nimParseBiggestFloat(s: string, number: var BiggestFloat,
|
||||||
inc(i)
|
inc(i)
|
||||||
if s[i] notin {'0'..'9'}:
|
if s[i] notin {'0'..'9'}:
|
||||||
return 0
|
return 0
|
||||||
while s[i] in {'0'..'9'}:
|
while i < s.len and s[i] in {'0'..'9'}:
|
||||||
exponent = exponent * 10 + (ord(s[i]) - ord('0'))
|
exponent = exponent * 10 + (ord(s[i]) - ord('0'))
|
||||||
inc(i)
|
inc(i)
|
||||||
while s[i] == '_': inc(i) # underscores are allowed and ignored
|
while i < s.len and s[i] == '_': inc(i) # underscores are allowed and ignored
|
||||||
|
|
||||||
var real_exponent = exp_sign*exponent - frac_exponent
|
var real_exponent = exp_sign*exponent - frac_exponent
|
||||||
let exp_negative = real_exponent < 0
|
let exp_negative = real_exponent < 0
|
||||||
|
|
@ -259,12 +259,12 @@ proc nimParseBiggestFloat(s: string, number: var BiggestFloat,
|
||||||
result = i - start
|
result = i - start
|
||||||
i = start
|
i = start
|
||||||
# re-parse without error checking, any error should be handled by the code above.
|
# re-parse without error checking, any error should be handled by the code above.
|
||||||
if s[i] == '.': i.inc
|
if i < s.len and s[i] == '.': i.inc
|
||||||
while s[i] in {'0'..'9','+','-'}:
|
while i < s.len and s[i] in {'0'..'9','+','-'}:
|
||||||
if ti < maxlen:
|
if ti < maxlen:
|
||||||
t[ti] = s[i]; inc(ti)
|
t[ti] = s[i]; inc(ti)
|
||||||
inc(i)
|
inc(i)
|
||||||
while s[i] in {'.', '_'}: # skip underscore and decimal point
|
while i < s.len and s[i] in {'.', '_'}: # skip underscore and decimal point
|
||||||
inc(i)
|
inc(i)
|
||||||
|
|
||||||
# insert exponent
|
# insert exponent
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,8 @@
|
||||||
## to prevent race conditions and improves efficiency. See `the manual for
|
## to prevent race conditions and improves efficiency. See `the manual for
|
||||||
## details of this memory model <manual.html#threads>`_.
|
## details of this memory model <manual.html#threads>`_.
|
||||||
##
|
##
|
||||||
## Example:
|
## Examples
|
||||||
|
## ========
|
||||||
##
|
##
|
||||||
## .. code-block:: Nim
|
## .. code-block:: Nim
|
||||||
##
|
##
|
||||||
|
|
@ -328,7 +329,7 @@ else:
|
||||||
proc createThread*(t: var Thread[void], tp: proc () {.thread, nimcall.}) =
|
proc createThread*(t: var Thread[void], tp: proc () {.thread, nimcall.}) =
|
||||||
createThread[void](t, tp)
|
createThread[void](t, tp)
|
||||||
|
|
||||||
## we need to cache current threadId to not perform syscall all the time
|
# we need to cache current threadId to not perform syscall all the time
|
||||||
var threadId {.threadvar.}: int
|
var threadId {.threadvar.}: int
|
||||||
|
|
||||||
when defined(windows):
|
when defined(windows):
|
||||||
|
|
|
||||||
|
|
@ -878,7 +878,9 @@ function main() {
|
||||||
<span class="Identifier">subdir</span> <span class="Operator">/</span> <span class="Identifier">subdir_b</span> <span class="Operator">/</span> <span class="Identifier">utils</span>
|
<span class="Identifier">subdir</span> <span class="Operator">/</span> <span class="Identifier">subdir_b</span> <span class="Operator">/</span> <span class="Identifier">utils</span>
|
||||||
|
|
||||||
<span class="Identifier">doAssert</span> <span class="Identifier">bar</span><span class="Other">(</span><span class="DecNumber">3</span><span class="Other">,</span> <span class="DecNumber">4</span><span class="Other">)</span> <span class="Operator">==</span> <span class="DecNumber">7</span>
|
<span class="Identifier">doAssert</span> <span class="Identifier">bar</span><span class="Other">(</span><span class="DecNumber">3</span><span class="Other">,</span> <span class="DecNumber">4</span><span class="Other">)</span> <span class="Operator">==</span> <span class="DecNumber">7</span>
|
||||||
<span class="Identifier">foo</span><span class="Other">(</span><span class="Identifier">enumValueA</span><span class="Other">,</span> <span class="Identifier">enumValueB</span><span class="Other">)</span></pre></p>
|
<span class="Identifier">foo</span><span class="Other">(</span><span class="Identifier">enumValueA</span><span class="Other">,</span> <span class="Identifier">enumValueB</span><span class="Other">)</span>
|
||||||
|
<span class="Keyword">for</span> <span class="Identifier">x</span> <span class="Keyword">in</span> <span class="StringLit">"xx"</span><span class="Other">:</span>
|
||||||
|
<span class="Keyword">discard</span></pre></p>
|
||||||
<div class="section" id="6">
|
<div class="section" id="6">
|
||||||
<h1><a class="toc-backref" href="#6">Imports</a></h1>
|
<h1><a class="toc-backref" href="#6">Imports</a></h1>
|
||||||
<dl class="item">
|
<dl class="item">
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,8 @@ runnableExamples:
|
||||||
import subdir / subdir_b / utils
|
import subdir / subdir_b / utils
|
||||||
doAssert bar(3, 4) == 7
|
doAssert bar(3, 4) == 7
|
||||||
foo(enumValueA, enumValueB)
|
foo(enumValueA, enumValueB)
|
||||||
|
# bug #11078
|
||||||
|
for x in "xx": discard
|
||||||
|
|
||||||
|
|
||||||
template foo*(a, b: SomeType) =
|
template foo*(a, b: SomeType) =
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,7 @@
|
||||||
## of the compiler.
|
## of the compiler.
|
||||||
|
|
||||||
import important_packages
|
import important_packages
|
||||||
|
import sequtils
|
||||||
|
|
||||||
const
|
const
|
||||||
specialCategories = [
|
specialCategories = [
|
||||||
|
|
@ -514,30 +515,21 @@ proc testNimblePackages(r: var TResults, cat: Category) =
|
||||||
let buildPath = packagesDir / name
|
let buildPath = packagesDir / name
|
||||||
if not existsDir(buildPath):
|
if not existsDir(buildPath):
|
||||||
if hasDep:
|
if hasDep:
|
||||||
let nimbleProcess = startProcess("nimble", "", ["install", "-y", name],
|
let (nimbleOutput, nimbleStatus) = execCmdEx2("nimble", ["install", "-y", name])
|
||||||
options = {poUsePath, poStdErrToStdOut})
|
|
||||||
let nimbleStatus = waitForExitEx(nimbleProcess)
|
|
||||||
nimbleProcess.close
|
|
||||||
if nimbleStatus != QuitSuccess:
|
if nimbleStatus != QuitSuccess:
|
||||||
r.addResult(test, targetC, "", "'nimble install' failed", reInstallFailed)
|
r.addResult(test, targetC, "", "'nimble install' failed\n" & nimbleOutput, reInstallFailed)
|
||||||
continue
|
continue
|
||||||
|
|
||||||
let installProcess = startProcess("git", "", ["clone", url, buildPath],
|
let (installOutput, installStatus) = execCmdEx2("git", ["clone", url, buildPath])
|
||||||
options = {poUsePath, poStdErrToStdOut})
|
|
||||||
let installStatus = waitForExitEx(installProcess)
|
|
||||||
installProcess.close
|
|
||||||
if installStatus != QuitSuccess:
|
if installStatus != QuitSuccess:
|
||||||
r.addResult(test, targetC, "", "'git clone' failed", reInstallFailed)
|
r.addResult(test, targetC, "", "'git clone' failed\n" & installOutput, reInstallFailed)
|
||||||
continue
|
continue
|
||||||
|
|
||||||
let cmdArgs = parseCmdLine(cmd)
|
let cmdArgs = parseCmdLine(cmd)
|
||||||
let buildProcess = startProcess(cmdArgs[0], buildPath, cmdArgs[1..^1],
|
|
||||||
options = {poUsePath, poStdErrToStdOut})
|
|
||||||
let buildStatus = waitForExitEx(buildProcess)
|
|
||||||
buildProcess.close
|
|
||||||
|
|
||||||
|
let (buildOutput, buildStatus) = execCmdEx2(cmdArgs[0], cmdArgs[1..^1], workingDir=buildPath)
|
||||||
if buildStatus != QuitSuccess:
|
if buildStatus != QuitSuccess:
|
||||||
r.addResult(test, targetC, "", "package test failed", reBuildFailed)
|
r.addResult(test, targetC, "", "package test failed\n" & buildOutput, reBuildFailed)
|
||||||
else:
|
else:
|
||||||
inc r.passed
|
inc r.passed
|
||||||
r.addResult(test, targetC, "", "", reSuccess)
|
r.addResult(test, targetC, "", "", reSuccess)
|
||||||
|
|
@ -653,8 +645,7 @@ proc runJoinedTest(r: var TResults, cat: Category, testsDir: string) =
|
||||||
|
|
||||||
let args = ["c", "--nimCache:" & outDir, "-d:testing", "--listCmd", "megatest.nim"]
|
let args = ["c", "--nimCache:" & outDir, "-d:testing", "--listCmd", "megatest.nim"]
|
||||||
proc onStdout(line: string) = echo line
|
proc onStdout(line: string) = echo line
|
||||||
var (buf, exitCode) = execCmdEx2(command = compilerPrefix, args = args, options = {poStdErrToStdOut, poUsePath}, input = "",
|
var (buf, exitCode) = execCmdEx2(command = compilerPrefix, args = args, input = "")
|
||||||
onStdout = if verboseMegatest: onStdout else: nil)
|
|
||||||
if exitCode != 0:
|
if exitCode != 0:
|
||||||
echo buf.string
|
echo buf.string
|
||||||
quit("megatest compilation failed")
|
quit("megatest compilation failed")
|
||||||
|
|
@ -688,8 +679,22 @@ proc runJoinedTest(r: var TResults, cat: Category, testsDir: string) =
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
proc processCategory(r: var TResults, cat: Category, options, testsDir: string,
|
proc loadSkipFrom(name: string): seq[string] =
|
||||||
|
# One skip per line, comments start with #
|
||||||
|
# used by `nlvm` (at least)
|
||||||
|
try:
|
||||||
|
for line in lines(name):
|
||||||
|
let sline = line.strip()
|
||||||
|
if sline.len > 0 and not sline.startsWith("#"):
|
||||||
|
result.add sline
|
||||||
|
except:
|
||||||
|
echo "Could not load " & name & ", ignoring"
|
||||||
|
|
||||||
|
proc processCategory(r: var TResults, cat: Category,
|
||||||
|
options, testsDir, skipFrom: string,
|
||||||
runJoinableTests: bool) =
|
runJoinableTests: bool) =
|
||||||
|
let skips = loadSkipFrom(skipFrom)
|
||||||
|
|
||||||
case cat.string.normalize
|
case cat.string.normalize
|
||||||
of "rodfiles":
|
of "rodfiles":
|
||||||
when false:
|
when false:
|
||||||
|
|
@ -745,6 +750,9 @@ proc processCategory(r: var TResults, cat: Category, options, testsDir: string,
|
||||||
|
|
||||||
for i, name in files:
|
for i, name in files:
|
||||||
var test = makeTest(name, options, cat)
|
var test = makeTest(name, options, cat)
|
||||||
|
if skips.anyIt(it in name):
|
||||||
|
test.spec.err = reDisabled
|
||||||
|
|
||||||
if runJoinableTests or not isJoinableSpec(test.spec) or cat.string in specialCategories:
|
if runJoinableTests or not isJoinableSpec(test.spec) or cat.string in specialCategories:
|
||||||
discard "run the test"
|
discard "run the test"
|
||||||
else:
|
else:
|
||||||
|
|
|
||||||
|
|
@ -19,8 +19,6 @@ include compiler/nodejs
|
||||||
var useColors = true
|
var useColors = true
|
||||||
var backendLogging = true
|
var backendLogging = true
|
||||||
var simulate = false
|
var simulate = false
|
||||||
var verboseMegatest = false # very verbose but can be useful
|
|
||||||
var verboseCommands = false
|
|
||||||
|
|
||||||
const
|
const
|
||||||
testsDir = "tests" & DirSep
|
testsDir = "tests" & DirSep
|
||||||
|
|
@ -39,8 +37,6 @@ Arguments:
|
||||||
arguments are passed to the compiler
|
arguments are passed to the compiler
|
||||||
Options:
|
Options:
|
||||||
--print also print results to the console
|
--print also print results to the console
|
||||||
--verboseMegatest log to stdout megatetest compilation
|
|
||||||
--verboseCommands log to stdout info about commands being run
|
|
||||||
--simulate see what tests would be run but don't run them (for debugging)
|
--simulate see what tests would be run but don't run them (for debugging)
|
||||||
--failing only show failing/ignored tests
|
--failing only show failing/ignored tests
|
||||||
--targets:"c c++ js objc" run tests for specified targets (default: all)
|
--targets:"c c++ js objc" run tests for specified targets (default: all)
|
||||||
|
|
@ -48,6 +44,7 @@ Options:
|
||||||
--directory:dir Change to directory dir before reading the tests or doing anything else.
|
--directory:dir Change to directory dir before reading the tests or doing anything else.
|
||||||
--colors:on|off Turn messagescoloring on|off.
|
--colors:on|off Turn messagescoloring on|off.
|
||||||
--backendLogging:on|off Disable or enable backend logging. By default turned on.
|
--backendLogging:on|off Disable or enable backend logging. By default turned on.
|
||||||
|
--skipFrom:file Read tests to skip from `file` - one test per line, # comments ignored
|
||||||
""" % resultsFile
|
""" % resultsFile
|
||||||
|
|
||||||
type
|
type
|
||||||
|
|
@ -91,11 +88,11 @@ proc getFileDir(filename: string): string =
|
||||||
if not result.isAbsolute():
|
if not result.isAbsolute():
|
||||||
result = getCurrentDir() / result
|
result = getCurrentDir() / result
|
||||||
|
|
||||||
proc execCmdEx2(command: string, args: openarray[string], options: set[ProcessOption], input: string, onStdout: proc(line: string) = nil): tuple[
|
proc execCmdEx2(command: string, args: openarray[string]; workingDir, input: string = ""): tuple[
|
||||||
output: TaintedString,
|
output: TaintedString,
|
||||||
exitCode: int] {.tags:
|
exitCode: int] {.tags:
|
||||||
[ExecIOEffect, ReadIOEffect, RootEffect], gcsafe.} =
|
[ExecIOEffect, ReadIOEffect, RootEffect], gcsafe.} =
|
||||||
var p = startProcess(command, args=args, options=options)
|
var p = startProcess(command, workingDir=workingDir, args=args, options={poStdErrToStdOut, poUsePath})
|
||||||
var outp = outputStream(p)
|
var outp = outputStream(p)
|
||||||
|
|
||||||
# There is no way to provide input for the child process
|
# There is no way to provide input for the child process
|
||||||
|
|
@ -111,20 +108,11 @@ proc execCmdEx2(command: string, args: openarray[string], options: set[ProcessOp
|
||||||
if outp.readLine(line):
|
if outp.readLine(line):
|
||||||
result.output.string.add(line.string)
|
result.output.string.add(line.string)
|
||||||
result.output.string.add("\n")
|
result.output.string.add("\n")
|
||||||
if onStdout != nil: onStdout(line.string)
|
|
||||||
else:
|
else:
|
||||||
result.exitCode = peekExitCode(p)
|
result.exitCode = peekExitCode(p)
|
||||||
if result.exitCode != -1: break
|
if result.exitCode != -1: break
|
||||||
close(p)
|
close(p)
|
||||||
|
|
||||||
if verboseCommands:
|
|
||||||
var command2 = command
|
|
||||||
if args.len > 0: command2.add " " & args.quoteShellCommand
|
|
||||||
echo (msg: "execCmdEx2",
|
|
||||||
command: command2,
|
|
||||||
options: options,
|
|
||||||
exitCode: result.exitCode)
|
|
||||||
|
|
||||||
proc nimcacheDir(filename, options: string, target: TTarget): string =
|
proc nimcacheDir(filename, options: string, target: TTarget): string =
|
||||||
## Give each test a private nimcache dir so they don't clobber each other's.
|
## Give each test a private nimcache dir so they don't clobber each other's.
|
||||||
let hashInput = options & $target
|
let hashInput = options & $target
|
||||||
|
|
@ -266,17 +254,25 @@ proc addResult(r: var TResults, test: TTest, target: TTarget,
|
||||||
maybeStyledEcho styleBright, fgRed, "FAIL: ", fgCyan, name
|
maybeStyledEcho styleBright, fgRed, "FAIL: ", fgCyan, name
|
||||||
maybeStyledEcho styleBright, fgCyan, "Test \"", test.name, "\"", " in category \"", test.cat.string, "\""
|
maybeStyledEcho styleBright, fgCyan, "Test \"", test.name, "\"", " in category \"", test.cat.string, "\""
|
||||||
maybeStyledEcho styleBright, fgRed, "Failure: ", $success
|
maybeStyledEcho styleBright, fgRed, "Failure: ", $success
|
||||||
maybeStyledEcho fgYellow, "Expected:"
|
if success in {reBuildFailed, reNimcCrash, reInstallFailed}:
|
||||||
maybeStyledEcho styleBright, expected, "\n"
|
# expected is empty, no reason to print it.
|
||||||
maybeStyledEcho fgYellow, "Gotten:"
|
echo given
|
||||||
maybeStyledEcho styleBright, given, "\n"
|
else:
|
||||||
|
maybeStyledEcho fgYellow, "Expected:"
|
||||||
|
maybeStyledEcho styleBright, expected, "\n"
|
||||||
|
maybeStyledEcho fgYellow, "Gotten:"
|
||||||
|
maybeStyledEcho styleBright, given, "\n"
|
||||||
|
|
||||||
|
|
||||||
if backendLogging and existsEnv("APPVEYOR"):
|
if backendLogging and existsEnv("APPVEYOR"):
|
||||||
let (outcome, msg) =
|
let (outcome, msg) =
|
||||||
if success == reSuccess:
|
case success
|
||||||
|
of reSuccess:
|
||||||
("Passed", "")
|
("Passed", "")
|
||||||
elif success in {reDisabled, reJoined}:
|
of reDisabled, reJoined:
|
||||||
("Skipped", "")
|
("Skipped", "")
|
||||||
|
of reBuildFailed, reNimcCrash, reInstallFailed:
|
||||||
|
("Failed", "Failure: " & $success & "\n" & given)
|
||||||
else:
|
else:
|
||||||
("Failed", "Failure: " & $success & "\nExpected:\n" & expected & "\n\n" & "Gotten:\n" & given)
|
("Failed", "Failure: " & $success & "\nExpected:\n" & expected & "\n\n" & "Gotten:\n" & given)
|
||||||
var p = startProcess("appveyor", args=["AddTest", test.name.replace("\\", "/") & test.options,
|
var p = startProcess("appveyor", args=["AddTest", test.name.replace("\\", "/") & test.options,
|
||||||
|
|
@ -320,7 +316,7 @@ proc generatedFile(test: TTest, target: TTarget): string =
|
||||||
let (_, name, _) = test.name.splitFile
|
let (_, name, _) = test.name.splitFile
|
||||||
let ext = targetToExt[target]
|
let ext = targetToExt[target]
|
||||||
result = nimcacheDir(test.name, test.options, target) /
|
result = nimcacheDir(test.name, test.options, target) /
|
||||||
("compiler_" & name.changeFileExt(ext))
|
name.replace("_", "__").changeFileExt(ext)
|
||||||
|
|
||||||
proc needsCodegenCheck(spec: TSpec): bool =
|
proc needsCodegenCheck(spec: TSpec): bool =
|
||||||
result = spec.maxCodeSize > 0 or spec.ccodeCheck.len > 0
|
result = spec.maxCodeSize > 0 or spec.ccodeCheck.len > 0
|
||||||
|
|
@ -424,7 +420,7 @@ proc testSpec(r: var TResults, test: TTest, targets: set[TTarget] = {}) =
|
||||||
# nested conditionals - the empty rows in between to clarify the "danger"
|
# nested conditionals - the empty rows in between to clarify the "danger"
|
||||||
var given = callCompiler(expected.getCmd, test.name, test.options, target)
|
var given = callCompiler(expected.getCmd, test.name, test.options, target)
|
||||||
if given.err != reSuccess:
|
if given.err != reSuccess:
|
||||||
r.addResult(test, target, "", given.msg, given.err)
|
r.addResult(test, target, "", given.nimout, given.err)
|
||||||
continue
|
continue
|
||||||
let isJsTarget = target == targetJS
|
let isJsTarget = target == targetJS
|
||||||
var exeFile = changeFileExt(test.name, if isJsTarget: "js" else: ExeExt)
|
var exeFile = changeFileExt(test.name, if isJsTarget: "js" else: ExeExt)
|
||||||
|
|
@ -445,7 +441,7 @@ proc testSpec(r: var TResults, test: TTest, targets: set[TTarget] = {}) =
|
||||||
args = concat(@[exeFile], args)
|
args = concat(@[exeFile], args)
|
||||||
else:
|
else:
|
||||||
exeCmd = exeFile
|
exeCmd = exeFile
|
||||||
var (buf, exitCode) = execCmdEx2(exeCmd, args, options = {poStdErrToStdOut}, input = expected.input)
|
var (buf, exitCode) = execCmdEx2(exeCmd, args, input = expected.input)
|
||||||
# Treat all failure codes from nodejs as 1. Older versions of nodejs used
|
# Treat all failure codes from nodejs as 1. Older versions of nodejs used
|
||||||
# to return other codes, but for us it is sufficient to know that it's not 0.
|
# to return other codes, but for us it is sufficient to know that it's not 0.
|
||||||
if exitCode != 0: exitCode = 1
|
if exitCode != 0: exitCode = 1
|
||||||
|
|
@ -514,6 +510,8 @@ const disabledFilesDefault = @[
|
||||||
"LockFreeHash.nim",
|
"LockFreeHash.nim",
|
||||||
"sharedstrings.nim",
|
"sharedstrings.nim",
|
||||||
"tableimpl.nim",
|
"tableimpl.nim",
|
||||||
|
"setimpl.nim",
|
||||||
|
"hashcommon.nim",
|
||||||
|
|
||||||
# Error: undeclared identifier: 'hasThreadSupport'
|
# Error: undeclared identifier: 'hasThreadSupport'
|
||||||
"ioselectors_epoll.nim",
|
"ioselectors_epoll.nim",
|
||||||
|
|
@ -545,14 +543,13 @@ proc main() =
|
||||||
var optFailing = false
|
var optFailing = false
|
||||||
var targetsStr = ""
|
var targetsStr = ""
|
||||||
var isMainProcess = true
|
var isMainProcess = true
|
||||||
|
var skipFrom = ""
|
||||||
|
|
||||||
var p = initOptParser()
|
var p = initOptParser()
|
||||||
p.next()
|
p.next()
|
||||||
while p.kind == cmdLongoption:
|
while p.kind == cmdLongoption:
|
||||||
case p.key.string.normalize
|
case p.key.string.normalize
|
||||||
of "print", "verbose": optPrintResults = true
|
of "print", "verbose": optPrintResults = true
|
||||||
of "verbosemegatest": verboseMegatest = true
|
|
||||||
of "verbosecommands": verboseCommands = true
|
|
||||||
of "failing": optFailing = true
|
of "failing": optFailing = true
|
||||||
of "pedantic": discard "now always enabled"
|
of "pedantic": discard "now always enabled"
|
||||||
of "targets":
|
of "targets":
|
||||||
|
|
@ -580,6 +577,8 @@ proc main() =
|
||||||
backendLogging = false
|
backendLogging = false
|
||||||
else:
|
else:
|
||||||
quit Usage
|
quit Usage
|
||||||
|
of "skipfrom":
|
||||||
|
skipFrom = p.val.string
|
||||||
else:
|
else:
|
||||||
quit Usage
|
quit Usage
|
||||||
p.next()
|
p.next()
|
||||||
|
|
@ -598,6 +597,9 @@ proc main() =
|
||||||
|
|
||||||
myself &= " " & quoteShell("--nim:" & compilerPrefix)
|
myself &= " " & quoteShell("--nim:" & compilerPrefix)
|
||||||
|
|
||||||
|
if skipFrom.len > 0:
|
||||||
|
myself &= " " & quoteShell("--skipFrom:" & skipFrom)
|
||||||
|
|
||||||
var cats: seq[string]
|
var cats: seq[string]
|
||||||
let rest = if p.cmdLineRest.string.len > 0: " " & p.cmdLineRest.string else: ""
|
let rest = if p.cmdLineRest.string.len > 0: " " & p.cmdLineRest.string else: ""
|
||||||
for kind, dir in walkDir(testsDir):
|
for kind, dir in walkDir(testsDir):
|
||||||
|
|
@ -617,13 +619,13 @@ proc main() =
|
||||||
if simulate:
|
if simulate:
|
||||||
for i, cati in cats:
|
for i, cati in cats:
|
||||||
progressStatus(i)
|
progressStatus(i)
|
||||||
processCategory(r, Category(cati), p.cmdLineRest.string, testsDir, runJoinableTests = false)
|
processCategory(r, Category(cati), p.cmdLineRest.string, testsDir, skipFrom, runJoinableTests = false)
|
||||||
else:
|
else:
|
||||||
quit osproc.execProcesses(cmds, {poEchoCmd, poStdErrToStdOut, poUsePath, poParentStreams}, beforeRunEvent = progressStatus)
|
quit osproc.execProcesses(cmds, {poEchoCmd, poStdErrToStdOut, poUsePath, poParentStreams}, beforeRunEvent = progressStatus)
|
||||||
of "c", "cat", "category":
|
of "c", "cat", "category":
|
||||||
var cat = Category(p.key)
|
var cat = Category(p.key)
|
||||||
p.next
|
p.next
|
||||||
processCategory(r, cat, p.cmdLineRest.string, testsDir, runJoinableTests = true)
|
processCategory(r, cat, p.cmdLineRest.string, testsDir, skipFrom, runJoinableTests = true)
|
||||||
of "pcat":
|
of "pcat":
|
||||||
# 'pcat' is used for running a category in parallel. Currently the only
|
# 'pcat' is used for running a category in parallel. Currently the only
|
||||||
# difference is that we don't want to run joinable tests here as they
|
# difference is that we don't want to run joinable tests here as they
|
||||||
|
|
@ -631,7 +633,7 @@ proc main() =
|
||||||
isMainProcess = false
|
isMainProcess = false
|
||||||
var cat = Category(p.key)
|
var cat = Category(p.key)
|
||||||
p.next
|
p.next
|
||||||
processCategory(r, cat, p.cmdLineRest.string, testsDir, runJoinableTests = false)
|
processCategory(r, cat, p.cmdLineRest.string, testsDir, skipFrom, runJoinableTests = false)
|
||||||
of "r", "run":
|
of "r", "run":
|
||||||
# at least one directory is required in the path, to use as a category name
|
# at least one directory is required in the path, to use as a category name
|
||||||
let pathParts = split(p.key.string, {DirSep, AltSep})
|
let pathParts = split(p.key.string, {DirSep, AltSep})
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@ discard """
|
||||||
output: '''(1, 1)
|
output: '''(1, 1)
|
||||||
(2, 2)
|
(2, 2)
|
||||||
(3, 3)
|
(3, 3)
|
||||||
|
@[1, 2, 3, 4]
|
||||||
'''
|
'''
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
@ -15,3 +16,12 @@ proc foo(args: varargs[int]) =
|
||||||
discard
|
discard
|
||||||
|
|
||||||
foo(1,2,3)
|
foo(1,2,3)
|
||||||
|
|
||||||
|
# 10999
|
||||||
|
|
||||||
|
proc varargsToSeq(vals: varargs[int32]): seq[int32] =
|
||||||
|
result = newSeqOfCap[int32](vals.len)
|
||||||
|
for v in vals:
|
||||||
|
result.add v
|
||||||
|
|
||||||
|
echo varargsToSeq(1, 2, 3, 4)
|
||||||
|
|
|
||||||
|
|
@ -7,8 +7,8 @@ mygeneric1 constructed
|
||||||
mygeneric1 destroyed
|
mygeneric1 destroyed
|
||||||
----
|
----
|
||||||
mygeneric2 constructed
|
mygeneric2 constructed
|
||||||
myobj destroyed
|
|
||||||
mygeneric2 destroyed
|
mygeneric2 destroyed
|
||||||
|
myobj destroyed
|
||||||
----
|
----
|
||||||
mygeneric3 constructed
|
mygeneric3 constructed
|
||||||
mygeneric1 destroyed
|
mygeneric1 destroyed
|
||||||
|
|
@ -20,10 +20,10 @@ mygeneric2 destroyed
|
||||||
----
|
----
|
||||||
----
|
----
|
||||||
myobj destroyed
|
myobj destroyed
|
||||||
|
myobj destroyed
|
||||||
|
myobj destroyed
|
||||||
|
myobj destroyed
|
||||||
mygeneric1 destroyed
|
mygeneric1 destroyed
|
||||||
myobj destroyed
|
|
||||||
myobj destroyed
|
|
||||||
myobj destroyed
|
|
||||||
---
|
---
|
||||||
myobj destroyed
|
myobj destroyed
|
||||||
myobj destroyed
|
myobj destroyed
|
||||||
|
|
|
||||||
|
|
@ -4,8 +4,8 @@ destroy
|
||||||
destroy
|
destroy
|
||||||
5
|
5
|
||||||
123
|
123
|
||||||
destroy Foo: 5
|
destroy Foo: 123
|
||||||
destroy Foo: 123'''
|
destroy Foo: 5'''
|
||||||
joinable: false
|
joinable: false
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
|
||||||
43
tests/destructor/tdont_return_unowned_from_owned.nim
Normal file
43
tests/destructor/tdont_return_unowned_from_owned.nim
Normal file
|
|
@ -0,0 +1,43 @@
|
||||||
|
discard """
|
||||||
|
cmd: "nim check --newruntime --hints:off $file"
|
||||||
|
nimout: '''tdont_return_unowned_from_owned.nim(24, 10) Error: cannot return an owned pointer as an unowned pointer; use 'owned(Obj)' as the return type
|
||||||
|
tdont_return_unowned_from_owned.nim(27, 10) Error: cannot return an owned pointer as an unowned pointer; use 'owned(Obj)' as the return type
|
||||||
|
tdont_return_unowned_from_owned.nim(30, 6) Error: type mismatch: got <Obj>
|
||||||
|
but expected one of:
|
||||||
|
proc new[T](a: var ref T; finalizer: proc (x: ref T) {.nimcall.})
|
||||||
|
2 other mismatching symbols have been suppressed; compile with --showAllMismatches:on to see them
|
||||||
|
|
||||||
|
expression: new(result)
|
||||||
|
tdont_return_unowned_from_owned.nim(30, 6) Error: illformed AST:
|
||||||
|
tdont_return_unowned_from_owned.nim(38, 13) Error: assignment produces a dangling ref: the unowned ref lives longer than the owned ref
|
||||||
|
tdont_return_unowned_from_owned.nim(39, 13) Error: assignment produces a dangling ref: the unowned ref lives longer than the owned ref
|
||||||
|
tdont_return_unowned_from_owned.nim(43, 10) Error: cannot return an owned pointer as an unowned pointer; use 'owned(RootRef)' as the return type
|
||||||
|
'''
|
||||||
|
errormsg: "cannot return an owned pointer as an unowned pointer; use 'owned(RootRef)' as the return type"
|
||||||
|
line: 43
|
||||||
|
"""
|
||||||
|
# bug #11073
|
||||||
|
type
|
||||||
|
Obj = ref object
|
||||||
|
|
||||||
|
proc newObjA(): Obj =
|
||||||
|
result = new Obj
|
||||||
|
|
||||||
|
proc newObjB(): Obj =
|
||||||
|
result = Obj()
|
||||||
|
|
||||||
|
proc newObjC(): Obj =
|
||||||
|
new(result)
|
||||||
|
|
||||||
|
let a = newObjA()
|
||||||
|
let b = newObjB()
|
||||||
|
let c = newObjC()
|
||||||
|
|
||||||
|
proc testA(result: var (RootRef, RootRef)) =
|
||||||
|
let r: owned RootRef = RootRef()
|
||||||
|
result[0] = r
|
||||||
|
result[1] = RootRef()
|
||||||
|
|
||||||
|
proc testB(): RootRef =
|
||||||
|
let r: owned RootRef = RootRef()
|
||||||
|
result = r
|
||||||
|
|
@ -4,7 +4,9 @@ discard """
|
||||||
ho
|
ho
|
||||||
ha
|
ha
|
||||||
@["arg", "asdfklasdfkl", "asdkfj", "dfasj", "klfjl"]
|
@["arg", "asdfklasdfkl", "asdkfj", "dfasj", "klfjl"]
|
||||||
22 22'''
|
@[1, 2, 3]
|
||||||
|
@["red", "yellow", "orange", "rtrt1", "pink"]
|
||||||
|
30 30'''
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import allocators
|
import allocators
|
||||||
|
|
@ -132,6 +134,41 @@ proc other =
|
||||||
|
|
||||||
other()
|
other()
|
||||||
|
|
||||||
|
# bug #11050
|
||||||
|
|
||||||
|
type
|
||||||
|
Obj* = object
|
||||||
|
f*: seq[int]
|
||||||
|
|
||||||
|
method main(o: Obj) {.base.} =
|
||||||
|
for newb in o.f:
|
||||||
|
discard
|
||||||
|
|
||||||
|
# test that o.f was not moved!
|
||||||
|
proc testforNoMove =
|
||||||
|
var o = Obj(f: @[1, 2, 3])
|
||||||
|
main(o)
|
||||||
|
echo o.f
|
||||||
|
|
||||||
|
testforNoMove()
|
||||||
|
|
||||||
|
# bug #11065
|
||||||
|
type
|
||||||
|
Warm = seq[string]
|
||||||
|
|
||||||
|
proc testWarm =
|
||||||
|
var w: Warm
|
||||||
|
w = @["red", "yellow", "orange"]
|
||||||
|
|
||||||
|
var x = "rt"
|
||||||
|
var y = "rt1"
|
||||||
|
w.add(x & y)
|
||||||
|
|
||||||
|
w.add("pink")
|
||||||
|
echo w
|
||||||
|
|
||||||
|
testWarm()
|
||||||
|
|
||||||
#echo s
|
#echo s
|
||||||
let (a, d) = allocCounters()
|
let (a, d) = allocCounters()
|
||||||
discard cprintf("%ld %ld\n", a, d)
|
discard cprintf("%ld %ld\n", a, d)
|
||||||
|
|
|
||||||
|
|
@ -2,30 +2,46 @@ discard """
|
||||||
output: '''works'''
|
output: '''works'''
|
||||||
"""
|
"""
|
||||||
|
|
||||||
type
|
# bug #11095
|
||||||
MyVal = object
|
|
||||||
f: ptr float
|
|
||||||
|
|
||||||
proc `=destroy`(x: var MyVal) =
|
type
|
||||||
|
MyVal[T] = object
|
||||||
|
f: ptr T
|
||||||
|
|
||||||
|
proc `=destroy`[T](x: var MyVal[T]) =
|
||||||
if x.f != nil:
|
if x.f != nil:
|
||||||
dealloc(x.f)
|
dealloc(x.f)
|
||||||
|
|
||||||
proc `=sink`(x1: var MyVal, x2: Myval) =
|
proc `=sink`[T](x1: var MyVal[T], x2: MyVal[T]) =
|
||||||
if x1.f != x2.f:
|
if x1.f != x2.f:
|
||||||
`=destroy`(x1)
|
`=destroy`(x1)
|
||||||
x1.f = x2.f
|
x1.f = x2.f
|
||||||
|
|
||||||
proc `=`(x1: var MyVal, x2: Myval) {.error.}
|
proc `=`[T](x1: var MyVal[T], x2: MyVal[T]) {.error.}
|
||||||
|
|
||||||
proc newVal(x: float): MyVal =
|
proc newVal[T](x: sink T): MyVal[T] =
|
||||||
result.f = create(float)
|
result.f = create(T)
|
||||||
result.f[] = x
|
result.f[] = x
|
||||||
|
|
||||||
proc sinkMe(x: sink MyVal) =
|
proc set[T](x: var MyVal[T], val: T) =
|
||||||
|
x.f[] = val
|
||||||
|
|
||||||
|
proc sinkMe[T](x: sink MyVal[T]) =
|
||||||
discard
|
discard
|
||||||
|
|
||||||
|
var flag = false
|
||||||
|
|
||||||
proc main =
|
proc main =
|
||||||
var y = (newVal(3.0), newVal(4.0))
|
var y = case flag
|
||||||
|
of true:
|
||||||
|
var x1 = newVal[float](1.0)
|
||||||
|
var x2 = newVal[float](2.0)
|
||||||
|
(newVal(x1), newVal(x2))
|
||||||
|
|
||||||
|
of false:
|
||||||
|
var x1 = newVal[float](1.0)
|
||||||
|
var x2 = newVal[float](2.0)
|
||||||
|
(newVal(x1), newVal(x2))
|
||||||
|
|
||||||
sinkMe y[0]
|
sinkMe y[0]
|
||||||
sinkMe y[1]
|
sinkMe y[1]
|
||||||
|
|
|
||||||
92
tests/destructor/towned_binary_tree.nim
Normal file
92
tests/destructor/towned_binary_tree.nim
Normal file
|
|
@ -0,0 +1,92 @@
|
||||||
|
discard """
|
||||||
|
cmd: '''nim c --newruntime $file'''
|
||||||
|
output: '''331665
|
||||||
|
allocs 0'''
|
||||||
|
"""
|
||||||
|
|
||||||
|
# bug #11053
|
||||||
|
|
||||||
|
import random
|
||||||
|
|
||||||
|
type Node = ref object
|
||||||
|
x, y: int32
|
||||||
|
left, right: owned Node
|
||||||
|
|
||||||
|
proc newNode(x: int32): owned Node =
|
||||||
|
result = Node(x: x, y: rand(high int32).int32)
|
||||||
|
|
||||||
|
proc merge(lower, greater: owned Node): owned Node =
|
||||||
|
if lower.isNil:
|
||||||
|
result = greater
|
||||||
|
elif greater.isNil:
|
||||||
|
result = lower
|
||||||
|
elif lower.y < greater.y:
|
||||||
|
lower.right = merge(lower.right, greater)
|
||||||
|
result = lower
|
||||||
|
else:
|
||||||
|
greater.left = merge(lower, greater.left)
|
||||||
|
result = greater
|
||||||
|
|
||||||
|
proc splitBinary(orig: owned Node, value: int32): (owned Node, owned Node) =
|
||||||
|
if orig.isNil:
|
||||||
|
result = (nil, nil)
|
||||||
|
elif orig.x < value:
|
||||||
|
let splitPair = splitBinary(orig.right, value)
|
||||||
|
orig.right = splitPair[0]
|
||||||
|
result = (orig, splitPair[1])
|
||||||
|
else:
|
||||||
|
let splitPair = splitBinary(orig.left, value)
|
||||||
|
orig.left = splitPair[1]
|
||||||
|
result = (splitPair[0], orig)
|
||||||
|
|
||||||
|
proc merge3(lower, equal, greater: owned Node): owned Node =
|
||||||
|
merge(merge(lower, equal), greater)
|
||||||
|
|
||||||
|
proc split(orig: owned Node, value: int32): tuple[lower, equal, greater: owned Node] =
|
||||||
|
let
|
||||||
|
(lower, equalGreater) = splitBinary(orig, value)
|
||||||
|
(equal, greater) = splitBinary(equalGreater, value + 1)
|
||||||
|
result = (lower, equal, greater)
|
||||||
|
|
||||||
|
type Tree = object
|
||||||
|
root: owned Node
|
||||||
|
|
||||||
|
proc hasValue(self: var Tree, x: int32): bool =
|
||||||
|
let splited = split(move self.root, x)
|
||||||
|
result = not splited.equal.isNil
|
||||||
|
self.root = merge3(splited.lower, splited.equal, splited.greater)
|
||||||
|
|
||||||
|
proc insert(self: var Tree, x: int32) =
|
||||||
|
var splited = split(move self.root, x)
|
||||||
|
if splited.equal.isNil:
|
||||||
|
splited.equal = newNode(x)
|
||||||
|
self.root = merge3(splited.lower, splited.equal, splited.greater)
|
||||||
|
|
||||||
|
proc erase(self: var Tree, x: int32) =
|
||||||
|
let splited = split(move self.root, x)
|
||||||
|
self.root = merge(splited.lower, splited.greater)
|
||||||
|
|
||||||
|
proc main() =
|
||||||
|
var
|
||||||
|
tree = Tree()
|
||||||
|
cur = 5'i32
|
||||||
|
res = 0
|
||||||
|
|
||||||
|
for i in 1 ..< 1000000:
|
||||||
|
let a = i mod 3
|
||||||
|
cur = (cur * 57 + 43) mod 10007
|
||||||
|
case a:
|
||||||
|
of 0:
|
||||||
|
tree.insert(cur)
|
||||||
|
of 1:
|
||||||
|
tree.erase(cur)
|
||||||
|
of 2:
|
||||||
|
if tree.hasValue(cur):
|
||||||
|
res += 1
|
||||||
|
else:
|
||||||
|
discard
|
||||||
|
echo res
|
||||||
|
|
||||||
|
when isMainModule:
|
||||||
|
main()
|
||||||
|
echo "allocs ", allocs
|
||||||
78
tests/destructor/twidgets.nim
Normal file
78
tests/destructor/twidgets.nim
Normal file
|
|
@ -0,0 +1,78 @@
|
||||||
|
discard """
|
||||||
|
cmd: '''nim c --newruntime $file'''
|
||||||
|
output: '''button
|
||||||
|
clicked!
|
||||||
|
1 1 alloc/dealloc pairs: 0'''
|
||||||
|
"""
|
||||||
|
|
||||||
|
import core / allocators
|
||||||
|
import system / ansi_c
|
||||||
|
|
||||||
|
type
|
||||||
|
Widget* = ref object of RootObj
|
||||||
|
drawImpl: owned(proc (self: Widget))
|
||||||
|
|
||||||
|
Button* = ref object of Widget
|
||||||
|
caption: string
|
||||||
|
onclick: owned(proc())
|
||||||
|
|
||||||
|
Window* = ref object of Widget
|
||||||
|
elements: seq[owned Widget]
|
||||||
|
|
||||||
|
|
||||||
|
proc newButton(caption: string; onclick: owned(proc())): owned Button =
|
||||||
|
proc draw(self: Widget) =
|
||||||
|
let b = Button(self)
|
||||||
|
echo b.caption
|
||||||
|
|
||||||
|
#result = Button(drawImpl: draw, caption: caption, onclick: onclick)
|
||||||
|
new(result)
|
||||||
|
result.drawImpl = draw
|
||||||
|
result.caption = caption
|
||||||
|
result.onclick = onclick
|
||||||
|
|
||||||
|
iterator unitems*[T](a: seq[owned T]): T {.inline.} =
|
||||||
|
## Iterates over each item of `a`.
|
||||||
|
var i = 0
|
||||||
|
let L = len(a)
|
||||||
|
while i < L:
|
||||||
|
yield a[i]
|
||||||
|
inc(i)
|
||||||
|
assert(len(a) == L, "seq modified while iterating over it")
|
||||||
|
|
||||||
|
proc newWindow(): owned Window =
|
||||||
|
proc windraw(self: Widget) =
|
||||||
|
let w = Window(self)
|
||||||
|
for i in 0..<len(w.elements):
|
||||||
|
let e = Widget(w.elements[i])
|
||||||
|
let d = (proc(self: Widget))e.drawImpl
|
||||||
|
if not d.isNil: d(e)
|
||||||
|
|
||||||
|
result = Window(drawImpl: windraw, elements: @[])
|
||||||
|
|
||||||
|
proc draw(w: Widget) =
|
||||||
|
let d = (proc(self: Widget))w.drawImpl
|
||||||
|
if not d.isNil: d(w)
|
||||||
|
|
||||||
|
proc add*(w: Window; elem: owned Widget) =
|
||||||
|
w.elements.add elem
|
||||||
|
|
||||||
|
proc main =
|
||||||
|
var w = newWindow()
|
||||||
|
|
||||||
|
var b = newButton("button", nil)
|
||||||
|
let u: Button = b
|
||||||
|
b.onclick = proc () =
|
||||||
|
b.caption = "clicked!"
|
||||||
|
w.add b
|
||||||
|
|
||||||
|
w.draw()
|
||||||
|
# simulate button click:
|
||||||
|
u.onclick()
|
||||||
|
|
||||||
|
w.draw()
|
||||||
|
|
||||||
|
main()
|
||||||
|
|
||||||
|
let (a, d) = allocCounters()
|
||||||
|
discard cprintf("%ld %ld alloc/dealloc pairs: %ld\n", a, d, allocs)
|
||||||
68
tests/destructor/twidgets_unown.nim
Normal file
68
tests/destructor/twidgets_unown.nim
Normal file
|
|
@ -0,0 +1,68 @@
|
||||||
|
discard """
|
||||||
|
cmd: '''nim c --newruntime $file'''
|
||||||
|
output: '''button
|
||||||
|
clicked!
|
||||||
|
3 3 alloc/dealloc pairs: 0'''
|
||||||
|
"""
|
||||||
|
|
||||||
|
import core / allocators
|
||||||
|
import system / ansi_c
|
||||||
|
|
||||||
|
type
|
||||||
|
Widget* = ref object of RootObj
|
||||||
|
drawImpl: owned(proc (self: Widget))
|
||||||
|
|
||||||
|
Button* = ref object of Widget
|
||||||
|
caption: string
|
||||||
|
onclick: owned(proc())
|
||||||
|
|
||||||
|
Window* = ref object of Widget
|
||||||
|
elements: seq[owned Widget]
|
||||||
|
|
||||||
|
|
||||||
|
proc newButton(caption: string; onclick: owned(proc())): owned Button =
|
||||||
|
proc draw(self: Widget) =
|
||||||
|
let b = Button(self)
|
||||||
|
echo b.caption
|
||||||
|
|
||||||
|
#result = Button(drawImpl: draw, caption: caption, onclick: onclick)
|
||||||
|
new(result)
|
||||||
|
result.drawImpl = draw
|
||||||
|
result.caption = caption
|
||||||
|
result.onclick = onclick
|
||||||
|
|
||||||
|
proc newWindow(): owned Window =
|
||||||
|
proc windraw(self: Widget) =
|
||||||
|
let w = Window(self)
|
||||||
|
for e in unown(w.elements):
|
||||||
|
let d = unown e.drawImpl
|
||||||
|
if not d.isNil: d(e)
|
||||||
|
|
||||||
|
result = Window(drawImpl: windraw, elements: @[])
|
||||||
|
|
||||||
|
proc draw(w: Widget) =
|
||||||
|
let d = unown w.drawImpl
|
||||||
|
if not d.isNil: d(w)
|
||||||
|
|
||||||
|
proc add*(w: Window; elem: owned Widget) =
|
||||||
|
w.elements.add elem
|
||||||
|
|
||||||
|
proc main =
|
||||||
|
var w = newWindow()
|
||||||
|
|
||||||
|
var b = newButton("button", nil)
|
||||||
|
let u = unown b
|
||||||
|
b.onclick = proc () =
|
||||||
|
b.caption = "clicked!"
|
||||||
|
w.add b
|
||||||
|
|
||||||
|
w.draw()
|
||||||
|
# simulate button click:
|
||||||
|
u.onclick()
|
||||||
|
|
||||||
|
w.draw()
|
||||||
|
|
||||||
|
main()
|
||||||
|
|
||||||
|
let (a, d) = allocCounters()
|
||||||
|
discard cprintf("%ld %ld alloc/dealloc pairs: %ld\n", a, d, allocs)
|
||||||
|
|
@ -3,6 +3,8 @@ output: '''
|
||||||
tdiscardable
|
tdiscardable
|
||||||
1
|
1
|
||||||
1
|
1
|
||||||
|
something defered
|
||||||
|
something defered
|
||||||
'''
|
'''
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
@ -45,3 +47,21 @@ proc foo: (proc: int) =
|
||||||
return bar
|
return bar
|
||||||
|
|
||||||
discard foo()
|
discard foo()
|
||||||
|
|
||||||
|
# bug #10842
|
||||||
|
|
||||||
|
proc myDiscardable(): int {.discardable.} =
|
||||||
|
discard
|
||||||
|
|
||||||
|
proc main1() =
|
||||||
|
defer:
|
||||||
|
echo "something defered"
|
||||||
|
discard myDiscardable()
|
||||||
|
|
||||||
|
proc main2() =
|
||||||
|
defer:
|
||||||
|
echo "something defered"
|
||||||
|
myDiscardable()
|
||||||
|
|
||||||
|
main1()
|
||||||
|
main2()
|
||||||
|
|
|
||||||
51
tests/macros/tnewproc.nim
Normal file
51
tests/macros/tnewproc.nim
Normal file
|
|
@ -0,0 +1,51 @@
|
||||||
|
import macros
|
||||||
|
|
||||||
|
macro test(a: untyped): untyped =
|
||||||
|
# proc hello*(x: int = 3, y: float32): int {.inline.} = discard
|
||||||
|
let
|
||||||
|
nameNode = nnkPostfix.newTree(
|
||||||
|
newIdentNode("*"),
|
||||||
|
newIdentNode("hello")
|
||||||
|
)
|
||||||
|
params = @[
|
||||||
|
newIdentNode("int"),
|
||||||
|
nnkIdentDefs.newTree(
|
||||||
|
newIdentNode("x"),
|
||||||
|
newIdentNode("int"),
|
||||||
|
newLit(3)
|
||||||
|
),
|
||||||
|
nnkIdentDefs.newTree(
|
||||||
|
newIdentNode("y"),
|
||||||
|
newIdentNode("float32"),
|
||||||
|
newEmptyNode()
|
||||||
|
)
|
||||||
|
]
|
||||||
|
paramsNode = nnkFormalParams.newTree(params)
|
||||||
|
pragmasNode = nnkPragma.newTree(
|
||||||
|
newIdentNode("inline")
|
||||||
|
)
|
||||||
|
bodyNode = nnkStmtList.newTree(
|
||||||
|
nnkDiscardStmt.newTree(
|
||||||
|
newEmptyNode()
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
var
|
||||||
|
expected = nnkProcDef.newTree(
|
||||||
|
nameNode,
|
||||||
|
newEmptyNode(),
|
||||||
|
newEmptyNode(),
|
||||||
|
paramsNode,
|
||||||
|
pragmasNode,
|
||||||
|
newEmptyNode(),
|
||||||
|
bodyNode
|
||||||
|
)
|
||||||
|
|
||||||
|
doAssert expected == newProc(name=nameNode, params=params,
|
||||||
|
body = bodyNode, pragmas=pragmasNode)
|
||||||
|
expected.pragma = newEmptyNode()
|
||||||
|
doAssert expected == newProc(name=nameNode, params=params,
|
||||||
|
body = bodyNode)
|
||||||
|
|
||||||
|
test:
|
||||||
|
42
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
proc flat*() = echo "flat"
|
||||||
|
|
@ -1,7 +1,10 @@
|
||||||
discard """
|
discard """
|
||||||
errormsg: "module names need to be unique per Nimble package"
|
output: '''nested
|
||||||
file: "tnotuniquename/mnotuniquename.nim"
|
flat'''
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import mnotuniquename
|
import mnotuniquename
|
||||||
import tnotuniquename/mnotuniquename as nun
|
import tnotuniquename_dir/mnotuniquename as nun
|
||||||
|
|
||||||
|
nested()
|
||||||
|
flat()
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
discard """
|
discard """
|
||||||
errormsg: "module names need to be unique per Nimble package"
|
errormsg: "module names need to be unique per Nimble package"
|
||||||
file: "tnotuniquename/mnotuniquename.nim"
|
file: "tnotuniquename/mnotuniquename.nim"
|
||||||
|
disabled: "true"
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import mnotuniquename
|
import mnotuniquename
|
||||||
|
|
|
||||||
2
tests/modules/tnotuniquename_dir/mnotuniquename.nim
Normal file
2
tests/modules/tnotuniquename_dir/mnotuniquename.nim
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
|
||||||
|
proc nested*() = echo "nested"
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
discard """
|
discard """
|
||||||
errormsg: "illegal recursion in type 'RefTreeInt'"
|
errormsg: "illegal recursion in type 'RefTree'"
|
||||||
"""
|
"""
|
||||||
|
|
||||||
type
|
type
|
||||||
|
|
|
||||||
9
tests/types/tillegaltuplerecursiongeneric2.nim
Normal file
9
tests/types/tillegaltuplerecursiongeneric2.nim
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
discard """
|
||||||
|
errormsg: "illegal recursion in type 'TPearl'"
|
||||||
|
"""
|
||||||
|
|
||||||
|
type
|
||||||
|
TPearl[T] = tuple
|
||||||
|
next: TPearl[T]
|
||||||
|
|
||||||
|
var x: TPearl[int]
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
discard """
|
discard """
|
||||||
errormsg: "invalid recursion in type 'Executor'"
|
errormsg: "illegal recursion in type 'Executor'"
|
||||||
line: 8
|
line: 8
|
||||||
"""
|
"""
|
||||||
# bug reported by PR #5637
|
# bug reported by PR #5637
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue