diff options
author | Andreas Rumpf <rumpf_a@web.de> | 2018-08-19 15:14:03 +0200 |
---|---|---|
committer | Andreas Rumpf <rumpf_a@web.de> | 2018-08-19 15:14:03 +0200 |
commit | 696c0c48c7c794453b79d2abf45c3f390a9b6fba (patch) | |
tree | 8a74e09ce97f64ea0e3377f6440a2e79d54ce0fd /lib | |
parent | 7ac6462cbd30bcdb1c3805fbb06be13b3346ce2a (diff) | |
parent | f2263cd129ff41259db99c68e98f966a681adf78 (diff) | |
download | Nim-696c0c48c7c794453b79d2abf45c3f390a9b6fba.tar.gz |
fixes merge conflict
Diffstat (limited to 'lib')
61 files changed, 879 insertions, 592 deletions
diff --git a/lib/core/macros.nim b/lib/core/macros.nim index d4e8ada0a..90fea440e 100644 --- a/lib/core/macros.nim +++ b/lib/core/macros.nim @@ -432,6 +432,9 @@ proc getLine(arg: NimNode): int {.magic: "NLineInfo", noSideEffect.} proc getColumn(arg: NimNode): int {.magic: "NLineInfo", noSideEffect.} proc getFile(arg: NimNode): string {.magic: "NLineInfo", noSideEffect.} +proc copyLineInfo*(arg: NimNode, info: NimNode) {.magic: "NLineInfo", noSideEffect.} + ## copy lineinfo from info node + proc lineInfoObj*(n: NimNode): LineInfo {.compileTime.} = ## returns ``LineInfo`` of ``n``, using absolute path for ``filename`` result.filename = n.getFile diff --git a/lib/deprecated/pure/actors.nim b/lib/deprecated/pure/actors.nim index 17321cc0e..451668825 100644 --- a/lib/deprecated/pure/actors.nim +++ b/lib/deprecated/pure/actors.nim @@ -43,7 +43,6 @@ type t: Thread[ptr Actor[In, Out]] PActor*[In, Out] = ptr Actor[In, Out] ## an actor -{.deprecated: [TTask: Task, TActor: Actor].} proc spawn*[In, Out](action: proc( self: PActor[In, Out]){.thread.}): PActor[In, Out] = @@ -168,7 +167,7 @@ proc terminate*[In, Out](a: var ActorPool[In, Out]) = for i in 0..<a.actors.len: join(a.actors[i]) when Out isnot void: close(a.outputs) - a.actors = nil + a.actors = @[] proc join*[In, Out](a: var ActorPool[In, Out]) = ## short-cut for `sync` and then `terminate`. diff --git a/lib/deprecated/pure/sockets.nim b/lib/deprecated/pure/sockets.nim index 05aebef76..76a9044d8 100644 --- a/lib/deprecated/pure/sockets.nim +++ b/lib/deprecated/pure/sockets.nim @@ -36,6 +36,8 @@ include "system/inclrtl" when hostOS == "solaris": {.passl: "-lsocket -lnsl".} +elif hostOS == "haiku": + {.passl: "-lnetwork".} import os, parseutils from times import epochTime diff --git a/lib/impure/db_mysql.nim b/lib/impure/db_mysql.nim index ca0e29d11..26bc7d0ad 100644 --- a/lib/impure/db_mysql.nim +++ b/lib/impure/db_mysql.nim @@ -128,10 +128,7 @@ proc dbFormat(formatstr: SqlQuery, args: varargs[string]): string = var a = 0 for c in items(string(formatstr)): if c == '?': - if args[a].isNil: - add(result, "NULL") - else: - add(result, dbQuote(args[a])) + add(result, dbQuote(args[a])) inc(a) else: add(result, c) @@ -183,24 +180,8 @@ iterator fastRows*(db: DbConn, query: SqlQuery, row = mysql.fetchRow(sqlres) if row == nil: break for i in 0..L-1: - if row[i] == nil: - if backup == nil: - newSeq(backup, L) - if backup[i] == nil and result[i] != nil: - shallowCopy(backup[i], result[i]) - result[i] = nil - else: - if result[i] == nil: - if backup != nil: - if backup[i] == nil: - backup[i] = "" - shallowCopy(result[i], backup[i]) - setLen(result[i], 0) - else: - result[i] = "" - else: - setLen(result[i], 0) - add(result[i], row[i]) + setLen(result[i], 0) + result[i].add row[i] yield result properFreeResult(sqlres, row) @@ -323,10 +304,7 @@ proc getRow*(db: DbConn, query: SqlQuery, if row != nil: for i in 0..L-1: setLen(result[i], 0) - if row[i] == nil: - result[i] = nil - else: - add(result[i], row[i]) + add(result[i], row[i]) properFreeResult(sqlres, row) proc getAllRows*(db: DbConn, query: SqlQuery, @@ -345,10 +323,7 @@ proc getAllRows*(db: DbConn, query: SqlQuery, setLen(result, j+1) newSeq(result[j], L) for i in 0..L-1: - if row[i] == nil: - result[j][i] = nil - else: - result[j][i] = $row[i] + result[j][i] = $row[i] inc(j) mysql.freeResult(sqlres) diff --git a/lib/impure/db_postgres.nim b/lib/impure/db_postgres.nim index 1459f0d7e..e765cc197 100644 --- a/lib/impure/db_postgres.nim +++ b/lib/impure/db_postgres.nim @@ -103,10 +103,7 @@ proc dbFormat(formatstr: SqlQuery, args: varargs[string]): string = else: for c in items(string(formatstr)): if c == '?': - if args[a] == nil: - add(result, "NULL") - else: - add(result, dbQuote(args[a])) + add(result, dbQuote(args[a])) inc(a) else: add(result, c) @@ -179,7 +176,7 @@ proc setRow(res: PPGresult, r: var Row, line, cols: int32) = setLen(r[col], 0) let x = pqgetvalue(res, line, col) if x.isNil: - r[col] = nil + r[col] = "" else: add(r[col], x) diff --git a/lib/impure/db_sqlite.nim b/lib/impure/db_sqlite.nim index fd25b2b94..a40c88a11 100644 --- a/lib/impure/db_sqlite.nim +++ b/lib/impure/db_sqlite.nim @@ -105,7 +105,6 @@ proc dbError*(db: DbConn) {.noreturn.} = proc dbQuote*(s: string): string = ## DB quotes the string. - if s.isNil: return "NULL" result = "'" for c in items(s): if c == '\'': add(result, "''") diff --git a/lib/impure/nre.nim b/lib/impure/nre.nim index 889210f62..32b1d0255 100644 --- a/lib/impure/nre.nim +++ b/lib/impure/nre.nim @@ -267,7 +267,7 @@ proc `[]`*(pattern: Captures, i: int): string = let bounds = bounds.get return pattern.str.substr(bounds.a, bounds.b) else: - return nil + return "" proc match*(pattern: RegexMatch): string = return pattern.captures[-1] @@ -291,9 +291,9 @@ template toTableImpl(cond: untyped) {.dirty.} = else: result[key] = nextVal -proc toTable*(pattern: Captures, default: string = nil): Table[string, string] = +proc toTable*(pattern: Captures, default: string = ""): Table[string, string] = result = initTable[string, string]() - toTableImpl(nextVal == nil) + toTableImpl(nextVal.len == 0) proc toTable*(pattern: CaptureBounds, default = none(HSlice[int, int])): Table[string, Option[HSlice[int, int]]] = @@ -312,13 +312,13 @@ template itemsImpl(cond: untyped) {.dirty.} = iterator items*(pattern: CaptureBounds, default = none(HSlice[int, int])): Option[HSlice[int, int]] = itemsImpl(nextVal.isNone) -iterator items*(pattern: Captures, default: string = nil): string = - itemsImpl(nextVal == nil) +iterator items*(pattern: Captures, default: string = ""): string = + itemsImpl(nextVal.len == 0) proc toSeq*(pattern: CaptureBounds, default = none(HSlice[int, int])): seq[Option[HSlice[int, int]]] = accumulateResult(pattern.items(default)) -proc toSeq*(pattern: Captures, default: string = nil): seq[string] = +proc toSeq*(pattern: Captures, default: string = ""): seq[string] = accumulateResult(pattern.items(default)) proc `$`*(pattern: RegexMatch): string = diff --git a/lib/impure/nre/private/util.nim b/lib/impure/nre/private/util.nim index 12d2506ea..a3ae84007 100644 --- a/lib/impure/nre/private/util.nim +++ b/lib/impure/nre/private/util.nim @@ -10,11 +10,7 @@ proc fget*[K, V](self: Table[K, V], key: K): V = const Ident = {'a'..'z', 'A'..'Z', '0'..'9', '_', '\128'..'\255'} const StartIdent = Ident - {'0'..'9'} -proc checkNil(arg: string): string = - if arg == nil: - raise newException(ValueError, "Cannot use nil capture") - else: - return arg +template checkNil(arg: string): string = arg template formatStr*(howExpr, namegetter, idgetter): untyped = let how = howExpr diff --git a/lib/impure/re.nim b/lib/impure/re.nim index 201c490f3..a60f70828 100644 --- a/lib/impure/re.nim +++ b/lib/impure/re.nim @@ -113,7 +113,7 @@ proc matchOrFind(buf: cstring, pattern: Regex, matches: var openArray[string], var b = rawMatches[i * 2 + 1] if a >= 0'i32: matches[i-1] = bufSubstr(buf, int(a), int(b)) - else: matches[i-1] = nil + else: matches[i-1] = "" return rawMatches[1] - rawMatches[0] proc findBounds*(buf: cstring, pattern: Regex, matches: var openArray[string], @@ -133,7 +133,7 @@ proc findBounds*(buf: cstring, pattern: Regex, matches: var openArray[string], var a = rawMatches[i * 2] var b = rawMatches[i * 2 + 1] if a >= 0'i32: matches[i-1] = bufSubstr(buf, int(a), int(b)) - else: matches[i-1] = nil + else: matches[i-1] = "" return (rawMatches[0].int, rawMatches[1].int - 1) proc findBounds*(s: string, pattern: Regex, matches: var openArray[string], @@ -287,7 +287,7 @@ proc find*(buf: cstring, pattern: Regex, matches: var openArray[string], var a = rawMatches[i * 2] var b = rawMatches[i * 2 + 1] if a >= 0'i32: matches[i-1] = bufSubstr(buf, int(a), int(b)) - else: matches[i-1] = nil + else: matches[i-1] = "" return rawMatches[0] proc find*(s: string, pattern: Regex, matches: var openArray[string], @@ -456,8 +456,6 @@ proc replacef*(s: string, sub: Regex, by: string): string = while true: var match = findBounds(s, sub, caps, prev) if match.first < 0: break - assert result != nil - assert s != nil add(result, substr(s, prev, match.first-1)) addf(result, by, caps) prev = match.last + 1 @@ -615,7 +613,7 @@ when isMainModule: doAssert false if "abc" =~ re"(cba)?.*": - doAssert matches[0] == nil + doAssert matches[0] == "" else: doAssert false if "abc" =~ re"().*": diff --git a/lib/js/jscore.nim b/lib/js/jscore.nim index 91f3ff8bb..bf64b0794 100644 --- a/lib/js/jscore.nim +++ b/lib/js/jscore.nim @@ -1,3 +1,12 @@ +# +# +# Nim's Runtime Library +# (c) Copyright 2018 Nim contributors +# +# See the file "copying.txt", included in this +# distribution, for details about the copyright. +# + ## This module wraps core JavaScript functions. ## ## Unless your application has very @@ -19,49 +28,47 @@ var Date* {.importc, nodecl.}: DateLib JSON* {.importc, nodecl.}: JsonLib -{.push importcpp.} - # Math library -proc abs*(m: MathLib, a: SomeNumber): SomeNumber -proc acos*(m: MathLib, a: SomeNumber): float -proc acosh*(m: MathLib, a: SomeNumber): float -proc asin*(m: MathLib, a: SomeNumber): float -proc asinh*(m: MathLib, a: SomeNumber): float -proc atan*(m: MathLib, a: SomeNumber): float -proc atan2*(m: MathLib, a: SomeNumber): float -proc atanh*(m: MathLib, a: SomeNumber): float -proc cbrt*(m: MathLib, f: SomeFloat): SomeFloat -proc ceil*(m: MathLib, f: SomeFloat): SomeFloat -proc clz32*(m: MathLib, f: SomeInteger): int -proc cos*(m: MathLib, a: SomeNumber): float -proc cosh*(m: MathLib, a: SomeNumber): float -proc exp*(m: MathLib, a: SomeNumber): float -proc expm1*(m: MathLib, a: SomeNumber): float -proc floor*(m: MathLib, f: SomeFloat): int -proc fround*(m: MathLib, f: SomeFloat): float32 -proc hypot*(m: MathLib, args: varargs[distinct SomeNumber]): float -proc imul*(m: MathLib, a, b: int32): int32 -proc log*(m: MathLib, a: SomeNumber): float -proc log10*(m: MathLib, a: SomeNumber): float -proc log1p*(m: MathLib, a: SomeNumber): float -proc log2*(m: MathLib, a: SomeNumber): float -proc max*(m: MathLib, a, b: SomeNumber): SomeNumber -proc min*[T: SomeNumber | JsRoot](m: MathLib, a, b: T): T -proc pow*(m: MathLib, a, b: distinct SomeNumber): float -proc random*(m: MathLib): float -proc round*(m: MathLib, f: SomeFloat): int -proc sign*(m: MathLib, f: SomeNumber): int -proc sin*(m: MathLib, a: SomeNumber): float -proc sinh*(m: MathLib, a: SomeNumber): float -proc sqrt*(m: MathLib, f: SomeFloat): SomeFloat -proc tan*(m: MathLib, a: SomeNumber): float -proc tanh*(m: MathLib, a: SomeNumber): float -proc trunc*(m: MathLib, f: SomeFloat): int +proc abs*(m: MathLib, a: SomeNumber): SomeNumber {.importcpp.} +proc acos*(m: MathLib, a: SomeNumber): float {.importcpp.} +proc acosh*(m: MathLib, a: SomeNumber): float {.importcpp.} +proc asin*(m: MathLib, a: SomeNumber): float {.importcpp.} +proc asinh*(m: MathLib, a: SomeNumber): float {.importcpp.} +proc atan*(m: MathLib, a: SomeNumber): float {.importcpp.} +proc atan2*(m: MathLib, a: SomeNumber): float {.importcpp.} +proc atanh*(m: MathLib, a: SomeNumber): float {.importcpp.} +proc cbrt*(m: MathLib, f: SomeFloat): SomeFloat {.importcpp.} +proc ceil*(m: MathLib, f: SomeFloat): SomeFloat {.importcpp.} +proc clz32*(m: MathLib, f: SomeInteger): int {.importcpp.} +proc cos*(m: MathLib, a: SomeNumber): float {.importcpp.} +proc cosh*(m: MathLib, a: SomeNumber): float {.importcpp.} +proc exp*(m: MathLib, a: SomeNumber): float {.importcpp.} +proc expm1*(m: MathLib, a: SomeNumber): float {.importcpp.} +proc floor*(m: MathLib, f: SomeFloat): int {.importcpp.} +proc fround*(m: MathLib, f: SomeFloat): float32 {.importcpp.} +proc hypot*(m: MathLib, args: varargs[distinct SomeNumber]): float {.importcpp.} +proc imul*(m: MathLib, a, b: int32): int32 {.importcpp.} +proc log*(m: MathLib, a: SomeNumber): float {.importcpp.} +proc log10*(m: MathLib, a: SomeNumber): float {.importcpp.} +proc log1p*(m: MathLib, a: SomeNumber): float {.importcpp.} +proc log2*(m: MathLib, a: SomeNumber): float {.importcpp.} +proc max*(m: MathLib, a, b: SomeNumber): SomeNumber {.importcpp.} +proc min*[T: SomeNumber | JsRoot](m: MathLib, a, b: T): T {.importcpp.} +proc pow*(m: MathLib, a, b: distinct SomeNumber): float {.importcpp.} +proc random*(m: MathLib): float {.importcpp.} +proc round*(m: MathLib, f: SomeFloat): int {.importcpp.} +proc sign*(m: MathLib, f: SomeNumber): int {.importcpp.} +proc sin*(m: MathLib, a: SomeNumber): float {.importcpp.} +proc sinh*(m: MathLib, a: SomeNumber): float {.importcpp.} +proc sqrt*(m: MathLib, f: SomeFloat): SomeFloat {.importcpp.} +proc tan*(m: MathLib, a: SomeNumber): float {.importcpp.} +proc tanh*(m: MathLib, a: SomeNumber): float {.importcpp.} +proc trunc*(m: MathLib, f: SomeFloat): int {.importcpp.} # Date library -proc now*(d: DateLib): int -proc UTC*(d: DateLib): int -proc parse*(d: DateLib, s: cstring): int +proc now*(d: DateLib): int {.importcpp.} +proc UTC*(d: DateLib): int {.importcpp.} +proc parse*(d: DateLib, s: cstring): int {.importcpp.} proc newDate*(): DateTime {. importcpp: "new Date()".} @@ -73,19 +80,17 @@ proc newDate*(year, month, day, hours, minutes, seconds, milliseconds: int): DateTime {. importcpp: "new Date(#,#,#,#,#,#,#)".} -proc getDay*(d: DateTime): int -proc getFullYear*(d: DateTime): int -proc getHours*(d: DateTime): int -proc getMilliseconds*(d: DateTime): int -proc getMinutes*(d: DateTime): int -proc getMonth*(d: DateTime): int -proc getSeconds*(d: DateTime): int -proc getYear*(d: DateTime): int -proc getTime*(d: DateTime): int -proc toString*(d: DateTime): cstring +proc getDay*(d: DateTime): int {.importcpp.} +proc getFullYear*(d: DateTime): int {.importcpp.} +proc getHours*(d: DateTime): int {.importcpp.} +proc getMilliseconds*(d: DateTime): int {.importcpp.} +proc getMinutes*(d: DateTime): int {.importcpp.} +proc getMonth*(d: DateTime): int {.importcpp.} +proc getSeconds*(d: DateTime): int {.importcpp.} +proc getYear*(d: DateTime): int {.importcpp.} +proc getTime*(d: DateTime): int {.importcpp.} +proc toString*(d: DateTime): cstring {.importcpp.} #JSON library -proc stringify*(l: JsonLib, s: JsRoot): cstring -proc parse*(l: JsonLib, s: cstring): JsRoot - -{.pop.} +proc stringify*(l: JsonLib, s: JsRoot): cstring {.importcpp.} +proc parse*(l: JsonLib, s: cstring): JsRoot {.importcpp.} diff --git a/lib/nimbase.h b/lib/nimbase.h index 6dc742910..84972fcb0 100644 --- a/lib/nimbase.h +++ b/lib/nimbase.h @@ -129,13 +129,13 @@ __clang__ defined __DMC__ || \ defined __BORLANDC__ ) # define NIM_THREADVAR __declspec(thread) +#elif defined(__TINYC__) || defined(__GENODE__) +# define NIM_THREADVAR /* note that ICC (linux) and Clang are covered by __GNUC__ */ #elif defined __GNUC__ || \ defined __SUNPRO_C || \ defined __xlC__ # define NIM_THREADVAR __thread -#elif defined __TINYC__ -# define NIM_THREADVAR #else # error "Cannot define NIM_THREADVAR" #endif diff --git a/lib/packages/docutils/rstast.nim b/lib/packages/docutils/rstast.nim index f3596b571..4a77b4f34 100644 --- a/lib/packages/docutils/rstast.nim +++ b/lib/packages/docutils/rstast.nim @@ -293,9 +293,9 @@ proc renderRstToJsonNode(node: PRstNode): JsonNode = (key: "kind", val: %($node.kind)), (key: "level", val: %BiggestInt(node.level)) ] - if node.text != nil: + if node.text.len > 0: result.add("text", %node.text) - if node.sons != nil and len(node.sons) > 0: + if len(node.sons) > 0: var accm = newSeq[JsonNode](len(node.sons)) for i, son in node.sons: accm[i] = renderRstToJsonNode(son) diff --git a/lib/packages/docutils/rstgen.nim b/lib/packages/docutils/rstgen.nim index 43a429a17..5b0b6c6ee 100644 --- a/lib/packages/docutils/rstgen.nim +++ b/lib/packages/docutils/rstgen.nim @@ -312,7 +312,6 @@ proc setIndexTerm*(d: var RstGenerator, id, term: string, ## The index won't be written to disk unless you call `writeIndexFile() ## <#writeIndexFile>`_. The purpose of the index is documented in the `docgen ## tools guide <docgen.html#index-switch>`_. - assert(not d.theIndex.isNil) var entry = term isTitle = false @@ -337,7 +336,7 @@ proc hash(n: PRstNode): int = result = hash(n.text) elif n.len > 0: result = hash(n.sons[0]) - for i in 1 .. <len(n): + for i in 1 ..< len(n): result = result !& hash(n.sons[i]) result = !$result @@ -398,9 +397,9 @@ proc hash(x: IndexEntry): Hash = proc `<-`(a: var IndexEntry, b: IndexEntry) = shallowCopy a.keyword, b.keyword shallowCopy a.link, b.link - if b.linkTitle.isNil: a.linkTitle = nil + if b.linkTitle.isNil: a.linkTitle = "" else: shallowCopy a.linkTitle, b.linkTitle - if b.linkDesc.isNil: a.linkDesc = nil + if b.linkDesc.isNil: a.linkDesc = "" else: shallowCopy a.linkDesc, b.linkDesc proc sortIndex(a: var openArray[IndexEntry]) = @@ -452,7 +451,7 @@ proc generateSymbolIndex(symbols: seq[IndexEntry]): string = title="$3" data-doc-search-tag="$2" href="$1">$2</a></li> """, [url, text, desc]) else: - result.addf("""<li><a class="reference external" + result.addf("""<li><a class="reference external" data-doc-search-tag="$2" href="$1">$2</a></li> """, [url, text]) inc j @@ -524,7 +523,7 @@ proc generateDocumentationTOC(entries: seq[IndexEntry]): string = titleTag = levels[L].text else: result.add(level.indentToLevel(levels[L].level)) - result.addf("""<li><a class="reference" data-doc-search-tag="$1" href="$2"> + result.addf("""<li><a class="reference" data-doc-search-tag="$1" href="$2"> $3</a></li> """, [titleTag & " : " & levels[L].text, link, levels[L].text]) inc L @@ -608,8 +607,8 @@ proc readIndexDir(dir: string): fileEntries[F].linkTitle = extraCols[1].unquoteIndexColumn fileEntries[F].linkDesc = extraCols[2].unquoteIndexColumn else: - fileEntries[F].linkTitle = nil - fileEntries[F].linkDesc = nil + fileEntries[F].linkTitle = "" + fileEntries[F].linkDesc = "" inc F # Depending on type add this to the list of symbols or table of APIs. if title.keyword.isNil: @@ -657,7 +656,6 @@ proc mergeIndexes*(dir: string): string = ## Returns the merged and sorted indices into a single HTML block which can ## be further embedded into nimdoc templates. var (modules, symbols, docs) = readIndexDir(dir) - assert(not symbols.isNil) result = "" # Generate a quick jump list of documents. diff --git a/lib/posix/posix_other.nim b/lib/posix/posix_other.nim index b7570bd15..99d67824e 100644 --- a/lib/posix/posix_other.nim +++ b/lib/posix/posix_other.nim @@ -10,7 +10,7 @@ {.deadCodeElim: on.} # dce option deprecated const - hasSpawnH = not defined(haiku) # should exist for every Posix system nowadays + hasSpawnH = true # should exist for every Posix system nowadays hasAioH = defined(linux) when defined(linux) and not defined(android): @@ -43,6 +43,9 @@ type Dirent* {.importc: "struct dirent", header: "<dirent.h>", final, pure.} = object ## dirent_t struct + when defined(haiku): + d_dev*: Dev ## Device (not POSIX) + d_pdev*: Dev ## Parent device (only for queries) (not POSIX) d_ino*: Ino ## File serial number. when defined(dragonfly): # DragonflyBSD doesn't have `d_reclen` field. @@ -54,6 +57,9 @@ type ## (not POSIX) when defined(linux) or defined(openbsd): d_off*: Off ## Not an offset. Value that ``telldir()`` would return. + elif defined(haiku): + d_pino*: Ino ## Parent inode (only for queries) (not POSIX) + d_reclen*: cushort ## Length of this record. (not POSIX) d_name*: array[0..255, char] ## Name of entry. @@ -599,6 +605,10 @@ else: MSG_NOSIGNAL* {.importc, header: "<sys/socket.h>".}: cint ## No SIGPIPE generated when an attempt to send is made on a stream-oriented socket that is no longer connected. +when defined(haiku): + const + SIGKILLTHR* = 21 ## BeOS specific: Kill just the thread, not team + when hasSpawnH: when defined(linux): # better be safe than sorry; Linux has this flag, macosx doesn't, don't diff --git a/lib/pure/asyncfutures.nim b/lib/pure/asyncfutures.nim index 863a6843b..5bf9183ed 100644 --- a/lib/pure/asyncfutures.nim +++ b/lib/pure/asyncfutures.nim @@ -219,10 +219,10 @@ proc getHint(entry: StackTraceEntry): string = ## We try to provide some hints about stack trace entries that the user ## may not be familiar with, in particular calls inside the stdlib. result = "" - if entry.procname == "processPendingCallbacks": + if entry.procname == cstring"processPendingCallbacks": if cmpIgnoreStyle(entry.filename, "asyncdispatch.nim") == 0: return "Executes pending callbacks" - elif entry.procname == "poll": + elif entry.procname == cstring"poll": if cmpIgnoreStyle(entry.filename, "asyncdispatch.nim") == 0: return "Processes asynchronous completion events" diff --git a/lib/pure/asyncnet.nim b/lib/pure/asyncnet.nim index e7552e3e3..71a1600dc 100644 --- a/lib/pure/asyncnet.nim +++ b/lib/pure/asyncnet.nim @@ -493,8 +493,6 @@ proc recvLineInto*(socket: AsyncSocket, resString: FutureVar[string], ## **Warning**: ``recvLineInto`` on unbuffered sockets assumes that the ## protocol uses ``\r\L`` to delimit a new line. assert SocketFlag.Peek notin flags ## TODO: - assert(not resString.mget.isNil(), - "String inside resString future needs to be initialised") result = newFuture[void]("asyncnet.recvLineInto") # TODO: Make the async transformation check for FutureVar params and complete @@ -657,7 +655,7 @@ when defineSsl: proc wrapConnectedSocket*(ctx: SslContext, socket: AsyncSocket, handshake: SslHandshakeType, - hostname: string = nil) = + hostname: string = "") = ## Wraps a connected socket in an SSL context. This function effectively ## turns ``socket`` into an SSL socket. ## ``hostname`` should be specified so that the client knows which hostname @@ -672,7 +670,7 @@ when defineSsl: case handshake of handshakeAsClient: - if not hostname.isNil and not isIpAddress(hostname): + if hostname.len > 0 and not isIpAddress(hostname): # Set the SNI address for this connection. This call can fail if # we're not using TLSv1+. discard SSL_set_tlsext_host_name(socket.sslHandle, hostname) diff --git a/lib/pure/collections/intsets.nim b/lib/pure/collections/intsets.nim index bfecfe447..545958977 100644 --- a/lib/pure/collections/intsets.nim +++ b/lib/pure/collections/intsets.nim @@ -184,7 +184,7 @@ proc missingOrExcl*(s: var IntSet, key: int) : bool = ## `key` is removed from `s` and false is returned. var count = s.elems exclImpl(s, key) - result = count == s.elems + result = count == s.elems proc containsOrIncl*(s: var IntSet, key: int): bool = ## returns true if `s` contains `key`, otherwise `key` is included in `s` @@ -212,7 +212,10 @@ proc initIntSet*: IntSet = #newSeq(result.data, InitIntSetSize) #result.max = InitIntSetSize-1 - result.data = nil + when defined(nimNoNilSeqs): + result.data = @[] + else: + result.data = nil result.max = 0 result.counter = 0 result.head = nil @@ -222,7 +225,10 @@ proc clear*(result: var IntSet) = #setLen(result.data, InitIntSetSize) #for i in 0..InitIntSetSize-1: result.data[i] = nil #result.max = InitIntSetSize-1 - result.data = nil + when defined(nimNoNilSeqs): + result.data = @[] + else: + result.data = nil result.max = 0 result.counter = 0 result.head = nil @@ -234,7 +240,10 @@ proc assign*(dest: var IntSet, src: IntSet) = ## copies `src` to `dest`. `dest` does not need to be initialized by ## `initIntSet`. if src.elems <= src.a.len: - dest.data = nil + when defined(nimNoNilSeqs): + dest.data = @[] + else: + dest.data = nil dest.max = 0 dest.counter = src.counter dest.head = nil @@ -247,11 +256,9 @@ proc assign*(dest: var IntSet, src: IntSet) = var it = src.head while it != nil: - var h = it.key and dest.max while dest.data[h] != nil: h = nextTry(h, dest.max) assert(dest.data[h] == nil) - var n: PTrunk new(n) n.next = dest.head @@ -259,7 +266,6 @@ proc assign*(dest: var IntSet, src: IntSet) = n.bits = it.bits dest.head = n dest.data[h] = n - it = it.next proc union*(s1, s2: IntSet): IntSet = @@ -315,7 +321,7 @@ proc len*(s: IntSet): int {.inline.} = for _ in s: inc(result) -proc card*(s: IntSet): int {.inline.} = +proc card*(s: IntSet): int {.inline.} = ## alias for `len() <#len>` _. result = s.len() @@ -361,7 +367,7 @@ when isMainModule: x.incl(1056) x.incl(1044) - x.excl(1044) + x.excl(1044) assert x.containsOrIncl(888) == false assert 888 in x diff --git a/lib/pure/collections/sequtils.nim b/lib/pure/collections/sequtils.nim index db33e41af..612624f1d 100644 --- a/lib/pure/collections/sequtils.nim +++ b/lib/pure/collections/sequtils.nim @@ -25,6 +25,28 @@ import macros when not defined(nimhygiene): {.pragma: dirty.} + +macro evalOnceAs(expAlias, exp: untyped, letAssigneable: static[bool]): untyped = + ## Injects ``expAlias`` in caller scope, to avoid bugs involving multiple + ## substitution in macro arguments such as + ## https://github.com/nim-lang/Nim/issues/7187 + ## ``evalOnceAs(myAlias, myExp)`` will behave as ``let myAlias = myExp`` + ## except when ``letAssigneable`` is false (eg to handle openArray) where + ## it just forwards ``exp`` unchanged + expectKind(expAlias, nnkIdent) + var val = exp + + result = newStmtList() + # If `exp` is not a symbol we evaluate it once here and then use the temporary + # symbol as alias + if exp.kind != nnkSym and letAssigneable: + val = genSym() + result.add(newLetStmt(val, exp)) + + result.add( + newProc(name = genSym(nskTemplate, $expAlias), params = [getType(untyped)], + body = val, procType = nnkTemplateDef)) + proc concat*[T](seqs: varargs[seq[T]]): seq[T] = ## Takes several sequences' items and returns them inside a new sequence. ## @@ -496,13 +518,17 @@ template toSeq*(iter: untyped): untyped = ## result = true) ## assert odd_numbers == @[1, 3, 5, 7, 9] + # Note: see also `mapIt` for explanation of some of the implementation + # subtleties. when compiles(iter.len): - var i = 0 - var result = newSeq[type(iter)](iter.len) - for x in iter: - result[i] = x - inc i - result + block: + evalOnceAs(iter2, iter, true) + var result = newSeq[type(iter)](iter2.len) + var i = 0 + for x in iter2: + result[i] = x + inc i + result else: var result: seq[type(iter)] = @[] for x in iter: @@ -635,8 +661,7 @@ template mapIt*(s, typ, op: untyped): untyped = result.add(op) result - -template mapIt*(s, op: untyped): untyped = +template mapIt*(s: typed, op: untyped): untyped = ## Convenience template around the ``map`` proc to reduce typing. ## ## The template injects the ``it`` variable which you can use directly in an @@ -653,19 +678,24 @@ template mapIt*(s, op: untyped): untyped = block: var it{.inject.}: type(items(s)); op)) - var result: seq[outType] when compiles(s.len): - let t = s - var i = 0 - result = newSeq[outType](t.len) - for it {.inject.} in t: - result[i] = op - i += 1 + block: # using a block avoids https://github.com/nim-lang/Nim/issues/8580 + + # BUG: `evalOnceAs(s2, s, false)` would lead to C compile errors + # (`error: use of undeclared identifier`) instead of Nim compile errors + evalOnceAs(s2, s, compiles((let _ = s))) + + var i = 0 + var result = newSeq[outType](s2.len) + for it {.inject.} in s2: + result[i] = op + i += 1 + result else: - result = @[] + var result: seq[outType] = @[] for it {.inject.} in s: result.add(op) - result + result template applyIt*(varSeq, op: untyped) = ## Convenience template around the mutable ``apply`` proc to reduce typing. @@ -752,6 +782,14 @@ macro mapLiterals*(constructor, op: untyped; when isMainModule: import strutils + + # helper for testing double substitution side effects which are handled + # by `evalOnceAs` + var counter = 0 + proc identity[T](a:T):auto= + counter.inc + a + block: # concat test let s1 = @[1, 2, 3] @@ -997,6 +1035,12 @@ when isMainModule: result = true) assert odd_numbers == @[1, 3, 5, 7, 9] + block: + # tests https://github.com/nim-lang/Nim/issues/7187 + counter = 0 + let ret = toSeq(@[1, 2, 3].identity().filter(proc (x: int): bool = x < 3)) + doAssert ret == @[1, 2] + doAssert counter == 1 block: # foldl tests let numbers = @[5, 9, 11] @@ -1023,10 +1067,12 @@ when isMainModule: assert multiplication == 495, "Multiplication is (5*(9*(11)))" assert concatenation == "nimiscool" - block: # mapIt tests + block: # mapIt + applyIt test + counter = 0 var nums = @[1, 2, 3, 4] - strings = nums.mapIt($(4 * it)) + strings = nums.identity.mapIt($(4 * it)) + doAssert counter == 1 nums.applyIt(it * 3) assert nums[0] + nums[3] == 15 assert strings[2] == "12" @@ -1044,5 +1090,51 @@ when isMainModule: doAssert mapLiterals((1, ("abc"), 2), float, nested=false) == (float(1), "abc", float(2)) doAssert mapLiterals(([1], ("abc"), 2), `$`, nested=true) == (["1"], "abc", "2") + block: # mapIt with openArray + counter = 0 + proc foo(x: openArray[int]): seq[int] = x.mapIt(it * 10) + doAssert foo([identity(1),identity(2)]) == @[10, 20] + doAssert counter == 2 + + block: # mapIt with direct openArray + proc foo1(x: openArray[int]): seq[int] = x.mapIt(it * 10) + counter = 0 + doAssert foo1(openArray[int]([identity(1),identity(2)])) == @[10,20] + doAssert counter == 2 + + # Corner cases (openArray litterals should not be common) + template foo2(x: openArray[int]): seq[int] = x.mapIt(it * 10) + counter = 0 + doAssert foo2(openArray[int]([identity(1),identity(2)])) == @[10,20] + # TODO: this fails; not sure how to fix this case + # doAssert counter == 2 + + counter = 0 + doAssert openArray[int]([identity(1), identity(2)]).mapIt(it) == @[1,2] + # ditto + # doAssert counter == 2 + + block: # mapIt empty test, see https://github.com/nim-lang/Nim/pull/8584#pullrequestreview-144723468 + # NOTE: `[].mapIt(it)` is illegal, just as `let a = @[]` is (lacks type + # of elements) + doAssert: not compiles(mapIt(@[], it)) + doAssert: not compiles(mapIt([], it)) + doAssert newSeq[int](0).mapIt(it) == @[] + + block: # mapIt redifinition check, see https://github.com/nim-lang/Nim/issues/8580 + let s2 = [1,2].mapIt(it) + doAssert s2 == @[1,2] + + block: + counter = 0 + doAssert [1,2].identity().mapIt(it*2).mapIt(it*10) == @[20, 40] + # https://github.com/nim-lang/Nim/issues/7187 test case + doAssert counter == 1 + + block: # mapIt with invalid RHS for `let` (#8566) + type X = enum + A, B + doAssert mapIt(X, $it) == @["A", "B"] + when not defined(testing): echo "Finished doc tests" diff --git a/lib/pure/collections/tables.nim b/lib/pure/collections/tables.nim index 308f31eae..f85de7546 100644 --- a/lib/pure/collections/tables.nim +++ b/lib/pure/collections/tables.nim @@ -1329,7 +1329,7 @@ when isMainModule: doAssert clearTable[42] == "asd" clearTable.clear() doAssert(not clearTable.hasKey(123123)) - doAssert clearTable.getOrDefault(42) == nil + doAssert clearTable.getOrDefault(42) == "" block: #5482 var a = [("wrong?","foo"), ("wrong?", "foo2")].newOrderedTable() diff --git a/lib/pure/concurrency/cpuinfo.nim b/lib/pure/concurrency/cpuinfo.nim index 6d41aa1b2..541265da9 100644 --- a/lib/pure/concurrency/cpuinfo.nim +++ b/lib/pure/concurrency/cpuinfo.nim @@ -43,6 +43,14 @@ when defined(genode): proc affinitySpaceTotal(env: GenodeEnvPtr): cuint {. importcpp: "@->cpu().affinity_space().total()".} +when defined(haiku): + {.emit: "#include <OS.h>".} + type + SystemInfo {.importc: "system_info", bycopy.} = object + cpuCount {.importc: "cpu_count".}: uint32 + + proc getSystemInfo(info: ptr SystemInfo): int32 {.importc: "get_system_info".} + proc countProcessors*(): int {.rtl, extern: "ncpi$1".} = ## returns the numer of the processors/cores the machine has. ## Returns 0 if it cannot be detected. @@ -86,6 +94,10 @@ proc countProcessors*(): int {.rtl, extern: "ncpi$1".} = result = sysconf(SC_NPROC_ONLN) elif defined(genode): result = runtimeEnv.affinitySpaceTotal().int + elif defined(haiku): + var sysinfo: SystemInfo + if getSystemInfo(addr sysinfo) == 0: + result = sysinfo.cpuCount.int else: result = sysconf(SC_NPROCESSORS_ONLN) if result <= 0: result = 0 diff --git a/lib/pure/concurrency/threadpool.nim b/lib/pure/concurrency/threadpool.nim index 6ec71e912..f3b13fac5 100644 --- a/lib/pure/concurrency/threadpool.nim +++ b/lib/pure/concurrency/threadpool.nim @@ -30,7 +30,7 @@ proc destroySemaphore(cv: var Semaphore) {.inline.} = deinitCond(cv.c) deinitLock(cv.L) -proc await(cv: var Semaphore) = +proc blockUntil(cv: var Semaphore) = acquire(cv.L) while cv.counter <= 0: wait(cv.c, cv.L) @@ -81,7 +81,7 @@ proc closeBarrier(b: ptr Barrier) {.compilerProc.} = fence() b.interest = true fence() - while b.left != b.entered: await(b.cv) + while b.left != b.entered: blockUntil(b.cv) destroySemaphore(b.cv) {.pop.} @@ -89,8 +89,6 @@ proc closeBarrier(b: ptr Barrier) {.compilerProc.} = # ---------------------------------------------------------------------------- type - foreign* = object ## a region that indicates the pointer comes from a - ## foreign thread heap. AwaitInfo = object cv: Semaphore idx: int @@ -99,7 +97,7 @@ type FlowVarBaseObj = object of RootObj ready, usesSemaphore, awaited: bool cv: Semaphore #\ - # for 'awaitAny' support + # for 'blockUntilAny' support ai: ptr AwaitInfo idx: int data: pointer # we incRef and unref it to keep it alive; note this MUST NOT @@ -130,12 +128,12 @@ type q: ToFreeQueue readyForTask: Semaphore -proc await*(fv: FlowVarBase) = +proc blockUntil*(fv: FlowVarBase) = ## waits until the value for the flowVar arrives. Usually it is not necessary ## to call this explicitly. if fv.usesSemaphore and not fv.awaited: fv.awaited = true - await(fv.cv) + blockUntil(fv.cv) destroySemaphore(fv.cv) proc selectWorker(w: ptr Worker; fn: WorkerProc; data: pointer): bool = @@ -143,7 +141,7 @@ proc selectWorker(w: ptr Worker; fn: WorkerProc; data: pointer): bool = w.data = data w.f = fn signal(w.taskArrived) - await(w.taskStarted) + blockUntil(w.taskStarted) result = true proc cleanFlowVars(w: ptr Worker) = @@ -178,11 +176,11 @@ proc attach(fv: FlowVarBase; i: int): bool = release(fv.cv.L) proc finished(fv: FlowVarBase) = - doAssert fv.ai.isNil, "flowVar is still attached to an 'awaitAny'" + doAssert fv.ai.isNil, "flowVar is still attached to an 'blockUntilAny'" # we have to protect against the rare cases where the owner of the flowVar # simply disregards the flowVar and yet the "flowVar" has not yet written # anything to it: - await(fv) + blockUntil(fv) if fv.data.isNil: return let owner = cast[ptr Worker](fv.owner) let q = addr(owner.q) @@ -191,7 +189,7 @@ proc finished(fv: FlowVarBase) = #echo "EXHAUSTED!" release(q.lock) wakeupWorkerToProcessQueue(owner) - await(q.empty) + blockUntil(q.empty) acquire(q.lock) q.data[q.len] = cast[pointer](fv.data) inc q.len @@ -222,7 +220,7 @@ proc awaitAndThen*[T](fv: FlowVar[T]; action: proc (x: T) {.closure.}) = ## to ``action``. Note that due to Nim's parameter passing semantics this ## means that ``T`` doesn't need to be copied and so ``awaitAndThen`` can ## sometimes be more efficient than ``^``. - await(fv) + blockUntil(fv) when T is string or T is seq: action(cast[T](fv.data)) elif T is ref: @@ -231,31 +229,31 @@ proc awaitAndThen*[T](fv: FlowVar[T]; action: proc (x: T) {.closure.}) = action(fv.blob) finished(fv) -proc unsafeRead*[T](fv: FlowVar[ref T]): foreign ptr T = +proc unsafeRead*[T](fv: FlowVar[ref T]): ptr T = ## blocks until the value is available and then returns this value. - await(fv) - result = cast[foreign ptr T](fv.data) + blockUntil(fv) + result = cast[ptr T](fv.data) proc `^`*[T](fv: FlowVar[ref T]): ref T = ## blocks until the value is available and then returns this value. - await(fv) + blockUntil(fv) let src = cast[ref T](fv.data) deepCopy result, src proc `^`*[T](fv: FlowVar[T]): T = ## blocks until the value is available and then returns this value. - await(fv) + blockUntil(fv) when T is string or T is seq: # XXX closures? deepCopy? result = cast[T](fv.data) else: result = fv.blob -proc awaitAny*(flowVars: openArray[FlowVarBase]): int = +proc blockUntilAny*(flowVars: openArray[FlowVarBase]): int = ## awaits any of the given flowVars. Returns the index of one flowVar for - ## which a value arrived. A flowVar only supports one call to 'awaitAny' at - ## the same time. That means if you awaitAny([a,b]) and awaitAny([b,c]) the second - ## call will only await 'c'. If there is no flowVar left to be able to wait + ## which a value arrived. A flowVar only supports one call to 'blockUntilAny' at + ## the same time. That means if you blockUntilAny([a,b]) and blockUntilAny([b,c]) the second + ## call will only blockUntil 'c'. If there is no flowVar left to be able to wait ## on, -1 is returned. ## **Note**: This results in non-deterministic behaviour and should be avoided. var ai: AwaitInfo @@ -271,7 +269,7 @@ proc awaitAny*(flowVars: openArray[FlowVarBase]): int = inc conflicts if conflicts < flowVars.len: if result < 0: - await(ai.cv) + blockUntil(ai.cv) result = ai.idx for i in 0 .. flowVars.high: discard cas(addr flowVars[i].ai, addr ai, nil) @@ -328,7 +326,7 @@ proc slave(w: ptr Worker) {.thread.} = w.ready = true readyWorker = w signal(gSomeReady) - await(w.taskArrived) + blockUntil(w.taskArrived) # XXX Somebody needs to look into this (why does this assertion fail # in Visual Studio?) when not defined(vcc) and not defined(tcc): assert(not w.ready) @@ -353,7 +351,7 @@ proc distinguishedSlave(w: ptr Worker) {.thread.} = else: w.ready = true signal(w.readyForTask) - await(w.taskArrived) + blockUntil(w.taskArrived) assert(not w.ready) w.f(w, w.data) if w.q.len != 0: w.cleanFlowVars @@ -501,7 +499,7 @@ proc nimSpawn3(fn: WorkerProc; data: pointer) {.compilerProc.} = # on the current thread instead. var self = addr(workersData[localThreadId-1]) fn(self, data) - await(self.taskStarted) + blockUntil(self.taskStarted) return if isSlave: @@ -526,7 +524,7 @@ proc nimSpawn3(fn: WorkerProc; data: pointer) {.compilerProc.} = inc numSlavesWaiting - await(gSomeReady) + blockUntil(gSomeReady) if isSlave: withLock numSlavesLock: @@ -544,7 +542,7 @@ proc nimSpawn4(fn: WorkerProc; data: pointer; id: ThreadId) {.compilerProc.} = release(distinguishedLock) while true: if selectWorker(addr(distinguishedData[id]), fn, data): break - await(distinguishedData[id].readyForTask) + blockUntil(distinguishedData[id].readyForTask) proc sync*() = @@ -557,7 +555,7 @@ proc sync*() = if not allReady: break allReady = allReady and workersData[i].ready if allReady: break - await(gSomeReady) + blockUntil(gSomeReady) inc toRelease for i in 0 ..< toRelease: diff --git a/lib/pure/coro.nim b/lib/pure/coro.nim index b6ef30e7c..6d7dcf078 100644 --- a/lib/pure/coro.nim +++ b/lib/pure/coro.nim @@ -43,6 +43,10 @@ when defined(windows): {.warning: "ucontext coroutine backend is not available on windows, defaulting to fibers.".} when defined(nimCoroutinesSetjmp): {.warning: "setjmp coroutine backend is not available on windows, defaulting to fibers.".} +elif defined(haiku): + const coroBackend = CORO_BACKEND_SETJMP + when defined(nimCoroutinesUcontext): + {.warning: "ucontext coroutine backend is not available on haiku, defaulting to setjmp".} elif defined(nimCoroutinesSetjmp) or defined(nimCoroutinesSetjmpBundled): const coroBackend = CORO_BACKEND_SETJMP else: @@ -55,21 +59,21 @@ when coroBackend == CORO_BACKEND_FIBERS: elif coroBackend == CORO_BACKEND_UCONTEXT: type - stack_t {.importc, header: "<sys/ucontext.h>".} = object + stack_t {.importc, header: "<ucontext.h>".} = object ss_sp: pointer ss_flags: int ss_size: int - ucontext_t {.importc, header: "<sys/ucontext.h>".} = object + ucontext_t {.importc, header: "<ucontext.h>".} = object uc_link: ptr ucontext_t uc_stack: stack_t Context = ucontext_t - proc getcontext(context: var ucontext_t): int32 {.importc, header: "<sys/ucontext.h>".} - proc setcontext(context: var ucontext_t): int32 {.importc, header: "<sys/ucontext.h>".} - proc swapcontext(fromCtx, toCtx: var ucontext_t): int32 {.importc, header: "<sys/ucontext.h>".} - proc makecontext(context: var ucontext_t, fn: pointer, argc: int32) {.importc, header: "<sys/ucontext.h>", varargs.} + proc getcontext(context: var ucontext_t): int32 {.importc, header: "<ucontext.h>".} + proc setcontext(context: var ucontext_t): int32 {.importc, header: "<ucontext.h>".} + proc swapcontext(fromCtx, toCtx: var ucontext_t): int32 {.importc, header: "<ucontext.h>".} + proc makecontext(context: var ucontext_t, fn: pointer, argc: int32) {.importc, header: "<ucontext.h>", varargs.} elif coroBackend == CORO_BACKEND_SETJMP: proc coroExecWithStack*(fn: pointer, stack: pointer) {.noreturn, importc: "narch_$1", fastcall.} @@ -190,7 +194,7 @@ proc switchTo(current, to: CoroutinePtr) = elif to.state == CORO_CREATED: # Coroutine is started. coroExecWithStack(runCurrentTask, to.stack.bottom) - doAssert false + #doAssert false else: {.error: "Invalid coroutine backend set.".} # Execution was just resumed. Restore frame information and set active stack. diff --git a/lib/pure/distros.nim b/lib/pure/distros.nim index 0adba5b1e..5847cfadb 100644 --- a/lib/pure/distros.nim +++ b/lib/pure/distros.nim @@ -126,6 +126,8 @@ type OpenBSD DragonFlyBSD + Haiku + const LacksDevPackages* = {Distribution.Gentoo, Distribution.Slackware, @@ -166,6 +168,8 @@ proc detectOsImpl(d: Distribution): bool = of Distribution.Solaris: let uname = toLowerAscii(uname()) result = ("sun" in uname) or ("solaris" in uname) + of Distribution.Haiku: + result = defined(haiku) else: let dd = toLowerAscii($d) result = dd in toLowerAscii(uname()) or dd in toLowerAscii(release()) @@ -224,6 +228,8 @@ proc foreignDepInstallCmd*(foreignPackageName: string): (string, bool) = result = ("pacman -S " & p, true) else: result = ("<your package manager here> install " & p, true) + elif defined(haiku): + result = ("pkgman install " & p, true) else: result = ("brew install " & p, false) diff --git a/lib/pure/encodings.nim b/lib/pure/encodings.nim index 3c1cf73f4..2039a31be 100644 --- a/lib/pure/encodings.nim +++ b/lib/pure/encodings.nim @@ -255,7 +255,7 @@ when defined(windows): else: when defined(haiku): - const iconvDll = "(libc.so.6|libiconv.so|libtextencoding.so)" + const iconvDll = "libiconv.so" elif defined(macosx): const iconvDll = "libiconv.dylib" else: @@ -272,6 +272,8 @@ else: const EILSEQ = 86.cint elif defined(solaris): const EILSEQ = 88.cint + elif defined(haiku): + const EILSEQ = -2147454938.cint var errno {.importc, header: "<errno.h>".}: cint diff --git a/lib/pure/fenv.nim b/lib/pure/fenv.nim index c946c4261..0725973ca 100644 --- a/lib/pure/fenv.nim +++ b/lib/pure/fenv.nim @@ -12,7 +12,7 @@ {.deadCodeElim: on.} # dce option deprecated -when defined(Posix) and not defined(haiku): +when defined(Posix): {.passl: "-lm".} var diff --git a/lib/pure/htmlparser.nim b/lib/pure/htmlparser.nim index c38c36874..f54fe87f7 100644 --- a/lib/pure/htmlparser.nim +++ b/lib/pure/htmlparser.nim @@ -1869,7 +1869,6 @@ proc entityToUtf8*(entity: string): string = ## "" is returned if the entity name is unknown. The HTML parser ## already converts entities to UTF-8. runnableExamples: - doAssert entityToUtf8(nil) == "" doAssert entityToUtf8("") == "" doAssert entityToUtf8("a") == "" doAssert entityToUtf8("gt") == ">" diff --git a/lib/pure/httpclient.nim b/lib/pure/httpclient.nim index 8b4fb0f8c..0192e71e7 100644 --- a/lib/pure/httpclient.nim +++ b/lib/pure/httpclient.nim @@ -378,23 +378,23 @@ proc newMultipartData*: MultipartData = ## Constructs a new ``MultipartData`` object. MultipartData(content: @[]) -proc add*(p: var MultipartData, name, content: string, filename: string = nil, - contentType: string = nil) = +proc add*(p: var MultipartData, name, content: string, filename: string = "", + contentType: string = "") = ## Add a value to the multipart data. Raises a `ValueError` exception if ## `name`, `filename` or `contentType` contain newline characters. if {'\c','\L'} in name: raise newException(ValueError, "name contains a newline character") - if filename != nil and {'\c','\L'} in filename: + if {'\c','\L'} in filename: raise newException(ValueError, "filename contains a newline character") - if contentType != nil and {'\c','\L'} in contentType: + if {'\c','\L'} in contentType: raise newException(ValueError, "contentType contains a newline character") var str = "Content-Disposition: form-data; name=\"" & name & "\"" - if filename != nil: + if filename.len > 0: str.add("; filename=\"" & filename & "\"") str.add("\c\L") - if contentType != nil: + if contentType.len > 0: str.add("Content-Type: " & contentType & "\c\L") str.add("\c\L" & content & "\c\L") @@ -434,7 +434,7 @@ proc addFiles*(p: var MultipartData, xs: openarray[tuple[name, file: string]]): var contentType: string let (_, fName, ext) = splitFile(file) if ext.len > 0: - contentType = m.getMimetype(ext[1..ext.high], nil) + contentType = m.getMimetype(ext[1..ext.high], "") p.add(name, readFile(file), fName & ext, contentType) result = p @@ -457,7 +457,7 @@ proc `[]=`*(p: var MultipartData, name: string, p.add(name, file.content, file.name, file.contentType) proc format(p: MultipartData): tuple[contentType, body: string] = - if p == nil or p.content == nil or p.content.len == 0: + if p == nil or p.content.len == 0: return ("", "") # Create boundary that is not in the data to be formatted @@ -807,6 +807,7 @@ type lastProgressReport: float when SocketType is AsyncSocket: bodyStream: FutureStream[string] + parseBodyFut: Future[void] else: bodyStream: Stream getBody: bool ## When `false`, the body is never read in requestAux. @@ -1066,10 +1067,14 @@ proc parseResponse(client: HttpClient | AsyncHttpClient, if getBody: when client is HttpClient: client.bodyStream = newStringStream() + result.bodyStream = client.bodyStream + parseBody(client, result.headers, result.version) else: client.bodyStream = newFutureStream[string]("parseResponse") - await parseBody(client, result.headers, result.version) - result.bodyStream = client.bodyStream + result.bodyStream = client.bodyStream + assert(client.parseBodyFut.isNil or client.parseBodyFut.finished) + client.parseBodyFut = parseBody(client, result.headers, result.version) + # do not wait here for the body request to complete proc newConnection(client: HttpClient | AsyncHttpClient, url: Uri) {.multisync.} = @@ -1159,6 +1164,12 @@ proc requestAux(client: HttpClient | AsyncHttpClient, url: string, # Helper that actually makes the request. Does not handle redirects. let requestUrl = parseUri(url) + when client is AsyncHttpClient: + if not client.parseBodyFut.isNil: + # let the current operation finish before making another request + await client.parseBodyFut + client.parseBodyFut = nil + await newConnection(client, requestUrl) let effectiveHeaders = client.headers.override(headers) diff --git a/lib/pure/ioselects/ioselectors_kqueue.nim b/lib/pure/ioselects/ioselectors_kqueue.nim index 142e988d0..0e133f650 100644 --- a/lib/pure/ioselects/ioselectors_kqueue.nim +++ b/lib/pure/ioselects/ioselectors_kqueue.nim @@ -567,8 +567,11 @@ proc selectInto*[T](s: Selector[T], timeout: int, doAssert(true, "Unsupported kqueue filter in the queue!") if (kevent.flags and EV_EOF) != 0: + # TODO this error handling needs to be rethought. + # `fflags` can sometimes be `0x80000000` and thus we use 'cast' + # here: if kevent.fflags != 0: - rkey.errorCode = kevent.fflags.OSErrorCode + rkey.errorCode = cast[OSErrorCode](kevent.fflags) else: # This assumes we are dealing with sockets. # TODO: For future-proofing it might be a good idea to give the diff --git a/lib/pure/ioselects/ioselectors_select.nim b/lib/pure/ioselects/ioselectors_select.nim index cd6a72b44..521b31a64 100644 --- a/lib/pure/ioselects/ioselectors_select.nim +++ b/lib/pure/ioselects/ioselectors_select.nim @@ -310,7 +310,10 @@ proc selectInto*[T](s: Selector[T], timeout: int, var rset, wset, eset: FdSet if timeout != -1: - tv.tv_sec = timeout.int32 div 1_000 + when defined(genode): + tv.tv_sec = Time(timeout div 1_000) + else: + tv.tv_sec = timeout.int32 div 1_000 tv.tv_usec = (timeout.int32 %% 1_000) * 1_000 else: ptv = nil @@ -391,7 +394,6 @@ proc contains*[T](s: Selector[T], fd: SocketHandle|int): bool {.inline.} = for i in 0..<FD_SETSIZE: if s.fds[i].ident == fdi: return true - inc(i) when hasThreadSupport: template withSelectLock[T](s: Selector[T], body: untyped) = diff --git a/lib/pure/json.nim b/lib/pure/json.nim index b9279b18c..69ffb1796 100644 --- a/lib/pure/json.nim +++ b/lib/pure/json.nim @@ -1144,7 +1144,7 @@ proc processType(typeName: NimNode, obj: NimNode, result = quote do: ( verifyJsonKind(`jsonNode`, {JString, JNull}, astToStr(`jsonNode`)); - if `jsonNode`.kind == JNull: nil else: `jsonNode`.str + if `jsonNode`.kind == JNull: "" else: `jsonNode`.str ) of "biggestint": result = quote do: diff --git a/lib/pure/math.nim b/lib/pure/math.nim index b921b1841..79f287651 100644 --- a/lib/pure/math.nim +++ b/lib/pure/math.nim @@ -49,7 +49,7 @@ proc fac*(n: int): int = {.push checks:off, line_dir:off, stack_trace:off.} -when defined(Posix) and not defined(haiku): +when defined(Posix): {.passl: "-lm".} const @@ -162,9 +162,6 @@ when not defined(JS): # C proc log10*(x: float32): float32 {.importc: "log10f", header: "<math.h>".} proc log10*(x: float64): float64 {.importc: "log10", header: "<math.h>".} ## Computes the common logarithm (base 10) of `x` - proc log2*(x: float32): float32 {.importc: "log2f", header: "<math.h>".} - proc log2*(x: float64): float64 {.importc: "log2", header: "<math.h>".} - ## Computes the binary logarithm (base 2) of `x` proc exp*(x: float32): float32 {.importc: "expf", header: "<math.h>".} proc exp*(x: float64): float64 {.importc: "exp", header: "<math.h>".} ## Computes the exponential function of `x` (pow(E, x)) @@ -268,6 +265,8 @@ proc arcsech*[T: float32|float64](x: T): T = arccosh(1.0 / x) proc arccsch*[T: float32|float64](x: T): T = arcsinh(1.0 / x) ## Computes the inverse hyperbolic cosecant of `x` +const windowsCC89 = defined(windows) and (defined(vcc) or defined(bcc)) + when not defined(JS): # C proc hypot*(x, y: float32): float32 {.importc: "hypotf", header: "<math.h>".} proc hypot*(x, y: float64): float64 {.importc: "hypot", header: "<math.h>".} @@ -280,25 +279,27 @@ when not defined(JS): # C ## ## To compute power between integers, use `^` e.g. 2 ^ 6 - proc erf*(x: float32): float32 {.importc: "erff", header: "<math.h>".} - proc erf*(x: float64): float64 {.importc: "erf", header: "<math.h>".} - ## The error function - proc erfc*(x: float32): float32 {.importc: "erfcf", header: "<math.h>".} - proc erfc*(x: float64): float64 {.importc: "erfc", header: "<math.h>".} - ## The complementary error function - - proc gamma*(x: float32): float32 {.importc: "tgammaf", header: "<math.h>".} - proc gamma*(x: float64): float64 {.importc: "tgamma", header: "<math.h>".} - ## The gamma function - proc tgamma*(x: float32): float32 - {.deprecated: "use gamma instead", importc: "tgammaf", header: "<math.h>".} - proc tgamma*(x: float64): float64 - {.deprecated: "use gamma instead", importc: "tgamma", header: "<math.h>".} - ## The gamma function - ## **Deprecated since version 0.19.0**: Use ``gamma`` instead. - proc lgamma*(x: float32): float32 {.importc: "lgammaf", header: "<math.h>".} - proc lgamma*(x: float64): float64 {.importc: "lgamma", header: "<math.h>".} - ## Natural log of the gamma function + # TODO: add C89 version on windows + when not windowsCC89: + proc erf*(x: float32): float32 {.importc: "erff", header: "<math.h>".} + proc erf*(x: float64): float64 {.importc: "erf", header: "<math.h>".} + ## The error function + proc erfc*(x: float32): float32 {.importc: "erfcf", header: "<math.h>".} + proc erfc*(x: float64): float64 {.importc: "erfc", header: "<math.h>".} + ## The complementary error function + + proc gamma*(x: float32): float32 {.importc: "tgammaf", header: "<math.h>".} + proc gamma*(x: float64): float64 {.importc: "tgamma", header: "<math.h>".} + ## The gamma function + proc tgamma*(x: float32): float32 + {.deprecated: "use gamma instead", importc: "tgammaf", header: "<math.h>".} + proc tgamma*(x: float64): float64 + {.deprecated: "use gamma instead", importc: "tgamma", header: "<math.h>".} + ## The gamma function + ## **Deprecated since version 0.19.0**: Use ``gamma`` instead. + proc lgamma*(x: float32): float32 {.importc: "lgammaf", header: "<math.h>".} + proc lgamma*(x: float64): float64 {.importc: "lgamma", header: "<math.h>".} + ## Natural log of the gamma function proc floor*(x: float32): float32 {.importc: "floorf", header: "<math.h>".} proc floor*(x: float64): float64 {.importc: "floor", header: "<math.h>".} @@ -314,7 +315,7 @@ when not defined(JS): # C ## .. code-block:: nim ## echo ceil(-2.1) ## -2.0 - when defined(windows) and (defined(vcc) or defined(bcc)): + when windowsCC89: # MSVC 2010 don't have trunc/truncf # this implementation was inspired by Go-lang Math.Trunc proc truncImpl(f: float64): float64 = @@ -452,6 +453,28 @@ when not defined(JS): var exp: int32 result = c_frexp(x, exp) exponent = exp + + when windowsCC89: + # taken from Go-lang Math.Log2 + const ln2 = 0.693147180559945309417232121458176568075500134360255254120680009 + template log2Impl[T](x: T): T = + var exp: int32 + var frac = frexp(x, exp) + # Make sure exact powers of two give an exact answer. + # Don't depend on Log(0.5)*(1/Ln2)+exp being exactly exp-1. + if frac == 0.5: return T(exp - 1) + log10(frac)*(1/ln2) + T(exp) + + proc log2*(x: float32): float32 = log2Impl(x) + proc log2*(x: float64): float64 = log2Impl(x) + ## Log2 returns the binary logarithm of x. + ## The special cases are the same as for Log. + + else: + proc log2*(x: float32): float32 {.importc: "log2f", header: "<math.h>".} + proc log2*(x: float64): float64 {.importc: "log2", header: "<math.h>".} + ## Computes the binary logarithm (base 2) of `x` + else: proc frexp*[T: float32|float64](x: T, exponent: var int): T = if x == 0.0: @@ -506,8 +529,8 @@ proc sgn*[T: SomeNumber](x: T): int {.inline.} = {.pop.} proc `^`*[T](x: T, y: Natural): T = - ## Computes ``x`` to the power ``y`. ``x`` must be non-negative, use - ## `pow <#pow,float,float>` for negative exponents. + ## Computes ``x`` to the power ``y``. ``x`` must be non-negative, use + ## `pow <#pow,float,float>`_ for negative exponents. when compiles(y >= T(0)): assert y >= T(0) else: @@ -564,7 +587,7 @@ proc lcm*[T](x, y: T): T = ## Computes the least common multiple of ``x`` and ``y``. x div gcd(x, y) * y -when isMainModule and not defined(JS): +when isMainModule and not defined(JS) and not windowsCC89: # Check for no side effect annotation proc mySqrt(num: float): float {.noSideEffect.} = return sqrt(num) @@ -692,3 +715,14 @@ when isMainModule: block: # log doAssert log(4.0, 3.0) == ln(4.0) / ln(3.0) + doAssert log2(8.0'f64) == 3.0'f64 + doAssert log2(4.0'f64) == 2.0'f64 + doAssert log2(2.0'f64) == 1.0'f64 + doAssert log2(1.0'f64) == 0.0'f64 + doAssert classify(log2(0.0'f64)) == fcNegInf + + doAssert log2(8.0'f32) == 3.0'f32 + doAssert log2(4.0'f32) == 2.0'f32 + doAssert log2(2.0'f32) == 1.0'f32 + doAssert log2(1.0'f32) == 0.0'f32 + doAssert classify(log2(0.0'f32)) == fcNegInf diff --git a/lib/pure/nativesockets.nim b/lib/pure/nativesockets.nim index 5545ca2d1..d5fb0f89b 100644 --- a/lib/pure/nativesockets.nim +++ b/lib/pure/nativesockets.nim @@ -248,9 +248,10 @@ proc getAddrInfo*(address: string, port: Port, domain: Domain = AF_INET, hints.ai_socktype = toInt(sockType) hints.ai_protocol = toInt(protocol) # OpenBSD doesn't support AI_V4MAPPED and doesn't define the macro AI_V4MAPPED. - # FreeBSD doesn't support AI_V4MAPPED but defines the macro. + # FreeBSD, Haiku don't support AI_V4MAPPED but defines the macro. # https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=198092 - when not defined(freebsd) and not defined(openbsd) and not defined(netbsd) and not defined(android): + # https://dev.haiku-os.org/ticket/14323 + when not defined(freebsd) and not defined(openbsd) and not defined(netbsd) and not defined(android) and not defined(haiku): if domain == AF_INET6: hints.ai_flags = AI_V4MAPPED var gaiResult = getaddrinfo(address, $port, addr(hints), result) diff --git a/lib/pure/net.nim b/lib/pure/net.nim index 771e7de10..0e56100d9 100644 --- a/lib/pure/net.nim +++ b/lib/pure/net.nim @@ -231,7 +231,7 @@ proc newSocket*(domain: Domain = AF_INET, sockType: SockType = SOCK_STREAM, raiseOSError(osLastError()) result = newSocket(fd, domain, sockType, protocol, buffered) -proc parseIPv4Address(address_str: string): IpAddress = +proc parseIPv4Address(addressStr: string): IpAddress = ## Parses IPv4 adresses ## Raises EInvalidValue on errors var @@ -241,15 +241,15 @@ proc parseIPv4Address(address_str: string): IpAddress = result.family = IpAddressFamily.IPv4 - for i in 0 .. high(address_str): - if address_str[i] in strutils.Digits: # Character is a number + for i in 0 .. high(addressStr): + if addressStr[i] in strutils.Digits: # Character is a number currentByte = currentByte * 10 + - cast[uint16](ord(address_str[i]) - ord('0')) + cast[uint16](ord(addressStr[i]) - ord('0')) if currentByte > 255'u16: raise newException(ValueError, "Invalid IP Address. Value is out of range") seperatorValid = true - elif address_str[i] == '.': # IPv4 address separator + elif addressStr[i] == '.': # IPv4 address separator if not seperatorValid or byteCount >= 3: raise newException(ValueError, "Invalid IP Address. The address consists of too many groups") @@ -265,11 +265,11 @@ proc parseIPv4Address(address_str: string): IpAddress = raise newException(ValueError, "Invalid IP Address") result.address_v4[byteCount] = cast[uint8](currentByte) -proc parseIPv6Address(address_str: string): IpAddress = +proc parseIPv6Address(addressStr: string): IpAddress = ## Parses IPv6 adresses ## Raises EInvalidValue on errors result.family = IpAddressFamily.IPv6 - if address_str.len < 2: + if addressStr.len < 2: raise newException(ValueError, "Invalid IP Address") var @@ -282,7 +282,7 @@ proc parseIPv6Address(address_str: string): IpAddress = v4StartPos = -1 byteCount = 0 - for i,c in address_str: + for i,c in addressStr: if c == ':': if not seperatorValid: raise newException(ValueError, @@ -293,7 +293,7 @@ proc parseIPv6Address(address_str: string): IpAddress = "Invalid IP Address. Address contains more than one \"::\" seperator") dualColonGroup = groupCount seperatorValid = false - elif i != 0 and i != high(address_str): + elif i != 0 and i != high(addressStr): if groupCount >= 8: raise newException(ValueError, "Invalid IP Address. The address consists of too many groups") @@ -303,11 +303,11 @@ proc parseIPv6Address(address_str: string): IpAddress = groupCount.inc() if dualColonGroup != -1: seperatorValid = false elif i == 0: # only valid if address starts with :: - if address_str[1] != ':': + if addressStr[1] != ':': raise newException(ValueError, "Invalid IP Address. Address may not start with \":\"") - else: # i == high(address_str) - only valid if address ends with :: - if address_str[high(address_str)-1] != ':': + else: # i == high(addressStr) - only valid if address ends with :: + if addressStr[high(addressStr)-1] != ':': raise newException(ValueError, "Invalid IP Address. Address may not end with \":\"") lastWasColon = true @@ -345,7 +345,7 @@ proc parseIPv6Address(address_str: string): IpAddress = result.address_v6[groupCount*2+1] = cast[uint8](currentShort and 0xFF) groupCount.inc() else: # Must parse IPv4 address - for i,c in address_str[v4StartPos..high(address_str)]: + for i,c in addressStr[v4StartPos..high(addressStr)]: if c in strutils.Digits: # Character is a number currentShort = currentShort * 10 + cast[uint32](ord(c) - ord('0')) if currentShort > 255'u32: @@ -386,21 +386,21 @@ proc parseIPv6Address(address_str: string): IpAddress = raise newException(ValueError, "Invalid IP Address. The address consists of too many groups") -proc parseIpAddress*(address_str: string): IpAddress = +proc parseIpAddress*(addressStr: string): IpAddress = ## Parses an IP address ## Raises EInvalidValue on error - if address_str == nil: - raise newException(ValueError, "IP Address string is nil") - if address_str.contains(':'): - return parseIPv6Address(address_str) + if addressStr.len == 0: + raise newException(ValueError, "IP Address string is empty") + if addressStr.contains(':'): + return parseIPv6Address(addressStr) else: - return parseIPv4Address(address_str) + return parseIPv4Address(addressStr) -proc isIpAddress*(address_str: string): bool {.tags: [].} = +proc isIpAddress*(addressStr: string): bool {.tags: [].} = ## Checks if a string is an IP address ## Returns true if it is, false otherwise try: - discard parseIpAddress(address_str) + discard parseIpAddress(addressStr) except ValueError: return false return true @@ -587,7 +587,7 @@ when defineSsl: proc pskClientCallback(ssl: SslPtr; hint: cstring; identity: cstring; max_identity_len: cuint; psk: ptr cuchar; max_psk_len: cuint): cuint {.cdecl.} = let ctx = SSLContext(context: ssl.SSL_get_SSL_CTX) - let hintString = if hint == nil: nil else: $hint + let hintString = if hint == nil: "" else: $hint let (identityString, pskString) = (ctx.clientGetPskFunc)(hintString) if psk.len.cuint > max_psk_len: return 0 @@ -657,7 +657,7 @@ when defineSsl: proc wrapConnectedSocket*(ctx: SSLContext, socket: Socket, handshake: SslHandshakeType, - hostname: string = nil) = + hostname: string = "") = ## Wraps a connected socket in an SSL context. This function effectively ## turns ``socket`` into an SSL socket. ## ``hostname`` should be specified so that the client knows which hostname @@ -671,7 +671,7 @@ when defineSsl: wrapSocket(ctx, socket) case handshake of handshakeAsClient: - if not hostname.isNil and not isIpAddress(hostname): + if hostname.len > 0 and not isIpAddress(hostname): # Discard result in case OpenSSL version doesn't support SNI, or we're # not using TLSv1+ discard SSL_set_tlsext_host_name(socket.sslHandle, hostname) diff --git a/lib/pure/options.nim b/lib/pure/options.nim index ce58943f9..12e38d8b5 100644 --- a/lib/pure/options.nim +++ b/lib/pure/options.nim @@ -129,7 +129,7 @@ proc get*[T](self: Option[T]): T = ## Returns contents of the Option. If it is none, then an exception is ## thrown. if self.isNone: - raise UnpackError(msg : "Can't obtain a value from a `none`") + raise UnpackError(msg: "Can't obtain a value from a `none`") self.val proc get*[T](self: Option[T], otherwise: T): T = @@ -139,6 +139,13 @@ proc get*[T](self: Option[T], otherwise: T): T = else: otherwise +proc get*[T](self: var Option[T]): var T = + ## Returns contents of the Option. If it is none, then an exception is + ## thrown. + if self.isNone: + raise UnpackError(msg: "Can't obtain a value from a `none`") + return self.val + proc map*[T](self: Option[T], callback: proc (input: T)) = ## Applies a callback to the value in this Option if self.isSome: @@ -187,9 +194,11 @@ proc `$`*[T](self: Option[T]): string = ## If the option does not have a value, the result will be `None[T]` where `T` is the name of ## the type contained in the option. if self.isSome: - "Some(" & $self.val & ")" + result = "Some(" + result.addQuoted self.val + result.add ")" else: - "None[" & name(T) & "]" + result = "None[" & name(T) & "]" when isMainModule: import unittest, sequtils @@ -240,7 +249,7 @@ when isMainModule: check(stringNone.get("Correct") == "Correct") test "$": - check($(some("Correct")) == "Some(Correct)") + check($(some("Correct")) == "Some(\"Correct\")") check($(stringNone) == "None[string]") test "map with a void result": diff --git a/lib/pure/os.nim b/lib/pure/os.nim index 9cc83c372..8fbc20bb5 100644 --- a/lib/pure/os.nim +++ b/lib/pure/os.nim @@ -615,7 +615,10 @@ proc copyFile*(source, dest: string) {.rtl, extern: "nos$1", when not declared(ENOENT) and not defined(Windows): when NoFakeVars: - const ENOENT = cint(2) # 2 on most systems including Solaris + when not defined(haiku): + const ENOENT = cint(2) # 2 on most systems including Solaris + else: + const ENOENT = cint(-2147459069) else: var ENOENT {.importc, header: "<errno.h>".}: cint @@ -972,6 +975,14 @@ proc rawCreateDir(dir: string): bool = result = false else: raiseOSError(osLastError(), dir) + elif defined(haiku): + let res = mkdir(dir, 0o777) + if res == 0'i32: + result = true + elif errno == EEXIST or errno == EROFS: + result = false + else: + raiseOSError(osLastError(), dir) elif defined(posix): let res = mkdir(dir, 0o777) if res == 0'i32: @@ -1352,9 +1363,15 @@ elif defined(nintendoswitch): proc paramCount*(): int {.tags: [ReadIOEffect].} = raise newException(OSError, "paramCount is not implemented on Nintendo Switch") +elif defined(genode): + proc paramStr*(i: int): TaintedString = + raise newException(OSError, "paramStr is not implemented on Genode") + + proc paramCount*(): int = + raise newException(OSError, "paramCount is not implemented on Genode") + elif not defined(createNimRtl) and - not(defined(posix) and appType == "lib") and - not defined(genode): + not(defined(posix) and appType == "lib"): # On Posix, there is no portable way to get the command line from a DLL. var cmdCount {.importc: "cmdCount".}: cint diff --git a/lib/pure/ospaths.nim b/lib/pure/ospaths.nim index a7ebd9d15..0414eae5d 100644 --- a/lib/pure/ospaths.nim +++ b/lib/pure/ospaths.nim @@ -525,14 +525,14 @@ proc getConfigDir*(): string {.rtl, extern: "nos$1", ## Returns the config directory of the current user for applications. ## ## On non-Windows OSs, this proc conforms to the XDG Base Directory - ## spec. Thus, this proc returns the value of the XDG_CONFIG_DIR environment + ## spec. Thus, this proc returns the value of the XDG_CONFIG_HOME environment ## variable if it is set, and returns the default configuration directory, ## "~/.config/", otherwise. ## ## An OS-dependent trailing slash is always present at the end of the ## returned string; `\\` on Windows and `/` on all other OSs. when defined(windows): return string(getEnv("APPDATA")) & "\\" - elif getEnv("XDG_CONFIG_DIR"): return string(getEnv("XDG_CONFIG_DIR")) & "/" + elif getEnv("XDG_CONFIG_HOME"): return string(getEnv("XDG_CONFIG_HOME")) & "/" else: return string(getEnv("HOME")) & "/.config/" proc getTempDir*(): string {.rtl, extern: "nos$1", @@ -553,25 +553,25 @@ proc getTempDir*(): string {.rtl, extern: "nos$1", proc expandTilde*(path: string): string {. tags: [ReadEnvEffect, ReadIOEffect].} = - ## Expands a path starting with ``~/`` to a full path. + ## Expands ``~`` or a path starting with ``~/`` to a full path, replacing + ## ``~`` with ``getHomeDir()`` (otherwise returns ``path`` unmodified). ## - ## If `path` starts with the tilde character and is followed by `/` or `\\` - ## this proc will return the reminder of the path appended to the result of - ## the getHomeDir() proc, otherwise the input path will be returned without - ## modification. - ## - ## The behaviour of this proc is the same on the Windows platform despite - ## not having this convention. Example: - ## - ## .. code-block:: nim - ## let configFile = expandTilde("~" / "appname.cfg") - ## echo configFile - ## # --> C:\Users\amber\appname.cfg - if len(path) > 1 and path[0] == '~' and (path[1] == '/' or path[1] == '\\'): + ## Windows: this is still supported despite Windows platform not having this + ## convention; also, both ``~/`` and ``~\`` are handled. + runnableExamples: + doAssert expandTilde("~" / "appname.cfg") == getHomeDir() / "appname.cfg" + if len(path) == 0 or path[0] != '~': + result = path + elif len(path) == 1: + result = getHomeDir() + elif (path[1] in {DirSep, AltSep}): result = getHomeDir() / path.substr(2) else: + # TODO: handle `~bob` and `~bob/` which means home of bob result = path +# TODO: consider whether quoteShellPosix, quoteShellWindows, quoteShell, quoteShellCommand +# belong in `strutils` instead; they are not specific to paths proc quoteShellWindows*(s: string): string {.noSideEffect, rtl, extern: "nosp$1".} = ## Quote s, so it can be safely passed to Windows API. ## Based on Python's subprocess.list2cmdline @@ -623,6 +623,18 @@ when defined(windows) or defined(posix) or defined(nintendoswitch): else: return quoteShellPosix(s) + proc quoteShellCommand*(args: openArray[string]): string = + ## Concatenates and quotes shell arguments `args` + runnableExamples: + when defined(posix): + assert quoteShellCommand(["aaa", "", "c d"]) == "aaa '' 'c d'" + when defined(windows): + assert quoteShellCommand(["aaa", "", "c d"]) == "aaa \"\" \"c d\"" + # can't use `map` pending https://github.com/nim-lang/Nim/issues/8303 + for i in 0..<args.len: + if i > 0: result.add " " + result.add quoteShell(args[i]) + when isMainModule: assert quoteShellWindows("aaa") == "aaa" assert quoteShellWindows("aaa\"") == "aaa\\\"" diff --git a/lib/pure/parseopt2.nim b/lib/pure/parseopt2.nim index b54a56c0c..51a70b6d1 100644 --- a/lib/pure/parseopt2.nim +++ b/lib/pure/parseopt2.nim @@ -44,10 +44,10 @@ type proc initOptParser*(cmdline: seq[string]): OptParser {.rtl.} = ## Initalizes option parses with cmdline. cmdline should not contain ## argument 0 - program name. - ## If cmdline == nil default to current command line arguments. + ## If cmdline.len == 0 default to current command line arguments. result.remainingShortOptions = "" when not defined(createNimRtl): - if cmdline == nil: + if cmdline.len == 0: result.cmd = commandLineParams() return else: @@ -60,7 +60,7 @@ proc initOptParser*(cmdline: string): OptParser {.rtl, deprecated.} = ## and calls initOptParser(openarray[string]) ## Do not use. if cmdline == "": # backward compatibility - return initOptParser(seq[string](nil)) + return initOptParser(@[]) else: return initOptParser(cmdline.split) diff --git a/lib/pure/parsexml.nim b/lib/pure/parsexml.nim index e0000aad3..fe933fb79 100644 --- a/lib/pure/parsexml.nim +++ b/lib/pure/parsexml.nim @@ -95,6 +95,7 @@ type kind: XmlEventKind err: XmlErrorKind state: ParserState + cIsEmpty: bool filename: string options: set[XmlParseOption] @@ -125,7 +126,8 @@ proc open*(my: var XmlParser, input: Stream, filename: string, my.kind = xmlError my.a = "" my.b = "" - my.c = nil + my.c = "" + my.cIsEmpty = true my.options = options proc close*(my: var XmlParser) {.inline.} = @@ -482,6 +484,7 @@ proc parseTag(my: var XmlParser) = my.kind = xmlElementOpen my.state = stateAttr my.c = my.a # save for later + my.cIsEmpty = false else: my.kind = xmlElementStart let slash = my.buf[my.bufpos] == '/' @@ -490,7 +493,8 @@ proc parseTag(my: var XmlParser) = if slash and my.buf[my.bufpos] == '>': inc(my.bufpos) my.state = stateEmptyElementTag - my.c = nil + my.c = "" + my.cIsEmpty = true elif my.buf[my.bufpos] == '>': inc(my.bufpos) else: @@ -678,7 +682,7 @@ proc next*(my: var XmlParser) = of stateEmptyElementTag: my.state = stateNormal my.kind = xmlElementEnd - if not my.c.isNil: + if not my.cIsEmpty: my.a = my.c of stateError: my.kind = xmlError diff --git a/lib/pure/pegs.nim b/lib/pure/pegs.nim index d16527a56..02a2d6900 100644 --- a/lib/pure/pegs.nim +++ b/lib/pure/pegs.nim @@ -765,7 +765,7 @@ template fillMatches(s, caps, c) = if startIdx != -1: caps[k] = substr(s, startIdx, endIdx) else: - caps[k] = nil + caps[k] = "" proc matchLen*(s: string, pattern: Peg, matches: var openArray[string], start = 0): int {.nosideEffect, rtl, extern: "npegs$1Capture".} = @@ -1854,7 +1854,7 @@ when isMainModule: assert match("prefix/start", peg"^start$", 7) if "foo" =~ peg"{'a'}?.*": - assert matches[0] == nil + assert matches[0].len == 0 else: assert false if "foo" =~ peg"{''}.*": diff --git a/lib/pure/ropes.nim b/lib/pure/ropes.nim index 9b9cdb52a..fb371cdce 100644 --- a/lib/pure/ropes.nim +++ b/lib/pure/ropes.nim @@ -37,7 +37,7 @@ type length: int data: string # != nil if a leaf -proc isConc(r: Rope): bool {.inline.} = return isNil(r.data) +proc isConc(r: Rope): bool {.inline.} = return r.length > 0 # Note that the left and right pointers are not needed for leafs. # Leaves have relatively high memory overhead (~30 bytes on a 32 @@ -50,12 +50,12 @@ proc isConc(r: Rope): bool {.inline.} = return isNil(r.data) proc len*(a: Rope): int {.rtl, extern: "nro$1".} = ## the rope's length if a == nil: result = 0 - else: result = a.length + else: result = abs a.length proc newRope(): Rope = new(result) proc newRope(data: string): Rope = new(result) - result.length = len(data) + result.length = -len(data) result.data = data var @@ -129,7 +129,7 @@ proc insertInCache(s: string, tree: Rope): Rope = result.left = t t.right = nil -proc rope*(s: string = nil): Rope {.rtl, extern: "nro$1Str".} = +proc rope*(s: string = ""): Rope {.rtl, extern: "nro$1Str".} = ## Converts a string to a rope. if s.len == 0: result = nil @@ -170,17 +170,7 @@ proc `&`*(a, b: Rope): Rope {.rtl, extern: "nroConcRopeRope".} = result = a else: result = newRope() - result.length = a.length + b.length - when false: - # XXX rebalancing would be nice, but is too expensive. - result.left = a.left - var x = newRope() - x.left = a.right - x.right = b - result.right = x - else: - result.left = a - result.right = b + result.length = abs(a.length) + abs(b.length) proc `&`*(a: Rope, b: string): Rope {.rtl, extern: "nroConcRopeStr".} = ## the concatenation operator for ropes. @@ -229,7 +219,6 @@ iterator leaves*(r: Rope): string = stack.add(it.right) it = it.left assert(it != nil) - assert(it.data != nil) yield it.data iterator items*(r: Rope): char = @@ -250,54 +239,6 @@ proc `$`*(r: Rope): string {.rtl, extern: "nroToString".}= result = newStringOfCap(r.len) for s in leaves(r): add(result, s) -when false: - # Format string caching seems reasonable: All leaves can be shared and format - # string parsing has to be done only once. A compiled format string is stored - # as a rope. A negative length is used for the index into the args array. - proc compiledArg(idx: int): Rope = - new(result) - result.length = -idx - - proc compileFrmt(frmt: string): Rope = - var i = 0 - var length = len(frmt) - result = nil - var num = 0 - while i < length: - if frmt[i] == '$': - inc(i) - case frmt[i] - of '$': - add(result, "$") - inc(i) - of '#': - inc(i) - add(result, compiledArg(num+1)) - inc(num) - of '0'..'9': - var j = 0 - while true: - j = j * 10 + ord(frmt[i]) - ord('0') - inc(i) - if frmt[i] notin {'0'..'9'}: break - add(s, compiledArg(j)) - of '{': - inc(i) - var j = 0 - while frmt[i] in {'0'..'9'}: - j = j * 10 + ord(frmt[i]) - ord('0') - inc(i) - if frmt[i] == '}': inc(i) - else: raise newException(EInvalidValue, "invalid format string") - add(s, compiledArg(j)) - else: raise newException(EInvalidValue, "invalid format string") - var start = i - while i < length: - if frmt[i] != '$': inc(i) - else: break - if i - 1 >= start: - add(result, substr(frmt, start, i-1)) - proc `%`*(frmt: string, args: openArray[Rope]): Rope {. rtl, extern: "nroFormat".} = ## `%` substitution operator for ropes. Does not support the ``$identifier`` diff --git a/lib/pure/smtp.nim b/lib/pure/smtp.nim index c2c674b84..d9b863a52 100644 --- a/lib/pure/smtp.nim +++ b/lib/pure/smtp.nim @@ -119,8 +119,7 @@ proc newSmtp*(useSsl = false, debug=false, when compiledWithSsl: sslContext.wrapSocket(result.sock) else: - raise newException(SystemError, - "SMTP module compiled without SSL support") + {.error: "SMTP module compiled without SSL support".} proc newAsyncSmtp*(useSsl = false, debug=false, sslContext = defaultSslContext): AsyncSmtp = @@ -133,8 +132,7 @@ proc newAsyncSmtp*(useSsl = false, debug=false, when compiledWithSsl: sslContext.wrapSocket(result.sock) else: - raise newException(SystemError, - "SMTP module compiled without SSL support") + {.error: "SMTP module compiled without SSL support".} proc quitExcpt(smtp: AsyncSmtp, msg: string): Future[void] = var retFuture = newFuture[void]() diff --git a/lib/pure/streams.nim b/lib/pure/streams.nim index 1ab73faea..09626136f 100644 --- a/lib/pure/streams.nim +++ b/lib/pure/streams.nim @@ -377,7 +377,10 @@ when not defined(js): proc ssClose(s: Stream) = var s = StringStream(s) - s.data = nil + when defined(nimNoNilSeqs): + s.data = "" + else: + s.data = nil proc newStringStream*(s: string = ""): StringStream = ## creates a new stream from the string `s`. diff --git a/lib/pure/strutils.nim b/lib/pure/strutils.nim index f8c5f9a91..33f153587 100644 --- a/lib/pure/strutils.nim +++ b/lib/pure/strutils.nim @@ -358,9 +358,6 @@ proc isNilOrEmpty*(s: string): bool {.noSideEffect, procvar, rtl, proc isNilOrWhitespace*(s: string): bool {.noSideEffect, procvar, rtl, extern: "nsuIsNilOrWhitespace".} = ## Checks if `s` is nil or consists entirely of whitespace characters. - if len(s) == 0: - return true - result = true for c in s: if not c.isSpaceAscii(): @@ -624,12 +621,13 @@ iterator rsplit*(s: string, sep: string, maxsplit: int = -1, ## Substrings are separated from the right by the string `sep` rsplitCommon(s, sep, maxsplit, sep.len) -iterator splitLines*(s: string): string = +iterator splitLines*(s: string, keepEol = false): string = ## Splits the string `s` into its containing lines. ## ## Every `character literal <manual.html#character-literals>`_ newline ## combination (CR, LF, CR-LF) is supported. The result strings contain no - ## trailing ``\n``. + ## trailing end of line characters unless parameter ``keepEol`` is set to + ## ``true``. ## ## Example: ## @@ -649,22 +647,30 @@ iterator splitLines*(s: string): string = ## "" var first = 0 var last = 0 + var eolpos = 0 while true: while last < s.len and s[last] notin {'\c', '\l'}: inc(last) - yield substr(s, first, last-1) - # skip newlines: - if last >= s.len: break - if s[last] == '\l': inc(last) - elif s[last] == '\c': - inc(last) - if last < s.len and s[last] == '\l': inc(last) + + eolpos = last + if last < s.len: + if s[last] == '\l': inc(last) + elif s[last] == '\c': + inc(last) + if last < s.len and s[last] == '\l': inc(last) + + yield substr(s, first, if keepEol: last-1 else: eolpos-1) + + # no eol characters consumed means that the string is over + if eolpos == last: + break + first = last -proc splitLines*(s: string): seq[string] {.noSideEffect, +proc splitLines*(s: string, keepEol = false): seq[string] {.noSideEffect, rtl, extern: "nsuSplitLines".} = ## The same as the `splitLines <#splitLines.i,string>`_ iterator, but is a ## proc that returns a sequence of substrings. - accumulateResult(splitLines(s)) + accumulateResult(splitLines(s, keepEol=keepEol)) proc countLines*(s: string): int {.noSideEffect, rtl, extern: "nsuCountLines".} = @@ -908,7 +914,7 @@ proc parseOctInt*(s: string): int {.noSideEffect, ## `s` are ignored. let L = parseutils.parseOct(s, result, 0) if L != s.len or L == 0: - raise newException(ValueError, "invalid oct integer: " & s) + raise newException(ValueError, "invalid oct integer: " & s) proc parseHexInt*(s: string): int {.noSideEffect, procvar, rtl, extern: "nsuParseHexInt".} = @@ -1369,9 +1375,11 @@ proc find*(s: string, sub: char, start: Natural = 0, last: Natural = 0): int {.n if sub == s[i]: return i else: when hasCStringBuiltin: - let found = c_memchr(s[start].unsafeAddr, sub, last-start+1) - if not found.isNil: - return cast[ByteAddress](found) -% cast[ByteAddress](s.cstring) + let L = last-start+1 + if L > 0: + let found = c_memchr(s[start].unsafeAddr, sub, L) + if not found.isNil: + return cast[ByteAddress](found) -% cast[ByteAddress](s.cstring) else: for i in start..last: if sub == s[i]: return i @@ -1518,7 +1526,7 @@ proc replace*(s, sub: string, by = ""): string {.noSideEffect, elif subLen == 1: # when the pattern is a single char, we use a faster # char-based search that doesn't need a skip table: - var c = sub[0] + let c = sub[0] let last = s.high var i = 0 while true: diff --git a/lib/pure/sugar.nim b/lib/pure/sugar.nim index 258b40191..8ded552d9 100644 --- a/lib/pure/sugar.nim +++ b/lib/pure/sugar.nim @@ -198,3 +198,41 @@ macro dump*(x: typed): untyped = let r = quote do: debugEcho `s`, " = ", `x` return r + +# TODO: consider exporting this in macros.nim +proc freshIdentNodes(ast: NimNode): NimNode = + # Replace NimIdent and NimSym by a fresh ident node + # see also https://github.com/nim-lang/Nim/pull/8531#issuecomment-410436458 + proc inspect(node: NimNode): NimNode = + case node.kind: + of nnkIdent, nnkSym: + result = ident($node) + of nnkEmpty, nnkLiterals: + result = node + else: + result = node.kind.newTree() + for child in node: + result.add inspect(child) + result = inspect(ast) + +macro distinctBase*(T: typedesc): untyped = + ## reverses ``type T = distinct A``; works recursively. + runnableExamples: + type T = distinct int + doAssert distinctBase(T) is int + doAssert: not compiles(distinctBase(int)) + type T2 = distinct T + doAssert distinctBase(T2) is int + + let typeNode = getTypeImpl(T) + expectKind(typeNode, nnkBracketExpr) + if typeNode[0].typeKind != ntyTypeDesc: + error "expected typeDesc, got " & $typeNode[0] + var typeSym = typeNode[1] + typeSym = getTypeImpl(typeSym) + if typeSym.typeKind != ntyDistinct: + error "type is not distinct" + typeSym = typeSym[0] + while typeSym.typeKind == ntyDistinct: + typeSym = getTypeImpl(typeSym)[0] + typeSym.freshIdentNodes diff --git a/lib/pure/times.nim b/lib/pure/times.nim index cdb7a4466..cbf3e6413 100644 --- a/lib/pure/times.nim +++ b/lib/pure/times.nim @@ -276,20 +276,22 @@ type FixedTimeUnit* = range[Nanoseconds..Weeks] ## Subrange of ``TimeUnit`` that only includes units of fixed duration. ## These are the units that can be represented by a ``Duration``. - Timezone* = object ## Timezone interface for supporting ``DateTime``'s of arbritary timezones. - ## The ``times`` module only supplies implementations for the systems local time and UTC. - ## The members ``zoneInfoFromUtc`` and ``zoneInfoFromTz`` should not be accessed directly - ## and are only exported so that ``Timezone`` can be implemented by other modules. - zoneInfoFromUtc*: proc (time: Time): ZonedTime {.tags: [], raises: [], benign.} - zoneInfoFromTz*: proc (adjTime: Time): ZonedTime {.tags: [], raises: [], benign.} - name*: string ## The name of the timezone, f.ex 'Europe/Stockholm' or 'Etc/UTC'. Used for checking equality. - ## Se also: https://en.wikipedia.org/wiki/List_of_tz_database_time_zones - - ZonedTime* = object ## Represents a zoned instant in time that is not associated with any calendar. - ## This type is only used for implementing timezones. - adjTime*: Time ## Time adjusted to a timezone. - utcOffset*: int ## Offset from UTC in seconds. - ## The point in time represented by ``ZonedTime`` is ``adjTime + utcOffset.seconds``. + Timezone* = ref object ## \ + ## Timezone interface for supporting ``DateTime``'s of arbritary + ## timezones. The ``times`` module only supplies implementations for the + ## systems local time and UTC. + zonedTimeFromTimeImpl: proc (x: Time): ZonedTime + {.tags: [], raises: [], benign.} + zonedTimeFromAdjTimeImpl: proc (x: Time): ZonedTime + {.tags: [], raises: [], benign.} + name: string + + ZonedTime* = object ## Represents a point in time with an associated + ## UTC offset and DST flag. This type is only used for + ## implementing timezones. + time*: Time ## The point in time being represented. + utcOffset*: int ## The offset in seconds west of UTC, + ## including any offset due to DST. isDst*: bool ## Determines whether DST is in effect. DurationParts* = array[FixedTimeUnit, int64] # Array of Duration parts starts @@ -343,10 +345,9 @@ proc normalize[T: Duration|Time](seconds, nanoseconds: int64): T = result.nanosecond = nanosecond.int # Forward declarations -proc utcZoneInfoFromUtc(time: Time): ZonedTime {.tags: [], raises: [], benign .} -proc utcZoneInfoFromTz(adjTime: Time): ZonedTime {.tags: [], raises: [], benign .} -proc localZoneInfoFromUtc(time: Time): ZonedTime {.tags: [], raises: [], benign .} -proc localZoneInfoFromTz(adjTime: Time): ZonedTime {.tags: [], raises: [], benign .} +proc utcTzInfo(time: Time): ZonedTime {.tags: [], raises: [], benign .} +proc localZonedTimeFromTime(time: Time): ZonedTime {.tags: [], raises: [], benign .} +proc localZonedTimeFromAdjTime(adjTime: Time): ZonedTime {.tags: [], raises: [], benign .} proc initTime*(unix: int64, nanosecond: NanosecondRange): Time {.tags: [], raises: [], benign noSideEffect.} @@ -493,7 +494,7 @@ proc fromEpochDay(epochday: int64): tuple[monthday: MonthdayRange, month: Month, proc getDayOfYear*(monthday: MonthdayRange, month: Month, year: int): YeardayRange {.tags: [], raises: [], benign .} = ## Returns the day of the year. - ## Equivalent with ``initDateTime(day, month, year).yearday``. + ## Equivalent with ``initDateTime(monthday, month, year, 0, 0, 0).yearday``. assertValidDate monthday, month, year const daysUntilMonth: array[Month, int] = [0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334] const daysUntilMonthLeap: array[Month, int] = [0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335] @@ -505,11 +506,11 @@ proc getDayOfYear*(monthday: MonthdayRange, month: Month, year: int): YeardayRan proc getDayOfWeek*(monthday: MonthdayRange, month: Month, year: int): WeekDay {.tags: [], raises: [], benign .} = ## Returns the day of the week enum from day, month and year. - ## Equivalent with ``initDateTime(day, month, year).weekday``. + ## Equivalent with ``initDateTime(monthday, month, year, 0, 0, 0).weekday``. assertValidDate monthday, month, year # 1970-01-01 is a Thursday, we adjust to the previous Monday let days = toEpochday(monthday, month, year) - 3 - let weeks = (if days >= 0: days else: days - 6) div 7 + let weeks = floorDiv(days, 7) let wd = days - weeks * 7 # The value of d is 0 for a Sunday, 1 for a Monday, 2 for a Tuesday, etc. # so we must correct for the WeekDay type. @@ -759,15 +760,14 @@ proc toTime*(dt: DateTime): Time {.tags: [], raises: [], benign.} = seconds.inc dt.hour * secondsInHour seconds.inc dt.minute * 60 seconds.inc dt.second - # The code above ignores the UTC offset of `timeInfo`, - # so we need to compensate for that here. seconds.inc dt.utcOffset result = initTime(seconds, dt.nanosecond) proc initDateTime(zt: ZonedTime, zone: Timezone): DateTime = ## Create a new ``DateTime`` using ``ZonedTime`` in the specified timezone. - let s = zt.adjTime.seconds - let epochday = (if s >= 0: s else: s - (secondsInDay - 1)) div secondsInDay + let adjTime = zt.time - initDuration(seconds = zt.utcOffset) + let s = adjTime.seconds + let epochday = floorDiv(s, secondsInDay) var rem = s - epochday * secondsInDay let hour = rem div secondsInHour rem = rem - hour * secondsInHour @@ -784,7 +784,7 @@ proc initDateTime(zt: ZonedTime, zone: Timezone): DateTime = hour: hour, minute: minute, second: second, - nanosecond: zt.adjTime.nanosecond, + nanosecond: zt.time.nanosecond, weekday: getDayOfWeek(d, m, y), yearday: getDayOfYear(d, m, y), isDst: zt.isDst, @@ -792,14 +792,55 @@ proc initDateTime(zt: ZonedTime, zone: Timezone): DateTime = utcOffset: zt.utcOffset ) -proc inZone*(time: Time, zone: Timezone): DateTime {.tags: [], raises: [], benign.} = - ## Break down ``time`` into a ``DateTime`` using ``zone`` as the timezone. - let zoneInfo = zone.zoneInfoFromUtc(time) - result = initDateTime(zoneInfo, zone) +proc newTimezone*( + name: string, + zonedTimeFromTimeImpl: proc (time: Time): ZonedTime {.tags: [], raises: [], benign.}, + zonedTimeFromAdjTimeImpl: proc (adjTime: Time): ZonedTime {.tags: [], raises: [], benign.} + ): Timezone = + ## Create a new ``Timezone``. + ## + ## ``zonedTimeFromTimeImpl`` and ``zonedTimeFromAdjTimeImpl`` is used + ## as the underlying implementations for ``zonedTimeFromTime`` and + ## ``zonedTimeFromAdjTime``. + ## + ## If possible, the name parameter should match the name used in the + ## tz database. If the timezone doesn't exist in the tz database, or if the + ## timezone name is unknown, then any string that describes the timezone + ## unambiguously can be used. Note that the timezones name is used for + ## checking equality! + runnableExamples: + proc utcTzInfo(time: Time): ZonedTime = + ZonedTime(utcOffset: 0, isDst: false, time: time) + let utc = newTimezone("Etc/UTC", utcTzInfo, utcTzInfo) + Timezone( + name: name, + zonedTimeFromTimeImpl: zonedTimeFromTimeImpl, + zonedTimeFromAdjTimeImpl: zonedTimeFromAdjTimeImpl + ) -proc inZone*(dt: DateTime, zone: Timezone): DateTime {.tags: [], raises: [], benign.} = - ## Convert ``dt`` into a ``DateTime`` using ``zone`` as the timezone. - dt.toTime.inZone(zone) +proc name*(zone: Timezone): string = + ## The name of the timezone. + ## + ## If possible, the name will be the name used in the tz database. + ## If the timezone doesn't exist in the tz database, or if the timezone + ## name is unknown, then any string that describes the timezone + ## unambiguously might be used. For example, the string "LOCAL" is used + ## for the systems local timezone. + ## + ## See also: https://en.wikipedia.org/wiki/Tz_database + zone.name + +proc zonedTimeFromTime*(zone: Timezone, time: Time): ZonedTime = + ## Returns the ``ZonedTime`` for some point in time. + zone.zonedTimeFromTimeImpl(time) + +proc zonedTimeFromAdjTime*(zone: TimeZone, adjTime: Time): ZonedTime = + ## Returns the ``ZonedTime`` for some local time. + ## + ## Note that the ``Time`` argument does not represent a point in time, it + ## represent a local time! E.g if ``adjTime`` is ``fromUnix(0)``, it should be + ## interpreted as 1970-01-01T00:00:00 in the ``zone`` timezone, not in UTC. + zone.zonedTimeFromAdjTimeImpl(adjTime) proc `$`*(zone: Timezone): string = ## Returns the name of the timezone. @@ -807,8 +848,20 @@ proc `$`*(zone: Timezone): string = proc `==`*(zone1, zone2: Timezone): bool = ## Two ``Timezone``'s are considered equal if their name is equal. + runnableExamples: + doAssert local() == local() + doAssert local() != utc() zone1.name == zone2.name +proc inZone*(time: Time, zone: Timezone): DateTime {.tags: [], raises: [], benign.} = + ## Convert ``time`` into a ``DateTime`` using ``zone`` as the timezone. + result = initDateTime(zone.zonedTimeFromTime(time), zone) + +proc inZone*(dt: DateTime, zone: Timezone): DateTime {.tags: [], raises: [], benign.} = + ## Returns a ``DateTime`` representing the same point in time as ``dt`` but + ## using ``zone`` as the timezone. + dt.toTime.inZone(zone) + proc toAdjTime(dt: DateTime): Time = let epochDay = toEpochday(dt.monthday, dt.month, dt.year) var seconds = epochDay * secondsInDay @@ -843,14 +896,14 @@ when defined(JS): proc getYear(js: JsDate): int {.tags: [], raises: [], benign, importcpp.} proc setFullYear(js: JsDate, year: int): void {.tags: [], raises: [], benign, importcpp.} - proc localZoneInfoFromUtc(time: Time): ZonedTime = + proc localZonedTimeFromTime(time: Time): ZonedTime = let jsDate = newDate(time.seconds.float * 1000) let offset = jsDate.getTimezoneOffset() * secondsInMin - result.adjTime = time - initDuration(seconds = offset) + result.time = time result.utcOffset = offset result.isDst = false - proc localZoneInfoFromTz(adjTime: Time): ZonedTime = + proc localZonedTimeFromAdjTime(adjTime: Time): ZonedTime = let utcDate = newDate(adjTime.seconds.float * 1000) let localDate = newDate(utcDate.getUTCFullYear(), utcDate.getUTCMonth(), utcDate.getUTCDate(), utcDate.getUTCHours(), utcDate.getUTCMinutes(), utcDate.getUTCSeconds(), 0) @@ -861,8 +914,8 @@ when defined(JS): if utcDate.getUTCFullYear() in 0 .. 99: localDate.setFullYear(utcDate.getUTCFullYear()) - result.adjTime = adjTime result.utcOffset = localDate.getTimezoneOffset() * secondsInMin + result.time = adjTime + initDuration(seconds = result.utcOffset) result.isDst = false else: @@ -892,7 +945,7 @@ else: weekday {.importc: "tm_wday".}, yearday {.importc: "tm_yday".}, isdst {.importc: "tm_isdst".}: cint - when defined(linux) and defined(amd64): + when defined(linux) and defined(amd64) or defined(haiku): gmtoff {.importc: "tm_gmtoff".}: clong zone {.importc: "tm_zone".}: cstring type @@ -915,13 +968,13 @@ else: return ((unix - tm.toAdjUnix).int, tm.isdst > 0) return (0, false) - proc localZoneInfoFromUtc(time: Time): ZonedTime = + proc localZonedTimeFromTime(time: Time): ZonedTime = let (offset, dst) = getLocalOffsetAndDst(time.seconds) - result.adjTime = time - initDuration(seconds = offset) + result.time = time result.utcOffset = offset result.isDst = dst - proc localZoneInfoFromTz(adjTime: Time): ZonedTime = + proc localZonedTimeFromAdjTime(adjTime: Time): ZonedTime = var adjUnix = adjTime.seconds let past = adjUnix - secondsInDay let (pastOffset, _) = getLocalOffsetAndDst(past) @@ -943,31 +996,34 @@ else: # as a result of offset changes (normally due to dst) let utcUnix = adjTime.seconds + utcOffset let (finalOffset, dst) = getLocalOffsetAndDst(utcUnix) - result.adjTime = initTime(utcUnix - finalOffset, adjTime.nanosecond) + result.time = initTime(utcUnix, adjTime.nanosecond) result.utcOffset = finalOffset result.isDst = dst -proc utcZoneInfoFromUtc(time: Time): ZonedTime = - result.adjTime = time - result.utcOffset = 0 - result.isDst = false +proc utcTzInfo(time: Time): ZonedTime = + ZonedTime(utcOffset: 0, isDst: false, time: time) -proc utcZoneInfoFromTz(adjTime: Time): ZonedTime = - utcZoneInfoFromUtc(adjTime) # adjTime == time since we are in UTC +var utcInstance {.threadvar.}: Timezone +var localInstance {.threadvar.}: Timezone proc utc*(): TimeZone = ## Get the ``Timezone`` implementation for the UTC timezone. runnableExamples: doAssert now().utc.timezone == utc() doAssert utc().name == "Etc/UTC" - Timezone(zoneInfoFromUtc: utcZoneInfoFromUtc, zoneInfoFromTz: utcZoneInfoFromTz, name: "Etc/UTC") + if utcInstance.isNil: + utcInstance = newTimezone("Etc/UTC", utcTzInfo, utcTzInfo) + result = utcInstance proc local*(): TimeZone = ## Get the ``Timezone`` implementation for the local timezone. runnableExamples: doAssert now().timezone == local() doAssert local().name == "LOCAL" - Timezone(zoneInfoFromUtc: localZoneInfoFromUtc, zoneInfoFromTz: localZoneInfoFromTz, name: "LOCAL") + if localInstance.isNil: + localInstance = newTimezone("LOCAL", localZonedTimeFromTime, + localZonedTimeFromAdjTime) + result = localInstance proc utc*(dt: DateTime): DateTime = ## Shorthand for ``dt.inZone(utc())``. @@ -1233,7 +1289,7 @@ proc initDateTime*(monthday: MonthdayRange, month: Month, year: int, second: second, nanosecond: nanosecond ) - result = initDateTime(zone.zoneInfoFromTz(dt.toAdjTime), zone) + result = initDateTime(zone.zonedTimeFromAdjTime(dt.toAdjTime), zone) proc initDateTime*(monthday: MonthdayRange, month: Month, year: int, hour: HourRange, minute: MinuteRange, second: SecondRange, @@ -1263,16 +1319,15 @@ proc `+`*(dt: DateTime, interval: TimeInterval): DateTime = let (adjDur, absDur) = evaluateInterval(dt, interval) if adjDur != DurationZero: - var zInfo = dt.timezone.zoneInfoFromTz(dt.toAdjTime + adjDur) + var zt = dt.timezone.zonedTimeFromAdjTime(dt.toAdjTime + adjDur) if absDur != DurationZero: - let offsetDur = initDuration(seconds = zInfo.utcOffset) - zInfo = dt.timezone.zoneInfoFromUtc(zInfo.adjTime + offsetDur + absDur) - result = initDateTime(zInfo, dt.timezone) + zt = dt.timezone.zonedTimeFromTime(zt.time + absDur) + result = initDateTime(zt, dt.timezone) else: - result = initDateTime(zInfo, dt.timezone) + result = initDateTime(zt, dt.timezone) else: - var zInfo = dt.timezone.zoneInfoFromUtc(dt.toTime + absDur) - result = initDateTime(zInfo, dt.timezone) + var zt = dt.timezone.zonedTimeFromTime(dt.toTime + absDur) + result = initDateTime(zt, dt.timezone) proc `-`*(dt: DateTime, interval: TimeInterval): DateTime = ## Subtract ``interval`` from ``dt``. Components from ``interval`` are subtracted @@ -1319,7 +1374,7 @@ proc `<=` * (a, b: DateTime): bool = return a.toTime <= b.toTime proc `==`*(a, b: DateTime): bool = - ## Returns true if ``a == b``, that is if both dates represent the same point in datetime. + ## Returns true if ``a == b``, that is if both dates represent the same point in time. return a.toTime == b.toTime @@ -2065,7 +2120,7 @@ proc toDateTime(p: ParsedTime, zone: Timezone, f: TimeFormat, if p.utcOffset.isNone: # No timezone parsed - assume timezone is `zone` - result = initDateTime(zone.zoneInfoFromTz(result.toAdjTime), zone) + result = initDateTime(zone.zonedTimeFromAdjTime(result.toAdjTime), zone) else: # Otherwise convert to `zone` result.utcOffset = p.utcOffset.get() @@ -2347,7 +2402,7 @@ proc fromSeconds*(since1970: int64): Time {.tags: [], raises: [], benign, deprec proc toSeconds*(time: Time): float {.tags: [], raises: [], benign, deprecated.} = ## Returns the time in seconds since the unix epoch. ## - ## **Deprecated since v0.18.0:** use ``fromUnix`` instead + ## **Deprecated since v0.18.0:** use ``toUnix`` instead time.seconds.float + time.nanosecond / convert(Seconds, Nanoseconds, 1) proc getLocalTime*(time: Time): DateTime {.tags: [], raises: [], benign, deprecated.} = @@ -2390,13 +2445,14 @@ proc timeInfoToTime*(dt: DateTime): Time {.tags: [], benign, deprecated.} = when defined(JS): var start = getTime() proc getStartMilsecs*(): int {.deprecated, tags: [TimeEffect], benign.} = - ## get the milliseconds from the start of the program. - ## **Deprecated since v0.8.10:** use ``epochTime`` or ``cpuTime`` instead. let dur = getTime() - start result = (convert(Seconds, Milliseconds, dur.seconds) + convert(Nanoseconds, Milliseconds, dur.nanosecond)).int else: proc getStartMilsecs*(): int {.deprecated, tags: [TimeEffect], benign.} = + ## get the milliseconds from the start of the program. + ## + ## **Deprecated since v0.8.10:** use ``epochTime`` or ``cpuTime`` instead. when defined(macosx): result = toInt(toFloat(int(getClock())) / (toFloat(clocksPerSec) / 1000.0)) else: @@ -2417,7 +2473,7 @@ proc getDayOfWeek*(day, month, year: int): WeekDay {.tags: [], raises: [], beni proc getDayOfWeekJulian*(day, month, year: int): WeekDay {.deprecated.} = ## Returns the day of the week enum from day, month and year, ## according to the Julian calendar. - ## **Deprecated since v0.18.0:** + ## **Deprecated since v0.18.0** # Day & month start from one. let a = (14 - month) div 12 @@ -2425,3 +2481,23 @@ proc getDayOfWeekJulian*(day, month, year: int): WeekDay {.deprecated.} = m = month + (12*a) - 2 d = (5 + day + y + (y div 4) + (31*m) div 12) mod 7 result = d.WeekDay + +proc adjTime*(zt: ZonedTime): Time + {.deprecated: "Use zt.time instead".} = + ## **Deprecated since v0.19.0:** use the ``time`` field instead. + zt.time - initDuration(seconds = zt.utcOffset) + +proc `adjTime=`*(zt: var ZonedTime, adjTime: Time) + {.deprecated: "Use zt.time instead".} = + ## **Deprecated since v0.19.0:** use the ``time`` field instead. + zt.time = adjTime + initDuration(seconds = zt.utcOffset) + +proc zoneInfoFromUtc*(zone: Timezone, time: Time): ZonedTime + {.deprecated: "Use zonedTimeFromTime instead".} = + ## **Deprecated since v0.19.0:** use ``zonedTimeFromTime`` instead. + zone.zonedTimeFromTime(time) + +proc zoneInfoFromTz*(zone: Timezone, adjTime: Time): ZonedTime + {.deprecated: "Use zonedTimeFromAdjTime instead".} = + ## **Deprecated since v0.19.0:** use the ``zonedTimeFromAdjTime`` instead. + zone.zonedTimeFromAdjTime(adjTime) \ No newline at end of file diff --git a/lib/pure/unittest.nim b/lib/pure/unittest.nim index d804ba7c8..757bf4745 100644 --- a/lib/pure/unittest.nim +++ b/lib/pure/unittest.nim @@ -176,10 +176,7 @@ method suiteEnded*(formatter: OutputFormatter) {.base, gcsafe.} = discard proc addOutputFormatter*(formatter: OutputFormatter) = - if formatters == nil: - formatters = @[formatter] - else: - formatters.add(formatter) + formatters.add(formatter) proc newConsoleOutputFormatter*(outputLevel: OutputLevel = PRINT_ALL, colorOutput = true): ConsoleOutputFormatter = @@ -225,7 +222,7 @@ method testStarted*(formatter: ConsoleOutputFormatter, testName: string) = formatter.isInTest = true method failureOccurred*(formatter: ConsoleOutputFormatter, checkpoints: seq[string], stackTrace: string) = - if stackTrace != nil: + if stackTrace.len > 0: echo stackTrace let prefix = if formatter.isInSuite: " " else: "" for msg in items(checkpoints): @@ -236,7 +233,7 @@ method testEnded*(formatter: ConsoleOutputFormatter, testResult: TestResult) = if formatter.outputLevel != PRINT_NONE and (formatter.outputLevel == PRINT_ALL or testResult.status == FAILED): - let prefix = if testResult.suiteName != nil: " " else: "" + let prefix = if testResult.suiteName.len > 0: " " else: "" template rawPrint() = echo(prefix, "[", $testResult.status, "] ", testResult.testName) when not defined(ECMAScript): if formatter.colorOutput and not defined(ECMAScript): @@ -301,7 +298,7 @@ method failureOccurred*(formatter: JUnitOutputFormatter, checkpoints: seq[string ## ``stackTrace`` is provided only if the failure occurred due to an exception. ## ``checkpoints`` is never ``nil``. formatter.testErrors.add(checkpoints) - if stackTrace != nil: + if stackTrace.len > 0: formatter.testStackTrace = stackTrace method testEnded*(formatter: JUnitOutputFormatter, testResult: TestResult) = @@ -392,7 +389,7 @@ proc shouldRun(currentSuiteName, testName: string): bool = return false proc ensureInitialized() = - if formatters == nil: + if formatters.len == 0: formatters = @[OutputFormatter(defaultConsoleFormatter())] if not disabledParamFiltering and not testsFilters.isValid: @@ -507,7 +504,7 @@ template test*(name, body) {.dirty.} = if testStatusIMPL == FAILED: programResult += 1 let testResult = TestResult( - suiteName: when declared(testSuiteName): testSuiteName else: nil, + suiteName: when declared(testSuiteName): testSuiteName else: "", testName: name, status: testStatusIMPL ) @@ -525,8 +522,6 @@ proc checkpoint*(msg: string) = ## checkpoint("Checkpoint B") ## ## outputs "Checkpoint A" once it fails. - if checkpoints == nil: - checkpoints = @[] checkpoints.add(msg) # TODO: add support for something like SCOPED_TRACE from Google Test @@ -557,7 +552,7 @@ template fail* = when declared(stackTrace): formatter.failureOccurred(checkpoints, stackTrace) else: - formatter.failureOccurred(checkpoints, nil) + formatter.failureOccurred(checkpoints, "") when not defined(ECMAScript): if abortOnError: quit(programResult) diff --git a/lib/pure/xmldom.nim b/lib/pure/xmldom.nim index 8cd47aa39..1a9e4ae26 100644 --- a/lib/pure/xmldom.nim +++ b/lib/pure/xmldom.nim @@ -217,9 +217,9 @@ proc createAttribute*(doc: PDocument, name: string): PAttr = new(attrNode) attrNode.fName = name attrNode.fNodeName = name - attrNode.fLocalName = nil - attrNode.prefix = nil - attrNode.fNamespaceURI = nil + attrNode.fLocalName = "" + attrNode.prefix = "" + attrNode.fNamespaceURI = "" attrNode.value = "" attrNode.fSpecified = false return attrNode @@ -254,7 +254,7 @@ proc createAttributeNS*(doc: PDocument, namespaceURI: string, qualifiedName: str attrNode.prefix = qualifiedName.split(':')[0] attrNode.fLocalName = qualifiedName.split(':')[1] else: - attrNode.prefix = nil + attrNode.prefix = "" attrNode.fLocalName = qualifiedName attrNode.value = "" @@ -298,9 +298,9 @@ proc createElement*(doc: PDocument, tagName: string): PElement = new(elNode) elNode.fTagName = tagName elNode.fNodeName = tagName - elNode.fLocalName = nil - elNode.prefix = nil - elNode.fNamespaceURI = nil + elNode.fLocalName = "" + elNode.prefix = "" + elNode.fNamespaceURI = "" elNode.childNodes = @[] elNode.attributes = @[] @@ -332,7 +332,7 @@ proc createElementNS*(doc: PDocument, namespaceURI: string, qualifiedName: strin elNode.prefix = qualifiedName.split(':')[0] elNode.fLocalName = qualifiedName.split(':')[1] else: - elNode.prefix = nil + elNode.prefix = "" elNode.fLocalName = qualifiedName elNode.fNamespaceURI = namespaceURI elNode.childNodes = @[] @@ -893,22 +893,22 @@ proc tagName*(el: PElement): string = proc getAttribute*(el: PNode, name: string): string = ## Retrieves an attribute value by ``name`` if isNil(el.attributes): - return nil + return "" var attribute = el.attributes.getNamedItem(name) if not isNil(attribute): return attribute.value else: - return nil + return "" proc getAttributeNS*(el: PNode, namespaceURI: string, localName: string): string = ## Retrieves an attribute value by ``localName`` and ``namespaceURI`` if isNil(el.attributes): - return nil + return "" var attribute = el.attributes.getNamedItemNS(namespaceURI, localName) if not isNil(attribute): return attribute.value else: - return nil + return "" proc getAttributeNode*(el: PElement, name: string): PAttr = ## Retrieves an attribute node by ``name`` diff --git a/lib/pure/xmldomparser.nim b/lib/pure/xmldomparser.nim index 7c7f7b99c..8d995102e 100644 --- a/lib/pure/xmldomparser.nim +++ b/lib/pure/xmldomparser.nim @@ -119,7 +119,7 @@ proc loadXMLStream*(stream: Stream): PDocument = ## a ``PDocument`` var x: XmlParser - open(x, stream, nil, {reportComments}) + open(x, stream, "", {reportComments}) var xmlDoc: PDocument var dom: PDOMImplementation = getDOM() @@ -161,7 +161,7 @@ when not defined(testing) and isMainModule: #echo(xml.getElementsByTagName("bla:test")[0].namespaceURI) #echo(xml.getElementsByTagName("test")[0].namespaceURI) for i in items(xml.getElementsByTagName("*")): - if i.namespaceURI != nil: + if i.namespaceURI.len > 0: echo(i.nodeName, "=", i.namespaceURI) diff --git a/lib/pure/xmltree.nim b/lib/pure/xmltree.nim index 47658b59b..d536cfed0 100644 --- a/lib/pure/xmltree.nim +++ b/lib/pure/xmltree.nim @@ -377,7 +377,6 @@ proc findAll*(n: XmlNode, tag: string, result: var seq[XmlNode]) = ## findAll(html, "img", tags) ## for imgTag in tags: ## process(imgTag) - assert isNil(result) == false assert n.k == xnElement for child in n.items(): if child.k != xnElement: diff --git a/lib/system.nim b/lib/system.nim index c033a632b..52ed524be 100644 --- a/lib/system.nim +++ b/lib/system.nim @@ -497,141 +497,103 @@ type raise_id: uint # set when exception is raised up: ref Exception # used for stacking exceptions. Not exported! - SystemError* = object of Exception ## \ - ## Abstract class for exceptions that the runtime system raises. - ## - ## See the full `exception hierarchy <manual.html#exception-handling-exception-hierarchy>`_. - IOError* = object of SystemError ## \ + Defect* = object of Exception ## \ + ## Abstract base class for all exceptions that Nim's runtime raises + ## but that are strictly uncatchable as they can also be mapped to + ## a ``quit`` / ``trap`` / ``exit`` operation. + + CatchableError* = object of Exception ## \ + ## Abstract class for all exceptions that are catchable. + IOError* = object of CatchableError ## \ ## Raised if an IO error occurred. - ## - ## See the full `exception hierarchy <manual.html#exception-handling-exception-hierarchy>`_. EOFError* = object of IOError ## \ ## Raised if an IO "end of file" error occurred. - ## - ## See the full `exception hierarchy <manual.html#exception-handling-exception-hierarchy>`_. - OSError* = object of SystemError ## \ + OSError* = object of CatchableError ## \ ## Raised if an operating system service failed. - ## - ## See the full `exception hierarchy <manual.html#exception-handling-exception-hierarchy>`_. errorCode*: int32 ## OS-defined error code describing this error. LibraryError* = object of OSError ## \ ## Raised if a dynamic library could not be loaded. - ## - ## See the full `exception hierarchy <manual.html#exception-handling-exception-hierarchy>`_. - ResourceExhaustedError* = object of SystemError ## \ + ResourceExhaustedError* = object of CatchableError ## \ ## Raised if a resource request could not be fulfilled. - ## - ## See the full `exception hierarchy <manual.html#exception-handling-exception-hierarchy>`_. - ArithmeticError* = object of Exception ## \ + ArithmeticError* = object of Defect ## \ ## Raised if any kind of arithmetic error occurred. - ## - ## See the full `exception hierarchy <manual.html#exception-handling-exception-hierarchy>`_. DivByZeroError* = object of ArithmeticError ## \ ## Raised for runtime integer divide-by-zero errors. - ## - ## See the full `exception hierarchy <manual.html#exception-handling-exception-hierarchy>`_. OverflowError* = object of ArithmeticError ## \ ## Raised for runtime integer overflows. ## ## This happens for calculations whose results are too large to fit in the - ## provided bits. See the full `exception hierarchy <manual.html#exception-handling-exception-hierarchy>`_. - AccessViolationError* = object of Exception ## \ + ## provided bits. + AccessViolationError* = object of Defect ## \ ## Raised for invalid memory access errors - ## - ## See the full `exception hierarchy <manual.html#exception-handling-exception-hierarchy>`_. - AssertionError* = object of Exception ## \ + AssertionError* = object of Defect ## \ ## Raised when assertion is proved wrong. ## - ## Usually the result of using the `assert() template <#assert>`_. See the - ## full `exception hierarchy <manual.html#exception-handling-exception-hierarchy>`_. - ValueError* = object of Exception ## \ + ## Usually the result of using the `assert() template <#assert>`_. + ValueError* = object of Defect ## \ ## Raised for string and object conversion errors. KeyError* = object of ValueError ## \ ## Raised if a key cannot be found in a table. ## ## Mostly used by the `tables <tables.html>`_ module, it can also be raised ## by other collection modules like `sets <sets.html>`_ or `strtabs - ## <strtabs.html>`_. See the full `exception hierarchy <manual.html#exception-handling-exception-hierarchy>`_. - OutOfMemError* = object of SystemError ## \ + ## <strtabs.html>`_. + OutOfMemError* = object of Defect ## \ ## Raised for unsuccessful attempts to allocate memory. - ## - ## See the full `exception hierarchy <manual.html#exception-handling-exception-hierarchy>`_. - IndexError* = object of Exception ## \ + IndexError* = object of Defect ## \ ## Raised if an array index is out of bounds. - ## - ## See the full `exception hierarchy <manual.html#exception-handling-exception-hierarchy>`_. - FieldError* = object of Exception ## \ + FieldError* = object of Defect ## \ ## Raised if a record field is not accessible because its dicriminant's ## value does not fit. - ## - ## See the full `exception hierarchy <manual.html#exception-handling-exception-hierarchy>`_. - RangeError* = object of Exception ## \ + RangeError* = object of Defect ## \ ## Raised if a range check error occurred. - ## - ## See the full `exception hierarchy <manual.html#exception-handling-exception-hierarchy>`_. - StackOverflowError* = object of SystemError ## \ + StackOverflowError* = object of Defect ## \ ## Raised if the hardware stack used for subroutine calls overflowed. - ## - ## See the full `exception hierarchy <manual.html#exception-handling-exception-hierarchy>`_. - ReraiseError* = object of Exception ## \ + ReraiseError* = object of Defect ## \ ## Raised if there is no exception to reraise. - ## - ## See the full `exception hierarchy <manual.html#exception-handling-exception-hierarchy>`_. - ObjectAssignmentError* = object of Exception ## \ + ObjectAssignmentError* = object of Defect ## \ ## Raised if an object gets assigned to its parent's object. - ## - ## See the full `exception hierarchy <manual.html#exception-handling-exception-hierarchy>`_. - ObjectConversionError* = object of Exception ## \ + ObjectConversionError* = object of Defect ## \ ## Raised if an object is converted to an incompatible object type. ## You can use ``of`` operator to check if conversion will succeed. - ## - ## See the full `exception hierarchy <manual.html#exception-handling-exception-hierarchy>`_. - FloatingPointError* = object of Exception ## \ + FloatingPointError* = object of Defect ## \ ## Base class for floating point exceptions. - ## - ## See the full `exception hierarchy <manual.html#exception-handling-exception-hierarchy>`_. FloatInvalidOpError* = object of FloatingPointError ## \ ## Raised by invalid operations according to IEEE. ## - ## Raised by ``0.0/0.0``, for example. See the full `exception - ## hierarchy <manual.html#exception-handling-exception-hierarchy>`_. + ## Raised by ``0.0/0.0``, for example. FloatDivByZeroError* = object of FloatingPointError ## \ ## Raised by division by zero. ## - ## Divisor is zero and dividend is a finite nonzero number. See the full - ## `exception hierarchy <manual.html#exception-handling-exception-hierarchy>`_. + ## Divisor is zero and dividend is a finite nonzero number. FloatOverflowError* = object of FloatingPointError ## \ ## Raised for overflows. ## ## The operation produced a result that exceeds the range of the exponent. - ## See the full `exception hierarchy <manual.html#exception-handling-exception-hierarchy>`_. FloatUnderflowError* = object of FloatingPointError ## \ ## Raised for underflows. ## ## The operation produced a result that is too small to be represented as a - ## normal number. See the full `exception hierarchy <manual.html#exception-handling-exception-hierarchy>`_. + ## normal number. FloatInexactError* = object of FloatingPointError ## \ ## Raised for inexact results. ## ## The operation produced a result that cannot be represented with infinite ## precision -- for example: ``2.0 / 3.0, log(1.1)`` ## - ## **NOTE**: Nim currently does not detect these! See the full - ## `exception hierarchy <manual.html#exception-handling-exception-hierarchy>`_. - DeadThreadError* = object of Exception ## \ + ## **NOTE**: Nim currently does not detect these! + DeadThreadError* = object of Defect ## \ ## Raised if it is attempted to send a message to a dead thread. - ## - ## See the full `exception hierarchy <manual.html#exception-handling-exception-hierarchy>`_. - NilAccessError* = object of SystemError ## \ + NilAccessError* = object of Defect ## \ ## Raised on dereferences of ``nil`` pointers. ## ## This is only raised if the ``segfaults.nim`` module was imported! when defined(nimNewRuntime): type - MoveError* = object of SystemError ## \ + MoveError* = object of Defect ## \ ## Raised on attempts to re-sink an already consumed ``sink`` parameter. when defined(js) or defined(nimdoc): @@ -985,6 +947,23 @@ else: proc `shl`*(x, y: int32): int32 {.magic: "ShlI", noSideEffect.} proc `shl`*(x, y: int64): int64 {.magic: "ShlI", noSideEffect.} +when defined(nimAshr): + proc ashr*(x: int, y: SomeInteger): int {.magic: "AshrI", noSideEffect.} + proc ashr*(x: int8, y: SomeInteger): int8 {.magic: "AshrI", noSideEffect.} + proc ashr*(x: int16, y: SomeInteger): int16 {.magic: "AshrI", noSideEffect.} + proc ashr*(x: int32, y: SomeInteger): int32 {.magic: "AshrI", noSideEffect.} + proc ashr*(x: int64, y: SomeInteger): int64 {.magic: "AshrI", noSideEffect.} + ## Shifts right by pushing copies of the leftmost bit in from the left, + ## and let the rightmost bits fall off. + ## + ## .. code-block:: Nim + ## 0b0001_0000'i8 shr 2 == 0b0000_0100'i8 + ## 0b1000_0000'i8 shr 8 == 0b1111_1111'i8 + ## 0b1000_0000'i8 shr 1 == 0b1100_0000'i8 +else: + # used for bootstrapping the compiler + proc ashr*[T](x: T, y: SomeInteger): T = discard + proc `and`*(x, y: int): int {.magic: "BitandI", noSideEffect.} proc `and`*(x, y: int8): int8 {.magic: "BitandI", noSideEffect.} proc `and`*(x, y: int16): int16 {.magic: "BitandI", noSideEffect.} @@ -1362,7 +1341,7 @@ const hostOS* {.magic: "HostOS".}: string = "" ## a string that describes the host operating system. Possible values: ## "windows", "macosx", "linux", "netbsd", "freebsd", "openbsd", "solaris", - ## "aix", "standalone". + ## "aix", "haiku", "standalone". hostCPU* {.magic: "HostCPU".}: string = "" ## a string that describes the host CPU. Possible values: @@ -1987,7 +1966,7 @@ when sizeof(int) <= 2: else: type IntLikeForCount = int|int8|int16|int32|char|bool|uint8|uint16|enum -iterator countdown*[T](a, b: T, step = 1): T {.inline.} = +iterator countdown*[T](a, b: T, step: Positive = 1): T {.inline.} = ## Counts from ordinal value `a` down to `b` (inclusive) with the given ## step count. `T` may be any ordinal type, `step` may only ## be positive. **Note**: This fails to count to ``low(int)`` if T = int for @@ -2010,7 +1989,7 @@ iterator countdown*[T](a, b: T, step = 1): T {.inline.} = dec(res, step) when defined(nimNewRoof): - iterator countup*[T](a, b: T, step = 1): T {.inline.} = + iterator countup*[T](a, b: T, step: Positive = 1): T {.inline.} = ## Counts from ordinal value `a` up to `b` (inclusive) with the given ## step count. `S`, `T` may be any ordinal type, `step` may only ## be positive. **Note**: This fails to count to ``high(int)`` if T = int for @@ -2027,7 +2006,7 @@ when defined(nimNewRoof): inc(res, step) iterator `..`*[T](a, b: T): T {.inline.} = - ## An alias for `countup`. + ## An alias for `countup(a, b, 1)`. when T is IntLikeForCount: var res = int(a) while res <= int(b): @@ -2313,9 +2292,9 @@ iterator mpairs*(a: var cstring): tuple[key: int, val: var char] {.inline.} = inc(i) -proc isNil*[T](x: seq[T]): bool {.noSideEffect, magic: "IsNil".} +proc isNil*[T](x: seq[T]): bool {.noSideEffect, magic: "IsNil", deprecated.} proc isNil*[T](x: ref T): bool {.noSideEffect, magic: "IsNil".} -proc isNil*(x: string): bool {.noSideEffect, magic: "IsNil".} +proc isNil*(x: string): bool {.noSideEffect, magic: "IsNil", deprecated.} proc isNil*[T](x: ptr T): bool {.noSideEffect, magic: "IsNil".} proc isNil*(x: pointer): bool {.noSideEffect, magic: "IsNil".} proc isNil*(x: cstring): bool {.noSideEffect, magic: "IsNil".} @@ -2374,8 +2353,12 @@ proc `==`*[T](x, y: seq[T]): bool {.noSideEffect.} = ## Generic equals operator for sequences: relies on a equals operator for ## the element type `T`. when nimvm: - if x.isNil and y.isNil: - return true + when not defined(nimNoNil): + if x.isNil and y.isNil: + return true + else: + if x.len == 0 and y.len == 0: + return true else: when not defined(JS): proc seqToPtr[T](x: seq[T]): pointer {.inline, nosideeffect.} = @@ -2512,7 +2495,7 @@ proc `$`*[T: tuple|object](x: T): string = result.add(name) result.add(": ") when compiles($value): - when compiles(value.isNil): + when value isnot string and value isnot seq and compiles(value.isNil): if value.isNil: result.add "nil" else: result.addQuoted(value) else: @@ -2531,7 +2514,7 @@ proc collectionToString[T](x: T, prefix, separator, suffix: string): string = else: result.add(separator) - when compiles(value.isNil): + when value isnot string and value isnot seq and compiles(value.isNil): # this branch should not be necessary if value.isNil: result.add "nil" @@ -2556,10 +2539,7 @@ proc `$`*[T](x: seq[T]): string = ## ## .. code-block:: nim ## $(@[23, 45]) == "@[23, 45]" - if x.isNil: - "nil" - else: - collectionToString(x, "@[", ", ", "]") + collectionToString(x, "@[", ", ", "]") # ----------------- GC interface --------------------------------------------- @@ -4001,19 +3981,28 @@ when hasAlloc: proc safeAdd*[T](x: var seq[T], y: T) {.noSideEffect, deprecated.} = ## Adds ``y`` to ``x`` unless ``x`` is not yet initialized; in that case, ## ``x`` becomes ``@[y]`` - if x == nil: x = @[y] - else: x.add(y) + when defined(nimNoNilSeqs): + x.add(y) + else: + if x == nil: x = @[y] + else: x.add(y) proc safeAdd*(x: var string, y: char) {.noSideEffect, deprecated.} = ## Adds ``y`` to ``x``. If ``x`` is ``nil`` it is initialized to ``""`` - if x == nil: x = "" - x.add(y) + when defined(nimNoNilSeqs): + x.add(y) + else: + if x == nil: x = "" + x.add(y) proc safeAdd*(x: var string, y: string) {.noSideEffect, deprecated.} = ## Adds ``y`` to ``x`` unless ``x`` is not yet initalized; in that ## case, ``x`` becomes ``y`` - if x == nil: x = y - else: x.add(y) + when defined(nimNoNilSeqs): + x.add(y) + else: + if x == nil: x = y + else: x.add(y) proc locals*(): RootObj {.magic: "Plugin", noSideEffect.} = ## generates a tuple constructor expression listing all the local variables @@ -4075,6 +4064,11 @@ proc `==`*(x, y: cstring): bool {.magic: "EqCString", noSideEffect, elif x.isNil or y.isNil: result = false else: result = strcmp(x, y) == 0 +when defined(nimNoNilSeqs2): + when not compileOption("nilseqs"): + proc `==`*(x: string; y: type(nil)): bool {.error.} = discard + proc `==`*(x: type(nil); y: string): bool {.error.} = discard + template closureScope*(body: untyped): untyped = ## Useful when creating a closure in a loop to capture local loop variables by ## their current iteration values. Example: @@ -4189,8 +4183,13 @@ when defined(cpp) and appType != "lib" and let ex = getCurrentException() let trace = ex.getStackTrace() - stderr.write trace & "Error: unhandled exception: " & ex.msg & - " [" & $ex.name & "]\n" + when defined(genode): + # stderr not available by default, use the LOG session + echo trace & "Error: unhandled exception: " & ex.msg & + " [" & $ex.name & "]\n" + else: + stderr.write trace & "Error: unhandled exception: " & ex.msg & + " [" & $ex.name & "]\n" quit 1 when not defined(js): diff --git a/lib/system/alloc.nim b/lib/system/alloc.nim index ffb7aaf86..b090117a9 100644 --- a/lib/system/alloc.nim +++ b/lib/system/alloc.nim @@ -454,10 +454,10 @@ proc requestOsChunks(a: var MemRegion, size: int): PBigChunk = a.addHeapLink(result, size) when defined(debugHeapLinks): cprintf("owner: %p; result: %p; next pointer %p; size: %ld\n", addr(a), - result, result.heapLink, result.origSize) + result, result.heapLink, result.size) when defined(memtracker): - trackLocation(addr result.origSize, sizeof(int)) + trackLocation(addr result.size, sizeof(int)) sysAssert((cast[ByteAddress](result) and PageMask) == 0, "requestOsChunks 1") #zeroMem(result, size) @@ -527,7 +527,7 @@ proc updatePrevSize(a: var MemRegion, c: PBigChunk, proc splitChunk2(a: var MemRegion, c: PBigChunk, size: int): PBigChunk = result = cast[PBigChunk](cast[ByteAddress](c) +% size) result.size = c.size - size - track("result.origSize", addr result.origSize, sizeof(int)) + track("result.size", addr result.size, sizeof(int)) # XXX check if these two nil assignments are dead code given # addChunkToMatrix's implementation: result.next = nil @@ -602,7 +602,7 @@ proc getBigChunk(a: var MemRegion, size: int): PBigChunk = splitChunk(a, result, size) # set 'used' to to true: result.prevSize = 1 - track("setUsedToFalse", addr result.origSize, sizeof(int)) + track("setUsedToFalse", addr result.size, sizeof(int)) incl(a, a.chunkStarts, pageIndex(result)) dec(a.freeMem, size) @@ -968,7 +968,7 @@ proc deallocOsPages(a: var MemRegion) = let (p, size) = it.chunks[i] when defined(debugHeapLinks): cprintf("owner %p; dealloc A: %p size: %ld; next: %p\n", addr(a), - it, it.origSize, next) + it, it.size, next) sysAssert size >= PageSize, "origSize too small" osDeallocPages(p, size) it = next diff --git a/lib/system/ansi_c.nim b/lib/system/ansi_c.nim index f593d4c99..67e42c0af 100644 --- a/lib/system/ansi_c.nim +++ b/lib/system/ansi_c.nim @@ -50,7 +50,7 @@ when defined(windows): SIGTERM = cint(15) elif defined(macosx) or defined(linux) or defined(freebsd) or defined(openbsd) or defined(netbsd) or defined(solaris) or - defined(dragonfly) or defined(nintendoswitch): + defined(dragonfly) or defined(nintendoswitch) or defined(genode): const SIGABRT = cint(6) SIGFPE = cint(8) @@ -59,6 +59,15 @@ elif defined(macosx) or defined(linux) or defined(freebsd) or SIGSEGV = cint(11) SIGTERM = cint(15) SIGPIPE = cint(13) +elif defined(haiku): + const + SIGABRT = cint(6) + SIGFPE = cint(8) + SIGILL = cint(4) + SIGINT = cint(2) + SIGSEGV = cint(11) + SIGTERM = cint(15) + SIGPIPE = cint(7) else: when NoFakeVars: {.error: "SIGABRT not ported to your platform".} @@ -74,6 +83,8 @@ else: when defined(macosx): const SIGBUS = cint(10) +elif defined(haiku): + const SIGBUS = cint(30) else: template SIGBUS: untyped = SIGSEGV diff --git a/lib/system/channels.nim b/lib/system/channels.nim index 254b87dfc..14d3a3005 100644 --- a/lib/system/channels.nim +++ b/lib/system/channels.nim @@ -233,7 +233,7 @@ proc send*[TMsg](c: var Channel[TMsg], msg: TMsg) {.inline.} = proc trySend*[TMsg](c: var Channel[TMsg], msg: TMsg): bool {.inline.} = ## Tries to send a message to a thread. `msg` is deeply copied. Doesn't block. ## Returns `false` if the message was not sent because number of pending items - ## in the cannel exceeded `maxItems`. + ## in the channel exceeded `maxItems`. sendImpl(cast[PRawChannel](addr c), cast[PNimType](getTypeInfo(msg)), unsafeAddr(msg), true) proc llRecv(q: PRawChannel, res: pointer, typ: PNimType) = diff --git a/lib/system/excpt.nim b/lib/system/excpt.nim index 1e590965a..7b4979cfa 100644 --- a/lib/system/excpt.nim +++ b/lib/system/excpt.nim @@ -31,7 +31,11 @@ proc showErrorMessage(data: cstring) {.gcsafe.} = if errorMessageWriter != nil: errorMessageWriter($data) else: - writeToStdErr(data) + when defined(genode): + # stderr not available by default, use the LOG session + echo data + else: + writeToStdErr(data) proc quitOrDebug() {.inline.} = when not defined(endb): diff --git a/lib/system/gc_common.nim b/lib/system/gc_common.nim index dcea0c4cc..88e150cd1 100644 --- a/lib/system/gc_common.nim +++ b/lib/system/gc_common.nim @@ -37,22 +37,28 @@ when defined(nimTypeNames): a[j] = v if h == 1: break - proc dumpNumberOfInstances* = - # also add the allocated strings to the list of known types: + iterator dumpHeapInstances*(): tuple[name: cstring; count: int; sizes: int] = + ## Iterate over summaries of types on heaps. + ## This data may be inaccurate if allocations + ## are made by the iterator body. if strDesc.nextType == nil: strDesc.nextType = nimTypeRoot strDesc.name = "string" nimTypeRoot = addr strDesc + var it = nimTypeRoot + while it != nil: + if (it.instances > 0 or it.sizes != 0): + yield (it.name, it.instances, it.sizes) + it = it.nextType + + proc dumpNumberOfInstances* = var a: InstancesInfo var n = 0 - var it = nimTypeRoot var totalAllocated = 0 - while it != nil: - if (it.instances > 0 or it.sizes != 0) and n < a.len: - a[n] = (it.name, it.instances, it.sizes) - inc n + for it in dumpHeapInstances(): + a[n] = it + inc n inc totalAllocated, it.sizes - it = it.nextType sortInstances(a, n) for i in 0 .. n-1: c_fprintf(stdout, "[Heap] %s: #%ld; bytes: %ld\n", a[i][0], a[i][1], a[i][2]) diff --git a/lib/system/jssys.nim b/lib/system/jssys.nim index e500444ea..152b48c24 100644 --- a/lib/system/jssys.nim +++ b/lib/system/jssys.nim @@ -108,7 +108,7 @@ proc getStackTrace*(e: ref Exception): string = e.trace proc unhandledException(e: ref Exception) {. compilerproc, asmNoStackFrame.} = var buf = "" - if e.msg != nil and e.msg[0] != '\0': + if e.msg.len != 0: add(buf, "Error: unhandled exception: ") add(buf, e.msg) else: diff --git a/lib/system/memtracker.nim b/lib/system/memtracker.nim index ae0297438..1b1f18039 100644 --- a/lib/system/memtracker.nim +++ b/lib/system/memtracker.nim @@ -73,12 +73,12 @@ proc addEntry(entry: LogEntry) = let x = cast[proc() {.nimcall, tags: [], gcsafe, locks: 0.}](writeStackTrace) x() quit 1 - if gLog.count > high(gLog.data): - gLogger(gLog) - gLog.count = 0 - gLog.data[gLog.count] = entry - inc gLog.count - gLog.disabled = false + #if gLog.count > high(gLog.data): + # gLogger(gLog) + # gLog.count = 0 + #gLog.data[gLog.count] = entry + #inc gLog.count + #gLog.disabled = false proc memTrackerWrite(address: pointer; size: int; file: cstring; line: int) {.compilerProc.} = addEntry LogEntry(op: "write", address: address, diff --git a/lib/system/osalloc.nim b/lib/system/osalloc.nim index 85c796ba0..06e89f130 100644 --- a/lib/system/osalloc.nim +++ b/lib/system/osalloc.nim @@ -207,6 +207,9 @@ elif defined(posix): # some arches like mips and alpha use different values const MAP_ANONYMOUS = 0x20 const MAP_PRIVATE = 0x02 # Changes are private + elif defined(haiku): + const MAP_ANONYMOUS = 0x08 + const MAP_PRIVATE = 0x02 else: var MAP_ANONYMOUS {.importc: "MAP_ANONYMOUS", header: "<sys/mman.h>".}: cint diff --git a/lib/system/sysstr.nim b/lib/system/sysstr.nim index 951435972..6438a0541 100644 --- a/lib/system/sysstr.nim +++ b/lib/system/sysstr.nim @@ -66,6 +66,7 @@ proc copyStrLast(s: NimString, start, last: int): NimString {.compilerProc.} = # This is not used by most recent versions of the compiler anymore, but # required for bootstrapping purposes. let start = max(start, 0) + if s == nil: return nil let len = min(last, s.len-1) - start + 1 if len > 0: result = rawNewStringNoInit(len) @@ -78,6 +79,7 @@ proc copyStrLast(s: NimString, start, last: int): NimString {.compilerProc.} = proc copyStr(s: NimString, start: int): NimString {.compilerProc.} = # This is not used by most recent versions of the compiler anymore, but # required for bootstrapping purposes. + if s == nil: return nil result = copyStrLast(s, start, s.len-1) proc nimToCStringConv(s: NimString): cstring {.compilerProc, inline.} = diff --git a/lib/system/threads.nim b/lib/system/threads.nim index 3bfaa1dc2..2434ea21e 100644 --- a/lib/system/threads.nim +++ b/lib/system/threads.nim @@ -162,10 +162,12 @@ elif defined(genode): mainTls else: - when not defined(macosx): + when not (defined(macosx) or defined(haiku)): {.passL: "-pthread".} - {.passC: "-pthread".} + when not defined(haiku): + {.passC: "-pthread".} + const schedh = "#define _GNU_SOURCE\n#include <sched.h>" pthreadh = "#define _GNU_SOURCE\n#include <pthread.h>" @@ -714,3 +716,13 @@ elif defined(solaris): if threadId == 0: threadId = int(thr_self()) result = threadId + +elif defined(haiku): + type thr_id {.importc: "thread_id", header: "<OS.h>".} = distinct int32 + proc find_thread(name: cstring): thr_id {.importc, header: "<OS.h>".} + + proc getThreadId*(): int = + ## get the ID of the currently running thread. + if threadId == 0: + threadId = int(find_thread(nil)) + result = threadId diff --git a/lib/windows/winlean.nim b/lib/windows/winlean.nim index 3263f1d37..60a6e5d9b 100644 --- a/lib/windows/winlean.nim +++ b/lib/windows/winlean.nim @@ -396,7 +396,7 @@ else: proc moveFileA*(lpExistingFileName, lpNewFileName: cstring): WINBOOL {. importc: "MoveFileA", stdcall, dynlib: "kernel32".} - proc moveFileExA*(lpExistingFileName, lpNewFileName: WideCString, + proc moveFileExA*(lpExistingFileName, lpNewFileName: cstring, flags: DWORD): WINBOOL {. importc: "MoveFileExA", stdcall, dynlib: "kernel32".} |