Merge branch 'devel' of https://github.com/Araq/Nimrod into devel

Conflicts:
	compiler/ccgexprs.nim
This commit is contained in:
Araq
2014-08-05 21:53:26 +02:00
28 changed files with 1125 additions and 389 deletions

View File

@@ -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:

View File

@@ -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)

View File

@@ -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)

View File

@@ -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 =

View File

@@ -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

View File

@@ -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.}) =

View File

@@ -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, "")

View File

@@ -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

View File

@@ -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>

View File

@@ -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
---------------------------

View File

@@ -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
View 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.

View File

@@ -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
------

View File

@@ -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)

View File

@@ -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)

View File

@@ -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.

View File

@@ -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

View File

@@ -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()

View File

@@ -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.

View File

@@ -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:

View File

@@ -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].} =

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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'"

View File

@@ -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

View File

@@ -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)