mirror of
https://github.com/nim-lang/Nim.git
synced 2025-12-31 02:12:11 +00:00
Merge branch 'devel' of https://github.com/Araq/Nimrod into devel
Conflicts: compiler/ccgexprs.nim
This commit is contained in:
@@ -1604,15 +1604,15 @@ proc genMagicExpr(p: BProc, e: PNode, d: var TLoc, op: TMagic) =
|
||||
of mGetTypeInfo: genGetTypeInfo(p, e, d)
|
||||
of mSwap: genSwap(p, e, d)
|
||||
of mUnaryLt:
|
||||
if optOverflowCheck notin p.options: unaryExpr(p, e, d, "$1 - 1")
|
||||
if optOverflowCheck notin p.options: unaryExpr(p, e, d, "($1 - 1)")
|
||||
else: unaryExpr(p, e, d, "#subInt($1, 1)")
|
||||
of mPred:
|
||||
# XXX: range checking?
|
||||
if optOverflowCheck notin p.options: binaryExpr(p, e, d, "$1 - $2")
|
||||
if optOverflowCheck notin p.options: binaryExpr(p, e, d, "($1 - $2)")
|
||||
else: binaryExpr(p, e, d, "#subInt($1, $2)")
|
||||
of mSucc:
|
||||
# XXX: range checking?
|
||||
if optOverflowCheck notin p.options: binaryExpr(p, e, d, "$1 + $2")
|
||||
if optOverflowCheck notin p.options: binaryExpr(p, e, d, "($1 + $2)")
|
||||
else: binaryExpr(p, e, d, "#addInt($1, $2)")
|
||||
of mInc:
|
||||
if optOverflowCheck notin p.options:
|
||||
|
||||
@@ -291,8 +291,12 @@ proc processSwitch(switch, arg: string, pass: TCmdLinePass, info: TLineInfo) =
|
||||
of "excludepath":
|
||||
expectArg(switch, arg, pass, info)
|
||||
let path = processPath(arg)
|
||||
lists.excludeStr(options.searchPaths, path)
|
||||
lists.excludeStr(options.lazyPaths, path)
|
||||
lists.excludePath(options.searchPaths, path)
|
||||
lists.excludePath(options.lazyPaths, path)
|
||||
if (len(path) > 0) and (path[len(path) - 1] == DirSep):
|
||||
let strippedPath = path[0 .. (len(path) - 2)]
|
||||
lists.excludePath(options.searchPaths, strippedPath)
|
||||
lists.excludePath(options.lazyPaths, strippedPath)
|
||||
of "nimcache":
|
||||
expectArg(switch, arg, pass, info)
|
||||
options.nimcacheDir = processPath(arg)
|
||||
|
||||
@@ -1321,7 +1321,7 @@ proc genMagic(p: PProc, n: PNode, r: var TCompRes) =
|
||||
of mLengthSeq, mLengthOpenArray, mLengthArray:
|
||||
unaryExpr(p, n, r, "", "$1.length")
|
||||
of mHigh:
|
||||
if skipTypes(n.sons[0].typ, abstractVar).kind == tyString:
|
||||
if skipTypes(n.sons[1].typ, abstractVar).kind == tyString:
|
||||
unaryExpr(p, n, r, "", "($1.length-2)")
|
||||
else:
|
||||
unaryExpr(p, n, r, "", "($1.length-1)")
|
||||
@@ -1532,6 +1532,7 @@ proc gen(p: PProc, n: PNode, r: var TCompRes) =
|
||||
genSym(p, n, r)
|
||||
of nkCharLit..nkInt64Lit:
|
||||
r.res = toRope(n.intVal)
|
||||
r.kind = resExpr
|
||||
of nkNilLit:
|
||||
if isEmptyType(n.typ):
|
||||
discard
|
||||
@@ -1539,8 +1540,10 @@ proc gen(p: PProc, n: PNode, r: var TCompRes) =
|
||||
r.typ = etyBaseIndex
|
||||
r.address = toRope"null" | toRope"nil"
|
||||
r.res = toRope"0"
|
||||
r.kind = resExpr
|
||||
else:
|
||||
r.res = toRope"null" | toRope"nil"
|
||||
r.kind = resExpr
|
||||
of nkStrLit..nkTripleStrLit:
|
||||
if skipTypes(n.typ, abstractVarRange).kind == tyString:
|
||||
useMagic(p, "cstrToNimstr")
|
||||
@@ -1556,6 +1559,7 @@ proc gen(p: PProc, n: PNode, r: var TCompRes) =
|
||||
if f > 0.0: r.res = toRope"Infinity"
|
||||
else: r.res = toRope"-Infinity"
|
||||
else: r.res = toRope(f.toStrMaxPrecision)
|
||||
r.kind = resExpr
|
||||
of nkCallKinds:
|
||||
if (n.sons[0].kind == nkSym) and (n.sons[0].sym.magic != mNone):
|
||||
genMagic(p, n, r)
|
||||
|
||||
@@ -8,7 +8,8 @@
|
||||
#
|
||||
|
||||
# This module implements a generic doubled linked list.
|
||||
|
||||
# TODO Remove this and replace it with something sensible
|
||||
import os
|
||||
type
|
||||
PListEntry* = ref TListEntry
|
||||
TListEntry* = object of TObject
|
||||
@@ -103,11 +104,12 @@ proc bringToFront*(list: var TLinkedList, entry: PListEntry) =
|
||||
entry.next = list.head
|
||||
list.head = entry
|
||||
|
||||
proc excludeStr*(list: var TLinkedList, data: string) =
|
||||
proc excludePath*(list: var TLinkedList, data: string) =
|
||||
var it = list.head
|
||||
while it != nil:
|
||||
let nxt = it.next
|
||||
if PStrEntry(it).data == data: remove(list, it)
|
||||
if cmpPaths(PStrEntry(it).data, data) == 0:
|
||||
remove(list, it)
|
||||
it = nxt
|
||||
|
||||
proc find*(list: TLinkedList, fn: TCompareProc, closure: pointer): PListEntry =
|
||||
|
||||
@@ -58,7 +58,7 @@ proc handleCmdLine() =
|
||||
if msgs.gErrorCounter == 0:
|
||||
when hasTinyCBackend:
|
||||
if gCmd == cmdRun:
|
||||
tccgen.run()
|
||||
tccgen.run(service.arguments)
|
||||
if optRun in gGlobalOptions:
|
||||
if gCmd == cmdCompileToJS:
|
||||
var ex: string
|
||||
|
||||
@@ -59,7 +59,7 @@ proc processCmdLine*(pass: TCmdLinePass, cmd: string) =
|
||||
inc argsCount
|
||||
|
||||
if pass == passCmd2:
|
||||
if optRun notin gGlobalOptions and arguments != "":
|
||||
if optRun notin gGlobalOptions and arguments != "" and options.command.normalize != "run":
|
||||
rawMessage(errArgsNeedRunOption, [])
|
||||
|
||||
proc serve*(action: proc (){.nimcall.}) =
|
||||
|
||||
@@ -68,11 +68,9 @@ proc compileCCode*(ccode: string) =
|
||||
setupEnvironment()
|
||||
discard compileString(gTinyC, ccode)
|
||||
|
||||
proc run*() =
|
||||
var a: array[0..1, cstring]
|
||||
a[0] = ""
|
||||
a[1] = ""
|
||||
var err = tinyc.run(gTinyC, 0'i32, cast[cstringArray](addr(a))) != 0'i32
|
||||
proc run*(args: string) =
|
||||
var s = @[cstring(gProjectName)] & map(split(args), proc(x: string): cstring = cstring(x))
|
||||
var err = tinyc.run(gTinyC, cint(len(s)), cast[cstringArray](addr(s[0]))) != 0'i32
|
||||
closeCCState(gTinyC)
|
||||
if err: rawMessage(errExecutionOfProgramFailed, "")
|
||||
|
||||
|
||||
@@ -12,6 +12,7 @@ Advanced commands:
|
||||
module dependency graph
|
||||
//dump dump all defined conditionals and search paths
|
||||
//check checks the project for syntax and semantic
|
||||
//pretty homogenizes source code style
|
||||
//idetools compiler support for IDEs: possible options:
|
||||
--track:FILE,LINE,COL track a file/cursor position
|
||||
--trackDirty:DIRTY_FILE,ORIG_FILE,LINE,COL
|
||||
|
||||
@@ -594,7 +594,8 @@ compiler.
|
||||
|
||||
.. raw:: html
|
||||
|
||||
<div id="officialPkgList"></div>
|
||||
<div id="officialPkgList"><b>If you are reading this you are missing
|
||||
babelpkglist.js or have javascript disabled in your browser.</b></div>
|
||||
|
||||
Unofficial packages
|
||||
-------------------
|
||||
@@ -605,7 +606,8 @@ Nimrod programming language.
|
||||
|
||||
.. raw:: html
|
||||
|
||||
<div id="unofficialPkgList"></div>
|
||||
<div id="unofficialPkgList"><b>If you are reading this you are missing
|
||||
babelpkglist.js or have javascript disabled in your browser.</b></div>
|
||||
|
||||
<script type="text/javascript" src="babelpkglist.js"></script>
|
||||
<script type="text/javascript" src="http://build.nimrod-lang.org/packages?callback=gotPackageList"></script>
|
||||
|
||||
@@ -1221,38 +1221,8 @@ branch switch ``system.reset`` has to be used.
|
||||
|
||||
Set type
|
||||
--------
|
||||
The set type models the mathematical notion of a set. The set's
|
||||
basetype can only be an ordinal type. The reason is that sets are implemented
|
||||
as high performance bit vectors.
|
||||
|
||||
Sets can be constructed via the set constructor: ``{}`` is the empty set. The
|
||||
empty set is type compatible with any special set type. The constructor
|
||||
can also be used to include elements (and ranges of elements) in the set:
|
||||
|
||||
.. code-block:: nimrod
|
||||
|
||||
{'a'..'z', '0'..'9'} # This constructs a set that contains the
|
||||
# letters from 'a' to 'z' and the digits
|
||||
# from '0' to '9'
|
||||
|
||||
These operations are supported by sets:
|
||||
|
||||
================== ========================================================
|
||||
operation meaning
|
||||
================== ========================================================
|
||||
``A + B`` union of two sets
|
||||
``A * B`` intersection of two sets
|
||||
``A - B`` difference of two sets (A without B's elements)
|
||||
``A == B`` set equality
|
||||
``A <= B`` subset relation (A is subset of B or equal to B)
|
||||
``A < B`` strong subset relation (A is a real subset of B)
|
||||
``e in A`` set membership (A contains element e)
|
||||
``A -+- B`` symmetric set difference (= (A - B) + (B - A))
|
||||
``card(A)`` the cardinality of A (number of elements in A)
|
||||
``incl(A, elem)`` same as A = A + {elem}
|
||||
``excl(A, elem)`` same as A = A - {elem}
|
||||
================== ========================================================
|
||||
|
||||
.. include:: sets_fragment.txt
|
||||
|
||||
Reference and pointer types
|
||||
---------------------------
|
||||
|
||||
@@ -540,6 +540,58 @@ in C/C++).
|
||||
**Note**: This pragma will not exist for the LLVM backend.
|
||||
|
||||
|
||||
Source code style
|
||||
=================
|
||||
|
||||
Nimrod allows you to `mix freely case and underscores as identifier separators
|
||||
<manual.html#identifiers-keywords>`_, so variables named ``MyPrecioussInt`` and
|
||||
``my_preciouss_int`` are equivalent:
|
||||
|
||||
.. code-block:: Nimrod
|
||||
var MyPrecioussInt = 3
|
||||
# Following line compiles fine!
|
||||
echo my_preciouss_int
|
||||
|
||||
Since this can lead to many variants of the same source code (you can use
|
||||
`nimgrep <nimgrep.html>`_ instead of your typical ``grep`` to ignore style
|
||||
problems) the compiler provides the command ``pretty`` to help unifying the
|
||||
style of source code. Running ``nimrod pretty ugly_test.nim`` with this
|
||||
example will generate a secondary file named ``ugly_test.pretty.nim`` with the
|
||||
following content:
|
||||
|
||||
.. code-block:: Nimrod
|
||||
var MyPrecioussInt = 3
|
||||
# Following line compiles fine!
|
||||
echo MyPrecioussInt
|
||||
|
||||
During execution the ``pretty`` command will also run on Nimrod's standard
|
||||
library, since it doesn't differentiate the standard library as something
|
||||
special, and hence will warn of many *errors* which are out of your hand to
|
||||
fix, creating respective ``.pretty.nim`` files all the way. You can ignore
|
||||
these errors if they don't belong to your source and simply compare your
|
||||
original version to the new pretty one. In fact, running ``pretty`` on our test
|
||||
file will show the following::
|
||||
|
||||
Hint: ugly_test [Processing]
|
||||
ugly_test.nim(1, 4) Error: name should be: myPrecioussInt
|
||||
ugly_test.nim(1, 4) Error: name should be: myPrecioussInt
|
||||
|
||||
At the moment ``pretty`` will homogenize the style of symbols but will leave
|
||||
important changes for you to review. In this case the command is warning that a
|
||||
variable name should not start with a capital letter, which is usually reserved
|
||||
to `object types <tut2.html#objects>`_. To learn about the accepted `camel case
|
||||
style <https://en.wikipedia.org/wiki/Camelcase>`_ read `Coding Guidelines in
|
||||
the Internals of Nimrod Compiler <intern.html#coding-guidelines>`_ or `Coding
|
||||
Guidelines <https://github.com/Araq/Nimrod/wiki/Coding-Guidelines>`_ and `NEP 1
|
||||
: Style Guide for Nimrod Code
|
||||
<https://github.com/Araq/Nimrod/wiki/NEP-1-:-Style-Guide-for-Nimrod-Code>`_
|
||||
from the Nimrod `GitHub wiki<https://github.com/Araq/Nimrod/wiki>`_.
|
||||
|
||||
This command is safe to run because it will never attempt to overwrite your
|
||||
existing sources, but the respective ``.pretty.nim`` files **will** be
|
||||
overwritten without notice.
|
||||
|
||||
|
||||
DynlibOverride
|
||||
==============
|
||||
|
||||
|
||||
40
doc/sets_fragment.txt
Normal file
40
doc/sets_fragment.txt
Normal file
@@ -0,0 +1,40 @@
|
||||
The set type models the mathematical notion of a set. The set's
|
||||
basetype can only be an ordinal type. The reason is that sets are implemented
|
||||
as high performance bit vectors.
|
||||
|
||||
Sets can be constructed via the set constructor: ``{}`` is the empty set. The
|
||||
empty set is type compatible with any concrete set type. The constructor
|
||||
can also be used to include elements (and ranges of elements):
|
||||
|
||||
.. code-block:: nimrod
|
||||
type
|
||||
TCharSet = set[char]
|
||||
var
|
||||
x: TCharSet
|
||||
x = {'a'..'z', '0'..'9'} # This constructs a set that contains the
|
||||
# letters from 'a' to 'z' and the digits
|
||||
# from '0' to '9'
|
||||
|
||||
These operations are supported by sets:
|
||||
|
||||
================== ========================================================
|
||||
operation meaning
|
||||
================== ========================================================
|
||||
``A + B`` union of two sets
|
||||
``A * B`` intersection of two sets
|
||||
``A - B`` difference of two sets (A without B's elements)
|
||||
``A == B`` set equality
|
||||
``A <= B`` subset relation (A is subset of B or equal to B)
|
||||
``A < B`` strong subset relation (A is a real subset of B)
|
||||
``e in A`` set membership (A contains element e)
|
||||
``e notin A`` A does not contain element e
|
||||
``contains(A, e)`` A contains element e
|
||||
``A -+- B`` symmetric set difference (= (A - B) + (B - A))
|
||||
``card(A)`` the cardinality of A (number of elements in A)
|
||||
``incl(A, elem)`` same as ``A = A + {elem}``
|
||||
``excl(A, elem)`` same as ``A = A - {elem}``
|
||||
================== ========================================================
|
||||
|
||||
Sets are often used to define a type for the *flags* of a procedure. This is
|
||||
a much cleaner (and type safe) solution than just defining integer
|
||||
constants that should be ``or``'ed together.
|
||||
41
doc/tut1.txt
41
doc/tut1.txt
@@ -1117,47 +1117,8 @@ avoid this common programming error.
|
||||
|
||||
Sets
|
||||
----
|
||||
The set type models the mathematical notion of a set. The set's
|
||||
basetype can only be an ordinal type. The reason is that sets are implemented
|
||||
as high performance bit vectors.
|
||||
|
||||
Sets can be constructed via the set constructor: ``{}`` is the empty set. The
|
||||
empty set is type compatible with any concrete set type. The constructor
|
||||
can also be used to include elements (and ranges of elements):
|
||||
|
||||
.. code-block:: nimrod
|
||||
type
|
||||
TCharSet = set[char]
|
||||
var
|
||||
x: TCharSet
|
||||
x = {'a'..'z', '0'..'9'} # This constructs a set that contains the
|
||||
# letters from 'a' to 'z' and the digits
|
||||
# from '0' to '9'
|
||||
|
||||
These operations are supported by sets:
|
||||
|
||||
================== ========================================================
|
||||
operation meaning
|
||||
================== ========================================================
|
||||
``A + B`` union of two sets
|
||||
``A * B`` intersection of two sets
|
||||
``A - B`` difference of two sets (A without B's elements)
|
||||
``A == B`` set equality
|
||||
``A <= B`` subset relation (A is subset of B or equal to B)
|
||||
``A < B`` strong subset relation (A is a real subset of B)
|
||||
``e in A`` set membership (A contains element e)
|
||||
``e notin A`` A does not contain element e
|
||||
``contains(A, e)`` A contains element e
|
||||
``A -+- B`` symmetric set difference (= (A - B) + (B - A))
|
||||
``card(A)`` the cardinality of A (number of elements in A)
|
||||
``incl(A, elem)`` same as ``A = A + {elem}``
|
||||
``excl(A, elem)`` same as ``A = A - {elem}``
|
||||
================== ========================================================
|
||||
|
||||
Sets are often used to define a type for the *flags* of a procedure. This is
|
||||
a much cleaner (and type safe) solution than just defining integer
|
||||
constants that should be ``or``'ed together.
|
||||
|
||||
.. include:: sets_fragment.txt
|
||||
|
||||
Arrays
|
||||
------
|
||||
|
||||
@@ -420,6 +420,59 @@ proc setBiggestInt*(x: TAny, y: biggestInt) =
|
||||
of tyUInt32: cast[ptr uint32](x.value)[] = uint32(y)
|
||||
else: assert false
|
||||
|
||||
proc getUInt*(x: TAny): uint =
|
||||
## retrieve the uint value out of `x`, `x` needs to represent an uint.
|
||||
assert skipRange(x.rawtype).kind == tyUInt
|
||||
result = cast[ptr uint](x.value)[]
|
||||
|
||||
proc getUInt8*(x: TAny): uint8 =
|
||||
## retrieve the uint8 value out of `x`, `x` needs to represent an
|
||||
## uint8.
|
||||
assert skipRange(x.rawtype).kind == tyUInt8
|
||||
result = cast[ptr uint8](x.value)[]
|
||||
|
||||
proc getUInt16*(x: TAny): uint16 =
|
||||
## retrieve the uint16 value out of `x`, `x` needs to represent an
|
||||
## uint16.
|
||||
assert skipRange(x.rawtype).kind == tyUInt16
|
||||
result = cast[ptr uint16](x.value)[]
|
||||
|
||||
proc getUInt32*(x: TAny): uint32 =
|
||||
## retrieve the uint32 value out of `x`, `x` needs to represent an
|
||||
## uint32.
|
||||
assert skipRange(x.rawtype).kind == tyUInt32
|
||||
result = cast[ptr uint32](x.value)[]
|
||||
|
||||
proc getUInt64*(x: TAny): uint64 =
|
||||
## retrieve the uint64 value out of `x`, `x` needs to represent an
|
||||
## uint64.
|
||||
assert skipRange(x.rawtype).kind == tyUInt64
|
||||
result = cast[ptr uint64](x.value)[]
|
||||
|
||||
proc getBiggestUint*(x: TAny): uint64 =
|
||||
## retrieve the unsigned integer value out of `x`. `x` needs to
|
||||
## represent an unsigned integer.
|
||||
var t = skipRange(x.rawtype)
|
||||
case t.kind
|
||||
of tyUInt: result = uint64(cast[ptr uint](x.value)[])
|
||||
of tyUInt8: result = uint64(cast[ptr uint8](x.value)[])
|
||||
of tyUInt16: result = uint64(cast[ptr uint16](x.value)[])
|
||||
of tyUInt32: result = uint64(cast[ptr uint32](x.value)[])
|
||||
of tyUInt64: result = uint64(cast[ptr uint64](x.value)[])
|
||||
else: assert false
|
||||
|
||||
proc setBiggestUint*(x: TAny; y: uint64) =
|
||||
## sets the unsigned integer value of `c`. `c` needs to represent an
|
||||
## unsigned integer.
|
||||
var t = skipRange(x.rawtype)
|
||||
case t.kind:
|
||||
of tyUInt: cast[ptr uint](x.value)[] = uint(y)
|
||||
of tyUInt8: cast[ptr uint8](x.value)[] = uint8(y)
|
||||
of tyUInt16: cast[ptr uint16](x.value)[] = uint16(y)
|
||||
of tyUInt32: cast[ptr uint32](x.value)[] = uint32(y)
|
||||
of tyUInt64: cast[ptr uint64](x.value)[] = uint64(y)
|
||||
else: assert false
|
||||
|
||||
proc getChar*(x: TAny): char =
|
||||
## retrieve the char value out of `x`. `x` needs to represent a char.
|
||||
var t = skipRange(x.rawtype)
|
||||
|
||||
@@ -775,7 +775,7 @@ proc renderCodeBlock(d: PDoc, n: PRstNode, result: var string) =
|
||||
dispA(d.target, result, "<pre>", "\\begin{rstpre}\n", [])
|
||||
if lang == langNone:
|
||||
d.msgHandler(d.filename, 1, 0, mwUnsupportedLanguage, langstr)
|
||||
result.add(m.text)
|
||||
for letter in m.text: escChar(d.target, result, letter)
|
||||
else:
|
||||
var g: TGeneralTokenizer
|
||||
initGeneralTokenizer(g, m.text)
|
||||
|
||||
@@ -90,7 +90,7 @@ type
|
||||
d_ino*: Tino ## File serial number.
|
||||
d_name*: array [0..255, char] ## Name of entry.
|
||||
|
||||
Tflock* {.importc: "flock", final, pure,
|
||||
Tflock* {.importc: "struct flock", final, pure,
|
||||
header: "<fcntl.h>".} = object ## flock type
|
||||
l_type*: cshort ## Type of lock; F_RDLCK, F_WRLCK, F_UNLCK.
|
||||
l_whence*: cshort ## Flag for starting offset.
|
||||
|
||||
@@ -58,7 +58,7 @@ template encodeInternal(s: expr, lineLen: int, newLine: string): stmt {.immediat
|
||||
if r+4 != result.len:
|
||||
setLen(result, r+4)
|
||||
else:
|
||||
assert(r == result.len)
|
||||
#assert(r == result.len)
|
||||
|
||||
proc encode*[T:TInteger|char](s: openarray[T], lineLen = 75, newLine="\13\10"): string =
|
||||
## encodes `s` into base64 representation. After `lineLen` characters, a
|
||||
|
||||
@@ -9,6 +9,10 @@
|
||||
|
||||
## The ``sets`` module implements an efficient hash set and ordered hash set.
|
||||
##
|
||||
## Hash sets are different from the `built in set type
|
||||
## <manual.html#set-type>`_. Sets allow you to store any value that can be
|
||||
## `hashed <hashes.html>`_ and they don't contain duplicate entries.
|
||||
##
|
||||
## **Note**: The data types declared here have *value semantics*: This means
|
||||
## that ``=`` performs a copy of the set.
|
||||
|
||||
@@ -23,20 +27,69 @@ type
|
||||
TSlotEnum = enum seEmpty, seFilled, seDeleted
|
||||
TKeyValuePair[A] = tuple[slot: TSlotEnum, key: A]
|
||||
TKeyValuePairSeq[A] = seq[TKeyValuePair[A]]
|
||||
TSet* {.final, myShallow.}[A] = object ## a generic hash set
|
||||
TSet* {.final, myShallow.}[A] = object ## \
|
||||
## A generic hash set.
|
||||
##
|
||||
## Use `init() <#init,TSet[A],int>`_ or `initSet[type]() <#initSet>`_
|
||||
## before calling other procs on it.
|
||||
data: TKeyValuePairSeq[A]
|
||||
counter: int
|
||||
|
||||
proc isValid*[A](s: TSet[A]): bool =
|
||||
## Returns `true` if the set has been initialized with `initSet <#initSet>`_.
|
||||
##
|
||||
## 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. Example:
|
||||
##
|
||||
## .. code-block :: nimrod
|
||||
## proc savePreferences(options: TSet[string]) =
|
||||
## assert options.isValid, "Pass an initialized set!"
|
||||
## # Do stuff here, may crash in release builds!
|
||||
result = not s.data.isNil
|
||||
|
||||
proc len*[A](s: TSet[A]): int =
|
||||
## returns the number of keys in `s`.
|
||||
## Returns the number of keys in `s`.
|
||||
##
|
||||
## Due to an implementation detail you can call this proc on variables which
|
||||
## have not been initialized yet. The proc will return zero as the length
|
||||
## then. Example:
|
||||
##
|
||||
## .. code-block::
|
||||
##
|
||||
## var values: TSet[int]
|
||||
## assert(not values.isValid)
|
||||
## assert values.len == 0
|
||||
result = s.counter
|
||||
|
||||
proc card*[A](s: TSet[A]): int =
|
||||
## alias for `len`.
|
||||
## Alias for `len() <#len,TSet[A]>`_.
|
||||
##
|
||||
## Card stands for the `cardinality
|
||||
## <http://en.wikipedia.org/wiki/Cardinality>`_ of a set.
|
||||
result = s.counter
|
||||
|
||||
iterator items*[A](s: TSet[A]): A =
|
||||
## iterates over any key in the table `t`.
|
||||
## Iterates over keys in the set `s`.
|
||||
##
|
||||
## If you need a sequence with the keys you can use `sequtils.toSeq()
|
||||
## <sequtils.html#toSeq>`_ on the iterator. Usage example:
|
||||
##
|
||||
## .. code-block::
|
||||
## type
|
||||
## pair = tuple[a, b: int]
|
||||
## var
|
||||
## a, b = initSet[pair]()
|
||||
## a.incl((2, 3))
|
||||
## a.incl((3, 2))
|
||||
## a.incl((2, 3))
|
||||
## for x, y in a.items:
|
||||
## b.incl((x - 2, y + 1))
|
||||
## 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):
|
||||
if s.data[h].slot == seFilled: yield s.data[h].key
|
||||
|
||||
@@ -73,12 +126,24 @@ proc mget*[A](s: var TSet[A], key: A): var A =
|
||||
## value as 'key' or raises the ``EInvalidKey`` exception. 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 index = rawGet(s, key)
|
||||
if index >= 0: result = t.data[index].key
|
||||
else: raise newException(EInvalidKey, "key not found: " & $key)
|
||||
|
||||
proc contains*[A](s: TSet[A], key: A): bool =
|
||||
## returns true iff `key` is in `s`.
|
||||
## Returns true iff `key` is in `s`.
|
||||
##
|
||||
## Example:
|
||||
##
|
||||
## .. code-block::
|
||||
## var values = initSet[int]()
|
||||
## assert(not values.contains(2))
|
||||
## values.incl(2)
|
||||
## assert values.contains(2)
|
||||
## values.excl(2)
|
||||
## assert(not values.contains(2))
|
||||
assert s.isValid, "The set needs to be initialized."
|
||||
var index = rawGet(s, key)
|
||||
result = index >= 0
|
||||
|
||||
@@ -109,38 +174,124 @@ template containsOrInclImpl() {.dirty.} =
|
||||
inc(s.counter)
|
||||
|
||||
proc incl*[A](s: var TSet[A], key: A) =
|
||||
## includes an element `key` in `s`.
|
||||
## Includes an element `key` in `s`.
|
||||
##
|
||||
## This doesn't do anything if `key` is already in `s`. Example:
|
||||
##
|
||||
## .. code-block::
|
||||
## var values = initSet[int]()
|
||||
## values.incl(2)
|
||||
## values.incl(2)
|
||||
## assert values.len == 1
|
||||
assert s.isValid, "The set needs to be initialized."
|
||||
inclImpl()
|
||||
|
||||
proc incl*[A](s: var TSet[A], other: TSet[A]) =
|
||||
## includes everything in `other` in `s`
|
||||
## Includes all elements from `other` into `s`.
|
||||
##
|
||||
## Example:
|
||||
##
|
||||
## .. code-block::
|
||||
## var values = initSet[int]()
|
||||
## values.incl(2)
|
||||
## var others = toSet([6, 7])
|
||||
## values.incl(others)
|
||||
## assert values.len == 3
|
||||
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 excl*[A](s: var TSet[A], key: A) =
|
||||
## excludes `key` from the set `s`.
|
||||
## Excludes `key` from the set `s`.
|
||||
##
|
||||
## This doesn't do anything if `key` is not found in `s`. Example:
|
||||
##
|
||||
## .. code-block::
|
||||
## var s = toSet([2, 3, 6, 7])
|
||||
## s.excl(2)
|
||||
## s.excl(2)
|
||||
## assert s.len == 3
|
||||
assert s.isValid, "The set needs to be initialized."
|
||||
var index = rawGet(s, key)
|
||||
if index >= 0:
|
||||
s.data[index].slot = seDeleted
|
||||
dec(s.counter)
|
||||
|
||||
proc excl*[A](s: var TSet[A], other: TSet[A]) =
|
||||
## excludes everything in `other` from `s`.
|
||||
## Excludes everything in `other` from `s`.
|
||||
##
|
||||
## Example:
|
||||
##
|
||||
## .. code-block::
|
||||
## var
|
||||
## numbers = toSet([1, 2, 3, 4, 5])
|
||||
## even = toSet([2, 4, 6, 8])
|
||||
## numbers.excl(even)
|
||||
## echo 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: excl(s, item)
|
||||
|
||||
proc containsOrIncl*[A](s: var TSet[A], key: A): bool =
|
||||
## returns true if `s` contains `key`, otherwise `key` is included in `s`
|
||||
## and false is returned.
|
||||
## Includes `key` in the set `s` and tells if `key` was added to `s`.
|
||||
##
|
||||
## The difference with regards to the `incl() <#incl,TSet[A],A>`_ proc is
|
||||
## that this proc returns `true` if `key` was already present in `s`. The
|
||||
## proc will return false if `key` was added as a new value to `s` during
|
||||
## this call. Example:
|
||||
##
|
||||
## .. code-block::
|
||||
## var values = initSet[int]()
|
||||
## assert values.containsOrIncl(2) == false
|
||||
## assert values.containsOrIncl(2) == true
|
||||
assert s.isValid, "The set needs to be initialized."
|
||||
containsOrInclImpl()
|
||||
|
||||
proc initSet*[A](initialSize=64): TSet[A] =
|
||||
## creates a new hash set that is empty. `initialSize` needs to be
|
||||
## a power of two.
|
||||
proc init*[A](s: var TSet[A], initialSize=64) =
|
||||
## Initializes a hash set.
|
||||
##
|
||||
## The `initialSize` parameter needs to be a power of too. You can use
|
||||
## `math.nextPowerOfTwo() <math.html#nextPowerOfTwo>`_ to guarantee that at
|
||||
## runtime. All set variables have to be initialized before you can use them
|
||||
## with other procs from this module with the exception of `isValid()
|
||||
## <#isValid,TSet[A]>`_ and `len() <#len,TSet[A]>`_.
|
||||
##
|
||||
## 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
|
||||
## existing values and calling `excl() <#excl,TSet[A],A>`_ on them. Example:
|
||||
##
|
||||
## .. code-block ::
|
||||
## var a: TSet[int]
|
||||
## a.init(4)
|
||||
## a.incl(2)
|
||||
## a.init
|
||||
## assert a.len == 0 and a.isValid
|
||||
assert isPowerOfTwo(initialSize)
|
||||
result.counter = 0
|
||||
newSeq(result.data, initialSize)
|
||||
s.counter = 0
|
||||
newSeq(s.data, initialSize)
|
||||
|
||||
proc initSet*[A](initialSize=64): TSet[A] =
|
||||
## Wrapper around `init() <#init,TSet[A],int>`_ for initialization of hash
|
||||
## sets.
|
||||
##
|
||||
## Returns an empty hash set you can assign directly in ``var`` blocks in a
|
||||
## single line. Example:
|
||||
##
|
||||
## .. code-block ::
|
||||
## var a = initSet[int](4)
|
||||
## a.incl(2)
|
||||
result.init(initialSize)
|
||||
|
||||
proc toSet*[A](keys: openArray[A]): TSet[A] =
|
||||
## creates a new hash set that contains the given `keys`.
|
||||
## Creates a new hash set that contains the given `keys`.
|
||||
##
|
||||
## Example:
|
||||
##
|
||||
## .. code-block::
|
||||
## var numbers = toSet([1, 2, 3, 4, 5])
|
||||
## assert numbers.contains(2)
|
||||
## assert numbers.contains(4)
|
||||
result = initSet[A](nextPowerOfTwo(keys.len+10))
|
||||
for key in items(keys): result.incl(key)
|
||||
|
||||
@@ -152,57 +303,190 @@ template dollarImpl(): stmt {.dirty.} =
|
||||
result.add("}")
|
||||
|
||||
proc `$`*[A](s: TSet[A]): string =
|
||||
## The `$` operator for hash sets.
|
||||
## Converts the set `s` to a string, mostly for logging purposes.
|
||||
##
|
||||
## Don't use this proc for serialization, the representation may change at
|
||||
## any moment and values are not escaped. Example:
|
||||
##
|
||||
## Example:
|
||||
##
|
||||
## .. code-block::
|
||||
## echo toSet([2, 4, 5])
|
||||
## # --> {2, 4, 5}
|
||||
## echo toSet(["no", "esc'aping", "is \" provided"])
|
||||
## # --> {no, esc'aping, is " provided}
|
||||
assert s.isValid, "The set needs to be initialized."
|
||||
dollarImpl()
|
||||
|
||||
proc union*[A](s1, s2: TSet[A]): TSet[A] =
|
||||
## returns a new set of all items that are contained in at
|
||||
## least one of `s1` and `s2`
|
||||
## Returns the union of the sets `s1` and `s2`.
|
||||
##
|
||||
## The union of two sets is represented mathematically as *A ∪ B* and is the
|
||||
## set of all objects that are members of `s1`, `s2` or both. Example:
|
||||
##
|
||||
## .. code-block::
|
||||
## var
|
||||
## a = toSet(["a", "b"])
|
||||
## b = toSet(["b", "c"])
|
||||
## c = union(a, b)
|
||||
## assert c == toSet(["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)
|
||||
|
||||
proc intersection*[A](s1, s2: TSet[A]): TSet[A] =
|
||||
## returns a new set of all items that are contained in both `s1` and `s2`
|
||||
## Returns the intersection of the sets `s1` and `s2`.
|
||||
##
|
||||
## The intersection of two sets is represented mathematically as *A ∩ B* and
|
||||
## is the set of all objects that are members of `s1` and `s2` at the same
|
||||
## time. Example:
|
||||
##
|
||||
## .. code-block::
|
||||
## var
|
||||
## a = toSet(["a", "b"])
|
||||
## b = toSet(["b", "c"])
|
||||
## c = intersection(a, b)
|
||||
## assert c == toSet(["b"])
|
||||
assert s1.isValid, "The set `s1` needs to be initialized."
|
||||
assert s2.isValid, "The set `s2` needs to be initialized."
|
||||
result = initSet[A](min(s1.data.len, s2.data.len))
|
||||
for item in s1:
|
||||
if item in s2: incl(result, item)
|
||||
|
||||
proc difference*[A](s1, s2: TSet[A]): TSet[A] =
|
||||
## returns a new set of all items that are contained in `s1`, but not in `s2`
|
||||
## Returns the difference of the sets `s1` and `s2`.
|
||||
##
|
||||
## The difference of two sets is represented mathematically as *A \ B* and is
|
||||
## the set of all objects that are members of `s1` and not members of `s2`.
|
||||
## Example:
|
||||
##
|
||||
## .. code-block::
|
||||
## var
|
||||
## a = toSet(["a", "b"])
|
||||
## b = toSet(["b", "c"])
|
||||
## c = difference(a, b)
|
||||
## assert c == toSet(["a"])
|
||||
assert s1.isValid, "The set `s1` needs to be initialized."
|
||||
assert s2.isValid, "The set `s2` needs to be initialized."
|
||||
result = initSet[A]()
|
||||
for item in s1:
|
||||
if not contains(s2, item):
|
||||
incl(result, item)
|
||||
|
||||
proc symmetricDifference*[A](s1, s2: TSet[A]): TSet[A] =
|
||||
## returns a new set of all items that are contained in either
|
||||
## `s1` or `s2`, but not both
|
||||
## Returns the symmetric difference of the sets `s1` and `s2`.
|
||||
##
|
||||
## The symmetric difference of two sets is represented mathematically as *A △
|
||||
## B* or *A ⊖ B* and is the set of all objects that are members of `s1` or
|
||||
## `s2` but not both at the same time. Example:
|
||||
##
|
||||
## .. code-block::
|
||||
## var
|
||||
## a = toSet(["a", "b"])
|
||||
## b = toSet(["b", "c"])
|
||||
## c = symmetricDifference(a, b)
|
||||
## assert c == toSet(["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)
|
||||
|
||||
proc `+`*[A](s1, s2: TSet[A]): TSet[A] {.inline.} =
|
||||
## alias for `union`
|
||||
## Alias for `union(s1, s2) <#union>`_.
|
||||
result = union(s1, s2)
|
||||
|
||||
proc `*`*[A](s1, s2: TSet[A]): TSet[A] {.inline.} =
|
||||
## alias for `intersection`
|
||||
## Alias for `intersection(s1, s2) <#intersection>`_.
|
||||
result = intersection(s1, s2)
|
||||
|
||||
proc `-`*[A](s1, s2: TSet[A]): TSet[A] {.inline.} =
|
||||
## alias for `difference`
|
||||
## Alias for `difference(s1, s2) <#difference>`_.
|
||||
result = difference(s1, s2)
|
||||
|
||||
proc `-+-`*[A](s1, s2: TSet[A]): TSet[A] {.inline.} =
|
||||
## alias for `symmetricDifference`
|
||||
## Alias for `symmetricDifference(s1, s2) <#symmetricDifference>`_.
|
||||
result = symmetricDifference(s1, s2)
|
||||
|
||||
proc disjoint*[A](s1, s2: TSet[A]): bool =
|
||||
## returns true iff `s1` and `s2` have no items in common
|
||||
## Returns true iff the sets `s1` and `s2` have no items in common.
|
||||
##
|
||||
## Example:
|
||||
##
|
||||
## .. code-block::
|
||||
## var
|
||||
## a = toSet(["a", "b"])
|
||||
## b = toSet(["b", "c"])
|
||||
## 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
|
||||
|
||||
proc `<`*[A](s, t: TSet[A]): bool =
|
||||
## Returns true if `s` is a strict or proper subset of `t`.
|
||||
##
|
||||
## A strict or proper subset `s` has all of its members in `t` but `t` has
|
||||
## more elements than `s`. Example:
|
||||
##
|
||||
## .. code-block::
|
||||
## var
|
||||
## a = toSet(["a", "b"])
|
||||
## b = toSet(["b", "c"])
|
||||
## c = intersection(a, b)
|
||||
## assert c < a and c < b
|
||||
## assert((a < a) == false)
|
||||
s.counter != t.counter and s <= t
|
||||
|
||||
proc `<=`*[A](s, t: TSet[A]): bool =
|
||||
## Returns true if `s` is subset of `t`.
|
||||
##
|
||||
## A subset `s` has all of its members in `t` and `t` doesn't necessarily
|
||||
## have more members than `s`. That is, `s` can be equal to `t`. Example:
|
||||
##
|
||||
## .. code-block::
|
||||
## var
|
||||
## a = toSet(["a", "b"])
|
||||
## b = toSet(["b", "c"])
|
||||
## c = intersection(a, b)
|
||||
## assert c <= a and c <= b
|
||||
## assert((a <= a))
|
||||
result = false
|
||||
if s.counter > t.counter: return
|
||||
result = true
|
||||
for item in s:
|
||||
if not(t.contains(item)):
|
||||
result = false
|
||||
return
|
||||
|
||||
proc `==`*[A](s, t: TSet[A]): bool =
|
||||
## Returns true if both `s` and `t` have the same members and set size.
|
||||
##
|
||||
## Example:
|
||||
##
|
||||
## .. code-block::
|
||||
## var
|
||||
## a = toSet([1, 2])
|
||||
## b = toSet([1])
|
||||
## b.incl(2)
|
||||
## assert a == b
|
||||
s.counter == t.counter and s <= t
|
||||
|
||||
proc map*[A, B](data: TSet[A], op: proc (x: A): B {.closure.}): TSet[B] =
|
||||
## Returns a new set after applying `op` on each of the elements of `data`.
|
||||
##
|
||||
## You can use this proc to transform the elements from a set. Example:
|
||||
##
|
||||
## .. code-block::
|
||||
## var a = toSet([1, 2, 3])
|
||||
## var b = a.map(proc (x: int): string = $x)
|
||||
## assert b == toSet(["1", "2", "3"])
|
||||
result = initSet[B]()
|
||||
for item in data: result.incl(op(item))
|
||||
|
||||
# ------------------------------ ordered set ------------------------------
|
||||
|
||||
type
|
||||
@@ -210,16 +494,48 @@ type
|
||||
slot: TSlotEnum, next: int, key: A]
|
||||
TOrderedKeyValuePairSeq[A] = seq[TOrderedKeyValuePair[A]]
|
||||
TOrderedSet* {.
|
||||
final, myShallow.}[A] = object ## set that remembers insertion order
|
||||
final, myShallow.}[A] = object ## \
|
||||
## A generic hash set that remembers insertion order.
|
||||
##
|
||||
## Use `init() <#init,TOrderedSet[A],int>`_ or `initOrderedSet[type]()
|
||||
## <#initOrderedSet>`_ before calling other procs on it.
|
||||
data: TOrderedKeyValuePairSeq[A]
|
||||
counter, first, last: int
|
||||
|
||||
proc isValid*[A](s: TOrderedSet[A]): bool =
|
||||
## Returns `true` if the ordered set has been initialized with `initSet
|
||||
## <#initOrderedSet>`_.
|
||||
##
|
||||
## Most operations over an uninitialized ordered 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 ordered sets passed to your procs are
|
||||
## correctly initialized. Example:
|
||||
##
|
||||
## .. code-block :: nimrod
|
||||
## proc saveTarotCards(cards: TOrderedSet[int]) =
|
||||
## assert cards.isValid, "Pass an initialized set!"
|
||||
## # Do stuff here, may crash in release builds!
|
||||
result = not s.data.isNil
|
||||
|
||||
proc len*[A](s: TOrderedSet[A]): int {.inline.} =
|
||||
## returns the number of keys in `s`.
|
||||
## Returns the number of keys in `s`.
|
||||
##
|
||||
## Due to an implementation detail you can call this proc on variables which
|
||||
## have not been initialized yet. The proc will return zero as the length
|
||||
## then. Example:
|
||||
##
|
||||
## .. code-block::
|
||||
##
|
||||
## var values: TOrderedSet[int]
|
||||
## assert(not values.isValid)
|
||||
## assert values.len == 0
|
||||
result = s.counter
|
||||
|
||||
proc card*[A](s: TOrderedSet[A]): int {.inline.} =
|
||||
## alias for `len`.
|
||||
## Alias for `len() <#len,TOrderedSet[A]>`_.
|
||||
##
|
||||
## Card stands for the `cardinality
|
||||
## <http://en.wikipedia.org/wiki/Cardinality>`_ of a set.
|
||||
result = s.counter
|
||||
|
||||
template forAllOrderedPairs(yieldStmt: stmt) {.dirty, immediate.} =
|
||||
@@ -230,7 +546,24 @@ template forAllOrderedPairs(yieldStmt: stmt) {.dirty, immediate.} =
|
||||
h = nxt
|
||||
|
||||
iterator items*[A](s: TOrderedSet[A]): A =
|
||||
## iterates over any key in the set `s` in insertion order.
|
||||
## Iterates over keys in the ordered set `s` in insertion order.
|
||||
##
|
||||
## If you need a sequence with the keys you can use `sequtils.toSeq()
|
||||
## <sequtils.html#toSeq>`_ on the iterator. Usage example:
|
||||
##
|
||||
## .. code-block::
|
||||
## var a = initOrderedSet[int]()
|
||||
## for value in [9, 2, 1, 5, 1, 8, 4, 2]:
|
||||
## a.incl(value)
|
||||
## for value in a.items:
|
||||
## echo "Got ", value
|
||||
## # --> Got 9
|
||||
## # --> Got 2
|
||||
## # --> Got 1
|
||||
## # --> Got 5
|
||||
## # --> Got 8
|
||||
## # --> Got 4
|
||||
assert s.isValid, "The set needs to be initialized."
|
||||
forAllOrderedPairs:
|
||||
yield s.data[h].key
|
||||
|
||||
@@ -238,7 +571,16 @@ proc rawGet[A](s: TOrderedSet[A], key: A): int =
|
||||
rawGetImpl()
|
||||
|
||||
proc contains*[A](s: TOrderedSet[A], key: A): bool =
|
||||
## returns true iff `key` is in `s`.
|
||||
## Returns true iff `key` is in `s`.
|
||||
##
|
||||
## Example:
|
||||
##
|
||||
## .. code-block::
|
||||
## var values = initOrderedSet[int]()
|
||||
## assert(not values.contains(2))
|
||||
## values.incl(2)
|
||||
## assert values.contains(2)
|
||||
assert s.isValid, "The set needs to be initialized."
|
||||
var index = rawGet(s, key)
|
||||
result = index >= 0
|
||||
|
||||
@@ -264,53 +606,279 @@ proc enlarge[A](s: var TOrderedSet[A]) =
|
||||
swap(s.data, n)
|
||||
|
||||
proc incl*[A](s: var TOrderedSet[A], key: A) =
|
||||
## includes an element `key` in `s`.
|
||||
## Includes an element `key` in `s`.
|
||||
##
|
||||
## This doesn't do anything if `key` is already in `s`. Example:
|
||||
##
|
||||
## .. code-block::
|
||||
## var values = initOrderedSet[int]()
|
||||
## values.incl(2)
|
||||
## values.incl(2)
|
||||
## assert values.len == 1
|
||||
assert s.isValid, "The set needs to be initialized."
|
||||
inclImpl()
|
||||
|
||||
proc incl*[A](s: var TSet[A], other: TOrderedSet[A]) =
|
||||
## includes everything in `other` in `s`
|
||||
## Includes all elements from `other` into `s`.
|
||||
##
|
||||
## Example:
|
||||
##
|
||||
## .. code-block::
|
||||
## var values = initOrderedSet[int]()
|
||||
## values.incl(2)
|
||||
## var others = toOrderedSet([6, 7])
|
||||
## values.incl(others)
|
||||
## assert values.len == 3
|
||||
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 containsOrIncl*[A](s: var TOrderedSet[A], key: A): bool =
|
||||
## returns true if `s` contains `key`, otherwise `key` is included in `s`
|
||||
## and false is returned.
|
||||
## Includes `key` in the set `s` and tells if `key` was added to `s`.
|
||||
##
|
||||
## The difference with regards to the `incl() <#incl,TOrderedSet[A],A>`_ proc
|
||||
## is that this proc returns `true` if `key` was already present in `s`. The
|
||||
## proc will return false if `key` was added as a new value to `s` during
|
||||
## this call. Example:
|
||||
##
|
||||
## .. code-block::
|
||||
## var values = initOrderedSet[int]()
|
||||
## assert values.containsOrIncl(2) == false
|
||||
## assert values.containsOrIncl(2) == true
|
||||
assert s.isValid, "The set needs to be initialized."
|
||||
containsOrInclImpl()
|
||||
|
||||
proc initOrderedSet*[A](initialSize=64): TOrderedSet[A] =
|
||||
## creates a new ordered hash set that is empty. `initialSize` needs to be
|
||||
## a power of two.
|
||||
proc init*[A](s: var TOrderedSet[A], initialSize=64) =
|
||||
## Initializes an ordered hash set.
|
||||
##
|
||||
## The `initialSize` parameter needs to be a power of too. You can use
|
||||
## `math.nextPowerOfTwo() <math.html#nextPowerOfTwo>`_ to guarantee that at
|
||||
## runtime. All set variables have to be initialized before you can use them
|
||||
## with other procs from this module with the exception of `isValid()
|
||||
## <#isValid,TOrderedSet[A]>`_ and `len() <#len,TOrderedSet[A]>`_.
|
||||
##
|
||||
## You can call this proc on a previously initialized ordered hash set to
|
||||
## discard its values. At the moment this is the only proc to remove elements
|
||||
## from an ordered hash set. Example:
|
||||
##
|
||||
## .. code-block ::
|
||||
## var a: TOrderedSet[int]
|
||||
## a.init(4)
|
||||
## a.incl(2)
|
||||
## a.init
|
||||
## assert a.len == 0 and a.isValid
|
||||
assert isPowerOfTwo(initialSize)
|
||||
result.counter = 0
|
||||
result.first = -1
|
||||
result.last = -1
|
||||
newSeq(result.data, initialSize)
|
||||
s.counter = 0
|
||||
s.first = -1
|
||||
s.last = -1
|
||||
newSeq(s.data, initialSize)
|
||||
|
||||
proc initOrderedSet*[A](initialSize=64): TOrderedSet[A] =
|
||||
## Wrapper around `init() <#init,TOrderedSet[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. Example:
|
||||
##
|
||||
## .. code-block ::
|
||||
## var a = initOrderedSet[int](4)
|
||||
## a.incl(2)
|
||||
result.init(initialSize)
|
||||
|
||||
proc toOrderedSet*[A](keys: openArray[A]): TOrderedSet[A] =
|
||||
## creates a new ordered hash set that contains the given `keys`.
|
||||
## Creates a new ordered hash set that contains the given `keys`.
|
||||
##
|
||||
## Example:
|
||||
##
|
||||
## .. code-block::
|
||||
## var numbers = toOrderedSet([1, 2, 3, 4, 5])
|
||||
## assert numbers.contains(2)
|
||||
## assert numbers.contains(4)
|
||||
result = initOrderedSet[A](nextPowerOfTwo(keys.len+10))
|
||||
for key in items(keys): result.incl(key)
|
||||
|
||||
proc `$`*[A](s: TOrderedSet[A]): string =
|
||||
## The `$` operator for ordered hash sets.
|
||||
## Converts the ordered hash set `s` to a string, mostly for logging purposes.
|
||||
##
|
||||
## Don't use this proc for serialization, the representation may change at
|
||||
## any moment and values are not escaped. Example:
|
||||
##
|
||||
## Example:
|
||||
##
|
||||
## .. code-block::
|
||||
## echo toOrderedSet([2, 4, 5])
|
||||
## # --> {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()
|
||||
|
||||
proc `<`*[A](s, t: TSet[A]): bool =
|
||||
## Is s a strict subset of t?
|
||||
s.counter != t.counter and s <= t
|
||||
proc testModule() =
|
||||
## Internal micro test to validate docstrings and such.
|
||||
block isValidTest:
|
||||
var options: TSet[string]
|
||||
proc savePreferences(options: TSet[string]) =
|
||||
assert options.isValid, "Pass an initialized set!"
|
||||
options = initSet[string]()
|
||||
options.savePreferences
|
||||
|
||||
proc `<=`*[A](s, t: TSet[A]): bool =
|
||||
## Is s a subset of t?
|
||||
result = false
|
||||
if s.counter > t.counter: return
|
||||
result = true
|
||||
for item in s:
|
||||
if not(t.contains(item)):
|
||||
result = false
|
||||
return
|
||||
|
||||
proc `==`*[A](s, t: TSet[A]): bool =
|
||||
s.counter == t.counter and s <= t
|
||||
block lenTest:
|
||||
var values: TSet[int]
|
||||
assert(not values.isValid)
|
||||
assert values.len == 0
|
||||
assert values.card == 0
|
||||
|
||||
proc map*[A, B](data: TSet[A], op: proc (x: A): B {.closure.}): TSet[B] =
|
||||
result = initSet[B]()
|
||||
for item in data: result.incl(op(item))
|
||||
block setIterator:
|
||||
type pair = tuple[a, b: int]
|
||||
var a, b = initSet[pair]()
|
||||
a.incl((2, 3))
|
||||
a.incl((3, 2))
|
||||
a.incl((2, 3))
|
||||
for x, y in a.items:
|
||||
b.incl((x - 2, y + 1))
|
||||
assert a.len == b.card
|
||||
assert a.len == 2
|
||||
#echo b
|
||||
|
||||
block setContains:
|
||||
var values = initSet[int]()
|
||||
assert(not values.contains(2))
|
||||
values.incl(2)
|
||||
assert values.contains(2)
|
||||
values.excl(2)
|
||||
assert(not values.contains(2))
|
||||
|
||||
values.incl(4)
|
||||
var others = toSet([6, 7])
|
||||
values.incl(others)
|
||||
assert values.len == 3
|
||||
|
||||
values.init
|
||||
assert values.containsOrIncl(2) == false
|
||||
assert values.containsOrIncl(2) == true
|
||||
var
|
||||
a = toSet([1, 2])
|
||||
b = toSet([1])
|
||||
b.incl(2)
|
||||
assert a == b
|
||||
|
||||
block exclusions:
|
||||
var s = toSet([2, 3, 6, 7])
|
||||
s.excl(2)
|
||||
s.excl(2)
|
||||
assert s.len == 3
|
||||
|
||||
var
|
||||
numbers = toSet([1, 2, 3, 4, 5])
|
||||
even = toSet([2, 4, 6, 8])
|
||||
numbers.excl(even)
|
||||
#echo numbers
|
||||
# --> {1, 3, 5}
|
||||
|
||||
block toSeqAndString:
|
||||
var a = toSet([2, 4, 5])
|
||||
var b = initSet[int]()
|
||||
for x in [2, 4, 5]: b.incl(x)
|
||||
assert($a == $b)
|
||||
#echo a
|
||||
#echo toSet(["no", "esc'aping", "is \" provided"])
|
||||
|
||||
#block orderedToSeqAndString:
|
||||
# echo toOrderedSet([2, 4, 5])
|
||||
# echo toOrderedSet(["no", "esc'aping", "is \" provided"])
|
||||
|
||||
block setOperations:
|
||||
var
|
||||
a = toSet(["a", "b"])
|
||||
b = toSet(["b", "c"])
|
||||
c = union(a, b)
|
||||
assert c == toSet(["a", "b", "c"])
|
||||
var d = intersection(a, b)
|
||||
assert d == toSet(["b"])
|
||||
var e = difference(a, b)
|
||||
assert e == toSet(["a"])
|
||||
var f = symmetricDifference(a, b)
|
||||
assert f == toSet(["a", "c"])
|
||||
assert d < a and d < b
|
||||
assert((a < a) == false)
|
||||
assert d <= a and d <= b
|
||||
assert((a <= a))
|
||||
# Alias test.
|
||||
assert a + b == toSet(["a", "b", "c"])
|
||||
assert a * b == toSet(["b"])
|
||||
assert a - b == toSet(["a"])
|
||||
assert a -+- b == toSet(["a", "c"])
|
||||
assert disjoint(a, b) == false
|
||||
assert disjoint(a, b - a) == true
|
||||
|
||||
block mapSet:
|
||||
var a = toSet([1, 2, 3])
|
||||
var b = a.map(proc (x: int): string = $x)
|
||||
assert b == toSet(["1", "2", "3"])
|
||||
|
||||
block isValidTest:
|
||||
var cards: TOrderedSet[string]
|
||||
proc saveTarotCards(cards: TOrderedSet[string]) =
|
||||
assert cards.isValid, "Pass an initialized set!"
|
||||
cards = initOrderedSet[string]()
|
||||
cards.saveTarotCards
|
||||
|
||||
block lenTest:
|
||||
var values: TOrderedSet[int]
|
||||
assert(not values.isValid)
|
||||
assert values.len == 0
|
||||
assert values.card == 0
|
||||
|
||||
block setIterator:
|
||||
type pair = tuple[a, b: int]
|
||||
var a, b = initOrderedSet[pair]()
|
||||
a.incl((2, 3))
|
||||
a.incl((3, 2))
|
||||
a.incl((2, 3))
|
||||
for x, y in a.items:
|
||||
b.incl((x - 2, y + 1))
|
||||
assert a.len == b.card
|
||||
assert a.len == 2
|
||||
|
||||
#block orderedSetIterator:
|
||||
# var a = initOrderedSet[int]()
|
||||
# for value in [9, 2, 1, 5, 1, 8, 4, 2]:
|
||||
# a.incl(value)
|
||||
# for value in a.items:
|
||||
# echo "Got ", value
|
||||
|
||||
block setContains:
|
||||
var values = initOrderedSet[int]()
|
||||
assert(not values.contains(2))
|
||||
values.incl(2)
|
||||
assert values.contains(2)
|
||||
|
||||
block toSeqAndString:
|
||||
var a = toOrderedSet([2, 4, 5])
|
||||
var b = initOrderedSet[int]()
|
||||
for x in [2, 4, 5]: b.incl(x)
|
||||
assert($a == $b)
|
||||
# assert(a == b) # https://github.com/Araq/Nimrod/issues/1413
|
||||
|
||||
block initBlocks:
|
||||
var a: TOrderedSet[int]
|
||||
a.init(4)
|
||||
a.incl(2)
|
||||
a.init
|
||||
assert a.len == 0 and a.isValid
|
||||
a = initOrderedSet[int](4)
|
||||
a.incl(2)
|
||||
assert a.len == 1
|
||||
|
||||
var b: TSet[int]
|
||||
b.init(4)
|
||||
b.incl(2)
|
||||
b.init
|
||||
assert b.len == 0 and b.isValid
|
||||
b = initSet[int](4)
|
||||
b.incl(2)
|
||||
assert b.len == 1
|
||||
|
||||
echo "Micro tests run successfully."
|
||||
|
||||
when isMainModule and not defined(release): testModule()
|
||||
|
||||
@@ -246,7 +246,8 @@ template equalsImpl() =
|
||||
# different insertion orders mean different 'data' seqs, so we have
|
||||
# to use the slow route here:
|
||||
for key, val in s:
|
||||
if not hasKey(t, key): return false
|
||||
# prefix notation leads to automatic dereference in case of PTable
|
||||
if not t.hasKey(key): return false
|
||||
if t[key] != val: return false
|
||||
return true
|
||||
|
||||
@@ -332,7 +333,9 @@ proc `$`*[A, B](t: PTable[A, B]): string =
|
||||
dollarImpl()
|
||||
|
||||
proc `==`*[A, B](s, t: PTable[A, B]): bool =
|
||||
equalsImpl()
|
||||
if isNil(s): result = isNil(t)
|
||||
elif isNil(t): result = false
|
||||
else: result = equalsImpl()
|
||||
|
||||
proc newTableFrom*[A, B, C](collection: A, index: proc(x: B): C): PTable[C, B] =
|
||||
## Index the collection with the proc provided.
|
||||
|
||||
@@ -621,9 +621,13 @@ proc `%`*(elements: openArray[PJsonNode]): PJsonNode =
|
||||
|
||||
proc `==`* (a,b: PJsonNode): bool =
|
||||
## Check two nodes for equality
|
||||
if a.kind != b.kind: false
|
||||
if a.isNil:
|
||||
if b.isNil: return true
|
||||
return false
|
||||
elif b.isNil or a.kind != b.kind:
|
||||
return false
|
||||
else:
|
||||
case a.kind
|
||||
return case a.kind
|
||||
of JString:
|
||||
a.str == b.str
|
||||
of JInt:
|
||||
|
||||
@@ -955,11 +955,12 @@ proc copyFile*(source, dest: string) {.rtl, extern: "nos$1",
|
||||
##
|
||||
## If this fails, `EOS` is raised. On the Windows platform this proc will
|
||||
## copy the source file's attributes into dest. On other platforms you need
|
||||
## to use getFilePermissions and setFilePermissions to copy them by hand (or
|
||||
## use the convenience copyFileWithPermissions() proc), otherwise `dest` will
|
||||
## inherit the default permissions of a newly created file for the user. If
|
||||
## `dest` already exists, the file attributes will be preserved and the
|
||||
## content overwritten.
|
||||
## to use `getFilePermissions() <#getFilePermissions>`_ and
|
||||
## `setFilePermissions() <#setFilePermissions>`_ to copy them by hand (or use
|
||||
## the convenience `copyFileWithPermissions() <#copyFileWithPermissions>`_
|
||||
## proc), otherwise `dest` will inherit the default permissions of a newly
|
||||
## created file for the user. If `dest` already exists, the file attributes
|
||||
## will be preserved and the content overwritten.
|
||||
when defined(Windows):
|
||||
when useWinUnicode:
|
||||
let s = newWideCString(source)
|
||||
@@ -1363,7 +1364,13 @@ proc createDir*(dir: string) {.rtl, extern: "nos$1", tags: [FWriteDir].} =
|
||||
|
||||
proc copyDir*(source, dest: string) {.rtl, extern: "nos$1",
|
||||
tags: [FWriteIO, FReadIO].} =
|
||||
## Copies a directory from `source` to `dest`. If this fails, `EOS` is raised.
|
||||
## Copies a directory from `source` to `dest`.
|
||||
##
|
||||
## If this fails, `EOS` is raised. On the Windows platform this proc will
|
||||
## copy the attributes from `source` into `dest`. On other platforms created
|
||||
## files and directories will inherit the default permissions of a newly
|
||||
## created file/directory for the user. To preserve attributes recursively on
|
||||
## these platforms use `copyDirWithPermissions() <#copyDirWithPermissions>`_.
|
||||
createDir(dest)
|
||||
for kind, path in walkDir(source):
|
||||
var noSource = path.substr(source.len()+1)
|
||||
@@ -1507,14 +1514,17 @@ proc copyFileWithPermissions*(source, dest: string,
|
||||
ignorePermissionErrors = true) =
|
||||
## Copies a file from `source` to `dest` preserving file permissions.
|
||||
##
|
||||
## This is a wrapper proc around copyFile, getFilePermissions and
|
||||
## setFilePermissions on non Windows platform. On windows this proc is just a
|
||||
## wrapper for copyFile since that proc already copies attributes.
|
||||
## This is a wrapper proc around `copyFile() <#copyFile>`_,
|
||||
## `getFilePermissions() <#getFilePermissions>`_ and `setFilePermissions()
|
||||
## <#setFilePermissions>`_ on non Windows platform. On Windows this proc is
|
||||
## just a wrapper for `copyFile() <#copyFile>`_ since that proc already
|
||||
## copies attributes.
|
||||
##
|
||||
## On non windows systems permissions are copied after the file itself has
|
||||
## On non Windows systems permissions are copied after the file itself has
|
||||
## been copied, which won't happen atomically and could lead to a race
|
||||
## condition. If ignorePermissionErrors is true, errors while reading/setting
|
||||
## file attributes will be ignored, otherwise will raise `OSError`.
|
||||
## condition. If `ignorePermissionErrors` is true, errors while
|
||||
## reading/setting file attributes will be ignored, otherwise will raise
|
||||
## `OSError`.
|
||||
copyFile(source, dest)
|
||||
when not defined(Windows):
|
||||
try:
|
||||
@@ -1523,6 +1533,37 @@ proc copyFileWithPermissions*(source, dest: string,
|
||||
if not ignorePermissionErrors:
|
||||
raise
|
||||
|
||||
proc copyDirWithPermissions*(source, dest: string,
|
||||
ignorePermissionErrors = true) {.rtl, extern: "nos$1",
|
||||
tags: [FWriteIO, FReadIO].} =
|
||||
## Copies a directory from `source` to `dest` preserving file permissions.
|
||||
##
|
||||
## If this fails, `EOS` is raised. This is a wrapper proc around `copyDir()
|
||||
## <#copyDir>`_ and `copyFileWithPermissions() <#copyFileWithPermissions>`_
|
||||
## on non Windows platforms. On Windows this proc is just a wrapper for
|
||||
## `copyDir() <#copyDir>`_ since that proc already copies attributes.
|
||||
##
|
||||
## On non Windows systems permissions are copied after the file or directory
|
||||
## itself has been copied, which won't happen atomically and could lead to a
|
||||
## race condition. If `ignorePermissionErrors` is true, errors while
|
||||
## reading/setting file attributes will be ignored, otherwise will raise
|
||||
## `OSError`.
|
||||
createDir(dest)
|
||||
when not defined(Windows):
|
||||
try:
|
||||
setFilePermissions(dest, getFilePermissions(source))
|
||||
except:
|
||||
if not ignorePermissionErrors:
|
||||
raise
|
||||
for kind, path in walkDir(source):
|
||||
var noSource = path.substr(source.len()+1)
|
||||
case kind
|
||||
of pcFile:
|
||||
copyFileWithPermissions(path, dest / noSource, ignorePermissionErrors)
|
||||
of pcDir:
|
||||
copyDirWithPermissions(path, dest / noSource, ignorePermissionErrors)
|
||||
else: discard
|
||||
|
||||
proc inclFilePermissions*(filename: string,
|
||||
permissions: set[TFilePermission]) {.
|
||||
rtl, extern: "nos$1", tags: [FReadDir, FWriteDir].} =
|
||||
|
||||
@@ -763,7 +763,7 @@ elif not defined(useNimRtl):
|
||||
discard write(data.pErrorPipe[writeIdx], addr error, sizeof(error))
|
||||
exitnow(1)
|
||||
|
||||
when defined(macosx):
|
||||
when defined(macosx) or defined(freebsd):
|
||||
var environ {.importc.}: cstringArray
|
||||
|
||||
proc startProcessAfterFork(data: ptr TStartProcessData) =
|
||||
@@ -793,7 +793,7 @@ elif not defined(useNimRtl):
|
||||
discard fcntl(data.pErrorPipe[writeIdx], F_SETFD, FD_CLOEXEC)
|
||||
|
||||
if data.optionPoUsePath:
|
||||
when defined(macosx):
|
||||
when defined(macosx) or defined(freebsd):
|
||||
# MacOSX doesn't have execvpe, so we need workaround.
|
||||
# On MacOSX we can arrive here only from fork, so this is safe:
|
||||
environ = data.sysEnv
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -2031,7 +2031,7 @@ proc echo*[T](x: varargs[T, `$`]) {.magic: "Echo", tags: [FWriteIO], gcsafe.}
|
||||
## Special built-in that takes a variable number of arguments. Each argument
|
||||
## is converted to a string via ``$``, so it works for user-defined
|
||||
## types that have an overloaded ``$`` operator.
|
||||
## It is roughly equivalent to ``writeln(stdout, x); flush(stdout)``, but
|
||||
## It is roughly equivalent to ``writeln(stdout, x); flushFile(stdout)``, but
|
||||
## available for the JavaScript target too.
|
||||
##
|
||||
## Unlike other IO operations this is guaranteed to be thread-safe as
|
||||
|
||||
@@ -104,6 +104,15 @@ block countTableTest1:
|
||||
block SyntaxTest:
|
||||
var x = newTable[int, string]({:})
|
||||
|
||||
block nilTest:
|
||||
var i, j: PTable[int, int] = nil
|
||||
assert i == j
|
||||
j = newTable[int, int]()
|
||||
assert i != j
|
||||
assert j != i
|
||||
i = newTable[int, int]()
|
||||
assert i == j
|
||||
|
||||
proc orderedTableSortTest() =
|
||||
var t = newOrderedTable[string, int](2)
|
||||
for key, val in items(data): t[key] = val
|
||||
|
||||
@@ -24,7 +24,7 @@ proc execCode(code: string): string =
|
||||
f.close()
|
||||
result = osproc.execProcess(
|
||||
"$# $# --verbosity:0 --hint[Conf]:off temp.nim" % [nimExe, runCmd],
|
||||
{poStdErrToStdOut})
|
||||
options = {poStdErrToStdOut})
|
||||
else:
|
||||
result = "cannot open file 'temp.nim'"
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@ proc walker(dir: string) =
|
||||
else:
|
||||
echo "Required: ", path
|
||||
# copy back:
|
||||
moveFile(dest=path, sourc=newName(path))
|
||||
moveFile(dest=path, source=newName(path))
|
||||
of pcDir:
|
||||
walker(path)
|
||||
else: discard
|
||||
|
||||
@@ -21,7 +21,7 @@ proc processContent(content: string) =
|
||||
var jsonArr = jsonDoc.elems
|
||||
|
||||
jsonArr.sort do (x, y: PJsonNode) -> int:
|
||||
system.cmp(x["name"].str, y["name"].str)
|
||||
strutils.cmpIgnoreCase(x["name"].str, y["name"].str)
|
||||
|
||||
var
|
||||
officialList = ""
|
||||
@@ -38,8 +38,7 @@ proc processContent(content: string) =
|
||||
else: pkg["url"].str
|
||||
let
|
||||
desc = pkg["description"].str
|
||||
# Review array index access when #1291 is solved.
|
||||
dot = if desc.high > 0 and desc[<desc.high] in endings: "" else: "."
|
||||
dot = if desc.high > 0 and desc[desc.high] in endings: "" else: "."
|
||||
listItem = li(a(href=pkgWeb, pkg["name"].str), " ", desc & dot)
|
||||
if pkg["url"].str.startsWith("git://github.com/nimrod-code") or
|
||||
"official" in pkg["tags"].elems:
|
||||
@@ -51,19 +50,17 @@ proc processContent(content: string) =
|
||||
|
||||
var officialPkgListDiv = document.getElementById("officialPkgList")
|
||||
|
||||
officialPkgListDiv.innerHTML.add(
|
||||
officialPkgListDiv.innerHTML =
|
||||
p("There are currently " & $officialCount &
|
||||
" official packages in the Babel package repository.") &
|
||||
ul(officialList)
|
||||
)
|
||||
|
||||
var unofficialPkgListDiv = document.getElementById("unofficialPkgList")
|
||||
|
||||
unofficialPkgListDiv.innerHTML.add(
|
||||
unofficialPkgListDiv.innerHTML =
|
||||
p("There are currently " & $unofficialCount &
|
||||
" unofficial packages in the Babel package repository.") &
|
||||
ul(unofficialList)
|
||||
)
|
||||
|
||||
proc gotPackageList(apiReply: TData) {.exportc.} =
|
||||
let decoded = decodeContent($apiReply.content)
|
||||
|
||||
Reference in New Issue
Block a user