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`).
|
||||
* Was it working in the previous Nim releases?
|
||||
* A link to a related issue or discussion.
|
||||
* A project reference where (and how) the issue causes problems.
|
||||
-->
|
||||
```
|
||||
$ nim -v
|
||||
|
|
|
|||
4
.gitignore
vendored
4
.gitignore
vendored
|
|
@ -52,6 +52,8 @@ xcuserdata/
|
|||
/compiler/nim.dot
|
||||
/reject.json
|
||||
/run.json
|
||||
*.json
|
||||
/pkgstemp/**/*
|
||||
# for `nim doc foo.nim`
|
||||
/*.html
|
||||
lib/**/*.html
|
||||
|
|
@ -59,6 +61,8 @@ lib/**/*.html
|
|||
|
||||
/testresults.json
|
||||
testament.db
|
||||
/tests/**/*.json
|
||||
/tests/**/*.js
|
||||
/csources
|
||||
dist/
|
||||
|
||||
|
|
|
|||
21
changelog.md
21
changelog.md
|
|
@ -47,11 +47,17 @@
|
|||
|
||||
- `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
|
||||
`ValueError` when the parsed integer is outside of the valid range.
|
||||
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`,
|
||||
`system.IOError` and `system.OSError`.
|
||||
This change only affects custom stream implementations.
|
||||
|
|
@ -91,6 +97,7 @@
|
|||
- Custom types that should be supported by `strformat` (&) now need an
|
||||
explicit overload of `formatValue`.
|
||||
|
||||
|
||||
#### Breaking changes in the compiler
|
||||
|
||||
- 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.
|
||||
|
||||
- Implicit imports via ``--import: module`` in a config file are now restricted
|
||||
to the main package.
|
||||
|
||||
|
|
@ -152,6 +160,9 @@ proc enumToString*(enums: openArray[enum]): string =
|
|||
|
||||
- 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
|
||||
|
||||
|
|
@ -179,6 +190,7 @@ proc enumToString*(enums: openArray[enum]): string =
|
|||
differently.
|
||||
|
||||
- `securehash` is moved to `lib/deprecated`
|
||||
|
||||
- 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,
|
||||
see [#9647](https://github.com/nim-lang/Nim/issues/9647).
|
||||
The previous extension will continue to work.
|
||||
|
||||
- Pragma syntax is now consistent. Previous syntax where type pragmas did not
|
||||
follow the type name is now deprecated. Also pragma before generic parameter
|
||||
list is deprecated to be consistent with how pragmas are used with a proc. See
|
||||
[#8514](https://github.com/nim-lang/Nim/issues/8514) and
|
||||
[#1872](https://github.com/nim-lang/Nim/issues/1872) for further details.
|
||||
|
||||
- Hash sets and tables are initialized by default. The explicit `initHashSet`,
|
||||
`initTable`, etc. are not needed anymore.
|
||||
|
||||
|
||||
### Tool changes
|
||||
|
||||
- `jsondoc` now include a `moduleDescription` field with the module
|
||||
description. `jsondoc0` shows comments as it's own objects as shown in the
|
||||
documentation.
|
||||
|
|
@ -218,6 +235,7 @@ proc enumToString*(enums: openArray[enum]): string =
|
|||
|
||||
|
||||
### Compiler changes
|
||||
|
||||
- The deprecated `fmod` proc is now unavailable on the VM'.
|
||||
- A new `--outdir` option was added.
|
||||
- The compiled JavaScript file for the project produced by executing `nim js`
|
||||
|
|
@ -226,4 +244,5 @@ proc enumToString*(enums: openArray[enum]): string =
|
|||
The compiler also provides a new more flexible API for handling the
|
||||
hot code reloading events in the code.
|
||||
|
||||
|
||||
### Bugfixes
|
||||
|
|
|
|||
|
|
@ -644,7 +644,7 @@ type
|
|||
mSwap, mIsNil, mArrToSeq, mCopyStr, mCopyStrLast,
|
||||
mNewString, mNewStringOfCap, mParseBiggestFloat,
|
||||
mMove, mWasMoved, mDestroy,
|
||||
mDefault, mAccessEnv, mReset,
|
||||
mDefault, mUnown, mAccessEnv, mReset,
|
||||
mArray, mOpenArray, mRange, mSet, mSeq, mOpt, mVarargs,
|
||||
mRef, mPtr, mVar, mDistinct, mVoid, mTuple,
|
||||
mOrdinal,
|
||||
|
|
@ -877,6 +877,13 @@ type
|
|||
|
||||
TTypeSeq* = seq[PType]
|
||||
TLockLevel* = distinct int16
|
||||
|
||||
TTypeAttachedOp* = enum ## as usual, order is important here
|
||||
attachedDestructor,
|
||||
attachedAsgn,
|
||||
attachedSink,
|
||||
attachedDeepCopy
|
||||
|
||||
TType* {.acyclic.} = object of TIdObj # \
|
||||
# types are identical iff they have the
|
||||
# same id; there may be multiple copies of a type
|
||||
|
|
@ -897,12 +904,7 @@ type
|
|||
owner*: PSym # the 'owner' of the type
|
||||
sym*: PSym # types have the sym associated with them
|
||||
# it is used for converting types to strings
|
||||
destructor*: PSym # destructor. warning: nil here may not necessary
|
||||
# mean that there is no destructor.
|
||||
# see instantiateDestructor in semdestruct.nim
|
||||
deepCopy*: PSym # overriden 'deepCopy' operation
|
||||
assignment*: PSym # overriden '=' operation
|
||||
sink*: PSym # overriden '=sink' operation
|
||||
attachedOps*: array[TTypeAttachedOp, PSym] # destructors, etc.
|
||||
methods*: seq[(int,PSym)] # attached methods
|
||||
size*: BiggestInt # the size of the type in bytes
|
||||
# -1 means that the size is unkwown
|
||||
|
|
@ -1275,6 +1277,7 @@ const
|
|||
UnspecifiedLockLevel* = TLockLevel(-1'i16)
|
||||
MaxLockLevel* = 1000'i16
|
||||
UnknownLockLevel* = TLockLevel(1001'i16)
|
||||
AttachedOpToStr*: array[TTypeAttachedOp, string] = ["=destroy", "=", "=sink", "=deepcopy"]
|
||||
|
||||
proc `$`*(x: TLockLevel): string =
|
||||
if x.ord == UnspecifiedLockLevel.ord: result = "<unspecified>"
|
||||
|
|
@ -1341,10 +1344,7 @@ proc assignType*(dest, src: PType) =
|
|||
dest.n = src.n
|
||||
dest.size = src.size
|
||||
dest.align = src.align
|
||||
dest.destructor = src.destructor
|
||||
dest.deepCopy = src.deepCopy
|
||||
dest.sink = src.sink
|
||||
dest.assignment = src.assignment
|
||||
dest.attachedOps = src.attachedOps
|
||||
dest.lockLevel = src.lockLevel
|
||||
# this fixes 'type TLock = TSysLock':
|
||||
if src.sym != nil:
|
||||
|
|
@ -1831,3 +1831,7 @@ proc addParam*(procType: PType; param: PSym) =
|
|||
param.position = procType.len-1
|
||||
addSon(procType.n, newSymNode(param))
|
||||
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
|
||||
else:
|
||||
add(pl, ~")")
|
||||
if p.module.compileToCpp and lfSingleUse in d.flags:
|
||||
# do not generate spurious temporaries for C++! For C we're better off
|
||||
# with them to prevent undefined behaviour and because the codegen
|
||||
# is free to emit expressions multiple times!
|
||||
d.k = locCall
|
||||
d.r = pl
|
||||
excl d.flags, lfSingleUse
|
||||
if p.module.compileToCpp:
|
||||
if lfSingleUse in d.flags:
|
||||
# do not generate spurious temporaries for C++! For C we're better off
|
||||
# with them to prevent undefined behaviour and because the codegen
|
||||
# is free to emit expressions multiple times!
|
||||
d.k = locCall
|
||||
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:
|
||||
if d.k == locNone: getTemp(p, typ.sons[0], 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)
|
||||
|
||||
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 callPattern = if tfIterator in typ.flags: PatIter else: PatProc
|
||||
if typ.sons[0] != nil:
|
||||
if isInvalidReturnType(p.config, typ.sons[0]):
|
||||
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:
|
||||
var list: TLoc
|
||||
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
|
||||
else:
|
||||
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))
|
||||
|
||||
proc genRawSetData(cs: TBitSet, size: int): Rope =
|
||||
var frmt: FormatStr
|
||||
if size > 8:
|
||||
result = "{$n" % []
|
||||
for i in countup(0, size - 1):
|
||||
if i < size - 1:
|
||||
# not last iteration?
|
||||
if (i + 1) mod 8 == 0: frmt = "0x$1,$n"
|
||||
else: frmt = "0x$1, "
|
||||
if (i + 1) mod 8 == 0:
|
||||
addf(result, "0x$1,$n", [rope(toHex(ze64(cs[i]), 2))])
|
||||
else:
|
||||
addf(result, "0x$1, ", [rope(toHex(ze64(cs[i]), 2))])
|
||||
else:
|
||||
frmt = "0x$1}$n"
|
||||
addf(result, frmt, [rope(toHex(ze64(cs[i]), 2))])
|
||||
addf(result, "0x$1}$n", [rope(toHex(ze64(cs[i]), 2))])
|
||||
else:
|
||||
result = intLiteral(bitSetToWord(cs, size))
|
||||
# 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",
|
||||
"addInt64", "subInt64"
|
||||
]
|
||||
opr: array[mAddI..mPred, string] = [
|
||||
"($#)($# + $#)", "($#)($# - $#)", "($#)($# * $#)",
|
||||
"($#)($# / $#)", "($#)($# % $#)",
|
||||
"($#)($# + $#)", "($#)($# - $#)"]
|
||||
opr: array[mAddI..mPred, string] = ["+", "-", "*", "/", "%", "+", "-"]
|
||||
var a, b: TLoc
|
||||
assert(e.sons[1].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'
|
||||
let t = e.typ.skipTypes(abstractRange)
|
||||
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)
|
||||
else:
|
||||
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])
|
||||
|
||||
proc unaryArithOverflow(p: BProc, e: PNode, d: var TLoc, m: TMagic) =
|
||||
const
|
||||
opr: array[mUnaryMinusI..mAbsI, string] = [
|
||||
mUnaryMinusI: "((NI$2)-($1))",
|
||||
mUnaryMinusI64: "-($1)",
|
||||
mAbsI: "($1 > 0? ($1) : -($1))"]
|
||||
var
|
||||
a: TLoc
|
||||
t: PType
|
||||
|
|
@ -545,54 +537,17 @@ proc unaryArithOverflow(p: BProc, e: PNode, d: var TLoc, m: TMagic) =
|
|||
if optOverflowCheck in p.options:
|
||||
linefmt(p, cpsStmts, "if ($1 == $2) #raiseOverflow();$n",
|
||||
[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) =
|
||||
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
|
||||
a, b: TLoc
|
||||
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
|
||||
s = max(getSize(p.config, a.t), getSize(p.config, b.t)) * 8
|
||||
k = getSize(p.config, a.t) * 8
|
||||
putIntoDest(p, d, e,
|
||||
binArithTab[op] % [rdLoc(a), rdLoc(b), rope(s),
|
||||
getSimpleTypeDesc(p.module, e.typ), rope(k)])
|
||||
|
||||
template applyFormat(frmt: untyped) =
|
||||
putIntoDest(p, d, e, frmt % [
|
||||
rdLoc(a), rdLoc(b), rope(s),
|
||||
getSimpleTypeDesc(p.module, e.typ), rope(k)]
|
||||
)
|
||||
|
||||
case op
|
||||
of mAddF64: applyFormat("(($4)($1) + ($4)($2))")
|
||||
of mSubF64: applyFormat("(($4)($1) - ($4)($2))")
|
||||
of mMulF64: applyFormat("(($4)($1) * ($4)($2))")
|
||||
of mDivF64: applyFormat("(($4)($1) / ($4)($2))")
|
||||
of mShrI: applyFormat("($4)((NU$5)($1) >> (NU$3)($2))")
|
||||
of mShlI: applyFormat("($4)((NU$3)($1) << (NU$3)($2))")
|
||||
of mAshrI: applyFormat("($4)((NI$3)($1) >> (NU$3)($2))")
|
||||
of mBitandI: applyFormat("($4)($1 & $2)")
|
||||
of mBitorI: applyFormat("($4)($1 | $2)")
|
||||
of mBitxorI: applyFormat("($4)($1 ^ $2)")
|
||||
of mMinI: applyFormat("(($1 <= $2) ? $1 : $2)")
|
||||
of mMaxI: applyFormat("(($1 >= $2) ? $1 : $2)")
|
||||
of 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) =
|
||||
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) =
|
||||
const
|
||||
unArithTab: array[mNot..mToBiggestInt, string] = ["!($1)", # Not
|
||||
unArithTab: array[mNot..mToBiggestInt, string] = [
|
||||
"!($1)", # Not
|
||||
"$1", # UnaryPlusI
|
||||
"($3)((NU$2) ~($1))", # BitnotI
|
||||
"$1", # UnaryPlusF64
|
||||
|
|
@ -654,10 +660,58 @@ proc unaryArith(p: BProc, e: PNode, d: var TLoc, op: TMagic) =
|
|||
assert(e.sons[1].typ != nil)
|
||||
initLocExpr(p, e.sons[1], a)
|
||||
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)])
|
||||
|
||||
|
||||
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.} =
|
||||
result = p.module.compileToCpp 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)
|
||||
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) =
|
||||
# how to generate code?
|
||||
# 'expr1 and expr2' becomes:
|
||||
|
|
@ -963,24 +1034,41 @@ proc genAndOr(p: BProc, e: PNode, d: var TLoc, m: TMagic) =
|
|||
# tmp = a
|
||||
# end:
|
||||
# a = tmp
|
||||
var
|
||||
L: TLabel
|
||||
tmp: TLoc
|
||||
getTemp(p, e.typ, tmp) # force it into a temp!
|
||||
inc p.splitDecls
|
||||
expr(p, e.sons[1], tmp)
|
||||
L = getLabel(p)
|
||||
if m == mOr:
|
||||
lineF(p, cpsStmts, "if ($1) goto $2;$n", [rdLoc(tmp), L])
|
||||
when false:
|
||||
#if isSimpleExpr(e) and p.module.compileToCpp:
|
||||
var tmpA, tmpB: TLoc
|
||||
#getTemp(p, e.typ, tmpA)
|
||||
#getTemp(p, e.typ, tmpB)
|
||||
initLocExprSingleUse(p, e.sons[1], tmpA)
|
||||
initLocExprSingleUse(p, e.sons[2], tmpB)
|
||||
tmpB.k = locExpr
|
||||
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:
|
||||
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
|
||||
var
|
||||
L: TLabel
|
||||
tmp: TLoc
|
||||
getTemp(p, e.typ, tmp) # force it into a temp!
|
||||
inc p.splitDecls
|
||||
expr(p, e.sons[1], tmp)
|
||||
L = getLabel(p)
|
||||
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) =
|
||||
# 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:
|
||||
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)])
|
||||
|
||||
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)")
|
||||
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
|
||||
assert(d.k == locNone)
|
||||
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:
|
||||
case op
|
||||
of mIncl:
|
||||
var ts = "NU" & $(size * 8)
|
||||
binaryStmtInExcl(p, e, d,
|
||||
"$1 |= ((" & ts & ")1)<<(($2)%(sizeof(" & ts & ")*8));$n")
|
||||
case size
|
||||
of 1: binaryStmtInExcl(p, e, d, "$1 |= ((NU8)1)<<(($2) & 7);$n")
|
||||
of 2: binaryStmtInExcl(p, e, d, "$1 |= ((NU16)1)<<(($2) & 15);$n")
|
||||
of 4: binaryStmtInExcl(p, e, d, "$1 |= ((NU32)1)<<(($2) & 31);$n")
|
||||
of 8: binaryStmtInExcl(p, e, d, "$1 |= ((NU64)1)<<(($2) & 63);$n")
|
||||
else: assert(false, $size)
|
||||
of mExcl:
|
||||
var ts = "NU" & $(size * 8)
|
||||
binaryStmtInExcl(p, e, d, "$1 &= ~(((" & ts & ")1) << (($2) % (sizeof(" &
|
||||
ts & ")*8)));$n")
|
||||
case size
|
||||
of 1: binaryStmtInExcl(p, e, d, "$1 &= ~(((NU8)1) << (($2) & 7));$n")
|
||||
of 2: binaryStmtInExcl(p, e, d, "$1 &= ~(((NU16)1) << (($2) & 15));$n")
|
||||
of 4: binaryStmtInExcl(p, e, d, "$1 &= ~(((NU32)1) << (($2) & 31));$n")
|
||||
of 8: binaryStmtInExcl(p, e, d, "$1 &= ~(((NU64)1) << (($2) & 63));$n")
|
||||
else: assert(false, $size)
|
||||
of mCard:
|
||||
if size <= 4: unaryExprChar(p, e, d, "#countBits32($1)")
|
||||
else: unaryExprChar(p, e, d, "#countBits64($1)")
|
||||
|
|
@ -1824,14 +1918,14 @@ proc genSomeCast(p: BProc, e: PNode, d: var TLoc) =
|
|||
var a: TLoc
|
||||
initLocExpr(p, e.sons[1], a)
|
||||
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:
|
||||
putIntoDest(p, d, e, "(*($1*) ($2))" %
|
||||
[getTypeDesc(p.module, e.typ), addrLoc(p.config, a)], a.storage)
|
||||
elif etyp.kind == tyProc and etyp.callConv == ccClosure:
|
||||
elif etyp.kind == tyProc and etyp.callConv == ccClosure and srcTyp.callConv != ccClosure:
|
||||
putIntoDest(p, d, e, "(($1) ($2))" %
|
||||
[getClosureType(p.module, etyp, clHalfWithEnv), rdCharLoc(a)], a.storage)
|
||||
else:
|
||||
let srcTyp = skipTypes(e.sons[1].typ, abstractRange)
|
||||
# C++ does not like direct casts from pointer to shorter integral types
|
||||
if srcTyp.kind in {tyPtr, tyPointer} and etyp.kind in IntegralTypes:
|
||||
putIntoDest(p, d, e, "(($1) (ptrdiff_t) ($2))" %
|
||||
|
|
@ -2085,7 +2179,7 @@ proc genMagicExpr(p: BProc, e: PNode, d: var TLoc, op: TMagic) =
|
|||
of mCharToStr: genDollar(p, e, d, "#nimCharToStr($1)")
|
||||
of mFloatToStr: genDollar(p, e, d, "#nimFloatToStr($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:
|
||||
if optNimV2 in p.config.globalOptions:
|
||||
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[1], b)
|
||||
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),
|
||||
rdSetElemLoc(p.config, b, e.typ)])
|
||||
rdSetElemLoc(p.config, b, e.typ), rope(ts)])
|
||||
else:
|
||||
initLocExpr(p, it, a)
|
||||
lineF(p, cpsStmts,
|
||||
"$1 |=((" & ts & ")(1)<<(($2)%(sizeof(" & ts & ")*8)));$n",
|
||||
[rdLoc(d), rdSetElemLoc(p.config, a, e.typ)])
|
||||
"$1 |=(($3)(1)<<(($2)%(sizeof($3)*8)));$n",
|
||||
[rdLoc(d), rdSetElemLoc(p.config, a, e.typ), rope(ts)])
|
||||
|
||||
proc genTupleConstr(p: BProc, n: PNode, d: var TLoc) =
|
||||
var rec: TLoc
|
||||
|
|
@ -2609,7 +2703,10 @@ proc expr(p: BProc, n: PNode, d: var TLoc) =
|
|||
genProc(p.module, prc)
|
||||
of nkParForStmt: genParForStmt(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)
|
||||
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
|
||||
if not p.module.compileToCpp or optNoCppExceptions in p.config.globalOptions:
|
||||
# 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", [])
|
||||
|
||||
# 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:
|
||||
# Pop exceptions that was handled by the
|
||||
# except-blocks we are in
|
||||
for i in countdown(howManyExcepts-1, 0):
|
||||
linefmt(p, cpsStmts, "#popCurrentException();$n", [])
|
||||
if not p.noSafePoints:
|
||||
for i in countdown(howManyExcepts-1, 0):
|
||||
linefmt(p, cpsStmts, "#popCurrentException();$n", [])
|
||||
|
||||
proc genGotoState(p: BProc, n: PNode) =
|
||||
# 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,
|
||||
howManyTrys = p.nestedTryStmts.len,
|
||||
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
|
||||
# consume it before we return.
|
||||
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)
|
||||
if not quirkyExceptions:
|
||||
p.module.includeHeader("<setjmp.h>")
|
||||
else:
|
||||
p.noSafePoints = true
|
||||
genLineDir(p, t)
|
||||
discard cgsym(p.module, "Exception")
|
||||
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])
|
||||
startBlock(p, "if ($1.status == 0) {$n", [safePoint])
|
||||
var length = sonsLen(t)
|
||||
add(p.nestedTryStmts, (t, false))
|
||||
add(p.nestedTryStmts, (t, quirkyExceptions))
|
||||
expr(p, t.sons[0], d)
|
||||
if not quirkyExceptions:
|
||||
linefmt(p, cpsStmts, "#popSafePoint();$n", [])
|
||||
|
|
@ -1135,9 +1138,9 @@ proc genAsmStmt(p: BProc, t: PNode) =
|
|||
# work:
|
||||
if p.prc == nil:
|
||||
# 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:
|
||||
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 =
|
||||
result = cfsProcHeaders
|
||||
|
|
|
|||
|
|
@ -338,12 +338,17 @@ proc getTypePre(m: BModule, typ: PType; sig: SigHash): Rope =
|
|||
if result == nil: result = cacheGetType(m.typeCache, sig)
|
||||
|
||||
proc structOrUnion(t: PType): Rope =
|
||||
let cachedUnion {.global.} = rope("union")
|
||||
let cachedStruct {.global.} = rope("struct")
|
||||
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 =
|
||||
if m.compileToCpp: result = "$1 $2;$n"
|
||||
else: result = "typedef $1 $2 $2;$n"
|
||||
proc addForwardStructFormat(m: BModule, structOrUnion: Rope, typename: Rope) =
|
||||
if m.compileToCpp:
|
||||
m.s[cfsForwardTypes].addf "$1 $2;$n", [structOrUnion, typename]
|
||||
else:
|
||||
m.s[cfsForwardTypes].addf "typedef $1 $2 $2;$n", [structOrUnion, typename]
|
||||
|
||||
proc seqStar(m: BModule): string =
|
||||
if m.config.selectedGC == gcDestructors: result = ""
|
||||
|
|
@ -360,8 +365,7 @@ proc getTypeForward(m: BModule, typ: PType; sig: SigHash): Rope =
|
|||
result = getTypeName(m, typ, sig)
|
||||
m.forwTypeCache[sig] = result
|
||||
if not isImportedType(concrete):
|
||||
addf(m.s[cfsForwardTypes], getForwardStructFormat(m),
|
||||
[structOrUnion(typ), result])
|
||||
addForwardStructFormat(m, structOrUnion(typ), result)
|
||||
else:
|
||||
pushType(m, concrete)
|
||||
doAssert m.forwTypeCache[sig] == result
|
||||
|
|
@ -733,8 +737,7 @@ proc getTypeDescAux(m: BModule, origTyp: PType, check: var IntSet): Rope =
|
|||
if result == nil:
|
||||
result = getTypeName(m, origTyp, sig)
|
||||
if not isImportedType(t):
|
||||
addf(m.s[cfsForwardTypes], getForwardStructFormat(m),
|
||||
[structOrUnion(t), result])
|
||||
addForwardStructFormat(m, structOrUnion(t), result)
|
||||
m.forwTypeCache[sig] = result
|
||||
assert(cacheGetType(m.typeCache, sig) == nil)
|
||||
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)
|
||||
m.forwTypeCache[sig] = result
|
||||
if not isImportedType(t):
|
||||
addf(m.s[cfsForwardTypes], getForwardStructFormat(m),
|
||||
[structOrUnion(t), result])
|
||||
addForwardStructFormat(m, structOrUnion(t), result)
|
||||
assert m.forwTypeCache[sig] == result
|
||||
m.typeCache[sig] = result # always call for sideeffects:
|
||||
if not incompleteType(t):
|
||||
|
|
@ -905,7 +907,8 @@ proc finishTypeDescriptions(m: BModule) =
|
|||
discard getTypeDesc(m, m.typeStack[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 =
|
||||
return m.hcrOn and sfNonReloadable notin prc.flags
|
||||
|
|
@ -943,7 +946,7 @@ proc genProcHeader(m: BModule, prc: PSym, asPtr: bool = false): Rope =
|
|||
params])
|
||||
else:
|
||||
let asPtrStr = if asPtr: (rope("(*") & name & ")") else: name
|
||||
result = prc.cgDeclFrmt % [rettype, asPtrStr, params]
|
||||
result = runtimeFormat(prc.cgDeclFrmt, [rettype, asPtrStr, params])
|
||||
|
||||
# ------------------ type info generation -------------------------------------
|
||||
|
||||
|
|
@ -1346,10 +1349,10 @@ proc genTypeInfo(m: BModule, t: PType; info: TLineInfo): Rope =
|
|||
# results are not deterministic!
|
||||
genTupleInfo(m, t, origType, result, info)
|
||||
else: internalError(m.config, "genTypeInfo(" & $t.kind & ')')
|
||||
if t.deepCopy != nil:
|
||||
genDeepCopyProc(m, t.deepCopy, result)
|
||||
elif origType.deepCopy != nil:
|
||||
genDeepCopyProc(m, origType.deepCopy, result)
|
||||
if t.attachedOps[attachedDeepCopy] != nil:
|
||||
genDeepCopyProc(m, t.attachedOps[attachedDeepCopy], result)
|
||||
elif origType.attachedOps[attachedDeepCopy] != nil:
|
||||
genDeepCopyProc(m, origType.attachedOps[attachedDeepCopy], result)
|
||||
result = prefixTI.rope & result & ")".rope
|
||||
|
||||
proc genTypeSection(m: BModule, n: PNode) =
|
||||
|
|
|
|||
|
|
@ -206,15 +206,15 @@ proc indentLine(p: BProc, r: Rope): Rope =
|
|||
prepend(result, "\t".rope)
|
||||
|
||||
template appcg(m: BModule, c: var Rope, frmt: FormatStr,
|
||||
args: varargs[untyped]) =
|
||||
args: untyped) =
|
||||
add(c, ropecg(m, frmt, args))
|
||||
|
||||
template appcg(m: BModule, sec: TCFileSection, frmt: FormatStr,
|
||||
args: varargs[untyped]) =
|
||||
args: untyped) =
|
||||
add(m.s[sec], ropecg(m, frmt, args))
|
||||
|
||||
template appcg(p: BProc, sec: TCProcSection, frmt: FormatStr,
|
||||
args: varargs[untyped]) =
|
||||
args: untyped) =
|
||||
add(p.s(sec), ropecg(p.module, frmt, args))
|
||||
|
||||
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))
|
||||
|
||||
template lineF(p: BProc, sec: TCProcSection, frmt: FormatStr,
|
||||
args: openarray[Rope]) =
|
||||
args: untyped) =
|
||||
add(p.s(sec), indentLine(p, frmt % args))
|
||||
|
||||
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 = {}
|
||||
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) =
|
||||
inc(p.labels)
|
||||
result.r = "T" & rope(p.labels) & "_"
|
||||
|
|
@ -484,7 +493,7 @@ proc localVarDecl(p: BProc; n: PNode): Rope =
|
|||
add(result, " ")
|
||||
add(result, s.loc.r)
|
||||
else:
|
||||
result = s.cgDeclFrmt % [result, s.loc.r]
|
||||
result = runtimeFormat(s.cgDeclFrmt, [result, s.loc.r])
|
||||
|
||||
proc assignLocalVar(p: BProc, n: PNode) =
|
||||
#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")
|
||||
addf(decl, " $1;$n", [s.loc.r])
|
||||
else:
|
||||
decl = (s.cgDeclFrmt & ";$n") % [td, s.loc.r]
|
||||
decl = runtimeFormat(s.cgDeclFrmt & ";$n", [td, s.loc.r])
|
||||
add(p.module.s[cfsVars], decl)
|
||||
if p.withinLoop > 0:
|
||||
# fixes tests/run/tzeroarray:
|
||||
|
|
|
|||
|
|
@ -70,6 +70,7 @@ type
|
|||
threadVarAccessed*: bool # true if the proc already accessed some threadvar
|
||||
hasCurFramePointer*: bool # true if _nimCurFrame var needed to recover after
|
||||
# exception is generated
|
||||
noSafePoints*: bool # the proc doesn't use safe points in exception handling
|
||||
lastLineInfo*: TLineInfo # to avoid generating excessive 'nimln' statements
|
||||
currLineInfo*: TLineInfo # AST codegen will make this superfluous
|
||||
nestedTryStmts*: seq[tuple[n: PNode, inExcept: bool]]
|
||||
|
|
|
|||
|
|
@ -567,19 +567,36 @@ proc genReturn(c: var Con; n: PNode) =
|
|||
|
||||
const
|
||||
InterestingSyms = {skVar, skResult, skLet, skParam, skForVar, skTemp}
|
||||
PathKinds* = {nkDotExpr, nkCheckedFieldExpr,
|
||||
PathKinds0 = {nkDotExpr, nkCheckedFieldExpr,
|
||||
nkBracketExpr, nkDerefExpr, nkHiddenDeref,
|
||||
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) =
|
||||
var n = orig
|
||||
var iters = 0
|
||||
while n.kind in PathKinds:
|
||||
n = n[0]
|
||||
inc iters
|
||||
let n = dfa.getRoot(orig)
|
||||
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 =
|
||||
var n = field
|
||||
|
|
@ -590,7 +607,7 @@ proc aliases(obj, field: PNode): bool =
|
|||
if sameTrees(obj, n): return true
|
||||
case n.kind
|
||||
of nkDotExpr, nkCheckedFieldExpr, nkHiddenSubConv, nkHiddenStdConv,
|
||||
nkObjDownConv, nkObjUpConv, nkHiddenDeref:
|
||||
nkObjDownConv, nkObjUpConv, nkHiddenDeref, nkDerefExpr:
|
||||
n = n[0]
|
||||
of nkBracketExpr:
|
||||
let x = n[0]
|
||||
|
|
@ -616,13 +633,19 @@ proc instrTargets*(ins: Instr; loc: PNode): bool =
|
|||
# use x; question does it affect 'x.f'? Yes.
|
||||
result = aliases(ins.n, loc) or aliases(loc, ins.n)
|
||||
|
||||
proc isAnalysableFieldAccess*(n: PNode; owner: PSym): bool =
|
||||
var n = n
|
||||
proc isAnalysableFieldAccess*(orig: PNode; owner: PSym): bool =
|
||||
var n = orig
|
||||
while true:
|
||||
case n.kind
|
||||
of nkDotExpr, nkCheckedFieldExpr, nkHiddenSubConv, nkHiddenStdConv,
|
||||
nkObjDownConv, nkObjUpConv, nkHiddenDeref:
|
||||
nkObjDownConv, nkObjUpConv:
|
||||
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:
|
||||
let x = n[0]
|
||||
if x.typ != nil and x.typ.skipTypes(abstractInst).kind == tyTuple:
|
||||
|
|
@ -633,11 +656,25 @@ proc isAnalysableFieldAccess*(n: PNode; owner: PSym): bool =
|
|||
break
|
||||
# XXX Allow closure deref operations here if we know
|
||||
# the owner controlled the closure allocation?
|
||||
result = n.kind == nkSym and n.sym.owner == owner and 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) =
|
||||
if n.kind == nkSym and n.sym.kind in InterestingSyms:
|
||||
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 =
|
||||
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
|
||||
# it doesn't matter and 'x = 34' is NOT a usage of 'x'.
|
||||
genDef(c, n[0])
|
||||
of PathKinds:
|
||||
of PathKinds0 - {nkHiddenStdConv, nkHiddenSubConv, nkObjDownConv, nkObjUpConv}:
|
||||
genUse(c, n)
|
||||
of nkIfStmt, nkIfExpr: genIf(c, n)
|
||||
of nkWhenStmt:
|
||||
|
|
@ -732,8 +769,8 @@ proc gen(c: var Con; n: PNode) =
|
|||
nkBracket, nkCurly, nkPar, nkTupleConstr, nkClosure, nkObjConstr:
|
||||
for x in n: gen(c, x)
|
||||
of nkPragmaBlock: gen(c, n.lastSon)
|
||||
of nkDiscardStmt: gen(c, n.sons[0])
|
||||
of nkConv, nkExprColonExpr, nkExprEqExpr, nkCast:
|
||||
of nkDiscardStmt, nkObjDownConv, nkObjUpConv: gen(c, n.sons[0])
|
||||
of nkConv, nkExprColonExpr, nkExprEqExpr, nkCast, nkHiddenSubConv, nkHiddenStdConv:
|
||||
gen(c, n.sons[1])
|
||||
of nkStringToCString, nkCStringToString: gen(c, n.sons[0])
|
||||
of nkVarSection, nkLetSection: genVarSection(c, n)
|
||||
|
|
|
|||
|
|
@ -172,7 +172,7 @@ proc newDocumentor*(filename: AbsoluteFile; cache: IdentCache; conf: ConfigRef,
|
|||
outExt, RelativeDir"htmldocs", false)
|
||||
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)
|
||||
else: addf(dest, tex, args)
|
||||
|
||||
|
|
|
|||
|
|
@ -20,11 +20,12 @@ type
|
|||
TGen = object of PPassContext
|
||||
doc: PDoc
|
||||
module: PSym
|
||||
config: ConfigRef
|
||||
PGen = ref TGen
|
||||
|
||||
template shouldProcess(g): bool =
|
||||
(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.} =
|
||||
var g = PGen(p)
|
||||
|
|
@ -60,6 +61,7 @@ template myOpenImpl(ext: untyped) {.dirty.} =
|
|||
var g: PGen
|
||||
new(g)
|
||||
g.module = module
|
||||
g.config = graph.config
|
||||
var d = newDocumentor(AbsoluteFile toFullPath(graph.config, FileIndex module.position),
|
||||
graph.cache, graph.config, ext)
|
||||
d.hasToc = true
|
||||
|
|
|
|||
|
|
@ -376,7 +376,7 @@ proc nameToCC*(name: string): TSystemCC =
|
|||
result = ccNone
|
||||
|
||||
proc isVSCompatible*(conf: ConfigRef): bool =
|
||||
return conf.cCompiler == ccVcc or
|
||||
return conf.cCompiler == ccVcc or
|
||||
conf.cCompiler == ccClangCl or
|
||||
(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
|
||||
# is accomplished using the /PDB:<filename> flag (there also exists the
|
||||
# /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
|
||||
# delete all .pdb files with a pattern and swallow exceptions.
|
||||
#
|
||||
|
|
@ -910,7 +910,8 @@ proc callCCompiler*(conf: ConfigRef) =
|
|||
else: AbsoluteFile(conf.projectName)
|
||||
linkCmd = getLinkCmd(conf, mainOutput, objfiles)
|
||||
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 support a feature where the command line can be passed via ``@linkcmd``
|
||||
# to them.
|
||||
|
|
|
|||
|
|
@ -136,7 +136,7 @@ to do it.
|
|||
import
|
||||
intsets, ast, astalgo, msgs, renderer, magicsys, types, idents, trees,
|
||||
strutils, options, dfa, lowerings, tables, modulegraphs, msgs,
|
||||
lineinfos, parampatterns
|
||||
lineinfos, parampatterns, sighashes
|
||||
|
||||
const
|
||||
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':
|
||||
c.otherRead = nil
|
||||
var instr = -1
|
||||
let m = dfa.skipConvDfa(n)
|
||||
|
||||
for i in 0..<c.g.len:
|
||||
# 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:
|
||||
instr = i
|
||||
break
|
||||
|
||||
dbg:
|
||||
echo "starting point for ", n, " is ", instr
|
||||
echo "starting point for ", n, " is ", instr, " ", n.kind
|
||||
|
||||
if instr < 0: return false
|
||||
# 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)
|
||||
addSonSkipIntLit(result, baseType)
|
||||
|
||||
template genOp(opr, opname, ri) =
|
||||
let op = opr
|
||||
proc addDestroy(c: var Con; n: PNode) =
|
||||
# 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:
|
||||
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))
|
||||
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")
|
||||
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))
|
||||
addrExp.add(dest)
|
||||
result = newTree(nkCall, newSymNode(op), addrExp)
|
||||
|
||||
proc genSink(c: Con; t: PType; dest, ri: PNode): PNode =
|
||||
let t = t.skipTypes({tyGenericInst, tyAlias, tySink})
|
||||
let op = if t.sink != nil: t.sink else: t.assignment
|
||||
if op != nil:
|
||||
genOp(op, "=sink", ri)
|
||||
let k = if t.attachedOps[attachedSink] != nil: attachedSink
|
||||
else: attachedAsgn
|
||||
if t.attachedOps[k] != nil:
|
||||
result = genOp(c, t, k, dest, ri)
|
||||
else:
|
||||
# in rare cases only =destroy exists but no sink or assignment
|
||||
# (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:
|
||||
checkForErrorPragma(c, t, ri, "=")
|
||||
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 =
|
||||
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 =
|
||||
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) =
|
||||
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 &
|
||||
"` 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 =
|
||||
result = newNodeIT(nkStmtListExpr, n.info, n.typ)
|
||||
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
|
||||
result.add arg[0]
|
||||
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}:
|
||||
discard "object construction to sink parameter: nothing to do"
|
||||
result = arg
|
||||
|
|
@ -518,16 +544,22 @@ proc moveOrCopy(dest, ri: PNode; c: var Con): PNode =
|
|||
let L = if parameters != nil: parameters.len else: 0
|
||||
ri2.add ri[0]
|
||||
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)
|
||||
result.add ri2
|
||||
of nkBracketExpr:
|
||||
if ri[0].kind == nkSym and isUnpackedTuple(ri[0].sym):
|
||||
# unpacking of tuple: move out the elements
|
||||
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:
|
||||
result = genCopy(c, dest.typ, dest, ri)
|
||||
result.add p(ri, c)
|
||||
result.add p(ri, c)
|
||||
of nkStmtListExpr:
|
||||
result = newNodeI(nkStmtList, ri.info)
|
||||
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.add p(ri, c)
|
||||
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)
|
||||
var b = newNodeIT(ri.kind, ri.info, ri.typ)
|
||||
b.add ri[0] # add empty node
|
||||
|
|
@ -673,6 +707,15 @@ proc injectDefaultCalls(n: PNode, c: var Con) =
|
|||
proc isCursor(n: PNode): bool {.inline.} =
|
||||
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 =
|
||||
case n.kind
|
||||
of nkVarSection, nkLetSection:
|
||||
|
|
@ -694,24 +737,17 @@ proc p(n: PNode; c: var Con): PNode =
|
|||
c.addTopVar v
|
||||
# make sure it's destroyed at the end of the proc:
|
||||
if not isUnpackedTuple(it[0].sym):
|
||||
c.destroys.add genDestroy(c, v.typ, v)
|
||||
if ri.kind != nkEmpty:
|
||||
let r = moveOrCopy(v, ri, c)
|
||||
result.add r
|
||||
c.addDestroy genDestroy(c, v.typ, v)
|
||||
if ri.kind != nkEmpty:
|
||||
let r = moveOrCopy(v, ri, c)
|
||||
result.add r
|
||||
else:
|
||||
# keep it, but transform 'ri':
|
||||
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
|
||||
result.add keepVar(n, it, c)
|
||||
of nkCallKinds:
|
||||
let parameters = n[0].typ
|
||||
let L = if parameters != nil: parameters.len else: 0
|
||||
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):
|
||||
discard "produce temp creation"
|
||||
result = newNodeIT(nkStmtListExpr, n.info, n.typ)
|
||||
|
|
@ -720,11 +756,11 @@ proc p(n: PNode; c: var Con): PNode =
|
|||
sinkExpr.add n
|
||||
result.add sinkExpr
|
||||
result.add tmp
|
||||
c.destroys.add genDestroy(c, n.typ, tmp)
|
||||
c.addDestroy genDestroy(c, n.typ, tmp)
|
||||
else:
|
||||
result = n
|
||||
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)
|
||||
else:
|
||||
result = copyNode(n)
|
||||
|
|
@ -783,8 +819,8 @@ proc injectDestructorCalls*(g: ModuleGraph; owner: PSym; n: PNode): PNode =
|
|||
let params = owner.typ.n
|
||||
for i in 1 ..< params.len:
|
||||
let param = params[i].sym
|
||||
if isSinkParam(param) and hasDestructor(param.typ.skipTypes({tySink})):
|
||||
c.destroys.add genDestroy(c, param.typ.skipTypes({tyGenericInst, tyAlias, tySink}), params[i])
|
||||
if isSinkTypeForParam(param.typ) and hasDestructor(param.typ.skipTypes({tySink})):
|
||||
c.addDestroy genDestroy(c, param.typ.skipTypes({tyGenericInst, tyAlias, tySink}), params[i])
|
||||
|
||||
#if optNimV2 in c.graph.config.globalOptions:
|
||||
# injectDefaultCalls(n, c)
|
||||
|
|
|
|||
|
|
@ -32,9 +32,9 @@ import
|
|||
ast, astalgo, strutils, hashes, trees, platform, magicsys, extccomp, options,
|
||||
nversion, nimsets, msgs, std / sha1, bitsets, idents, types, os, tables,
|
||||
times, ropes, math, passes, ccgutils, wordrecg, renderer,
|
||||
intsets, cgmeth, lowerings, sighashes, modulegraphs, lineinfos, rodutils,
|
||||
intsets, cgmeth, lowerings, sighashes, modulegraphs, lineinfos, rodutils,
|
||||
pathutils, transf
|
||||
|
||||
|
||||
|
||||
from modulegraphs import ModuleGraph, PPassContext
|
||||
|
||||
|
|
@ -365,92 +365,92 @@ proc genOr(p: PProc, a, b: PNode, r: var TCompRes) =
|
|||
line(p, "}")
|
||||
|
||||
type
|
||||
TMagicFrmt = array[0..3, string]
|
||||
TMagicFrmt = array[0..1, string]
|
||||
TMagicOps = array[mAddI..mStrToStr, TMagicFrmt]
|
||||
|
||||
const # magic checked op; magic unchecked op; checked op; unchecked op
|
||||
jsOps: TMagicOps = [
|
||||
["addInt", "", "addInt($1, $2)", "($1 + $2)"], # AddI
|
||||
["subInt", "", "subInt($1, $2)", "($1 - $2)"], # SubI
|
||||
["mulInt", "", "mulInt($1, $2)", "($1 * $2)"], # MulI
|
||||
["divInt", "", "divInt($1, $2)", "Math.trunc($1 / $2)"], # DivI
|
||||
["modInt", "", "modInt($1, $2)", "Math.trunc($1 % $2)"], # ModI
|
||||
["addInt", "", "addInt($1, $2)", "($1 + $2)"], # Succ
|
||||
["subInt", "", "subInt($1, $2)", "($1 - $2)"], # Pred
|
||||
["", "", "($1 + $2)", "($1 + $2)"], # AddF64
|
||||
["", "", "($1 - $2)", "($1 - $2)"], # SubF64
|
||||
["", "", "($1 * $2)", "($1 * $2)"], # MulF64
|
||||
["", "", "($1 / $2)", "($1 / $2)"], # DivF64
|
||||
["", "", "", ""], # ShrI
|
||||
["", "", "($1 << $2)", "($1 << $2)"], # ShlI
|
||||
["", "", "($1 >> $2)", "($1 >> $2)"], # AshrI
|
||||
["", "", "($1 & $2)", "($1 & $2)"], # BitandI
|
||||
["", "", "($1 | $2)", "($1 | $2)"], # BitorI
|
||||
["", "", "($1 ^ $2)", "($1 ^ $2)"], # BitxorI
|
||||
["nimMin", "nimMin", "nimMin($1, $2)", "nimMin($1, $2)"], # MinI
|
||||
["nimMax", "nimMax", "nimMax($1, $2)", "nimMax($1, $2)"], # MaxI
|
||||
["nimMin", "nimMin", "nimMin($1, $2)", "nimMin($1, $2)"], # MinF64
|
||||
["nimMax", "nimMax", "nimMax($1, $2)", "nimMax($1, $2)"], # MaxF64
|
||||
["", "", "", ""], # addU
|
||||
["", "", "", ""], # subU
|
||||
["", "", "", ""], # mulU
|
||||
["", "", "", ""], # divU
|
||||
["", "", "($1 % $2)", "($1 % $2)"], # modU
|
||||
["", "", "($1 == $2)", "($1 == $2)"], # EqI
|
||||
["", "", "($1 <= $2)", "($1 <= $2)"], # LeI
|
||||
["", "", "($1 < $2)", "($1 < $2)"], # LtI
|
||||
["", "", "($1 == $2)", "($1 == $2)"], # EqF64
|
||||
["", "", "($1 <= $2)", "($1 <= $2)"], # LeF64
|
||||
["", "", "($1 < $2)", "($1 < $2)"], # LtF64
|
||||
["", "", "($1 <= $2)", "($1 <= $2)"], # leU
|
||||
["", "", "($1 < $2)", "($1 < $2)"], # ltU
|
||||
["", "", "($1 <= $2)", "($1 <= $2)"], # leU64
|
||||
["", "", "($1 < $2)", "($1 < $2)"], # ltU64
|
||||
["", "", "($1 == $2)", "($1 == $2)"], # EqEnum
|
||||
["", "", "($1 <= $2)", "($1 <= $2)"], # LeEnum
|
||||
["", "", "($1 < $2)", "($1 < $2)"], # LtEnum
|
||||
["", "", "($1 == $2)", "($1 == $2)"], # EqCh
|
||||
["", "", "($1 <= $2)", "($1 <= $2)"], # LeCh
|
||||
["", "", "($1 < $2)", "($1 < $2)"], # LtCh
|
||||
["", "", "($1 == $2)", "($1 == $2)"], # EqB
|
||||
["", "", "($1 <= $2)", "($1 <= $2)"], # LeB
|
||||
["", "", "($1 < $2)", "($1 < $2)"], # LtB
|
||||
["", "", "($1 == $2)", "($1 == $2)"], # EqRef
|
||||
["", "", "($1 == $2)", "($1 == $2)"], # EqUntracedRef
|
||||
["", "", "($1 <= $2)", "($1 <= $2)"], # LePtr
|
||||
["", "", "($1 < $2)", "($1 < $2)"], # LtPtr
|
||||
["", "", "($1 != $2)", "($1 != $2)"], # Xor
|
||||
["", "", "($1 == $2)", "($1 == $2)"], # EqCString
|
||||
["", "", "($1 == $2)", "($1 == $2)"], # EqProc
|
||||
["negInt", "", "negInt($1)", "-($1)"], # UnaryMinusI
|
||||
["negInt64", "", "negInt64($1)", "-($1)"], # UnaryMinusI64
|
||||
["absInt", "", "absInt($1)", "Math.abs($1)"], # AbsI
|
||||
["", "", "!($1)", "!($1)"], # Not
|
||||
["", "", "+($1)", "+($1)"], # UnaryPlusI
|
||||
["", "", "~($1)", "~($1)"], # BitnotI
|
||||
["", "", "+($1)", "+($1)"], # UnaryPlusF64
|
||||
["", "", "-($1)", "-($1)"], # UnaryMinusF64
|
||||
["", "", "Math.abs($1)", "Math.abs($1)"], # AbsF64
|
||||
["Ze8ToI", "Ze8ToI", "Ze8ToI($1)", "Ze8ToI($1)"], # mZe8ToI
|
||||
["Ze8ToI64", "Ze8ToI64", "Ze8ToI64($1)", "Ze8ToI64($1)"], # mZe8ToI64
|
||||
["Ze16ToI", "Ze16ToI", "Ze16ToI($1)", "Ze16ToI($1)"], # mZe16ToI
|
||||
["Ze16ToI64", "Ze16ToI64", "Ze16ToI64($1)", "Ze16ToI64($1)"], # mZe16ToI64
|
||||
["Ze32ToI64", "Ze32ToI64", "Ze32ToI64($1)", "Ze32ToI64($1)"], # mZe32ToI64
|
||||
["ZeIToI64", "ZeIToI64", "ZeIToI64($1)", "ZeIToI64($1)"], # mZeIToI64
|
||||
["toU8", "toU8", "toU8($1)", "toU8($1)"], # toU8
|
||||
["toU16", "toU16", "toU16($1)", "toU16($1)"], # toU16
|
||||
["toU32", "toU32", "toU32($1)", "toU32($1)"], # toU32
|
||||
["", "", "$1", "$1"], # ToFloat
|
||||
["", "", "$1", "$1"], # ToBiggestFloat
|
||||
["", "", "Math.trunc($1)", "Math.trunc($1)"], # ToInt
|
||||
["", "", "Math.trunc($1)", "Math.trunc($1)"], # ToBiggestInt
|
||||
["nimCharToStr", "nimCharToStr", "nimCharToStr($1)", "nimCharToStr($1)"],
|
||||
["nimBoolToStr", "nimBoolToStr", "nimBoolToStr($1)", "nimBoolToStr($1)"],
|
||||
["cstrToNimstr", "cstrToNimstr", "cstrToNimstr(($1)+\"\")", "cstrToNimstr(($1)+\"\")"],
|
||||
["cstrToNimstr", "cstrToNimstr", "cstrToNimstr(($1)+\"\")", "cstrToNimstr(($1)+\"\")"],
|
||||
["cstrToNimstr", "cstrToNimstr", "cstrToNimstr(($1)+\"\")", "cstrToNimstr(($1)+\"\")"],
|
||||
["cstrToNimstr", "cstrToNimstr", "cstrToNimstr($1)", "cstrToNimstr($1)"],
|
||||
["", "", "$1", "$1"]]
|
||||
const # magic checked op; magic unchecked op;
|
||||
jsMagics: TMagicOps = [
|
||||
["addInt", ""], # AddI
|
||||
["subInt", ""], # SubI
|
||||
["mulInt", ""], # MulI
|
||||
["divInt", ""], # DivI
|
||||
["modInt", ""], # ModI
|
||||
["addInt", ""], # Succ
|
||||
["subInt", ""], # Pred
|
||||
["", ""], # AddF64
|
||||
["", ""], # SubF64
|
||||
["", ""], # MulF64
|
||||
["", ""], # DivF64
|
||||
["", ""], # ShrI
|
||||
["", ""], # ShlI
|
||||
["", ""], # AshrI
|
||||
["", ""], # BitandI
|
||||
["", ""], # BitorI
|
||||
["", ""], # BitxorI
|
||||
["nimMin", "nimMin"], # MinI
|
||||
["nimMax", "nimMax"], # MaxI
|
||||
["nimMin", "nimMin"], # MinF64
|
||||
["nimMax", "nimMax"], # MaxF64
|
||||
["", ""], # addU
|
||||
["", ""], # subU
|
||||
["", ""], # mulU
|
||||
["", ""], # divU
|
||||
["", ""], # modU
|
||||
["", ""], # EqI
|
||||
["", ""], # LeI
|
||||
["", ""], # LtI
|
||||
["", ""], # EqF64
|
||||
["", ""], # LeF64
|
||||
["", ""], # LtF64
|
||||
["", ""], # leU
|
||||
["", ""], # ltU
|
||||
["", ""], # leU64
|
||||
["", ""], # ltU64
|
||||
["", ""], # EqEnum
|
||||
["", ""], # LeEnum
|
||||
["", ""], # LtEnum
|
||||
["", ""], # EqCh
|
||||
["", ""], # LeCh
|
||||
["", ""], # LtCh
|
||||
["", ""], # EqB
|
||||
["", ""], # LeB
|
||||
["", ""], # LtB
|
||||
["", ""], # EqRef
|
||||
["", ""], # EqUntracedRef
|
||||
["", ""], # LePtr
|
||||
["", ""], # LtPtr
|
||||
["", ""], # Xor
|
||||
["", ""], # EqCString
|
||||
["", ""], # EqProc
|
||||
["negInt", ""], # UnaryMinusI
|
||||
["negInt64", ""], # UnaryMinusI64
|
||||
["absInt", ""], # AbsI
|
||||
["", ""], # Not
|
||||
["", ""], # UnaryPlusI
|
||||
["", ""], # BitnotI
|
||||
["", ""], # UnaryPlusF64
|
||||
["", ""], # UnaryMinusF64
|
||||
["", ""], # AbsF64
|
||||
["Ze8ToI", "Ze8ToI"], # mZe8ToI
|
||||
["Ze8ToI64", "Ze8ToI64"], # mZe8ToI64
|
||||
["Ze16ToI", "Ze16ToI"], # mZe16ToI
|
||||
["Ze16ToI64", "Ze16ToI64"], # mZe16ToI64
|
||||
["Ze32ToI64", "Ze32ToI64"], # mZe32ToI64
|
||||
["ZeIToI64", "ZeIToI64"], # mZeIToI64
|
||||
["toU8", "toU8"], # toU8
|
||||
["toU16", "toU16"], # toU16
|
||||
["toU32", "toU32"], # toU32
|
||||
["", ""], # ToFloat
|
||||
["", ""], # ToBiggestFloat
|
||||
["", ""], # ToInt
|
||||
["", ""], # ToBiggestInt
|
||||
["nimCharToStr", "nimCharToStr"],
|
||||
["nimBoolToStr", "nimBoolToStr"],
|
||||
["cstrToNimstr", "cstrToNimstr"],
|
||||
["cstrToNimstr", "cstrToNimstr"],
|
||||
["cstrToNimstr", "cstrToNimstr"],
|
||||
["cstrToNimstr", "cstrToNimstr"],
|
||||
["", ""]]
|
||||
|
||||
proc needsTemp(p: PProc; n: PNode): bool =
|
||||
# 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:
|
||||
(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,
|
||||
# if $3 or $4 are present they will be substituted with temps for
|
||||
# lhs and rhs respectively
|
||||
|
|
@ -490,8 +490,8 @@ proc binaryExpr(p: PProc, n: PNode, r: var TCompRes, magic, frmt: string) =
|
|||
var
|
||||
a, tmp = x.rdLoc
|
||||
b, tmp2 = y.rdLoc
|
||||
if "$3" in frmt: (a, tmp) = maybeMakeTemp(p, n[1], x)
|
||||
if "$4" in frmt: (a, tmp) = maybeMakeTemp(p, n[1], x)
|
||||
when "$3" 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.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.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
|
||||
useMagic(p, magic)
|
||||
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.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
|
||||
useMagic(p, magic)
|
||||
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) =
|
||||
var
|
||||
x, y: TCompRes
|
||||
xLoc,yLoc: Rope
|
||||
let i = ord(optOverflowCheck notin p.options)
|
||||
useMagic(p, jsOps[op][i])
|
||||
useMagic(p, jsMagics[op][i])
|
||||
if sonsLen(n) > 2:
|
||||
gen(p, n.sons[1], x)
|
||||
gen(p, n.sons[2], y)
|
||||
r.res = jsOps[op][i + 2] % [x.rdLoc, y.rdLoc]
|
||||
xLoc = x.rdLoc
|
||||
yLoc = y.rdLoc
|
||||
else:
|
||||
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) =
|
||||
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)
|
||||
r.res = s.loc.r
|
||||
of skProc, skFunc, skConverter, skMethod:
|
||||
if sfCompileTime in s.flags:
|
||||
localError(p.config, n.info, "request to generate code for .compileTime proc: " &
|
||||
s.name.s)
|
||||
discard mangleName(p.module, s)
|
||||
r.res = s.loc.r
|
||||
if lfNoDecl in s.loc.flags or s.magic != mNone or
|
||||
|
|
@ -1603,6 +1699,9 @@ proc genVarInit(p: PProc, v: PSym, n: PNode) =
|
|||
else:
|
||||
varCode = "var $2"
|
||||
else:
|
||||
# Is this really a thought through feature? this basically unused
|
||||
# feature makes it impossible for almost all format strings in
|
||||
# this function to be checked at compile time.
|
||||
varCode = v.constraint.strVal
|
||||
|
||||
if n.kind == nkEmpty:
|
||||
|
|
@ -1611,8 +1710,7 @@ proc genVarInit(p: PProc, v: PSym, n: PNode) =
|
|||
lineF(p, "var $1 = null;$n", [varName])
|
||||
lineF(p, "var $1_Idx = 0;$n", [varName])
|
||||
else:
|
||||
lineF(p, varCode & " = $3;$n",
|
||||
[returnType, varName, createVar(p, v.typ, isIndirect(v))])
|
||||
line(p, runtimeFormat(varCode & " = $3;$n", [returnType, varName, createVar(p, v.typ, isIndirect(v))]))
|
||||
else:
|
||||
gen(p, n, a)
|
||||
case mapType(p, v.typ)
|
||||
|
|
@ -1626,29 +1724,29 @@ proc genVarInit(p: PProc, v: PSym, n: PNode) =
|
|||
let targetBaseIndex = {sfAddrTaken, sfGlobal} * v.flags == {}
|
||||
if a.typ == etyBaseIndex:
|
||||
if targetBaseIndex:
|
||||
lineF(p, varCode & " = $3, $2_Idx = $4;$n",
|
||||
[returnType, v.loc.r, a.address, a.res])
|
||||
line(p, runtimeFormat(varCode & " = $3, $2_Idx = $4;$n",
|
||||
[returnType, v.loc.r, a.address, a.res]))
|
||||
else:
|
||||
if isIndirect(v):
|
||||
lineF(p, varCode & " = [[$3, $4]];$n",
|
||||
[returnType, v.loc.r, a.address, a.res])
|
||||
line(p, runtimeFormat(varCode & " = [[$3, $4]];$n",
|
||||
[returnType, v.loc.r, a.address, a.res]))
|
||||
else:
|
||||
lineF(p, varCode & " = [$3, $4];$n",
|
||||
[returnType, v.loc.r, a.address, a.res])
|
||||
line(p, runtimeFormat(varCode & " = [$3, $4];$n",
|
||||
[returnType, v.loc.r, a.address, a.res]))
|
||||
else:
|
||||
if targetBaseIndex:
|
||||
let tmp = p.getTemp
|
||||
lineF(p, "var $1 = $2, $3 = $1[0], $3_Idx = $1[1];$n",
|
||||
[tmp, a.res, v.loc.r])
|
||||
else:
|
||||
lineF(p, varCode & " = $3;$n", [returnType, v.loc.r, a.res])
|
||||
line(p, runtimeFormat(varCode & " = $3;$n", [returnType, v.loc.r, a.res]))
|
||||
return
|
||||
else:
|
||||
s = a.res
|
||||
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:
|
||||
lineF(p, varCode & " = $3;$n", [returnType, v.loc.r, s])
|
||||
line(p, runtimeFormat(varCode & " = $3;$n", [returnType, v.loc.r, s]))
|
||||
|
||||
if useReloadingGuard:
|
||||
dec p.extraIndent
|
||||
|
|
@ -2117,7 +2215,7 @@ proc genReturnStmt(p: PProc, n: PNode) =
|
|||
lineF(p, "break BeforeRet;$n", [])
|
||||
|
||||
proc frameCreate(p: PProc; procname, filename: Rope): Rope =
|
||||
let frameFmt =
|
||||
const frameFmt =
|
||||
"var F={procname:$1,prev:framePtr,filename:$2,line:0};$n"
|
||||
|
||||
result = p.indentLine(frameFmt % [procname, filename])
|
||||
|
|
@ -2185,7 +2283,7 @@ proc genProc(oldProc: PProc, prc: PSym): Rope =
|
|||
|
||||
var def: Rope
|
||||
if not prc.constraint.isNil:
|
||||
def = (prc.constraint.strVal & " {$n$#$#$#$#$#") %
|
||||
def = runtimeFormat(prc.constraint.strVal & " {$n$#$#$#$#$#",
|
||||
[ returnType,
|
||||
name,
|
||||
header,
|
||||
|
|
@ -2193,7 +2291,7 @@ proc genProc(oldProc: PProc, prc: PSym): Rope =
|
|||
optionalLine(p.locals),
|
||||
optionalLine(resultAsgn),
|
||||
optionalLine(genProcBody(p, prc)),
|
||||
optionalLine(p.indentLine(returnStmt))]
|
||||
optionalLine(p.indentLine(returnStmt))])
|
||||
else:
|
||||
result = ~"\L"
|
||||
|
||||
|
|
|
|||
|
|
@ -261,7 +261,7 @@ proc liftIterSym*(g: ModuleGraph; n: PNode; owner: PSym): PNode =
|
|||
|
||||
proc freshVarForClosureIter*(g: ModuleGraph; s, owner: PSym): PNode =
|
||||
let envParam = getHiddenParam(g, owner)
|
||||
let obj = envParam.typ.lastSon
|
||||
let obj = envParam.typ.skipTypes({tyOwned, tyRef})
|
||||
addField(obj, s, g.cache)
|
||||
|
||||
var access = newSymNode(envParam)
|
||||
|
|
@ -320,15 +320,23 @@ proc getEnvTypeForOwner(c: var DetectionPass; owner: PSym;
|
|||
rawAddSon(result, obj)
|
||||
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;
|
||||
info: TLineInfo): PType =
|
||||
var r = c.getEnvTypeForOwner(owner, info)
|
||||
result = newType(tyPtr, owner)
|
||||
rawAddSon(result, r.base)
|
||||
rawAddSon(result, r.skipTypes({tyOwned, tyRef}))
|
||||
|
||||
proc createUpField(c: var DetectionPass; dest, dep: PSym; info: TLineInfo) =
|
||||
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
|
||||
# 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
|
||||
|
|
@ -343,7 +351,7 @@ proc createUpField(c: var DetectionPass; dest, dep: PSym; info: TLineInfo) =
|
|||
let upIdent = getIdent(c.graph.cache, upName)
|
||||
let upField = lookupInRecord(obj.n, upIdent)
|
||||
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")
|
||||
else:
|
||||
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)
|
||||
if interestingIterVar(s):
|
||||
if not c.capturedVars.containsOrIncl(s.id):
|
||||
let obj = getHiddenParam(c.graph, owner).typ.lastSon
|
||||
#let obj = c.getEnvTypeForOwner(s.owner).lastSon
|
||||
let obj = getHiddenParam(c.graph, owner).typ.skipTypes({tyOwned, tyRef})
|
||||
#let obj = c.getEnvTypeForOwner(s.owner).skipTypes({tyOwned, tyRef})
|
||||
|
||||
if s.name.id == getIdent(c.graph.cache, ":state").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
|
||||
# variable 's' is actually captured:
|
||||
if interestingVar(s) and not c.capturedVars.containsOrIncl(s.id):
|
||||
let obj = c.getEnvTypeForOwner(ow, n.info).lastSon
|
||||
#getHiddenParam(owner).typ.lastSon
|
||||
let obj = c.getEnvTypeForOwner(ow, n.info).skipTypes({tyOwned, tyRef})
|
||||
#getHiddenParam(owner).typ.skipTypes({tyOwned, tyRef})
|
||||
addField(obj, s, c.graph.cache)
|
||||
# create required upFields:
|
||||
var w = owner.skipGenericOwner
|
||||
|
|
@ -530,14 +538,14 @@ proc setupEnvVar(owner: PSym; d: DetectionPass;
|
|||
let envVarType = d.ownerToType.getOrDefault(owner.id)
|
||||
if envVarType.isNil:
|
||||
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
|
||||
|
||||
proc getUpViaParam(g: ModuleGraph; owner: PSym): PNode =
|
||||
let p = getHiddenParam(g, owner)
|
||||
result = p.newSymNode
|
||||
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:
|
||||
localError(g.config, owner.info, "could not find up reference for closure iter")
|
||||
else:
|
||||
|
|
@ -566,10 +574,10 @@ proc rawClosureCreation(owner: PSym;
|
|||
# add ``env.param = param``
|
||||
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:
|
||||
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),
|
||||
up, env.info))
|
||||
#elif oldenv != nil and oldenv.typ == upField.typ:
|
||||
|
|
@ -584,11 +592,11 @@ proc closureCreationForIter(iter: PNode;
|
|||
let owner = iter.sym.skipGenericOwner
|
||||
var v = newSym(skVar, getIdent(d.graph.cache, envName), owner, iter.info)
|
||||
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
|
||||
if owner.isIterator:
|
||||
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)
|
||||
else:
|
||||
vnode = v.newSymNode
|
||||
|
|
@ -597,10 +605,10 @@ proc closureCreationForIter(iter: PNode;
|
|||
result.add(vs)
|
||||
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:
|
||||
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),
|
||||
u, iter.info))
|
||||
else:
|
||||
|
|
@ -610,7 +618,7 @@ proc closureCreationForIter(iter: PNode;
|
|||
proc accessViaEnvVar(n: PNode; owner: PSym; d: DetectionPass;
|
||||
c: var LiftingPass): PNode =
|
||||
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)
|
||||
if field != nil:
|
||||
result = rawIndirectAccess(access, field, n.info)
|
||||
|
|
@ -619,7 +627,7 @@ proc accessViaEnvVar(n: PNode; owner: PSym; d: DetectionPass;
|
|||
result = n
|
||||
|
||||
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;
|
||||
c: var LiftingPass): PNode
|
||||
|
|
@ -644,7 +652,7 @@ proc symToClosure(n: PNode; owner: PSym; d: DetectionPass;
|
|||
while true:
|
||||
if access.typ == wanted:
|
||||
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))
|
||||
if upField == nil:
|
||||
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.} =
|
||||
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;
|
||||
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)
|
||||
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
|
||||
if op == nil and useNoGc(c, t):
|
||||
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)
|
||||
onUse(c.info, op)
|
||||
body.add destructorCall(c.graph, op, x)
|
||||
result = true
|
||||
elif useNoGc(c, t):
|
||||
internalError(c.graph.config, c.info,
|
||||
"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
|
||||
# patch generic destructor:
|
||||
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)
|
||||
onUse(c.info, op)
|
||||
|
|
@ -207,9 +206,9 @@ proc considerUserDefinedOp(c: var TLiftCtx; t: PType; body, x, y: PNode): bool =
|
|||
of attachedAsgn:
|
||||
result = considerAsgnOrSink(c, t, body, x, y, t.assignment)
|
||||
of attachedSink:
|
||||
result = considerAsgnOrSink(c, t, body, x, y, t.sink)
|
||||
result = considerAsgnOrSink(c, t, body, x, y, t.asink)
|
||||
of attachedDeepCopy:
|
||||
let op = t.deepCopy
|
||||
let op = t.attachedOps[attachedDeepCopy]
|
||||
if op != nil:
|
||||
markUsed(c.graph.config, c.info, op, c.graph.usageSym)
|
||||
onUse(c.info, op)
|
||||
|
|
@ -323,8 +322,8 @@ proc useSeqOrStrOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
|||
body.add moveCall
|
||||
# alternatively we could do this:
|
||||
when false:
|
||||
doAssert t.sink != nil
|
||||
body.add newAsgnCall(c.graph, t.sink, x, y)
|
||||
doAssert t.asink != nil
|
||||
body.add newAsgnCall(c.graph, t.asink, x, y)
|
||||
of attachedDestructor:
|
||||
doAssert t.destructor != nil
|
||||
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)
|
||||
|
||||
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)
|
||||
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)
|
||||
|
||||
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 =
|
||||
assert typ.kind == tyDistinct
|
||||
let baseType = typ[0]
|
||||
case kind
|
||||
of attachedAsgn:
|
||||
if baseType.assignment == nil:
|
||||
discard produceSym(c, baseType, kind, info)
|
||||
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
|
||||
if baseType.attachedOps[kind] == nil:
|
||||
discard produceSym(c, baseType, kind, info)
|
||||
typ.attachedOps[kind] = baseType.attachedOps[kind]
|
||||
result = typ.attachedOps[kind]
|
||||
|
||||
proc produceSym(c: PContext; typ: PType; kind: TTypeAttachedOp;
|
||||
info: TLineInfo): PSym =
|
||||
|
|
@ -536,11 +518,7 @@ proc produceSym(c: PContext; typ: PType; kind: TTypeAttachedOp;
|
|||
a.c = c
|
||||
let g = c.graph
|
||||
let body = newNodeI(nkStmtList, info)
|
||||
let procname = case kind
|
||||
of attachedAsgn: getIdent(g.cache, "=")
|
||||
of attachedSink: getIdent(g.cache, "=sink")
|
||||
of attachedDeepCopy: getIdent(g.cache, "=deepcopy")
|
||||
of attachedDestructor: getIdent(g.cache, "=destroy")
|
||||
let procname = getIdent(g.cache, AttachedOpToStr[kind])
|
||||
|
||||
result = newSym(skProc, procname, typ.owner, info)
|
||||
a.fn = result
|
||||
|
|
@ -557,11 +535,7 @@ proc produceSym(c: PContext; typ: PType; kind: TTypeAttachedOp;
|
|||
result.typ.addParam src
|
||||
|
||||
# register this operation already:
|
||||
case kind
|
||||
of attachedAsgn: typ.assignment = result
|
||||
of attachedSink: typ.sink = result
|
||||
of attachedDeepCopy: typ.deepCopy = result
|
||||
of attachedDestructor: typ.destructor = result
|
||||
typ.attachedOps[kind] = result
|
||||
|
||||
var tk: TTypeKind
|
||||
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.
|
||||
let typ = canon.skipTypes({tyGenericInst, tyAlias})
|
||||
# we generate the destructor first so that other operators can depend on it:
|
||||
if typ.destructor == nil:
|
||||
discard produceSym(c, typ, attachedDestructor, info)
|
||||
else:
|
||||
inst(typ.destructor, typ)
|
||||
if typ.assignment == nil:
|
||||
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)
|
||||
for k in attachedDestructor..attachedSink:
|
||||
if typ.attachedOps[k] == nil:
|
||||
discard produceSym(c, typ, k, info)
|
||||
else:
|
||||
inst(typ.attachedOps[k], typ)
|
||||
|
||||
if overwrite:
|
||||
orig.destructor = typ.destructor
|
||||
orig.assignment = typ.assignment
|
||||
orig.sink = typ.sink
|
||||
for k in attachedDestructor..attachedSink:
|
||||
orig.attachedOps[k] = typ.attachedOps[k]
|
||||
|
||||
if not isTrival(orig.destructor):
|
||||
#or not isTrival(orig.assignment) or
|
||||
|
|
|
|||
|
|
@ -17,9 +17,9 @@ import
|
|||
proc resetSystemArtifacts*(g: ModuleGraph) =
|
||||
magicsys.resetSysTypes(g)
|
||||
|
||||
proc partialInitModule(result: PSym; graph: ModuleGraph; fileIdx: FileIndex; filename: string) =
|
||||
proc partialInitModule(result: PSym; graph: ModuleGraph; fileIdx: FileIndex; filename: AbsoluteFile) =
|
||||
let
|
||||
pck = getPackageName(graph.config, filename)
|
||||
pck = getPackageName(graph.config, filename.string)
|
||||
pck2 = if pck.len > 0: pck else: "unknown"
|
||||
pack = getIdent(graph.cache, pck2)
|
||||
var packSym = graph.packageSyms.strTableGet(pack)
|
||||
|
|
@ -27,6 +27,22 @@ proc partialInitModule(result: PSym; graph: ModuleGraph; fileIdx: FileIndex; fil
|
|||
packSym = newSym(skPackage, getIdent(graph.cache, pck2), nil, result.info)
|
||||
initStrTable(packSym.tab)
|
||||
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.position = int fileIdx
|
||||
|
||||
|
|
@ -37,13 +53,7 @@ proc partialInitModule(result: PSym; graph: ModuleGraph; fileIdx: FileIndex; fil
|
|||
incl(result.flags, sfUsed)
|
||||
initStrTable(result.tab)
|
||||
strTableAdd(result.tab, result) # a module knows itself
|
||||
let existing = strTableGet(packSym.tab, result.name)
|
||||
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)
|
||||
strTableAdd(packSym.tab, result)
|
||||
|
||||
proc newModule(graph: ModuleGraph; fileIdx: FileIndex): PSym =
|
||||
# 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)
|
||||
result.id = -1 # for better error checking
|
||||
result.kind = skModule
|
||||
let filename = toFullPath(graph.config, fileIdx)
|
||||
let filename = AbsoluteFile toFullPath(graph.config, fileIdx)
|
||||
result.name = getIdent(graph.cache, splitFile(filename).name)
|
||||
if not isNimIdentifier(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 =
|
||||
result = graph.getModule(fileIdx)
|
||||
if result == nil:
|
||||
let filename = toFullPath(graph.config, fileIdx)
|
||||
let (r, id) = loadModuleSym(graph, fileIdx, AbsoluteFile filename)
|
||||
let filename = AbsoluteFile toFullPath(graph.config, fileIdx)
|
||||
let (r, id) = loadModuleSym(graph, fileIdx, filename)
|
||||
result = r
|
||||
if result == nil:
|
||||
result = newModule(graph, fileIdx)
|
||||
|
|
|
|||
|
|
@ -29,9 +29,6 @@ proc getPackageName*(conf: ConfigRef; path: string): string =
|
|||
for file in walkFiles(d / "*.nimble"):
|
||||
result = file.splitFile.name
|
||||
break packageSearch
|
||||
for file in walkFiles(d / "*.babel"):
|
||||
result = file.splitFile.name
|
||||
break packageSearch
|
||||
# we also store if we didn't find anything:
|
||||
when not defined(nimNoNilSeqs):
|
||||
if result.isNil: result = ""
|
||||
|
|
@ -41,10 +38,19 @@ proc getPackageName*(conf: ConfigRef; path: string): string =
|
|||
dec parents
|
||||
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 =
|
||||
let x = getPackageName(conf, path.string)
|
||||
if x.len == 0:
|
||||
result = path
|
||||
else:
|
||||
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: string) = a = b & a
|
||||
|
||||
proc `%`*(frmt: FormatStr, args: openArray[Rope]): Rope =
|
||||
proc runtimeFormat*(frmt: FormatStr, args: openArray[Rope]): Rope =
|
||||
var i = 0
|
||||
var length = len(frmt)
|
||||
result = nil
|
||||
|
|
@ -269,7 +269,10 @@ proc `%`*(frmt: FormatStr, args: openArray[Rope]): Rope =
|
|||
add(result, substr(frmt, start, i - 1))
|
||||
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)``.
|
||||
add(c, frmt % args)
|
||||
|
||||
|
|
|
|||
|
|
@ -72,12 +72,6 @@ type
|
|||
|
||||
TExprFlags* = set[TExprFlag]
|
||||
|
||||
TTypeAttachedOp* = enum
|
||||
attachedAsgn,
|
||||
attachedSink,
|
||||
attachedDeepCopy,
|
||||
attachedDestructor
|
||||
|
||||
PContext* = ref TContext
|
||||
TContext* = object of TPassContext # a context represents a module
|
||||
enforceVoidContext*: PType
|
||||
|
|
|
|||
|
|
@ -937,7 +937,7 @@ proc semExprNoType(c: PContext, n: PNode): PNode =
|
|||
let isPush = hintExtendedContext in c.config.notes
|
||||
if isPush: pushInfoContext(c.config, n.info)
|
||||
result = semExpr(c, n, {efWantStmt})
|
||||
result = discardCheck(c, result, {})
|
||||
discardCheck(c, result, {})
|
||||
if isPush: popInfoContext(c.config)
|
||||
|
||||
proc isTypeExpr(n: PNode): bool =
|
||||
|
|
@ -1526,17 +1526,52 @@ proc asgnToResultVar(c: PContext, n, le, ri: PNode) {.inline.} =
|
|||
x.typ.flags.incl tfVarIsPtr
|
||||
#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':
|
||||
if ri.typ != nil and ri.typ.skipTypes(abstractInst).kind == tyOwned and
|
||||
le.typ != nil and le.typ.skipTypes(abstractInst).kind != tyOwned and ri.kind in nkCallKinds:
|
||||
localError(c.config, n.info, "cannot return an owned pointer as an unowned pointer; " &
|
||||
"use 'owned(" & typeToString(le.typ) & ")' as the return type")
|
||||
const absInst = abstractInst - {tyOwned}
|
||||
if ri.typ != nil and ri.typ.skipTypes(absInst).kind == tyOwned and
|
||||
le.typ != nil and le.typ.skipTypes(absInst).kind != tyOwned and
|
||||
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 =
|
||||
typ.isMetaType and typ.kind != tyTypeDesc
|
||||
|
||||
|
||||
proc goodLineInfo(arg: PNode): TLineinfo =
|
||||
if arg.kind == nkStmtListExpr and arg.len > 0:
|
||||
goodLineInfo(arg[^1])
|
||||
|
|
@ -1623,7 +1658,7 @@ proc semAsgn(c: PContext, n: PNode; mode=asgnNormal): PNode =
|
|||
c.p.owner.typ.sons[0] = rhsTyp
|
||||
else:
|
||||
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]))
|
||||
liftTypeBoundOps(c, lhs.typ, lhs.info)
|
||||
|
|
@ -1673,7 +1708,7 @@ proc semProcBody(c: PContext, n: PNode): PNode =
|
|||
a.sons[1] = result
|
||||
result = semAsgn(c, a)
|
||||
else:
|
||||
result = discardCheck(c, result, {})
|
||||
discardCheck(c, result, {})
|
||||
|
||||
if c.p.owner.kind notin {skMacro, skTemplate} and
|
||||
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)
|
||||
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,
|
||||
flags: TExprFlags): PNode =
|
||||
## 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)
|
||||
if t.destructor != nil:
|
||||
result.sons[0] = newSymNode(t.destructor)
|
||||
of mUnown:
|
||||
result = semUnown(c, 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)
|
||||
|
||||
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) =
|
||||
let a = skipConvAndClosure(n)
|
||||
|
|
@ -738,7 +738,7 @@ proc track(tracked: PEffects, n: PNode) =
|
|||
mergeTags(tracked, effectList.sons[tagEffects], n)
|
||||
gcsafeAndSideeffectCheck()
|
||||
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}:
|
||||
# may not look like an assignment, but it is:
|
||||
let arg = n.sons[1]
|
||||
|
|
@ -754,7 +754,7 @@ proc track(tracked: PEffects, n: PNode) =
|
|||
track(tracked, n.sons[i])
|
||||
of nkDotExpr:
|
||||
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:
|
||||
track(tracked, n.sons[0])
|
||||
if warnProveField in tracked.config.notes:
|
||||
|
|
@ -821,7 +821,7 @@ proc track(tracked: PEffects, n: PNode) =
|
|||
of nkForStmt, nkParForStmt:
|
||||
# we are very conservative here and assume the loop is never executed:
|
||||
let oldState = tracked.init.len
|
||||
for i in 0 .. len(n)-3:
|
||||
for i in 0 .. n.len-3:
|
||||
let it = n[i]
|
||||
track(tracked, it)
|
||||
if tracked.owner.kind != skMacro:
|
||||
|
|
@ -830,13 +830,19 @@ proc track(tracked: PEffects, n: PNode) =
|
|||
createTypeBoundOps(tracked.c, x.typ, x.info)
|
||||
else:
|
||||
createTypeBoundOps(tracked.c, it.typ, it.info)
|
||||
for i in len(n)-2..len(n)-1:
|
||||
track(tracked, n.sons[i])
|
||||
let iterCall = n[n.len-2]
|
||||
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)
|
||||
of nkObjConstr:
|
||||
when false: track(tracked, n.sons[0])
|
||||
let oldFacts = tracked.guards.s.len
|
||||
for i in 1 ..< len(n):
|
||||
for i in 1 ..< n.len:
|
||||
let x = n.sons[i]
|
||||
track(tracked, x)
|
||||
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])
|
||||
let sonType = n.sons[0].typ
|
||||
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)
|
||||
if sonType.kind == tyProc and sonKind notin nkCallKinds:
|
||||
# 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)
|
||||
n.typ = nil
|
||||
|
||||
proc discardCheck(c: PContext, expr: PNode, flags: TExprFlags): PNode =
|
||||
result = expr
|
||||
proc discardCheck(c: PContext, result: PNode, flags: TExprFlags) =
|
||||
if c.matchedConcept != nil or efInTypeof in flags: return
|
||||
|
||||
if result.typ != nil and result.typ.kind notin {tyStmt, tyVoid}:
|
||||
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:
|
||||
var n = result
|
||||
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)
|
||||
if isEmptyType(typ) or typ.kind in {tyNil, tyExpr} or
|
||||
(not hasElse and efInTypeof notin flags):
|
||||
for it in n:
|
||||
it.sons[^1] = discardCheck(c, it.sons[^1], flags)
|
||||
for it in n: discardCheck(c, it.lastSon, flags)
|
||||
result.kind = nkIfStmt
|
||||
# propagate any enforced VoidContext:
|
||||
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
|
||||
if isEmptyType(typ) or typ.kind in {tyNil, tyExpr}:
|
||||
n.sons[0] = discardCheck(c, n.sons[0], flags)
|
||||
for i in 1..n.len-1:
|
||||
n.sons[i].sons[^1] = discardCheck(c, n.sons[i].sons[^1], flags)
|
||||
discardCheck(c, n.sons[0], flags)
|
||||
for i in 1..n.len-1: discardCheck(c, n.sons[i].lastSon, flags)
|
||||
if typ == c.enforceVoidContext:
|
||||
result.typ = c.enforceVoidContext
|
||||
else:
|
||||
if n.lastSon.kind == nkFinally:
|
||||
n.sons[^1].sons[^1] = discardCheck(c, n.sons[^1].sons[^1], flags)
|
||||
if n.lastSon.kind == nkFinally: discardCheck(c, n.lastSon.lastSon, flags)
|
||||
n.sons[0] = fitNode(c, typ, n.sons[0], n.sons[0].info)
|
||||
for i in 1..last:
|
||||
var it = n.sons[i]
|
||||
|
|
@ -474,10 +471,14 @@ proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode =
|
|||
($typ.kind).substr(2).toLowerAscii)
|
||||
elif typ.kind == tyProc and tfUnresolved in typ.flags:
|
||||
localError(c.config, def.info, errProcHasNoConcreteType % def.renderTree)
|
||||
elif 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
|
||||
when false:
|
||||
# XXX This typing rule is neither documented nor complete enough to
|
||||
# justify it. Instead use the newer 'unowned x' until we figured out
|
||||
# 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:
|
||||
if symkind == skLet: localError(c.config, a.info, errLetNeedsInit)
|
||||
|
||||
|
|
@ -729,7 +730,7 @@ proc semForVars(c: PContext, n: PNode; flags: TExprFlags): PNode =
|
|||
openScope(c)
|
||||
n.sons[length-1] = semExprBranch(c, n.sons[length-1], flags)
|
||||
if efInTypeof notin flags:
|
||||
n.sons[^1] = discardCheck(c, n.sons[^1], flags)
|
||||
discardCheck(c, n.sons[length-1], flags)
|
||||
closeScope(c)
|
||||
dec(c.p.nestedLoopCounter)
|
||||
|
||||
|
|
@ -919,8 +920,7 @@ proc semCase(c: PContext, n: PNode; flags: TExprFlags): PNode =
|
|||
closeScope(c)
|
||||
if isEmptyType(typ) or typ.kind in {tyNil, tyExpr} or
|
||||
(not hasElse and efInTypeof notin flags):
|
||||
for i in 1..n.len-1:
|
||||
n.sons[i].sons[^1] = discardCheck(c, n.sons[i].sons[^1], flags)
|
||||
for i in 1..n.len-1: discardCheck(c, n.sons[i].lastSon, flags)
|
||||
# propagate any enforced VoidContext:
|
||||
if 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}:
|
||||
obj = canonType(c, obj)
|
||||
if obj.destructor.isNil:
|
||||
obj.destructor = s
|
||||
obj.attachedOps[attachedDestructor] = s
|
||||
else:
|
||||
prevDestructor(c, obj.destructor, obj, n.info)
|
||||
noError = true
|
||||
|
|
@ -1592,7 +1592,7 @@ proc semOverride(c: PContext, s: PSym, n: PNode) =
|
|||
elif t.kind == tyGenericInvocation: t = t.sons[0]
|
||||
else: break
|
||||
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:
|
||||
localError(c.config, n.info, errGenerated,
|
||||
"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
|
||||
obj = canonType(c, 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)
|
||||
if opr[].isNil:
|
||||
opr[] = s
|
||||
let k = if name == "=": attachedAsgn else: attachedSink
|
||||
if obj.attachedOps[k].isNil:
|
||||
obj.attachedOps[k] = s
|
||||
else:
|
||||
prevDestructor(c, opr[], obj, n.info)
|
||||
prevDestructor(c, obj.attachedOps[k], obj, n.info)
|
||||
if obj.owner.getModule != s.getModule:
|
||||
localError(c.config, n.info, errGenerated,
|
||||
"type bound operation `" & name & "` can be defined only in the same module with its type (" & obj.typeToString() & ")")
|
||||
|
|
@ -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 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 &
|
||||
" operator has to be enabled with {.experimental: \"dotOperators\".}")
|
||||
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
|
||||
if not isEmptyType(n.typ): n.kind = nkStmtListExpr
|
||||
elif i != last or voidContext:
|
||||
n.sons[i] = discardCheck(c, n.sons[i], flags)
|
||||
discardCheck(c, n.sons[i], flags)
|
||||
else:
|
||||
n.typ = n.sons[i].typ
|
||||
if not isEmptyType(n.typ): n.kind = nkStmtListExpr
|
||||
|
|
|
|||
|
|
@ -559,9 +559,11 @@ proc semTemplateDef(c: PContext, n: PNode): PNode =
|
|||
else:
|
||||
s = semIdentVis(c, skTemplate, n.sons[0], {})
|
||||
|
||||
if s.owner != nil and sfSystemModule in s.owner.flags and
|
||||
s.name.s in ["!=", ">=", ">", "incl", "excl", "in", "notin", "isnot"]:
|
||||
incl(s.flags, sfCallsite)
|
||||
if s.owner != nil:
|
||||
const names = ["!=", ">=", ">", "incl", "excl", "in", "notin", "isnot"]
|
||||
if sfSystemModule in s.owner.flags and s.name.s in names or
|
||||
s.owner.name.s == "vm" and s.name.s == "stackTrace":
|
||||
incl(s.flags, sfCallsite)
|
||||
|
||||
styleCheckDef(c.config, s)
|
||||
onDef(n[0].info, s)
|
||||
|
|
|
|||
|
|
@ -1348,8 +1348,8 @@ proc semGeneric(c: PContext, n: PNode, s: PSym, prev: PType): PType =
|
|||
# special check for generic object with
|
||||
# generic/partial specialized parent
|
||||
let tx = result.skipTypes(abstractPtrs, 50)
|
||||
if tx.isNil:
|
||||
localError(c.config, n.info, "invalid recursion in type '$1'" % typeToString(result[0]))
|
||||
if tx.isNil or isTupleRecursive(tx):
|
||||
localError(c.config, n.info, "illegal recursion in type '$1'" % typeToString(result[0]))
|
||||
return errorType(c)
|
||||
if tx != result and tx.kind == tyObject and tx.sons[0] != nil:
|
||||
semObjectTypeForInheritedGenericInst(c, n, tx)
|
||||
|
|
|
|||
|
|
@ -380,13 +380,13 @@ proc handleGenericInvocation(cl: var TReplTypeVars, t: PType): PType =
|
|||
if newbody.isGenericAlias: newbody = newbody.skipGenericAlias
|
||||
rawAddSon(result, newbody)
|
||||
checkPartialConstructedType(cl.c.config, cl.info, newbody)
|
||||
let dc = newbody.deepCopy
|
||||
let dc = newbody.attachedOps[attachedDeepCopy]
|
||||
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
|
||||
# generics *when the type is constructed*:
|
||||
newbody.deepCopy = cl.c.instTypeBoundOp(cl.c, dc, result, cl.info,
|
||||
attachedDeepCopy, 1)
|
||||
newbody.attachedOps[attachedDeepCopy] = cl.c.instTypeBoundOp(cl.c, dc, result, cl.info,
|
||||
attachedDeepCopy, 1)
|
||||
if bodyIsNew and newbody.typeInst == nil:
|
||||
#doassert newbody.typeInst == nil
|
||||
newbody.typeInst = result
|
||||
|
|
@ -592,7 +592,8 @@ proc replaceTypeVarsTAux(cl: var TReplTypeVars, t: PType): PType =
|
|||
skipIntLiteralParams(result)
|
||||
|
||||
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:
|
||||
let s = cl.c.graph.sysTypes[tySequence]
|
||||
var old = copyType(s, s.owner, keepId=false)
|
||||
|
|
@ -601,9 +602,7 @@ proc replaceTypeVarsTAux(cl: var TReplTypeVars, t: PType): PType =
|
|||
old.n = nil
|
||||
old.flags = {tfHasAsgn}
|
||||
old.addSonSkipIntLit result[0]
|
||||
result.destructor = old.destructor
|
||||
result.assignment = old.assignment
|
||||
result.sink = old.sink
|
||||
result.attachedOps = old.attachedOps
|
||||
cl.c.typesWithOps.add((result, old))
|
||||
|
||||
else: discard
|
||||
|
|
@ -619,19 +618,19 @@ proc replaceTypeVarsTAux(cl: var TReplTypeVars, t: PType): PType =
|
|||
result.n = replaceObjBranches(cl, result.n)
|
||||
|
||||
template typeBound(c, newty, oldty, field, info) =
|
||||
let opr = newty.field
|
||||
let opr = newty.attachedOps[field]
|
||||
if opr != nil and sfFromGeneric notin opr.flags:
|
||||
# '=' needs to be instantiated for generics when the type is constructed:
|
||||
#echo "DESTROY: instantiating ", astToStr(field), " for ", typeToString(oldty)
|
||||
newty.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) =
|
||||
var i = 0
|
||||
while i < c.typesWithOps.len:
|
||||
let (newty, oldty) = c.typesWithOps[i]
|
||||
typeBound(c, newty, oldty, destructor, info)
|
||||
typeBound(c, newty, oldty, sink, info)
|
||||
typeBound(c, newty, oldty, assignment, info)
|
||||
typeBound(c, newty, oldty, attachedDestructor, info)
|
||||
typeBound(c, newty, oldty, attachedSink, info)
|
||||
typeBound(c, newty, oldty, attachedAsgn, info)
|
||||
inc i
|
||||
setLen(c.typesWithOps, 0)
|
||||
|
||||
|
|
|
|||
|
|
@ -349,7 +349,7 @@ proc hashBodyTree(graph: ModuleGraph, c: var MD5Context, n: PNode) =
|
|||
c &= n.strVal
|
||||
else:
|
||||
for i in 0..<n.len:
|
||||
hashTree(c, n.sons[i])
|
||||
hashBodyTree(graph, c, n.sons[i])
|
||||
|
||||
proc symBodyDigest*(graph: ModuleGraph, sym: PSym): SigHash =
|
||||
## compute unique digest of the proc/func/method symbols
|
||||
|
|
@ -369,7 +369,7 @@ proc symBodyDigest*(graph: ModuleGraph, sym: PSym): SigHash =
|
|||
if sym.ast != nil:
|
||||
md5Init(c)
|
||||
c.md5Update(cast[cstring](result.addr), sizeof(result))
|
||||
c.hashTree(sym.ast[bodyPos])
|
||||
hashBodyTree(graph, c, sym.ast[bodyPos])
|
||||
c.md5Final(result.Md5Digest)
|
||||
graph.symBodyHashes[sym.id] = result
|
||||
|
||||
|
|
|
|||
|
|
@ -2092,14 +2092,21 @@ proc paramTypesMatchAux(m: var TCandidate, f, a: PType,
|
|||
result = localConvMatch(c, m, f, a, arg)
|
||||
else:
|
||||
r = typeRel(m, base(f), a)
|
||||
if r >= isGeneric:
|
||||
case r
|
||||
of isGeneric:
|
||||
inc(m.convMatches)
|
||||
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
|
||||
# bug #4799, varargs accepting subtype relation object
|
||||
elif r == isSubtype:
|
||||
of isFromIntLit:
|
||||
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)
|
||||
if base(f).kind == tyTypeDesc:
|
||||
result = arg
|
||||
|
|
|
|||
|
|
@ -651,6 +651,8 @@ proc transformFor(c: PTransf, n: PNode): PTransNode =
|
|||
var t = formal.typ
|
||||
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.
|
||||
elif t.destructor == nil and arg.typ.destructor != nil:
|
||||
t = arg.typ
|
||||
# generate a temporary and produce an assignment statement:
|
||||
var temp = newTemp(c, t, formal.info)
|
||||
#temp.sym.flags.incl sfCursor
|
||||
|
|
|
|||
|
|
@ -101,7 +101,7 @@ proc isDeepConstExpr*(n: PNode): bool =
|
|||
if not isDeepConstExpr(n.sons[i]): return false
|
||||
if n.typ.isNil: result = true
|
||||
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 != tyObject or not isCaseObj(t.n):
|
||||
result = true
|
||||
|
|
|
|||
|
|
@ -931,6 +931,21 @@ proc ldNullOpcode(t: PType): TOpcode =
|
|||
assert t != nil
|
||||
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) =
|
||||
case m
|
||||
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.genAsgnPatch(n.sons[1], 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:
|
||||
unused(c, n, dest)
|
||||
c.genNew(n)
|
||||
|
|
@ -1330,6 +1345,17 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest; m: TMagic) =
|
|||
of mRunnableExamples:
|
||||
discard "just ignore any call to runnableExamples"
|
||||
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:
|
||||
# mGCref, mGCunref,
|
||||
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):
|
||||
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) =
|
||||
let tmp = c.genx(ri)
|
||||
assert dest >= 0
|
||||
|
|
@ -1585,8 +1596,8 @@ proc genGlobalInit(c: PCtx; n: PNode; s: PSym) =
|
|||
# var decls{.compileTime.}: seq[NimNode] = @[]
|
||||
let dest = c.getTemp(s.typ)
|
||||
c.gABx(n, opcLdGlobal, dest, s.position)
|
||||
if s.ast != nil:
|
||||
let tmp = c.genx(s.ast)
|
||||
if s.astdef != nil:
|
||||
let tmp = c.genx(s.astdef)
|
||||
c.genAdditionalCopy(n, opcWrDeref, dest, 0, tmp)
|
||||
c.freeTemp(dest)
|
||||
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
|
||||
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
|
||||
----------------------
|
||||
|
|
@ -186,21 +201,25 @@ file.
|
|||
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
|
||||
in your source code the hyper link *See source* will appear pointing to the
|
||||
implementation of that item on a GitHub repository. You can click the link to
|
||||
see the implementation of the item.
|
||||
With the ``git.url`` switch the *See source* hyperlink will appear below each
|
||||
documented item in your source code pointing to the implementation of that
|
||||
item on a GitHub repository.
|
||||
You can click the link to see the implementation of the item.
|
||||
|
||||
If you want to reuse this feature in your own documentation you will have to
|
||||
modify ``config/nimdoc.cfg`` to contain a ``doc.item.seesrc`` value with a
|
||||
hyper link to your own code repository. As you will see by the comments in that
|
||||
file, the value ``txt`` passed on the command line will be used in the HTML
|
||||
template along others like ``$path`` and ``$line``.
|
||||
The ``git.commit`` switch overrides the hardcoded `devel` branch in config/nimdoc.cfg.
|
||||
This is useful to link to a different branch e.g. `--git.commit:master`,
|
||||
or to a tag e.g. `--git.commit:1.2.3` or a commit.
|
||||
|
||||
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
|
||||
``tools/nimweb.nim`` helper queries the current git commit hash during doc
|
||||
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
|
||||
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
|
||||
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
|
||||
|
|
@ -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
|
||||
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,
|
||||
you will need to use the complex name. A complex name for a callable type is
|
||||
made up from several parts:
|
||||
|
|
@ -313,14 +332,14 @@ columns is:
|
|||
|
||||
1. Mandatory term being indexed. Terms can include quoting according to
|
||||
Nim's rules (eg. \`^\`).
|
||||
2. Base filename plus anchor hyper link (eg.
|
||||
2. Base filename plus anchor hyperlink (eg.
|
||||
``algorithm.html#*,int,SortOrder``).
|
||||
3. Optional human readable string to display as hyper link. If the value is not
|
||||
present or is the empty string, the hyper link will be rendered
|
||||
3. Optional human readable string to display as hyperlink. If the value is not
|
||||
present or is the empty string, the hyperlink will be rendered
|
||||
using the term. Prefix whitespace indicates that this entry is
|
||||
not for an API symbol but for a TOC entry.
|
||||
4. Optional title or description of the hyper link. Browsers usually display
|
||||
this as a tooltip after hovering a moment over the hyper link.
|
||||
4. Optional title or description of the hyperlink. Browsers usually display
|
||||
this as a tooltip after hovering a moment over the hyperlink.
|
||||
|
||||
The index generation tools try to differentiate between documentation
|
||||
generated from ``.nim`` files and documentation generated from ``.txt`` or
|
||||
|
|
|
|||
|
|
@ -22,7 +22,10 @@ precise wording. This manual is constantly evolving into a proper specification.
|
|||
**Note**: The experimental features of Nim are
|
||||
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)*``
|
||||
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)
|
||||
|
||||
``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
|
||||
|
|
@ -4434,7 +4437,7 @@ templates:
|
|||
| ``notin`` and ``isnot`` have the obvious meanings.
|
||||
|
||||
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
|
||||
are expected.
|
||||
|
||||
|
|
@ -4598,7 +4601,7 @@ In templates identifiers can be constructed with the backticks notation:
|
|||
.. code-block:: nim
|
||||
:test: "nim c $1"
|
||||
|
||||
template typedef(name: untyped, typ: type) =
|
||||
template typedef(name: untyped, typ: typedesc) =
|
||||
type
|
||||
`T name`* {.inject.} = typ
|
||||
`P name`* {.inject.} = ref `T name`
|
||||
|
|
@ -4660,7 +4663,7 @@ template cannot be accessed in the instantiation context:
|
|||
.. code-block:: nim
|
||||
:test: "nim c $1"
|
||||
|
||||
template newException*(exceptn: type, message: string): untyped =
|
||||
template newException*(exceptn: typedesc, message: string): untyped =
|
||||
var
|
||||
e: ref exceptn # e is implicitly gensym'ed here
|
||||
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
|
||||
possible type mismatch error.
|
||||
|
||||
type[T]
|
||||
-------
|
||||
typedesc[T]
|
||||
-----------
|
||||
|
||||
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
|
||||
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
|
||||
``int`` is ``type[int]``. Just like with regular generic types, when the
|
||||
generic param is ommited, ``type`` denotes the type class of all types.
|
||||
As a syntactic convenience, you can also use ``type`` as a modifier.
|
||||
``type int`` is considered the same as ``type[int]``.
|
||||
``typedesc`` acts like a generic type. For instance, the type of the symbol
|
||||
``int`` is ``typedesc[int]``. Just like with regular generic types, when the
|
||||
generic param is ommited, ``typedesc`` denotes the type class of all types.
|
||||
As a syntactic convenience, you can also use ``typedesc`` as a modifier.
|
||||
|
||||
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
|
||||
and within the body of the proc, the name of each param will refer to
|
||||
the bound concrete type:
|
||||
|
||||
.. code-block:: nim
|
||||
|
||||
proc new(T: type): ref T =
|
||||
proc new(T: typedesc): ref T =
|
||||
echo "allocating ", T.name
|
||||
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:
|
||||
|
||||
.. 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:
|
||||
|
||||
.. code-block:: nim
|
||||
:test: "nim c $1"
|
||||
|
||||
template declareVariableWithType(T: type, value: T) =
|
||||
template declareVariableWithType(T: typedesc, value: T) =
|
||||
var x: T = value
|
||||
|
||||
declareVariableWithType int, 42
|
||||
|
|
@ -5145,8 +5147,8 @@ types that will match the type param:
|
|||
.. code-block:: nim
|
||||
:test: "nim c $1"
|
||||
|
||||
template maxval(T: type int): int = high(int)
|
||||
template maxval(T: type float): float = Inf
|
||||
template maxval(T: typedesc[int]): int = high(int)
|
||||
template maxval(T: typedesc[float]): float = Inf
|
||||
|
||||
var i = int.maxval
|
||||
var f = float.maxval
|
||||
|
|
@ -5159,7 +5161,8 @@ The constraint can be a concrete type or a type class.
|
|||
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``
|
||||
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
|
||||
template dbTable(name: string, table_space: string = "") {.pragma.}
|
||||
template dbKey(name: string = "", primary_key: bool = false) {.pragma.}
|
||||
template dbForeignKey(t: type) {.pragma.}
|
||||
template dbForeignKey(t: typedesc) {.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
|
||||
|
||||
# Adapt the Matrix type to the concept's requirements
|
||||
template Rows*(M: type Matrix): int = M.M
|
||||
template Cols*(M: type Matrix): int = M.N
|
||||
template ValueType*(M: type Matrix): type = M.T
|
||||
template Rows*(M: typedesc[Matrix]): int = M.M
|
||||
template Cols*(M: typedesc[Matrix]): int = M.N
|
||||
template ValueType*(M: typedesc[Matrix]): typedesc = M.T
|
||||
|
||||
-------------
|
||||
### usage.nim
|
||||
|
|
|
|||
|
|
@ -1076,20 +1076,24 @@ proc expectKind*(n: NimNode; k: set[NimNodeKind]) {.compileTime.} =
|
|||
## macros that check the AST that is passed to them.
|
||||
if n.kind notin k: error("Expected one of " & $k & ", got " & $n.kind, n)
|
||||
|
||||
proc newProc*(name = newEmptyNode(); params: openArray[NimNode] = [newEmptyNode()];
|
||||
body: NimNode = newStmtList(), procType = nnkProcDef): NimNode {.compileTime.} =
|
||||
proc newProc*(name = newEmptyNode();
|
||||
params: openArray[NimNode] = [newEmptyNode()];
|
||||
body: NimNode = newStmtList();
|
||||
procType = nnkProcDef;
|
||||
pragmas: NimNode = newEmptyNode()): NimNode {.compileTime.} =
|
||||
## shortcut for creating a new proc
|
||||
##
|
||||
## The ``params`` array must start with the return type of the proc,
|
||||
## followed by a list of IdentDefs which specify the params.
|
||||
if procType notin RoutineNodes:
|
||||
error("Expected one of " & $RoutineNodes & ", got " & $procType)
|
||||
pragmas.expectKind({nnkEmpty, nnkPragma})
|
||||
result = newNimNode(procType).add(
|
||||
name,
|
||||
newEmptyNode(),
|
||||
newEmptyNode(),
|
||||
newNimNode(nnkFormalParams).add(params),
|
||||
newEmptyNode(), # pragmas
|
||||
pragmas,
|
||||
newEmptyNode(),
|
||||
body)
|
||||
|
||||
|
|
|
|||
|
|
@ -47,8 +47,9 @@ proc nimNewObj(size: int): pointer {.compilerRtl.} =
|
|||
when defined(nimscript):
|
||||
discard
|
||||
elif defined(useMalloc):
|
||||
result = c_malloc(s) +! sizeof(RefHeader)
|
||||
nimZeroMem(result, s)
|
||||
var orig = c_malloc(s)
|
||||
nimZeroMem(orig, s)
|
||||
result = orig +! sizeof(RefHeader)
|
||||
else:
|
||||
result = alloc0(s) +! sizeof(RefHeader)
|
||||
inc allocs
|
||||
|
|
|
|||
|
|
@ -104,6 +104,10 @@ proc newSeqPayload(cap, elemSize: int): pointer {.compilerRtl, raises: [].} =
|
|||
proc prepareSeqAdd(len: int; p: pointer; addlen, elemSize: int): pointer {.
|
||||
compilerRtl, noSideEffect, raises: [].} =
|
||||
{.noSideEffect.}:
|
||||
template `+!`(p: pointer, s: int): pointer =
|
||||
cast[pointer](cast[int](p) +% s)
|
||||
|
||||
const headerSize = sizeof(int) + sizeof(Allocator)
|
||||
if addlen <= 0:
|
||||
result = p
|
||||
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
|
||||
# they get reallocated here. This needs to be documented clearly.
|
||||
var p = cast[ptr PayloadBase](p)
|
||||
let allocator = if p.allocator == nil: getLocalAllocator() else: p.allocator
|
||||
let cap = max(resize(p.cap), len+addlen)
|
||||
var q = cast[ptr PayloadBase](allocator.realloc(allocator, p,
|
||||
sizeof(int) + sizeof(Allocator) + elemSize * p.cap,
|
||||
sizeof(int) + sizeof(Allocator) + elemSize * cap))
|
||||
q.allocator = allocator
|
||||
q.cap = cap
|
||||
result = q
|
||||
if p.allocator == nil:
|
||||
let allocator = getLocalAllocator()
|
||||
var q = cast[ptr PayloadBase](allocator.alloc(allocator,
|
||||
headerSize + elemSize * cap))
|
||||
copyMem(q +! headerSize, p +! headerSize, len * elemSize)
|
||||
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) =
|
||||
mixin `=destroy`
|
||||
|
|
@ -140,6 +153,20 @@ proc grow*[T](x: var seq[T]; newLen: Natural; value: T) =
|
|||
for i in oldLen .. newLen-1:
|
||||
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) =
|
||||
{.noSideEffect.}:
|
||||
if newlen < s.len:
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@
|
|||
## `db_postgres <db_postgres.html>`_.
|
||||
##
|
||||
## Parameter substitution
|
||||
## ----------------------
|
||||
## ======================
|
||||
##
|
||||
## All ``db_*`` modules support the same form of parameter substitution.
|
||||
## That is, using the ``?`` (question mark) to signify the place where a
|
||||
|
|
@ -25,10 +25,10 @@
|
|||
##
|
||||
##
|
||||
## Examples
|
||||
## --------
|
||||
## ========
|
||||
##
|
||||
## Opening a connection to a database
|
||||
## ==================================
|
||||
## ----------------------------------
|
||||
##
|
||||
## .. code-block:: Nim
|
||||
## import db_mysql
|
||||
|
|
@ -36,7 +36,7 @@
|
|||
## db.close()
|
||||
##
|
||||
## Creating a table
|
||||
## ================
|
||||
## ----------------
|
||||
##
|
||||
## .. code-block:: Nim
|
||||
## db.exec(sql"DROP TABLE IF EXISTS myTable")
|
||||
|
|
@ -45,14 +45,14 @@
|
|||
## name varchar(50) not null)"""))
|
||||
##
|
||||
## Inserting data
|
||||
## ==============
|
||||
## --------------
|
||||
##
|
||||
## .. code-block:: Nim
|
||||
## db.exec(sql"INSERT INTO myTable (id, name) VALUES (0, ?)",
|
||||
## "Dominik")
|
||||
##
|
||||
## Larger example
|
||||
## ==============
|
||||
## --------------
|
||||
##
|
||||
## .. code-block:: Nim
|
||||
##
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@
|
|||
## `db_mysql <db_mysql.html>`_.
|
||||
##
|
||||
## Parameter substitution
|
||||
## ----------------------
|
||||
## ======================
|
||||
##
|
||||
## All ``db_*`` modules support the same form of parameter substitution.
|
||||
## That is, using the ``?`` (question mark) to signify the place where a
|
||||
|
|
@ -31,10 +31,10 @@
|
|||
##
|
||||
##
|
||||
## Examples
|
||||
## --------
|
||||
## ========
|
||||
##
|
||||
## Opening a connection to a database
|
||||
## ==================================
|
||||
## ----------------------------------
|
||||
##
|
||||
## .. code-block:: Nim
|
||||
## import db_odbc
|
||||
|
|
@ -42,7 +42,7 @@
|
|||
## db.close()
|
||||
##
|
||||
## Creating a table
|
||||
## ================
|
||||
## ----------------
|
||||
##
|
||||
## .. code-block:: Nim
|
||||
## db.exec(sql"DROP TABLE IF EXISTS myTable")
|
||||
|
|
@ -51,14 +51,14 @@
|
|||
## name varchar(50) not null)"""))
|
||||
##
|
||||
## Inserting data
|
||||
## ==============
|
||||
## --------------
|
||||
##
|
||||
## .. code-block:: Nim
|
||||
## db.exec(sql"INSERT INTO myTable (id, name) VALUES (0, ?)",
|
||||
## "Andreas")
|
||||
##
|
||||
## Large example
|
||||
## =============
|
||||
## -------------
|
||||
##
|
||||
## .. code-block:: Nim
|
||||
##
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@
|
|||
## `db_mysql <db_mysql.html>`_.
|
||||
##
|
||||
## Parameter substitution
|
||||
## ----------------------
|
||||
## ======================
|
||||
##
|
||||
## All ``db_*`` modules support the same form of parameter substitution.
|
||||
## That is, using the ``?`` (question mark) to signify the place where a
|
||||
|
|
@ -38,10 +38,10 @@
|
|||
## 3)
|
||||
##
|
||||
## Examples
|
||||
## --------
|
||||
## ========
|
||||
##
|
||||
## Opening a connection to a database
|
||||
## ==================================
|
||||
## ----------------------------------
|
||||
##
|
||||
## .. code-block:: Nim
|
||||
## import db_postgres
|
||||
|
|
@ -49,7 +49,7 @@
|
|||
## db.close()
|
||||
##
|
||||
## Creating a table
|
||||
## ================
|
||||
## ----------------
|
||||
##
|
||||
## .. code-block:: Nim
|
||||
## db.exec(sql"DROP TABLE IF EXISTS myTable")
|
||||
|
|
@ -58,7 +58,7 @@
|
|||
## name varchar(50) not null)"""))
|
||||
##
|
||||
## Inserting data
|
||||
## ==============
|
||||
## --------------
|
||||
##
|
||||
## .. code-block:: Nim
|
||||
## db.exec(sql"INSERT INTO myTable (id, name) VALUES (0, ?)",
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@
|
|||
## `db_mysql <db_mysql.html>`_.
|
||||
##
|
||||
## Parameter substitution
|
||||
## ----------------------
|
||||
## ======================
|
||||
##
|
||||
## All ``db_*`` modules support the same form of parameter substitution.
|
||||
## That is, using the ``?`` (question mark) to signify the place where a
|
||||
|
|
@ -24,10 +24,10 @@
|
|||
## sql"INSERT INTO myTable (colA, colB, colC) VALUES (?, ?, ?)"
|
||||
##
|
||||
## Examples
|
||||
## --------
|
||||
## ========
|
||||
##
|
||||
## Opening a connection to a database
|
||||
## ==================================
|
||||
## ----------------------------------
|
||||
##
|
||||
## .. code-block:: Nim
|
||||
## import db_sqlite
|
||||
|
|
@ -35,7 +35,7 @@
|
|||
## db.close()
|
||||
##
|
||||
## Creating a table
|
||||
## ================
|
||||
## ----------------
|
||||
##
|
||||
## .. code-block:: Nim
|
||||
## db.exec(sql"DROP TABLE IF EXISTS myTable")
|
||||
|
|
@ -44,14 +44,14 @@
|
|||
## name varchar(50) not null)"""))
|
||||
##
|
||||
## Inserting data
|
||||
## ==============
|
||||
## --------------
|
||||
##
|
||||
## .. code-block:: Nim
|
||||
## db.exec(sql"INSERT INTO myTable (id, name) VALUES (0, ?)",
|
||||
## "Jack")
|
||||
##
|
||||
## Larger example
|
||||
## ==============
|
||||
## --------------
|
||||
##
|
||||
## .. code-block:: nim
|
||||
##
|
||||
|
|
|
|||
|
|
@ -6,18 +6,6 @@
|
|||
# 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?
|
||||
## ============
|
||||
##
|
||||
|
|
@ -67,6 +55,15 @@ runnableExamples:
|
|||
let matchBounds = firstVowel.get().captureBounds[-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
|
||||
|
|
|
|||
|
|
@ -50,7 +50,7 @@
|
|||
## proc loadGame(name: string): Future[Game] {.async.}
|
||||
##
|
||||
## JavaScript compatibility
|
||||
## ~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
## ========================
|
||||
##
|
||||
## Nim currently generates `async/await` JavaScript code which is supported in modern
|
||||
## EcmaScript and most modern versions of browsers, Node.js and Electron.
|
||||
|
|
|
|||
|
|
@ -7,23 +7,6 @@
|
|||
# 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,
|
||||
## a ``Future`` type implementation, and an ``async`` macro which allows
|
||||
## 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.
|
||||
##
|
||||
## Asynchronous procedures
|
||||
## -----------------------
|
||||
## =======================
|
||||
##
|
||||
## 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
|
||||
|
|
@ -116,7 +99,7 @@ export asyncstreams
|
|||
## exceptions in async procs.
|
||||
##
|
||||
## Handling Exceptions
|
||||
## ~~~~~~~~~~~~~~~~~~~
|
||||
## -------------------
|
||||
##
|
||||
## The most reliable way to handle exceptions is to use ``yield`` on a future
|
||||
## then check the future's ``failed`` property. For example:
|
||||
|
|
@ -142,7 +125,7 @@ export asyncstreams
|
|||
##
|
||||
##
|
||||
## Discarding futures
|
||||
## ------------------
|
||||
## ==================
|
||||
##
|
||||
## 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
|
||||
|
|
@ -151,17 +134,31 @@ export asyncstreams
|
|||
## ``waitFor`` for that purpose.
|
||||
##
|
||||
## Examples
|
||||
## --------
|
||||
## ========
|
||||
##
|
||||
## For examples take a look at the documentation for the modules implementing
|
||||
## asynchronous IO. A good place to start is the
|
||||
## `asyncnet module <asyncnet.html>`_.
|
||||
##
|
||||
## Limitations/Bugs
|
||||
## ----------------
|
||||
## ================
|
||||
##
|
||||
## * 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
|
||||
|
||||
type
|
||||
|
|
@ -1827,4 +1824,4 @@ proc setEvent*(ev: AsyncEvent) {.deprecated.} =
|
|||
## Set event ``ev`` to signaled state.
|
||||
##
|
||||
## **Deprecated since v0.18.0:** Use ``trigger`` instead.
|
||||
ev.trigger()
|
||||
ev.trigger()
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@
|
|||
## * Navigation through the FTP server's directories.
|
||||
##
|
||||
## Connecting to an FTP server
|
||||
## ------------------------
|
||||
## ===========================
|
||||
##
|
||||
## In order to begin any sort of transfer of files you must first
|
||||
## connect to an FTP server. You can do so with the ``connect`` procedure.
|
||||
|
|
@ -34,7 +34,7 @@
|
|||
## client will be connected after the ``await ftp.connect()`` call.
|
||||
##
|
||||
## Uploading a new file
|
||||
## --------------------
|
||||
## ====================
|
||||
##
|
||||
## After a connection is made you can use the ``store`` procedure to upload
|
||||
## a new file to the FTP server. Make sure to check you are in the correct
|
||||
|
|
@ -53,7 +53,7 @@
|
|||
## waitFor(main())
|
||||
##
|
||||
## Checking the progress of a file transfer
|
||||
## ----------------------------------------
|
||||
## ========================================
|
||||
##
|
||||
## The progress of either a file upload or a file download can be checked
|
||||
## by specifying a ``onProgressChanged`` procedure to the ``store`` or
|
||||
|
|
|
|||
|
|
@ -14,9 +14,8 @@
|
|||
## application you should use a reverse proxy (for example nginx) instead of
|
||||
## allowing users to connect directly to this server.
|
||||
##
|
||||
##
|
||||
## Basic usage
|
||||
## -----------
|
||||
## ===========
|
||||
##
|
||||
## 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"
|
||||
|
|
|
|||
|
|
@ -7,8 +7,6 @@
|
|||
# distribution, for details about the copyright.
|
||||
#
|
||||
|
||||
## AsyncMacro
|
||||
## *************
|
||||
## `asyncdispatch` module depends on the `asyncmacro` module to work properly.
|
||||
|
||||
import macros, strutils, asyncfutures
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@
|
|||
## asynchronous dispatcher defined in the ``asyncdispatch`` module.
|
||||
##
|
||||
## Asynchronous IO in Nim
|
||||
## ----------------------
|
||||
## ======================
|
||||
##
|
||||
## 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.
|
||||
##
|
||||
## SSL
|
||||
## ----
|
||||
## ===
|
||||
##
|
||||
## 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.
|
||||
##
|
||||
## Examples
|
||||
## --------
|
||||
## ========
|
||||
##
|
||||
## 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
|
||||
## four 6-bit Base64 digits.
|
||||
##
|
||||
##
|
||||
## 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.
|
||||
##
|
||||
## 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
|
||||
DoublyLinkedNode*[T] = ref DoublyLinkedNodeObj[T]
|
||||
|
||||
SinglyLinkedNodeObj*[T] = object ## A node a singly linked list consists of.
|
||||
##
|
||||
## It consists of a `value` field, and a pointer to `next`.
|
||||
next*: ref SinglyLinkedNodeObj[T]
|
||||
next*: <//>(ref SinglyLinkedNodeObj[T])
|
||||
value*: T
|
||||
SinglyLinkedNode*[T] = ref SinglyLinkedNodeObj[T]
|
||||
|
||||
|
|
@ -95,19 +96,22 @@ type
|
|||
##
|
||||
## Use `initSinglyLinkedList proc <#initSinglyLinkedList>`_ to create
|
||||
## a new empty list.
|
||||
head*, tail*: SinglyLinkedNode[T]
|
||||
head*: <//>(SinglyLinkedNode[T])
|
||||
tail*: SinglyLinkedNode[T]
|
||||
|
||||
DoublyLinkedList*[T] = object ## A doubly linked list.
|
||||
##
|
||||
## Use `initDoublyLinkedList proc <#initDoublyLinkedList>`_ to create
|
||||
## a new empty list.
|
||||
head*, tail*: DoublyLinkedNode[T]
|
||||
head*: <//>(DoublyLinkedNode[T])
|
||||
tail*: DoublyLinkedNode[T]
|
||||
|
||||
SinglyLinkedRing*[T] = object ## A singly linked ring.
|
||||
##
|
||||
## Use `initSinglyLinkedRing proc <#initSinglyLinkedRing>`_ to create
|
||||
## a new empty ring.
|
||||
head*, tail*: SinglyLinkedNode[T]
|
||||
head*: <//>(SinglyLinkedNode[T])
|
||||
tail*: SinglyLinkedNode[T]
|
||||
|
||||
DoublyLinkedRing*[T] = object ## A doubly linked ring.
|
||||
##
|
||||
|
|
@ -147,7 +151,7 @@ proc initDoublyLinkedRing*[T](): DoublyLinkedRing[T] =
|
|||
var a = initDoublyLinkedRing[int]()
|
||||
discard
|
||||
|
||||
proc newDoublyLinkedNode*[T](value: T): DoublyLinkedNode[T] =
|
||||
proc newDoublyLinkedNode*[T](value: T): <//>(DoublyLinkedNode[T]) =
|
||||
## Creates a new doubly linked node with the given `value`.
|
||||
runnableExamples:
|
||||
var n = newDoublyLinkedNode[int](5)
|
||||
|
|
@ -156,7 +160,7 @@ proc newDoublyLinkedNode*[T](value: T): DoublyLinkedNode[T] =
|
|||
new(result)
|
||||
result.value = value
|
||||
|
||||
proc newSinglyLinkedNode*[T](value: T): SinglyLinkedNode[T] =
|
||||
proc newSinglyLinkedNode*[T](value: T): <//>(SinglyLinkedNode[T]) =
|
||||
## Creates a new singly linked node with the given `value`.
|
||||
runnableExamples:
|
||||
var n = newSinglyLinkedNode[int](5)
|
||||
|
|
|
|||
|
|
@ -949,6 +949,14 @@ macro mapLiterals*(constructor, op: untyped;
|
|||
## works for nested tuples of arrays of sets etc.
|
||||
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:
|
||||
import strutils
|
||||
from algorithm import sorted
|
||||
|
|
@ -1382,5 +1390,13 @@ when isMainModule:
|
|||
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):
|
||||
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]>`_
|
||||
##
|
||||
## .. 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}
|
||||
##
|
||||
## let
|
||||
|
|
@ -69,148 +69,32 @@ type
|
|||
data: KeyValuePairSeq[A]
|
||||
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
|
||||
|
||||
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("}")
|
||||
include setimpl
|
||||
|
||||
proc rightSize*(count: Natural): int {.inline.}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------
|
||||
# ------------------------------ HashSet ------------------------------
|
||||
# ---------------------------------------------------------------------
|
||||
|
||||
|
||||
proc init*[A](s: var HashSet[A], initialSize=64) =
|
||||
proc init*[A](s: var HashSet[A], initialSize = defaultInitialSize) =
|
||||
## Initializes a hash set.
|
||||
##
|
||||
## 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
|
||||
## <#rightSize,Natural>`_ from this module.
|
||||
##
|
||||
## All set variables must be initialized before
|
||||
## use with other procs from this module, with the exception of `isValid proc
|
||||
## <#isValid,HashSet[A]>`_ and `len() <#len,HashSet[A]>`_.
|
||||
## Starting from Nim v0.20, sets are initialized by default and it is
|
||||
## not necessary to call this function explicitly.
|
||||
##
|
||||
## 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
|
||||
|
|
@ -235,42 +118,26 @@ proc init*[A](s: var HashSet[A], initialSize=64) =
|
|||
init(a)
|
||||
assert a.isValid
|
||||
|
||||
assert isPowerOfTwo(initialSize)
|
||||
s.counter = 0
|
||||
newSeq(s.data, initialSize)
|
||||
initImpl(s, 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
|
||||
## hash sets.
|
||||
##
|
||||
## Returns an empty hash set you can assign directly in ``var`` blocks in a
|
||||
## single line.
|
||||
##
|
||||
## Starting from Nim v0.20, sets are initialized by default and it is
|
||||
## not necessary to call this function explicitly.
|
||||
##
|
||||
## See also:
|
||||
## * `toHashSet proc <#toHashSet,openArray[A]>`_
|
||||
runnableExamples:
|
||||
var a = initHashSet[int]()
|
||||
assert a.isValid
|
||||
a.incl(3)
|
||||
assert len(a) == 1
|
||||
result.init(initialSize)
|
||||
|
||||
proc isValid*[A](s: HashSet[A]): bool =
|
||||
## 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
|
||||
result.init(initialSize)
|
||||
|
||||
proc `[]`*[A](s: var HashSet[A], key: A): var A =
|
||||
## 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
|
||||
## reference semantics for sharing.
|
||||
assert s.isValid, "The set needs to be initialized."
|
||||
var hc: Hash
|
||||
var index = rawGet(s, key, hc)
|
||||
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 2 in values
|
||||
|
||||
assert s.isValid, "The set needs to be initialized."
|
||||
var hc: Hash
|
||||
var index = rawGet(s, key, hc)
|
||||
result = index >= 0
|
||||
|
|
@ -325,7 +190,6 @@ proc incl*[A](s: var HashSet[A], key: A) =
|
|||
values.incl(2)
|
||||
assert values.len == 1
|
||||
|
||||
assert s.isValid, "The set needs to be initialized."
|
||||
inclImpl()
|
||||
|
||||
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)
|
||||
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)
|
||||
|
||||
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))
|
||||
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 =
|
||||
## Iterates over elements of the set `s`.
|
||||
##
|
||||
|
|
@ -395,8 +249,7 @@ iterator items*[A](s: HashSet[A]): A =
|
|||
## assert a.len == 2
|
||||
## echo b
|
||||
## # --> {(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
|
||||
|
||||
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(3) == false
|
||||
|
||||
assert s.isValid, "The set needs to be initialized."
|
||||
containsOrInclImpl()
|
||||
|
||||
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)
|
||||
assert s.len == 3
|
||||
|
||||
discard exclImpl(s, key)
|
||||
|
||||
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
|
||||
## 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)
|
||||
|
||||
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(6) == false
|
||||
assert s.missingOrExcl(6) == true
|
||||
|
||||
exclImpl(s, key)
|
||||
|
||||
proc pop*[A](s: var HashSet[A]): A =
|
||||
|
|
@ -489,7 +341,7 @@ proc pop*[A](s: var HashSet[A]): A =
|
|||
assert s.pop == 2
|
||||
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):
|
||||
result = s.data[h].key
|
||||
excl(s, result)
|
||||
|
|
@ -510,7 +362,7 @@ proc clear*[A](s: var HashSet[A]) =
|
|||
assert len(s) == 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].key = default(type(s.data[i].key))
|
||||
|
||||
|
|
@ -525,6 +377,7 @@ proc len*[A](s: HashSet[A]): int =
|
|||
assert len(a) == 0
|
||||
let s = toHashSet([3, 5, 7])
|
||||
assert len(s) == 3
|
||||
|
||||
result = s.counter
|
||||
|
||||
proc card*[A](s: HashSet[A]): int =
|
||||
|
|
@ -554,8 +407,6 @@ proc union*[A](s1, s2: HashSet[A]): HashSet[A] =
|
|||
c = union(a, b)
|
||||
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
|
||||
incl(result, s2)
|
||||
|
||||
|
|
@ -579,9 +430,7 @@ proc intersection*[A](s1, s2: HashSet[A]): HashSet[A] =
|
|||
c = intersection(a, b)
|
||||
assert c == toHashSet(["b"])
|
||||
|
||||
assert s1.isValid, "The set `s1` needs to be initialized."
|
||||
assert s2.isValid, "The set `s2` needs to be initialized."
|
||||
result = initHashSet[A](min(s1.data.len, s2.data.len))
|
||||
result = initHashSet[A](max(min(s1.data.len, s2.data.len), 2))
|
||||
for item in s1:
|
||||
if item in s2: incl(result, item)
|
||||
|
||||
|
|
@ -604,8 +453,6 @@ proc difference*[A](s1, s2: HashSet[A]): HashSet[A] =
|
|||
c = difference(a, b)
|
||||
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]()
|
||||
for item in s1:
|
||||
if not contains(s2, item):
|
||||
|
|
@ -631,8 +478,6 @@ proc symmetricDifference*[A](s1, s2: HashSet[A]): HashSet[A] =
|
|||
c = symmetricDifference(a, b)
|
||||
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
|
||||
for item in s2:
|
||||
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 - 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:
|
||||
if item in s2: return false
|
||||
return true
|
||||
|
|
@ -681,6 +524,7 @@ proc `<`*[A](s, t: HashSet[A]): bool =
|
|||
c = intersection(a, b)
|
||||
assert c < a and c < b
|
||||
assert(not (a < a))
|
||||
|
||||
s.counter != t.counter and s <= t
|
||||
|
||||
proc `<=`*[A](s, t: HashSet[A]): bool =
|
||||
|
|
@ -711,6 +555,7 @@ proc `==`*[A](s, t: HashSet[A]): bool =
|
|||
a = toHashSet([1, 2])
|
||||
b = toHashSet([2, 1])
|
||||
assert a == b
|
||||
|
||||
s.counter == t.counter and s <= t
|
||||
|
||||
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 =
|
||||
## 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
|
||||
|
||||
|
|
@ -747,7 +591,6 @@ proc `$`*[A](s: HashSet[A]): string =
|
|||
## # --> {2, 4, 5}
|
||||
## echo toHashSet(["no", "esc'aping", "is \" provided"])
|
||||
## # --> {no, esc'aping, is " provided}
|
||||
assert s.isValid, "The set needs to be initialized."
|
||||
dollarImpl()
|
||||
|
||||
proc rightSize*(count: Natural): int {.inline.} =
|
||||
|
|
@ -760,8 +603,28 @@ proc rightSize*(count: Natural): int {.inline.} =
|
|||
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 ------------------------------
|
||||
# ---------------------------------------------------------------------
|
||||
|
||||
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.} =
|
||||
var h = s.first
|
||||
var idx = 0
|
||||
while h >= 0:
|
||||
var nxt = s.data[h].next
|
||||
if isFilled(s.data[h].hcode):
|
||||
yieldStmt
|
||||
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
|
||||
if s.data.len > 0:
|
||||
var h = s.first
|
||||
var idx = 0
|
||||
while h >= 0:
|
||||
var nxt = s.data[h].next
|
||||
if isFilled(s.data[h].hcode):
|
||||
yieldStmt
|
||||
inc(idx)
|
||||
h = nxt
|
||||
|
||||
|
||||
|
||||
# -----------------------------------------------------------------------
|
||||
|
||||
|
||||
|
||||
proc init*[A](s: var OrderedSet[A], initialSize=64) =
|
||||
proc init*[A](s: var OrderedSet[A], initialSize = defaultInitialSize) =
|
||||
## Initializes an ordered hash set.
|
||||
##
|
||||
## 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
|
||||
## <#rightSize,Natural>`_ from this module.
|
||||
##
|
||||
## All set variables must be initialized before
|
||||
## use with other procs from this module, with the exception of `isValid proc
|
||||
## <#isValid,HashSet[A]>`_ and `len() <#len,HashSet[A]>`_.
|
||||
## Starting from Nim v0.20, sets are initialized by default and it is
|
||||
## not necessary to call this function explicitly.
|
||||
##
|
||||
## 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
|
||||
|
|
@ -876,26 +668,25 @@ proc init*[A](s: var OrderedSet[A], initialSize=64) =
|
|||
init(a)
|
||||
assert a.isValid
|
||||
|
||||
assert isPowerOfTwo(initialSize)
|
||||
s.counter = 0
|
||||
s.first = -1
|
||||
s.last = -1
|
||||
newSeq(s.data, initialSize)
|
||||
initImpl(s, 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
|
||||
## ordered hash sets.
|
||||
##
|
||||
## Returns an empty ordered hash set you can assign directly in ``var`` blocks
|
||||
## in a single line.
|
||||
##
|
||||
## Starting from Nim v0.20, sets are initialized by default and it is
|
||||
## not necessary to call this function explicitly.
|
||||
##
|
||||
## See also:
|
||||
## * `toOrderedSet proc <#toOrderedSet,openArray[A]>`_
|
||||
runnableExamples:
|
||||
var a = initOrderedSet[int]()
|
||||
assert a.isValid
|
||||
a.incl(3)
|
||||
assert len(a) == 1
|
||||
|
||||
result.init(initialSize)
|
||||
|
||||
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))
|
||||
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 =
|
||||
## 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 2 in values
|
||||
|
||||
assert s.isValid, "The set needs to be initialized."
|
||||
var hc: Hash
|
||||
var index = rawGet(s, key, hc)
|
||||
result = index >= 0
|
||||
|
|
@ -972,7 +745,6 @@ proc incl*[A](s: var OrderedSet[A], key: A) =
|
|||
values.incl(2)
|
||||
assert values.len == 1
|
||||
|
||||
assert s.isValid, "The set needs to be initialized."
|
||||
inclImpl()
|
||||
|
||||
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])
|
||||
values.incl(others)
|
||||
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)
|
||||
|
||||
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(3) == false
|
||||
|
||||
assert s.isValid, "The set needs to be initialized."
|
||||
containsOrInclImpl()
|
||||
|
||||
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)
|
||||
assert s.len == 3
|
||||
|
||||
discard exclImpl(s, key)
|
||||
|
||||
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(6) == false
|
||||
assert s.missingOrExcl(6) == true
|
||||
|
||||
exclImpl(s, key)
|
||||
|
||||
proc clear*[A](s: var OrderedSet[A]) =
|
||||
|
|
@ -1059,7 +831,7 @@ proc clear*[A](s: var OrderedSet[A]) =
|
|||
s.counter = 0
|
||||
s.first = -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].next = 0
|
||||
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
|
||||
let s = toHashSet([3, 5, 7])
|
||||
assert len(s) == 3
|
||||
|
||||
result = s.counter
|
||||
|
||||
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 =
|
||||
## Hashing of OrderedSet.
|
||||
assert s.isValid, "The set needs to be initialized."
|
||||
forAllOrderedPairs:
|
||||
result = result !& s.data[h].hcode
|
||||
result = !$result
|
||||
|
|
@ -1129,7 +901,6 @@ proc `$`*[A](s: OrderedSet[A]): string =
|
|||
## # --> {2, 4, 5}
|
||||
## echo toOrderedSet(["no", "esc'aping", "is \" provided"])
|
||||
## # --> {no, esc'aping, is " provided}
|
||||
assert s.isValid, "The set needs to be initialized."
|
||||
dollarImpl()
|
||||
|
||||
|
||||
|
|
@ -1152,11 +923,9 @@ iterator items*[A](s: OrderedSet[A]): A =
|
|||
## # --> Got 5
|
||||
## # --> Got 8
|
||||
## # --> Got 4
|
||||
assert s.isValid, "The set needs to be initialized."
|
||||
forAllOrderedPairs:
|
||||
yield s.data[h].key
|
||||
|
||||
|
||||
iterator pairs*[A](s: OrderedSet[A]): tuple[a: int, b: A] =
|
||||
## Iterates through (position, value) tuples of OrderedSet `s`.
|
||||
runnableExamples:
|
||||
|
|
@ -1166,12 +935,27 @@ iterator pairs*[A](s: OrderedSet[A]): tuple[a: int, b: A] =
|
|||
p.add(x)
|
||||
assert p == @[(0, 'a'), (1, 'b'), (2, 'r'), (3, 'c'), (4, 'd')]
|
||||
|
||||
assert s.isValid, "The set needs to be initialized"
|
||||
forAllOrderedPairs:
|
||||
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):
|
||||
proc testModule() =
|
||||
## Internal micro test to validate docstrings and such.
|
||||
block isValidTest:
|
||||
block isValidTest: # isValid is deprecated
|
||||
var options: HashSet[string]
|
||||
proc savePreferences(options: HashSet[string]) =
|
||||
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)
|
||||
assert b == toHashSet(["1", "2", "3"])
|
||||
|
||||
block isValidTest:
|
||||
block isValidTest: # isValid is deprecated
|
||||
var cards: OrderedSet[string]
|
||||
proc saveTarotCards(cards: OrderedSet[string]) =
|
||||
assert cards.isValid, "Pass an initialized set!"
|
||||
|
|
@ -1393,6 +1177,69 @@ when isMainModule and not defined(release):
|
|||
bb.incl(y)
|
||||
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):
|
||||
echo "Micro tests run successfully."
|
||||
|
||||
|
|
|
|||
|
|
@ -29,6 +29,14 @@ template maxHash(t): untyped = t.dataLen-1
|
|||
|
||||
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]) =
|
||||
let oldSize = t.dataLen
|
||||
let size = oldSize * growthFactor
|
||||
|
|
@ -176,7 +184,7 @@ proc withKey*[A, B](t: var SharedTable[A, B], key: A,
|
|||
var val: B
|
||||
mapper(key, val, pairExists)
|
||||
if pairExists:
|
||||
maybeRehashPutImpl(enlarge)
|
||||
st_maybeRehashPutImpl(enlarge)
|
||||
|
||||
proc `[]=`*[A, B](t: var SharedTable[A, B], key: A, val: B) =
|
||||
## puts a (key, value)-pair into `t`.
|
||||
|
|
|
|||
|
|
@ -9,49 +9,7 @@
|
|||
|
||||
# An ``include`` file for the different table implementations.
|
||||
|
||||
# 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
|
||||
|
||||
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()
|
||||
include hashcommon
|
||||
|
||||
template rawGetDeepImpl() {.dirty.} = # Search algo for unconditional add
|
||||
genHashImpl(key, hc)
|
||||
|
|
@ -65,20 +23,16 @@ template rawInsertImpl() {.dirty.} =
|
|||
data[h].val = val
|
||||
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.} =
|
||||
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],
|
||||
key: A, val: B, hc: Hash, h: Hash) =
|
||||
rawInsertImpl()
|
||||
|
||||
template addImpl(enlarge) {.dirty.} =
|
||||
if t.dataLen == 0:
|
||||
initImpl(t, defaultInitialSize)
|
||||
if mustRehash(t.dataLen, t.counter): enlarge(t)
|
||||
var hc: Hash
|
||||
var j = rawGetDeep(t, key, hc)
|
||||
|
|
@ -86,6 +40,8 @@ template addImpl(enlarge) {.dirty.} =
|
|||
inc(t.counter)
|
||||
|
||||
template maybeRehashPutImpl(enlarge) {.dirty.} =
|
||||
if t.dataLen == 0:
|
||||
initImpl(t, defaultInitialSize)
|
||||
if mustRehash(t.dataLen, t.counter):
|
||||
enlarge(t)
|
||||
index = rawGetKnownHC(t, key, hc)
|
||||
|
|
@ -94,12 +50,16 @@ template maybeRehashPutImpl(enlarge) {.dirty.} =
|
|||
inc(t.counter)
|
||||
|
||||
template putImpl(enlarge) {.dirty.} =
|
||||
if t.dataLen == 0:
|
||||
initImpl(t, defaultInitialSize)
|
||||
var hc: Hash
|
||||
var index = rawGet(t, key, hc)
|
||||
if index >= 0: t.data[index].val = val
|
||||
else: maybeRehashPutImpl(enlarge)
|
||||
|
||||
template mgetOrPutImpl(enlarge) {.dirty.} =
|
||||
if t.dataLen == 0:
|
||||
initImpl(t, defaultInitialSize)
|
||||
var hc: Hash
|
||||
var index = rawGet(t, key, hc)
|
||||
if index < 0:
|
||||
|
|
@ -109,6 +69,8 @@ template mgetOrPutImpl(enlarge) {.dirty.} =
|
|||
result = t.data[index].val
|
||||
|
||||
template hasKeyOrPutImpl(enlarge) {.dirty.} =
|
||||
if t.dataLen == 0:
|
||||
initImpl(t, defaultInitialSize)
|
||||
var hc: Hash
|
||||
var index = rawGet(t, key, hc)
|
||||
if index < 0:
|
||||
|
|
@ -116,11 +78,6 @@ template hasKeyOrPutImpl(enlarge) {.dirty.} =
|
|||
maybeRehashPutImpl(enlarge)
|
||||
else: result = true
|
||||
|
||||
when not defined(nimHasDefault):
|
||||
template default[T](t: typedesc[T]): T =
|
||||
var v: T
|
||||
v
|
||||
|
||||
template delImplIdx(t, i) =
|
||||
let msk = maxHash(t)
|
||||
if i >= 0:
|
||||
|
|
@ -150,9 +107,53 @@ template delImpl() {.dirty.} =
|
|||
delImplIdx(t, i)
|
||||
|
||||
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
|
||||
t.data[i].hcode = 0
|
||||
t.data[i].key = default(type(t.data[i].key))
|
||||
t.data[i].val = default(type(t.data[i].val))
|
||||
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**
|
||||
## 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>`_,
|
||||
## `OrderedTableRef<#OrderedTableRef>`_, and `CountTableRef<#CountTableRef>`_.
|
||||
##
|
||||
|
|
@ -237,9 +237,13 @@ type
|
|||
## For creating a new empty TableRef, use `newTable proc
|
||||
## <#newTable,int>`_.
|
||||
|
||||
const
|
||||
defaultInitialSize* = 64
|
||||
|
||||
# ------------------------------ 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 dataLen(t): untyped = len(t.data)
|
||||
|
||||
|
|
@ -260,30 +264,6 @@ template get(t, key): untyped =
|
|||
else:
|
||||
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]) =
|
||||
var n: KeyValuePairSeq[A, B]
|
||||
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)
|
||||
while isFilled(t.data[j].hcode):
|
||||
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 ------------------------------
|
||||
# -------------------------------------------------------------------
|
||||
|
||||
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.
|
||||
##
|
||||
## ``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>`_
|
||||
## 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:
|
||||
## * `toTable proc<#toTable,openArray[]>`_
|
||||
## * `newTable proc<#newTable,int>`_ for creating a `TableRef`
|
||||
|
|
@ -327,9 +302,7 @@ proc initTable*[A, B](initialSize=64): Table[A, B] =
|
|||
let
|
||||
a = initTable[int, string]()
|
||||
b = initTable[char, seq[int]]()
|
||||
assert isPowerOfTwo(initialSize)
|
||||
result.counter = 0
|
||||
newSeq(result.data, initialSize)
|
||||
initImpl(result, initialSize)
|
||||
|
||||
proc toTable*[A, B](pairs: openArray[(A, B)]): Table[A, B] =
|
||||
## 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 b = toTable(a)
|
||||
assert b == {'a': 5, 'b': 9}.toTable
|
||||
|
||||
result = initTable[A, B](rightSize(pairs.len))
|
||||
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['y'] = 33
|
||||
doAssert a == {'x': 7, 'y': 33}.toTable
|
||||
|
||||
putImpl(enlarge)
|
||||
|
||||
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
|
||||
doAssert a.hasKey('a') == true
|
||||
doAssert a.hasKey('z') == false
|
||||
|
||||
var hc: Hash
|
||||
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
|
||||
doAssert 'b' in a == true
|
||||
doAssert a.contains('z') == false
|
||||
|
||||
return hasKey[A, B](t, key)
|
||||
|
||||
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):
|
||||
a['z'] = 99
|
||||
doAssert a == {'a': 99, 'b': 9, 'z': 50}.toTable
|
||||
|
||||
hasKeyOrPutImpl(enlarge)
|
||||
|
||||
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
|
||||
doAssert a.getOrDefault('a') == 5
|
||||
doAssert a.getOrDefault('z') == 0
|
||||
|
||||
getOrDefaultImpl(t, key)
|
||||
|
||||
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
|
||||
doAssert a.getOrDefault('a', 99) == 5
|
||||
doAssert a.getOrDefault('z', 99) == 99
|
||||
|
||||
getOrDefaultImpl(t, key, default)
|
||||
|
||||
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('z', 99) == 99
|
||||
doAssert a == {'a': 5, 'b': 9, 'z': 99}.toTable
|
||||
|
||||
mgetOrPutImpl(enlarge)
|
||||
|
||||
proc len*[A, B](t: Table[A, B]): int =
|
||||
|
|
@ -504,6 +485,7 @@ proc len*[A, B](t: Table[A, B]): int =
|
|||
runnableExamples:
|
||||
let a = {'a': 5, 'b': 9}.toTable
|
||||
doAssert len(a) == 2
|
||||
|
||||
result = t.counter
|
||||
|
||||
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
|
||||
a.del('z')
|
||||
doAssert a == {'b': 9, 'c': 13}.toTable
|
||||
|
||||
delImpl()
|
||||
|
||||
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
|
||||
clear(a)
|
||||
doAssert len(a) == 0
|
||||
|
||||
clearImpl()
|
||||
|
||||
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
|
||||
b = {'b': 9, 'c': 13, 'a': 5}.toTable
|
||||
doAssert a == b
|
||||
|
||||
equalsImpl(s, t)
|
||||
|
||||
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:
|
||||
v.add(v[0] + 10)
|
||||
doAssert a == {'e': @[2, 4, 6, 8, 12], 'o': @[1, 5, 7, 9, 11]}.toTable
|
||||
|
||||
for h in 0..high(t.data):
|
||||
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:
|
||||
a[k].add(99)
|
||||
doAssert a == {'e': @[2, 4, 6, 8, 99], 'o': @[1, 5, 7, 9, 99]}.toTable
|
||||
|
||||
for h in 0..high(t.data):
|
||||
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
|
||||
for v in a.values:
|
||||
doAssert v.len == 4
|
||||
|
||||
for h in 0..high(t.data):
|
||||
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:
|
||||
v.add(99)
|
||||
doAssert a == {'e': @[2, 4, 6, 8, 99], 'o': @[1, 5, 7, 9, 99]}.toTable
|
||||
|
||||
for h in 0..high(t.data):
|
||||
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.
|
||||
##
|
||||
## ``initialSize`` must be a power of two (default: 64).
|
||||
|
|
@ -796,10 +785,11 @@ proc newTable*[A, B](initialSize=64): TableRef[A, B] =
|
|||
let
|
||||
a = newTable[int, string]()
|
||||
b = newTable[char, seq[int]]()
|
||||
|
||||
new(result)
|
||||
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``.
|
||||
##
|
||||
## ``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 b = newTable(a)
|
||||
assert b == {'a': 5, 'b': 9}.newTable
|
||||
|
||||
new(result)
|
||||
result[] = toTable[A, B](pairs)
|
||||
|
||||
proc newTableFrom*[A, B, C](collection: A, index: proc(x: B): C): TableRef[C, B] =
|
||||
proc newTableFrom*[A, B, C](collection: A, index: proc(x: B): C): <//>TableRef[C, B] =
|
||||
## Index the collection with the proc provided.
|
||||
# TODO: As soon as supported, change collection: A to collection: A[B]
|
||||
result = newTable[C, B]()
|
||||
|
|
@ -842,6 +833,7 @@ proc `[]`*[A, B](t: TableRef[A, B], key: A): var B =
|
|||
doAssert a['a'] == 5
|
||||
doAssertRaises(KeyError):
|
||||
echo a['z']
|
||||
|
||||
result = t[][key]
|
||||
|
||||
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['y'] = 33
|
||||
doAssert a == {'x': 7, 'y': 33}.newTable
|
||||
|
||||
t[][key] = val
|
||||
|
||||
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
|
||||
doAssert a.hasKey('a') == true
|
||||
doAssert a.hasKey('z') == false
|
||||
|
||||
result = t[].hasKey(key)
|
||||
|
||||
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
|
||||
doAssert 'b' in a == true
|
||||
doAssert a.contains('z') == false
|
||||
|
||||
return hasKey[A, B](t, key)
|
||||
|
||||
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):
|
||||
a['z'] = 99
|
||||
doAssert a == {'a': 99, 'b': 9, 'z': 50}.newTable
|
||||
|
||||
t[].hasKeyOrPut(key, val)
|
||||
|
||||
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
|
||||
doAssert a.getOrDefault('a') == 5
|
||||
doAssert a.getOrDefault('z') == 0
|
||||
|
||||
getOrDefault(t[], key)
|
||||
|
||||
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
|
||||
doAssert a.getOrDefault('a', 99) == 5
|
||||
doAssert a.getOrDefault('z', 99) == 99
|
||||
|
||||
getOrDefault(t[], key, default)
|
||||
|
||||
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('z', 99) == 99
|
||||
doAssert a == {'a': 5, 'b': 9, 'z': 99}.newTable
|
||||
|
||||
t[].mgetOrPut(key, val)
|
||||
|
||||
proc len*[A, B](t: TableRef[A, B]): int =
|
||||
|
|
@ -963,6 +962,7 @@ proc len*[A, B](t: TableRef[A, B]): int =
|
|||
runnableExamples:
|
||||
let a = {'a': 5, 'b': 9}.newTable
|
||||
doAssert len(a) == 2
|
||||
|
||||
result = t.counter
|
||||
|
||||
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
|
||||
a.del('z')
|
||||
doAssert a == {'b': 9, 'c': 13}.newTable
|
||||
|
||||
t[].del(key)
|
||||
|
||||
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 == {'a': 5, 'c': 13}.newTable
|
||||
doAssert i == 0
|
||||
|
||||
result = t[].take(key, val)
|
||||
|
||||
proc clear*[A, B](t: TableRef[A, B]) =
|
||||
|
|
@ -1025,6 +1027,7 @@ proc clear*[A, B](t: TableRef[A, B]) =
|
|||
doAssert len(a) == 3
|
||||
clear(a)
|
||||
doAssert len(a) == 0
|
||||
|
||||
clearImpl()
|
||||
|
||||
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
|
||||
b = {'b': 9, 'c': 13, 'a': 5}.newTable
|
||||
doAssert a == b
|
||||
|
||||
if isNil(s): result = isNil(t)
|
||||
elif isNil(t): result = false
|
||||
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:
|
||||
v.add(v[0] + 10)
|
||||
doAssert a == {'e': @[2, 4, 6, 8, 12], 'o': @[1, 5, 7, 9, 11]}.newTable
|
||||
|
||||
for h in 0..high(t.data):
|
||||
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:
|
||||
a[k].add(99)
|
||||
doAssert a == {'e': @[2, 4, 6, 8, 99], 'o': @[1, 5, 7, 9, 99]}.newTable
|
||||
|
||||
for h in 0..high(t.data):
|
||||
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
|
||||
for v in a.values:
|
||||
doAssert v.len == 4
|
||||
|
||||
for h in 0..high(t.data):
|
||||
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:
|
||||
v.add(99)
|
||||
doAssert a == {'e': @[2, 4, 6, 8, 99], 'o': @[1, 5, 7, 9, 99]}.newTable
|
||||
|
||||
for h in 0..high(t.data):
|
||||
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.
|
||||
##
|
||||
## ``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>`_
|
||||
## 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:
|
||||
## * `toOrderedTable proc<#toOrderedTable,openArray[]>`_
|
||||
## * `newOrderedTable proc<#newOrderedTable,int>`_ for creating an
|
||||
|
|
@ -1235,11 +1246,9 @@ proc initOrderedTable*[A, B](initialSize=64): OrderedTable[A, B] =
|
|||
let
|
||||
a = initOrderedTable[int, string]()
|
||||
b = initOrderedTable[char, seq[int]]()
|
||||
assert isPowerOfTwo(initialSize)
|
||||
result.counter = 0
|
||||
initImpl(result, initialSize)
|
||||
result.first = -1
|
||||
result.last = -1
|
||||
newSeq(result.data, initialSize)
|
||||
|
||||
proc toOrderedTable*[A, B](pairs: openArray[(A, B)]): OrderedTable[A, B] =
|
||||
## 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 b = toOrderedTable(a)
|
||||
assert b == {'a': 5, 'b': 9}.toOrderedTable
|
||||
|
||||
result = initOrderedTable[A, B](rightSize(pairs.len))
|
||||
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
|
||||
doAssertRaises(KeyError):
|
||||
echo a['z']
|
||||
|
||||
get(t, key)
|
||||
|
||||
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['y'] = 33
|
||||
doAssert a == {'x': 7, 'y': 33}.toOrderedTable
|
||||
|
||||
putImpl(enlarge)
|
||||
|
||||
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
|
||||
doAssert a.hasKey('a') == true
|
||||
doAssert a.hasKey('z') == false
|
||||
|
||||
var hc: Hash
|
||||
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
|
||||
doAssert 'b' in a == true
|
||||
doAssert a.contains('z') == false
|
||||
|
||||
return hasKey[A, B](t, key)
|
||||
|
||||
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):
|
||||
a['z'] = 99
|
||||
doAssert a == {'a': 99, 'b': 9, 'z': 50}.toOrderedTable
|
||||
|
||||
hasKeyOrPutImpl(enlarge)
|
||||
|
||||
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
|
||||
doAssert a.getOrDefault('a') == 5
|
||||
doAssert a.getOrDefault('z') == 0
|
||||
|
||||
getOrDefaultImpl(t, key)
|
||||
|
||||
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
|
||||
doAssert a.getOrDefault('a', 99) == 5
|
||||
doAssert a.getOrDefault('z', 99) == 99
|
||||
|
||||
getOrDefaultImpl(t, key, default)
|
||||
|
||||
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('z', 99) == 99
|
||||
doAssert a == {'a': 5, 'b': 9, 'z': 99}.toOrderedTable
|
||||
|
||||
mgetOrPutImpl(enlarge)
|
||||
|
||||
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:
|
||||
let a = {'a': 5, 'b': 9}.toOrderedTable
|
||||
doAssert len(a) == 2
|
||||
|
||||
result = t.counter
|
||||
|
||||
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
|
||||
a.del('z')
|
||||
doAssert a == {'b': 9, 'c': 13}.toOrderedTable
|
||||
|
||||
var n: OrderedKeyValuePairSeq[A, B]
|
||||
newSeq(n, len(t.data))
|
||||
var h = t.first
|
||||
|
|
@ -1467,6 +1487,7 @@ proc clear*[A, B](t: var OrderedTable[A, B]) =
|
|||
doAssert len(a) == 3
|
||||
clear(a)
|
||||
doAssert len(a) == 0
|
||||
|
||||
clearImpl()
|
||||
t.first = -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:
|
||||
v.add(v[0] + 10)
|
||||
doAssert a == {'o': @[1, 5, 7, 9, 11], 'e': @[2, 4, 6, 8, 12]}.toOrderedTable
|
||||
|
||||
forAllOrderedPairs:
|
||||
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:
|
||||
a[k].add(99)
|
||||
doAssert a == {'o': @[1, 5, 7, 9, 99], 'e': @[2, 4, 6, 8, 99]}.toOrderedTable
|
||||
|
||||
forAllOrderedPairs:
|
||||
yield t.data[h].key
|
||||
|
||||
|
|
@ -1636,6 +1659,7 @@ iterator values*[A, B](t: OrderedTable[A, B]): B =
|
|||
}.toOrderedTable
|
||||
for v in a.values:
|
||||
doAssert v.len == 4
|
||||
|
||||
forAllOrderedPairs:
|
||||
yield t.data[h].val
|
||||
|
||||
|
|
@ -1666,7 +1690,7 @@ iterator mvalues*[A, B](t: var OrderedTable[A, B]): var B =
|
|||
# --------------------------- 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.
|
||||
##
|
||||
## ``initialSize`` must be a power of two (default: 64).
|
||||
|
|
@ -1687,7 +1711,7 @@ proc newOrderedTable*[A, B](initialSize=64): OrderedTableRef[A, B] =
|
|||
new(result)
|
||||
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``.
|
||||
##
|
||||
## ``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 b = newOrderedTable(a)
|
||||
assert b == {'a': 5, 'b': 9}.newOrderedTable
|
||||
|
||||
result = newOrderedTable[A, B](rightSize(pairs.len))
|
||||
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['y'] = 33
|
||||
doAssert a == {'x': 7, 'y': 33}.newOrderedTable
|
||||
|
||||
t[][key] = val
|
||||
|
||||
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
|
||||
doAssert a.hasKey('a') == true
|
||||
doAssert a.hasKey('z') == false
|
||||
|
||||
result = t[].hasKey(key)
|
||||
|
||||
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
|
||||
doAssert 'b' in a == true
|
||||
doAssert a.contains('z') == false
|
||||
|
||||
return hasKey[A, B](t, key)
|
||||
|
||||
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):
|
||||
a['z'] = 99
|
||||
doAssert a == {'a': 99, 'b': 9, 'z': 50}.newOrderedTable
|
||||
|
||||
result = t[].hasKeyOrPut(key, val)
|
||||
|
||||
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
|
||||
doAssert a.getOrDefault('a') == 5
|
||||
doAssert a.getOrDefault('z') == 0
|
||||
|
||||
getOrDefault(t[], key)
|
||||
|
||||
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
|
||||
doAssert a.getOrDefault('a', 99) == 5
|
||||
doAssert a.getOrDefault('z', 99) == 99
|
||||
|
||||
getOrDefault(t[], key, default)
|
||||
|
||||
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('z', 99) == 99
|
||||
doAssert a == {'a': 5, 'b': 9, 'z': 99}.newOrderedTable
|
||||
|
||||
result = t[].mgetOrPut(key, val)
|
||||
|
||||
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:
|
||||
let a = {'a': 5, 'b': 9}.newOrderedTable
|
||||
doAssert len(a) == 2
|
||||
|
||||
result = t.counter
|
||||
|
||||
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
|
||||
a.del('z')
|
||||
doAssert a == {'b': 9, 'c': 13}.newOrderedTable
|
||||
|
||||
t[].del(key)
|
||||
|
||||
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
|
||||
clear(a)
|
||||
doAssert len(a) == 0
|
||||
|
||||
clear(t[])
|
||||
|
||||
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
|
||||
a.sort(system.cmp, order=SortOrder.Descending)
|
||||
doAssert a == {'c': 0, 'b': 20, 'a': 10}.newOrderedTable
|
||||
|
||||
t[].sort(cmp, order=order)
|
||||
|
||||
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
|
||||
b = {'b': 9, 'c': 13, 'a': 5}.newOrderedTable
|
||||
doAssert a != b
|
||||
|
||||
if isNil(s): result = isNil(t)
|
||||
elif isNil(t): result = false
|
||||
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:
|
||||
v.add(v[0] + 10)
|
||||
doAssert a == {'o': @[1, 5, 7, 9, 11], 'e': @[2, 4, 6, 8, 12]}.newOrderedTable
|
||||
|
||||
forAllOrderedPairs:
|
||||
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:
|
||||
a[k].add(99)
|
||||
doAssert a == {'o': @[1, 5, 7, 9, 99], 'e': @[2, 4, 6, 8, 99]}.newOrderedTable
|
||||
|
||||
forAllOrderedPairs:
|
||||
yield t.data[h].key
|
||||
|
||||
|
|
@ -1998,6 +2037,7 @@ iterator values*[A, B](t: OrderedTableRef[A, B]): B =
|
|||
}.newOrderedTable
|
||||
for v in a.values:
|
||||
doAssert v.len == 4
|
||||
|
||||
forAllOrderedPairs:
|
||||
yield t.data[h].val
|
||||
|
||||
|
|
@ -2016,6 +2056,7 @@ iterator mvalues*[A, B](t: OrderedTableRef[A, B]): var B =
|
|||
for v in a.mvalues:
|
||||
v.add(99)
|
||||
doAssert a == {'o': @[1, 5, 7, 9, 99], 'e': @[2, 4, 6, 8, 99]}.newOrderedTable
|
||||
|
||||
forAllOrderedPairs:
|
||||
yield t.data[h].val
|
||||
|
||||
|
|
@ -2046,7 +2087,7 @@ type
|
|||
|
||||
# ------------------------------ 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) =
|
||||
var h: Hash = hash(key) and 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]]
|
||||
newSeq(n, len(t.data) * growthFactor)
|
||||
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)
|
||||
|
||||
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
|
||||
while t.data[h].val != 0:
|
||||
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.
|
||||
##
|
||||
## ``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>`_
|
||||
## 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:
|
||||
## * `toCountTable proc<#toCountTable,openArray[A]>`_
|
||||
## * `newCountTable proc<#newCountTable,int>`_ for creating a
|
||||
## `CountTableRef`
|
||||
assert isPowerOfTwo(initialSize)
|
||||
result.counter = 0
|
||||
newSeq(result.data, initialSize)
|
||||
initImpl(result, initialSize)
|
||||
|
||||
proc toCountTable*[A](keys: openArray[A]): CountTable[A] =
|
||||
## 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:
|
||||
t.data[h].val = val
|
||||
else:
|
||||
if mustRehash(len(t.data), t.counter): enlarge(t)
|
||||
rawInsert(t, t.data, key, val)
|
||||
inc(t.counter)
|
||||
insertImpl()
|
||||
|
||||
proc inc*[A](t: var CountTable[A], key: A, val = 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('b', 10)
|
||||
doAssert a == toCountTable("aaabbbbbbbbbbb")
|
||||
|
||||
var index = rawGet(t, key)
|
||||
if index >= 0:
|
||||
inc(t.data[index].val, val)
|
||||
if t.data[index].val == 0: dec(t.counter)
|
||||
else:
|
||||
if mustRehash(len(t.data), t.counter): enlarge(t)
|
||||
rawInsert(t, t.data, key, val)
|
||||
inc(t.counter)
|
||||
insertImpl()
|
||||
|
||||
proc smallest*[A](t: CountTable[A]): tuple[key: A, val: int] =
|
||||
## 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]
|
||||
a.sort(SortOrder.Ascending)
|
||||
doAssert toSeq(a.values) == @[1, 1, 2, 2, 5]
|
||||
|
||||
t.data.sort(cmp=ctCmp, order=order)
|
||||
|
||||
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")
|
||||
a.merge(b)
|
||||
doAssert a == toCountTable("aaabbbccc")
|
||||
|
||||
for key, value in t:
|
||||
s.inc(key, value)
|
||||
|
||||
|
|
@ -2248,6 +2291,7 @@ proc merge*[A](s, t: CountTable[A]): CountTable[A] =
|
|||
a = toCountTable("aaabbc")
|
||||
b = toCountTable("bcc")
|
||||
doAssert merge(a, b) == toCountTable("aaabbbccc")
|
||||
|
||||
result = initCountTable[A](nextPowerOfTwo(max(s.len, t.len)))
|
||||
for table in @[s, t]:
|
||||
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):
|
||||
v = 2
|
||||
doAssert a == toCountTable("aabbccddrr")
|
||||
|
||||
for h in 0..high(t.data):
|
||||
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):
|
||||
a[k] = 2
|
||||
doAssert a == toCountTable("aabbccddrr")
|
||||
|
||||
for h in 0..high(t.data):
|
||||
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")
|
||||
for v in values(a):
|
||||
assert v < 10
|
||||
|
||||
for h in 0..high(t.data):
|
||||
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):
|
||||
v = 2
|
||||
doAssert a == toCountTable("aabbccddrr")
|
||||
|
||||
for h in 0..high(t.data):
|
||||
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 newCountTable*[A](initialSize=64): CountTableRef[A] =
|
||||
proc newCountTable*[A](initialsize = defaultInitialSize): <//>CountTableRef[A] =
|
||||
## Creates a new ref count table that is empty.
|
||||
##
|
||||
## ``initialSize`` must be a power of two (default: 64).
|
||||
|
|
@ -2381,7 +2429,7 @@ proc newCountTable*[A](initialSize=64): CountTableRef[A] =
|
|||
new(result)
|
||||
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``
|
||||
## having a count of how many times it occurs in that container.
|
||||
result = newCountTable[A](rightSize(keys.len))
|
||||
|
|
@ -2493,6 +2541,7 @@ proc merge*[A](s, t: CountTableRef[A]) =
|
|||
b = newCountTable("bcc")
|
||||
a.merge(b)
|
||||
doAssert a == newCountTable("aaabbbccc")
|
||||
|
||||
s[].merge(t[])
|
||||
|
||||
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):
|
||||
v = 2
|
||||
doAssert a == newCountTable("aabbccddrr")
|
||||
|
||||
for h in 0..high(t.data):
|
||||
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):
|
||||
a[k] = 2
|
||||
doAssert a == newCountTable("aabbccddrr")
|
||||
|
||||
for h in 0..high(t.data):
|
||||
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")
|
||||
for v in values(a):
|
||||
assert v < 10
|
||||
|
||||
for h in 0..high(t.data):
|
||||
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):
|
||||
v = 2
|
||||
doAssert a == newCountTable("aabbccddrr")
|
||||
|
||||
for h in 0..high(t.data):
|
||||
if t.data[h].val != 0: yield t.data[h].val
|
||||
|
||||
|
|
@ -2813,3 +2866,99 @@ when isMainModule:
|
|||
doAssert "test1" == orf.getOrDefault("test1", "test1")
|
||||
orf["test2"] = "test2"
|
||||
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``.
|
||||
##
|
||||
## Examples
|
||||
## --------
|
||||
## ========
|
||||
##
|
||||
## Loading a simple C function
|
||||
## ^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
## ---------------------------
|
||||
##
|
||||
## The following example demonstrates loading a function called 'greet'
|
||||
## 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
|
||||
## that generates a string with its HTML representation.
|
||||
##
|
||||
## Example:
|
||||
## Examples
|
||||
## ========
|
||||
##
|
||||
## .. code-block:: Nim
|
||||
## var nim = "Nim"
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
# 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!".}
|
||||
|
||||
from parseutils import skipIgnoreCase
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
# 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!".}
|
||||
|
||||
when not defined(nimscript):
|
||||
|
|
|
|||
|
|
@ -12,48 +12,46 @@
|
|||
## 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
|
||||
## 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
|
||||
## :literal:
|
||||
##
|
||||
|
||||
##[ Here is an example of how to use the configuration file parser:
|
||||
|
||||
.. code-block:: nim
|
||||
|
||||
import
|
||||
os, parsecfg, strutils, streams
|
||||
|
||||
var f = newFileStream(paramStr(1), fmRead)
|
||||
if f != nil:
|
||||
var p: CfgParser
|
||||
open(p, f, paramStr(1))
|
||||
while true:
|
||||
var e = next(p)
|
||||
case e.kind
|
||||
of cfgEof: break
|
||||
of cfgSectionStart: ## a ``[section]`` has been parsed
|
||||
echo("new section: " & e.section)
|
||||
of cfgKeyValuePair:
|
||||
echo("key-value-pair: " & e.key & ": " & e.value)
|
||||
of cfgOption:
|
||||
echo("command: " & e.key & ": " & e.value)
|
||||
of cfgError:
|
||||
echo(e.msg)
|
||||
close(p)
|
||||
else:
|
||||
echo("cannot open: " & paramStr(1))
|
||||
|
||||
]##
|
||||
|
||||
## Here is an example of how to use the configuration file parser:
|
||||
##
|
||||
## .. code-block:: nim
|
||||
##
|
||||
## import os, parsecfg, strutils, streams
|
||||
##
|
||||
## var f = newFileStream(paramStr(1), fmRead)
|
||||
## if f != nil:
|
||||
## var p: CfgParser
|
||||
## open(p, f, paramStr(1))
|
||||
## while true:
|
||||
## var e = next(p)
|
||||
## case e.kind
|
||||
## of cfgEof: break
|
||||
## of cfgSectionStart: ## a ``[section]`` has been parsed
|
||||
## echo("new section: " & e.section)
|
||||
## of cfgKeyValuePair:
|
||||
## echo("key-value-pair: " & e.key & ": " & e.value)
|
||||
## of cfgOption:
|
||||
## echo("command: " & e.key & ": " & e.value)
|
||||
## of cfgError:
|
||||
## echo(e.msg)
|
||||
## close(p)
|
||||
## else:
|
||||
## echo("cannot open: " & paramStr(1))
|
||||
##
|
||||
##
|
||||
## Examples
|
||||
## --------
|
||||
## ========
|
||||
##
|
||||
## This is an example of a configuration file.
|
||||
## Configuration file example
|
||||
## --------------------------
|
||||
##
|
||||
## ::
|
||||
## .. code-block:: nim
|
||||
##
|
||||
## charset = "utf-8"
|
||||
## [Package]
|
||||
|
|
@ -64,8 +62,8 @@
|
|||
## qq = "10214028"
|
||||
## email = "lihaifeng@wxm.com"
|
||||
##
|
||||
## Creating a configuration file.
|
||||
## ==============================
|
||||
## Creating a configuration file
|
||||
## -----------------------------
|
||||
## .. code-block:: nim
|
||||
##
|
||||
## import parsecfg
|
||||
|
|
@ -78,8 +76,8 @@
|
|||
## dict.setSectionKey("Author","email","lihaifeng@wxm.com")
|
||||
## dict.writeConfig("config.ini")
|
||||
##
|
||||
## Reading a configuration file.
|
||||
## =============================
|
||||
## Reading a configuration file
|
||||
## ----------------------------
|
||||
## .. code-block:: nim
|
||||
##
|
||||
## import parsecfg
|
||||
|
|
@ -92,8 +90,8 @@
|
|||
## var email = dict.getSectionValue("Author","email")
|
||||
## echo pname & "\n" & name & "\n" & qq & "\n" & email
|
||||
##
|
||||
## Modifying a configuration file.
|
||||
## ===============================
|
||||
## Modifying a configuration file
|
||||
## ------------------------------
|
||||
## .. code-block:: nim
|
||||
##
|
||||
## import parsecfg
|
||||
|
|
@ -101,8 +99,8 @@
|
|||
## dict.setSectionKey("Author","name","lhf")
|
||||
## dict.writeConfig("config.ini")
|
||||
##
|
||||
## Deleting a section key in a configuration file.
|
||||
## ===============================================
|
||||
## Deleting a section key in a configuration file
|
||||
## ----------------------------------------------
|
||||
## .. code-block:: nim
|
||||
##
|
||||
## import parsecfg
|
||||
|
|
@ -443,17 +441,17 @@ proc next*(c: var CfgParser): CfgEvent {.rtl, extern: "npc$1".} =
|
|||
|
||||
# ---------------- Configuration file related operations ----------------
|
||||
type
|
||||
Config* = OrderedTableRef[string, OrderedTableRef[string, string]]
|
||||
Config* = OrderedTableRef[string, <//>OrderedTableRef[string, string]]
|
||||
|
||||
proc newConfig*(): Config =
|
||||
## Create a new configuration table.
|
||||
## Useful when wanting to create a configuration file.
|
||||
result = newOrderedTable[string, OrderedTableRef[string, string]]()
|
||||
result = newOrderedTable[string, <//>OrderedTableRef[string, string]]()
|
||||
|
||||
proc loadConfig*(stream: Stream, filename: string = "[stream]"): Config =
|
||||
proc loadConfig*(stream: Stream, filename: string = "[stream]"): <//>Config =
|
||||
## Load the specified configuration from stream into a new Config instance.
|
||||
## `filename` parameter is only used for nicer error messages.
|
||||
var dict = newOrderedTable[string, OrderedTableRef[string, string]]()
|
||||
var dict = newOrderedTable[string, <//>OrderedTableRef[string, string]]()
|
||||
var curSection = "" ## Current section,
|
||||
## the default value of the current section is "",
|
||||
## which means that the current section is a common
|
||||
|
|
@ -483,7 +481,7 @@ proc loadConfig*(stream: Stream, filename: string = "[stream]"): Config =
|
|||
close(p)
|
||||
result = dict
|
||||
|
||||
proc loadConfig*(filename: string): Config =
|
||||
proc loadConfig*(filename: string): <//>Config =
|
||||
## Load the specified configuration file into a new Config instance.
|
||||
let file = open(filename, fmRead)
|
||||
let fileStream = newFileStream(file)
|
||||
|
|
|
|||
|
|
@ -64,104 +64,154 @@ const
|
|||
proc toLower(c: char): char {.inline.} =
|
||||
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 {.
|
||||
rtl, extern: "npuParseHex", noSideEffect.} =
|
||||
proc parseBin*[T: SomeInteger](s: string, number: var T, start = 0, maxLen = 0): int
|
||||
{.noSideEffect.} =
|
||||
## Parses a binary number and stores its value in ``number``.
|
||||
##
|
||||
## Returns the number of the parsed characters or 0 in case of an error.
|
||||
## 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``.
|
||||
##
|
||||
## Returns the number of the parsed characters or 0 in case of an error. This
|
||||
## proc is sensitive to the already existing value of ``number`` and will
|
||||
## 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.
|
||||
## 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 length of the hexadecimal number has no upper bound.
|
||||
## Else no more than ``start + maxLen`` characters are parsed, up to the
|
||||
## length of the string.
|
||||
## If ``maxLen == 0``, the parsing continues until the first non-hex character
|
||||
## or to the end of the string. Otherwise, no more than ``maxLen`` characters
|
||||
## are parsed starting from the ``start`` position.
|
||||
##
|
||||
## It does not check for overflow. If the value represented by the string is
|
||||
## too big to fit into ``number``, only the value of last fitting characters
|
||||
## will be stored in ``number`` without producing an error.
|
||||
runnableExamples:
|
||||
var value = 0
|
||||
discard parseHex("0x38", value)
|
||||
assert value == 56
|
||||
discard parseHex("0x34", value)
|
||||
assert value == 56 * 256 + 52
|
||||
value = -1
|
||||
discard parseHex("0x38", value)
|
||||
assert value == -200
|
||||
var num: int
|
||||
doAssert parseHex("4E_69_ED", num) == 8
|
||||
doAssert num == 5138925
|
||||
doAssert parseHex("X", num) == 0
|
||||
doAssert parseHex("#ABC", num) == 4
|
||||
var num8: int8
|
||||
doAssert parseHex("0x_4E_69_ED", num8) == 11
|
||||
doAssert num8 == 0xED'i8
|
||||
doAssert parseHex("0x_4E_69_ED", num8, 3, 2) == 2
|
||||
doAssert num8 == 0x4E'i8
|
||||
var num8u: uint8
|
||||
doAssert parseHex("0x_4E_69_ED", num8u) == 11
|
||||
doAssert num8u == 237
|
||||
var num64: int64
|
||||
doAssert parseHex("4E69ED4E69ED", num64) == 12
|
||||
doAssert num64 == 86216859871725
|
||||
var i = start
|
||||
var output = T(0)
|
||||
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 {'x', 'X'}): inc(i, 2)
|
||||
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)
|
||||
elif i < last and s[i] == '#': inc(i)
|
||||
while i < last:
|
||||
case s[i]
|
||||
of '_': discard
|
||||
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
|
||||
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
|
||||
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
|
||||
else: break
|
||||
inc(i)
|
||||
if foundDigit: result = i-start
|
||||
|
||||
proc parseOct*(s: string, number: var int, start = 0, maxLen = 0): int {.
|
||||
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
|
||||
if foundDigit:
|
||||
number = output
|
||||
result = i - start
|
||||
|
||||
proc parseIdent*(s: string, ident: var string, start = 0): int =
|
||||
## Parses an identifier and stores it in ``ident``. Returns
|
||||
|
|
@ -605,11 +655,6 @@ when isMainModule:
|
|||
var value = 0
|
||||
discard parseHex("0x38", value)
|
||||
doAssert value == 56
|
||||
discard parseHex("0x34", value)
|
||||
doAssert value == 56 * 256 + 52
|
||||
value = -1
|
||||
discard parseHex("0x38", value)
|
||||
doAssert value == -200
|
||||
|
||||
value = -1
|
||||
doAssert(parseSaturatedNatural("848", value) == 3)
|
||||
|
|
|
|||
|
|
@ -129,7 +129,7 @@ proc next*(r: var Rand): uint64 =
|
|||
## a given upper bound
|
||||
## * `rand proc<#rand,Rand,range[]>`_ that returns a float
|
||||
## * `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>`_
|
||||
runnableExamples:
|
||||
var r = initRand(2019)
|
||||
|
|
@ -242,7 +242,7 @@ proc rand*(r: var Rand; max: Natural): int {.benign.} =
|
|||
## random number generator
|
||||
## * `rand proc<#rand,Rand,range[]>`_ that returns a float
|
||||
## * `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:
|
||||
var r = initRand(123)
|
||||
doAssert r.rand(100) == 0
|
||||
|
|
@ -268,7 +268,7 @@ proc rand*(max: int): int {.benign.} =
|
|||
## provided state
|
||||
## * `rand proc<#rand,float>`_ that returns a float
|
||||
## * `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:
|
||||
randomize(123)
|
||||
doAssert rand(100) == 0
|
||||
|
|
@ -285,7 +285,7 @@ proc rand*(r: var Rand; max: range[0.0 .. high(float)]): float {.benign.} =
|
|||
## random number generator
|
||||
## * `rand proc<#rand,Rand,Natural>`_ that returns an integer
|
||||
## * `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:
|
||||
var r = initRand(234)
|
||||
let f = r.rand(1.0)
|
||||
|
|
@ -311,7 +311,7 @@ proc rand*(max: float): float {.benign.} =
|
|||
## provided state
|
||||
## * `rand proc<#rand,int>`_ that returns an integer
|
||||
## * `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:
|
||||
randomize(234)
|
||||
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
|
||||
## * `rand proc<#rand,Rand,Natural>`_ that returns an integer
|
||||
## * `rand proc<#rand,Rand,range[]>`_ that returns a float
|
||||
## * `rand proc<#rand,typedesc[T]>`_ that accepts an integer type
|
||||
## * `rand proc<#rand,typedesc[T]>`_ that accepts an integer or range type
|
||||
runnableExamples:
|
||||
var r = initRand(345)
|
||||
doAssert r.rand(1..6) == 4
|
||||
|
|
@ -349,7 +349,7 @@ proc rand*[T: Ordinal](x: HSlice[T, T]): T =
|
|||
## a provided state
|
||||
## * `rand proc<#rand,int>`_ that returns an integer
|
||||
## * `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:
|
||||
randomize(345)
|
||||
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) == 4052940463'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.} =
|
||||
## **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
|
||||
## sense to call this directly, but it is required to exist
|
||||
## by the ``&`` macro.
|
||||
if specifier.len == 0:
|
||||
result.add $value
|
||||
return
|
||||
let spec = parseStandardFormatSpecifier(specifier)
|
||||
var radix = 10
|
||||
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)
|
||||
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
|
||||
## sense to call this directly, but it is required to exist
|
||||
## by the ``&`` macro.
|
||||
if specifier.len == 0:
|
||||
result.add $value
|
||||
return
|
||||
let spec = parseStandardFormatSpecifier(specifier)
|
||||
|
||||
var fmode = ffDefault
|
||||
|
|
@ -457,7 +463,7 @@ proc formatValue*(result: var string; value: SomeFloat; specifier: string): void
|
|||
if value >= 0.0:
|
||||
if spec.sign != '-':
|
||||
sign = true
|
||||
if value == 0.0:
|
||||
if value == 0.0:
|
||||
if 1.0 / value == Inf:
|
||||
# only insert the sign if value != negZero
|
||||
f.insert($spec.sign, 0)
|
||||
|
|
@ -467,16 +473,16 @@ proc formatValue*(result: var string; value: SomeFloat; specifier: string): void
|
|||
sign = true
|
||||
|
||||
if spec.padWithZero:
|
||||
var sign_str = ""
|
||||
var signStr = ""
|
||||
if sign:
|
||||
sign_str = $f[0]
|
||||
signStr = $f[0]
|
||||
f = f[1..^1]
|
||||
|
||||
let toFill = spec.minimumWidth - f.len - ord(sign)
|
||||
if toFill > 0:
|
||||
f = repeat('0', toFill) & f
|
||||
if sign:
|
||||
f = sign_str & f
|
||||
f = signStr & f
|
||||
|
||||
# the default for numbers is right-alignment:
|
||||
let align = if spec.align == '\0': '>' else: spec.align
|
||||
|
|
@ -712,6 +718,13 @@ when isMainModule:
|
|||
for s in invalidUtf8:
|
||||
check &"{s:>5}", repeat(" ", 5-s.len) & s
|
||||
|
||||
# bug #11089
|
||||
let flfoo: float = 1.0
|
||||
check &"{flfoo}", "1.0"
|
||||
|
||||
# bug #11092
|
||||
check &"{high(int64)}", "9223372036854775807"
|
||||
check &"{low(int64)}", "-9223372036854775808"
|
||||
|
||||
import json
|
||||
|
||||
|
|
|
|||
|
|
@ -17,7 +17,8 @@
|
|||
resolution used by ``getTime()`` depends on the platform and backend
|
||||
(JS is limited to millisecond precision).
|
||||
|
||||
Examples:
|
||||
Examples
|
||||
========
|
||||
|
||||
.. code-block:: nim
|
||||
import times, os
|
||||
|
|
@ -38,7 +39,7 @@
|
|||
echo "One month from now : ", now() + 1.months
|
||||
|
||||
Parsing and Formatting Dates
|
||||
----------------------------
|
||||
============================
|
||||
|
||||
The ``DateTime`` type can be parsed and formatted using the different
|
||||
``parse`` and ``format`` procedures.
|
||||
|
|
@ -128,7 +129,7 @@
|
|||
only for years in the range 1..9999).
|
||||
|
||||
Duration vs TimeInterval
|
||||
----------------------------
|
||||
============================
|
||||
The ``times`` module exports two similiar types that are both used to
|
||||
represent some amount of time: `Duration <#Duration>`_ and
|
||||
`TimeInterval <#TimeInterval>`_.
|
||||
|
|
@ -137,7 +138,7 @@
|
|||
needed).
|
||||
|
||||
Duration
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
----------------------------
|
||||
A ``Duration`` represents a duration of time stored as seconds and
|
||||
nanoseconds. A ``Duration`` is always fully normalized, so
|
||||
``initDuration(hours = 1)`` and ``initDuration(minutes = 60)`` are equivilant.
|
||||
|
|
@ -147,7 +148,7 @@
|
|||
is more performant and easier to understand it should generally prefered.
|
||||
|
||||
TimeInterval
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
----------------------------
|
||||
A ``TimeInterval`` represents some amount of time expressed in calendar
|
||||
units, for example "1 year and 2 days". Since some units cannot be
|
||||
normalized (the length of a year is different for leap years for example),
|
||||
|
|
@ -164,7 +165,7 @@
|
|||
``Duration`` doesn't have.
|
||||
|
||||
How long is a day?
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
----------------------------
|
||||
It should be especially noted that the handling of days differs between
|
||||
``TimeInterval`` and ``Duration``. The ``Duration`` type always treats a day
|
||||
as exactly 86400 seconds. For ``TimeInterval``, it's more complex.
|
||||
|
|
@ -2651,7 +2652,7 @@ proc microseconds*(dur: Duration): int {.inline, deprecated.} =
|
|||
doAssert dur.microseconds == 7008
|
||||
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**
|
||||
## part of the duration.
|
||||
##
|
||||
|
|
|
|||
|
|
@ -57,8 +57,8 @@
|
|||
## # Run suites starting with 'bug #' and standalone tests starting with '#'
|
||||
## nim c -r test 'bug #*::' '::#*'
|
||||
##
|
||||
## Example
|
||||
## -------
|
||||
## Examples
|
||||
## ========
|
||||
##
|
||||
## .. code:: nim
|
||||
##
|
||||
|
|
@ -177,13 +177,13 @@ proc addOutputFormatter*(formatter: OutputFormatter) =
|
|||
formatters.add(formatter)
|
||||
|
||||
proc newConsoleOutputFormatter*(outputLevel: OutputLevel = PRINT_ALL,
|
||||
colorOutput = true): ConsoleOutputFormatter =
|
||||
colorOutput = true): <//>ConsoleOutputFormatter =
|
||||
ConsoleOutputFormatter(
|
||||
outputLevel: outputLevel,
|
||||
colorOutput: colorOutput
|
||||
)
|
||||
|
||||
proc defaultConsoleFormatter*(): ConsoleOutputFormatter =
|
||||
proc defaultConsoleFormatter*(): <//>ConsoleOutputFormatter =
|
||||
when declared(stdout):
|
||||
# Reading settings
|
||||
# On a terminal this branch is executed
|
||||
|
|
@ -263,7 +263,7 @@ proc xmlEscape(s: string): string =
|
|||
else:
|
||||
result.add(c)
|
||||
|
||||
proc newJUnitOutputFormatter*(stream: Stream): JUnitOutputFormatter =
|
||||
proc newJUnitOutputFormatter*(stream: Stream): <//>JUnitOutputFormatter =
|
||||
## Creates a formatter that writes report to the specified stream in
|
||||
## JUnit format.
|
||||
## The ``stream`` is NOT closed automatically when the test are finished,
|
||||
|
|
|
|||
|
|
@ -1619,7 +1619,7 @@ template `isnot`*(x, y: untyped): untyped = not (x is y)
|
|||
## assert @[1, 2] isnot enum
|
||||
|
||||
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.}
|
||||
## 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)
|
||||
new(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:
|
||||
template owned*(t: typeDesc): typedesc = t
|
||||
template unown*(x: typed): typed = x
|
||||
|
||||
proc new*[T](a: var ref T) {.magic: "New", noSideEffect.}
|
||||
## Creates a new object of type ``T`` and returns a safe (traced)
|
||||
|
|
@ -1657,6 +1665,9 @@ else:
|
|||
new(r)
|
||||
return r
|
||||
|
||||
# This is only required to make 0.20 compile with the 0.19 line.
|
||||
template `<//>`*(t: untyped): untyped = t
|
||||
|
||||
template disarm*(x: typed) =
|
||||
## Useful for ``disarming`` dangling pointers explicitly for the
|
||||
## --newruntime. Regardless of whether --newruntime is used or not
|
||||
|
|
@ -1926,13 +1937,11 @@ const
|
|||
## failure.
|
||||
|
||||
when defined(nodejs) and not defined(nimscript):
|
||||
var programResult* {.importc: "process.exitCode".}: int
|
||||
var programResult* {.importc: "process.exitCode", deprecated.}: int
|
||||
programResult = 0
|
||||
else:
|
||||
var programResult* {.compilerproc, exportc: "nim_program_result".}: int
|
||||
## Modify this variable to specify the exit code of the program
|
||||
## under normal circumstances. When the program is terminated
|
||||
## prematurely using `quit proc <#quit,int>`_, this value is ignored.
|
||||
elif hostOS != "standalone":
|
||||
var programResult* {.compilerproc, exportc: "nim_program_result", deprecated.}: int
|
||||
## deprecated, prefer ``quit``
|
||||
|
||||
when defined(nimdoc):
|
||||
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):
|
||||
proc addChar(s: NimString, c: char): NimString {.compilerProc, benign.}
|
||||
|
||||
when 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:
|
||||
when not defined(gcDestructors):
|
||||
proc add*[T](x: var seq[T], y: T) {.magic: "AppendSeqElem", noSideEffect.}
|
||||
## 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.
|
||||
|
||||
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.
|
||||
##
|
||||
## Exactly ``size`` bytes will be overwritten. Like any procedure
|
||||
## dealing with raw memory this is **unsafe**.
|
||||
|
||||
proc copyMem*(dest, source: pointer, size: Natural) {.inline, benign,
|
||||
tags: [], locks: 0.}
|
||||
tags: [], locks: 0, raises: [].}
|
||||
## Copies the contents from the memory at ``source`` to the memory
|
||||
## at ``dest``.
|
||||
## Exactly ``size`` bytes will be copied. The memory
|
||||
|
|
@ -2254,7 +2253,7 @@ when not defined(nimscript) and not defined(JS):
|
|||
## memory this is **unsafe**.
|
||||
|
||||
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
|
||||
## 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
|
||||
## 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
|
||||
## be compared.
|
||||
##
|
||||
|
|
|
|||
|
|
@ -148,4 +148,4 @@ proc rawWrite*(f: CFilePtr, s: cstring) {.compilerproc, nonreloadable, inline.}
|
|||
# we cannot throw an exception here!
|
||||
discard c_fwrite(s, 1, s.len, f)
|
||||
|
||||
{.pop}
|
||||
{.pop.}
|
||||
|
|
|
|||
|
|
@ -10,6 +10,8 @@ when hostOS == "standalone":
|
|||
panic(arg)
|
||||
|
||||
elif defined(nimQuirky) and not defined(nimscript):
|
||||
import ansi_c
|
||||
|
||||
proc name(t: typedesc): string {.magic: "TypeTrait".}
|
||||
|
||||
proc sysFatal(exceptn: typedesc, message, arg: string) {.inline, noReturn.} =
|
||||
|
|
|
|||
|
|
@ -147,58 +147,58 @@ proc nimParseBiggestFloat(s: string, number: var BiggestFloat,
|
|||
has_sign = false
|
||||
|
||||
# Sign?
|
||||
if s[i] == '+' or s[i] == '-':
|
||||
if i < s.len and (s[i] == '+' or s[i] == '-'):
|
||||
has_sign = true
|
||||
if s[i] == '-':
|
||||
sign = -1.0
|
||||
inc(i)
|
||||
|
||||
# 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+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
|
||||
return i+3 - start
|
||||
return 0
|
||||
|
||||
# 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+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
|
||||
return i+3 - start
|
||||
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)
|
||||
# Integer part?
|
||||
while s[i] in {'0'..'9'}:
|
||||
while i < s.len and s[i] in {'0'..'9'}:
|
||||
inc(kdigits)
|
||||
integer = integer * 10'u64 + (s[i].ord - '0'.ord).uint64
|
||||
inc(i)
|
||||
while s[i] == '_': inc(i)
|
||||
while i < s.len and s[i] == '_': inc(i)
|
||||
|
||||
# Fractional part?
|
||||
if s[i] == '.':
|
||||
if i < s.len and s[i] == '.':
|
||||
inc(i)
|
||||
# if no integer part, Skip leading zeros
|
||||
if kdigits <= 0:
|
||||
while s[i] == '0':
|
||||
while i < s.len and s[i] == '0':
|
||||
inc(frac_exponent)
|
||||
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)
|
||||
# get fractional part
|
||||
while s[i] in {'0'..'9'}:
|
||||
while i < s.len and s[i] in {'0'..'9'}:
|
||||
inc(fdigits)
|
||||
inc(frac_exponent)
|
||||
integer = integer * 10'u64 + (s[i].ord - '0'.ord).uint64
|
||||
inc(i)
|
||||
while s[i] == '_': inc(i)
|
||||
while i < s.len and s[i] == '_': inc(i)
|
||||
|
||||
# if has no digits: return error
|
||||
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 '-
|
||||
return 0
|
||||
|
||||
if s[i] in {'e', 'E'}:
|
||||
if i+1 < s.len and s[i] in {'e', 'E'}:
|
||||
inc(i)
|
||||
if s[i] == '+' or s[i] == '-':
|
||||
if s[i] == '-':
|
||||
|
|
@ -215,10 +215,10 @@ proc nimParseBiggestFloat(s: string, number: var BiggestFloat,
|
|||
inc(i)
|
||||
if s[i] notin {'0'..'9'}:
|
||||
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'))
|
||||
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
|
||||
let exp_negative = real_exponent < 0
|
||||
|
|
@ -259,12 +259,12 @@ proc nimParseBiggestFloat(s: string, number: var BiggestFloat,
|
|||
result = i - start
|
||||
i = start
|
||||
# re-parse without error checking, any error should be handled by the code above.
|
||||
if s[i] == '.': i.inc
|
||||
while s[i] in {'0'..'9','+','-'}:
|
||||
if i < s.len and s[i] == '.': i.inc
|
||||
while i < s.len and s[i] in {'0'..'9','+','-'}:
|
||||
if ti < maxlen:
|
||||
t[ti] = s[i]; inc(ti)
|
||||
inc(i)
|
||||
while s[i] in {'.', '_'}: # skip underscore and decimal point
|
||||
while i < s.len and s[i] in {'.', '_'}: # skip underscore and decimal point
|
||||
inc(i)
|
||||
|
||||
# insert exponent
|
||||
|
|
|
|||
|
|
@ -19,7 +19,8 @@
|
|||
## to prevent race conditions and improves efficiency. See `the manual for
|
||||
## details of this memory model <manual.html#threads>`_.
|
||||
##
|
||||
## Example:
|
||||
## Examples
|
||||
## ========
|
||||
##
|
||||
## .. code-block:: Nim
|
||||
##
|
||||
|
|
@ -328,7 +329,7 @@ else:
|
|||
proc createThread*(t: var Thread[void], tp: proc () {.thread, nimcall.}) =
|
||||
createThread[void](t, tp)
|
||||
|
||||
## we need to cache current threadId to not perform syscall all the time
|
||||
# we need to cache current threadId to not perform syscall all the time
|
||||
var threadId {.threadvar.}: int
|
||||
|
||||
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">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">
|
||||
<h1><a class="toc-backref" href="#6">Imports</a></h1>
|
||||
<dl class="item">
|
||||
|
|
|
|||
|
|
@ -6,6 +6,8 @@ runnableExamples:
|
|||
import subdir / subdir_b / utils
|
||||
doAssert bar(3, 4) == 7
|
||||
foo(enumValueA, enumValueB)
|
||||
# bug #11078
|
||||
for x in "xx": discard
|
||||
|
||||
|
||||
template foo*(a, b: SomeType) =
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@
|
|||
## of the compiler.
|
||||
|
||||
import important_packages
|
||||
import sequtils
|
||||
|
||||
const
|
||||
specialCategories = [
|
||||
|
|
@ -514,30 +515,21 @@ proc testNimblePackages(r: var TResults, cat: Category) =
|
|||
let buildPath = packagesDir / name
|
||||
if not existsDir(buildPath):
|
||||
if hasDep:
|
||||
let nimbleProcess = startProcess("nimble", "", ["install", "-y", name],
|
||||
options = {poUsePath, poStdErrToStdOut})
|
||||
let nimbleStatus = waitForExitEx(nimbleProcess)
|
||||
nimbleProcess.close
|
||||
let (nimbleOutput, nimbleStatus) = execCmdEx2("nimble", ["install", "-y", name])
|
||||
if nimbleStatus != QuitSuccess:
|
||||
r.addResult(test, targetC, "", "'nimble install' failed", reInstallFailed)
|
||||
r.addResult(test, targetC, "", "'nimble install' failed\n" & nimbleOutput, reInstallFailed)
|
||||
continue
|
||||
|
||||
let installProcess = startProcess("git", "", ["clone", url, buildPath],
|
||||
options = {poUsePath, poStdErrToStdOut})
|
||||
let installStatus = waitForExitEx(installProcess)
|
||||
installProcess.close
|
||||
let (installOutput, installStatus) = execCmdEx2("git", ["clone", url, buildPath])
|
||||
if installStatus != QuitSuccess:
|
||||
r.addResult(test, targetC, "", "'git clone' failed", reInstallFailed)
|
||||
r.addResult(test, targetC, "", "'git clone' failed\n" & installOutput, reInstallFailed)
|
||||
continue
|
||||
|
||||
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:
|
||||
r.addResult(test, targetC, "", "package test failed", reBuildFailed)
|
||||
r.addResult(test, targetC, "", "package test failed\n" & buildOutput, reBuildFailed)
|
||||
else:
|
||||
inc r.passed
|
||||
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"]
|
||||
proc onStdout(line: string) = echo line
|
||||
var (buf, exitCode) = execCmdEx2(command = compilerPrefix, args = args, options = {poStdErrToStdOut, poUsePath}, input = "",
|
||||
onStdout = if verboseMegatest: onStdout else: nil)
|
||||
var (buf, exitCode) = execCmdEx2(command = compilerPrefix, args = args, input = "")
|
||||
if exitCode != 0:
|
||||
echo buf.string
|
||||
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) =
|
||||
let skips = loadSkipFrom(skipFrom)
|
||||
|
||||
case cat.string.normalize
|
||||
of "rodfiles":
|
||||
when false:
|
||||
|
|
@ -745,6 +750,9 @@ proc processCategory(r: var TResults, cat: Category, options, testsDir: string,
|
|||
|
||||
for i, name in files:
|
||||
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:
|
||||
discard "run the test"
|
||||
else:
|
||||
|
|
|
|||
|
|
@ -19,8 +19,6 @@ include compiler/nodejs
|
|||
var useColors = true
|
||||
var backendLogging = true
|
||||
var simulate = false
|
||||
var verboseMegatest = false # very verbose but can be useful
|
||||
var verboseCommands = false
|
||||
|
||||
const
|
||||
testsDir = "tests" & DirSep
|
||||
|
|
@ -39,8 +37,6 @@ Arguments:
|
|||
arguments are passed to the compiler
|
||||
Options:
|
||||
--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)
|
||||
--failing only show failing/ignored tests
|
||||
--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.
|
||||
--colors:on|off Turn messagescoloring on|off.
|
||||
--backendLogging:on|off Disable or enable backend logging. By default turned on.
|
||||
--skipFrom:file Read tests to skip from `file` - one test per line, # comments ignored
|
||||
""" % resultsFile
|
||||
|
||||
type
|
||||
|
|
@ -91,11 +88,11 @@ proc getFileDir(filename: string): string =
|
|||
if not result.isAbsolute():
|
||||
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,
|
||||
exitCode: int] {.tags:
|
||||
[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)
|
||||
|
||||
# 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):
|
||||
result.output.string.add(line.string)
|
||||
result.output.string.add("\n")
|
||||
if onStdout != nil: onStdout(line.string)
|
||||
else:
|
||||
result.exitCode = peekExitCode(p)
|
||||
if result.exitCode != -1: break
|
||||
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 =
|
||||
## Give each test a private nimcache dir so they don't clobber each other's.
|
||||
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, fgCyan, "Test \"", test.name, "\"", " in category \"", test.cat.string, "\""
|
||||
maybeStyledEcho styleBright, fgRed, "Failure: ", $success
|
||||
maybeStyledEcho fgYellow, "Expected:"
|
||||
maybeStyledEcho styleBright, expected, "\n"
|
||||
maybeStyledEcho fgYellow, "Gotten:"
|
||||
maybeStyledEcho styleBright, given, "\n"
|
||||
if success in {reBuildFailed, reNimcCrash, reInstallFailed}:
|
||||
# expected is empty, no reason to print it.
|
||||
echo given
|
||||
else:
|
||||
maybeStyledEcho fgYellow, "Expected:"
|
||||
maybeStyledEcho styleBright, expected, "\n"
|
||||
maybeStyledEcho fgYellow, "Gotten:"
|
||||
maybeStyledEcho styleBright, given, "\n"
|
||||
|
||||
|
||||
if backendLogging and existsEnv("APPVEYOR"):
|
||||
let (outcome, msg) =
|
||||
if success == reSuccess:
|
||||
case success
|
||||
of reSuccess:
|
||||
("Passed", "")
|
||||
elif success in {reDisabled, reJoined}:
|
||||
of reDisabled, reJoined:
|
||||
("Skipped", "")
|
||||
of reBuildFailed, reNimcCrash, reInstallFailed:
|
||||
("Failed", "Failure: " & $success & "\n" & given)
|
||||
else:
|
||||
("Failed", "Failure: " & $success & "\nExpected:\n" & expected & "\n\n" & "Gotten:\n" & given)
|
||||
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 ext = targetToExt[target]
|
||||
result = nimcacheDir(test.name, test.options, target) /
|
||||
("compiler_" & name.changeFileExt(ext))
|
||||
name.replace("_", "__").changeFileExt(ext)
|
||||
|
||||
proc needsCodegenCheck(spec: TSpec): bool =
|
||||
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"
|
||||
var given = callCompiler(expected.getCmd, test.name, test.options, target)
|
||||
if given.err != reSuccess:
|
||||
r.addResult(test, target, "", given.msg, given.err)
|
||||
r.addResult(test, target, "", given.nimout, given.err)
|
||||
continue
|
||||
let isJsTarget = target == targetJS
|
||||
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)
|
||||
else:
|
||||
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
|
||||
# to return other codes, but for us it is sufficient to know that it's not 0.
|
||||
if exitCode != 0: exitCode = 1
|
||||
|
|
@ -514,6 +510,8 @@ const disabledFilesDefault = @[
|
|||
"LockFreeHash.nim",
|
||||
"sharedstrings.nim",
|
||||
"tableimpl.nim",
|
||||
"setimpl.nim",
|
||||
"hashcommon.nim",
|
||||
|
||||
# Error: undeclared identifier: 'hasThreadSupport'
|
||||
"ioselectors_epoll.nim",
|
||||
|
|
@ -545,14 +543,13 @@ proc main() =
|
|||
var optFailing = false
|
||||
var targetsStr = ""
|
||||
var isMainProcess = true
|
||||
var skipFrom = ""
|
||||
|
||||
var p = initOptParser()
|
||||
p.next()
|
||||
while p.kind == cmdLongoption:
|
||||
case p.key.string.normalize
|
||||
of "print", "verbose": optPrintResults = true
|
||||
of "verbosemegatest": verboseMegatest = true
|
||||
of "verbosecommands": verboseCommands = true
|
||||
of "failing": optFailing = true
|
||||
of "pedantic": discard "now always enabled"
|
||||
of "targets":
|
||||
|
|
@ -580,6 +577,8 @@ proc main() =
|
|||
backendLogging = false
|
||||
else:
|
||||
quit Usage
|
||||
of "skipfrom":
|
||||
skipFrom = p.val.string
|
||||
else:
|
||||
quit Usage
|
||||
p.next()
|
||||
|
|
@ -598,6 +597,9 @@ proc main() =
|
|||
|
||||
myself &= " " & quoteShell("--nim:" & compilerPrefix)
|
||||
|
||||
if skipFrom.len > 0:
|
||||
myself &= " " & quoteShell("--skipFrom:" & skipFrom)
|
||||
|
||||
var cats: seq[string]
|
||||
let rest = if p.cmdLineRest.string.len > 0: " " & p.cmdLineRest.string else: ""
|
||||
for kind, dir in walkDir(testsDir):
|
||||
|
|
@ -617,13 +619,13 @@ proc main() =
|
|||
if simulate:
|
||||
for i, cati in cats:
|
||||
progressStatus(i)
|
||||
processCategory(r, Category(cati), p.cmdLineRest.string, testsDir, runJoinableTests = false)
|
||||
processCategory(r, Category(cati), p.cmdLineRest.string, testsDir, skipFrom, runJoinableTests = false)
|
||||
else:
|
||||
quit osproc.execProcesses(cmds, {poEchoCmd, poStdErrToStdOut, poUsePath, poParentStreams}, beforeRunEvent = progressStatus)
|
||||
of "c", "cat", "category":
|
||||
var cat = Category(p.key)
|
||||
p.next
|
||||
processCategory(r, cat, p.cmdLineRest.string, testsDir, runJoinableTests = true)
|
||||
processCategory(r, cat, p.cmdLineRest.string, testsDir, skipFrom, runJoinableTests = true)
|
||||
of "pcat":
|
||||
# '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
|
||||
|
|
@ -631,7 +633,7 @@ proc main() =
|
|||
isMainProcess = false
|
||||
var cat = Category(p.key)
|
||||
p.next
|
||||
processCategory(r, cat, p.cmdLineRest.string, testsDir, runJoinableTests = false)
|
||||
processCategory(r, cat, p.cmdLineRest.string, testsDir, skipFrom, runJoinableTests = false)
|
||||
of "r", "run":
|
||||
# at least one directory is required in the path, to use as a category name
|
||||
let pathParts = split(p.key.string, {DirSep, AltSep})
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ discard """
|
|||
output: '''(1, 1)
|
||||
(2, 2)
|
||||
(3, 3)
|
||||
@[1, 2, 3, 4]
|
||||
'''
|
||||
"""
|
||||
|
||||
|
|
@ -15,3 +16,12 @@ proc foo(args: varargs[int]) =
|
|||
discard
|
||||
|
||||
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
|
||||
----
|
||||
mygeneric2 constructed
|
||||
myobj destroyed
|
||||
mygeneric2 destroyed
|
||||
myobj destroyed
|
||||
----
|
||||
mygeneric3 constructed
|
||||
mygeneric1 destroyed
|
||||
|
|
@ -20,10 +20,10 @@ mygeneric2 destroyed
|
|||
----
|
||||
----
|
||||
myobj destroyed
|
||||
myobj destroyed
|
||||
myobj destroyed
|
||||
myobj destroyed
|
||||
mygeneric1 destroyed
|
||||
myobj destroyed
|
||||
myobj destroyed
|
||||
myobj destroyed
|
||||
---
|
||||
myobj destroyed
|
||||
myobj destroyed
|
||||
|
|
|
|||
|
|
@ -4,8 +4,8 @@ destroy
|
|||
destroy
|
||||
5
|
||||
123
|
||||
destroy Foo: 5
|
||||
destroy Foo: 123'''
|
||||
destroy Foo: 123
|
||||
destroy Foo: 5'''
|
||||
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
|
||||
ha
|
||||
@["arg", "asdfklasdfkl", "asdkfj", "dfasj", "klfjl"]
|
||||
22 22'''
|
||||
@[1, 2, 3]
|
||||
@["red", "yellow", "orange", "rtrt1", "pink"]
|
||||
30 30'''
|
||||
"""
|
||||
|
||||
import allocators
|
||||
|
|
@ -132,6 +134,41 @@ proc 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
|
||||
let (a, d) = allocCounters()
|
||||
discard cprintf("%ld %ld\n", a, d)
|
||||
|
|
|
|||
|
|
@ -2,30 +2,46 @@ discard """
|
|||
output: '''works'''
|
||||
"""
|
||||
|
||||
type
|
||||
MyVal = object
|
||||
f: ptr float
|
||||
# bug #11095
|
||||
|
||||
proc `=destroy`(x: var MyVal) =
|
||||
type
|
||||
MyVal[T] = object
|
||||
f: ptr T
|
||||
|
||||
proc `=destroy`[T](x: var MyVal[T]) =
|
||||
if x.f != nil:
|
||||
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:
|
||||
`=destroy`(x1)
|
||||
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 =
|
||||
result.f = create(float)
|
||||
proc newVal[T](x: sink T): MyVal[T] =
|
||||
result.f = create(T)
|
||||
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
|
||||
|
||||
var flag = false
|
||||
|
||||
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[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
|
||||
1
|
||||
1
|
||||
something defered
|
||||
something defered
|
||||
'''
|
||||
"""
|
||||
|
||||
|
|
@ -45,3 +47,21 @@ proc foo: (proc: int) =
|
|||
return bar
|
||||
|
||||
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 """
|
||||
errormsg: "module names need to be unique per Nimble package"
|
||||
file: "tnotuniquename/mnotuniquename.nim"
|
||||
output: '''nested
|
||||
flat'''
|
||||
"""
|
||||
|
||||
import mnotuniquename
|
||||
import tnotuniquename/mnotuniquename as nun
|
||||
import tnotuniquename_dir/mnotuniquename as nun
|
||||
|
||||
nested()
|
||||
flat()
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
discard """
|
||||
errormsg: "module names need to be unique per Nimble package"
|
||||
file: "tnotuniquename/mnotuniquename.nim"
|
||||
disabled: "true"
|
||||
"""
|
||||
|
||||
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 """
|
||||
errormsg: "illegal recursion in type 'RefTreeInt'"
|
||||
errormsg: "illegal recursion in type 'RefTree'"
|
||||
"""
|
||||
|
||||
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 """
|
||||
errormsg: "invalid recursion in type 'Executor'"
|
||||
errormsg: "illegal recursion in type 'Executor'"
|
||||
line: 8
|
||||
"""
|
||||
# bug reported by PR #5637
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue