diff options
Diffstat (limited to 'lib')
107 files changed, 2075 insertions, 2269 deletions
diff --git a/lib/core/macros.nim b/lib/core/macros.nim index 7217475c6..fa5cd67df 100644 --- a/lib/core/macros.nim +++ b/lib/core/macros.nim @@ -14,11 +14,8 @@ include "system/inclrtl" ## .. include:: ../../doc/astspec.txt -# If you look for the implementation of the magic symbols, copy the -# magic string and open the file "../../compiler/vm.nim" and search -# for the magic string with the prefix "opc". For example the -# implementation of ``{.magic: "FooBar".}`` will be right under -# ``of opcFooBar:``. +# If you look for the implementation of the magic symbol +# ``{.magic: "Foo".}``, search for `mFoo` and `opcFoo`. type NimNodeKind* = enum @@ -388,7 +385,7 @@ type {.deprecated: [TBindSymRule: BindSymRule].} -proc bindSym*(ident: string, rule: BindSymRule = brClosed): NimNode {. +proc bindSym*(ident: static[string], rule: BindSymRule = brClosed): NimNode {. magic: "NBindSym", noSideEffect.} ## creates a node that binds `ident` to a symbol node. The bound symbol ## may be an overloaded symbol. @@ -709,8 +706,7 @@ proc treeRepr*(n: NimNode): string {.compileTime, benign.} = res.add(($n.kind).substr(3)) case n.kind - of nnkEmpty: discard # same as nil node in this representation - of nnkNilLit: res.add(" nil") + of nnkEmpty, nnkNilLit: discard # same as nil node in this representation of nnkCharLit..nnkInt64Lit: res.add(" " & $n.intVal) of nnkFloatLit..nnkFloat64Lit: res.add(" " & $n.floatVal) of nnkStrLit..nnkTripleStrLit, nnkIdent, nnkSym: @@ -733,8 +729,7 @@ proc lispRepr*(n: NimNode): string {.compileTime, benign.} = add(result, "(") case n.kind - of nnkEmpty: discard # same as nil node in this representation - of nnkNilLit: add(result, "nil") + of nnkEmpty, nnkNilLit: discard # same as nil node in this representation of nnkCharLit..nnkInt64Lit: add(result, $n.intVal) of nnkFloatLit..nnkFloat64Lit: add(result, $n.floatVal) of nnkStrLit..nnkTripleStrLit, nnkCommentStmt, nnkident, nnkSym: @@ -769,7 +764,7 @@ proc astGenRepr*(n: NimNode): string {.compileTime, benign.} = ## See also `repr`, `treeRepr`, and `lispRepr`. const - NodeKinds = {nnkEmpty, nnkNilLit, nnkIdent, nnkSym, nnkNone, nnkCommentStmt} + NodeKinds = {nnkEmpty, nnkIdent, nnkSym, nnkNone, nnkCommentStmt} LitKinds = {nnkCharLit..nnkInt64Lit, nnkFloatLit..nnkFloat64Lit, nnkStrLit..nnkTripleStrLit} proc traverse(res: var string, level: int, n: NimNode) {.benign.} = @@ -778,12 +773,13 @@ proc astGenRepr*(n: NimNode): string {.compileTime, benign.} = res.add("new" & ($n.kind).substr(3) & "Node(") elif n.kind in LitKinds: res.add("newLit(") + elif n.kind == nnkNilLit: + res.add("newNilLit()") else: res.add($n.kind) case n.kind - of nnkEmpty: discard - of nnkNilLit: res.add("nil") + of nnkEmpty, nnkNilLit: discard of nnkCharLit: res.add("'" & $chr(n.intVal) & "'") of nnkIntLit..nnkInt64Lit: res.add($n.intVal) of nnkFloatLit..nnkFloat64Lit: res.add($n.floatVal) @@ -1292,7 +1288,11 @@ proc customPragmaNode(n: NimNode): NimNode = typ = n.getTypeInst() if typ.typeKind == ntyTypeDesc: - return typ[1].getImpl()[0][1] + let impl = typ[1].getImpl() + if impl[0].kind == nnkPragmaExpr: + return impl[0][1] + else: + return impl[0] # handle types which don't have macro at all if n.kind == nnkSym: # either an variable or a proc let impl = n.getImpl() diff --git a/lib/deprecated/pure/asyncio.nim b/lib/deprecated/pure/asyncio.nim index 5fd45b215..34cabefb0 100644 --- a/lib/deprecated/pure/asyncio.nim +++ b/lib/deprecated/pure/asyncio.nim @@ -101,8 +101,8 @@ when defined(windows): from winlean import TimeVal, SocketHandle, FD_SET, FD_ZERO, TFdSet, FD_ISSET, select else: - from posix import TimeVal, SocketHandle, FD_SET, FD_ZERO, TFdSet, - FD_ISSET, select + from posix import TimeVal, Time, Suseconds, SocketHandle, FD_SET, FD_ZERO, + TFdSet, FD_ISSET, select type DelegateObj* = object @@ -556,8 +556,12 @@ proc send*(sock: AsyncSocket, data: string) = proc timeValFromMilliseconds(timeout = 500): Timeval = if timeout != -1: var seconds = timeout div 1000 - result.tv_sec = seconds.int32 - result.tv_usec = ((timeout - seconds * 1000) * 1000).int32 + when defined(posix): + result.tv_sec = seconds.Time + result.tv_usec = ((timeout - seconds * 1000) * 1000).Suseconds + else: + result.tv_sec = seconds.int32 + result.tv_usec = ((timeout - seconds * 1000) * 1000).int32 proc createFdSet(fd: var TFdSet, s: seq[Delegate], m: var int) = FD_ZERO(fd) diff --git a/lib/deprecated/pure/ftpclient.nim b/lib/deprecated/pure/ftpclient.nim index ed2f14450..7645258b6 100644 --- a/lib/deprecated/pure/ftpclient.nim +++ b/lib/deprecated/pure/ftpclient.nim @@ -12,7 +12,7 @@ import sockets, strutils, parseutils, times, os, asyncio from asyncnet import nil from nativesockets import nil -from asyncdispatch import PFuture +from asyncdispatch import Future ## **Note**: This module is deprecated since version 0.11.3. ## You should use the async version of this module ## `asyncftpclient <asyncftpclient.html>`_. diff --git a/lib/deprecated/pure/sockets.nim b/lib/deprecated/pure/sockets.nim index f068c7d56..05aebef76 100644 --- a/lib/deprecated/pure/sockets.nim +++ b/lib/deprecated/pure/sockets.nim @@ -32,7 +32,7 @@ include "system/inclrtl" -{.deadCodeElim: on.} +{.deadCodeElim: on.} # dce option deprecated when hostOS == "solaris": {.passl: "-lsocket -lnsl".} @@ -953,8 +953,12 @@ when defined(ssl): proc timeValFromMilliseconds(timeout = 500): Timeval = if timeout != -1: var seconds = timeout div 1000 - result.tv_sec = seconds.int32 - result.tv_usec = ((timeout - seconds * 1000) * 1000).int32 + when defined(posix): + result.tv_sec = seconds.Time + result.tv_usec = ((timeout - seconds * 1000) * 1000).Suseconds + else: + result.tv_sec = seconds.int32 + result.tv_usec = ((timeout - seconds * 1000) * 1000).int32 proc createFdSet(fd: var TFdSet, s: seq[Socket], m: var int) = FD_ZERO(fd) diff --git a/lib/impure/db_sqlite.nim b/lib/impure/db_sqlite.nim index 21049571f..b1541c1ba 100644 --- a/lib/impure/db_sqlite.nim +++ b/lib/impure/db_sqlite.nim @@ -81,7 +81,7 @@ ## ## theDb.close() -{.deadCodeElim:on.} +{.deadCodeElim: on.} # dce option deprecated import strutils, sqlite3 @@ -126,6 +126,7 @@ proc tryExec*(db: DbConn, query: SqlQuery, args: varargs[string, `$`]): bool {. tags: [ReadDbEffect, WriteDbEffect].} = ## tries to execute the query and returns true if successful, false otherwise. + assert(not db.isNil, "Database not connected.") var q = dbFormat(query, args) var stmt: sqlite3.Pstmt if prepare_v2(db, q, q.len.cint, stmt, nil) == SQLITE_OK: @@ -144,6 +145,7 @@ proc newRow(L: int): Row = proc setupQuery(db: DbConn, query: SqlQuery, args: varargs[string]): Pstmt = + assert(not db.isNil, "Database not connected.") var q = dbFormat(query, args) if prepare_v2(db, q, q.len.cint, result, nil) != SQLITE_OK: dbError(db) @@ -267,6 +269,7 @@ proc tryInsertID*(db: DbConn, query: SqlQuery, {.tags: [WriteDbEffect], raises: [].} = ## executes the query (typically "INSERT") and returns the ## generated ID for the row or -1 in case of an error. + assert(not db.isNil, "Database not connected.") var q = dbFormat(query, args) var stmt: sqlite3.Pstmt result = -1 diff --git a/lib/impure/nre.nim b/lib/impure/nre.nim index 3d4afc0ae..6058128dd 100644 --- a/lib/impure/nre.nim +++ b/lib/impure/nre.nim @@ -10,7 +10,7 @@ from pcre import nil import nre.private.util import tables -from strutils import toLower, `%` +from strutils import `%` from math import ceil import options from unicode import runeLenAt @@ -326,15 +326,15 @@ proc `$`*(pattern: RegexMatch): string = proc `==`*(a, b: Regex): bool = if not a.isNil and not b.isNil: - return a.pattern == b.pattern and - a.pcreObj == b.pcreObj and + return a.pattern == b.pattern and + a.pcreObj == b.pcreObj and a.pcreExtra == b.pcreExtra else: return system.`==`(a, b) proc `==`*(a, b: RegexMatch): bool = return a.pattern == b.pattern and - a.str == b.str + a.str == b.str # }}} # Creation & Destruction {{{ @@ -645,7 +645,6 @@ template replaceImpl(str: string, pattern: Regex, let bounds = match.matchBounds result.add(str.substr(lastIdx, bounds.a - 1)) let nextVal = replacement - assert(nextVal != nil) result.add(nextVal) lastIdx = bounds.b + 1 diff --git a/lib/impure/rdstdin.nim b/lib/impure/rdstdin.nim index 5aa4cfcc3..54bab82f0 100644 --- a/lib/impure/rdstdin.nim +++ b/lib/impure/rdstdin.nim @@ -13,7 +13,7 @@ ## is used. This suffices because Windows' console already provides the ## wanted functionality. -{.deadCodeElim: on.} +{.deadCodeElim: on.} # dce option deprecated when defined(Windows): proc readLineFromStdin*(prompt: string): TaintedString {. diff --git a/lib/js/dom.nim b/lib/js/dom.nim index cd7609729..fd81fdf3f 100644 --- a/lib/js/dom.nim +++ b/lib/js/dom.nim @@ -62,6 +62,7 @@ type frames*: seq[TFrame] screen*: Screen performance*: Performance + onpopstate*: proc (event: Event) Frame* = ref FrameObj FrameObj {.importc.} = object of WindowObj @@ -175,6 +176,12 @@ type text*: cstring value*: cstring + TextAreaElement* = ref object of ElementObj + value*: cstring + selectionStart*, selectionEnd*: int + selectionDirection*: cstring + rows*, cols*: int + FormElement* = ref FormObj FormObj {.importc.} = object of ElementObj action*: cstring @@ -446,6 +453,7 @@ type proc addEventListener*(et: EventTarget, ev: cstring, cb: proc(ev: Event), useCapture: bool = false) proc addEventListener*(et: EventTarget, ev: cstring, cb: proc(ev: Event), options: AddEventListenerOptions) proc removeEventListener*(et: EventTarget, ev: cstring, cb: proc(ev: Event), useCapture: bool = false) +proc dispatchEvent*(et: EventTarget, ev: Event) # Window "methods" proc alert*(w: Window, msg: cstring) @@ -500,7 +508,7 @@ proc removeAttributeNode*(n, attr: Node) proc removeChild*(n, child: Node) proc replaceChild*(n, newNode, oldNode: Node) proc replaceData*(n: Node, start, len: int, text: cstring) -proc scrollIntoView*(n: Node) +proc scrollIntoView*(n: Node, alignToTop: bool=true) proc setAttribute*(n: Node, name, value: cstring) proc setAttributeNode*(n: Node, attr: Node) @@ -596,6 +604,7 @@ proc parseFloat*(s: cstring): BiggestFloat {.importc, nodecl.} proc parseInt*(s: cstring): int {.importc, nodecl.} proc parseInt*(s: cstring, radix: int):int {.importc, nodecl.} +proc newEvent*(name: cstring): Event {.importcpp: "new Event(@)", constructor.} type TEventHandlers* {.deprecated.} = EventTargetObj diff --git a/lib/packages/docutils/rst.nim b/lib/packages/docutils/rst.nim index 223fc836a..adac16777 100644 --- a/lib/packages/docutils/rst.nim +++ b/lib/packages/docutils/rst.nim @@ -43,8 +43,8 @@ type mwUnsupportedField MsgHandler* = proc (filename: string, line, col: int, msgKind: MsgKind, - arg: string) {.nimcall.} ## what to do in case of an error - FindFileHandler* = proc (filename: string): string {.nimcall.} + arg: string) {.closure.} ## what to do in case of an error + FindFileHandler* = proc (filename: string): string {.closure.} const messages: array[MsgKind, string] = [ @@ -853,7 +853,6 @@ type DirKind = enum # must be ordered alphabetically! dkNone, dkAuthor, dkAuthors, dkCode, dkCodeBlock, dkContainer, dkContents, dkFigure, dkImage, dkInclude, dkIndex, dkRaw, dkTitle -{.deprecated: [TDirKind: DirKind].} const DirIds: array[0..12, string] = ["", "author", "authors", "code", @@ -1114,7 +1113,6 @@ proc parseHeadline(p: var RstParser): PRstNode = type IntSeq = seq[int] -{.deprecated: [TIntSeq: IntSeq].} proc tokEnd(p: RstParser): int = result = p.tok[p.idx].col + len(p.tok[p.idx].symbol) - 1 @@ -1408,8 +1406,6 @@ type hasArg, hasOptions, argIsFile, argIsWord DirFlags = set[DirFlag] SectionParser = proc (p: var RstParser): PRstNode {.nimcall.} -{.deprecated: [TDirFlag: DirFlag, TDirFlags: DirFlags, - TSectionParser: SectionParser].} proc parseDirective(p: var RstParser, flags: DirFlags): PRstNode = ## Parses arguments and options for a directive block. diff --git a/lib/packages/docutils/rstast.nim b/lib/packages/docutils/rstast.nim index 7be4470c1..f3596b571 100644 --- a/lib/packages/docutils/rstast.nim +++ b/lib/packages/docutils/rstast.nim @@ -70,8 +70,6 @@ type ## the document or the section level*: int ## valid for some node kinds sons*: RstNodeSeq ## the node's sons -{.deprecated: [TRstNodeKind: RstNodeKind, TRstNodeSeq: RstNodeSeq, - TRstNode: RstNode].} proc len*(n: PRstNode): int = result = len(n.sons) @@ -99,7 +97,6 @@ type RenderContext {.pure.} = object indent: int verbatim: int -{.deprecated: [TRenderContext: RenderContext].} proc renderRstToRst(d: var RenderContext, n: PRstNode, result: var string) {.gcsafe.} diff --git a/lib/packages/docutils/rstgen.nim b/lib/packages/docutils/rstgen.nim index ee1f0076c..1547c888d 100644 --- a/lib/packages/docutils/rstgen.nim +++ b/lib/packages/docutils/rstgen.nim @@ -774,7 +774,7 @@ proc renderImage(d: PDoc, n: PRstNode, result: var string) = Digits + Letters + WhiteSpace) let arg = getArgument(n) - isObject = arg.toLower().endsWith(".svg") + isObject = arg.toLowerAscii().endsWith(".svg") var options = "" content = "" diff --git a/lib/posix/epoll.nim b/lib/posix/epoll.nim index c5ed1a873..2d38137bb 100644 --- a/lib/posix/epoll.nim +++ b/lib/posix/epoll.nim @@ -7,7 +7,7 @@ # distribution, for details about the copyright. # -{.deadCodeElim:on.} +{.deadCodeElim: on.} # dce option deprecated from posix import SocketHandle diff --git a/lib/posix/inotify.nim b/lib/posix/inotify.nim index a206f8067..359e88617 100644 --- a/lib/posix/inotify.nim +++ b/lib/posix/inotify.nim @@ -7,7 +7,7 @@ # distribution, for details about the copyright. # -{.deadCodeElim:on.} +{.deadCodeElim: on.} # dce option deprecated # Get the platform-dependent flags. # Structure describing an inotify event. diff --git a/lib/posix/kqueue.nim b/lib/posix/kqueue.nim index 730491a53..18b47f5d5 100644 --- a/lib/posix/kqueue.nim +++ b/lib/posix/kqueue.nim @@ -7,8 +7,6 @@ # distribution, for details about the copyright. # -{.deadCodeElim:on.} - from posix import Timespec when defined(macosx) or defined(freebsd) or defined(openbsd) or @@ -61,7 +59,7 @@ const EV_CLEAR* = 0x0020 ## Clear event state after reporting. EV_RECEIPT* = 0x0040 ## Force EV_ERROR on success, data == 0 EV_DISPATCH* = 0x0080 ## Disable event after reporting. - + EV_SYSFLAGS* = 0xF000 ## Reserved by system EV_DROP* = 0x1000 ## Not should be dropped EV_FLAG1* = 0x2000 ## Filter-specific flag @@ -87,10 +85,10 @@ when defined(macosx) or defined(freebsd) or defined(dragonfly): NOTE_FFAND* = 0x40000000'u32 ## AND fflags NOTE_FFOR* = 0x80000000'u32 ## OR fflags NOTE_FFCOPY* = 0xc0000000'u32 ## copy fflags - NOTE_FFCTRLMASK* = 0xc0000000'u32 ## masks for operations + NOTE_FFCTRLMASK* = 0xc0000000'u32 ## masks for operations NOTE_FFLAGSMASK* = 0x00ffffff'u32 - NOTE_TRIGGER* = 0x01000000'u32 ## Cause the event to be triggered + NOTE_TRIGGER* = 0x01000000'u32 ## Cause the event to be triggered ## for output. # data/hint flags for EVFILT_{READ|WRITE}, shared with userspace diff --git a/lib/posix/linux.nim b/lib/posix/linux.nim index 6f9f75e34..25c7c7979 100644 --- a/lib/posix/linux.nim +++ b/lib/posix/linux.nim @@ -1,4 +1,4 @@ -{.deadCodeElim:on.} +{.deadCodeElim: on.} # dce option deprecated import posix diff --git a/lib/posix/posix.nim b/lib/posix/posix.nim index a23d76050..3ff156bdf 100644 --- a/lib/posix/posix.nim +++ b/lib/posix/posix.nim @@ -27,10 +27,10 @@ ## resulting C code will just ``#include <XYZ.h>`` and *not* define the ## symbols declared here. -# This ensures that we don't accidentally generate #includes for files that -# might not exist on a specific platform! The user will get an error only -# if they actualy try to use the missing declaration -{.deadCodeElim: on.} +# Dead code elimination ensures that we don't accidentally generate #includes +# for files that might not exist on a specific platform! The user will get an +# error only if they actualy try to use the missing declaration +{.deadCodeElim: on.} # dce option deprecated # TODO these constants don't seem to be fetched from a header file for unknown # platforms - where do they come from and why are they here? diff --git a/lib/posix/posix_linux_amd64.nim b/lib/posix/posix_linux_amd64.nim index 9e6211b63..4f114d394 100644 --- a/lib/posix/posix_linux_amd64.nim +++ b/lib/posix/posix_linux_amd64.nim @@ -351,8 +351,8 @@ type Timeval* {.importc: "struct timeval", header: "<sys/select.h>", final, pure.} = object ## struct timeval - tv_sec*: clong ## Seconds. - tv_usec*: clong ## Microseconds. + tv_sec*: Time ## Seconds. + tv_usec*: Suseconds ## Microseconds. TFdSet* {.importc: "fd_set", header: "<sys/select.h>", final, pure.} = object abi: array[1024 div (8 * sizeof(clong)), clong] diff --git a/lib/posix/posix_other.nim b/lib/posix/posix_other.nim index 01bc1c1e5..004a4205b 100644 --- a/lib/posix/posix_other.nim +++ b/lib/posix/posix_other.nim @@ -7,7 +7,7 @@ # distribution, for details about the copyright. # -{.deadCodeElim:on.} +{.deadCodeElim: on.} # dce option deprecated const hasSpawnH = not defined(haiku) # should exist for every Posix system nowadays @@ -335,8 +335,8 @@ type Timeval* {.importc: "struct timeval", header: "<sys/select.h>", final, pure.} = object ## struct timeval - tv_sec*: int ## Seconds. - tv_usec*: int ## Microseconds. + tv_sec*: Time ## Seconds. + tv_usec*: Suseconds ## Microseconds. TFdSet* {.importc: "fd_set", header: "<sys/select.h>", final, pure.} = object Mcontext* {.importc: "mcontext_t", header: "<ucontext.h>", diff --git a/lib/posix/termios.nim b/lib/posix/termios.nim index f86e408fb..60d540107 100644 --- a/lib/posix/termios.nim +++ b/lib/posix/termios.nim @@ -7,7 +7,7 @@ # distribution, for details about the copyright. # -{.deadCodeElim: on.} +{.deadCodeElim: on.} # dce option deprecated import posix type diff --git a/lib/pure/algorithm.nim b/lib/pure/algorithm.nim index 45e031574..2b668d1ca 100644 --- a/lib/pure/algorithm.nim +++ b/lib/pure/algorithm.nim @@ -13,9 +13,6 @@ type SortOrder* = enum ## sort order Descending, Ascending -{.deprecated: [TSortOrder: SortOrder].} - - proc `*`*(x: int, order: SortOrder): int {.inline.} = ## flips `x` if ``order == Descending``; ## if ``order == Ascending`` then `x` is returned. @@ -69,21 +66,25 @@ proc reversed*[T](a: openArray[T]): seq[T] = proc binarySearch*[T](a: openArray[T], key: T): int = ## binary search for `key` in `a`. Returns -1 if not found. - var b = len(a) - while result < b: - var mid = (result + b) div 2 - if a[mid] < key: result = mid + 1 - else: b = mid - if result >= len(a) or a[result] != key: result = -1 - -proc smartBinarySearch*[T](a: openArray[T], key: T): int = - ## ``a.len`` must be a power of 2 for this to work. - var step = a.len div 2 - while step > 0: - if a[result or step] <= key: - result = result or step - step = step shr 1 - if a[result] != key: result = -1 + if ((a.len - 1) and a.len) == 0 and a.len > 0: + # when `a.len` is a power of 2, a faster div can be used. + var step = a.len div 2 + while step > 0: + if a[result or step] <= key: + result = result or step + step = step shr 1 + if a[result] != key: result = -1 + else: + var b = len(a) + while result < b: + var mid = (result + b) div 2 + if a[mid] < key: result = mid + 1 + else: b = mid + if result >= len(a) or a[result] != key: result = -1 + +proc smartBinarySearch*[T](a: openArray[T], key: T): int {.deprecated.} = + ## **Deprecated since version 0.18.1**; Use ``binarySearch`` instead. + binarySearch(a,key) const onlySafeCode = true @@ -363,10 +364,11 @@ when isMainModule: var srt1 = [1,2,3,4,4,4,4,5] var srt2 = ["iello","hello"] var srt3 = [1.0,1.0,1.0] - var srt4: seq[int] = @[] + var srt4: seq[int] assert srt1.isSorted(cmp) == true assert srt2.isSorted(cmp) == false assert srt3.isSorted(cmp) == true + assert srt4.isSorted(cmp) == true var srtseq = newSeq[int]() assert srtseq.isSorted(cmp) == true # Tests for reversed @@ -395,7 +397,7 @@ proc rotateInternal[T](arg: var openarray[T]; first, middle, last: int): int = swap(arg[mFirst], arg[next]) mFirst += 1 - next += 1 + next += 1 if mFirst == mMiddle: mMiddle = next @@ -514,4 +516,24 @@ when isMainModule: block fillEmptySeq: var s = newSeq[int]() - s.fill(0) \ No newline at end of file + s.fill(0) + + block testBinarySearch: + var noData: seq[int] + doAssert binarySearch(noData, 7) == -1 + let oneData = @[1] + doAssert binarySearch(oneData, 1) == 0 + doAssert binarySearch(onedata, 7) == -1 + let someData = @[1,3,4,7] + doAssert binarySearch(someData, 1) == 0 + doAssert binarySearch(somedata, 7) == 3 + doAssert binarySearch(someData, -1) == -1 + doAssert binarySearch(someData, 5) == -1 + doAssert binarySearch(someData, 13) == -1 + let moreData = @[1,3,5,7,4711] + doAssert binarySearch(moreData, -1) == -1 + doAssert binarySearch(moreData, 1) == 0 + doAssert binarySearch(moreData, 5) == 2 + doAssert binarySearch(moreData, 6) == -1 + doAssert binarySearch(moreData, 4711) == 4 + doAssert binarySearch(moreData, 4712) == -1 diff --git a/lib/pure/asyncfutures.nim b/lib/pure/asyncfutures.nim index 6df6527d5..863a6843b 100644 --- a/lib/pure/asyncfutures.nim +++ b/lib/pure/asyncfutures.nim @@ -27,8 +27,6 @@ type FutureError* = object of Exception cause*: FutureBase -{.deprecated: [PFutureBase: FutureBase, PFuture: Future].} - when not defined(release): var currentID = 0 @@ -177,7 +175,7 @@ proc fail*[T](future: Future[T], error: ref Exception) = if getStackTrace(error) == "": getStackTrace() else: getStackTrace(error) future.callbacks.call() -proc clearCallbacks(future: FutureBase) = +proc clearCallbacks*(future: FutureBase) = future.callbacks.function = nil future.callbacks.next = nil diff --git a/lib/pure/asynchttpserver.nim b/lib/pure/asynchttpserver.nim index ba1615651..fe5a835d7 100644 --- a/lib/pure/asynchttpserver.nim +++ b/lib/pure/asynchttpserver.nim @@ -60,9 +60,6 @@ type reusePort: bool maxBody: int ## The maximum content-length that will be read for the body. -{.deprecated: [TRequest: Request, PAsyncHttpServer: AsyncHttpServer, - THttpCode: HttpCode, THttpVersion: HttpVersion].} - proc newAsyncHttpServer*(reuseAddr = true, reusePort = false, maxBody = 8388608): AsyncHttpServer = ## Creates a new ``AsyncHttpServer`` instance. diff --git a/lib/pure/asyncnet.nim b/lib/pure/asyncnet.nim index a75f9daac..e7552e3e3 100644 --- a/lib/pure/asyncnet.nim +++ b/lib/pure/asyncnet.nim @@ -134,8 +134,6 @@ type protocol: Protocol AsyncSocket* = ref AsyncSocketDesc -{.deprecated: [PAsyncSocket: AsyncSocket].} - proc newAsyncSocket*(fd: AsyncFD, domain: Domain = AF_INET, sockType: SockType = SOCK_STREAM, protocol: Protocol = IPPROTO_TCP, buffered = true): AsyncSocket = @@ -201,7 +199,7 @@ when defineSsl: flags: set[SocketFlag]) {.async.} = let len = bioCtrlPending(socket.bioOut) if len > 0: - var data = newStringOfCap(len) + var data = newString(len) let read = bioRead(socket.bioOut, addr data[0], len) assert read != 0 if read < 0: diff --git a/lib/pure/base64.nim b/lib/pure/base64.nim index 4b0d08292..bfb8a1666 100644 --- a/lib/pure/base64.nim +++ b/lib/pure/base64.nim @@ -52,7 +52,7 @@ template encodeInternal(s: typed, lineLen: int, newLine: string): untyped = if numLines > 0: inc(total, (numLines - 1) * newLine.len) result = newString(total) - var + var i = 0 r = 0 currLine = 0 @@ -76,7 +76,7 @@ template encodeInternal(s: typed, lineLen: int, newLine: string): untyped = currLine = 0 if i < s.len-1: - let + let a = ord(s[i]) b = ord(s[i+1]) result[r] = cb64[a shr 2] @@ -130,11 +130,11 @@ proc decode*(s: string): string = # total is an upper bound, as we will skip arbitrary whitespace: result = newString(total) - var + var i = 0 r = 0 while true: - while s[i] in Whitespace: inc(i) + while i < s.len and s[i] in Whitespace: inc(i) if i < s.len-3: let a = s[i].decodeByte diff --git a/lib/pure/cgi.nim b/lib/pure/cgi.nim index 5de6aa487..e0cdc2ec0 100644 --- a/lib/pure/cgi.nim +++ b/lib/pure/cgi.nim @@ -64,8 +64,6 @@ type methodPost, ## query uses the POST method methodGet ## query uses the GET method -{.deprecated: [TRequestMethod: RequestMethod, ECgi: CgiError].} - proc cgiError*(msg: string) {.noreturn.} = ## raises an ECgi exception with message `msg`. var e: ref CgiError @@ -97,11 +95,10 @@ iterator decodeData*(data: string): tuple[key, value: TaintedString] = var name = "" var value = "" # decode everything in one pass: - while data[i] != '\0': + while i < data.len: setLen(name, 0) # reuse memory - while true: + while i < data.len: case data[i] - of '\0': break of '%': var x = 0 handleHexChar(data[i+1], x) @@ -112,15 +109,16 @@ iterator decodeData*(data: string): tuple[key, value: TaintedString] = of '=', '&': break else: add(name, data[i]) inc(i) - if data[i] != '=': cgiError("'=' expected") + if i >= data.len or data[i] != '=': cgiError("'=' expected") inc(i) # skip '=' setLen(value, 0) # reuse memory - while true: + while i < data.len: case data[i] of '%': var x = 0 - handleHexChar(data[i+1], x) - handleHexChar(data[i+2], x) + if i+2 < data.len: + handleHexChar(data[i+1], x) + handleHexChar(data[i+2], x) inc(i, 2) add(value, chr(x)) of '+': add(value, ' ') @@ -128,9 +126,9 @@ iterator decodeData*(data: string): tuple[key, value: TaintedString] = else: add(value, data[i]) inc(i) yield (name.TaintedString, value.TaintedString) - if data[i] == '&': inc(i) - elif data[i] == '\0': break - else: cgiError("'&' expected") + if i < data.len: + if data[i] == '&': inc(i) + else: cgiError("'&' expected") iterator decodeData*(allowedMethods: set[RequestMethod] = {methodNone, methodPost, methodGet}): tuple[key, value: TaintedString] = diff --git a/lib/pure/collections/critbits.nim b/lib/pure/collections/critbits.nim index 34f5c5470..5ae5e26b2 100644 --- a/lib/pure/collections/critbits.nim +++ b/lib/pure/collections/critbits.nim @@ -74,18 +74,19 @@ proc rawInsert[T](c: var CritBitTree[T], key: string): Node[T] = var newByte = 0 block blockX: while newbyte < key.len: - if it.key[newbyte] != key[newbyte]: - newotherbits = it.key[newbyte].ord xor key[newbyte].ord + let ch = if newbyte < it.key.len: it.key[newbyte] else: '\0' + if ch != key[newbyte]: + newotherbits = ch.ord xor key[newbyte].ord break blockX inc newbyte - if it.key[newbyte] != '\0': + if newbyte < it.key.len: newotherbits = it.key[newbyte].ord else: return it while (newOtherBits and (newOtherBits-1)) != 0: newOtherBits = newOtherBits and (newOtherBits-1) newOtherBits = newOtherBits xor 255 - let ch = it.key[newByte] + let ch = if newByte < it.key.len: it.key[newByte] else: '\0' let dir = (1 + (ord(ch) or newOtherBits)) shr 8 var inner: Node[T] @@ -162,13 +163,13 @@ proc containsOrIncl*(c: var CritBitTree[void], key: string): bool = var n = rawInsert(c, key) result = c.count == oldCount -proc inc*(c: var CritBitTree[int]; key: string) = - ## counts the 'key'. +proc inc*(c: var CritBitTree[int]; key: string, val: int = 1) = + ## increments `c[key]` by `val`. let oldCount = c.count var n = rawInsert(c, key) - if c.count == oldCount: + if c.count == oldCount or oldCount == 0: # not a new key: - inc n.val + inc n.val, val proc incl*(c: var CritBitTree[void], key: string) = ## includes `key` in `c`. @@ -351,3 +352,13 @@ when isMainModule: assert toSeq(r.items) == @["abc", "definition", "prefix", "xyz"] assert toSeq(r.itemsWithPrefix("de")) == @["definition"] + var c = CritBitTree[int]() + + c.inc("a") + assert c["a"] == 1 + + c.inc("a", 4) + assert c["a"] == 5 + + c.inc("a", -5) + assert c["a"] == 0 diff --git a/lib/pure/collections/sets.nim b/lib/pure/collections/sets.nim index 32b6387ad..59c90bc2b 100644 --- a/lib/pure/collections/sets.nim +++ b/lib/pure/collections/sets.nim @@ -120,6 +120,13 @@ iterator items*[A](s: HashSet[A]): A = for h in 0..high(s.data): if isFilled(s.data[h].hcode): yield s.data[h].key +proc hash*[A](s: HashSet[A]): Hash = + ## hashing of HashSet + assert s.isValid, "The set needs to be initialized." + for h in 0..high(s.data): + result = result xor s.data[h].hcode + result = !$result + const growthFactor = 2 @@ -690,6 +697,13 @@ iterator items*[A](s: OrderedSet[A]): A = forAllOrderedPairs: yield s.data[h].key +proc hash*[A](s: OrderedSet[A]): Hash = + ## hashing of OrderedSet + assert s.isValid, "The set needs to be initialized." + forAllOrderedPairs: + result = result !& s.data[h].hcode + result = !$result + iterator pairs*[A](s: OrderedSet[A]): tuple[a: int, b: A] = assert s.isValid, "The set needs to be initialized" forAllOrderedPairs: diff --git a/lib/pure/colors.nim b/lib/pure/colors.nim index 4ec76dee0..25a2fb870 100644 --- a/lib/pure/colors.nim +++ b/lib/pure/colors.nim @@ -14,8 +14,6 @@ import strutils type Color* = distinct int ## a color stored as RGB -{.deprecated: [TColor: Color].} - proc `==` *(a, b: Color): bool {.borrow.} ## compares two colors. diff --git a/lib/pure/complex.nim b/lib/pure/complex.nim index ccde5ee0a..98cab1a5a 100644 --- a/lib/pure/complex.nim +++ b/lib/pure/complex.nim @@ -25,8 +25,6 @@ type Complex* = tuple[re, im: float] ## a complex number, consisting of a real and an imaginary part -{.deprecated: [TComplex: Complex].} - proc toComplex*(x: SomeInteger): Complex = ## Convert some integer ``x`` to a complex number. result.re = x diff --git a/lib/pure/concurrency/threadpool.nim b/lib/pure/concurrency/threadpool.nim index a5eaec86e..6ec71e912 100644 --- a/lib/pure/concurrency/threadpool.nim +++ b/lib/pure/concurrency/threadpool.nim @@ -168,6 +168,15 @@ proc wakeupWorkerToProcessQueue(w: ptr Worker) = signal(w.q.empty) signal(w.taskArrived) +proc attach(fv: FlowVarBase; i: int): bool = + acquire(fv.cv.L) + if fv.cv.counter <= 0: + fv.idx = i + result = true + else: + result = false + release(fv.cv.L) + proc finished(fv: FlowVarBase) = doAssert fv.ai.isNil, "flowVar is still attached to an 'awaitAny'" # we have to protect against the rare cases where the owner of the flowVar @@ -245,26 +254,27 @@ proc `^`*[T](fv: FlowVar[T]): T = proc awaitAny*(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 await([a,b]) and await([b,c]) the second + ## 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 ## on, -1 is returned. - ## **Note**: This results in non-deterministic behaviour and so should be - ## avoided. + ## **Note**: This results in non-deterministic behaviour and should be avoided. var ai: AwaitInfo ai.cv.initSemaphore() var conflicts = 0 + result = -1 for i in 0 .. flowVars.high: if cas(addr flowVars[i].ai, nil, addr ai): - flowVars[i].idx = i + if not attach(flowVars[i], i): + result = i + break else: inc conflicts if conflicts < flowVars.len: - await(ai.cv) - result = ai.idx + if result < 0: + await(ai.cv) + result = ai.idx for i in 0 .. flowVars.high: discard cas(addr flowVars[i].ai, addr ai, nil) - else: - result = -1 destroySemaphore(ai.cv) proc isReady*(fv: FlowVarBase): bool = @@ -321,7 +331,7 @@ proc slave(w: ptr Worker) {.thread.} = await(w.taskArrived) # XXX Somebody needs to look into this (why does this assertion fail # in Visual Studio?) - when not defined(vcc): assert(not w.ready) + when not defined(vcc) and not defined(tcc): assert(not w.ready) withLock numSlavesLock: inc numSlavesRunning diff --git a/lib/pure/cookies.nim b/lib/pure/cookies.nim index aca0ac2b4..eaff86ae6 100644 --- a/lib/pure/cookies.nim +++ b/lib/pure/cookies.nim @@ -25,16 +25,16 @@ proc parseCookies*(s: string): StringTableRef = result = newStringTable(modeCaseInsensitive) var i = 0 while true: - while s[i] == ' ' or s[i] == '\t': inc(i) + while i < s.len and (s[i] == ' ' or s[i] == '\t'): inc(i) var keystart = i - while s[i] != '=' and s[i] != '\0': inc(i) + while i < s.len and s[i] != '=': inc(i) var keyend = i-1 - if s[i] == '\0': break + if i >= s.len: break inc(i) # skip '=' var valstart = i - while s[i] != ';' and s[i] != '\0': inc(i) + while i < s.len and s[i] != ';': inc(i) result[substr(s, keystart, keyend)] = substr(s, valstart, i-1) - if s[i] == '\0': break + if i >= s.len: break inc(i) # skip ';' proc setCookie*(key, value: string, domain = "", path = "", diff --git a/lib/pure/db_common.nim b/lib/pure/db_common.nim index 957389605..f8689024b 100644 --- a/lib/pure/db_common.nim +++ b/lib/pure/db_common.nim @@ -83,9 +83,6 @@ type foreignKey*: bool ## is this a foreign key? DbColumns* = seq[DbColumn] -{.deprecated: [EDb: DbError, TSqlQuery: SqlQuery, FDb: DbEffect, - FReadDb: ReadDbEffect, FWriteDb: WriteDbEffect].} - template sql*(query: string): SqlQuery = ## constructs a SqlQuery from the string `query`. This is supposed to be ## used as a raw-string-literal modifier: diff --git a/lib/pure/dynlib.nim b/lib/pure/dynlib.nim index ec6cbb232..5bd06f6fb 100644 --- a/lib/pure/dynlib.nim +++ b/lib/pure/dynlib.nim @@ -17,9 +17,9 @@ ## Loading a simple C function ## ^^^^^^^^^^^^^^^^^^^^^^^^^^^ ## -## The following example demonstrates loading a function called 'greet' +## The following example demonstrates loading a function called 'greet' ## from a library that is determined at runtime based upon a language choice. -## If the library fails to load or the function 'greet' is not found, +## If the library fails to load or the function 'greet' is not found, ## it quits with a failure error code. ## ## .. code-block::nim @@ -59,8 +59,6 @@ import strutils type LibHandle* = pointer ## a handle to a dynamically loaded library -{.deprecated: [TLibHandle: LibHandle].} - proc loadLib*(path: string, global_symbols=false): LibHandle {.gcsafe.} ## loads a library from `path`. Returns nil if the library could not ## be loaded. diff --git a/lib/pure/encodings.nim b/lib/pure/encodings.nim index 5840d443d..c67cd7579 100644 --- a/lib/pure/encodings.nim +++ b/lib/pure/encodings.nim @@ -27,8 +27,6 @@ type EncodingError* = object of ValueError ## exception that is raised ## for encoding errors -{.deprecated: [EInvalidEncoding: EncodingError, PConverter: EncodingConverter].} - when defined(windows): proc eqEncodingNames(a, b: string): bool = var i = 0 @@ -36,7 +34,7 @@ when defined(windows): while i < a.len and j < b.len: if a[i] in {'-', '_'}: inc i if b[j] in {'-', '_'}: inc j - if a[i].toLower != b[j].toLower: return false + if i < a.len and j < b.len and a[i].toLowerAscii != b[j].toLowerAscii: return false inc i inc j result = i == a.len and j == b.len diff --git a/lib/pure/events.nim b/lib/pure/events.nim index 23a8a2c58..a39dbe3bf 100644 --- a/lib/pure/events.nim +++ b/lib/pure/events.nim @@ -43,9 +43,6 @@ type s: seq[EventHandler] EventError* = object of ValueError -{.deprecated: [TEventArgs: EventArgs, TEventHandler: EventHandler, - TEventEmitter: EventEmitter, EInvalidEvent: EventError].} - proc initEventHandler*(name: string): EventHandler = ## Initializes an EventHandler with the specified name and returns it. result.handlers = @[] diff --git a/lib/pure/fenv.nim b/lib/pure/fenv.nim index f8f115ecc..c946c4261 100644 --- a/lib/pure/fenv.nim +++ b/lib/pure/fenv.nim @@ -10,7 +10,7 @@ ## Floating-point environment. Handling of floating-point rounding and ## exceptions (overflow, division by zero, etc.). -{.deadCodeElim:on.} +{.deadCodeElim: on.} # dce option deprecated when defined(Posix) and not defined(haiku): {.passl: "-lm".} @@ -102,32 +102,33 @@ proc feupdateenv*(envp: ptr Tfenv): cint {.importc, header: "<fenv.h>".} ## represented by object pointed to by `envp` and raise exceptions ## according to saved exceptions. -var FP_RADIX_INTERNAL {. importc: "FLT_RADIX" header: "<float.h>" .} : int - -template fpRadix* : int = FP_RADIX_INTERNAL +const + FLT_RADIX = 2 ## the radix of the exponent representation + + FLT_MANT_DIG = 24 ## the number of base FLT_RADIX digits in the mantissa part of a float + FLT_DIG = 6 ## the number of digits of precision of a float + FLT_MIN_EXP = -125 # the minimum value of base FLT_RADIX in the exponent part of a float + FLT_MAX_EXP = 128 # the maximum value of base FLT_RADIX in the exponent part of a float + FLT_MIN_10_EXP = -37 ## the minimum value in base 10 of the exponent part of a float + FLT_MAX_10_EXP = 38 ## the maximum value in base 10 of the exponent part of a float + FLT_MIN = 1.17549435e-38'f32 ## the minimum value of a float + FLT_MAX = 3.40282347e+38'f32 ## the maximum value of a float + FLT_EPSILON = 1.19209290e-07'f32 ## the difference between 1 and the least value greater than 1 of a float + + DBL_MANT_DIG = 53 ## the number of base FLT_RADIX digits in the mantissa part of a double + DBL_DIG = 15 ## the number of digits of precision of a double + DBL_MIN_EXP = -1021 ## the minimum value of base FLT_RADIX in the exponent part of a double + DBL_MAX_EXP = 1024 ## the maximum value of base FLT_RADIX in the exponent part of a double + DBL_MIN_10_EXP = -307 ## the minimum value in base 10 of the exponent part of a double + DBL_MAX_10_EXP = 308 ## the maximum value in base 10 of the exponent part of a double + DBL_MIN = 2.2250738585072014E-308 ## the minimal value of a double + DBL_MAX = 1.7976931348623157E+308 ## the minimal value of a double + DBL_EPSILON = 2.2204460492503131E-16 ## the difference between 1 and the least value greater than 1 of a double + +template fpRadix* : int = FLT_RADIX ## The (integer) value of the radix used to represent any floating ## point type on the architecture used to build the program. -var FLT_MANT_DIG {. importc: "FLT_MANT_DIG" header: "<float.h>" .} : int -var FLT_DIG {. importc: "FLT_DIG" header: "<float.h>" .} : int -var FLT_MIN_EXP {. importc: "FLT_MIN_EXP" header: "<float.h>" .} : int -var FLT_MAX_EXP {. importc: "FLT_MAX_EXP" header: "<float.h>" .} : int -var FLT_MIN_10_EXP {. importc: "FLT_MIN_10_EXP" header: "<float.h>" .} : int -var FLT_MAX_10_EXP {. importc: "FLT_MAX_10_EXP" header: "<float.h>" .} : int -var FLT_MIN {. importc: "FLT_MIN" header: "<float.h>" .} : cfloat -var FLT_MAX {. importc: "FLT_MAX" header: "<float.h>" .} : cfloat -var FLT_EPSILON {. importc: "FLT_EPSILON" header: "<float.h>" .} : cfloat - -var DBL_MANT_DIG {. importc: "DBL_MANT_DIG" header: "<float.h>" .} : int -var DBL_DIG {. importc: "DBL_DIG" header: "<float.h>" .} : int -var DBL_MIN_EXP {. importc: "DBL_MIN_EXP" header: "<float.h>" .} : int -var DBL_MAX_EXP {. importc: "DBL_MAX_EXP" header: "<float.h>" .} : int -var DBL_MIN_10_EXP {. importc: "DBL_MIN_10_EXP" header: "<float.h>" .} : int -var DBL_MAX_10_EXP {. importc: "DBL_MAX_10_EXP" header: "<float.h>" .} : int -var DBL_MIN {. importc: "DBL_MIN" header: "<float.h>" .} : cdouble -var DBL_MAX {. importc: "DBL_MAX" header: "<float.h>" .} : cdouble -var DBL_EPSILON {. importc: "DBL_EPSILON" header: "<float.h>" .} : cdouble - template mantissaDigits*(T : typedesc[float32]) : int = FLT_MANT_DIG ## Number of digits (in base ``floatingPointRadix``) in the mantissa ## of 32-bit floating-point numbers. @@ -179,3 +180,11 @@ template maximumPositiveValue*(T : typedesc[float64]) : float64 = DBL_MAX template epsilon*(T : typedesc[float64]): float64 = DBL_EPSILON ## The difference between 1.0 and the smallest number greater than ## 1.0 that can be represented in a 64-bit floating-point type. + + +when isMainModule: + func is_significant(x: float): bool = + if x > minimumPositiveValue(float) and x < maximumPositiveValue(float): true + else: false + + doAssert is_significant(10.0) diff --git a/lib/pure/hashes.nim b/lib/pure/hashes.nim index b015ed311..6535bb0d3 100644 --- a/lib/pure/hashes.nim +++ b/lib/pure/hashes.nim @@ -45,7 +45,6 @@ type Hash* = int ## a hash value; hash tables using these values should ## always have a size of a power of two and can use the ``and`` ## operator instead of ``mod`` for truncation of the hash value. -{.deprecated: [THash: Hash].} proc `!&`*(h: Hash, val: int): Hash {.inline.} = ## mixes a hash value `h` with `val` to produce a new hash value. This is diff --git a/lib/pure/htmlparser.nim b/lib/pure/htmlparser.nim index 2f833804d..9d4b57dc0 100644 --- a/lib/pure/htmlparser.nim +++ b/lib/pure/htmlparser.nim @@ -178,7 +178,6 @@ type tagVar, ## the HTML ``var`` element tagVideo, ## the HTML ``video`` element tagWbr ## the HTML ``wbr`` element -{.deprecated: [THtmlTag: HtmlTag].} const tagToStr* = [ diff --git a/lib/pure/httpclient.nim b/lib/pure/httpclient.nim index 70fb19434..3f1a3d067 100644 --- a/lib/pure/httpclient.nim +++ b/lib/pure/httpclient.nim @@ -73,12 +73,18 @@ ## progress of the HTTP request. ## ## .. code-block:: Nim -## var client = newAsyncHttpClient() +## import asyncdispatch, httpclient +## ## proc onProgressChanged(total, progress, speed: BiggestInt) {.async.} = ## echo("Downloaded ", progress, " of ", total) ## echo("Current rate: ", speed div 1000, "kb/s") -## client.onProgressChanged = onProgressChanged -## discard await client.getContent("http://speedtest-ams2.digitalocean.com/100mb.test") +## +## proc asyncProc() {.async.} = +## var client = newAsyncHttpClient() +## client.onProgressChanged = onProgressChanged +## discard await client.getContent("http://speedtest-ams2.digitalocean.com/100mb.test") +## +## waitFor asyncProc() ## ## If you would like to remove the callback simply set it to ``nil``. ## @@ -213,10 +219,6 @@ type ## and ``postContent`` proc, ## when the server returns an error -{.deprecated: [TResponse: Response, PProxy: Proxy, - EInvalidProtocol: ProtocolError, EHttpRequestErr: HttpRequestError -].} - const defUserAgent* = "Nim httpclient/" & NimVersion proc httpError(msg: string) = @@ -241,7 +243,7 @@ proc parseChunks(s: Socket, timeout: int): string = var i = 0 if chunkSizeStr == "": httpError("Server terminated connection prematurely") - while true: + while i < chunkSizeStr.len: case chunkSizeStr[i] of '0'..'9': chunkSize = chunkSize shl 4 or (ord(chunkSizeStr[i]) - ord('0')) @@ -249,8 +251,6 @@ proc parseChunks(s: Socket, timeout: int): string = chunkSize = chunkSize shl 4 or (ord(chunkSizeStr[i]) - ord('a') + 10) of 'A'..'F': chunkSize = chunkSize shl 4 or (ord(chunkSizeStr[i]) - ord('A') + 10) - of '\0': - break of ';': # http://tools.ietf.org/html/rfc2616#section-3.6.1 # We don't care about chunk-extensions. @@ -358,8 +358,6 @@ proc parseResponse(s: Socket, getBody: bool, timeout: int): Response = else: result.body = "" -{.deprecated: [THttpMethod: HttpMethod].} - when not defined(ssl): type SSLContext = ref object var defaultSSLContext {.threadvar.}: SSLContext @@ -581,7 +579,7 @@ proc request*(url: string, httpMethod: string, extraHeaders = "", result = parseResponse(s, httpMethod != "HEAD", timeout) -proc request*(url: string, httpMethod = httpGET, extraHeaders = "", +proc request*(url: string, httpMethod = HttpGET, extraHeaders = "", body = "", sslContext = defaultSSLContext, timeout = -1, userAgent = defUserAgent, proxy: Proxy = nil): Response {.deprecated.} = @@ -623,13 +621,13 @@ proc get*(url: string, extraHeaders = "", maxRedirects = 5, ## server takes longer than specified an ETimeout exception will be raised. ## ## **Deprecated since version 0.15.0**: use ``HttpClient.get`` instead. - result = request(url, httpGET, extraHeaders, "", sslContext, timeout, + result = request(url, HttpGET, extraHeaders, "", sslContext, timeout, userAgent, proxy) var lastURL = url for i in 1..maxRedirects: if result.status.redirection(): let redirectTo = getNewLocation(lastURL, result.headers) - result = request(redirectTo, httpGET, extraHeaders, "", sslContext, + result = request(redirectTo, HttpGET, extraHeaders, "", sslContext, timeout, userAgent, proxy) lastURL = redirectTo @@ -683,13 +681,13 @@ proc post*(url: string, extraHeaders = "", body = "", if not multipart.isNil: xh.add(withNewLine("Content-Type: " & mpContentType)) - result = request(url, httpPOST, xh, xb, sslContext, timeout, userAgent, + result = request(url, HttpPOST, xh, xb, sslContext, timeout, userAgent, proxy) var lastURL = url for i in 1..maxRedirects: if result.status.redirection(): let redirectTo = getNewLocation(lastURL, result.headers) - var meth = if result.status != "307": httpGet else: httpPost + var meth = if result.status != "307": HttpGet else: HttpPost result = request(redirectTo, meth, xh, xb, sslContext, timeout, userAgent, proxy) lastURL = redirectTo @@ -739,12 +737,13 @@ proc downloadFile*(url: string, outputFilename: string, proc generateHeaders(requestUrl: Uri, httpMethod: string, headers: HttpHeaders, body: string, proxy: Proxy): string = # GET - result = httpMethod.toUpperAscii() + let upperMethod = httpMethod.toUpperAscii() + result = upperMethod result.add ' ' - if proxy.isNil or (not proxy.isNil and requestUrl.scheme == "https"): + if proxy.isNil or requestUrl.scheme == "https": # /path?query - if requestUrl.path[0] != '/': result.add '/' + if not requestUrl.path.startsWith("/"): result.add '/' result.add(requestUrl.path) if requestUrl.query.len > 0: result.add("?" & requestUrl.query) @@ -768,7 +767,9 @@ proc generateHeaders(requestUrl: Uri, httpMethod: string, add(result, "Connection: Keep-Alive\c\L") # Content length header. - if body.len > 0 and not headers.hasKey("Content-Length"): + const requiresBody = ["POST", "PUT", "PATCH"] + let needsContentLength = body.len > 0 or upperMethod in requiresBody + if needsContentLength and not headers.hasKey("Content-Length"): add(result, "Content-Length: " & $body.len & "\c\L") # Proxy auth header. @@ -929,7 +930,7 @@ proc parseChunks(client: HttpClient | AsyncHttpClient): Future[void] var i = 0 if chunkSizeStr == "": httpError("Server terminated connection prematurely") - while true: + while i < chunkSizeStr.len: case chunkSizeStr[i] of '0'..'9': chunkSize = chunkSize shl 4 or (ord(chunkSizeStr[i]) - ord('0')) @@ -937,8 +938,6 @@ proc parseChunks(client: HttpClient | AsyncHttpClient): Future[void] chunkSize = chunkSize shl 4 or (ord(chunkSizeStr[i]) - ord('a') + 10) of 'A'..'F': chunkSize = chunkSize shl 4 or (ord(chunkSizeStr[i]) - ord('A') + 10) - of '\0': - break of ';': # http://tools.ietf.org/html/rfc2616#section-3.6.1 # We don't care about chunk-extensions. diff --git a/lib/pure/httpcore.nim b/lib/pure/httpcore.nim index f150fa1c1..f85375111 100644 --- a/lib/pure/httpcore.nim +++ b/lib/pure/httpcore.nim @@ -45,10 +45,6 @@ type ## TCP/IP tunnel, usually used for proxies. HttpPatch ## Applies partial modifications to a resource. -{.deprecated: [httpGet: HttpGet, httpHead: HttpHead, httpPost: HttpPost, - httpPut: HttpPut, httpDelete: HttpDelete, httpTrace: HttpTrace, - httpOptions: HttpOptions, httpConnect: HttpConnect].} - const Http100* = HttpCode(100) @@ -190,11 +186,11 @@ proc len*(headers: HttpHeaders): int = return headers.table.len proc parseList(line: string, list: var seq[string], start: int): int = var i = 0 var current = "" - while line[start + i] notin {'\c', '\l', '\0'}: + while start+i < line.len and line[start + i] notin {'\c', '\l'}: i += line.skipWhitespace(start + i) i += line.parseUntil(current, {'\c', '\l', ','}, start + i) list.add(current) - if line[start + i] == ',': + if start+i < line.len and line[start + i] == ',': i.inc # Skip , current.setLen(0) diff --git a/lib/pure/httpserver.nim b/lib/pure/httpserver.nim index 632eb198a..a81e8c0a8 100644 --- a/lib/pure/httpserver.nim +++ b/lib/pure/httpserver.nim @@ -110,7 +110,6 @@ when false: # TODO: Fix this, or get rid of it. type RequestMethod = enum reqGet, reqPost - {.deprecated: [TRequestMethod: RequestMethod].} proc executeCgi(client: Socket, path, query: string, meth: RequestMethod) = var env = newStringTable(modeCaseInsensitive) @@ -126,7 +125,7 @@ when false: var dataAvail = false while dataAvail: dataAvail = recvLine(client, buf) # TODO: This is incorrect. - var L = toLower(buf.string) + var L = toLowerAscii(buf.string) if L.startsWith("content-length:"): var i = len("content-length:") while L[i] in Whitespace: inc(i) @@ -199,7 +198,7 @@ when false: notFound(client) else: when defined(Windows): - var ext = splitFile(path).ext.toLower + var ext = splitFile(path).ext.toLowerAscii if ext == ".exe" or ext == ".cgi": # XXX: extract interpreter information here? cgi = true @@ -225,7 +224,6 @@ type PAsyncHTTPServer* = ref AsyncHTTPServer AsyncHTTPServer = object of Server asyncSocket: AsyncSocket -{.deprecated: [TAsyncHTTPServer: AsyncHTTPServer, TServer: Server].} proc open*(s: var Server, port = Port(80), reuseAddr = false) = ## creates a new server at port `port`. If ``port == 0`` a free port is @@ -303,7 +301,7 @@ proc next*(s: var Server) = if s.reqMethod == "POST": # Check for Expect header if s.headers.hasKey("Expect"): - if s.headers["Expect"].toLower == "100-continue": + if s.headers["Expect"].toLowerAscii == "100-continue": s.client.sendStatus("100 Continue") else: s.client.sendStatus("417 Expectation Failed") @@ -427,7 +425,7 @@ proc nextAsync(s: PAsyncHTTPServer) = if s.reqMethod == "POST": # Check for Expect header if s.headers.hasKey("Expect"): - if s.headers["Expect"].toLower == "100-continue": + if s.headers["Expect"].toLowerAscii == "100-continue": s.client.sendStatus("100 Continue") else: s.client.sendStatus("417 Expectation Failed") diff --git a/lib/pure/includes/asynccommon.nim b/lib/pure/includes/asynccommon.nim index 45b10c584..06f4958c6 100644 --- a/lib/pure/includes/asynccommon.nim +++ b/lib/pure/includes/asynccommon.nim @@ -18,13 +18,13 @@ proc createAsyncNativeSocket*(domain: Domain = Domain.AF_INET, createAsyncNativeSocketImpl(domain, sockType, protocol) proc newAsyncNativeSocket*(domain: cint, sockType: cint, - protocol: cint): AsyncFD {.deprecated.} = + protocol: cint): AsyncFD {.deprecated: "use createAsyncNativeSocket instead".} = createAsyncNativeSocketImpl(domain, sockType, protocol) proc newAsyncNativeSocket*(domain: Domain = Domain.AF_INET, sockType: SockType = SOCK_STREAM, protocol: Protocol = IPPROTO_TCP): AsyncFD - {.deprecated.} = + {.deprecated: "use createAsyncNativeSocket instead".} = createAsyncNativeSocketImpl(domain, sockType, protocol) when defined(windows) or defined(nimdoc): diff --git a/lib/pure/includes/oserr.nim b/lib/pure/includes/oserr.nim index 0889d7383..bfb6118f2 100644 --- a/lib/pure/includes/oserr.nim +++ b/lib/pure/includes/oserr.nim @@ -12,50 +12,6 @@ when not defined(nimscript): when defined(windows): import winlean -proc osErrorMsg*(): string {.rtl, extern: "nos$1", deprecated.} = - ## Retrieves the operating system's error flag, ``errno``. - ## On Windows ``GetLastError`` is checked before ``errno``. - ## Returns "" if no error occurred. - ## - ## **Deprecated since version 0.9.4**: use the other ``osErrorMsg`` proc. - - result = "" - when defined(Windows) and not defined(nimscript): - var err = getLastError() - if err != 0'i32: - when useWinUnicode: - var msgbuf: WideCString - if formatMessageW(0x00000100 or 0x00001000 or 0x00000200 or 0x000000FF, - nil, err, 0, addr(msgbuf), 0, nil) != 0'i32: - result = $msgbuf - if msgbuf != nil: localFree(cast[pointer](msgbuf)) - else: - var msgbuf: cstring - if formatMessageA(0x00000100 or 0x00001000 or 0x00000200 or 0x000000FF, - nil, err, 0, addr(msgbuf), 0, nil) != 0'i32: - result = $msgbuf - if msgbuf != nil: localFree(msgbuf) - when not defined(nimscript): - if errno != 0'i32: - result = $c_strerror(errno) - -{.push warning[deprecated]: off.} -proc raiseOSError*(msg: string = "") {.noinline, rtl, extern: "nos$1", - deprecated.} = - ## raises an OSError exception with the given message ``msg``. - ## If ``msg == ""``, the operating system's error flag - ## (``errno``) is converted to a readable error message. On Windows - ## ``GetLastError`` is checked before ``errno``. - ## If no error flag is set, the message ``unknown OS error`` is used. - ## - ## **Deprecated since version 0.9.4**: use the other ``raiseOSError`` proc. - if len(msg) == 0: - var m = osErrorMsg() - raise newException(OSError, if m.len > 0: m else: "unknown OS error") - else: - raise newException(OSError, msg) -{.pop.} - proc `==`*(err1, err2: OSErrorCode): bool {.borrow.} proc `$`*(err: OSErrorCode): string {.borrow.} diff --git a/lib/pure/json.nim b/lib/pure/json.nim index 6740f483d..ca7e164b8 100644 --- a/lib/pure/json.nim +++ b/lib/pure/json.nim @@ -89,510 +89,22 @@ ## echo j2 import - hashes, tables, strutils, lexbase, streams, unicode, macros + hashes, tables, strutils, lexbase, streams, unicode, macros, parsejson export tables.`$` +export + parsejson.JsonEventKind, parsejson.JsonError, JsonParser, JsonKindError, + open, close, str, getInt, getFloat, kind, getColumn, getLine, getFilename, + errorMsg, errorMsgExpected, next, JsonParsingError, raiseParseErr + when defined(nimJsonGet): {.pragma: deprecatedGet, deprecated.} else: {.pragma: deprecatedGet.} type - JsonEventKind* = enum ## enumeration of all events that may occur when parsing - jsonError, ## an error occurred during parsing - jsonEof, ## end of file reached - jsonString, ## a string literal - jsonInt, ## an integer literal - jsonFloat, ## a float literal - jsonTrue, ## the value ``true`` - jsonFalse, ## the value ``false`` - jsonNull, ## the value ``null`` - jsonObjectStart, ## start of an object: the ``{`` token - jsonObjectEnd, ## end of an object: the ``}`` token - jsonArrayStart, ## start of an array: the ``[`` token - jsonArrayEnd ## start of an array: the ``]`` token - - TokKind = enum # must be synchronized with TJsonEventKind! - tkError, - tkEof, - tkString, - tkInt, - tkFloat, - tkTrue, - tkFalse, - tkNull, - tkCurlyLe, - tkCurlyRi, - tkBracketLe, - tkBracketRi, - tkColon, - tkComma - - JsonError* = enum ## enumeration that lists all errors that can occur - errNone, ## no error - errInvalidToken, ## invalid token - errStringExpected, ## string expected - errColonExpected, ## ``:`` expected - errCommaExpected, ## ``,`` expected - errBracketRiExpected, ## ``]`` expected - errCurlyRiExpected, ## ``}`` expected - errQuoteExpected, ## ``"`` or ``'`` expected - errEOC_Expected, ## ``*/`` expected - errEofExpected, ## EOF expected - errExprExpected ## expr expected - - ParserState = enum - stateEof, stateStart, stateObject, stateArray, stateExpectArrayComma, - stateExpectObjectComma, stateExpectColon, stateExpectValue - - JsonParser* = object of BaseLexer ## the parser object. - a: string - tok: TokKind - kind: JsonEventKind - err: JsonError - state: seq[ParserState] - filename: string - - JsonKindError* = object of ValueError ## raised by the ``to`` macro if the - ## JSON kind is incorrect. - -{.deprecated: [TJsonEventKind: JsonEventKind, TJsonError: JsonError, - TJsonParser: JsonParser, TTokKind: TokKind].} - -const - errorMessages: array[JsonError, string] = [ - "no error", - "invalid token", - "string expected", - "':' expected", - "',' expected", - "']' expected", - "'}' expected", - "'\"' or \"'\" expected", - "'*/' expected", - "EOF expected", - "expression expected" - ] - tokToStr: array[TokKind, string] = [ - "invalid token", - "EOF", - "string literal", - "int literal", - "float literal", - "true", - "false", - "null", - "{", "}", "[", "]", ":", "," - ] - -proc open*(my: var JsonParser, input: Stream, filename: string) = - ## initializes the parser with an input stream. `Filename` is only used - ## for nice error messages. - lexbase.open(my, input) - my.filename = filename - my.state = @[stateStart] - my.kind = jsonError - my.a = "" - -proc close*(my: var JsonParser) {.inline.} = - ## closes the parser `my` and its associated input stream. - lexbase.close(my) - -proc str*(my: JsonParser): string {.inline.} = - ## returns the character data for the events: ``jsonInt``, ``jsonFloat``, - ## ``jsonString`` - assert(my.kind in {jsonInt, jsonFloat, jsonString}) - return my.a - -proc getInt*(my: JsonParser): BiggestInt {.inline.} = - ## returns the number for the event: ``jsonInt`` - assert(my.kind == jsonInt) - return parseBiggestInt(my.a) - -proc getFloat*(my: JsonParser): float {.inline.} = - ## returns the number for the event: ``jsonFloat`` - assert(my.kind == jsonFloat) - return parseFloat(my.a) - -proc kind*(my: JsonParser): JsonEventKind {.inline.} = - ## returns the current event type for the JSON parser - return my.kind - -proc getColumn*(my: JsonParser): int {.inline.} = - ## get the current column the parser has arrived at. - result = getColNumber(my, my.bufpos) - -proc getLine*(my: JsonParser): int {.inline.} = - ## get the current line the parser has arrived at. - result = my.lineNumber - -proc getFilename*(my: JsonParser): string {.inline.} = - ## get the filename of the file that the parser processes. - result = my.filename - -proc errorMsg*(my: JsonParser): string = - ## returns a helpful error message for the event ``jsonError`` - assert(my.kind == jsonError) - result = "$1($2, $3) Error: $4" % [ - my.filename, $getLine(my), $getColumn(my), errorMessages[my.err]] - -proc errorMsgExpected*(my: JsonParser, e: string): string = - ## returns an error message "`e` expected" in the same format as the - ## other error messages - result = "$1($2, $3) Error: $4" % [ - my.filename, $getLine(my), $getColumn(my), e & " expected"] - -proc handleHexChar(c: char, x: var int): bool = - result = true # Success - case c - of '0'..'9': x = (x shl 4) or (ord(c) - ord('0')) - of 'a'..'f': x = (x shl 4) or (ord(c) - ord('a') + 10) - of 'A'..'F': x = (x shl 4) or (ord(c) - ord('A') + 10) - else: result = false # error - -proc parseEscapedUTF16(buf: cstring, pos: var int): int = - result = 0 - #UTF-16 escape is always 4 bytes. - for _ in 0..3: - if handleHexChar(buf[pos], result): - inc(pos) - else: - return -1 - -proc parseString(my: var JsonParser): TokKind = - result = tkString - var pos = my.bufpos + 1 - var buf = my.buf - while true: - case buf[pos] - of '\0': - my.err = errQuoteExpected - result = tkError - break - of '"': - inc(pos) - break - of '\\': - case buf[pos+1] - of '\\', '"', '\'', '/': - add(my.a, buf[pos+1]) - inc(pos, 2) - of 'b': - add(my.a, '\b') - inc(pos, 2) - of 'f': - add(my.a, '\f') - inc(pos, 2) - of 'n': - add(my.a, '\L') - inc(pos, 2) - of 'r': - add(my.a, '\C') - inc(pos, 2) - of 't': - add(my.a, '\t') - inc(pos, 2) - of 'u': - inc(pos, 2) - var r = parseEscapedUTF16(buf, pos) - if r < 0: - my.err = errInvalidToken - break - # Deal with surrogates - if (r and 0xfc00) == 0xd800: - if buf[pos] & buf[pos+1] != "\\u": - my.err = errInvalidToken - break - inc(pos, 2) - var s = parseEscapedUTF16(buf, pos) - if (s and 0xfc00) == 0xdc00 and s > 0: - r = 0x10000 + (((r - 0xd800) shl 10) or (s - 0xdc00)) - else: - my.err = errInvalidToken - break - add(my.a, toUTF8(Rune(r))) - else: - # don't bother with the error - add(my.a, buf[pos]) - inc(pos) - of '\c': - pos = lexbase.handleCR(my, pos) - buf = my.buf - add(my.a, '\c') - of '\L': - pos = lexbase.handleLF(my, pos) - buf = my.buf - add(my.a, '\L') - else: - add(my.a, buf[pos]) - inc(pos) - my.bufpos = pos # store back - -proc skip(my: var JsonParser) = - var pos = my.bufpos - var buf = my.buf - while true: - case buf[pos] - of '/': - if buf[pos+1] == '/': - # skip line comment: - inc(pos, 2) - while true: - case buf[pos] - of '\0': - break - of '\c': - pos = lexbase.handleCR(my, pos) - buf = my.buf - break - of '\L': - pos = lexbase.handleLF(my, pos) - buf = my.buf - break - else: - inc(pos) - elif buf[pos+1] == '*': - # skip long comment: - inc(pos, 2) - while true: - case buf[pos] - of '\0': - my.err = errEOC_Expected - break - of '\c': - pos = lexbase.handleCR(my, pos) - buf = my.buf - of '\L': - pos = lexbase.handleLF(my, pos) - buf = my.buf - of '*': - inc(pos) - if buf[pos] == '/': - inc(pos) - break - else: - inc(pos) - else: - break - of ' ', '\t': - inc(pos) - of '\c': - pos = lexbase.handleCR(my, pos) - buf = my.buf - of '\L': - pos = lexbase.handleLF(my, pos) - buf = my.buf - else: - break - my.bufpos = pos - -proc parseNumber(my: var JsonParser) = - var pos = my.bufpos - var buf = my.buf - if buf[pos] == '-': - add(my.a, '-') - inc(pos) - if buf[pos] == '.': - add(my.a, "0.") - inc(pos) - else: - while buf[pos] in Digits: - add(my.a, buf[pos]) - inc(pos) - if buf[pos] == '.': - add(my.a, '.') - inc(pos) - # digits after the dot: - while buf[pos] in Digits: - add(my.a, buf[pos]) - inc(pos) - if buf[pos] in {'E', 'e'}: - add(my.a, buf[pos]) - inc(pos) - if buf[pos] in {'+', '-'}: - add(my.a, buf[pos]) - inc(pos) - while buf[pos] in Digits: - add(my.a, buf[pos]) - inc(pos) - my.bufpos = pos - -proc parseName(my: var JsonParser) = - var pos = my.bufpos - var buf = my.buf - if buf[pos] in IdentStartChars: - while buf[pos] in IdentChars: - add(my.a, buf[pos]) - inc(pos) - my.bufpos = pos - -proc getTok(my: var JsonParser): TokKind = - setLen(my.a, 0) - skip(my) # skip whitespace, comments - case my.buf[my.bufpos] - of '-', '.', '0'..'9': - parseNumber(my) - if {'.', 'e', 'E'} in my.a: - result = tkFloat - else: - result = tkInt - of '"': - result = parseString(my) - of '[': - inc(my.bufpos) - result = tkBracketLe - of '{': - inc(my.bufpos) - result = tkCurlyLe - of ']': - inc(my.bufpos) - result = tkBracketRi - of '}': - inc(my.bufpos) - result = tkCurlyRi - of ',': - inc(my.bufpos) - result = tkComma - of ':': - inc(my.bufpos) - result = tkColon - of '\0': - result = tkEof - of 'a'..'z', 'A'..'Z', '_': - parseName(my) - case my.a - of "null": result = tkNull - of "true": result = tkTrue - of "false": result = tkFalse - else: result = tkError - else: - inc(my.bufpos) - result = tkError - my.tok = result - -proc next*(my: var JsonParser) = - ## retrieves the first/next event. This controls the parser. - var tk = getTok(my) - var i = my.state.len-1 - # the following code is a state machine. If we had proper coroutines, - # the code could be much simpler. - case my.state[i] - of stateEof: - if tk == tkEof: - my.kind = jsonEof - else: - my.kind = jsonError - my.err = errEofExpected - of stateStart: - # tokens allowed? - case tk - of tkString, tkInt, tkFloat, tkTrue, tkFalse, tkNull: - my.state[i] = stateEof # expect EOF next! - my.kind = JsonEventKind(ord(tk)) - of tkBracketLe: - my.state.add(stateArray) # we expect any - my.kind = jsonArrayStart - of tkCurlyLe: - my.state.add(stateObject) - my.kind = jsonObjectStart - of tkEof: - my.kind = jsonEof - else: - my.kind = jsonError - my.err = errEofExpected - of stateObject: - case tk - of tkString, tkInt, tkFloat, tkTrue, tkFalse, tkNull: - my.state.add(stateExpectColon) - my.kind = JsonEventKind(ord(tk)) - of tkBracketLe: - my.state.add(stateExpectColon) - my.state.add(stateArray) - my.kind = jsonArrayStart - of tkCurlyLe: - my.state.add(stateExpectColon) - my.state.add(stateObject) - my.kind = jsonObjectStart - of tkCurlyRi: - my.kind = jsonObjectEnd - discard my.state.pop() - else: - my.kind = jsonError - my.err = errCurlyRiExpected - of stateArray: - case tk - of tkString, tkInt, tkFloat, tkTrue, tkFalse, tkNull: - my.state.add(stateExpectArrayComma) # expect value next! - my.kind = JsonEventKind(ord(tk)) - of tkBracketLe: - my.state.add(stateExpectArrayComma) - my.state.add(stateArray) - my.kind = jsonArrayStart - of tkCurlyLe: - my.state.add(stateExpectArrayComma) - my.state.add(stateObject) - my.kind = jsonObjectStart - of tkBracketRi: - my.kind = jsonArrayEnd - discard my.state.pop() - else: - my.kind = jsonError - my.err = errBracketRiExpected - of stateExpectArrayComma: - case tk - of tkComma: - discard my.state.pop() - next(my) - of tkBracketRi: - my.kind = jsonArrayEnd - discard my.state.pop() # pop stateExpectArrayComma - discard my.state.pop() # pop stateArray - else: - my.kind = jsonError - my.err = errBracketRiExpected - of stateExpectObjectComma: - case tk - of tkComma: - discard my.state.pop() - next(my) - of tkCurlyRi: - my.kind = jsonObjectEnd - discard my.state.pop() # pop stateExpectObjectComma - discard my.state.pop() # pop stateObject - else: - my.kind = jsonError - my.err = errCurlyRiExpected - of stateExpectColon: - case tk - of tkColon: - my.state[i] = stateExpectValue - next(my) - else: - my.kind = jsonError - my.err = errColonExpected - of stateExpectValue: - case tk - of tkString, tkInt, tkFloat, tkTrue, tkFalse, tkNull: - my.state[i] = stateExpectObjectComma - my.kind = JsonEventKind(ord(tk)) - of tkBracketLe: - my.state[i] = stateExpectObjectComma - my.state.add(stateArray) - my.kind = jsonArrayStart - of tkCurlyLe: - my.state[i] = stateExpectObjectComma - my.state.add(stateObject) - my.kind = jsonObjectStart - else: - my.kind = jsonError - my.err = errExprExpected - - -# ------------- higher level interface --------------------------------------- - -type JsonNodeKind* = enum ## possible JSON node types JNull, JBool, @@ -620,15 +132,6 @@ type of JArray: elems*: seq[JsonNode] - JsonParsingError* = object of ValueError ## is raised for a JSON error - -{.deprecated: [EJsonParsingError: JsonParsingError, TJsonNode: JsonNodeObj, - PJsonNode: JsonNode, TJsonNodeKind: JsonNodeKind].} - -proc raiseParseErr*(p: JsonParser, msg: string) {.noinline, noreturn.} = - ## raises an `EJsonParsingError` exception. - raise newException(JsonParsingError, errorMsgExpected(p, msg)) - proc newJString*(s: string): JsonNode = ## Creates a new `JString JsonNode`. new(result) @@ -695,8 +198,8 @@ proc getBiggestInt*(n: JsonNode, default: BiggestInt = 0): BiggestInt = if n.isNil or n.kind != JInt: return default else: return n.num -proc getNum*(n: JsonNode, default: BiggestInt = 0): BiggestInt {.deprecated.} = - ## Deprecated - use getInt or getBiggestInt instead +proc getNum*(n: JsonNode, default: BiggestInt = 0): BiggestInt {.deprecated: "use getInt or getBiggestInt instead".} = + ## **Deprecated since v0.18.2:** use ``getInt`` or ``getBiggestInt`` instead. getBiggestInt(n, default) proc getFloat*(n: JsonNode, default: float = 0.0): float = @@ -709,8 +212,8 @@ proc getFloat*(n: JsonNode, default: float = 0.0): float = of JInt: return float(n.num) else: return default -proc getFNum*(n: JsonNode, default: float = 0.0): float {.deprecated.} = - ## Deprecated - use getFloat instead +proc getFNum*(n: JsonNode, default: float = 0.0): float {.deprecated: "use getFloat instead".} = + ## **Deprecated since v0.18.2:** use ``getFloat`` instead. getFloat(n, default) proc getBool*(n: JsonNode, default: bool = false): bool = @@ -720,8 +223,8 @@ proc getBool*(n: JsonNode, default: bool = false): bool = if n.isNil or n.kind != JBool: return default else: return n.bval -proc getBVal*(n: JsonNode, default: bool = false): bool {.deprecated.} = - ## Deprecated - use getBVal instead +proc getBVal*(n: JsonNode, default: bool = false): bool {.deprecated: "use getBool instead".} = + ## **Deprecated since v0.18.2:** use ``getBool`` instead. getBool(n, default) proc getFields*(n: JsonNode, @@ -838,6 +341,9 @@ proc toJson(x: NimNode): NimNode {.compiletime.} = result = newCall(bindSym"newJObject") of nnkNilLit: result = newCall(bindSym"newJNull") + of nnkPar: + if x.len == 1: result = toJson(x[0]) + else: result = newCall(bindSym"%", x) else: result = newCall(bindSym"%", x) @@ -946,8 +452,8 @@ proc contains*(node: JsonNode, val: JsonNode): bool = assert(node.kind == JArray) find(node.elems, val) >= 0 -proc existsKey*(node: JsonNode, key: string): bool {.deprecated.} = node.hasKey(key) - ## Deprecated for `hasKey` +proc existsKey*(node: JsonNode, key: string): bool {.deprecated: "use hasKey instead".} = node.hasKey(key) + ## **Deprecated:** use `hasKey` instead. proc `[]=`*(obj: JsonNode, key: string, val: JsonNode) {.inline.} = ## Sets a field from a `JObject`. @@ -1197,10 +703,6 @@ iterator mpairs*(node: var JsonNode): tuple[key: string, val: var JsonNode] = for key, val in mpairs(node.fields): yield (key, val) -proc eat(p: var JsonParser, tok: TokKind) = - if p.tok == tok: discard getTok(p) - else: raiseParseErr(p, tokToStr[tok]) - proc parseJson(p: var JsonParser): JsonNode = ## Parses JSON from a JSON Parser `p`. case p.tok @@ -1256,10 +758,12 @@ when not defined(js): ## If `s` contains extra data, it will raise `JsonParsingError`. var p: JsonParser p.open(s, filename) - defer: p.close() - discard getTok(p) # read first token - result = p.parseJson() - eat(p, tkEof) # check if there is no extra data + try: + discard getTok(p) # read first token + result = p.parseJson() + eat(p, tkEof) # check if there is no extra data + finally: + p.close() proc parseJson*(buffer: string): JsonNode = ## Parses JSON from `buffer`. @@ -1277,7 +781,6 @@ else: from math import `mod` type JSObject = object - {.deprecated: [TJSObject: JSObject].} proc parseNativeJson(x: cstring): JSObject {.importc: "JSON.parse".} diff --git a/lib/pure/lexbase.nim b/lib/pure/lexbase.nim index 15a390f0b..e38acd5ef 100644 --- a/lib/pure/lexbase.nim +++ b/lib/pure/lexbase.nim @@ -40,8 +40,6 @@ type offsetBase*: int # use ``offsetBase + bufpos`` to get the offset refillChars: set[char] -{.deprecated: [TBaseLexer: BaseLexer].} - const chrSize = sizeof(char) diff --git a/lib/pure/logging.nim b/lib/pure/logging.nim index 751fc0e8d..cdff1f548 100644 --- a/lib/pure/logging.nim +++ b/lib/pure/logging.nim @@ -96,10 +96,6 @@ when not defined(js): logFiles: int # how many log files already created, e.g. basename.1, basename.2... bufSize: int # size of output buffer (-1: use system defaults, 0: unbuffered, >0: fixed buffer size) - {.deprecated: [PFileLogger: FileLogger, PRollingFileLogger: RollingFileLogger].} - -{.deprecated: [TLevel: Level, PLogger: Logger, PConsoleLogger: ConsoleLogger].} - var level {.threadvar.}: Level ## global log filter handlers {.threadvar.}: seq[Logger] ## handlers with their own log levels @@ -126,7 +122,7 @@ proc substituteLog*(frmt: string, level: Level, args: varargs[string, `$`]): str var v = "" let app = when defined(js): "" else: getAppFilename() while frmt[i] in IdentChars: - v.add(toLower(frmt[i])) + v.add(toLowerAscii(frmt[i])) inc(i) case v of "date": result.add(getDateStr()) diff --git a/lib/pure/marshal.nim b/lib/pure/marshal.nim index 6ee830786..b88c9dd85 100644 --- a/lib/pure/marshal.nim +++ b/lib/pure/marshal.nim @@ -309,7 +309,6 @@ when not defined(testing) and isMainModule: Node = object next, prev: PNode data: string - {.deprecated: [TNode: Node].} proc buildList(): PNode = new(result) diff --git a/lib/pure/matchers.nim b/lib/pure/matchers.nim index 6366fee1a..97223ed01 100644 --- a/lib/pure/matchers.nim +++ b/lib/pure/matchers.nim @@ -12,7 +12,7 @@ ## **Warning:** This module is deprecated since version 0.14.0. {.deprecated.} -{.deadCodeElim: on.} +{.deadCodeElim: on.} # dce option deprecated {.push debugger:off .} # the user does not want to trace a part # of the standard library! @@ -29,21 +29,21 @@ proc validEmailAddress*(s: string): bool {.noSideEffect, chars = Letters + Digits + {'!','#','$','%','&', '\'','*','+','/','=','?','^','_','`','{','}','|','~','-','.'} var i = 0 - if s[i] notin chars or s[i] == '.': return false - while s[i] in chars: - if s[i] == '.' and s[i+1] == '.': return false + if i >= s.len or s[i] notin chars or s[i] == '.': return false + while i < s.len and s[i] in chars: + if i+1 < s.len and s[i] == '.' and s[i+1] == '.': return false inc(i) - if s[i] != '@': return false + if i >= s.len or s[i] != '@': return false var j = len(s)-1 - if s[j] notin Letters: return false + if j >= 0 and s[j] notin Letters: return false while j >= i and s[j] in Letters: dec(j) inc(i) # skip '@' - while s[i] in {'0'..'9', 'a'..'z', '-', '.'}: inc(i) - if s[i] != '\0': return false + while i < s.len and s[i] in {'0'..'9', 'a'..'z', '-', '.'}: inc(i) + if i != s.len: return false var x = substr(s, j+1) if len(x) == 2 and x[0] in Letters and x[1] in Letters: return true - case toLower(x) + case toLowerAscii(x) of "com", "org", "net", "gov", "mil", "biz", "info", "mobi", "name", "aero", "jobs", "museum": return true else: return false diff --git a/lib/pure/math.nim b/lib/pure/math.nim index cbd04a145..9e590debc 100644 --- a/lib/pure/math.nim +++ b/lib/pure/math.nim @@ -29,11 +29,21 @@ proc binom*(n, k: int): int {.noSideEffect.} = for i in countup(2, k): result = (result * (n + 1 - i)) div i -proc fac*(n: int): int {.noSideEffect.} = +proc createFactTable[N: static[int]]: array[N, int] = + result[0] = 1 + for i in 1 ..< N: + result[i] = result[i - 1] * i + +proc fac*(n: int): int = ## Computes the faculty/factorial function. - result = 1 - for i in countup(2, n): - result = result * i + const factTable = + when sizeof(int) == 4: + createFactTable[13]() + else: + createFactTable[21]() + assert(n >= 0, $n & " must not be negative.") + assert(n < factTable.len, $n & " is too large to look up in the table") + factTable[n] {.push checks:off, line_dir:off, stack_trace:off.} @@ -550,3 +560,14 @@ when isMainModule: assert sgn(Inf) == 1 assert sgn(NaN) == 0 + block: # fac() tests + try: + discard fac(-1) + except AssertionError: + discard + + doAssert fac(0) == 1 + doAssert fac(1) == 1 + doAssert fac(2) == 2 + doAssert fac(3) == 6 + doAssert fac(4) == 24 diff --git a/lib/pure/memfiles.nim b/lib/pure/memfiles.nim index 5c73381ff..bf6795b0c 100644 --- a/lib/pure/memfiles.nim +++ b/lib/pure/memfiles.nim @@ -38,8 +38,6 @@ type else: handle: cint -{.deprecated: [TMemFile: MemFile].} - proc mapMem*(m: var MemFile, mode: FileMode = fmRead, mappedSize = -1, offset = 0): pointer = ## returns a pointer to a mapped portion of MemFile `m` diff --git a/lib/pure/mersenne.nim b/lib/pure/mersenne.nim index 6ac0c4e56..b2227f114 100644 --- a/lib/pure/mersenne.nim +++ b/lib/pure/mersenne.nim @@ -12,8 +12,6 @@ type mt: array[0..623, uint32] index: int -{.deprecated: [TMersenneTwister: MersenneTwister].} - proc newMersenneTwister*(seed: uint32): MersenneTwister = result.index = 0 result.mt[0] = seed diff --git a/lib/pure/mimetypes.nim b/lib/pure/mimetypes.nim index b397ef47b..ff69ba61e 100644 --- a/lib/pure/mimetypes.nim +++ b/lib/pure/mimetypes.nim @@ -13,8 +13,6 @@ type MimeDB* = object mimes: StringTableRef -{.deprecated: [TMimeDB: MimeDB].} - const mimes* = { "ez": "application/andrew-inset", "anx": "application/annodex", diff --git a/lib/pure/nativesockets.nim b/lib/pure/nativesockets.nim index 74b2c9741..5545ca2d1 100644 --- a/lib/pure/nativesockets.nim +++ b/lib/pure/nativesockets.nim @@ -85,9 +85,6 @@ type length*: int addrList*: seq[string] -{.deprecated: [TPort: Port, TDomain: Domain, TType: SockType, - TProtocol: Protocol, TServent: Servent, THostent: Hostent].} - when useWinVersion: let osInvalidSocket* = winlean.INVALID_SOCKET @@ -616,8 +613,12 @@ proc setBlocking*(s: SocketHandle, blocking: bool) = proc timeValFromMilliseconds(timeout = 500): Timeval = if timeout != -1: var seconds = timeout div 1000 - result.tv_sec = seconds.int32 - result.tv_usec = ((timeout - seconds * 1000) * 1000).int32 + when useWinVersion: + result.tv_sec = seconds.int32 + result.tv_usec = ((timeout - seconds * 1000) * 1000).int32 + else: + result.tv_sec = seconds.Time + result.tv_usec = ((timeout - seconds * 1000) * 1000).Suseconds proc createFdSet(fd: var TFdSet, s: seq[SocketHandle], m: var int) = FD_ZERO(fd) @@ -636,7 +637,7 @@ proc pruneSocketSet(s: var seq[SocketHandle], fd: var TFdSet) = inc(i) setLen(s, L) -proc select*(readfds: var seq[SocketHandle], timeout = 500): int {.deprecated.} = +proc select*(readfds: var seq[SocketHandle], timeout = 500): int {.deprecated: "use selectRead instead".} = ## When a socket in ``readfds`` is ready to be read from then a non-zero ## value will be returned specifying the count of the sockets which can be ## read from. The sockets which can be read from will also be removed diff --git a/lib/pure/net.nim b/lib/pure/net.nim index ccf02a1fc..fc04ef1af 100644 --- a/lib/pure/net.nim +++ b/lib/pure/net.nim @@ -68,7 +68,7 @@ ## ``newSocket()``. The difference is that the latter creates a new file ## descriptor. -{.deadCodeElim: on.} +{.deadCodeElim: on.} # dce option deprecated import nativesockets, os, strutils, parseutils, times, sets, options export Port, `$`, `==` export Domain, SockType, Protocol @@ -110,9 +110,6 @@ when defineSsl: serverGetPskFunc: SslServerGetPskFunc clientGetPskFunc: SslClientGetPskFunc - {.deprecated: [ESSL: SSLError, TSSLCVerifyMode: SSLCVerifyMode, - TSSLProtVersion: SSLProtVersion, PSSLContext: SSLContext, - TSSLAcceptResult: SSLAcceptResult].} else: type SslContext* = void # TODO: Workaround #4797. @@ -159,10 +156,6 @@ type Peek, SafeDisconn ## Ensures disconnection exceptions (ECONNRESET, EPIPE etc) are not thrown. -{.deprecated: [TSocketFlags: SocketFlag, ETimeout: TimeoutError, - TReadLineResult: ReadLineResult, TSOBool: SOBool, PSocket: Socket, - TSocketImpl: SocketImpl].} - type IpAddressFamily* {.pure.} = enum ## Describes the type of an IP address IPv6, ## IPv6 address @@ -176,8 +169,6 @@ type of IpAddressFamily.IPv4: address_v4*: array[0..3, uint8] ## Contains the IP address in bytes in ## case of IPv4 -{.deprecated: [TIpAddress: IpAddress].} - proc socketError*(socket: Socket, err: int = -1, async = false, lastError = (-1).OSErrorCode): void {.gcsafe.} @@ -969,7 +960,7 @@ when defined(posix) and not defined(nimdoc): raise newException(ValueError, "socket path too long") copyMem(addr result.sun_path, path.cstring, path.len + 1) -when defined(posix): +when defined(posix) or defined(nimdoc): proc connectUnix*(socket: Socket, path: string) = ## Connects to Unix socket on `path`. ## This only works on Unix-style systems: Mac OS X, BSD and Linux @@ -1421,8 +1412,7 @@ proc sendTo*(socket: Socket, address: string, port: Port, data: pointer, ## ## **Note:** This proc is not available for SSL sockets. assert(not socket.isClosed, "Cannot `sendTo` on a closed socket") - var aiList = getAddrInfo(address, port, af) - + var aiList = getAddrInfo(address, port, af, socket.sockType, socket.protocol) # try all possibilities: var success = false var it = aiList @@ -1443,7 +1433,7 @@ proc sendTo*(socket: Socket, address: string, port: Port, ## this function will try each IP of that hostname. ## ## This is the high-level version of the above ``sendTo`` function. - result = socket.sendTo(address, port, cstring(data), data.len) + result = socket.sendTo(address, port, cstring(data), data.len, socket.domain ) proc isSsl*(socket: Socket): bool = diff --git a/lib/pure/nimprof.nim b/lib/pure/nimprof.nim index 4289eb049..506c6bfaa 100644 --- a/lib/pure/nimprof.nim +++ b/lib/pure/nimprof.nim @@ -30,7 +30,6 @@ when not declared(system.StackTrace): type StackTrace = object lines: array[0..20, cstring] files: array[0..20, cstring] - {.deprecated: [TStackTrace: StackTrace].} proc `[]`*(st: StackTrace, i: int): cstring = st.lines[i] # We use a simple hash table of bounded size to keep track of the stack traces: @@ -39,7 +38,6 @@ type total: int st: StackTrace ProfileData = array[0..64*1024-1, ptr ProfileEntry] -{.deprecated: [TProfileEntry: ProfileEntry, TProfileData: ProfileData].} proc `==`(a, b: StackTrace): bool = for i in 0 .. high(a.lines): diff --git a/lib/pure/oids.nim b/lib/pure/oids.nim index 26a55de6c..d6369b5f9 100644 --- a/lib/pure/oids.nim +++ b/lib/pure/oids.nim @@ -23,11 +23,9 @@ type fuzz: int32 ## count: int32 ## -{.deprecated: [Toid: Oid].} - proc `==`*(oid1: Oid, oid2: Oid): bool = - ## Compare two Mongo Object IDs for equality - return (oid1.time == oid2.time) and (oid1.fuzz == oid2.fuzz) and (oid1.count == oid2.count) + ## Compare two Mongo Object IDs for equality + return (oid1.time == oid2.time) and (oid1.fuzz == oid2.fuzz) and (oid1.count == oid2.count) proc hexbyte*(hex: char): int = case hex diff --git a/lib/pure/options.nim b/lib/pure/options.nim index 8a771be71..bd01b208a 100644 --- a/lib/pure/options.nim +++ b/lib/pure/options.nim @@ -104,6 +104,10 @@ proc none*(T: typedesc): Option[T] = # the default is the none type discard +proc none*[T]: Option[T] = + ## Alias for ``none(T)``. + none(T) + proc isSome*[T](self: Option[T]): bool {.inline.} = when T is SomePointer: self.val != nil @@ -130,7 +134,7 @@ proc get*[T](self: Option[T]): T = proc get*[T](self: Option[T], otherwise: T): T = ## Returns the contents of this option or `otherwise` if the option is none. - if self.has: + if self.isSome: self.val else: otherwise @@ -150,7 +154,7 @@ proc map*[T, R](self: Option[T], callback: proc (input: T): R): Option[R] = proc flatten*[A](self: Option[Option[A]]): Option[A] = ## Remove one level of structure in a nested Option. - if self.has: + if self.isSome: self.val else: none(A) @@ -175,14 +179,14 @@ proc filter*[T](self: Option[T], callback: proc (input: T): bool): Option[T] = proc `==`*(a, b: Option): bool = ## Returns ``true`` if both ``Option``s are ``none``, ## or if they have equal values - (a.has and b.has and a.val == b.val) or (not a.has and not b.has) + (a.isSome and b.isSome and a.val == b.val) or (not a.isSome and not b.isSome) proc `$`*[T](self: Option[T]): string = ## Get the string representation of this option. If the option has a value, ## the result will be `Some(x)` where `x` is the string representation of the contained value. ## 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.has: + if self.isSome: "Some(" & $self.val & ")" else: "None[" & T.name & "]" @@ -290,3 +294,7 @@ when isMainModule: let tmp = option(intref) check(sizeof(tmp) == sizeof(ptr int)) + + test "none[T]": + check(none[int]().isNone) + check(none(int) == none[int]()) diff --git a/lib/pure/os.nim b/lib/pure/os.nim index a77bee99d..03445a035 100644 --- a/lib/pure/os.nim +++ b/lib/pure/os.nim @@ -10,7 +10,7 @@ ## This module contains basic operating system facilities like ## retrieving environment variables, reading command line arguments, ## working with directories, running shell commands, etc. -{.deadCodeElim: on.} +{.deadCodeElim: on.} # dce option deprecated {.push debugger: off.} @@ -188,7 +188,7 @@ proc getLastModificationTime*(file: string): times.Time {.rtl, extern: "nos$1".} var f: WIN32_FIND_DATA var h = findFirstFile(file, f) if h == -1'i32: raiseOSError(osLastError()) - result = fromUnix(winTimeToUnixTime(rdFileTime(f.ftLastWriteTime)).int64) + result = fromWinTime(rdFileTime(f.ftLastWriteTime)) findClose(h) proc getLastAccessTime*(file: string): times.Time {.rtl, extern: "nos$1".} = @@ -201,7 +201,7 @@ proc getLastAccessTime*(file: string): times.Time {.rtl, extern: "nos$1".} = var f: WIN32_FIND_DATA var h = findFirstFile(file, f) if h == -1'i32: raiseOSError(osLastError()) - result = fromUnix(winTimeToUnixTime(rdFileTime(f.ftLastAccessTime)).int64) + result = fromWinTime(rdFileTime(f.ftLastAccessTime)) findClose(h) proc getCreationTime*(file: string): times.Time {.rtl, extern: "nos$1".} = @@ -218,7 +218,7 @@ proc getCreationTime*(file: string): times.Time {.rtl, extern: "nos$1".} = var f: WIN32_FIND_DATA var h = findFirstFile(file, f) if h == -1'i32: raiseOSError(osLastError()) - result = fromUnix(winTimeToUnixTime(rdFileTime(f.ftCreationTime)).int64) + result = fromWinTime(rdFileTime(f.ftCreationTime)) findClose(h) proc fileNewer*(a, b: string): bool {.rtl, extern: "nos$1".} = @@ -329,20 +329,21 @@ proc expandFilename*(filename: string): string {.rtl, extern: "nos$1", c_free(cast[pointer](r)) when defined(Windows): - proc openHandle(path: string, followSymlink=true): Handle = + proc openHandle(path: string, followSymlink=true, writeAccess=false): Handle = var flags = FILE_FLAG_BACKUP_SEMANTICS or FILE_ATTRIBUTE_NORMAL if not followSymlink: flags = flags or FILE_FLAG_OPEN_REPARSE_POINT + let access = if writeAccess: GENERIC_WRITE else: 0'i32 when useWinUnicode: result = createFileW( - newWideCString(path), 0'i32, + newWideCString(path), access, FILE_SHARE_DELETE or FILE_SHARE_READ or FILE_SHARE_WRITE, nil, OPEN_EXISTING, flags, 0 ) else: result = createFileA( - path, 0'i32, + path, access, FILE_SHARE_DELETE or FILE_SHARE_READ or FILE_SHARE_WRITE, nil, OPEN_EXISTING, flags, 0 ) @@ -430,8 +431,6 @@ type fpOthersWrite, ## write access for others fpOthersRead ## read access for others -{.deprecated: [TFilePermission: FilePermission].} - proc getFilePermissions*(filename: string): set[FilePermission] {. rtl, extern: "nos$1", tags: [ReadDirEffect].} = ## retrieves file permissions for `filename`. `OSError` is raised in case of @@ -729,8 +728,6 @@ type pcDir, ## path refers to a directory pcLinkToDir ## path refers to a symbolic link to a directory -{.deprecated: [TPathComponent: PathComponent].} - when defined(posix): proc getSymlinkFileKind(path: string): PathComponent = @@ -1059,18 +1056,17 @@ proc parseCmdLine*(c: string): seq[string] {. while true: setLen(a, 0) # eat all delimiting whitespace - while c[i] == ' ' or c[i] == '\t' or c[i] == '\l' or c[i] == '\r' : inc(i) + while i < c.len and c[i] in {' ', '\t', '\l', '\r'}: inc(i) + if i >= c.len: break when defined(windows): # parse a single argument according to the above rules: - if c[i] == '\0': break var inQuote = false - while true: + while i < c.len: case c[i] - of '\0': break of '\\': var j = i - while c[j] == '\\': inc(j) - if c[j] == '"': + while j < c.len and c[j] == '\\': inc(j) + if j < c.len and c[j] == '"': for k in 1..(j-i) div 2: a.add('\\') if (j-i) mod 2 == 0: i = j @@ -1083,7 +1079,7 @@ proc parseCmdLine*(c: string): seq[string] {. of '"': inc(i) if not inQuote: inQuote = true - elif c[i] == '"': + elif i < c.len and c[i] == '"': a.add(c[i]) inc(i) else: @@ -1101,13 +1097,12 @@ proc parseCmdLine*(c: string): seq[string] {. of '\'', '\"': var delim = c[i] inc(i) # skip ' or " - while c[i] != '\0' and c[i] != delim: + while i < c.len and c[i] != delim: add a, c[i] inc(i) - if c[i] != '\0': inc(i) - of '\0': break + if i < c.len: inc(i) else: - while c[i] > ' ': + while i < c.len and c[i] > ' ': add(a, c[i]) inc(i) add(result, a) @@ -1442,18 +1437,6 @@ proc getAppFilename*(): string {.rtl, extern: "nos$1", tags: [ReadIOEffect].} = if result.len == 0: result = getApplHeuristic() -proc getApplicationFilename*(): string {.rtl, extern: "nos$1", deprecated.} = - ## Returns the filename of the application's executable. - ## **Deprecated since version 0.8.12**: use ``getAppFilename`` - ## instead. - result = getAppFilename() - -proc getApplicationDir*(): string {.rtl, extern: "nos$1", deprecated.} = - ## Returns the directory of the application's executable. - ## **Deprecated since version 0.8.12**: use ``getAppDir`` - ## instead. - result = splitFile(getAppFilename()).dir - proc getAppDir*(): string {.rtl, extern: "nos$1", tags: [ReadIOEffect].} = ## Returns the directory of the application's executable. result = splitFile(getAppFilename()).dir @@ -1511,16 +1494,14 @@ template rawToFormalFileInfo(rawInfo, path, formalInfo): untyped = ## 'rawInfo' is either a 'TBY_HANDLE_FILE_INFORMATION' structure on Windows, ## or a 'Stat' structure on posix when defined(Windows): - template toTime(e: FILETIME): untyped {.gensym.} = - fromUnix(winTimeToUnixTime(rdFileTime(e)).int64) # local templates default to bind semantics template merge(a, b): untyped = a or (b shl 32) formalInfo.id.device = rawInfo.dwVolumeSerialNumber formalInfo.id.file = merge(rawInfo.nFileIndexLow, rawInfo.nFileIndexHigh) formalInfo.size = merge(rawInfo.nFileSizeLow, rawInfo.nFileSizeHigh) formalInfo.linkCount = rawInfo.nNumberOfLinks - formalInfo.lastAccessTime = toTime(rawInfo.ftLastAccessTime) - formalInfo.lastWriteTime = toTime(rawInfo.ftLastWriteTime) - formalInfo.creationTime = toTime(rawInfo.ftCreationTime) + formalInfo.lastAccessTime = fromWinTime(rdFileTime(rawInfo.ftLastAccessTime)) + formalInfo.lastWriteTime = fromWinTime(rdFileTime(rawInfo.ftLastWriteTime)) + formalInfo.creationTime = fromWinTime(rdFileTime(rawInfo.ftCreationTime)) # Retrieve basic permissions if (rawInfo.dwFileAttributes and FILE_ATTRIBUTE_READONLY) != 0'i32: @@ -1654,3 +1635,18 @@ proc isHidden*(path: string): bool = result = (fileName[0] == '.') and (fileName[3] != '.') {.pop.} + +proc setLastModificationTime*(file: string, t: times.Time) = + ## Sets the `file`'s last modification time. `OSError` is raised in case of + ## an error. + when defined(posix): + let unixt = posix.Time(t.toUnix) + var timevals = [Timeval(tv_sec: unixt), Timeval(tv_sec: unixt)] # [last access, last modification] + if utimes(file, timevals.addr) != 0: raiseOSError(osLastError()) + else: + let h = openHandle(path = file, writeAccess = true) + if h == INVALID_HANDLE_VALUE: raiseOSError(osLastError()) + var ft = t.toWinTime.toFILETIME + let res = setFileTime(h, nil, nil, ft.addr) + discard h.closeHandle + if res == 0'i32: raiseOSError(osLastError()) \ No newline at end of file diff --git a/lib/pure/ospaths.nim b/lib/pure/ospaths.nim index 0d638abb9..c0d3ef512 100644 --- a/lib/pure/ospaths.nim +++ b/lib/pure/ospaths.nim @@ -29,11 +29,6 @@ type OSErrorCode* = distinct int32 ## Specifies an OS Error Code. -{.deprecated: [FReadEnv: ReadEnvEffect, FWriteEnv: WriteEnvEffect, - FReadDir: ReadDirEffect, - FWriteDir: WriteDirEffect, - TOSErrorCode: OSErrorCode -].} const doslikeFileSystem* = defined(windows) or defined(OS2) or defined(DOS) @@ -196,7 +191,7 @@ proc joinPath*(head, tail: string): string {. else: result = head & tail else: - if tail[0] in {DirSep, AltSep}: + if tail.len > 0 and tail[0] in {DirSep, AltSep}: result = head & tail else: result = head & DirSep & tail @@ -477,7 +472,7 @@ proc unixToNativePath*(path: string, drive=""): string {. var i = start while i < len(path): # ../../../ --> :::: - if path[i] == '.' and path[i+1] == '.' and path[i+2] == '/': + if i+2 < path.len and path[i] == '.' and path[i+1] == '.' and path[i+2] == '/': # parent directory when defined(macos): if result[high(result)] == ':': diff --git a/lib/pure/osproc.nim b/lib/pure/osproc.nim index 555626514..664446d54 100644 --- a/lib/pure/osproc.nim +++ b/lib/pure/osproc.nim @@ -61,9 +61,6 @@ type Process* = ref ProcessObj ## represents an operating system process -{.deprecated: [TProcess: ProcessObj, PProcess: Process, - TProcessOption: ProcessOption].} - const poUseShell* {.deprecated.} = poUsePath ## Deprecated alias for poUsePath. @@ -373,17 +370,15 @@ template streamAccess(p) = when defined(Windows) and not defined(useNimRtl): # We need to implement a handle stream for Windows: type - PFileHandleStream = ref FileHandleStream - FileHandleStream = object of StreamObj + FileHandleStream = ref object of StreamObj handle: Handle atTheEnd: bool - {.deprecated: [TFileHandleStream: FileHandleStream].} proc hsClose(s: Stream) = discard # nothing to do here - proc hsAtEnd(s: Stream): bool = return PFileHandleStream(s).atTheEnd + proc hsAtEnd(s: Stream): bool = return FileHandleStream(s).atTheEnd proc hsReadData(s: Stream, buffer: pointer, bufLen: int): int = - var s = PFileHandleStream(s) + var s = FileHandleStream(s) if s.atTheEnd: return 0 var br: int32 var a = winlean.readFile(s.handle, buffer, bufLen.cint, addr br, nil) @@ -395,13 +390,13 @@ when defined(Windows) and not defined(useNimRtl): result = br proc hsWriteData(s: Stream, buffer: pointer, bufLen: int) = - var s = PFileHandleStream(s) + var s = FileHandleStream(s) var bytesWritten: int32 var a = winlean.writeFile(s.handle, buffer, bufLen.cint, addr bytesWritten, nil) if a == 0: raiseOSError(osLastError()) - proc newFileHandleStream(handle: Handle): PFileHandleStream = + proc newFileHandleStream(handle: Handle): FileHandleStream = new(result) result.handle = handle result.closeImpl = hsClose @@ -527,7 +522,7 @@ when defined(Windows) and not defined(useNimRtl): if setHandleInformation(he, DWORD(1), DWORD(0)) == 0'i32: raiseOsError(osLastError()) if setHandleInformation(hi, DWORD(1), DWORD(0)) == 0'i32: - raiseOsError(osLastError()) + raiseOsError(osLastError()) if setHandleInformation(ho, DWORD(1), DWORD(0)) == 0'i32: raiseOsError(osLastError()) else: @@ -752,14 +747,14 @@ elif not defined(useNimRtl): copyMem(result[i], addr(x[0]), x.len+1) inc(i) - type StartProcessData = object - sysCommand: string - sysArgs: cstringArray - sysEnv: cstringArray - workingDir: cstring - pStdin, pStdout, pStderr, pErrorPipe: array[0..1, cint] - options: set[ProcessOption] - {.deprecated: [TStartProcessData: StartProcessData].} + type + StartProcessData = object + sysCommand: string + sysArgs: cstringArray + sysEnv: cstringArray + workingDir: cstring + pStdin, pStdout, pStderr, pErrorPipe: array[0..1, cint] + options: set[ProcessOption] const useProcessAuxSpawn = declared(posix_spawn) and not defined(useFork) and not defined(useClone) and not defined(linux) @@ -1001,13 +996,21 @@ elif not defined(useNimRtl): {.pop} proc close(p: Process) = - if p.inStream != nil: close(p.inStream) - if p.outStream != nil: close(p.outStream) - if p.errStream != nil: close(p.errStream) if poParentStreams notin p.options: - discard close(p.inHandle) - discard close(p.outHandle) - discard close(p.errHandle) + if p.inStream != nil: + close(p.inStream) + else: + discard close(p.inHandle) + + if p.outStream != nil: + close(p.outStream) + else: + discard close(p.outHandle) + + if p.errStream != nil: + close(p.errStream) + else: + discard close(p.errHandle) proc suspend(p: Process) = if kill(p.id, SIGSTOP) != 0'i32: raiseOsError(osLastError()) @@ -1281,7 +1284,7 @@ elif not defined(useNimRtl): proc select(readfds: var seq[Process], timeout = 500): int = var tv: Timeval - tv.tv_sec = 0 + tv.tv_sec = posix.Time(0) tv.tv_usec = timeout * 1000 var rd: TFdSet diff --git a/lib/pure/parsecfg.nim b/lib/pure/parsecfg.nim index 2a5dbc8f8..5fa2d8dc3 100644 --- a/lib/pure/parsecfg.nim +++ b/lib/pure/parsecfg.nim @@ -125,9 +125,6 @@ type tok: Token filename: string -{.deprecated: [TCfgEventKind: CfgEventKind, TCfgEvent: CfgEvent, - TTokKind: TokKind, TToken: Token, TCfgParser: CfgParser].} - # implementation const diff --git a/lib/pure/parsecsv.nim b/lib/pure/parsecsv.nim index 358ce829d..796114d37 100644 --- a/lib/pure/parsecsv.nim +++ b/lib/pure/parsecsv.nim @@ -66,8 +66,6 @@ type CsvError* = object of IOError ## exception that is raised if ## a parsing error occurs -{.deprecated: [TCsvRow: CsvRow, TCsvParser: CsvParser, EInvalidCsv: CsvError].} - proc raiseEInvalidCsv(filename: string, line, col: int, msg: string) {.noreturn.} = var e: ref CsvError diff --git a/lib/pure/parsejson.nim b/lib/pure/parsejson.nim new file mode 100644 index 000000000..9c53af6a6 --- /dev/null +++ b/lib/pure/parsejson.nim @@ -0,0 +1,535 @@ +# +# +# 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 implements a json parser. It is used +## and exported by the ``json`` standard library +## module, but can also be used in its own right. + +import + strutils, lexbase, streams, unicode + +type + JsonEventKind* = enum ## enumeration of all events that may occur when parsing + jsonError, ## an error occurred during parsing + jsonEof, ## end of file reached + jsonString, ## a string literal + jsonInt, ## an integer literal + jsonFloat, ## a float literal + jsonTrue, ## the value ``true`` + jsonFalse, ## the value ``false`` + jsonNull, ## the value ``null`` + jsonObjectStart, ## start of an object: the ``{`` token + jsonObjectEnd, ## end of an object: the ``}`` token + jsonArrayStart, ## start of an array: the ``[`` token + jsonArrayEnd ## start of an array: the ``]`` token + + TokKind* = enum # must be synchronized with TJsonEventKind! + tkError, + tkEof, + tkString, + tkInt, + tkFloat, + tkTrue, + tkFalse, + tkNull, + tkCurlyLe, + tkCurlyRi, + tkBracketLe, + tkBracketRi, + tkColon, + tkComma + + JsonError* = enum ## enumeration that lists all errors that can occur + errNone, ## no error + errInvalidToken, ## invalid token + errStringExpected, ## string expected + errColonExpected, ## ``:`` expected + errCommaExpected, ## ``,`` expected + errBracketRiExpected, ## ``]`` expected + errCurlyRiExpected, ## ``}`` expected + errQuoteExpected, ## ``"`` or ``'`` expected + errEOC_Expected, ## ``*/`` expected + errEofExpected, ## EOF expected + errExprExpected ## expr expected + + ParserState = enum + stateEof, stateStart, stateObject, stateArray, stateExpectArrayComma, + stateExpectObjectComma, stateExpectColon, stateExpectValue + + JsonParser* = object of BaseLexer ## the parser object. + a*: string + tok*: TokKind + kind: JsonEventKind + err: JsonError + state: seq[ParserState] + filename: string + rawStringLiterals: bool + + JsonKindError* = object of ValueError ## raised by the ``to`` macro if the + ## JSON kind is incorrect. + JsonParsingError* = object of ValueError ## is raised for a JSON error + +const + errorMessages*: array[JsonError, string] = [ + "no error", + "invalid token", + "string expected", + "':' expected", + "',' expected", + "']' expected", + "'}' expected", + "'\"' or \"'\" expected", + "'*/' expected", + "EOF expected", + "expression expected" + ] + tokToStr: array[TokKind, string] = [ + "invalid token", + "EOF", + "string literal", + "int literal", + "float literal", + "true", + "false", + "null", + "{", "}", "[", "]", ":", "," + ] + +proc open*(my: var JsonParser, input: Stream, filename: string; + rawStringLiterals = false) = + ## initializes the parser with an input stream. `Filename` is only used + ## for nice error messages. If `rawStringLiterals` is true, string literals + ## are kepts with their surrounding quotes and escape sequences in them are + ## left untouched too. + lexbase.open(my, input) + my.filename = filename + my.state = @[stateStart] + my.kind = jsonError + my.a = "" + my.rawStringLiterals = rawStringLiterals + +proc close*(my: var JsonParser) {.inline.} = + ## closes the parser `my` and its associated input stream. + lexbase.close(my) + +proc str*(my: JsonParser): string {.inline.} = + ## returns the character data for the events: ``jsonInt``, ``jsonFloat``, + ## ``jsonString`` + assert(my.kind in {jsonInt, jsonFloat, jsonString}) + return my.a + +proc getInt*(my: JsonParser): BiggestInt {.inline.} = + ## returns the number for the event: ``jsonInt`` + assert(my.kind == jsonInt) + return parseBiggestInt(my.a) + +proc getFloat*(my: JsonParser): float {.inline.} = + ## returns the number for the event: ``jsonFloat`` + assert(my.kind == jsonFloat) + return parseFloat(my.a) + +proc kind*(my: JsonParser): JsonEventKind {.inline.} = + ## returns the current event type for the JSON parser + return my.kind + +proc getColumn*(my: JsonParser): int {.inline.} = + ## get the current column the parser has arrived at. + result = getColNumber(my, my.bufpos) + +proc getLine*(my: JsonParser): int {.inline.} = + ## get the current line the parser has arrived at. + result = my.lineNumber + +proc getFilename*(my: JsonParser): string {.inline.} = + ## get the filename of the file that the parser processes. + result = my.filename + +proc errorMsg*(my: JsonParser): string = + ## returns a helpful error message for the event ``jsonError`` + assert(my.kind == jsonError) + result = "$1($2, $3) Error: $4" % [ + my.filename, $getLine(my), $getColumn(my), errorMessages[my.err]] + +proc errorMsgExpected*(my: JsonParser, e: string): string = + ## returns an error message "`e` expected" in the same format as the + ## other error messages + result = "$1($2, $3) Error: $4" % [ + my.filename, $getLine(my), $getColumn(my), e & " expected"] + +proc handleHexChar(c: char, x: var int): bool = + result = true # Success + case c + of '0'..'9': x = (x shl 4) or (ord(c) - ord('0')) + of 'a'..'f': x = (x shl 4) or (ord(c) - ord('a') + 10) + of 'A'..'F': x = (x shl 4) or (ord(c) - ord('A') + 10) + else: result = false # error + +proc parseEscapedUTF16*(buf: cstring, pos: var int): int = + result = 0 + #UTF-16 escape is always 4 bytes. + for _ in 0..3: + if handleHexChar(buf[pos], result): + inc(pos) + else: + return -1 + +proc parseString(my: var JsonParser): TokKind = + result = tkString + var pos = my.bufpos + 1 + var buf = my.buf + if my.rawStringLiterals: + add(my.a, '"') + while true: + case buf[pos] + of '\0': + my.err = errQuoteExpected + result = tkError + break + of '"': + if my.rawStringLiterals: + add(my.a, '"') + inc(pos) + break + of '\\': + if my.rawStringLiterals: + add(my.a, '\\') + case buf[pos+1] + of '\\', '"', '\'', '/': + add(my.a, buf[pos+1]) + inc(pos, 2) + of 'b': + add(my.a, '\b') + inc(pos, 2) + of 'f': + add(my.a, '\f') + inc(pos, 2) + of 'n': + add(my.a, '\L') + inc(pos, 2) + of 'r': + add(my.a, '\C') + inc(pos, 2) + of 't': + add(my.a, '\t') + inc(pos, 2) + of 'u': + if my.rawStringLiterals: + add(my.a, 'u') + inc(pos, 2) + var pos2 = pos + var r = parseEscapedUTF16(buf, pos) + if r < 0: + my.err = errInvalidToken + break + # Deal with surrogates + if (r and 0xfc00) == 0xd800: + if buf[pos] != '\\' or buf[pos+1] != 'u': + my.err = errInvalidToken + break + inc(pos, 2) + var s = parseEscapedUTF16(buf, pos) + if (s and 0xfc00) == 0xdc00 and s > 0: + r = 0x10000 + (((r - 0xd800) shl 10) or (s - 0xdc00)) + else: + my.err = errInvalidToken + break + if my.rawStringLiterals: + let length = pos - pos2 + for i in 1 .. length: + if buf[pos2] in {'0'..'9', 'A'..'F', 'a'..'f'}: + add(my.a, buf[pos2]) + inc pos2 + else: + break + else: + add(my.a, toUTF8(Rune(r))) + else: + # don't bother with the error + add(my.a, buf[pos]) + inc(pos) + of '\c': + pos = lexbase.handleCR(my, pos) + buf = my.buf + add(my.a, '\c') + of '\L': + pos = lexbase.handleLF(my, pos) + buf = my.buf + add(my.a, '\L') + else: + add(my.a, buf[pos]) + inc(pos) + my.bufpos = pos # store back + +proc skip(my: var JsonParser) = + var pos = my.bufpos + var buf = my.buf + while true: + case buf[pos] + of '/': + if buf[pos+1] == '/': + # skip line comment: + inc(pos, 2) + while true: + case buf[pos] + of '\0': + break + of '\c': + pos = lexbase.handleCR(my, pos) + buf = my.buf + break + of '\L': + pos = lexbase.handleLF(my, pos) + buf = my.buf + break + else: + inc(pos) + elif buf[pos+1] == '*': + # skip long comment: + inc(pos, 2) + while true: + case buf[pos] + of '\0': + my.err = errEOC_Expected + break + of '\c': + pos = lexbase.handleCR(my, pos) + buf = my.buf + of '\L': + pos = lexbase.handleLF(my, pos) + buf = my.buf + of '*': + inc(pos) + if buf[pos] == '/': + inc(pos) + break + else: + inc(pos) + else: + break + of ' ', '\t': + inc(pos) + of '\c': + pos = lexbase.handleCR(my, pos) + buf = my.buf + of '\L': + pos = lexbase.handleLF(my, pos) + buf = my.buf + else: + break + my.bufpos = pos + +proc parseNumber(my: var JsonParser) = + var pos = my.bufpos + var buf = my.buf + if buf[pos] == '-': + add(my.a, '-') + inc(pos) + if buf[pos] == '.': + add(my.a, "0.") + inc(pos) + else: + while buf[pos] in Digits: + add(my.a, buf[pos]) + inc(pos) + if buf[pos] == '.': + add(my.a, '.') + inc(pos) + # digits after the dot: + while buf[pos] in Digits: + add(my.a, buf[pos]) + inc(pos) + if buf[pos] in {'E', 'e'}: + add(my.a, buf[pos]) + inc(pos) + if buf[pos] in {'+', '-'}: + add(my.a, buf[pos]) + inc(pos) + while buf[pos] in Digits: + add(my.a, buf[pos]) + inc(pos) + my.bufpos = pos + +proc parseName(my: var JsonParser) = + var pos = my.bufpos + var buf = my.buf + if buf[pos] in IdentStartChars: + while buf[pos] in IdentChars: + add(my.a, buf[pos]) + inc(pos) + my.bufpos = pos + +proc getTok*(my: var JsonParser): TokKind = + setLen(my.a, 0) + skip(my) # skip whitespace, comments + case my.buf[my.bufpos] + of '-', '.', '0'..'9': + parseNumber(my) + if {'.', 'e', 'E'} in my.a: + result = tkFloat + else: + result = tkInt + of '"': + result = parseString(my) + of '[': + inc(my.bufpos) + result = tkBracketLe + of '{': + inc(my.bufpos) + result = tkCurlyLe + of ']': + inc(my.bufpos) + result = tkBracketRi + of '}': + inc(my.bufpos) + result = tkCurlyRi + of ',': + inc(my.bufpos) + result = tkComma + of ':': + inc(my.bufpos) + result = tkColon + of '\0': + result = tkEof + of 'a'..'z', 'A'..'Z', '_': + parseName(my) + case my.a + of "null": result = tkNull + of "true": result = tkTrue + of "false": result = tkFalse + else: result = tkError + else: + inc(my.bufpos) + result = tkError + my.tok = result + + +proc next*(my: var JsonParser) = + ## retrieves the first/next event. This controls the parser. + var tk = getTok(my) + var i = my.state.len-1 + # the following code is a state machine. If we had proper coroutines, + # the code could be much simpler. + case my.state[i] + of stateEof: + if tk == tkEof: + my.kind = jsonEof + else: + my.kind = jsonError + my.err = errEofExpected + of stateStart: + # tokens allowed? + case tk + of tkString, tkInt, tkFloat, tkTrue, tkFalse, tkNull: + my.state[i] = stateEof # expect EOF next! + my.kind = JsonEventKind(ord(tk)) + of tkBracketLe: + my.state.add(stateArray) # we expect any + my.kind = jsonArrayStart + of tkCurlyLe: + my.state.add(stateObject) + my.kind = jsonObjectStart + of tkEof: + my.kind = jsonEof + else: + my.kind = jsonError + my.err = errEofExpected + of stateObject: + case tk + of tkString, tkInt, tkFloat, tkTrue, tkFalse, tkNull: + my.state.add(stateExpectColon) + my.kind = JsonEventKind(ord(tk)) + of tkBracketLe: + my.state.add(stateExpectColon) + my.state.add(stateArray) + my.kind = jsonArrayStart + of tkCurlyLe: + my.state.add(stateExpectColon) + my.state.add(stateObject) + my.kind = jsonObjectStart + of tkCurlyRi: + my.kind = jsonObjectEnd + discard my.state.pop() + else: + my.kind = jsonError + my.err = errCurlyRiExpected + of stateArray: + case tk + of tkString, tkInt, tkFloat, tkTrue, tkFalse, tkNull: + my.state.add(stateExpectArrayComma) # expect value next! + my.kind = JsonEventKind(ord(tk)) + of tkBracketLe: + my.state.add(stateExpectArrayComma) + my.state.add(stateArray) + my.kind = jsonArrayStart + of tkCurlyLe: + my.state.add(stateExpectArrayComma) + my.state.add(stateObject) + my.kind = jsonObjectStart + of tkBracketRi: + my.kind = jsonArrayEnd + discard my.state.pop() + else: + my.kind = jsonError + my.err = errBracketRiExpected + of stateExpectArrayComma: + case tk + of tkComma: + discard my.state.pop() + next(my) + of tkBracketRi: + my.kind = jsonArrayEnd + discard my.state.pop() # pop stateExpectArrayComma + discard my.state.pop() # pop stateArray + else: + my.kind = jsonError + my.err = errBracketRiExpected + of stateExpectObjectComma: + case tk + of tkComma: + discard my.state.pop() + next(my) + of tkCurlyRi: + my.kind = jsonObjectEnd + discard my.state.pop() # pop stateExpectObjectComma + discard my.state.pop() # pop stateObject + else: + my.kind = jsonError + my.err = errCurlyRiExpected + of stateExpectColon: + case tk + of tkColon: + my.state[i] = stateExpectValue + next(my) + else: + my.kind = jsonError + my.err = errColonExpected + of stateExpectValue: + case tk + of tkString, tkInt, tkFloat, tkTrue, tkFalse, tkNull: + my.state[i] = stateExpectObjectComma + my.kind = JsonEventKind(ord(tk)) + of tkBracketLe: + my.state[i] = stateExpectObjectComma + my.state.add(stateArray) + my.kind = jsonArrayStart + of tkCurlyLe: + my.state[i] = stateExpectObjectComma + my.state.add(stateObject) + my.kind = jsonObjectStart + else: + my.kind = jsonError + my.err = errExprExpected + +proc raiseParseErr*(p: JsonParser, msg: string) {.noinline, noreturn.} = + ## raises an `EJsonParsingError` exception. + raise newException(JsonParsingError, errorMsgExpected(p, msg)) + +proc eat*(p: var JsonParser, tok: TokKind) = + if p.tok == tok: discard getTok(p) + else: raiseParseErr(p, tokToStr[tok]) diff --git a/lib/pure/parseopt.nim b/lib/pure/parseopt.nim index ffb69a72b..58e1be0e4 100644 --- a/lib/pure/parseopt.nim +++ b/lib/pure/parseopt.nim @@ -54,29 +54,27 @@ type ## or the argument, ``value`` is not "" if ## the option was given a value -{.deprecated: [TCmdLineKind: CmdLineKind, TOptParser: OptParser].} - proc parseWord(s: string, i: int, w: var string, - delim: set[char] = {'\x09', ' ', '\0'}): int = + delim: set[char] = {'\x09', ' '}): int = result = i - if s[result] == '\"': + if result < s.len and s[result] == '\"': inc(result) - while not (s[result] in {'\0', '\"'}): + while result < s.len and s[result] != '\"': add(w, s[result]) inc(result) - if s[result] == '\"': inc(result) + if result < s.len and s[result] == '\"': inc(result) else: - while not (s[result] in delim): + while result < s.len and s[result] notin delim: add(w, s[result]) inc(result) when declared(os.paramCount): proc quote(s: string): string = - if find(s, {' ', '\t'}) >= 0 and s[0] != '"': + if find(s, {' ', '\t'}) >= 0 and s.len > 0 and s[0] != '"': if s[0] == '-': result = newStringOfCap(s.len) - var i = parseWord(s, 0, result, {'\0', ' ', '\x09', ':', '='}) - if s[i] in {':','='}: + var i = parseWord(s, 0, result, {' ', '\x09', ':', '='}) + if i < s.len and s[i] in {':','='}: result.add s[i] inc i result.add '"' @@ -144,43 +142,45 @@ proc handleShortOption(p: var OptParser) = add(p.key.string, p.cmd[i]) inc(i) p.inShortState = true - while p.cmd[i] in {'\x09', ' '}: + while i < p.cmd.len and p.cmd[i] in {'\x09', ' '}: inc(i) p.inShortState = false - if p.cmd[i] in {':', '='} or card(p.shortNoVal) > 0 and p.key.string[0] notin p.shortNoVal: - if p.cmd[i] in {':', '='}: + if i < p.cmd.len and p.cmd[i] in {':', '='} or + card(p.shortNoVal) > 0 and p.key.string[0] notin p.shortNoVal: + if i < p.cmd.len and p.cmd[i] in {':', '='}: inc(i) p.inShortState = false - while p.cmd[i] in {'\x09', ' '}: inc(i) + while i < p.cmd.len and p.cmd[i] in {'\x09', ' '}: inc(i) i = parseWord(p.cmd, i, p.val.string) - if p.cmd[i] == '\0': p.inShortState = false + if i >= p.cmd.len: p.inShortState = false p.pos = i proc next*(p: var OptParser) {.rtl, extern: "npo$1".} = ## parses the first or next option; ``p.kind`` describes what token has been ## parsed. ``p.key`` and ``p.val`` are set accordingly. var i = p.pos - while p.cmd[i] in {'\x09', ' '}: inc(i) + while i < p.cmd.len and p.cmd[i] in {'\x09', ' '}: inc(i) p.pos = i setLen(p.key.string, 0) setLen(p.val.string, 0) if p.inShortState: handleShortOption(p) return - case p.cmd[i] - of '\0': + if i >= p.cmd.len: p.kind = cmdEnd - of '-': + return + if p.cmd[i] == '-': inc(i) - if p.cmd[i] == '-': + if i < p.cmd.len and p.cmd[i] == '-': p.kind = cmdLongOption inc(i) - i = parseWord(p.cmd, i, p.key.string, {'\0', ' ', '\x09', ':', '='}) - while p.cmd[i] in {'\x09', ' '}: inc(i) - if p.cmd[i] in {':', '='} or len(p.longNoVal) > 0 and p.key.string notin p.longNoVal: - if p.cmd[i] in {':', '='}: + i = parseWord(p.cmd, i, p.key.string, {' ', '\x09', ':', '='}) + while i < p.cmd.len and p.cmd[i] in {'\x09', ' '}: inc(i) + if i < p.cmd.len and p.cmd[i] in {':', '='} or + len(p.longNoVal) > 0 and p.key.string notin p.longNoVal: + if i < p.cmd.len and p.cmd[i] in {':', '='}: inc(i) - while p.cmd[i] in {'\x09', ' '}: inc(i) + while i < p.cmd.len and p.cmd[i] in {'\x09', ' '}: inc(i) p.pos = parseWord(p.cmd, i, p.val.string) else: p.pos = i diff --git a/lib/pure/parseopt2.nim b/lib/pure/parseopt2.nim index 7254337d0..b54a56c0c 100644 --- a/lib/pure/parseopt2.nim +++ b/lib/pure/parseopt2.nim @@ -41,8 +41,6 @@ type ## or the argument, ``value`` is not "" if ## the option was given a value -{.deprecated: [TCmdLineKind: CmdLineKind, TOptParser: OptParser].} - proc initOptParser*(cmdline: seq[string]): OptParser {.rtl.} = ## Initalizes option parses with cmdline. cmdline should not contain ## argument 0 - program name. @@ -122,8 +120,6 @@ proc cmdLineRest*(p: OptParser): TaintedString {.rtl, extern: "npo2$1", deprecat type GetoptResult* = tuple[kind: CmdLineKind, key, val: TaintedString] -{.deprecated: [TGetoptResult: GetoptResult].} - iterator getopt*(p: var OptParser): GetoptResult = ## This is an convenience iterator for iterating over the given OptParser object. ## Example: diff --git a/lib/pure/parsesql.nim b/lib/pure/parsesql.nim index fcc757bea..e3bab9a8d 100644 --- a/lib/pure/parsesql.nim +++ b/lib/pure/parsesql.nim @@ -45,8 +45,6 @@ type SqlLexer* = object of BaseLexer ## the parser object. filename: string -{.deprecated: [TToken: Token, TSqlLexer: SqlLexer].} - const tokKindToStr: array[TokKind, string] = [ "invalid", "[EOF]", "identifier", "quoted identifier", "string constant", diff --git a/lib/pure/parseutils.nim b/lib/pure/parseutils.nim index 57387e62e..d77790fe0 100644 --- a/lib/pure/parseutils.nim +++ b/lib/pure/parseutils.nim @@ -11,7 +11,7 @@ ## ## To unpack raw bytes look at the `streams <streams.html>`_ module. -{.deadCodeElim: on.} +{.deadCodeElim: on.} # dce option deprecated {.push debugger:off .} # the user does not want to trace a part # of the standard library! @@ -51,9 +51,9 @@ proc parseHex*(s: string, number: var int, start = 0; maxLen = 0): int {. ## upper bound. Not more than ```maxLen`` characters are parsed. var i = start var foundDigit = false - if s[i] == '0' and (s[i+1] == 'x' or s[i+1] == 'X'): inc(i, 2) - elif s[i] == '#': inc(i) let last = if maxLen == 0: s.len else: i+maxLen + if i+1 < last and s[i] == '0' and (s[i+1] == 'x' or s[i+1] == 'X'): inc(i, 2) + elif i < last and s[i] == '#': inc(i) while i < last: case s[i] of '_': discard @@ -76,8 +76,8 @@ proc parseOct*(s: string, number: var int, start = 0): int {. ## the number of the parsed characters or 0 in case of an error. var i = start var foundDigit = false - if s[i] == '0' and (s[i+1] == 'o' or s[i+1] == 'O'): inc(i, 2) - while true: + if i+1 < s.len and s[i] == '0' and (s[i+1] == 'o' or s[i+1] == 'O'): inc(i, 2) + while i < s.len: case s[i] of '_': discard of '0'..'7': @@ -93,8 +93,8 @@ proc parseBin*(s: string, number: var int, start = 0): int {. ## the number of the parsed characters or 0 in case of an error. var i = start var foundDigit = false - if s[i] == '0' and (s[i+1] == 'b' or s[i+1] == 'B'): inc(i, 2) - while true: + if i+1 < s.len and s[i] == '0' and (s[i+1] == 'b' or s[i+1] == 'B'): inc(i, 2) + while i < s.len: case s[i] of '_': discard of '0'..'1': @@ -108,9 +108,9 @@ proc parseIdent*(s: string, ident: var string, start = 0): int = ## parses an identifier and stores it in ``ident``. Returns ## the number of the parsed characters or 0 in case of an error. var i = start - if s[i] in IdentStartChars: + if i < s.len and s[i] in IdentStartChars: inc(i) - while s[i] in IdentChars: inc(i) + while i < s.len and s[i] in IdentChars: inc(i) ident = substr(s, start, i-1) result = i-start @@ -119,11 +119,9 @@ proc parseIdent*(s: string, start = 0): string = ## Returns the parsed identifier or an empty string in case of an error. result = "" var i = start - - if s[i] in IdentStartChars: + if i < s.len and s[i] in IdentStartChars: inc(i) - while s[i] in IdentChars: inc(i) - + while i < s.len and s[i] in IdentChars: inc(i) result = substr(s, start, i-1) proc parseToken*(s: string, token: var string, validChars: set[char], @@ -134,24 +132,26 @@ proc parseToken*(s: string, token: var string, validChars: set[char], ## ## **Deprecated since version 0.8.12**: Use ``parseWhile`` instead. var i = start - while s[i] in validChars: inc(i) + while i < s.len and s[i] in validChars: inc(i) result = i-start token = substr(s, start, i-1) proc skipWhitespace*(s: string, start = 0): int {.inline.} = ## skips the whitespace starting at ``s[start]``. Returns the number of ## skipped characters. - while s[start+result] in Whitespace: inc(result) + while start+result < s.len and s[start+result] in Whitespace: inc(result) proc skip*(s, token: string, start = 0): int {.inline.} = ## skips the `token` starting at ``s[start]``. Returns the length of `token` ## or 0 if there was no `token` at ``s[start]``. - while result < token.len and s[result+start] == token[result]: inc(result) + while start+result < s.len and result < token.len and + s[result+start] == token[result]: + inc(result) if result != token.len: result = 0 proc skipIgnoreCase*(s, token: string, start = 0): int = ## same as `skip` but case is ignored for token matching. - while result < token.len and + while start+result < s.len and result < token.len and toLower(s[result+start]) == toLower(token[result]): inc(result) if result != token.len: result = 0 @@ -159,18 +159,18 @@ proc skipUntil*(s: string, until: set[char], start = 0): int {.inline.} = ## Skips all characters until one char from the set `until` is found ## or the end is reached. ## Returns number of characters skipped. - while s[result+start] notin until and s[result+start] != '\0': inc(result) + while start+result < s.len and s[result+start] notin until: inc(result) proc skipUntil*(s: string, until: char, start = 0): int {.inline.} = ## Skips all characters until the char `until` is found ## or the end is reached. ## Returns number of characters skipped. - while s[result+start] != until and s[result+start] != '\0': inc(result) + while start+result < s.len and s[result+start] != until: inc(result) proc skipWhile*(s: string, toSkip: set[char], start = 0): int {.inline.} = ## Skips all characters while one char from the set `token` is found. ## Returns number of characters skipped. - while s[result+start] in toSkip and s[result+start] != '\0': inc(result) + while start+result < s.len and s[result+start] in toSkip: inc(result) proc parseUntil*(s: string, token: var string, until: set[char], start = 0): int {.inline.} = @@ -214,7 +214,7 @@ proc parseWhile*(s: string, token: var string, validChars: set[char], ## the number of the parsed characters or 0 in case of an error. A token ## consists of the characters in `validChars`. var i = start - while s[i] in validChars: inc(i) + while i < s.len and s[i] in validChars: inc(i) result = i-start token = substr(s, start, i-1) @@ -231,16 +231,17 @@ proc rawParseInt(s: string, b: var BiggestInt, start = 0): int = var sign: BiggestInt = -1 i = start - if s[i] == '+': inc(i) - elif s[i] == '-': - inc(i) - sign = 1 - if s[i] in {'0'..'9'}: + if i < s.len: + if s[i] == '+': inc(i) + elif s[i] == '-': + inc(i) + sign = 1 + if i < s.len and s[i] in {'0'..'9'}: b = 0 - while s[i] in {'0'..'9'}: + while i < s.len and s[i] in {'0'..'9'}: b = b * 10 - (ord(s[i]) - ord('0')) inc(i) - while s[i] == '_': inc(i) # underscores are allowed and ignored + while i < s.len and s[i] == '_': inc(i) # underscores are allowed and ignored b = b * sign result = i - start {.pop.} # overflowChecks @@ -281,17 +282,17 @@ proc parseSaturatedNatural*(s: string, b: var int, start = 0): int = ## discard parseSaturatedNatural("848", res) ## doAssert res == 848 var i = start - if s[i] == '+': inc(i) - if s[i] in {'0'..'9'}: + if i < s.len and s[i] == '+': inc(i) + if i < s.len and s[i] in {'0'..'9'}: b = 0 - while s[i] in {'0'..'9'}: + while i < s.len and s[i] in {'0'..'9'}: let c = ord(s[i]) - ord('0') if b <= (high(int) - c) div 10: b = b * 10 + c else: b = high(int) inc(i) - while s[i] == '_': inc(i) # underscores are allowed and ignored + while i < s.len and s[i] == '_': inc(i) # underscores are allowed and ignored result = i - start # overflowChecks doesn't work with BiggestUInt @@ -300,16 +301,16 @@ proc rawParseUInt(s: string, b: var BiggestUInt, start = 0): int = res = 0.BiggestUInt prev = 0.BiggestUInt i = start - if s[i] == '+': inc(i) # Allow - if s[i] in {'0'..'9'}: + if i < s.len and s[i] == '+': inc(i) # Allow + if i < s.len and s[i] in {'0'..'9'}: b = 0 - while s[i] in {'0'..'9'}: + while i < s.len and s[i] in {'0'..'9'}: prev = res res = res * 10 + (ord(s[i]) - ord('0')).BiggestUInt if prev > res: return 0 # overflowChecks emulation inc(i) - while s[i] == '_': inc(i) # underscores are allowed and ignored + while i < s.len and s[i] == '_': inc(i) # underscores are allowed and ignored b = res result = i - start @@ -364,8 +365,6 @@ type ikVar, ## ``var`` part of the interpolated string ikExpr ## ``expr`` part of the interpolated string -{.deprecated: [TInterpolatedKind: InterpolatedKind].} - iterator interpolatedFragments*(s: string): tuple[kind: InterpolatedKind, value: string] = ## Tokenizes the string `s` into substrings for interpolation purposes. @@ -389,31 +388,31 @@ iterator interpolatedFragments*(s: string): tuple[kind: InterpolatedKind, var kind: InterpolatedKind while true: var j = i - if s[j] == '$': - if s[j+1] == '{': + if j < s.len and s[j] == '$': + if j+1 < s.len and s[j+1] == '{': inc j, 2 var nesting = 0 - while true: - case s[j] - of '{': inc nesting - of '}': - if nesting == 0: - inc j - break - dec nesting - of '\0': - raise newException(ValueError, - "Expected closing '}': " & substr(s, i, s.high)) - else: discard - inc j + block curlies: + while j < s.len: + case s[j] + of '{': inc nesting + of '}': + if nesting == 0: + inc j + break curlies + dec nesting + else: discard + inc j + raise newException(ValueError, + "Expected closing '}': " & substr(s, i, s.high)) inc i, 2 # skip ${ kind = ikExpr - elif s[j+1] in IdentStartChars: + elif j+1 < s.len and s[j+1] in IdentStartChars: inc j, 2 - while s[j] in IdentChars: inc(j) + while j < s.len and s[j] in IdentChars: inc(j) inc i # skip $ kind = ikVar - elif s[j+1] == '$': + elif j+1 < s.len and s[j+1] == '$': inc j, 2 inc i # skip $ kind = ikDollar diff --git a/lib/pure/parsexml.nim b/lib/pure/parsexml.nim index 978c9c516..e0000aad3 100644 --- a/lib/pure/parsexml.nim +++ b/lib/pure/parsexml.nim @@ -98,9 +98,6 @@ type filename: string options: set[XmlParseOption] -{.deprecated: [TXmlParser: XmlParser, TXmlParseOptions: XmlParseOption, - TXmlError: XmlErrorKind, TXmlEventKind: XmlEventKind].} - const errorMessages: array[XmlErrorKind, string] = [ "no error", diff --git a/lib/pure/pegs.nim b/lib/pure/pegs.nim index 5ae2d9182..830429842 100644 --- a/lib/pure/pegs.nim +++ b/lib/pure/pegs.nim @@ -74,8 +74,8 @@ type line: int ## line the symbol has been declared/used in col: int ## column the symbol has been declared/used in flags: set[NonTerminalFlag] ## the nonterminal's flags - rule: Node ## the rule that the symbol refers to - Node {.shallow.} = object + rule: Peg ## the rule that the symbol refers to + Peg* {.shallow.} = object ## type that represents a PEG case kind: PegKind of pkEmpty..pkWhitespace: nil of pkTerminal, pkTerminalIgnoreCase, pkTerminalIgnoreStyle: term: string @@ -83,13 +83,9 @@ type of pkCharChoice, pkGreedyRepSet: charChoice: ref set[char] of pkNonTerminal: nt: NonTerminal of pkBackRef..pkBackRefIgnoreStyle: index: range[0..MaxSubpatterns] - else: sons: seq[Node] + else: sons: seq[Peg] NonTerminal* = ref NonTerminalObj - Peg* = Node ## type that represents a PEG - -{.deprecated: [TPeg: Peg, TNode: Node].} - proc term*(t: string): Peg {.nosideEffect, rtl, extern: "npegs$1Str".} = ## constructs a PEG from a terminal string if t.len != 1: @@ -534,15 +530,15 @@ proc rawMatch*(s: string, p: Peg, start: int, c: var Captures): int {. case p.kind of pkEmpty: result = 0 # match of length 0 of pkAny: - if s[start] != '\0': result = 1 + if start < s.len: result = 1 else: result = -1 of pkAnyRune: - if s[start] != '\0': + if start < s.len: result = runeLenAt(s, start) else: result = -1 of pkLetter: - if s[start] != '\0': + if start < s.len: var a: Rune result = start fastRuneAt(s, result, a) @@ -551,7 +547,7 @@ proc rawMatch*(s: string, p: Peg, start: int, c: var Captures): int {. else: result = -1 of pkLower: - if s[start] != '\0': + if start < s.len: var a: Rune result = start fastRuneAt(s, result, a) @@ -560,7 +556,7 @@ proc rawMatch*(s: string, p: Peg, start: int, c: var Captures): int {. else: result = -1 of pkUpper: - if s[start] != '\0': + if start < s.len: var a: Rune result = start fastRuneAt(s, result, a) @@ -569,7 +565,7 @@ proc rawMatch*(s: string, p: Peg, start: int, c: var Captures): int {. else: result = -1 of pkTitle: - if s[start] != '\0': + if start < s.len: var a: Rune result = start fastRuneAt(s, result, a) @@ -578,7 +574,7 @@ proc rawMatch*(s: string, p: Peg, start: int, c: var Captures): int {. else: result = -1 of pkWhitespace: - if s[start] != '\0': + if start < s.len: var a: Rune result = start fastRuneAt(s, result, a) @@ -589,15 +585,15 @@ proc rawMatch*(s: string, p: Peg, start: int, c: var Captures): int {. of pkGreedyAny: result = len(s) - start of pkNewLine: - if s[start] == '\L': result = 1 - elif s[start] == '\C': - if s[start+1] == '\L': result = 2 + if start < s.len and s[start] == '\L': result = 1 + elif start < s.len and s[start] == '\C': + if start+1 < s.len and s[start+1] == '\L': result = 2 else: result = 1 else: result = -1 of pkTerminal: result = len(p.term) for i in 0..result-1: - if p.term[i] != s[start+i]: + if start+i >= s.len or p.term[i] != s[start+i]: result = -1 break of pkTerminalIgnoreCase: @@ -606,6 +602,9 @@ proc rawMatch*(s: string, p: Peg, start: int, c: var Captures): int {. a, b: Rune result = start while i < len(p.term): + if result >= s.len: + result = -1 + break fastRuneAt(p.term, i, a) fastRuneAt(s, result, b) if toLower(a) != toLower(b): @@ -618,21 +617,26 @@ proc rawMatch*(s: string, p: Peg, start: int, c: var Captures): int {. a, b: Rune result = start while i < len(p.term): - while true: + while i < len(p.term): fastRuneAt(p.term, i, a) if a != Rune('_'): break - while true: + while result < s.len: fastRuneAt(s, result, b) if b != Rune('_'): break - if toLower(a) != toLower(b): + if result >= s.len: + if i >= p.term.len: break + else: + result = -1 + break + elif toLower(a) != toLower(b): result = -1 break dec(result, start) of pkChar: - if p.ch == s[start]: result = 1 + if start < s.len and p.ch == s[start]: result = 1 else: result = -1 of pkCharChoice: - if contains(p.charChoice[], s[start]): result = 1 + if start < s.len and contains(p.charChoice[], s[start]): result = 1 else: result = -1 of pkNonTerminal: var oldMl = c.ml @@ -695,10 +699,10 @@ proc rawMatch*(s: string, p: Peg, start: int, c: var Captures): int {. of pkGreedyRepChar: result = 0 var ch = p.ch - while ch == s[start+result]: inc(result) + while start+result < s.len and ch == s[start+result]: inc(result) of pkGreedyRepSet: result = 0 - while contains(p.charChoice[], s[start+result]): inc(result) + while start+result < s.len and contains(p.charChoice[], s[start+result]): inc(result) of pkOption: result = max(0, rawMatch(s, p.sons[0], start, c)) of pkAndPredicate: diff --git a/lib/pure/random.nim b/lib/pure/random.nim index 968a326d2..01ea9c845 100644 --- a/lib/pure/random.nim +++ b/lib/pure/random.nim @@ -192,7 +192,7 @@ when not defined(nimscript): ## Initializes the random number generator with a "random" ## number, i.e. a tickcount. Note: Does not work for NimScript. let now = times.getTime() - randomize(convert(Seconds, Nanoseconds, now.toUnix) + now.nanoseconds) + randomize(convert(Seconds, Nanoseconds, now.toUnix) + now.nanosecond) {.pop.} diff --git a/lib/pure/ropes.nim b/lib/pure/ropes.nim index 6ddd61afa..9b9cdb52a 100644 --- a/lib/pure/ropes.nim +++ b/lib/pure/ropes.nim @@ -19,7 +19,7 @@ include "system/inclrtl" import streams -{.deadCodeElim: on.} +{.deadCodeElim: on.} # dce option deprecated {.push debugger:off .} # the user does not want to trace a part # of the standard library! @@ -37,8 +37,6 @@ type length: int data: string # != nil if a leaf -{.deprecated: [PRope: Rope].} - proc isConc(r: Rope): bool {.inline.} = return isNil(r.data) # Note that the left and right pointers are not needed for leafs. diff --git a/lib/pure/scgi.nim b/lib/pure/scgi.nim index 1ff26954e..e36803823 100644 --- a/lib/pure/scgi.nim +++ b/lib/pure/scgi.nim @@ -94,9 +94,6 @@ type disp: Dispatcher AsyncScgiState* = ref AsyncScgiStateObj -{.deprecated: [EScgi: ScgiError, TScgiState: ScgiState, - PAsyncScgiState: AsyncScgiState].} - proc recvBuffer(s: var ScgiState, L: int) = if L > s.bufLen: s.bufLen = L diff --git a/lib/pure/selectors.nim b/lib/pure/selectors.nim index dda5658a2..640df8282 100644 --- a/lib/pure/selectors.nim +++ b/lib/pure/selectors.nim @@ -271,7 +271,7 @@ else: msg.add("Internal Error\n") var err = newException(IOSelectorsException, msg) raise err - + proc setNonBlocking(fd: cint) {.inline.} = setBlocking(fd.SocketHandle, false) @@ -316,18 +316,18 @@ else: include ioselects/ioselectors_poll proc register*[T](s: Selector[T], fd: int | SocketHandle, - events: set[Event], data: T) {.deprecated.} = + events: set[Event], data: T) {.deprecated: "use registerHandle instead".} = ## **Deprecated since v0.18.0:** Use ``registerHandle`` instead. s.registerHandle(fd, events, data) -proc setEvent*(ev: SelectEvent) {.deprecated.} = +proc setEvent*(ev: SelectEvent) {.deprecated: "use trigger instead".} = ## Trigger event ``ev``. ## ## **Deprecated since v0.18.0:** Use ``trigger`` instead. ev.trigger() proc update*[T](s: Selector[T], fd: int | SocketHandle, - events: set[Event]) {.deprecated.} = + events: set[Event]) {.deprecated: "use updateHandle instead".} = ## Update file/socket descriptor ``fd``, registered in selector ## ``s`` with new events set ``event``. ## diff --git a/lib/pure/smtp.nim b/lib/pure/smtp.nim index 08e6c8112..c2c674b84 100644 --- a/lib/pure/smtp.nim +++ b/lib/pure/smtp.nim @@ -51,8 +51,6 @@ type Smtp* = SmtpBase[Socket] AsyncSmtp* = SmtpBase[AsyncSocket] -{.deprecated: [EInvalidReply: ReplyError, TMessage: Message, TSMTP: Smtp].} - proc debugSend(smtp: Smtp | AsyncSmtp, cmd: string) {.multisync.} = if smtp.debug: echo("C:" & cmd) diff --git a/lib/pure/stats.nim b/lib/pure/stats.nim index 964938133..ce32108c2 100644 --- a/lib/pure/stats.nim +++ b/lib/pure/stats.nim @@ -65,8 +65,6 @@ type y_stats*: RunningStat ## stats for second set of data s_xy: float ## accumulated data for combined xy -{.deprecated: [TFloatClass: FloatClass, TRunningStat: RunningStat].} - # ----------- RunningStat -------------------------- proc clear*(s: var RunningStat) = ## reset `s` diff --git a/lib/pure/streams.nim b/lib/pure/streams.nim index 8fb14b6f6..68922f730 100644 --- a/lib/pure/streams.nim +++ b/lib/pure/streams.nim @@ -57,8 +57,6 @@ type tags: [WriteIOEffect], gcsafe.} flushImpl*: proc (s: Stream) {.nimcall, tags: [WriteIOEffect], gcsafe.} -{.deprecated: [PStream: Stream, TStream: StreamObj].} - proc flush*(s: Stream) = ## flushes the buffers that the stream `s` might use. if not isNil(s.flushImpl): s.flushImpl(s) @@ -76,27 +74,14 @@ proc atEnd*(s: Stream): bool = ## been read. result = s.atEndImpl(s) -proc atEnd*(s, unused: Stream): bool {.deprecated.} = - ## checks if more data can be read from `f`. Returns true if all data has - ## been read. - result = s.atEndImpl(s) - proc setPosition*(s: Stream, pos: int) = ## sets the position `pos` of the stream `s`. s.setPositionImpl(s, pos) -proc setPosition*(s, unused: Stream, pos: int) {.deprecated.} = - ## sets the position `pos` of the stream `s`. - s.setPositionImpl(s, pos) - proc getPosition*(s: Stream): int = ## retrieves the current position in the stream `s`. result = s.getPositionImpl(s) -proc getPosition*(s, unused: Stream): int {.deprecated.} = - ## retrieves the current position in the stream `s`. - result = s.getPositionImpl(s) - proc readData*(s: Stream, buffer: pointer, bufLen: int): int = ## low level proc that reads data into an untyped `buffer` of `bufLen` size. result = s.readDataImpl(s, buffer, bufLen) @@ -114,11 +99,6 @@ proc readAll*(s: Stream): string = inc r, bufferSize setLen(result, r+bufferSize) -proc readData*(s, unused: Stream, buffer: pointer, - bufLen: int): int {.deprecated.} = - ## low level proc that reads data into an untyped `buffer` of `bufLen` size. - result = s.readDataImpl(s, buffer, bufLen) - proc peekData*(s: Stream, buffer: pointer, bufLen: int): int = ## low level proc that reads data into an untyped `buffer` of `bufLen` size ## without moving stream position @@ -153,11 +133,6 @@ proc write*(s: Stream, x: string) = else: if x.len > 0: writeData(s, unsafeAddr x[0], x.len) -proc writeLn*(s: Stream, args: varargs[string, `$`]) {.deprecated.} = - ## **Deprecated since version 0.11.4:** Use **writeLine** instead. - for str in args: write(s, str) - write(s, "\n") - proc writeLine*(s: Stream, args: varargs[string, `$`]) = ## writes one or more strings to the the stream `s` followed ## by a new line. No length field or terminating zero is written. @@ -348,8 +323,6 @@ when not defined(js): data*: string pos: int - {.deprecated: [PStringStream: StringStream, TStringStream: StringStreamObj].} - proc ssAtEnd(s: Stream): bool = var s = StringStream(s) return s.pos >= s.data.len @@ -409,7 +382,6 @@ when not defined(js): FileStream* = ref FileStreamObj ## a stream that encapsulates a `File` FileStreamObj* = object of Stream f: File - {.deprecated: [PFileStream: FileStream, TFileStream: FileStreamObj].} proc fsClose(s: Stream) = if FileStream(s).f != nil: @@ -472,9 +444,6 @@ else: handle*: FileHandle pos: int - {.deprecated: [PFileHandleStream: FileHandleStream, - TFileHandleStream: FileHandleStreamObj].} - proc newEOS(msg: string): ref OSError = new(result) result.msg = msg diff --git a/lib/pure/strformat.nim b/lib/pure/strformat.nim index abdb655d7..a8b128460 100644 --- a/lib/pure/strformat.nim +++ b/lib/pure/strformat.nim @@ -278,7 +278,7 @@ template callFormatOption(res, arg, option) {.dirty.} = macro `&`*(pattern: string): untyped = ## For a specification of the ``&`` macro, see the module level documentation. if pattern.kind notin {nnkStrLit..nnkTripleStrLit}: - error "& only works with string literals", pattern + error "string formatting (fmt(), &) only works with string literals", pattern let f = pattern.strVal var i = 0 let res = genSym(nskVar, "fmtRes") diff --git a/lib/pure/strmisc.nim b/lib/pure/strmisc.nim index 89ef2fcd2..d1ff920c9 100644 --- a/lib/pure/strmisc.nim +++ b/lib/pure/strmisc.nim @@ -12,7 +12,7 @@ import strutils -{.deadCodeElim: on.} +{.deadCodeElim: on.} # dce option deprecated proc expandTabs*(s: string, tabSize: int = 8): string {.noSideEffect, procvar.} = diff --git a/lib/pure/strscans.nim b/lib/pure/strscans.nim index f8a0c43ee..b0af149b5 100644 --- a/lib/pure/strscans.nim +++ b/lib/pure/strscans.nim @@ -87,7 +87,7 @@ which we then use in our scanf pattern to help us in the matching process: proc someSep(input: string; start: int; seps: set[char] = {':','-','.'}): int = # Note: The parameters and return value must match to what ``scanf`` requires result = 0 - while input[start+result] in seps: inc result + while start+result < input.len and input[start+result] in seps: inc result if scanf(input, "$w$[someSep]$w", key, value): ... @@ -231,7 +231,7 @@ is performed. var i = start var u = 0 while true: - if s[i] == '\0' or s[i] == unless: + if i >= s.len or s[i] == unless: return 0 elif s[i] == until[0]: u = 1 @@ -315,6 +315,8 @@ macro scanf*(input: string; pattern: static[string]; results: varargs[typed]): b conds.add resLen.notZero conds.add resLen + template at(s: string; i: int): char = (if i < s.len: s[i] else: '\0') + var i = 0 var p = 0 var idx = genSym(nskVar, "idx") @@ -397,7 +399,7 @@ macro scanf*(input: string; pattern: static[string]; results: varargs[typed]): b var nesting = 0 let start = p while true: - case pattern[p] + case pattern.at(p) of '{': inc nesting of '}': if nesting == 0: break @@ -419,7 +421,7 @@ macro scanf*(input: string; pattern: static[string]; results: varargs[typed]): b var nesting = 0 let start = p while true: - case pattern[p] + case pattern.at(p) of '[': inc nesting of ']': if nesting == 0: break @@ -451,10 +453,12 @@ macro scanf*(input: string; pattern: static[string]; results: varargs[typed]): b template atom*(input: string; idx: int; c: char): bool = ## Used in scanp for the matching of atoms (usually chars). - input[idx] == c + idx < input.len and input[idx] == c template atom*(input: string; idx: int; s: set[char]): bool = - input[idx] in s + idx < input.len and input[idx] in s + +template hasNxt*(input: string; idx: int): bool = idx < input.len #template prepare*(input: string): int = 0 template success*(x: int): bool = x != 0 @@ -462,7 +466,7 @@ template success*(x: int): bool = x != 0 template nxt*(input: string; idx, step: int = 1) = inc(idx, step) macro scanp*(input, idx: typed; pattern: varargs[untyped]): bool = - ## ``scanp`` is currently undocumented. + ## See top level documentation of his module of how ``scanf`` works. type StmtTriple = tuple[init, cond, action: NimNode] template interf(x): untyped = bindSym(x, brForceOpen) @@ -508,8 +512,8 @@ macro scanp*(input, idx: typed; pattern: varargs[untyped]): bool = !!newCall(interf"nxt", input, idx, resLen)) of nnkCallKinds: # *{'A'..'Z'} !! s.add(!_) - template buildWhile(init, cond, action): untyped = - while true: + template buildWhile(input, idx, init, cond, action): untyped = + while hasNxt(input, idx): init if not cond: break action @@ -528,7 +532,7 @@ macro scanp*(input, idx: typed; pattern: varargs[untyped]): bool = !!newCall(interf"nxt", input, idx, it[2])) elif it.kind == nnkPrefix and it[0].eqIdent"*": let (init, cond, action) = atm(it[1], input, idx, attached) - result = (getAst(buildWhile(init, cond, action)), + result = (getAst(buildWhile(input, idx, init, cond, action)), newEmptyNode(), newEmptyNode()) elif it.kind == nnkPrefix and it[0].eqIdent"+": # x+ is the same as xx* @@ -621,7 +625,7 @@ macro scanp*(input, idx: typed; pattern: varargs[untyped]): bool = when isMainModule: proc twoDigits(input: string; x: var int; start: int): int = - if input[start] == '0' and input[start+1] == '0': + if start+1 < input.len and input[start] == '0' and input[start+1] == '0': result = 2 x = 13 else: @@ -629,10 +633,10 @@ when isMainModule: proc someSep(input: string; start: int; seps: set[char] = {';',',','-','.'}): int = result = 0 - while input[start+result] in seps: inc result + while start+result < input.len and input[start+result] in seps: inc result proc demangle(s: string; res: var string; start: int): int = - while s[result+start] in {'_', '@'}: inc result + while result+start < s.len and s[result+start] in {'_', '@'}: inc result res = "" while result+start < s.len and s[result+start] > ' ' and s[result+start] != '_': res.add s[result+start] @@ -652,7 +656,7 @@ when isMainModule: var info = "" if scanp(resp, idx, *`whites`, '#', *`digits`, +`whites`, ?("0x", *`hexdigits`, " in "), demangle($input, prc, $index), *`whites`, '(', * ~ ')', ')', - *`whites`, "at ", +(~{'\C', '\L', '\0'} -> info.add($_)) ): + *`whites`, "at ", +(~{'\C', '\L'} -> info.add($_)) ): result.add prc & " " & info else: break @@ -713,7 +717,7 @@ when isMainModule: "NimMainInner c:/users/anwender/projects/nim/lib/system.nim:2605", "NimMain c:/users/anwender/projects/nim/lib/system.nim:2613", "main c:/users/anwender/projects/nim/lib/system.nim:2620"] - doAssert parseGDB(gdbOut) == result + #doAssert parseGDB(gdbOut) == result # bug #6487 var count = 0 diff --git a/lib/pure/strtabs.nim b/lib/pure/strtabs.nim index 75c5e171d..d8a23286a 100644 --- a/lib/pure/strtabs.nim +++ b/lib/pure/strtabs.nim @@ -17,9 +17,7 @@ import when defined(js): {.pragma: rtlFunc.} - {.pragma: deprecatedGetFunc.} else: - {.pragma: deprecatedGetFunc, deprecatedGet.} {.pragma: rtlFunc, rtl.} import os include "system/inclrtl" @@ -29,7 +27,7 @@ type modeCaseSensitive, ## the table is case sensitive modeCaseInsensitive, ## the table is case insensitive modeStyleInsensitive ## the table is style insensitive - KeyValuePair = tuple[key, val: string] + KeyValuePair = tuple[key, val: string, hasValue: bool] KeyValuePairSeq = seq[KeyValuePair] StringTableObj* = object of RootObj counter: int @@ -38,9 +36,6 @@ type StringTableRef* = ref StringTableObj ## use this type to declare string tables -{.deprecated: [TStringTableMode: StringTableMode, - TStringTable: StringTableObj, PStringTable: StringTableRef].} - proc len*(t: StringTableRef): int {.rtlFunc, extern: "nst$1".} = ## returns the number of keys in `t`. result = t.counter @@ -48,19 +43,19 @@ proc len*(t: StringTableRef): int {.rtlFunc, extern: "nst$1".} = iterator pairs*(t: StringTableRef): tuple[key, value: string] = ## iterates over every (key, value) pair in the table `t`. for h in 0..high(t.data): - if not isNil(t.data[h].key): + if t.data[h].hasValue: yield (t.data[h].key, t.data[h].val) iterator keys*(t: StringTableRef): string = ## iterates over every key in the table `t`. for h in 0..high(t.data): - if not isNil(t.data[h].key): + if t.data[h].hasValue: yield t.data[h].key iterator values*(t: StringTableRef): string = ## iterates over every value in the table `t`. for h in 0..high(t.data): - if not isNil(t.data[h].key): + if t.data[h].hasValue: yield t.data[h].val type @@ -73,10 +68,6 @@ type useKey ## do not replace ``$key`` if it is not found ## in the table (or in the environment) -{.deprecated: [TFormatFlag: FormatFlag].} - -# implementation - const growthFactor = 2 startSize = 64 @@ -102,7 +93,7 @@ proc nextTry(h, maxHash: Hash): Hash {.inline.} = proc rawGet(t: StringTableRef, key: string): int = var h: Hash = myhash(t, key) and high(t.data) # start with real hash value - while not isNil(t.data[h].key): + while t.data[h].hasValue: if myCmp(t, t.data[h].key, key): return h h = nextTry(h, high(t.data)) @@ -118,17 +109,12 @@ template get(t: StringTableRef, key: string) = raise newException(KeyError, "key not found") proc `[]`*(t: StringTableRef, key: string): var string {. - rtlFunc, extern: "nstTake", deprecatedGetFunc.} = + rtlFunc, extern: "nstTake".} = ## retrieves the location at ``t[key]``. If `key` is not in `t`, the ## ``KeyError`` exception is raised. One can check with ``hasKey`` whether ## the key exists. get(t, key) -proc mget*(t: StringTableRef, key: string): var string {.deprecated.} = - ## retrieves the location at ``t[key]``. If `key` is not in `t`, the - ## ``KeyError`` exception is raised. Use ```[]``` instead. - get(t, key) - proc getOrDefault*(t: StringTableRef; key: string, default: string = ""): string = var index = rawGet(t, key) if index >= 0: result = t.data[index].val @@ -144,16 +130,17 @@ proc contains*(t: StringTableRef, key: string): bool = proc rawInsert(t: StringTableRef, data: var KeyValuePairSeq, key, val: string) = var h: Hash = myhash(t, key) and high(data) - while not isNil(data[h].key): + while data[h].hasValue: h = nextTry(h, high(data)) data[h].key = key data[h].val = val + data[h].hasValue = true proc enlarge(t: StringTableRef) = var n: KeyValuePairSeq newSeq(n, len(t.data) * growthFactor) for i in countup(0, high(t.data)): - if not isNil(t.data[i].key): rawInsert(t, n, t.data[i].key, t.data[i].val) + if t.data[i].hasValue: rawInsert(t, n, t.data[i].key, t.data[i].val) swap(t.data, n) proc `[]=`*(t: StringTableRef, key, val: string) {.rtlFunc, extern: "nstPut".} = @@ -192,14 +179,14 @@ proc newStringTable*(mode: StringTableMode): StringTableRef {. result.counter = 0 newSeq(result.data, startSize) -proc clear*(s: StringTableRef, mode: StringTableMode) = +proc clear*(s: StringTableRef, mode: StringTableMode) {. + rtlFunc, extern: "nst$1".} = ## resets a string table to be empty again. s.mode = mode s.counter = 0 s.data.setLen(startSize) for i in 0..<s.data.len: - if not isNil(s.data[i].key): - s.data[i].key = nil + s.data[i].hasValue = false proc newStringTable*(keyValuePairs: varargs[string], mode: StringTableMode): StringTableRef {. diff --git a/lib/pure/strutils.nim b/lib/pure/strutils.nim index d772b066c..ee3072c85 100644 --- a/lib/pure/strutils.nim +++ b/lib/pure/strutils.nim @@ -17,7 +17,7 @@ import parseutils from math import pow, round, floor, log10 from algorithm import reverse -{.deadCodeElim: on.} +{.deadCodeElim: on.} # dce option deprecated {.push debugger:off .} # the user does not want to trace a part # of the standard library! @@ -106,6 +106,12 @@ proc isUpperAscii*(c: char): bool {.noSideEffect, procvar, ## This checks ASCII characters only. return c in {'A'..'Z'} +template isImpl(call) = + if s.len == 0: return false + result = true + for c in s: + if not call(c): return false + proc isAlphaAscii*(s: string): bool {.noSideEffect, procvar, rtl, extern: "nsuIsAlphaAsciiStr".} = ## Checks whether or not `s` is alphabetical. @@ -114,12 +120,7 @@ proc isAlphaAscii*(s: string): bool {.noSideEffect, procvar, ## Returns true if all characters in `s` are ## alphabetic and there is at least one character ## in `s`. - if s.len() == 0: - return false - - result = true - for c in s: - if not c.isAlphaAscii(): return false + isImpl isAlphaAscii proc isAlphaNumeric*(s: string): bool {.noSideEffect, procvar, rtl, extern: "nsuIsAlphaNumericStr".} = @@ -129,13 +130,7 @@ proc isAlphaNumeric*(s: string): bool {.noSideEffect, procvar, ## Returns true if all characters in `s` are ## alpanumeric and there is at least one character ## in `s`. - if s.len() == 0: - return false - - result = true - for c in s: - if not c.isAlphaNumeric(): - return false + isImpl isAlphaNumeric proc isDigit*(s: string): bool {.noSideEffect, procvar, rtl, extern: "nsuIsDigitStr".} = @@ -145,13 +140,7 @@ proc isDigit*(s: string): bool {.noSideEffect, procvar, ## Returns true if all characters in `s` are ## numeric and there is at least one character ## in `s`. - if s.len() == 0: - return false - - result = true - for c in s: - if not c.isDigit(): - return false + isImpl isDigit proc isSpaceAscii*(s: string): bool {.noSideEffect, procvar, rtl, extern: "nsuIsSpaceAsciiStr".} = @@ -159,13 +148,7 @@ proc isSpaceAscii*(s: string): bool {.noSideEffect, procvar, ## ## Returns true if all characters in `s` are whitespace ## characters and there is at least one character in `s`. - if s.len() == 0: - return false - - result = true - for c in s: - if not c.isSpaceAscii(): - return false + isImpl isSpaceAscii proc isLowerAscii*(s: string): bool {.noSideEffect, procvar, rtl, extern: "nsuIsLowerAsciiStr".} = @@ -174,13 +157,7 @@ proc isLowerAscii*(s: string): bool {.noSideEffect, procvar, ## This checks ASCII characters only. ## Returns true if all characters in `s` are lower case ## and there is at least one character in `s`. - if s.len() == 0: - return false - - for c in s: - if not c.isLowerAscii(): - return false - true + isImpl isLowerAscii proc isUpperAscii*(s: string): bool {.noSideEffect, procvar, rtl, extern: "nsuIsUpperAsciiStr".} = @@ -189,13 +166,7 @@ proc isUpperAscii*(s: string): bool {.noSideEffect, procvar, ## This checks ASCII characters only. ## Returns true if all characters in `s` are upper case ## and there is at least one character in `s`. - if s.len() == 0: - return false - - for c in s: - if not c.isUpperAscii(): - return false - true + isImpl isUpperAscii proc toLowerAscii*(c: char): char {.noSideEffect, procvar, rtl, extern: "nsuToLowerAsciiChar".} = @@ -209,6 +180,11 @@ proc toLowerAscii*(c: char): char {.noSideEffect, procvar, else: result = c +template toImpl(call) = + result = newString(len(s)) + for i in 0..len(s) - 1: + result[i] = call(s[i]) + proc toLowerAscii*(s: string): string {.noSideEffect, procvar, rtl, extern: "nsuToLowerAsciiStr".} = ## Converts `s` into lower case. @@ -216,9 +192,7 @@ proc toLowerAscii*(s: string): string {.noSideEffect, procvar, ## This works only for the letters ``A-Z``. See `unicode.toLower ## <unicode.html#toLower>`_ for a version that works for any Unicode ## character. - result = newString(len(s)) - for i in 0..len(s) - 1: - result[i] = toLowerAscii(s[i]) + toImpl toLowerAscii proc toUpperAscii*(c: char): char {.noSideEffect, procvar, rtl, extern: "nsuToUpperAsciiChar".} = @@ -239,147 +213,15 @@ proc toUpperAscii*(s: string): string {.noSideEffect, procvar, ## This works only for the letters ``A-Z``. See `unicode.toUpper ## <unicode.html#toUpper>`_ for a version that works for any Unicode ## character. - result = newString(len(s)) - for i in 0..len(s) - 1: - result[i] = toUpperAscii(s[i]) + toImpl toUpperAscii proc capitalizeAscii*(s: string): string {.noSideEffect, procvar, rtl, extern: "nsuCapitalizeAscii".} = ## Converts the first character of `s` into upper case. ## ## This works only for the letters ``A-Z``. - result = toUpperAscii(s[0]) & substr(s, 1) - -proc isSpace*(c: char): bool {.noSideEffect, procvar, - rtl, deprecated, extern: "nsuIsSpaceChar".}= - ## Checks whether or not `c` is a whitespace character. - ## - ## **Deprecated since version 0.15.0**: use ``isSpaceAscii`` instead. - isSpaceAscii(c) - -proc isLower*(c: char): bool {.noSideEffect, procvar, - rtl, deprecated, extern: "nsuIsLowerChar".}= - ## Checks whether or not `c` is a lower case character. - ## - ## This checks ASCII characters only. - ## - ## **Deprecated since version 0.15.0**: use ``isLowerAscii`` instead. - isLowerAscii(c) - -proc isUpper*(c: char): bool {.noSideEffect, procvar, - rtl, deprecated, extern: "nsuIsUpperChar".}= - ## Checks whether or not `c` is an upper case character. - ## - ## This checks ASCII characters only. - ## - ## **Deprecated since version 0.15.0**: use ``isUpperAscii`` instead. - isUpperAscii(c) - -proc isAlpha*(c: char): bool {.noSideEffect, procvar, - rtl, deprecated, extern: "nsuIsAlphaChar".}= - ## Checks whether or not `c` is alphabetical. - ## - ## This checks a-z, A-Z ASCII characters only. - ## - ## **Deprecated since version 0.15.0**: use ``isAlphaAscii`` instead. - isAlphaAscii(c) - -proc isAlpha*(s: string): bool {.noSideEffect, procvar, - rtl, deprecated, extern: "nsuIsAlphaStr".}= - ## Checks whether or not `s` is alphabetical. - ## - ## This checks a-z, A-Z ASCII characters only. - ## Returns true if all characters in `s` are - ## alphabetic and there is at least one character - ## in `s`. - ## - ## **Deprecated since version 0.15.0**: use ``isAlphaAscii`` instead. - isAlphaAscii(s) - -proc isSpace*(s: string): bool {.noSideEffect, procvar, - rtl, deprecated, extern: "nsuIsSpaceStr".}= - ## Checks whether or not `s` is completely whitespace. - ## - ## Returns true if all characters in `s` are whitespace - ## characters and there is at least one character in `s`. - ## - ## **Deprecated since version 0.15.0**: use ``isSpaceAscii`` instead. - isSpaceAscii(s) - -proc isLower*(s: string): bool {.noSideEffect, procvar, - rtl, deprecated, extern: "nsuIsLowerStr".}= - ## Checks whether or not `s` contains all lower case characters. - ## - ## This checks ASCII characters only. - ## Returns true if all characters in `s` are lower case - ## and there is at least one character in `s`. - ## - ## **Deprecated since version 0.15.0**: use ``isLowerAscii`` instead. - isLowerAscii(s) - -proc isUpper*(s: string): bool {.noSideEffect, procvar, - rtl, deprecated, extern: "nsuIsUpperStr".}= - ## Checks whether or not `s` contains all upper case characters. - ## - ## This checks ASCII characters only. - ## Returns true if all characters in `s` are upper case - ## and there is at least one character in `s`. - ## - ## **Deprecated since version 0.15.0**: use ``isUpperAscii`` instead. - isUpperAscii(s) - -proc toLower*(c: char): char {.noSideEffect, procvar, - rtl, deprecated, extern: "nsuToLowerChar".} = - ## Converts `c` into lower case. - ## - ## This works only for the letters ``A-Z``. See `unicode.toLower - ## <unicode.html#toLower>`_ for a version that works for any Unicode - ## character. - ## - ## **Deprecated since version 0.15.0**: use ``toLowerAscii`` instead. - toLowerAscii(c) - -proc toLower*(s: string): string {.noSideEffect, procvar, - rtl, deprecated, extern: "nsuToLowerStr".} = - ## Converts `s` into lower case. - ## - ## This works only for the letters ``A-Z``. See `unicode.toLower - ## <unicode.html#toLower>`_ for a version that works for any Unicode - ## character. - ## - ## **Deprecated since version 0.15.0**: use ``toLowerAscii`` instead. - toLowerAscii(s) - -proc toUpper*(c: char): char {.noSideEffect, procvar, - rtl, deprecated, extern: "nsuToUpperChar".} = - ## Converts `c` into upper case. - ## - ## This works only for the letters ``A-Z``. See `unicode.toUpper - ## <unicode.html#toUpper>`_ for a version that works for any Unicode - ## character. - ## - ## **Deprecated since version 0.15.0**: use ``toUpperAscii`` instead. - toUpperAscii(c) - -proc toUpper*(s: string): string {.noSideEffect, procvar, - rtl, deprecated, extern: "nsuToUpperStr".} = - ## Converts `s` into upper case. - ## - ## This works only for the letters ``A-Z``. See `unicode.toUpper - ## <unicode.html#toUpper>`_ for a version that works for any Unicode - ## character. - ## - ## **Deprecated since version 0.15.0**: use ``toUpperAscii`` instead. - toUpperAscii(s) - -proc capitalize*(s: string): string {.noSideEffect, procvar, - rtl, deprecated, extern: "nsuCapitalize".} = - ## Converts the first character of `s` into upper case. - ## - ## This works only for the letters ``A-Z``. - ## - ## **Deprecated since version 0.15.0**: use ``capitalizeAscii`` instead. - capitalizeAscii(s) + if s.len == 0: result = "" + else: result = toUpperAscii(s[0]) & substr(s, 1) proc normalize*(s: string): string {.noSideEffect, procvar, rtl, extern: "nsuNormalize".} = @@ -419,9 +261,9 @@ proc cmpIgnoreCase*(a, b: string): int {.noSideEffect, proc cmpIgnoreStyle*(a, b: string): int {.noSideEffect, rtl, extern: "nsuCmpIgnoreStyle", procvar.} = ## Semantically the same as ``cmp(normalize(a), normalize(b))``. It - ## is just optimized to not allocate temporary strings. This should + ## is just optimized to not allocate temporary strings. This should ## NOT be used to compare Nim identifier names. use `macros.eqIdent` - ## for that. Returns: + ## for that. Returns: ## ## | 0 iff a == b ## | < 0 iff a < b @@ -429,14 +271,22 @@ proc cmpIgnoreStyle*(a, b: string): int {.noSideEffect, var i = 0 var j = 0 while true: - while a[i] == '_': inc(i) - while b[j] == '_': inc(j) # BUGFIX: typo - var aa = toLowerAscii(a[i]) - var bb = toLowerAscii(b[j]) + while i < a.len and a[i] == '_': inc i + while j < b.len and b[j] == '_': inc j + var aa = if i < a.len: toLowerAscii(a[i]) else: '\0' + var bb = if j < b.len: toLowerAscii(b[j]) else: '\0' result = ord(aa) - ord(bb) - if result != 0 or aa == '\0': break - inc(i) - inc(j) + if result != 0: return result + # the characters are identical: + if i >= a.len: + # both cursors at the end: + if j >= b.len: return 0 + # not yet at the end of 'b': + return -1 + elif j >= b.len: + return 1 + inc i + inc j proc strip*(s: string, leading = true, trailing = true, chars: set[char] = Whitespace): string @@ -451,7 +301,7 @@ proc strip*(s: string, leading = true, trailing = true, first = 0 last = len(s)-1 if leading: - while s[first] in chars: inc(first) + while first <= last and s[first] in chars: inc(first) if trailing: while last >= 0 and s[last] in chars: dec(last) result = substr(s, first, last) @@ -467,7 +317,9 @@ proc toOctal*(c: char): string {.noSideEffect, rtl, extern: "nsuToOctal".} = result[i] = chr(val mod 8 + ord('0')) val = val div 8 -proc isNilOrEmpty*(s: string): bool {.noSideEffect, procvar, rtl, extern: "nsuIsNilOrEmpty".} = +proc isNilOrEmpty*(s: string): bool {.noSideEffect, procvar, rtl, + extern: "nsuIsNilOrEmpty", + deprecated: "use 'x.len == 0' instead".} = ## Checks if `s` is nil or empty. result = len(s) == 0 @@ -486,7 +338,6 @@ proc substrEq(s: string, pos: int, substr: string): bool = var length = substr.len while i < length and s[pos+i] == substr[i]: inc i - return i == length # --------- Private templates for different split separators ----------- @@ -520,7 +371,7 @@ template oldSplit(s, seps, maxsplit) = var splits = maxsplit assert(not ('\0' in seps)) while last < len(s): - while s[last] in seps: inc(last) + while last < len(s) and s[last] in seps: inc(last) var first = last while last < len(s) and s[last] notin seps: inc(last) if first <= last-1: @@ -571,10 +422,7 @@ iterator split*(s: string, seps: set[char] = Whitespace, ## "08" ## "08.398990" ## - when defined(nimOldSplit): - oldSplit(s, seps, maxsplit) - else: - splitCommon(s, seps, maxsplit, 1) + splitCommon(s, seps, maxsplit, 1) iterator splitWhitespace*(s: string, maxsplit: int = -1): string = ## Splits the string ``s`` at whitespace stripping leading and trailing @@ -660,7 +508,6 @@ iterator split*(s: string, sep: string, maxsplit: int = -1): string = ## "is" ## "corrupted" ## - splitCommon(s, sep, maxsplit, sep.len) template rsplitCommon(s, sep, maxsplit, sepLen) = @@ -670,29 +517,21 @@ template rsplitCommon(s, sep, maxsplit, sepLen) = first = last splits = maxsplit startPos = 0 - # go to -1 in order to get separators at the beginning while first >= -1: while first >= 0 and not stringHasSep(s, first, sep): dec(first) - if splits == 0: # No more splits means set first to the beginning first = -1 - if first == -1: startPos = 0 else: startPos = first + sepLen - yield substr(s, startPos, last) - - if splits == 0: - break - + if splits == 0: break dec(splits) dec(first) - last = first iterator rsplit*(s: string, seps: set[char] = Whitespace, @@ -712,7 +551,6 @@ iterator rsplit*(s: string, seps: set[char] = Whitespace, ## "foo" ## ## Substrings are separated from the right by the set of chars `seps` - rsplitCommon(s, seps, maxsplit, 1) iterator rsplit*(s: string, sep: char, @@ -779,14 +617,14 @@ iterator splitLines*(s: string): string = var first = 0 var last = 0 while true: - while s[last] notin {'\0', '\c', '\l'}: inc(last) + 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 s[last] == '\l': inc(last) - else: break # was '\0' + if last < s.len and s[last] == '\l': inc(last) first = last proc splitLines*(s: string): seq[string] {.noSideEffect, @@ -811,7 +649,7 @@ proc countLines*(s: string): int {.noSideEffect, while i < s.len: case s[i] of '\c': - if s[i+1] == '\l': inc i + if i+1 < s.len and s[i+1] == '\l': inc i inc result of '\l': inc result else: discard @@ -1025,9 +863,9 @@ proc parseHexInt*(s: string): int {.noSideEffect, procvar, ## of the following optional prefixes: ``0x``, ``0X``, ``#``. Underscores ## within `s` are ignored. var i = 0 - if s[i] == '0' and (s[i+1] == 'x' or s[i+1] == 'X'): inc(i, 2) - elif s[i] == '#': inc(i) - while true: + if i+1 < s.len and s[i] == '0' and (s[i+1] == 'x' or s[i+1] == 'X'): inc(i, 2) + elif i < s.len and s[i] == '#': inc(i) + while i < s.len: case s[i] of '_': inc(i) of '0'..'9': @@ -1039,7 +877,6 @@ proc parseHexInt*(s: string): int {.noSideEffect, procvar, of 'A'..'F': result = result shl 4 or (ord(s[i]) - ord('A') + 10) inc(i) - of '\0': break else: raise newException(ValueError, "invalid integer: " & s) proc generateHexCharToValueMap(): string = @@ -1148,14 +985,6 @@ template spaces*(n: Natural): string = repeat(' ', n) ## echo text1 & spaces(max(0, width - text1.len)) & "|" ## echo text2 & spaces(max(0, width - text2.len)) & "|" -proc repeatChar*(count: Natural, c: char = ' '): string {.deprecated.} = - ## deprecated: use repeat() or spaces() - repeat(c, count) - -proc repeatStr*(count: Natural, s: string): string {.deprecated.} = - ## deprecated: use repeat(string, count) or string.repeat(count) - repeat(s, count) - proc align*(s: string, count: Natural, padding = ' '): string {. noSideEffect, rtl, extern: "nsuAlignString".} = ## Aligns a string `s` with `padding`, so that it is of length `count`. @@ -1226,7 +1055,7 @@ iterator tokenize*(s: string, seps: set[char] = Whitespace): tuple[ var i = 0 while true: var j = i - var isSep = s[j] in seps + var isSep = j < s.len and s[j] in seps while j < s.len and (s[j] in seps) == isSep: inc(j) if j > i: yield (substr(s, i, j-1), isSep) @@ -1297,7 +1126,7 @@ proc unindent*(s: string, count: Natural, padding: string = " "): string var indentCount = 0 for j in 0..<count.int: indentCount.inc - if line[j .. j + padding.len-1] != padding: + if j + padding.len-1 >= line.len or line[j .. j + padding.len-1] != padding: indentCount = j break result.add(line[indentCount*padding.len .. ^1]) @@ -1325,13 +1154,13 @@ proc startsWith*(s, prefix: string): bool {.noSideEffect, ## If ``prefix == ""`` true is returned. var i = 0 while true: - if prefix[i] == '\0': return true - if s[i] != prefix[i]: return false + if i >= prefix.len: return true + if i >= s.len or s[i] != prefix[i]: return false inc(i) proc startsWith*(s: string, prefix: char): bool {.noSideEffect, inline.} = ## Returns true iff ``s`` starts with ``prefix``. - result = s[0] == prefix + result = s.len > 0 and s[0] == prefix proc endsWith*(s, suffix: string): bool {.noSideEffect, rtl, extern: "nsuEndsWith".} = @@ -1343,11 +1172,11 @@ proc endsWith*(s, suffix: string): bool {.noSideEffect, while i+j <% s.len: if s[i+j] != suffix[i]: return false inc(i) - if suffix[i] == '\0': return true + if i >= suffix.len: return true proc endsWith*(s: string, suffix: char): bool {.noSideEffect, inline.} = ## Returns true iff ``s`` ends with ``suffix``. - result = s[s.high] == suffix + result = s.len > 0 and s[s.high] == suffix proc continuesWith*(s, substr: string, start: Natural): bool {.noSideEffect, rtl, extern: "nsuContinuesWith".} = @@ -1356,8 +1185,8 @@ proc continuesWith*(s, substr: string, start: Natural): bool {.noSideEffect, ## If ``substr == ""`` true is returned. var i = 0 while true: - if substr[i] == '\0': return true - if s[i+start] != substr[i]: return false + if i >= substr.len: return true + if i+start >= s.len or s[i+start] != substr[i]: return false inc(i) proc addSep*(dest: var string, sep = ", ", startLen: Natural = 0) @@ -1433,21 +1262,20 @@ proc initSkipTable*(a: var SkipTable, sub: string) {.noSideEffect, rtl, extern: "nsuInitSkipTable".} = ## Preprocess table `a` for `sub`. let m = len(sub) - let m1 = m + 1 var i = 0 while i <= 0xff-7: - a[chr(i + 0)] = m1 - a[chr(i + 1)] = m1 - a[chr(i + 2)] = m1 - a[chr(i + 3)] = m1 - a[chr(i + 4)] = m1 - a[chr(i + 5)] = m1 - a[chr(i + 6)] = m1 - a[chr(i + 7)] = m1 + a[chr(i + 0)] = m + a[chr(i + 1)] = m + a[chr(i + 2)] = m + a[chr(i + 3)] = m + a[chr(i + 4)] = m + a[chr(i + 5)] = m + a[chr(i + 6)] = m + a[chr(i + 7)] = m i += 8 - for i in 0..m-1: - a[sub[i]] = m-i + for i in 0 ..< m - 1: + a[sub[i]] = m - 1 - i proc find*(a: SkipTable, s, sub: string, start: Natural = 0, last: Natural = 0): int {.noSideEffect, rtl, extern: "nsuFindStrA".} = @@ -1455,18 +1283,29 @@ proc find*(a: SkipTable, s, sub: string, start: Natural = 0, last: Natural = 0): ## If `last` is unspecified, it defaults to `s.high`. ## ## Searching is case-sensitive. If `sub` is not in `s`, -1 is returned. + let last = if last==0: s.high else: last - m = len(sub) - n = last + 1 - # search: - var j = start - while j <= n - m: - block match: - for k in 0..m-1: - if sub[k] != s[k+j]: break match - return j - inc(j, a[s[j+m]]) + sLen = last - start + 1 + subLast = sub.len - 1 + + if subLast == -1: + # this was an empty needle string, + # we count this as match in the first possible position: + return start + + # This is an implementation of the Boyer-Moore Horspool algorithms + # https://en.wikipedia.org/wiki/Boyer%E2%80%93Moore%E2%80%93Horspool_algorithm + var skip = start + + while last - skip >= subLast: + var i = subLast + while s[skip + i] == sub[i]: + if i == 0: + return skip + dec i + inc skip, a[s[skip + subLast]] + return -1 when not (defined(js) or defined(nimdoc) or defined(nimscript)): @@ -1502,12 +1341,8 @@ proc find*(s, sub: string, start: Natural = 0, last: Natural = 0): int {.noSideE ## If `last` is unspecified, it defaults to `s.high`. ## ## Searching is case-sensitive. If `sub` is not in `s`, -1 is returned. - if sub.len > s.len: - return -1 - - if sub.len == 1: - return find(s, sub[0], start, last) - + if sub.len > s.len: return -1 + if sub.len == 1: return find(s, sub[0], start, last) var a {.noinit.}: SkipTable initSkipTable(a, sub) result = find(a, s, sub, start, last) @@ -1564,18 +1399,14 @@ proc center*(s: string, width: int, fillChar: char = ' '): string {. ## ## The original string is returned if `width` is less than or equal ## to `s.len`. - if width <= s.len: - return s - + if width <= s.len: return s result = newString(width) - # Left padding will be one fillChar # smaller if there are an odd number # of characters let charsLeft = (width - s.len) leftPadding = charsLeft div 2 - for i in 0 ..< width: if i >= leftPadding and i < leftPadding + s.len: # we are where the string should be located @@ -1593,27 +1424,22 @@ proc count*(s: string, sub: string, overlapping: bool = false): int {. var i = 0 while true: i = s.find(sub, i) - if i < 0: - break - if overlapping: - inc i - else: - i += sub.len + if i < 0: break + if overlapping: inc i + else: i += sub.len inc result proc count*(s: string, sub: char): int {.noSideEffect, rtl, extern: "nsuCountChar".} = ## Count the occurrences of the character `sub` in the string `s`. for c in s: - if c == sub: - inc result + if c == sub: inc result proc count*(s: string, subs: set[char]): int {.noSideEffect, rtl, extern: "nsuCountCharSet".} = ## Count the occurrences of the group of character `subs` in the string `s`. for c in s: - if c in subs: - inc result + if c in subs: inc result proc quoteIfContainsWhite*(s: string): string {.deprecated.} = ## Returns ``'"' & s & '"'`` if `s` contains a space and does not @@ -1621,10 +1447,8 @@ proc quoteIfContainsWhite*(s: string): string {.deprecated.} = ## ## **DEPRECATED** as it was confused for shell quoting function. For this ## application use `osproc.quoteShell <osproc.html#quoteShell>`_. - if find(s, {' ', '\t'}) >= 0 and s[0] != '"': - result = '"' & s & '"' - else: - result = s + if find(s, {' ', '\t'}) >= 0 and s[0] != '"': result = '"' & s & '"' + else: result = s proc contains*(s: string, c: char): bool {.noSideEffect.} = ## Same as ``find(s, c) >= 0``. @@ -1641,19 +1465,41 @@ proc contains*(s: string, chars: set[char]): bool {.noSideEffect.} = proc replace*(s, sub: string, by = ""): string {.noSideEffect, rtl, extern: "nsuReplaceStr".} = ## Replaces `sub` in `s` by the string `by`. - var a {.noinit.}: SkipTable result = "" - initSkipTable(a, sub) - let last = s.high - var i = 0 - while true: - var j = find(a, s, sub, i, last) - if j < 0: break - add result, substr(s, i, j - 1) + let subLen = sub.len + if subLen == 0: + for c in s: + add result, by + add result, c add result, by - i = j + len(sub) - # copy the rest: - add result, substr(s, i) + return + 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 last = s.high + var i = 0 + while true: + let j = find(s, c, i, last) + if j < 0: break + add result, substr(s, i, j - 1) + add result, by + i = j + subLen + # copy the rest: + add result, substr(s, i) + else: + var a {.noinit.}: SkipTable + initSkipTable(a, sub) + let last = s.high + var i = 0 + while true: + let j = find(a, s, sub, i, last) + if j < 0: break + add result, substr(s, i, j - 1) + add result, by + i = j + subLen + # copy the rest: + add result, substr(s, i) proc replace*(s: string, sub, by: char): string {.noSideEffect, rtl, extern: "nsuReplaceChar".} = @@ -1674,12 +1520,14 @@ proc replaceWord*(s, sub: string, by = ""): string {.noSideEffect, ## Each occurrence of `sub` has to be surrounded by word boundaries ## (comparable to ``\\w`` in regular expressions), otherwise it is not ## replaced. + if sub.len == 0: return s const wordChars = {'a'..'z', 'A'..'Z', '0'..'9', '_', '\128'..'\255'} var a {.noinit.}: SkipTable result = "" initSkipTable(a, sub) var i = 0 let last = s.high + let sublen = max(sub.len, 1) while true: var j = find(a, s, sub, i, last) if j < 0: break @@ -1688,7 +1536,7 @@ proc replaceWord*(s, sub: string, by = ""): string {.noSideEffect, (j+sub.len >= s.len or s[j+sub.len] notin wordChars): add result, substr(s, i, j - 1) add result, by - i = j + len(sub) + i = j + sublen else: add result, substr(s, i, j) i = j + 1 @@ -1699,9 +1547,8 @@ proc multiReplace*(s: string, replacements: varargs[(string, string)]): string { ## Same as replace, but specialized for doing multiple replacements in a single ## pass through the input string. ## - ## Calling replace multiple times after each other is inefficient and result in too many allocations - ## follwed by immediate deallocations as portions of the string gets replaced. - ## multiReplace performs all replacements in a single pass. + ## multiReplace performs all replacements in a single pass, this means it can be used + ## to swap the occurences of "a" and "b", for instance. ## ## If the resulting string is not longer than the original input string, only a single ## memory allocation is required. @@ -1748,14 +1595,13 @@ proc parseOctInt*(s: string): int {.noSideEffect, ## of the following optional prefixes: ``0o``, ``0O``. Underscores within ## `s` are ignored. var i = 0 - if s[i] == '0' and (s[i+1] == 'o' or s[i+1] == 'O'): inc(i, 2) - while true: + if i+1 < s.len and s[i] == '0' and (s[i+1] == 'o' or s[i+1] == 'O'): inc(i, 2) + while i < s.len: case s[i] of '_': inc(i) of '0'..'7': result = result shl 3 or (ord(s[i]) - ord('0')) inc(i) - of '\0': break else: raise newException(ValueError, "invalid integer: " & s) proc toOct*(x: BiggestInt, len: Positive): string {.noSideEffect, @@ -1813,20 +1659,29 @@ proc insertSep*(s: string, sep = '_', digits = 3): string {.noSideEffect, dec(L) proc escape*(s: string, prefix = "\"", suffix = "\""): string {.noSideEffect, - rtl, extern: "nsuEscape".} = + rtl, extern: "nsuEscape", deprecated.} = ## Escapes a string `s`. See `system.addEscapedChar <system.html#addEscapedChar>`_ ## for the escaping scheme. ## ## The resulting string is prefixed with `prefix` and suffixed with `suffix`. ## Both may be empty strings. + ## + ## **Warning:** This procedure is deprecated because it's to easy to missuse. result = newStringOfCap(s.len + s.len shr 2) result.add(prefix) for c in items(s): - result.addEscapedChar(c) + case c + of '\0'..'\31', '\127'..'\255': + add(result, "\\x") + add(result, toHex(ord(c), 2)) + of '\\': add(result, "\\\\") + of '\'': add(result, "\\'") + of '\"': add(result, "\\\"") + else: add(result, c) add(result, suffix) proc unescape*(s: string, prefix = "\"", suffix = "\""): string {.noSideEffect, - rtl, extern: "nsuUnescape".} = + rtl, extern: "nsuUnescape", deprecated.} = ## Unescapes a string `s`. ## ## This complements `escape <#escape>`_ as it performs the opposite @@ -1834,15 +1689,19 @@ proc unescape*(s: string, prefix = "\"", suffix = "\""): string {.noSideEffect, ## ## If `s` does not begin with ``prefix`` and end with ``suffix`` a ## ValueError exception will be raised. + ## + ## **Warning:** This procedure is deprecated because it's to easy to missuse. result = newStringOfCap(s.len) var i = prefix.len if not s.startsWith(prefix): raise newException(ValueError, - "String does not start with a prefix of: " & prefix) + "String does not start with: " & prefix) while true: - if i == s.len-suffix.len: break - case s[i] - of '\\': + if i >= s.len-suffix.len: break + if s[i] == '\\': + if i+1 >= s.len: + result.add('\\') + break case s[i+1]: of 'x': inc i, 2 @@ -1856,15 +1715,15 @@ proc unescape*(s: string, prefix = "\"", suffix = "\""): string {.noSideEffect, result.add('\'') of '\"': result.add('\"') - else: result.add("\\" & s[i+1]) - inc(i) - of '\0': break + else: + result.add("\\" & s[i+1]) + inc(i, 2) else: result.add(s[i]) - inc(i) + inc(i) if not s.endsWith(suffix): raise newException(ValueError, - "String does not end with a suffix of: " & suffix) + "String does not end in: " & suffix) proc validIdentifier*(s: string): bool {.noSideEffect, rtl, extern: "nsuValidIdentifier".} = @@ -1874,7 +1733,7 @@ proc validIdentifier*(s: string): bool {.noSideEffect, ## and is followed by any number of characters of the set `IdentChars`. runnableExamples: doAssert "abc_def08".validIdentifier - if s[0] in IdentStartChars: + if s.len > 0 and s[0] in IdentStartChars: for i in 1..s.len-1: if s[i] notin IdentChars: return false return true @@ -1893,7 +1752,7 @@ proc editDistance*(a, b: string): int {.noSideEffect, # strip common prefix: var s = 0 - while a[s] == b[s] and a[s] != '\0': + while s < len1 and a[s] == b[s]: inc(s) dec(len1) dec(len2) @@ -1966,8 +1825,6 @@ proc editDistance*(a, b: string): int {.noSideEffect, if x > c3: x = c3 row[p] = x result = row[e] - #dealloc(row) - # floating point formating: when not defined(js): @@ -2076,7 +1933,7 @@ proc trimZeros*(x: var string) {.noSideEffect.} = var spl: seq[string] if x.contains('.') or x.contains(','): if x.contains('e'): - spl= x.split('e') + spl = x.split('e') x = spl[0] while x[x.high] == '0': x.setLen(x.len-1) @@ -2294,9 +2151,8 @@ proc addf*(s: var string, formatstr: string, a: varargs[string, `$`]) {. var i = 0 var num = 0 while i < len(formatstr): - if formatstr[i] == '$': - case formatstr[i+1] # again we use the fact that strings - # are zero-terminated here + if formatstr[i] == '$' and i+1 < len(formatstr): + case formatstr[i+1] of '#': if num > a.high: invalidFormatString() add s, a[num] @@ -2310,7 +2166,7 @@ proc addf*(s: var string, formatstr: string, a: varargs[string, `$`]) {. inc(i) # skip $ var negative = formatstr[i] == '-' if negative: inc i - while formatstr[i] in Digits: + while i < formatstr.len and formatstr[i] in Digits: j = j * 10 + ord(formatstr[i]) - ord('0') inc(i) let idx = if not negative: j-1 else: a.len-j @@ -2322,7 +2178,7 @@ proc addf*(s: var string, formatstr: string, a: varargs[string, `$`]) {. var negative = formatstr[j] == '-' if negative: inc j var isNumber = 0 - while formatstr[j] notin {'\0', '}'}: + while j < formatstr.len and formatstr[j] notin {'\0', '}'}: if formatstr[j] in Digits: k = k * 10 + ord(formatstr[j]) - ord('0') if isNumber == 0: isNumber = 1 @@ -2340,7 +2196,7 @@ proc addf*(s: var string, formatstr: string, a: varargs[string, `$`]) {. i = j+1 of 'a'..'z', 'A'..'Z', '\128'..'\255', '_': var j = i+1 - while formatstr[j] in PatternChars: inc(j) + while j < formatstr.len and formatstr[j] in PatternChars: inc(j) var x = findNormalized(substr(formatstr, i+1, j-1), a) if x >= 0 and x < high(a): add s, a[x+1] else: invalidFormatString() @@ -2499,239 +2355,243 @@ proc removePrefix*(s: var string, prefix: string) {. s.delete(0, prefix.len - 1) when isMainModule: - doAssert align("abc", 4) == " abc" - doAssert align("a", 0) == "a" - doAssert align("1232", 6) == " 1232" - doAssert align("1232", 6, '#') == "##1232" - - doAssert alignLeft("abc", 4) == "abc " - doAssert alignLeft("a", 0) == "a" - doAssert alignLeft("1232", 6) == "1232 " - doAssert alignLeft("1232", 6, '#') == "1232##" - - let - inp = """ this is a long text -- muchlongerthan10chars and here - it goes""" - outp = " this is a\nlong text\n--\nmuchlongerthan10chars\nand here\nit goes" - doAssert wordWrap(inp, 10, false) == outp - - let - longInp = """ThisIsOneVeryLongStringWhichWeWillSplitIntoEightSeparatePartsNow""" - longOutp = "ThisIsOn\neVeryLon\ngStringW\nhichWeWi\nllSplitI\nntoEight\nSeparate\nPartsNow" - doAssert wordWrap(longInp, 8, true) == longOutp - - doAssert formatBiggestFloat(1234.567, ffDecimal, -1) == "1234.567000" - doAssert formatBiggestFloat(1234.567, ffDecimal, 0) == "1235." - doAssert formatBiggestFloat(1234.567, ffDecimal, 1) == "1234.6" - doAssert formatBiggestFloat(0.00000000001, ffDecimal, 11) == "0.00000000001" - doAssert formatBiggestFloat(0.00000000001, ffScientific, 1, ',') in - ["1,0e-11", "1,0e-011"] - # bug #6589 - doAssert formatFloat(123.456, ffScientific, precision = -1) == "1.234560e+02" - - doAssert "$# $3 $# $#" % ["a", "b", "c"] == "a c b c" - doAssert "${1}12 ${-1}$2" % ["a", "b"] == "a12 bb" - - block: # formatSize tests - doAssert formatSize((1'i64 shl 31) + (300'i64 shl 20)) == "2.293GiB" - doAssert formatSize((2.234*1024*1024).int) == "2.234MiB" - doAssert formatSize(4096) == "4KiB" - doAssert formatSize(4096, prefix=bpColloquial, includeSpace=true) == "4 kB" - doAssert formatSize(4096, includeSpace=true) == "4 KiB" - doAssert formatSize(5_378_934, prefix=bpColloquial, decimalSep=',') == "5,13MB" - - doAssert "$animal eats $food." % ["animal", "The cat", "food", "fish"] == - "The cat eats fish." - - doAssert "-ld a-ldz -ld".replaceWord("-ld") == " a-ldz " - doAssert "-lda-ldz -ld abc".replaceWord("-ld") == "-lda-ldz abc" - - type MyEnum = enum enA, enB, enC, enuD, enE - doAssert parseEnum[MyEnum]("enu_D") == enuD - - doAssert parseEnum("invalid enum value", enC) == enC - - doAssert center("foo", 13) == " foo " - doAssert center("foo", 0) == "foo" - doAssert center("foo", 3, fillChar = 'a') == "foo" - doAssert center("foo", 10, fillChar = '\t') == "\t\t\tfoo\t\t\t\t" - - doAssert count("foofoofoo", "foofoo") == 1 - doAssert count("foofoofoo", "foofoo", overlapping = true) == 2 - doAssert count("foofoofoo", 'f') == 3 - doAssert count("foofoofoobar", {'f','b'}) == 4 - - doAssert strip(" foofoofoo ") == "foofoofoo" - doAssert strip("sfoofoofoos", chars = {'s'}) == "foofoofoo" - doAssert strip("barfoofoofoobar", chars = {'b', 'a', 'r'}) == "foofoofoo" - doAssert strip("stripme but don't strip this stripme", - chars = {'s', 't', 'r', 'i', 'p', 'm', 'e'}) == - " but don't strip this " - doAssert strip("sfoofoofoos", leading = false, chars = {'s'}) == "sfoofoofoo" - doAssert strip("sfoofoofoos", trailing = false, chars = {'s'}) == "foofoofoos" - - doAssert " foo\n bar".indent(4, "Q") == "QQQQ foo\nQQQQ bar" - - doAssert "abba".multiReplace(("a", "b"), ("b", "a")) == "baab" - doAssert "Hello World.".multiReplace(("ello", "ELLO"), ("World.", "PEOPLE!")) == "HELLO PEOPLE!" - doAssert "aaaa".multiReplace(("a", "aa"), ("aa", "bb")) == "aaaaaaaa" - - doAssert isAlphaAscii('r') - doAssert isAlphaAscii('A') - doAssert(not isAlphaAscii('$')) - - doAssert isAlphaAscii("Rasp") - doAssert isAlphaAscii("Args") - doAssert(not isAlphaAscii("$Tomato")) - - doAssert isAlphaNumeric('3') - doAssert isAlphaNumeric('R') - doAssert(not isAlphaNumeric('!')) - - doAssert isAlphaNumeric("34ABc") - doAssert isAlphaNumeric("Rad") - doAssert isAlphaNumeric("1234") - doAssert(not isAlphaNumeric("@nose")) - - doAssert isDigit('3') - doAssert(not isDigit('a')) - doAssert(not isDigit('%')) - - doAssert isDigit("12533") - doAssert(not isDigit("12.33")) - doAssert(not isDigit("A45b")) - - doAssert isSpaceAscii('\t') - doAssert isSpaceAscii('\l') - doAssert(not isSpaceAscii('A')) - - doAssert isSpaceAscii("\t\l \v\r\f") - doAssert isSpaceAscii(" ") - doAssert(not isSpaceAscii("ABc \td")) - - doAssert(isNilOrEmpty("")) - doAssert(isNilOrEmpty(nil)) - doAssert(not isNilOrEmpty("test")) - doAssert(not isNilOrEmpty(" ")) - - doAssert(isNilOrWhitespace("")) - doAssert(isNilOrWhitespace(nil)) - doAssert(isNilOrWhitespace(" ")) - doAssert(isNilOrWhitespace("\t\l \v\r\f")) - doAssert(not isNilOrWhitespace("ABc \td")) - - doAssert isLowerAscii('a') - doAssert isLowerAscii('z') - doAssert(not isLowerAscii('A')) - doAssert(not isLowerAscii('5')) - doAssert(not isLowerAscii('&')) - - doAssert isLowerAscii("abcd") - doAssert(not isLowerAscii("abCD")) - doAssert(not isLowerAscii("33aa")) - - doAssert isUpperAscii('A') - doAssert(not isUpperAscii('b')) - doAssert(not isUpperAscii('5')) - doAssert(not isUpperAscii('%')) - - doAssert isUpperAscii("ABC") - doAssert(not isUpperAscii("AAcc")) - doAssert(not isUpperAscii("A#$")) - - doAssert rsplit("foo bar", seps=Whitespace) == @["foo", "bar"] - doAssert rsplit(" foo bar", seps=Whitespace, maxsplit=1) == @[" foo", "bar"] - doAssert rsplit(" foo bar ", seps=Whitespace, maxsplit=1) == @[" foo bar", ""] - doAssert rsplit(":foo:bar", sep=':') == @["", "foo", "bar"] - doAssert rsplit(":foo:bar", sep=':', maxsplit=2) == @["", "foo", "bar"] - doAssert rsplit(":foo:bar", sep=':', maxsplit=3) == @["", "foo", "bar"] - doAssert rsplit("foothebar", sep="the") == @["foo", "bar"] - - doAssert(unescape(r"\x013", "", "") == "\x013") - - doAssert join(["foo", "bar", "baz"]) == "foobarbaz" - doAssert join(@["foo", "bar", "baz"], ", ") == "foo, bar, baz" - doAssert join([1, 2, 3]) == "123" - doAssert join(@[1, 2, 3], ", ") == "1, 2, 3" - - doAssert """~~!!foo + proc nonStaticTests = + doAssert formatBiggestFloat(1234.567, ffDecimal, -1) == "1234.567000" + doAssert formatBiggestFloat(1234.567, ffDecimal, 0) == "1235." + doAssert formatBiggestFloat(1234.567, ffDecimal, 1) == "1234.6" + doAssert formatBiggestFloat(0.00000000001, ffDecimal, 11) == "0.00000000001" + doAssert formatBiggestFloat(0.00000000001, ffScientific, 1, ',') in + ["1,0e-11", "1,0e-011"] + # bug #6589 + doAssert formatFloat(123.456, ffScientific, precision = -1) == "1.234560e+02" + + doAssert "$# $3 $# $#" % ["a", "b", "c"] == "a c b c" + doAssert "${1}12 ${-1}$2" % ["a", "b"] == "a12 bb" + + block: # formatSize tests + doAssert formatSize((1'i64 shl 31) + (300'i64 shl 20)) == "2.293GiB" + doAssert formatSize((2.234*1024*1024).int) == "2.234MiB" + doAssert formatSize(4096) == "4KiB" + doAssert formatSize(4096, prefix=bpColloquial, includeSpace=true) == "4 kB" + doAssert formatSize(4096, includeSpace=true) == "4 KiB" + doAssert formatSize(5_378_934, prefix=bpColloquial, decimalSep=',') == "5,13MB" + + block: # formatEng tests + doAssert formatEng(0, 2, trim=false) == "0.00" + doAssert formatEng(0, 2) == "0" + doAssert formatEng(53, 2, trim=false) == "53.00" + doAssert formatEng(0.053, 2, trim=false) == "53.00e-3" + doAssert formatEng(0.053, 4, trim=false) == "53.0000e-3" + doAssert formatEng(0.053, 4, trim=true) == "53e-3" + doAssert formatEng(0.053, 0) == "53e-3" + doAssert formatEng(52731234) == "52.731234e6" + doAssert formatEng(-52731234) == "-52.731234e6" + doAssert formatEng(52731234, 1) == "52.7e6" + doAssert formatEng(-52731234, 1) == "-52.7e6" + doAssert formatEng(52731234, 1, decimalSep=',') == "52,7e6" + doAssert formatEng(-52731234, 1, decimalSep=',') == "-52,7e6" + + doAssert formatEng(4100, siPrefix=true, unit="V") == "4.1 kV" + doAssert formatEng(4.1, siPrefix=true, unit="V", useUnitSpace=true) == "4.1 V" + doAssert formatEng(4.1, siPrefix=true) == "4.1" # Note lack of space + doAssert formatEng(4100, siPrefix=true) == "4.1 k" + doAssert formatEng(4.1, siPrefix=true, unit="", useUnitSpace=true) == "4.1 " # Includes space + doAssert formatEng(4100, siPrefix=true, unit="") == "4.1 k" + doAssert formatEng(4100) == "4.1e3" + doAssert formatEng(4100, unit="V", useUnitSpace=true) == "4.1e3 V" + doAssert formatEng(4100, unit="", useUnitSpace=true) == "4.1e3 " + # Don't use SI prefix as number is too big + doAssert formatEng(3.1e22, siPrefix=true, unit="a", useUnitSpace=true) == "31e21 a" + # Don't use SI prefix as number is too small + doAssert formatEng(3.1e-25, siPrefix=true, unit="A", useUnitSpace=true) == "310e-27 A" + + proc staticTests = + doAssert align("abc", 4) == " abc" + doAssert align("a", 0) == "a" + doAssert align("1232", 6) == " 1232" + doAssert align("1232", 6, '#') == "##1232" + + doAssert alignLeft("abc", 4) == "abc " + doAssert alignLeft("a", 0) == "a" + doAssert alignLeft("1232", 6) == "1232 " + doAssert alignLeft("1232", 6, '#') == "1232##" + + let + inp = """ this is a long text -- muchlongerthan10chars and here + it goes""" + outp = " this is a\nlong text\n--\nmuchlongerthan10chars\nand here\nit goes" + doAssert wordWrap(inp, 10, false) == outp + + let + longInp = """ThisIsOneVeryLongStringWhichWeWillSplitIntoEightSeparatePartsNow""" + longOutp = "ThisIsOn\neVeryLon\ngStringW\nhichWeWi\nllSplitI\nntoEight\nSeparate\nPartsNow" + doAssert wordWrap(longInp, 8, true) == longOutp + + doAssert "$animal eats $food." % ["animal", "The cat", "food", "fish"] == + "The cat eats fish." + + doAssert "-ld a-ldz -ld".replaceWord("-ld") == " a-ldz " + doAssert "-lda-ldz -ld abc".replaceWord("-ld") == "-lda-ldz abc" + + doAssert "-lda-ldz -ld abc".replaceWord("") == "-lda-ldz -ld abc" + doAssert "oo".replace("", "abc") == "abcoabcoabc" + + type MyEnum = enum enA, enB, enC, enuD, enE + doAssert parseEnum[MyEnum]("enu_D") == enuD + + doAssert parseEnum("invalid enum value", enC) == enC + + doAssert center("foo", 13) == " foo " + doAssert center("foo", 0) == "foo" + doAssert center("foo", 3, fillChar = 'a') == "foo" + doAssert center("foo", 10, fillChar = '\t') == "\t\t\tfoo\t\t\t\t" + + doAssert count("foofoofoo", "foofoo") == 1 + doAssert count("foofoofoo", "foofoo", overlapping = true) == 2 + doAssert count("foofoofoo", 'f') == 3 + doAssert count("foofoofoobar", {'f','b'}) == 4 + + doAssert strip(" foofoofoo ") == "foofoofoo" + doAssert strip("sfoofoofoos", chars = {'s'}) == "foofoofoo" + doAssert strip("barfoofoofoobar", chars = {'b', 'a', 'r'}) == "foofoofoo" + doAssert strip("stripme but don't strip this stripme", + chars = {'s', 't', 'r', 'i', 'p', 'm', 'e'}) == + " but don't strip this " + doAssert strip("sfoofoofoos", leading = false, chars = {'s'}) == "sfoofoofoo" + doAssert strip("sfoofoofoos", trailing = false, chars = {'s'}) == "foofoofoos" + + doAssert " foo\n bar".indent(4, "Q") == "QQQQ foo\nQQQQ bar" + + doAssert "abba".multiReplace(("a", "b"), ("b", "a")) == "baab" + doAssert "Hello World.".multiReplace(("ello", "ELLO"), ("World.", "PEOPLE!")) == "HELLO PEOPLE!" + doAssert "aaaa".multiReplace(("a", "aa"), ("aa", "bb")) == "aaaaaaaa" + + doAssert isAlphaAscii('r') + doAssert isAlphaAscii('A') + doAssert(not isAlphaAscii('$')) + + doAssert isAlphaAscii("Rasp") + doAssert isAlphaAscii("Args") + doAssert(not isAlphaAscii("$Tomato")) + + doAssert isAlphaNumeric('3') + doAssert isAlphaNumeric('R') + doAssert(not isAlphaNumeric('!')) + + doAssert isAlphaNumeric("34ABc") + doAssert isAlphaNumeric("Rad") + doAssert isAlphaNumeric("1234") + doAssert(not isAlphaNumeric("@nose")) + + doAssert isDigit('3') + doAssert(not isDigit('a')) + doAssert(not isDigit('%')) + + doAssert isDigit("12533") + doAssert(not isDigit("12.33")) + doAssert(not isDigit("A45b")) + + doAssert isSpaceAscii('\t') + doAssert isSpaceAscii('\l') + doAssert(not isSpaceAscii('A')) + + doAssert isSpaceAscii("\t\l \v\r\f") + doAssert isSpaceAscii(" ") + doAssert(not isSpaceAscii("ABc \td")) + + doAssert(isNilOrWhitespace("")) + doAssert(isNilOrWhitespace(" ")) + doAssert(isNilOrWhitespace("\t\l \v\r\f")) + doAssert(not isNilOrWhitespace("ABc \td")) + + doAssert isLowerAscii('a') + doAssert isLowerAscii('z') + doAssert(not isLowerAscii('A')) + doAssert(not isLowerAscii('5')) + doAssert(not isLowerAscii('&')) + + doAssert isLowerAscii("abcd") + doAssert(not isLowerAscii("abCD")) + doAssert(not isLowerAscii("33aa")) + + doAssert isUpperAscii('A') + doAssert(not isUpperAscii('b')) + doAssert(not isUpperAscii('5')) + doAssert(not isUpperAscii('%')) + + doAssert isUpperAscii("ABC") + doAssert(not isUpperAscii("AAcc")) + doAssert(not isUpperAscii("A#$")) + + doAssert rsplit("foo bar", seps=Whitespace) == @["foo", "bar"] + doAssert rsplit(" foo bar", seps=Whitespace, maxsplit=1) == @[" foo", "bar"] + doAssert rsplit(" foo bar ", seps=Whitespace, maxsplit=1) == @[" foo bar", ""] + doAssert rsplit(":foo:bar", sep=':') == @["", "foo", "bar"] + doAssert rsplit(":foo:bar", sep=':', maxsplit=2) == @["", "foo", "bar"] + doAssert rsplit(":foo:bar", sep=':', maxsplit=3) == @["", "foo", "bar"] + doAssert rsplit("foothebar", sep="the") == @["foo", "bar"] + + doAssert(unescape(r"\x013", "", "") == "\x013") + + doAssert join(["foo", "bar", "baz"]) == "foobarbaz" + doAssert join(@["foo", "bar", "baz"], ", ") == "foo, bar, baz" + doAssert join([1, 2, 3]) == "123" + doAssert join(@[1, 2, 3], ", ") == "1, 2, 3" + + doAssert """~~!!foo ~~!!bar ~~!!baz""".unindent(2, "~~!!") == "foo\nbar\nbaz" - doAssert """~~!!foo + doAssert """~~!!foo ~~!!bar ~~!!baz""".unindent(2, "~~!!aa") == "~~!!foo\n~~!!bar\n~~!!baz" - doAssert """~~foo + doAssert """~~foo ~~ bar ~~ baz""".unindent(4, "~") == "foo\n bar\n baz" - doAssert """foo + doAssert """foo bar baz """.unindent(4) == "foo\nbar\nbaz\n" - doAssert """foo + doAssert """foo bar baz """.unindent(2) == "foo\n bar\n baz\n" - doAssert """foo + doAssert """foo bar baz """.unindent(100) == "foo\nbar\nbaz\n" - doAssert """foo + doAssert """foo foo bar """.unindent() == "foo\nfoo\nbar\n" - let s = " this is an example " - let s2 = ":this;is;an:example;;" - - doAssert s.split() == @["", "this", "is", "an", "example", "", ""] - doAssert s2.split(seps={':', ';'}) == @["", "this", "is", "an", "example", "", ""] - doAssert s.split(maxsplit=4) == @["", "this", "is", "an", "example "] - doAssert s.split(' ', maxsplit=1) == @["", "this is an example "] - doAssert s.split(" ", maxsplit=4) == @["", "this", "is", "an", "example "] - - doAssert s.splitWhitespace() == @["this", "is", "an", "example"] - doAssert s.splitWhitespace(maxsplit=1) == @["this", "is an example "] - doAssert s.splitWhitespace(maxsplit=2) == @["this", "is", "an example "] - doAssert s.splitWhitespace(maxsplit=3) == @["this", "is", "an", "example "] - doAssert s.splitWhitespace(maxsplit=4) == @["this", "is", "an", "example"] - - block: # formatEng tests - doAssert formatEng(0, 2, trim=false) == "0.00" - doAssert formatEng(0, 2) == "0" - doAssert formatEng(53, 2, trim=false) == "53.00" - doAssert formatEng(0.053, 2, trim=false) == "53.00e-3" - doAssert formatEng(0.053, 4, trim=false) == "53.0000e-3" - doAssert formatEng(0.053, 4, trim=true) == "53e-3" - doAssert formatEng(0.053, 0) == "53e-3" - doAssert formatEng(52731234) == "52.731234e6" - doAssert formatEng(-52731234) == "-52.731234e6" - doAssert formatEng(52731234, 1) == "52.7e6" - doAssert formatEng(-52731234, 1) == "-52.7e6" - doAssert formatEng(52731234, 1, decimalSep=',') == "52,7e6" - doAssert formatEng(-52731234, 1, decimalSep=',') == "-52,7e6" - - doAssert formatEng(4100, siPrefix=true, unit="V") == "4.1 kV" - doAssert formatEng(4.1, siPrefix=true, unit="V", useUnitSpace=true) == "4.1 V" - doAssert formatEng(4.1, siPrefix=true) == "4.1" # Note lack of space - doAssert formatEng(4100, siPrefix=true) == "4.1 k" - doAssert formatEng(4.1, siPrefix=true, unit="", useUnitSpace=true) == "4.1 " # Includes space - doAssert formatEng(4100, siPrefix=true, unit="") == "4.1 k" - doAssert formatEng(4100) == "4.1e3" - doAssert formatEng(4100, unit="V", useUnitSpace=true) == "4.1e3 V" - doAssert formatEng(4100, unit="", useUnitSpace=true) == "4.1e3 " - # Don't use SI prefix as number is too big - doAssert formatEng(3.1e22, siPrefix=true, unit="a", useUnitSpace=true) == "31e21 a" - # Don't use SI prefix as number is too small - doAssert formatEng(3.1e-25, siPrefix=true, unit="A", useUnitSpace=true) == "310e-27 A" - - block: # startsWith / endsWith char tests - var s = "abcdef" - doAssert s.startsWith('a') - doAssert s.startsWith('b') == false - doAssert s.endsWith('f') - doAssert s.endsWith('a') == false - doAssert s.endsWith('\0') == false - - #echo("strutils tests passed") + let s = " this is an example " + let s2 = ":this;is;an:example;;" + + doAssert s.split() == @["", "this", "is", "an", "example", "", ""] + doAssert s2.split(seps={':', ';'}) == @["", "this", "is", "an", "example", "", ""] + doAssert s.split(maxsplit=4) == @["", "this", "is", "an", "example "] + doAssert s.split(' ', maxsplit=1) == @["", "this is an example "] + doAssert s.split(" ", maxsplit=4) == @["", "this", "is", "an", "example "] + + doAssert s.splitWhitespace() == @["this", "is", "an", "example"] + doAssert s.splitWhitespace(maxsplit=1) == @["this", "is an example "] + doAssert s.splitWhitespace(maxsplit=2) == @["this", "is", "an example "] + doAssert s.splitWhitespace(maxsplit=3) == @["this", "is", "an", "example "] + doAssert s.splitWhitespace(maxsplit=4) == @["this", "is", "an", "example"] + + block: # startsWith / endsWith char tests + var s = "abcdef" + doAssert s.startsWith('a') + doAssert s.startsWith('b') == false + doAssert s.endsWith('f') + doAssert s.endsWith('a') == false + doAssert s.endsWith('\0') == false + + #echo("strutils tests passed") + + nonStaticTests() + staticTests() + static: staticTests() + diff --git a/lib/pure/subexes.nim b/lib/pure/subexes.nim index 9d807abd4..8149c72cc 100644 --- a/lib/pure/subexes.nim +++ b/lib/pure/subexes.nim @@ -31,8 +31,6 @@ type SubexError* = object of ValueError ## exception that is raised for ## an invalid subex -{.deprecated: [EInvalidSubex: SubexError].} - proc raiseInvalidFormat(msg: string) {.noinline.} = raise newException(SubexError, "invalid format string: " & msg) @@ -44,7 +42,6 @@ type else: f: cstring num, i, lineLen: int -{.deprecated: [TFormatParser: FormatParser].} template call(x: untyped): untyped = p.i = i diff --git a/lib/pure/terminal.nim b/lib/pure/terminal.nim index 4f2f73ba7..fcca4d5d7 100644 --- a/lib/pure/terminal.nim +++ b/lib/pure/terminal.nim @@ -29,9 +29,7 @@ when not hasThreadSupport: var colorsFGCache = initTable[Color, string]() colorsBGCache = initTable[Color, string]() - when not defined(windows): - var - styleCache = initTable[int, string]() + styleCache = initTable[int, string]() var trueColorIsSupported: bool @@ -41,10 +39,8 @@ var const fgPrefix = "\x1b[38;2;" bgPrefix = "\x1b[48;2;" - -when not defined(windows): - const - stylePrefix = "\e[" + ansiResetCode* = "\e[0m" + stylePrefix = "\e[" when defined(windows): import winlean, os @@ -468,7 +464,7 @@ proc resetAttributes*(f: File) = else: discard setConsoleTextAttribute(hStdout, oldStdoutAttr) else: - f.write("\e[0m") + f.write(ansiResetCode) type Style* = enum ## different styles for text output @@ -487,15 +483,22 @@ when not defined(windows): gFG {.threadvar.}: int gBG {.threadvar.}: int - proc getStyleStr(style: int): string = - when hasThreadSupport: - result = fmt"{stylePrefix}{style}m" +proc ansiStyleCode*(style: int): string = + when hasThreadSupport: + result = fmt"{stylePrefix}{style}m" + else: + if styleCache.hasKey(style): + result = styleCache[style] else: - if styleCache.hasKey(style): - result = styleCache[style] - else: - result = fmt"{stylePrefix}{style}m" - styleCache[style] = result + result = fmt"{stylePrefix}{style}m" + styleCache[style] = result + +template ansiStyleCode*(style: Style): string = + ansiStyleCode(style.int) + +# The styleCache can be skipped when `style` is known at compile-time +template ansiStyleCode*(style: static[Style]): string = + (static(stylePrefix & $style.int & "m")) proc setStyle*(f: File, style: set[Style]) = ## Sets the terminal style. @@ -510,7 +513,7 @@ proc setStyle*(f: File, style: set[Style]) = discard setConsoleTextAttribute(h, old or a) else: for s in items(style): - f.write(getStyleStr(ord(s))) + f.write(ansiStyleCode(s)) proc writeStyled*(txt: string, style: set[Style] = {styleBright}) = ## Writes the text `txt` in a given `style` to stdout. @@ -524,9 +527,9 @@ proc writeStyled*(txt: string, style: set[Style] = {styleBright}) = stdout.write(txt) stdout.resetAttributes() if gFG != 0: - stdout.write(getStyleStr(gFG)) + stdout.write(ansiStyleCode(gFG)) if gBG != 0: - stdout.write(getStyleStr(gBG)) + stdout.write(ansiStyleCode(gBG)) type ForegroundColor* = enum ## terminal's foreground colors @@ -557,8 +560,8 @@ proc setForegroundColor*(f: File, fg: ForegroundColor, bright=false) = when defined(windows): let h = conHandle(f) var old = getAttributes(h) and not FOREGROUND_RGB - if bright: - old = old or FOREGROUND_INTENSITY + old = if bright: old or FOREGROUND_INTENSITY + else: old and not(FOREGROUND_INTENSITY) const lookup: array[ForegroundColor, int] = [ 0, (FOREGROUND_RED), @@ -572,15 +575,15 @@ proc setForegroundColor*(f: File, fg: ForegroundColor, bright=false) = else: gFG = ord(fg) if bright: inc(gFG, 60) - f.write(getStyleStr(gFG)) + f.write(ansiStyleCode(gFG)) proc setBackgroundColor*(f: File, bg: BackgroundColor, bright=false) = ## Sets the terminal's background color. when defined(windows): let h = conHandle(f) var old = getAttributes(h) and not BACKGROUND_RGB - if bright: - old = old or BACKGROUND_INTENSITY + old = if bright: old or BACKGROUND_INTENSITY + else: old and not(BACKGROUND_INTENSITY) const lookup: array[BackgroundColor, int] = [ 0, (BACKGROUND_RED), @@ -594,10 +597,18 @@ proc setBackgroundColor*(f: File, bg: BackgroundColor, bright=false) = else: gBG = ord(bg) if bright: inc(gBG, 60) - f.write(getStyleStr(gBG)) + f.write(ansiStyleCode(gBG)) + +proc ansiForegroundColorCode*(fg: ForegroundColor, bright=false): string = + var style = ord(fg) + if bright: inc(style, 60) + return ansiStyleCode(style) +template ansiForegroundColorCode*(fg: static[ForegroundColor], + bright: static[bool] = false): string = + ansiStyleCode(fg.int + bright.int * 60) -proc getFGColorStr(color: Color): string = +proc ansiForegroundColorCode*(color: Color): string = when hasThreadSupport: let rgb = extractRGB(color) result = fmt"{fgPrefix}{rgb.r};{rgb.g};{rgb.b}m" @@ -609,7 +620,11 @@ proc getFGColorStr(color: Color): string = result = fmt"{fgPrefix}{rgb.r};{rgb.g};{rgb.b}m" colorsFGCache[color] = result -proc getBGColorStr(color: Color): string = +template ansiForegroundColorCode*(color: static[Color]): string = + const rgb = extractRGB(color) + (static(fmt"{fgPrefix}{rgb.r};{rgb.g};{rgb.b}m")) + +proc ansiBackgroundColorCode*(color: Color): string = when hasThreadSupport: let rgb = extractRGB(color) result = fmt"{bgPrefix}{rgb.r};{rgb.g};{rgb.b}m" @@ -621,15 +636,19 @@ proc getBGColorStr(color: Color): string = result = fmt"{bgPrefix}{rgb.r};{rgb.g};{rgb.b}m" colorsFGCache[color] = result +template ansiBackgroundColorCode*(color: static[Color]): string = + const rgb = extractRGB(color) + (static(fmt"{bgPrefix}{rgb.r};{rgb.g};{rgb.b}m")) + proc setForegroundColor*(f: File, color: Color) = ## Sets the terminal's foreground true color. if trueColorIsEnabled: - f.write(getFGColorStr(color)) + f.write(ansiForegroundColorCode(color)) proc setBackgroundColor*(f: File, color: Color) = ## Sets the terminal's background true color. if trueColorIsEnabled: - f.write(getBGColorStr(color)) + f.write(ansiBackgroundColorCode(color)) proc setTrueColor(f: File, color: Color) = if fgSetColor: @@ -759,6 +778,10 @@ when defined(windows): x = runeLenAt(password.string, i) inc i, x password.string.setLen(max(password.len - x, 0)) + of chr(0x0): + # modifier key - ignore - for details see + # https://github.com/nim-lang/Nim/issues/7764 + continue else: password.string.add(toUTF8(c.Rune)) stdout.write "\n" diff --git a/lib/pure/times.nim b/lib/pure/times.nim index dc216477b..f6567cf58 100644 --- a/lib/pure/times.nim +++ b/lib/pure/times.nim @@ -103,7 +103,7 @@ type Time* = object ## Represents a point in time. seconds: int64 - nanoseconds: NanosecondRange + nanosecond: NanosecondRange DateTime* = object of RootObj ## Represents a time in different parts. ## Although this type can represent leap @@ -159,7 +159,7 @@ type ## This type should be prefered over ``TimeInterval`` unless ## non-static time units is needed. seconds: int64 - nanoseconds: NanosecondRange + nanosecond: NanosecondRange TimeUnit* = enum ## Different units of time. Nanoseconds, Microseconds, Milliseconds, Seconds, Minutes, Hours, Days, Weeks, Months, Years @@ -191,6 +191,7 @@ const secondsInHour = 60*60 secondsInDay = 60*60*24 minutesInHour = 60 + rateDiff = 10000000'i64 # 100 nsecs # The number of hectonanoseconds between 1601/01/01 (windows epoch) # and 1970/01/01 (unix epoch). epochDiff = 116444736000000000'i64 @@ -222,21 +223,21 @@ proc normalize[T: Duration|Time](seconds, nanoseconds: int64): T = ## a ``Duration`` or ``Time``. A normalized ``Duration|Time`` has a ## positive nanosecond part in the range ``NanosecondRange``. result.seconds = seconds + convert(Nanoseconds, Seconds, nanoseconds) - var nanoseconds = nanoseconds mod convert(Seconds, Nanoseconds, 1) - if nanoseconds < 0: - nanoseconds += convert(Seconds, Nanoseconds, 1) + var nanosecond = nanoseconds mod convert(Seconds, Nanoseconds, 1) + if nanosecond < 0: + nanosecond += convert(Seconds, Nanoseconds, 1) result.seconds -= 1 - result.nanoseconds = nanoseconds.int + result.nanosecond = nanosecond.int -proc initTime*(unix: int64, nanoseconds: NanosecondRange): Time = +proc initTime*(unix: int64, nanosecond: NanosecondRange): Time = ## Create a ``Time`` from a unix timestamp and a nanosecond part. result.seconds = unix - result.nanoseconds = nanoseconds + result.nanosecond = nanosecond -proc nanoseconds*(time: Time): NanosecondRange = +proc nanosecond*(time: Time): NanosecondRange = ## Get the fractional part of a ``Time`` as the number ## of nanoseconds of the second. - time.nanoseconds + time.nanosecond proc initDuration*(nanoseconds, microseconds, milliseconds, seconds, minutes, hours, days, weeks: int64 = 0): Duration = @@ -280,7 +281,7 @@ proc milliseconds*(dur: Duration): int {.inline.} = runnableExamples: let dur = initDuration(seconds = 1, milliseconds = 1) doAssert dur.milliseconds == 1 - convert(Nanoseconds, Milliseconds, dur.nanoseconds) + convert(Nanoseconds, Milliseconds, dur.nanosecond) proc microseconds*(dur: Duration): int {.inline.} = ## Number of whole microseconds represented by the **fractional** @@ -288,7 +289,7 @@ proc microseconds*(dur: Duration): int {.inline.} = runnableExamples: let dur = initDuration(seconds = 1, microseconds = 1) doAssert dur.microseconds == 1 - convert(Nanoseconds, Microseconds, dur.nanoseconds) + convert(Nanoseconds, Microseconds, dur.nanosecond) proc nanoseconds*(dur: Duration): int {.inline.} = ## Number of whole nanoseconds represented by the **fractional** @@ -296,14 +297,14 @@ proc nanoseconds*(dur: Duration): int {.inline.} = runnableExamples: let dur = initDuration(seconds = 1, nanoseconds = 1) doAssert dur.nanoseconds == 1 - dur.nanoseconds + dur.nanosecond proc fractional*(dur: Duration): Duration {.inline.} = ## The fractional part of duration, as a duration. runnableExamples: let dur = initDuration(seconds = 1, nanoseconds = 5) doAssert dur.fractional == initDuration(nanoseconds = 5) - initDuration(nanoseconds = dur.nanoseconds) + initDuration(nanoseconds = dur.nanosecond) const DurationZero* = initDuration() ## Zero value for durations. Useful for comparisons. ## @@ -321,7 +322,7 @@ proc `$`*(dur: Duration): string = doAssert $initDuration(milliseconds = -1500) == "-1 second and -500 milliseconds" var parts = newSeq[string]() var remS = dur.seconds - var remNs = dur.nanoseconds.int + var remNs = dur.nanosecond.int # Normally ``nanoseconds`` should always be positive, but # that makes no sense when printing. @@ -352,7 +353,9 @@ proc `$`*(dur: Duration): string = parts.add $quantity & " " & unitStrings[unit] & "s" result = "" - if parts.len == 1: + if parts.len == 0: + result.add "0 nanoseconds" + elif parts.len == 1: result = parts[0] elif parts.len == 2: result = parts[0] & " and " & parts[1] @@ -371,6 +374,21 @@ proc toUnix*(t: Time): int64 {.benign, tags: [], raises: [], noSideEffect.} = ## Convert ``t`` to a unix timestamp (seconds since ``1970-01-01T00:00:00Z``). t.seconds +proc fromWinTime*(win: int64): Time = + ## Convert a Windows file time (100-nanosecond intervals since ``1601-01-01T00:00:00Z``) + ## to a ``Time``. + let hnsecsSinceEpoch = (win - epochDiff) + var seconds = hnsecsSinceEpoch div rateDiff + var nanos = ((hnsecsSinceEpoch mod rateDiff) * 100).int + if nanos < 0: + nanos += convert(Seconds, Nanoseconds, 1) + seconds -= 1 + result = initTime(seconds, nanos) + +proc toWinTime*(t: Time): int64 = + ## Convert ``t`` to a Windows file time (100-nanosecond intervals since ``1601-01-01T00:00:00Z``). + result = t.seconds * rateDiff + epochDiff + t.nanosecond div 100 + proc isLeapYear*(year: int): bool = ## Returns true if ``year`` is a leap year. year mod 4 == 0 and (year mod 100 != 0 or year mod 400 == 0) @@ -455,21 +473,21 @@ proc localZoneInfoFromTz(adjTime: Time): ZonedTime {.tags: [], raises: [], benig {. pragma: operator, rtl, noSideEffect, benign .} template subImpl[T: Duration|Time](a: Duration|Time, b: Duration|Time): T = - normalize[T](a.seconds - b.seconds, a.nanoseconds - b.nanoseconds) + normalize[T](a.seconds - b.seconds, a.nanosecond - b.nanosecond) template addImpl[T: Duration|Time](a: Duration|Time, b: Duration|Time): T = - normalize[T](a.seconds + b.seconds, a.nanoseconds + b.nanoseconds) + normalize[T](a.seconds + b.seconds, a.nanosecond + b.nanosecond) template ltImpl(a: Duration|Time, b: Duration|Time): bool = a.seconds < b.seconds or ( - a.seconds == b.seconds and a.nanoseconds < b.nanoseconds) + a.seconds == b.seconds and a.nanosecond < b.nanosecond) template lqImpl(a: Duration|Time, b: Duration|Time): bool = - a.seconds <= b.seconds or ( - a.seconds == b.seconds and a.nanoseconds <= b.seconds) + a.seconds < b.seconds or ( + a.seconds == b.seconds and a.nanosecond <= b.nanosecond) template eqImpl(a: Duration|Time, b: Duration|Time): bool = - a.seconds == b.seconds and a.nanoseconds == b.nanoseconds + a.seconds == b.seconds and a.nanosecond == b.nanosecond proc `+`*(a, b: Duration): Duration {.operator.} = ## Add two durations together. @@ -489,7 +507,7 @@ proc `-`*(a: Duration): Duration {.operator.} = ## Reverse a duration. runnableExamples: doAssert -initDuration(seconds = 1) == initDuration(seconds = -1) - normalize[Duration](-a.seconds, -a.nanoseconds) + normalize[Duration](-a.seconds, -a.nanosecond) proc `<`*(a, b: Duration): bool {.operator.} = ## Note that a duration can be negative, @@ -512,7 +530,7 @@ proc `*`*(a: int64, b: Duration): Duration {.operator} = ## Multiply a duration by some scalar. runnableExamples: doAssert 5 * initDuration(seconds = 1) == initDuration(seconds = 5) - normalize[Duration](a * b.seconds, a * b.nanoseconds) + normalize[Duration](a * b.seconds, a * b.nanosecond) proc `*`*(a: Duration, b: int64): Duration {.operator} = ## Multiply a duration by some scalar. @@ -526,7 +544,7 @@ proc `div`*(a: Duration, b: int64): Duration {.operator} = doAssert initDuration(seconds = 3) div 2 == initDuration(milliseconds = 1500) doAssert initDuration(nanoseconds = 3) div 2 == initDuration(nanoseconds = 1) let carryOver = convert(Seconds, Nanoseconds, a.seconds mod b) - normalize[Duration](a.seconds div b, (a.nanoseconds + carryOver) div b) + normalize[Duration](a.seconds div b, (a.nanosecond + carryOver) div b) proc `-`*(a, b: Time): Duration {.operator, extern: "ntDiffTime".} = ## Computes the duration between two points in time. @@ -582,7 +600,7 @@ proc abs*(a: Duration): Duration = runnableExamples: doAssert initDuration(milliseconds = -1500).abs == initDuration(milliseconds = 1500) - initDuration(seconds = abs(a.seconds), nanoseconds = -a.nanoseconds) + initDuration(seconds = abs(a.seconds), nanoseconds = -a.nanosecond) proc toTime*(dt: DateTime): Time {.tags: [], raises: [], benign.} = ## Converts a broken-down time structure to @@ -632,7 +650,7 @@ proc initDateTime(zt: ZonedTime, zone: Timezone): DateTime = hour: hour, minute: minute, second: second, - nanosecond: zt.adjTime.nanoseconds, + nanosecond: zt.adjTime.nanosecond, weekday: getDayOfWeek(d, m, y), yearday: getDayOfYear(d, m, y), isDst: zt.isDst, @@ -791,7 +809,7 @@ 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.nanoseconds) + result.adjTime = initTime(utcUnix - finalOffset, adjTime.nanosecond) result.utcOffset = finalOffset result.isDst = dst @@ -853,10 +871,7 @@ proc getTime*(): Time {.tags: [TimeEffect], benign.} = elif defined(windows): var f: FILETIME getSystemTimeAsFileTime(f) - let nanosSinceEpoch = (rdFileTime(f) - epochDiff) * 100 - let seconds = convert(Nanoseconds, Seconds, nanosSinceEpoch) - let nanos = (nanosSinceEpoch mod convert(Seconds, Nanoseconds, 1)).int - result = initTime(seconds, nanos) + result = fromWinTime(rdFileTime(f)) proc now*(): DateTime {.tags: [TimeEffect], benign.} = ## Get the current time as a ``DateTime`` in the local timezone. @@ -1158,12 +1173,16 @@ proc formatToken(dt: DateTime, token: string, buf: var string) = of "dddd": buf.add($dt.weekday) of "h": - buf.add($(if dt.hour > 12: dt.hour - 12 else: dt.hour)) + if dt.hour == 0: buf.add("12") + else: buf.add($(if dt.hour > 12: dt.hour - 12 else: dt.hour)) of "hh": - let amerHour = if dt.hour > 12: dt.hour - 12 else: dt.hour - if amerHour < 10: - buf.add('0') - buf.add($amerHour) + if dt.hour == 0: + buf.add("12") + else: + let amerHour = if dt.hour > 12: dt.hour - 12 else: dt.hour + if amerHour < 10: + buf.add('0') + buf.add($amerHour) of "H": buf.add($dt.hour) of "HH": @@ -1303,24 +1322,23 @@ proc format*(dt: DateTime, f: string): string {.tags: [].}= result = "" var i = 0 var currentF = "" - while true: + while i < f.len: case f[i] - of ' ', '-', '/', ':', '\'', '\0', '(', ')', '[', ']', ',': + of ' ', '-', '/', ':', '\'', '(', ')', '[', ']', ',': formatToken(dt, currentF, result) currentF = "" - if f[i] == '\0': break if f[i] == '\'': inc(i) # Skip ' - while f[i] != '\'' and f.len-1 > i: + while i < f.len-1 and f[i] != '\'': result.add(f[i]) inc(i) else: result.add(f[i]) else: # Check if the letter being added matches previous accumulated buffer. - if currentF.len < 1 or currentF[high(currentF)] == f[i]: + if currentF.len == 0 or currentF[high(currentF)] == f[i]: currentF.add(f[i]) else: formatToken(dt, currentF, result) @@ -1328,6 +1346,7 @@ proc format*(dt: DateTime, f: string): string {.tags: [].}= currentF = "" inc(i) + formatToken(dt, currentF, result) proc `$`*(dt: DateTime): string {.tags: [], raises: [], benign.} = ## Converts a `DateTime` object to a string representation. @@ -1424,58 +1443,58 @@ proc parseToken(dt: var DateTime; token, value: string; j: var int) = dt.month = month.Month of "MMM": case value[j..j+2].toLowerAscii(): - of "jan": dt.month = mJan - of "feb": dt.month = mFeb - of "mar": dt.month = mMar - of "apr": dt.month = mApr - of "may": dt.month = mMay - of "jun": dt.month = mJun - of "jul": dt.month = mJul - of "aug": dt.month = mAug - of "sep": dt.month = mSep - of "oct": dt.month = mOct - of "nov": dt.month = mNov - of "dec": dt.month = mDec + of "jan": dt.month = mJan + of "feb": dt.month = mFeb + of "mar": dt.month = mMar + of "apr": dt.month = mApr + of "may": dt.month = mMay + of "jun": dt.month = mJun + of "jul": dt.month = mJul + of "aug": dt.month = mAug + of "sep": dt.month = mSep + of "oct": dt.month = mOct + of "nov": dt.month = mNov + of "dec": dt.month = mDec else: raise newException(ValueError, "Couldn't parse month (MMM), got: " & value) j += 3 of "MMMM": if value.len >= j+7 and value[j..j+6].cmpIgnoreCase("january") == 0: - dt.month = mJan + dt.month = mJan j += 7 elif value.len >= j+8 and value[j..j+7].cmpIgnoreCase("february") == 0: - dt.month = mFeb + dt.month = mFeb j += 8 elif value.len >= j+5 and value[j..j+4].cmpIgnoreCase("march") == 0: - dt.month = mMar + dt.month = mMar j += 5 elif value.len >= j+5 and value[j..j+4].cmpIgnoreCase("april") == 0: - dt.month = mApr + dt.month = mApr j += 5 elif value.len >= j+3 and value[j..j+2].cmpIgnoreCase("may") == 0: - dt.month = mMay + dt.month = mMay j += 3 elif value.len >= j+4 and value[j..j+3].cmpIgnoreCase("june") == 0: - dt.month = mJun + dt.month = mJun j += 4 elif value.len >= j+4 and value[j..j+3].cmpIgnoreCase("july") == 0: - dt.month = mJul + dt.month = mJul j += 4 elif value.len >= j+6 and value[j..j+5].cmpIgnoreCase("august") == 0: - dt.month = mAug + dt.month = mAug j += 6 elif value.len >= j+9 and value[j..j+8].cmpIgnoreCase("september") == 0: - dt.month = mSep + dt.month = mSep j += 9 elif value.len >= j+7 and value[j..j+6].cmpIgnoreCase("october") == 0: - dt.month = mOct + dt.month = mOct j += 7 elif value.len >= j+8 and value[j..j+7].cmpIgnoreCase("november") == 0: - dt.month = mNov + dt.month = mNov j += 8 elif value.len >= j+8 and value[j..j+7].cmpIgnoreCase("december") == 0: - dt.month = mDec + dt.month = mDec j += 8 else: raise newException(ValueError, @@ -1488,11 +1507,15 @@ proc parseToken(dt: var DateTime; token, value: string; j: var int) = dt.second = value[j..j+1].parseInt() j += 2 of "t": - if value[j] == 'P' and dt.hour > 0 and dt.hour < 12: + if value[j] == 'A' and dt.hour == 12: + dt.hour = 0 + elif value[j] == 'P' and dt.hour > 0 and dt.hour < 12: dt.hour += 12 j += 1 of "tt": - if value[j..j+1] == "PM" and dt.hour > 0 and dt.hour < 12: + if value[j..j+1] == "AM" and dt.hour == 12: + dt.hour = 0 + elif value[j..j+1] == "PM" and dt.hour > 0 and dt.hour < 12: dt.hour += 12 j += 2 of "yy": @@ -1506,44 +1529,47 @@ proc parseToken(dt: var DateTime; token, value: string; j: var int) = j += 4 of "z": dt.isDst = false - if value[j] == '+': + let ch = if j < value.len: value[j] else: '\0' + if ch == '+': dt.utcOffset = 0 - parseInt($value[j+1]) * secondsInHour - elif value[j] == '-': + elif ch == '-': dt.utcOffset = parseInt($value[j+1]) * secondsInHour - elif value[j] == 'Z': + elif ch == 'Z': dt.utcOffset = 0 j += 1 return else: raise newException(ValueError, - "Couldn't parse timezone offset (z), got: " & value[j]) + "Couldn't parse timezone offset (z), got: " & ch) j += 2 of "zz": dt.isDst = false - if value[j] == '+': + let ch = if j < value.len: value[j] else: '\0' + if ch == '+': dt.utcOffset = 0 - value[j+1..j+2].parseInt() * secondsInHour - elif value[j] == '-': + elif ch == '-': dt.utcOffset = value[j+1..j+2].parseInt() * secondsInHour - elif value[j] == 'Z': + elif ch == 'Z': dt.utcOffset = 0 j += 1 return else: raise newException(ValueError, - "Couldn't parse timezone offset (zz), got: " & value[j]) + "Couldn't parse timezone offset (zz), got: " & ch) j += 3 of "zzz": dt.isDst = false var factor = 0 - if value[j] == '+': factor = -1 - elif value[j] == '-': factor = 1 - elif value[j] == 'Z': + let ch = if j < value.len: value[j] else: '\0' + if ch == '+': factor = -1 + elif ch == '-': factor = 1 + elif ch == 'Z': dt.utcOffset = 0 j += 1 return else: raise newException(ValueError, - "Couldn't parse timezone offset (zzz), got: " & value[j]) + "Couldn't parse timezone offset (zzz), got: " & ch) dt.utcOffset = factor * value[j+1..j+2].parseInt() * secondsInHour j += 4 dt.utcOffset += factor * value[j..j+1].parseInt() * 60 @@ -1605,20 +1631,18 @@ proc parse*(value, layout: string, zone: Timezone = local()): DateTime = dt.nanosecond = 0 dt.isDst = true # using this is flag for checking whether a timezone has \ # been read (because DST is always false when a tz is parsed) - while true: + while i < layout.len: case layout[i] - of ' ', '-', '/', ':', '\'', '\0', '(', ')', '[', ']', ',': + of ' ', '-', '/', ':', '\'', '(', ')', '[', ']', ',': if token.len > 0: parseToken(dt, token, value, j) # Reset token token = "" - # Break if at end of line - if layout[i] == '\0': break # Skip separator and everything between single quotes # These are literals in both the layout and the value string if layout[i] == '\'': inc(i) - while layout[i] != '\'' and layout.len-1 > i: + while i < layout.len-1 and layout[i] != '\'': inc(i) inc(j) inc(i) @@ -1627,13 +1651,15 @@ proc parse*(value, layout: string, zone: Timezone = local()): DateTime = inc(j) else: # Check if the letter being added matches previous accumulated buffer. - if token.len < 1 or token[high(token)] == layout[i]: + if token.len == 0 or token[high(token)] == layout[i]: token.add(layout[i]) inc(i) else: parseToken(dt, token, value, j) token = "" + if i >= layout.len and token.len > 0: + parseToken(dt, token, value, j) if dt.isDst: # No timezone parsed - assume timezone is `zone` result = initDateTime(zone.zoneInfoFromTz(dt.toAdjTime), zone) @@ -1713,17 +1739,6 @@ when not defined(JS): var clocksPerSec {.importc: "CLOCKS_PER_SEC", nodecl.}: int - const - rateDiff = 10000000'i64 # 100 nsecs - - proc unixTimeToWinTime*(time: CTime): int64 = - ## converts a UNIX `Time` (``time_t``) to a Windows file time - result = int64(time) * rateDiff + epochDiff - - proc winTimeToUnixTime*(time: int64): CTime = - ## converts a Windows time to a UNIX `Time` (``time_t``) - result = CTime((time - epochDiff) div rateDiff) - when not defined(useNimRtl): proc cpuTime*(): float {.rtl, extern: "nt$1", tags: [TimeEffect].} = ## gets time spent that the CPU spent to run the current process in @@ -1748,7 +1763,7 @@ when not defined(JS): when defined(posix): var a: Timeval gettimeofday(a) - result = toFloat(a.tv_sec) + toFloat(a.tv_usec)*0.00_0001 + result = toBiggestFloat(a.tv_sec.int64) + toFloat(a.tv_usec)*0.00_0001 elif defined(windows): var f: winlean.FILETIME getSystemTimeAsFileTime(f) @@ -1765,6 +1780,19 @@ when defined(JS): # Deprecated procs +when not defined(JS): + proc unixTimeToWinTime*(time: CTime): int64 {.deprecated: "Use toWinTime instead".} = + ## Converts a UNIX `Time` (``time_t``) to a Windows file time + ## + ## **Deprecated:** use ``toWinTime`` instead. + result = int64(time) * rateDiff + epochDiff + + proc winTimeToUnixTime*(time: int64): CTime {.deprecated: "Use fromWinTime instead".} = + ## Converts a Windows time to a UNIX `Time` (``time_t``) + ## + ## **Deprecated:** use ``fromWinTime`` instead. + result = CTime((time - epochDiff) div rateDiff) + proc initInterval*(seconds, minutes, hours, days, months, years: int = 0): TimeInterval {.deprecated.} = ## **Deprecated since v0.18.0:** use ``initTimeInterval`` instead. @@ -1789,7 +1817,7 @@ 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 - time.seconds.float + time.nanoseconds / convert(Seconds, Nanoseconds, 1) + time.seconds.float + time.nanosecond / convert(Seconds, Nanoseconds, 1) proc getLocalTime*(time: Time): DateTime {.tags: [], raises: [], benign, deprecated.} = ## Converts the calendar time `time` to broken-time representation, @@ -1836,7 +1864,7 @@ when defined(JS): ## version 0.8.10.** Use ``epochTime`` or ``cpuTime`` instead. let dur = getTime() - start result = (convert(Seconds, Milliseconds, dur.seconds) + - convert(Nanoseconds, Milliseconds, dur.nanoseconds)).int + convert(Nanoseconds, Milliseconds, dur.nanosecond)).int else: proc getStartMilsecs*(): int {.deprecated, tags: [TimeEffect], benign.} = when defined(macosx): diff --git a/lib/pure/unicode.nim b/lib/pure/unicode.nim index 257c620f7..bfd01be55 100644 --- a/lib/pure/unicode.nim +++ b/lib/pure/unicode.nim @@ -9,7 +9,7 @@ ## This module provides support to handle the Unicode UTF-8 encoding. -{.deadCodeElim: on.} +{.deadCodeElim: on.} # dce option deprecated include "system/inclrtl" @@ -1826,8 +1826,8 @@ when isMainModule: doAssert(runeSubStr(s, 17, 1) == "€") # echo runeStrAtPos(s, 18) # index error - doAssert(runeSubStr(s, 0) == "Hänsel ««: 10,00€") - doAssert(runeSubStr(s, -18) == "Hänsel ««: 10,00€") + doAssert(runeSubStr(s, 0) == "Hänsel ««: 10,00€") + doAssert(runeSubStr(s, -18) == "Hänsel ««: 10,00€") doAssert(runeSubStr(s, 10) == ": 10,00€") doAssert(runeSubStr(s, 18) == "") doAssert(runeSubStr(s, 0, 10) == "Hänsel ««") @@ -1840,7 +1840,7 @@ when isMainModule: doAssert(runeSubStr(s, -6, 5) == "10,00") doAssert(runeSubStr(s, -6, -1) == "10,00") - doAssert(runeSubStr(s, 0, 100) == "Hänsel ««: 10,00€") - doAssert(runeSubStr(s, -100, 100) == "Hänsel ««: 10,00€") + doAssert(runeSubStr(s, 0, 100) == "Hänsel ««: 10,00€") + doAssert(runeSubStr(s, -100, 100) == "Hänsel ««: 10,00€") doAssert(runeSubStr(s, 0, -100) == "") doAssert(runeSubStr(s, 100, -100) == "") diff --git a/lib/pure/uri.nim b/lib/pure/uri.nim index d2d11253a..dd8040928 100644 --- a/lib/pure/uri.nim +++ b/lib/pure/uri.nim @@ -18,14 +18,12 @@ type hostname*, port*, path*, query*, anchor*: string opaque*: bool -{.deprecated: [TUrl: Url, TUri: Uri].} - {.push warning[deprecated]: off.} -proc `$`*(url: Url): string {.deprecated.} = +proc `$`*(url: Url): string {.deprecated: "use Uri instead".} = ## **Deprecated since 0.9.6**: Use ``Uri`` instead. return string(url) -proc `/`*(a, b: Url): Url {.deprecated.} = +proc `/`*(a, b: Url): Url {.deprecated: "use Uri instead".} = ## Joins two URLs together, separating them with / if needed. ## ## **Deprecated since 0.9.6**: Use ``Uri`` instead. @@ -40,39 +38,50 @@ proc `/`*(a, b: Url): Url {.deprecated.} = urlS.add(bs) result = Url(urlS) -proc add*(url: var Url, a: Url) {.deprecated.} = +proc add*(url: var Url, a: Url) {.deprecated: "use Uri instead".} = ## Appends url to url. ## ## **Deprecated since 0.9.6**: Use ``Uri`` instead. url = url / a {.pop.} -proc encodeUrl*(s: string): string = - ## Encodes a value to be HTTP safe: This means that characters in the set - ## ``{'A'..'Z', 'a'..'z', '0'..'9', '_'}`` are carried over to the result, - ## a space is converted to ``'+'`` and every other character is encoded as - ## ``'%xx'`` where ``xx`` denotes its hexadecimal value. +proc encodeUrl*(s: string, usePlus=true): string = + ## Encodes a URL according to RFC3986. + ## + ## This means that characters in the set + ## ``{'a'..'z', 'A'..'Z', '0'..'9', '-', '.', '_', '~'}`` are + ## carried over to the result. + ## All other characters are encoded as ``''%xx'`` where ``xx`` + ## denotes its hexadecimal value. + ## + ## As a special rule, when the value of ``usePlus`` is true, + ## spaces are encoded as ``'+'`` instead of ``'%20'``. result = newStringOfCap(s.len + s.len shr 2) # assume 12% non-alnum-chars - for i in 0..s.len-1: - case s[i] - of 'a'..'z', 'A'..'Z', '0'..'9', '_': add(result, s[i]) - of ' ': add(result, '+') + let fromSpace = if usePlus: "+" else: "%20" + for c in s: + case c + of 'a'..'z', 'A'..'Z', '0'..'9', '-', '.', '_', '~': add(result, c) + of ' ': add(result, fromSpace) else: add(result, '%') - add(result, toHex(ord(s[i]), 2)) - -proc decodeUrl*(s: string): string = - ## Decodes a value from its HTTP representation: This means that a ``'+'`` - ## is converted to a space, ``'%xx'`` (where ``xx`` denotes a hexadecimal - ## value) is converted to the character with ordinal number ``xx``, and + add(result, toHex(ord(c), 2)) + +proc decodeUrl*(s: string, decodePlus=true): string = + ## Decodes a URL according to RFC3986. + ## + ## This means that any ``'%xx'`` (where ``xx`` denotes a hexadecimal + ## value) are converted to the character with ordinal number ``xx``, ## and every other character is carried over. + ## + ## As a special rule, when the value of ``decodePlus`` is true, ``'+'`` + ## characters are converted to a space. proc handleHexChar(c: char, x: var int) {.inline.} = case c of '0'..'9': x = (x shl 4) or (ord(c) - ord('0')) of 'a'..'f': x = (x shl 4) or (ord(c) - ord('a') + 10) of 'A'..'F': x = (x shl 4) or (ord(c) - ord('A') + 10) else: assert(false) - + result = newString(s.len) var i = 0 var j = 0 @@ -84,7 +93,11 @@ proc decodeUrl*(s: string): string = handleHexChar(s[i+2], x) inc(i, 2) result[j] = chr(x) - of '+': result[j] = ' ' + of '+': + if decodePlus: + result[j] = ' ' + else: + result[j] = s[i] else: result[j] = s[i] inc(i) inc(j) @@ -94,7 +107,7 @@ proc parseAuthority(authority: string, result: var Uri) = var i = 0 var inPort = false var inIPv6 = false - while true: + while i < authority.len: case authority[i] of '@': swap result.password, result.port @@ -111,7 +124,6 @@ proc parseAuthority(authority: string, result: var Uri) = inIPv6 = true of ']': inIPv6 = false - of '\0': break else: if inPort: result.port.add(authority[i]) @@ -128,11 +140,11 @@ proc parsePath(uri: string, i: var int, result: var Uri) = parseAuthority(result.path, result) result.path.setLen(0) - if uri[i] == '?': + if i < uri.len and uri[i] == '?': i.inc # Skip '?' i.inc parseUntil(uri, result.query, {'#'}, i) - if uri[i] == '#': + if i < uri.len and uri[i] == '#': i.inc # Skip '#' i.inc parseUntil(uri, result.anchor, {}, i) @@ -156,7 +168,7 @@ proc parseUri*(uri: string, result: var Uri) = # Check if this is a reference URI (relative URI) let doubleSlash = uri.len > 1 and uri[1] == '/' - if uri[i] == '/': + if i < uri.len and uri[i] == '/': # Make sure ``uri`` doesn't begin with '//'. if not doubleSlash: parsePath(uri, i, result) @@ -164,7 +176,7 @@ proc parseUri*(uri: string, result: var Uri) = # Scheme i.inc parseWhile(uri, result.scheme, Letters + Digits + {'+', '-', '.'}, i) - if uri[i] != ':' and not doubleSlash: + if (i >= uri.len or uri[i] != ':') and not doubleSlash: # Assume this is a reference URI (relative URI) i = 0 result.scheme.setLen(0) @@ -174,7 +186,7 @@ proc parseUri*(uri: string, result: var Uri) = i.inc # Skip ':' # Authority - if uri[i] == '/' and uri[i+1] == '/': + if i+1 < uri.len and uri[i] == '/' and uri[i+1] == '/': i.inc(2) # Skip // var authority = "" i.inc parseUntil(uri, authority, {'/', '?', '#'}, i) @@ -197,13 +209,13 @@ proc removeDotSegments(path: string): string = let endsWithSlash = path[path.len-1] == '/' var i = 0 var currentSegment = "" - while true: + while i < path.len: case path[i] of '/': collection.add(currentSegment) currentSegment = "" of '.': - if path[i+1] == '.' and path[i+2] == '/': + if i+2 < path.len and path[i+1] == '.' and path[i+2] == '/': if collection.len > 0: discard collection.pop() i.inc 3 @@ -212,13 +224,11 @@ proc removeDotSegments(path: string): string = i.inc 2 continue currentSegment.add path[i] - of '\0': - if currentSegment != "": - collection.add currentSegment - break else: currentSegment.add path[i] i.inc + if currentSegment != "": + collection.add currentSegment result = collection.join("/") if endsWithSlash: result.add '/' @@ -320,18 +330,18 @@ proc `/`*(x: Uri, path: string): Uri = result = x if result.path.len == 0: - if path[0] != '/': + if path.len == 0 or path[0] != '/': result.path = "/" result.path.add(path) return - if result.path[result.path.len-1] == '/': - if path[0] == '/': + if result.path.len > 0 and result.path[result.path.len-1] == '/': + if path.len > 0 and path[0] == '/': result.path.add(path[1 .. path.len-1]) else: result.path.add(path) else: - if path[0] != '/': + if path.len == 0 or path[0] != '/': result.path.add '/' result.path.add(path) @@ -373,7 +383,10 @@ when isMainModule: const test1 = "abc\L+def xyz" doAssert encodeUrl(test1) == "abc%0A%2Bdef+xyz" doAssert decodeUrl(encodeUrl(test1)) == test1 - + doAssert encodeUrl(test1, false) == "abc%0A%2Bdef%20xyz" + doAssert decodeUrl(encodeUrl(test1, false), false) == test1 + doAssert decodeUrl(encodeUrl(test1)) == test1 + block: let str = "http://localhost" let test = parseUri(str) @@ -464,7 +477,7 @@ when isMainModule: doAssert test.hostname == "github.com" doAssert test.port == "dom96" doAssert test.path == "/packages" - + block: let str = "file:///foo/bar/baz.txt" let test = parseUri(str) diff --git a/lib/pure/xmldom.nim b/lib/pure/xmldom.nim index 3c891c81b..8cd47aa39 100644 --- a/lib/pure/xmldom.nim +++ b/lib/pure/xmldom.nim @@ -232,10 +232,10 @@ proc createAttributeNS*(doc: PDocument, namespaceURI: string, qualifiedName: str raise newException(EInvalidCharacterErr, "Invalid character") # Exceptions if qualifiedName.contains(':'): - let qfnamespaces = qualifiedName.toLower().split(':') + let qfnamespaces = qualifiedName.toLowerAscii().split(':') if isNil(namespaceURI): raise newException(ENamespaceErr, "When qualifiedName contains a prefix namespaceURI cannot be nil") - elif qfnamespaces[0] == "xml" and + elif qfnamespaces[0] == "xml" and namespaceURI != "http://www.w3.org/XML/1998/namespace" and qfnamespaces[1] notin stdattrnames: raise newException(ENamespaceErr, @@ -311,10 +311,10 @@ proc createElement*(doc: PDocument, tagName: string): PElement = proc createElementNS*(doc: PDocument, namespaceURI: string, qualifiedName: string): PElement = ## Creates an element of the given qualified name and namespace URI. if qualifiedName.contains(':'): - let qfnamespaces = qualifiedName.toLower().split(':') + let qfnamespaces = qualifiedName.toLowerAscii().split(':') if isNil(namespaceURI): raise newException(ENamespaceErr, "When qualifiedName contains a prefix namespaceURI cannot be nil") - elif qfnamespaces[0] == "xml" and + elif qfnamespaces[0] == "xml" and namespaceURI != "http://www.w3.org/XML/1998/namespace" and qfnamespaces[1] notin stdattrnames: raise newException(ENamespaceErr, @@ -533,13 +533,13 @@ proc `prefix=`*(n: PNode, value: string) = if isNil(n.fNamespaceURI): raise newException(ENamespaceErr, "namespaceURI cannot be nil") - elif value.toLower() == "xml" and n.fNamespaceURI != "http://www.w3.org/XML/1998/namespace": + elif value.toLowerAscii() == "xml" and n.fNamespaceURI != "http://www.w3.org/XML/1998/namespace": raise newException(ENamespaceErr, "When the namespace prefix is \"xml\" namespaceURI has to be \"http://www.w3.org/XML/1998/namespace\"") - elif value.toLower() == "xmlns" and n.fNamespaceURI != "http://www.w3.org/2000/xmlns/": + elif value.toLowerAscii() == "xmlns" and n.fNamespaceURI != "http://www.w3.org/2000/xmlns/": raise newException(ENamespaceErr, "When the namespace prefix is \"xmlns\" namespaceURI has to be \"http://www.w3.org/2000/xmlns/\"") - elif value.toLower() == "xmlns" and n.fNodeType == AttributeNode: + elif value.toLowerAscii() == "xmlns" and n.fNodeType == AttributeNode: raise newException(ENamespaceErr, "An AttributeNode cannot have a prefix of \"xmlns\"") n.fNodeName = value & ":" & n.fLocalName @@ -1069,17 +1069,15 @@ proc splitData*(textNode: PText, offset: int): PText = var newNode: PText = textNode.fOwnerDocument.createTextNode(right) return newNode - # ProcessingInstruction proc target*(pi: PProcessingInstruction): string = ## Returns the Processing Instructions target return pi.fTarget - -# --Other stuff-- -# Writer -proc addEscaped(s: string): string = +proc escapeXml*(s: string; result: var string) = + ## Prepares a string for insertion into a XML document + ## by escaping the XML special characters. result = "" for c in items(s): case c @@ -1089,11 +1087,20 @@ proc addEscaped(s: string): string = of '"': result.add(""") else: result.add(c) +proc escapeXml*(s: string): string = + ## Prepares a string for insertion into a XML document + ## by escaping the XML special characters. + result = newStringOfCap(s.len + s.len shr 4) + escapeXml(s, result) + +# --Other stuff-- +# Writer + proc nodeToXml(n: PNode, indent: int = 0): string = result = spaces(indent) & "<" & n.nodeName if not isNil(n.attributes): for i in items(n.attributes): - result.add(" " & i.name & "=\"" & addEscaped(i.value) & "\"") + result.add(" " & i.name & "=\"" & escapeXml(i.value) & "\"") if isNil(n.childNodes) or n.childNodes.len() == 0: result.add("/>") # No idea why this doesn't need a \n :O @@ -1106,7 +1113,7 @@ proc nodeToXml(n: PNode, indent: int = 0): string = result.add(nodeToXml(i, indent + 2)) of TextNode: result.add(spaces(indent * 2)) - result.add(addEscaped(i.nodeValue)) + result.add(escapeXml(i.nodeValue)) of CDataSectionNode: result.add(spaces(indent * 2)) result.add("<![CDATA[" & i.nodeValue & "]]>") diff --git a/lib/pure/xmlparser.nim b/lib/pure/xmlparser.nim index 9159b4bfc..597b80eb5 100644 --- a/lib/pure/xmlparser.nim +++ b/lib/pure/xmlparser.nim @@ -16,8 +16,6 @@ type ## for invalid XML. errors*: seq[string] ## All detected parsing errors. -{.deprecated: [EInvalidXml: XmlError].} - proc raiseInvalidXml(errors: seq[string]) = var e: ref XmlError new(e) diff --git a/lib/pure/xmltree.nim b/lib/pure/xmltree.nim index 45696c80c..8f85cb5c9 100644 --- a/lib/pure/xmltree.nim +++ b/lib/pure/xmltree.nim @@ -12,9 +12,9 @@ import macros, strtabs type - XmlNode* = ref XmlNodeObj ## an XML tree consists of ``PXmlNode``'s. + XmlNode* = ref XmlNodeObj ## an XML tree consists of ``XmlNode``'s. - XmlNodeKind* = enum ## different kinds of ``PXmlNode``'s + XmlNodeKind* = enum ## different kinds of ``XmlNode``'s xnText, ## a text element xnElement, ## an element with 0 or more children xnCData, ## a CDATA node @@ -33,9 +33,6 @@ type fAttr: XmlAttributes fClientData: int ## for other clients -{.deprecated: [PXmlNode: XmlNode, TXmlNodeKind: XmlNodeKind, PXmlAttributes: - XmlAttributes, TXmlNode: XmlNodeObj].} - proc newXmlNode(kind: XmlNodeKind): XmlNode = ## creates a new ``XmlNode``. new(result) @@ -155,11 +152,6 @@ proc `[]`* (n: var XmlNode, i: int): var XmlNode {.inline.} = assert n.k == xnElement result = n.s[i] -proc mget*(n: var XmlNode, i: int): var XmlNode {.inline, deprecated.} = - ## returns the `i`'th child of `n` so that it can be modified. Use ```[]``` - ## instead. - n[i] - iterator items*(n: XmlNode): XmlNode {.inline.} = ## iterates over any child of `n`. assert n.k == xnElement @@ -315,14 +307,12 @@ proc newXmlTree*(tag: string, children: openArray[XmlNode], for i in 0..children.len-1: result.s[i] = children[i] result.fAttr = attributes -proc xmlConstructor(e: NimNode): NimNode {.compileTime.} = - expectLen(e, 2) - var a = e[1] +proc xmlConstructor(a: NimNode): NimNode {.compileTime.} = if a.kind == nnkCall: result = newCall("newXmlTree", toStrLit(a[0])) var attrs = newNimNode(nnkBracket, a) - var newStringTabCall = newCall("newStringTable", attrs, - newIdentNode("modeCaseSensitive")) + var newStringTabCall = newCall(bindSym"newStringTable", attrs, + bindSym"modeCaseSensitive") var elements = newNimNode(nnkBracket, a) for i in 1..a.len-1: if a[i].kind == nnkExprEqExpr: @@ -348,7 +338,6 @@ macro `<>`*(x: untyped): untyped = ## ## <a href="http://nim-lang.org">Nim rules.</a> ## - let x = callsite() result = xmlConstructor(x) proc child*(n: XmlNode, name: string): XmlNode = diff --git a/lib/system.nim b/lib/system.nim index 3761a35f8..5781aff27 100644 --- a/lib/system.nim +++ b/lib/system.nim @@ -146,7 +146,7 @@ proc declared*(x: untyped): bool {.magic: "Defined", noSideEffect, compileTime.} ## # missing it. when defined(useNimRtl): - {.deadCodeElim: on.} + {.deadCodeElim: on.} # dce option deprecated proc definedInScope*(x: untyped): bool {. magic: "DefinedInScope", noSideEffect, deprecated, compileTime.} @@ -1280,15 +1280,13 @@ proc setLen*[T](s: var seq[T], newlen: Natural) {. ## sets the length of `s` to `newlen`. ## ``T`` may be any sequence type. ## If the current length is greater than the new length, - ## ``s`` will be truncated. `s` cannot be nil! To initialize a sequence with - ## a size, use ``newSeq`` instead. + ## ``s`` will be truncated. proc setLen*(s: var string, newlen: Natural) {. magic: "SetLengthStr", noSideEffect.} ## sets the length of `s` to `newlen`. ## If the current length is greater than the new length, - ## ``s`` will be truncated. `s` cannot be nil! To initialize a string with - ## a size, use ``newString`` instead. + ## ``s`` will be truncated. ## ## .. code-block:: Nim ## var myS = "Nim is great!!" @@ -2404,7 +2402,7 @@ proc `==` *[T](x, y: seq[T]): bool {.noSideEffect.} = if x.isNil and y.isNil: return true else: - when not defined(JS) or defined(nimphp): + when not defined(JS): proc seqToPtr[T](x: seq[T]): pointer {.inline, nosideeffect.} = result = cast[pointer](x) else: @@ -2414,8 +2412,9 @@ proc `==` *[T](x, y: seq[T]): bool {.noSideEffect.} = if seqToPtr(x) == seqToPtr(y): return true - if x.isNil or y.isNil: - return false + when not defined(nimNoNil): + if x.isNil or y.isNil: + return false if x.len != y.len: return false @@ -2766,17 +2765,14 @@ type when defined(JS): proc add*(x: var string, y: cstring) {.asmNoStackFrame.} = - when defined(nimphp): - asm """`x` .= `y`;""" - else: - asm """ - var len = `x`[0].length-1; - for (var i = 0; i < `y`.length; ++i) { - `x`[0][len] = `y`.charCodeAt(i); - ++len; - } - `x`[0][len] = 0 - """ + asm """ + var len = `x`[0].length-1; + for (var i = 0; i < `y`.length; ++i) { + `x`[0][len] = `y`.charCodeAt(i); + ++len; + } + `x`[0][len] = 0 + """ proc add*(x: var cstring, y: cstring) {.magic: "AppendStrStr".} elif hasAlloc: @@ -3227,7 +3223,7 @@ when not defined(JS): #and not defined(nimscript): when declared(initGC): initGC() when not defined(nimscript): - proc setControlCHook*(hook: proc () {.noconv.} not nil) + proc setControlCHook*(hook: proc () {.noconv.}) ## allows you to override the behaviour of your application when CTRL+C ## is pressed. Only one such hook is supported. @@ -3936,29 +3932,48 @@ proc addEscapedChar*(s: var string, c: char) {.noSideEffect, inline.} = ## * replaces any ``\`` by ``\\`` ## * replaces any ``'`` by ``\'`` ## * replaces any ``"`` by ``\"`` - ## * replaces any other character in the set ``{'\0'..'\31', '\127'..'\255'}`` + ## * replaces any ``\a`` by ``\\a`` + ## * replaces any ``\b`` by ``\\b`` + ## * replaces any ``\t`` by ``\\t`` + ## * replaces any ``\n`` by ``\\n`` + ## * replaces any ``\v`` by ``\\v`` + ## * replaces any ``\f`` by ``\\f`` + ## * replaces any ``\c`` by ``\\c`` + ## * replaces any ``\e`` by ``\\e`` + ## * replaces any other character not in the set ``{'\21..'\126'} ## by ``\xHH`` where ``HH`` is its hexadecimal value. ## ## The procedure has been designed so that its output is usable for many ## different common syntaxes. ## **Note**: This is not correct for producing Ansi C code! case c - of '\0'..'\31', '\127'..'\255': - add(s, "\\x") + of '\a': s.add "\\a" # \x07 + of '\b': s.add "\\b" # \x08 + of '\t': s.add "\\t" # \x09 + of '\n': s.add "\\n" # \x0A + of '\v': s.add "\\v" # \x0B + of '\f': s.add "\\f" # \x0C + of '\c': s.add "\\c" # \x0D + of '\e': s.add "\\e" # \x1B + of '\\': s.add("\\\\") + of '\'': s.add("\\'") + of '\"': s.add("\\\"") + of {'\32'..'\126'} - {'\\', '\'', '\"'}: s.add(c) + else: + s.add("\\x") const HexChars = "0123456789ABCDEF" let n = ord(c) s.add(HexChars[int((n and 0xF0) shr 4)]) s.add(HexChars[int(n and 0xF)]) - of '\\': add(s, "\\\\") - of '\'': add(s, "\\'") - of '\"': add(s, "\\\"") - else: add(s, c) proc addQuoted*[T](s: var string, x: T) = ## Appends `x` to string `s` in place, applying quoting and escaping ## if `x` is a string or char. See ## `addEscapedChar <system.html#addEscapedChar>`_ - ## for the escaping scheme. + ## for the escaping scheme. When `x` is a string, characters in the + ## range ``{\128..\255}`` are never escaped so that multibyte UTF-8 + ## characters are untouched (note that this behavior is different from + ## ``addEscapedChar``). ## ## The Nim standard library uses this function on the elements of ## collections when producing a string representation of a collection. @@ -3977,7 +3992,12 @@ proc addQuoted*[T](s: var string, x: T) = when T is string: s.add("\"") for c in x: - s.addEscapedChar(c) + # Only ASCII chars are escaped to avoid butchering + # multibyte UTF-8 characters. + if c <= 127.char: + s.addEscapedChar(c) + else: + s.add c s.add("\"") elif T is char: s.add("'") @@ -3991,18 +4011,18 @@ proc addQuoted*[T](s: var string, x: T) = when hasAlloc: # XXX: make these the default (or implement the NilObject optimization) - proc safeAdd*[T](x: var seq[T], y: T) {.noSideEffect.} = + 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) - proc safeAdd*(x: var string, y: char) = + 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) - proc safeAdd*(x: var string, y: string) = + 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 @@ -4139,20 +4159,22 @@ template doAssertRaises*(exception, code: untyped): typed = runnableExamples: doAssertRaises(ValueError): raise newException(ValueError, "Hello World") - + var wrong = false try: - block: - code - raiseAssert(astToStr(exception) & " wasn't raised by:\n" & astToStr(code)) + code + wrong = true except exception: discard except Exception as exc: raiseAssert(astToStr(exception) & " wasn't raised, another error was raised instead by:\n"& astToStr(code)) + if wrong: + raiseAssert(astToStr(exception) & " wasn't raised by:\n" & astToStr(code)) -when defined(cpp) and appType != "lib" and not defined(js) and - not defined(nimscript) and hostOS != "standalone": +when defined(cpp) and appType != "lib" and + not defined(js) and not defined(nimscript) and + hostOS != "standalone" and not defined(noCppExceptions): proc setTerminate(handler: proc() {.noconv.}) {.importc: "std::set_terminate", header: "<exception>".} setTerminate proc() {.noconv.} = diff --git a/lib/system/assign.nim b/lib/system/assign.nim index f061c89cf..16b56aba7 100644 --- a/lib/system/assign.nim +++ b/lib/system/assign.nim @@ -74,13 +74,17 @@ proc genericAssignAux(dest, src: pointer, mt: PNimType, shallow: bool) = var dst = cast[ByteAddress](cast[PPointer](dest)[]) for i in 0..seq.len-1: genericAssignAux( - cast[pointer](dst +% i*% mt.base.size +% GenericSeqSize), + cast[pointer](dst +% i *% mt.base.size +% GenericSeqSize), cast[pointer](cast[ByteAddress](s2) +% i *% mt.base.size +% GenericSeqSize), mt.base, shallow) of tyObject: - if mt.base != nil: - genericAssignAux(dest, src, mt.base, shallow) + var it = mt.base + # don't use recursion here on the PNimType because the subtype + # check should only be done at the very end: + while it != nil: + genericAssignAux(dest, src, it.node, shallow) + it = it.base genericAssignAux(dest, src, mt.node, shallow) # we need to copy m_type field for tyObject, as it could be empty for # sequence reallocations: @@ -89,13 +93,15 @@ proc genericAssignAux(dest, src: pointer, mt: PNimType, shallow: bool) = # if p of TB: # var tbObj = TB(p) # tbObj of TC # needs to be false! + #c_fprintf(stdout, "%s %s\n", pint[].name, mt.name) + chckObjAsgn(cast[ptr PNimType](src)[], mt) pint[] = mt # cast[ptr PNimType](src)[] of tyTuple: genericAssignAux(dest, src, mt.node, shallow) of tyArray, tyArrayConstr: for i in 0..(mt.size div mt.base.size)-1: - genericAssignAux(cast[pointer](d +% i*% mt.base.size), - cast[pointer](s +% i*% mt.base.size), mt.base, shallow) + genericAssignAux(cast[pointer](d +% i *% mt.base.size), + cast[pointer](s +% i *% mt.base.size), mt.base, shallow) of tyRef: unsureAsgnRef(cast[PPointer](dest), cast[PPointer](s)[]) of tyOptAsRef: @@ -160,8 +166,8 @@ proc genericAssignOpenArray(dest, src: pointer, len: int, d = cast[ByteAddress](dest) s = cast[ByteAddress](src) for i in 0..len-1: - genericAssign(cast[pointer](d +% i*% mt.base.size), - cast[pointer](s +% i*% mt.base.size), mt.base) + genericAssign(cast[pointer](d +% i *% mt.base.size), + cast[pointer](s +% i *% mt.base.size), mt.base) proc objectInit(dest: pointer, typ: PNimType) {.compilerProc, benign.} proc objectInitAux(dest: pointer, n: ptr TNimNode) {.benign.} = @@ -229,7 +235,7 @@ proc genericReset(dest: pointer, mt: PNimType) = pint[] = nil of tyArray, tyArrayConstr: for i in 0..(mt.size div mt.base.size)-1: - genericReset(cast[pointer](d +% i*% mt.base.size), mt.base) + genericReset(cast[pointer](d +% i *% mt.base.size), mt.base) else: zeroMem(dest, mt.size) # set raw bits to zero diff --git a/lib/system/atomics.nim b/lib/system/atomics.nim index fa3700190..56ebde823 100644 --- a/lib/system/atomics.nim +++ b/lib/system/atomics.nim @@ -241,7 +241,7 @@ when defined(vcc): else: {.error: "invalid CAS instruction".} -elif defined(tcc) and not defined(windows): +elif defined(tcc): when defined(amd64): {.emit:""" static int __tcc_cas(int *ptr, int oldVal, int newVal) @@ -262,7 +262,7 @@ static int __tcc_cas(int *ptr, int oldVal, int newVal) } """.} else: - assert sizeof(int) == 4 + #assert sizeof(int) == 4 {.emit:""" static int __tcc_cas(int *ptr, int oldVal, int newVal) { diff --git a/lib/system/deepcopy.nim b/lib/system/deepcopy.nim index 51e138e5e..750da00cf 100644 --- a/lib/system/deepcopy.nim +++ b/lib/system/deepcopy.nim @@ -105,7 +105,7 @@ proc genericDeepCopyAux(dest, src: pointer, mt: PNimType; tab: var PtrTable) = var dst = cast[ByteAddress](cast[PPointer](dest)[]) for i in 0..seq.len-1: genericDeepCopyAux( - cast[pointer](dst +% i*% mt.base.size +% GenericSeqSize), + cast[pointer](dst +% i *% mt.base.size +% GenericSeqSize), cast[pointer](cast[ByteAddress](s2) +% i *% mt.base.size +% GenericSeqSize), mt.base, tab) @@ -122,8 +122,8 @@ proc genericDeepCopyAux(dest, src: pointer, mt: PNimType; tab: var PtrTable) = genericDeepCopyAux(dest, src, mt.node, tab) of tyArray, tyArrayConstr: for i in 0..(mt.size div mt.base.size)-1: - genericDeepCopyAux(cast[pointer](d +% i*% mt.base.size), - cast[pointer](s +% i*% mt.base.size), mt.base, tab) + genericDeepCopyAux(cast[pointer](d +% i *% mt.base.size), + cast[pointer](s +% i *% mt.base.size), mt.base, tab) of tyRef, tyOptAsRef: let s2 = cast[PPointer](src)[] if s2 == nil: @@ -183,5 +183,5 @@ proc genericDeepCopyOpenArray(dest, src: pointer, len: int, d = cast[ByteAddress](dest) s = cast[ByteAddress](src) for i in 0..len-1: - genericDeepCopy(cast[pointer](d +% i*% mt.base.size), - cast[pointer](s +% i*% mt.base.size), mt.base) + genericDeepCopy(cast[pointer](d +% i *% mt.base.size), + cast[pointer](s +% i *% mt.base.size), mt.base) diff --git a/lib/system/embedded.nim b/lib/system/embedded.nim index a14f43e7e..46e84e056 100644 --- a/lib/system/embedded.nim +++ b/lib/system/embedded.nim @@ -40,4 +40,4 @@ proc reraiseException() {.compilerRtl.} = proc writeStackTrace() = discard -proc setControlCHook(hook: proc () {.noconv.} not nil) = discard +proc setControlCHook(hook: proc () {.noconv.}) = discard diff --git a/lib/system/excpt.nim b/lib/system/excpt.nim index afeab2b6c..fb38948f7 100644 --- a/lib/system/excpt.nim +++ b/lib/system/excpt.nim @@ -56,7 +56,7 @@ var # list of exception handlers # a global variable for the root of all try blocks currException {.threadvar.}: ref Exception - raise_counter {.threadvar.}: uint + raise_counter {.threadvar.}: uint gcFramePtr {.threadvar.}: GcFrame @@ -126,10 +126,10 @@ proc popCurrentExceptionEx(id: uint) {.compilerRtl.} = while cur != nil and cur.raise_id != id: prev = cur cur = cur.up - if cur == nil: + if cur == nil: showErrorMessage("popCurrentExceptionEx() exception was not found in the exception stack. Aborting...") quitOrDebug() - prev.up = cur.up + prev.up = cur.up # some platforms have native support for stack traces: const @@ -503,7 +503,7 @@ when not defined(noSignalHandler) and not defined(useNimRtl): registerSignalHandler() # call it in initialization section -proc setControlCHook(hook: proc () {.noconv.} not nil) = +proc setControlCHook(hook: proc () {.noconv.}) = # ugly cast, but should work on all architectures: type SignalHandler = proc (sign: cint) {.noconv, benign.} c_signal(SIGINT, cast[SignalHandler](hook)) diff --git a/lib/system/gc.nim b/lib/system/gc.nim index 66d49ce1b..425963f3f 100644 --- a/lib/system/gc.nim +++ b/lib/system/gc.nim @@ -535,7 +535,7 @@ proc growObj(old: pointer, newsize: int, gch: var GcHeap): pointer = var oldsize = cast[PGenericSeq](old).len*elemSize + GenericSeqSize copyMem(res, ol, oldsize + sizeof(Cell)) - zeroMem(cast[pointer](cast[ByteAddress](res)+% oldsize +% sizeof(Cell)), + zeroMem(cast[pointer](cast[ByteAddress](res) +% oldsize +% sizeof(Cell)), newsize-oldsize) sysAssert((cast[ByteAddress](res) and (MemAlign-1)) == 0, "growObj: 3") # This can be wrong for intermediate temps that are nevertheless on the diff --git a/lib/system/jssys.nim b/lib/system/jssys.nim index 8c3175eac..56a9f6575 100644 --- a/lib/system/jssys.nim +++ b/lib/system/jssys.nim @@ -48,10 +48,7 @@ proc nimCharToStr(x: char): string {.compilerproc.} = result[0] = x proc isNimException(): bool {.asmNoStackFrame.} = - when defined(nimphp): - asm "return isset(`lastJSError`['m_type']);" - else: - asm "return `lastJSError`.m_type;" + asm "return `lastJSError`.m_type;" proc getCurrentException*(): ref Exception {.compilerRtl, benign.} = if isNimException(): result = cast[ref Exception](lastJSError) @@ -61,15 +58,14 @@ proc getCurrentExceptionMsg*(): string = if isNimException(): return cast[Exception](lastJSError).msg else: - when not defined(nimphp): - var msg: cstring - {.emit: """ - if (`lastJSError`.message !== undefined) { - `msg` = `lastJSError`.message; - } - """.} - if not msg.isNil: - return $msg + var msg: cstring + {.emit: """ + if (`lastJSError`.message !== undefined) { + `msg` = `lastJSError`.message; + } + """.} + if not msg.isNil: + return $msg return "" proc auxWriteStackTrace(f: PCallFrame): string = @@ -140,12 +136,9 @@ proc raiseException(e: ref Exception, ename: cstring) {. e.name = ename if excHandler == 0: unhandledException(e) - when defined(nimphp): - asm """throw new Exception($`e`["message"]);""" - else: - when NimStackTrace: - e.trace = rawWriteStackTrace() - asm "throw `e`;" + when NimStackTrace: + e.trace = rawWriteStackTrace() + asm "throw `e`;" proc reraiseException() {.compilerproc, asmNoStackFrame.} = if lastJSError == nil: @@ -173,57 +166,35 @@ proc raiseFieldError(f: string) {.compilerproc, noreturn.} = raise newException(FieldError, f & " is not accessible") proc setConstr() {.varargs, asmNoStackFrame, compilerproc.} = - when defined(nimphp): - asm """ - $args = func_get_args(); - $result = array(); - foreach ($args as $x) { - if (is_array($x)) { - for ($j = $x[0]; $j <= $x[1]; $j++) { - $result[$j] = true; - } - } else { - $result[$x] = true; - } - } - return $result; - """ - else: - asm """ - var result = {}; - for (var i = 0; i < arguments.length; ++i) { - var x = arguments[i]; - if (typeof(x) == "object") { - for (var j = x[0]; j <= x[1]; ++j) { - result[j] = true; - } - } else { - result[x] = true; + asm """ + var result = {}; + for (var i = 0; i < arguments.length; ++i) { + var x = arguments[i]; + if (typeof(x) == "object") { + for (var j = x[0]; j <= x[1]; ++j) { + result[j] = true; } + } else { + result[x] = true; } - return result; - """ - -proc makeNimstrLit(c: cstring): string {.asmNoStackFrame, compilerproc.} = - when defined(nimphp): - {.emit: """return `c`;""".} - else: - {.emit: """ - var ln = `c`.length; - var result = new Array(ln + 1); - var i = 0; - for (; i < ln; ++i) { - result[i] = `c`.charCodeAt(i); } - result[i] = 0; // terminating zero return result; - """.} + """ + +proc makeNimstrLit(c: cstring): string {.asmNoStackFrame, compilerproc.} = + {.emit: """ + var ln = `c`.length; + var result = new Array(ln + 1); + var i = 0; + for (; i < ln; ++i) { + result[i] = `c`.charCodeAt(i); + } + result[i] = 0; // terminating zero + return result; + """.} proc cstrToNimstr(c: cstring): string {.asmNoStackFrame, compilerproc.} = - when defined(nimphp): - {.emit: """return `c`;""".} - else: - {.emit: """ + {.emit: """ var ln = `c`.length; var result = new Array(ln); var r = 0; @@ -261,156 +232,93 @@ proc cstrToNimstr(c: cstring): string {.asmNoStackFrame, compilerproc.} = """.} proc toJSStr(s: string): cstring {.asmNoStackFrame, compilerproc.} = - when defined(nimphp): - {.emit: """return `s`;""".} - else: - asm """ - var len = `s`.length-1; - var asciiPart = new Array(len); - var fcc = String.fromCharCode; - var nonAsciiPart = null; - var nonAsciiOffset = 0; - for (var i = 0; i < len; ++i) { - if (nonAsciiPart !== null) { - var offset = (i - nonAsciiOffset) * 2; - var code = `s`[i].toString(16); - if (code.length == 1) { - code = "0"+code; - } - nonAsciiPart[offset] = "%"; - nonAsciiPart[offset + 1] = code; - } - else if (`s`[i] < 128) - asciiPart[i] = fcc(`s`[i]); - else { - asciiPart.length = i; - nonAsciiOffset = i; - nonAsciiPart = new Array((len - i) * 2); - --i; + asm """ + var len = `s`.length-1; + var asciiPart = new Array(len); + var fcc = String.fromCharCode; + var nonAsciiPart = null; + var nonAsciiOffset = 0; + for (var i = 0; i < len; ++i) { + if (nonAsciiPart !== null) { + var offset = (i - nonAsciiOffset) * 2; + var code = `s`[i].toString(16); + if (code.length == 1) { + code = "0"+code; } + nonAsciiPart[offset] = "%"; + nonAsciiPart[offset + 1] = code; } - asciiPart = asciiPart.join(""); - return (nonAsciiPart === null) ? - asciiPart : asciiPart + decodeURIComponent(nonAsciiPart.join("")); + else if (`s`[i] < 128) + asciiPart[i] = fcc(`s`[i]); + else { + asciiPart.length = i; + nonAsciiOffset = i; + nonAsciiPart = new Array((len - i) * 2); + --i; + } + } + asciiPart = asciiPart.join(""); + return (nonAsciiPart === null) ? + asciiPart : asciiPart + decodeURIComponent(nonAsciiPart.join("")); """ proc mnewString(len: int): string {.asmNoStackFrame, compilerproc.} = - when defined(nimphp): - asm """ - return str_repeat(chr(0),`len`); - """ - else: - asm """ - var result = new Array(`len`+1); - result[0] = 0; - result[`len`] = 0; - return result; - """ - -when defined(nimphp): - proc nimAt(x: string; i: int): string {.asmNoStackFrame, compilerproc.} = - asm """ - return `x`[`i`]; - """ - -when defined(nimphp): - proc nimSubstr(s: string; a, b: int): string {. - asmNoStackFrame, compilerproc.} = - asm """return substr(`s`,`a`,`b`-`a`+1);""" + asm """ + var result = new Array(`len`+1); + result[0] = 0; + result[`len`] = 0; + return result; + """ proc SetCard(a: int): int {.compilerproc, asmNoStackFrame.} = # argument type is a fake - when defined(nimphp): - asm """ - return count(`a`); - """ - else: - asm """ - var result = 0; - for (var elem in `a`) { ++result; } - return result; - """ + asm """ + var result = 0; + for (var elem in `a`) { ++result; } + return result; + """ proc SetEq(a, b: int): bool {.compilerproc, asmNoStackFrame.} = - when defined(nimphp): - asm """ - foreach (`a` as $elem=>$_) { if (!isset(`b`[$elem])) return false; } - foreach (`b` as $elem=>$_) { if (!isset(`a`[$elem])) return false; } - return true; - """ - else: - asm """ - for (var elem in `a`) { if (!`b`[elem]) return false; } - for (var elem in `b`) { if (!`a`[elem]) return false; } - return true; - """ + asm """ + for (var elem in `a`) { if (!`b`[elem]) return false; } + for (var elem in `b`) { if (!`a`[elem]) return false; } + return true; + """ proc SetLe(a, b: int): bool {.compilerproc, asmNoStackFrame.} = - when defined(nimphp): - asm """ - foreach (`a` as $elem=>$_) { if (!isset(`b`[$elem])) return false; } - return true; - """ - else: - asm """ - for (var elem in `a`) { if (!`b`[elem]) return false; } - return true; - """ + asm """ + for (var elem in `a`) { if (!`b`[elem]) return false; } + return true; + """ proc SetLt(a, b: int): bool {.compilerproc.} = result = SetLe(a, b) and not SetEq(a, b) proc SetMul(a, b: int): int {.compilerproc, asmNoStackFrame.} = - when defined(nimphp): - asm """ - var $result = array(); - foreach (`a` as $elem=>$_) { - if (isset(`b`[$elem])) { $result[$elem] = true; } - } - return $result; - """ - else: - asm """ - var result = {}; - for (var elem in `a`) { - if (`b`[elem]) { result[elem] = true; } - } - return result; - """ + asm """ + var result = {}; + for (var elem in `a`) { + if (`b`[elem]) { result[elem] = true; } + } + return result; + """ proc SetPlus(a, b: int): int {.compilerproc, asmNoStackFrame.} = - when defined(nimphp): - asm """ - var $result = array(); - foreach (`a` as $elem=>$_) { $result[$elem] = true; } - foreach (`b` as $elem=>$_) { $result[$elem] = true; } - return $result; - """ - else: - asm """ - var result = {}; - for (var elem in `a`) { result[elem] = true; } - for (var elem in `b`) { result[elem] = true; } - return result; - """ + asm """ + var result = {}; + for (var elem in `a`) { result[elem] = true; } + for (var elem in `b`) { result[elem] = true; } + return result; + """ proc SetMinus(a, b: int): int {.compilerproc, asmNoStackFrame.} = - when defined(nimphp): - asm """ - $result = array(); - foreach (`a` as $elem=>$_) { - if (!isset(`b`[$elem])) { $result[$elem] = true; } - } - return $result; - """ - else: - asm """ - var result = {}; - for (var elem in `a`) { - if (!`b`[elem]) { result[elem] = true; } - } - return result; - """ + asm """ + var result = {}; + for (var elem in `a`) { + if (!`b`[elem]) { result[elem] = true; } + } + return result; + """ proc cmpStrings(a, b: string): int {.asmNoStackFrame, compilerProc.} = asm """ @@ -424,15 +332,8 @@ proc cmpStrings(a, b: string): int {.asmNoStackFrame, compilerProc.} = return `a`.length - `b`.length; """ -proc cmp(x, y: string): int = - when defined(nimphp): - asm """ - if(`x` < `y`) `result` = -1; - elseif (`x` > `y`) `result` = 1; - else `result` = 0; - """ - else: - return cmpStrings(x, y) +proc cmp(x, y: string): int = + return cmpStrings(x, y) proc eqStrings(a, b: string): bool {.asmNoStackFrame, compilerProc.} = asm """ @@ -467,7 +368,7 @@ elif not defined(nimOldEcho): console.log(buf); """ -elif not defined(nimphp): +else: proc ewriteln(x: cstring) = var node : JSRef {.emit: "`node` = document.getElementsByTagName('body')[0];".} @@ -493,127 +394,77 @@ elif not defined(nimphp): # Arithmetic: proc addInt(a, b: int): int {.asmNoStackFrame, compilerproc.} = - when defined(nimphp): - asm """ - return `a` + `b`; - """ - else: - asm """ - var result = `a` + `b`; - if (result > 2147483647 || result < -2147483648) `raiseOverflow`(); - return result; - """ + asm """ + var result = `a` + `b`; + if (result > 2147483647 || result < -2147483648) `raiseOverflow`(); + return result; + """ proc subInt(a, b: int): int {.asmNoStackFrame, compilerproc.} = - when defined(nimphp): - asm """ - return `a` - `b`; - """ - else: - asm """ - var result = `a` - `b`; - if (result > 2147483647 || result < -2147483648) `raiseOverflow`(); - return result; - """ + asm """ + var result = `a` - `b`; + if (result > 2147483647 || result < -2147483648) `raiseOverflow`(); + return result; + """ proc mulInt(a, b: int): int {.asmNoStackFrame, compilerproc.} = - when defined(nimphp): - asm """ - return `a` * `b`; - """ - else: - asm """ - var result = `a` * `b`; - if (result > 2147483647 || result < -2147483648) `raiseOverflow`(); - return result; - """ + asm """ + var result = `a` * `b`; + if (result > 2147483647 || result < -2147483648) `raiseOverflow`(); + return result; + """ proc divInt(a, b: int): int {.asmNoStackFrame, compilerproc.} = - when defined(nimphp): - asm """ - return trunc(`a` / `b`); - """ - else: - asm """ - if (`b` == 0) `raiseDivByZero`(); - if (`b` == -1 && `a` == 2147483647) `raiseOverflow`(); - return Math.trunc(`a` / `b`); - """ + asm """ + if (`b` == 0) `raiseDivByZero`(); + if (`b` == -1 && `a` == 2147483647) `raiseOverflow`(); + return Math.trunc(`a` / `b`); + """ proc modInt(a, b: int): int {.asmNoStackFrame, compilerproc.} = - when defined(nimphp): - asm """ - return `a` % `b`; - """ - else: - asm """ - if (`b` == 0) `raiseDivByZero`(); - if (`b` == -1 && `a` == 2147483647) `raiseOverflow`(); - return Math.trunc(`a` % `b`); - """ + asm """ + if (`b` == 0) `raiseDivByZero`(); + if (`b` == -1 && `a` == 2147483647) `raiseOverflow`(); + return Math.trunc(`a` % `b`); + """ proc addInt64(a, b: int): int {.asmNoStackFrame, compilerproc.} = - when defined(nimphp): - asm """ - return `a` + `b`; - """ - else: - asm """ - var result = `a` + `b`; - if (result > 9223372036854775807 - || result < -9223372036854775808) `raiseOverflow`(); - return result; - """ + asm """ + var result = `a` + `b`; + if (result > 9223372036854775807 + || result < -9223372036854775808) `raiseOverflow`(); + return result; + """ proc subInt64(a, b: int): int {.asmNoStackFrame, compilerproc.} = - when defined(nimphp): - asm """ - return `a` - `b`; - """ - else: - asm """ - var result = `a` - `b`; - if (result > 9223372036854775807 - || result < -9223372036854775808) `raiseOverflow`(); - return result; - """ + asm """ + var result = `a` - `b`; + if (result > 9223372036854775807 + || result < -9223372036854775808) `raiseOverflow`(); + return result; + """ proc mulInt64(a, b: int): int {.asmNoStackFrame, compilerproc.} = - when defined(nimphp): - asm """ - return `a` * `b`; - """ - else: - asm """ - var result = `a` * `b`; - if (result > 9223372036854775807 - || result < -9223372036854775808) `raiseOverflow`(); - return result; - """ + asm """ + var result = `a` * `b`; + if (result > 9223372036854775807 + || result < -9223372036854775808) `raiseOverflow`(); + return result; + """ proc divInt64(a, b: int): int {.asmNoStackFrame, compilerproc.} = - when defined(nimphp): - asm """ - return trunc(`a` / `b`); - """ - else: - asm """ - if (`b` == 0) `raiseDivByZero`(); - if (`b` == -1 && `a` == 9223372036854775807) `raiseOverflow`(); - return Math.trunc(`a` / `b`); - """ + asm """ + if (`b` == 0) `raiseDivByZero`(); + if (`b` == -1 && `a` == 9223372036854775807) `raiseOverflow`(); + return Math.trunc(`a` / `b`); + """ proc modInt64(a, b: int): int {.asmNoStackFrame, compilerproc.} = - when defined(nimphp): - asm """ - return `a` % `b`; - """ - else: - asm """ - if (`b` == 0) `raiseDivByZero`(); - if (`b` == -1 && `a` == 9223372036854775807) `raiseOverflow`(); - return Math.trunc(`a` % `b`); - """ + asm """ + if (`b` == 0) `raiseDivByZero`(); + if (`b` == -1 && `a` == 9223372036854775807) `raiseOverflow`(); + return Math.trunc(`a` % `b`); + """ proc negInt(a: int): int {.compilerproc.} = result = a*(-1) @@ -767,24 +618,14 @@ proc genericReset(x: JSRef, ti: PNimType): JSRef {.compilerproc.} = else: discard -when defined(nimphp): - proc arrayConstr(len: int, value: string, typ: string): JSRef {. - asmNoStackFrame, compilerproc.} = - # types are fake - asm """ - $result = array(); - for ($i = 0; $i < `len`; $i++) $result[] = `value`; - return $result; - """ -else: - proc arrayConstr(len: int, value: JSRef, typ: PNimType): JSRef {. - asmNoStackFrame, compilerproc.} = +proc arrayConstr(len: int, value: JSRef, typ: PNimType): JSRef {. + asmNoStackFrame, compilerproc.} = # types are fake - asm """ - var result = new Array(`len`); - for (var i = 0; i < `len`; ++i) result[i] = nimCopy(null, `value`, `typ`); - return result; - """ + asm """ + var result = new Array(`len`); + for (var i = 0; i < `len`; ++i) result[i] = nimCopy(null, `value`, `typ`); + return result; + """ proc chckIndx(i, a, b: int): int {.compilerproc.} = if i >= a and i <= b: return i diff --git a/lib/system/osalloc.nim b/lib/system/osalloc.nim index 1ad4cf695..9609b6d39 100644 --- a/lib/system/osalloc.nim +++ b/lib/system/osalloc.nim @@ -58,7 +58,7 @@ when defined(emscripten): # Convert pointer to PageSize correct one. var new_pos = cast[ByteAddress](pos) +% (PageSize - (pos %% PageSize)) - if (new_pos-pos)< sizeof(EmscriptenMMapBlock): + if (new_pos-pos) < sizeof(EmscriptenMMapBlock): new_pos = new_pos +% PageSize result = cast[pointer](new_pos) diff --git a/lib/system/sysstr.nim b/lib/system/sysstr.nim index ea0273340..c5c8a9cac 100644 --- a/lib/system/sysstr.nim +++ b/lib/system/sysstr.nim @@ -22,21 +22,34 @@ proc resize(old: int): int {.inline.} = proc cmpStrings(a, b: NimString): int {.inline, compilerProc.} = if a == b: return 0 - if a == nil: return -1 - if b == nil: return 1 - let minlen = min(a.len, b.len) + when defined(nimNoNil): + let alen = if a == nil: 0 else: a.len + let blen = if b == nil: 0 else: b.len + else: + if a == nil: return -1 + if b == nil: return 1 + let alen = a.len + let blen = b.len + let minlen = min(alen, blen) if minlen > 0: result = c_memcmp(addr a.data, addr b.data, minlen.csize) if result == 0: - result = a.len - b.len + result = alen - blen else: - result = a.len - b.len + result = alen - blen proc eqStrings(a, b: NimString): bool {.inline, compilerProc.} = if a == b: return true - if a == nil or b == nil: return false - return a.len == b.len and - equalMem(addr(a.data), addr(b.data), a.len) + when defined(nimNoNil): + let alen = if a == nil: 0 else: a.len + let blen = if b == nil: 0 else: b.len + else: + if a == nil or b == nil: return false + let alen = a.len + let blen = b.len + if alen == blen: + if alen == 0: return true + return equalMem(addr(a.data), addr(b.data), alen) when declared(allocAtomic): template allocStr(size: untyped): untyped = @@ -63,6 +76,7 @@ proc rawNewStringNoInit(space: int): NimString {.compilerProc.} = if s < 7: s = 7 result = allocStrNoInit(sizeof(TGenericSeq) + s + 1) result.reserved = s + result.len = 0 when defined(gogc): result.elemSize = 1 @@ -71,6 +85,7 @@ proc rawNewString(space: int): NimString {.compilerProc.} = if s < 7: s = 7 result = allocStr(sizeof(TGenericSeq) + s + 1) result.reserved = s + result.len = 0 when defined(gogc): result.elemSize = 1 @@ -101,9 +116,6 @@ proc cstrToNimstr(str: cstring): NimString {.compilerRtl.} = if str == nil: NimString(nil) else: toNimStr(str, str.len) -template wasMoved(x: NimString): bool = false -# (x.reserved and seqShallowFlag) != 0 - proc copyString(src: NimString): NimString {.compilerRtl.} = if src != nil: if (src.reserved and seqShallowFlag) != 0: @@ -161,14 +173,16 @@ proc hashString(s: string): int {.compilerproc.} = proc addChar(s: NimString, c: char): NimString = # is compilerproc! - result = s - if result.len >= result.space: - let r = resize(result.space) - result = cast[NimString](growObj(result, - sizeof(TGenericSeq) + r + 1)) - result.reserved = r - elif wasMoved(s): - result = newOwnedString(s, s.len) + if s == nil: + result = rawNewStringNoInit(1) + result.len = 0 + else: + result = s + if result.len >= result.space: + let r = resize(result.space) + result = cast[NimString](growObj(result, + sizeof(TGenericSeq) + r + 1)) + result.reserved = r result.data[result.len] = c result.data[result.len+1] = '\0' inc(result.len) @@ -205,7 +219,9 @@ proc addChar(s: NimString, c: char): NimString = # s = rawNewString(0); proc resizeString(dest: NimString, addlen: int): NimString {.compilerRtl.} = - if dest.len + addlen <= dest.space and not wasMoved(dest): + if dest == nil: + result = rawNewStringNoInit(addlen) + elif dest.len + addlen <= dest.space: result = dest else: # slow path: var sp = max(resize(dest.space), dest.len + addlen) @@ -216,8 +232,9 @@ proc resizeString(dest: NimString, addlen: int): NimString {.compilerRtl.} = # DO NOT UPDATE LEN YET: dest.len = newLen proc appendString(dest, src: NimString) {.compilerproc, inline.} = - copyMem(addr(dest.data[dest.len]), addr(src.data), src.len + 1) - inc(dest.len, src.len) + if src != nil: + copyMem(addr(dest.data[dest.len]), addr(src.data), src.len + 1) + inc(dest.len, src.len) proc appendChar(dest: NimString, c: char) {.compilerproc, inline.} = dest.data[dest.len] = c @@ -226,8 +243,8 @@ proc appendChar(dest: NimString, c: char) {.compilerproc, inline.} = proc setLengthStr(s: NimString, newLen: int): NimString {.compilerRtl.} = var n = max(newLen, 0) - if wasMoved(s): - result = newOwnedString(s, n) + if s == nil: + result = mnewString(newLen) elif n <= s.space: result = s else: @@ -261,6 +278,18 @@ proc incrSeqV2(seq: PGenericSeq, elemSize: int): PGenericSeq {.compilerProc.} = GenericSeqSize)) result.reserved = r +proc incrSeqV3(s: PGenericSeq, typ: PNimType): PGenericSeq {.compilerProc.} = + if s == nil: + result = cast[PGenericSeq](newSeq(typ, 1)) + result.len = 0 + else: + result = s + if result.len >= result.space: + let r = resize(result.space) + result = cast[PGenericSeq](growObj(result, typ.base.size * r + + GenericSeqSize)) + result.reserved = r + proc setLengthSeq(seq: PGenericSeq, elemSize, newLen: int): PGenericSeq {. compilerRtl, inl.} = result = seq @@ -301,6 +330,13 @@ proc setLengthSeq(seq: PGenericSeq, elemSize, newLen: int): PGenericSeq {. (newLen*%elemSize)), (result.len-%newLen) *% elemSize) result.len = newLen +proc setLengthSeqV2(s: PGenericSeq, typ: PNimType, newLen: int): PGenericSeq {. + compilerRtl.} = + if s == nil: + result = cast[PGenericSeq](newSeq(typ, newLen)) + else: + result = setLengthSeq(s, typ.base.size, newLen) + # --------------- other string routines ---------------------------------- proc add*(result: var string; x: int64) = let base = result.len diff --git a/lib/windows/winlean.nim b/lib/windows/winlean.nim index f40344396..7e22f98c7 100644 --- a/lib/windows/winlean.nim +++ b/lib/windows/winlean.nim @@ -10,7 +10,7 @@ ## This module implements a small wrapper for some needed Win API procedures, ## so that the Nim compiler does not depend on the huge Windows module. -{.deadCodeElim:on.} +{.deadCodeElim: on.} # dce option deprecated import dynlib @@ -1086,3 +1086,14 @@ proc ConvertThreadToFiberEx*(param: pointer, flags: int32): pointer {.stdcall, d proc DeleteFiber*(fiber: pointer): void {.stdcall, discardable, dynlib: "kernel32", importc.} proc SwitchToFiber*(fiber: pointer): void {.stdcall, discardable, dynlib: "kernel32", importc.} proc GetCurrentFiber*(): pointer {.stdcall, importc, header: "Windows.h".} + +proc toFILETIME*(t: int64): FILETIME = + ## Convert the Windows file time timestamp ``t`` to ``FILETIME``. + result = FILETIME(dwLowDateTime: cast[DWORD](t), dwHighDateTime: DWORD(t shr 32)) + +type + LPFILETIME* = ptr FILETIME + +proc setFileTime*(hFile: HANDLE, lpCreationTime: LPFILETIME, + lpLastAccessTime: LPFILETIME, lpLastWriteTime: LPFILETIME): WINBOOL + {.stdcall, dynlib: "kernel32", importc: "SetFileTime".} diff --git a/lib/wrappers/iup.nim b/lib/wrappers/iup.nim index d910173ca..dee2e14c7 100644 --- a/lib/wrappers/iup.nim +++ b/lib/wrappers/iup.nim @@ -34,7 +34,7 @@ # SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # **************************************************************************** -{.deadCodeElim: on.} +{.deadCodeElim: on.} # dce option deprecated when defined(windows): const dllname = "iup(|30|27|26|25|24).dll" diff --git a/lib/wrappers/mysql.nim b/lib/wrappers/mysql.nim index 4464eae50..06c663822 100644 --- a/lib/wrappers/mysql.nim +++ b/lib/wrappers/mysql.nim @@ -7,7 +7,7 @@ # distribution, for details about the copyright. # -{.deadCodeElim: on.} +{.deadCodeElim: on.} # dce option deprecated {.push, callconv: cdecl.} when defined(Unix): diff --git a/lib/wrappers/odbcsql.nim b/lib/wrappers/odbcsql.nim index 1b2544ec0..43d1c504d 100644 --- a/lib/wrappers/odbcsql.nim +++ b/lib/wrappers/odbcsql.nim @@ -7,7 +7,7 @@ # distribution, for details about the copyright. # -{.deadCodeElim: on.} +{.deadCodeElim: on.} # dce option deprecated when not defined(ODBCVER): const diff --git a/lib/wrappers/openssl.nim b/lib/wrappers/openssl.nim index 357343bff..6609dc245 100644 --- a/lib/wrappers/openssl.nim +++ b/lib/wrappers/openssl.nim @@ -22,7 +22,7 @@ ## ./bin/nim c -d:ssl -p:. -r tests/untestable/tssl.nim ## ./bin/nim c -d:ssl -p:. --dynlibOverride:ssl --passL:-lcrypto --passL:-lssl -r tests/untestable/tssl.nim -{.deadCodeElim: on.} +{.deadCodeElim: on.} # dce option deprecated const useWinVersion = defined(Windows) or defined(nimdoc) @@ -38,7 +38,7 @@ when useWinVersion: from winlean import SocketHandle else: - const versions = "(.1.1|.38|.39|.41|.43|.44|.10|.1.0.2|.1.0.1|.1.0.0|.0.9.9|.0.9.8|)" + const versions = "(.1.1|.38|.39|.41|.43|.44|.45|.10|.1.0.2|.1.0.1|.1.0.0|.0.9.9|.0.9.8|)" when defined(macosx): const diff --git a/lib/wrappers/pcre.nim b/lib/wrappers/pcre.nim index 6c7088bbf..e9e11960c 100644 --- a/lib/wrappers/pcre.nim +++ b/lib/wrappers/pcre.nim @@ -7,7 +7,7 @@ # distribution, for details about the copyright. # -{.deadCodeElim: on.} +{.deadCodeElim: on.} # dce option deprecated # The current PCRE version information. diff --git a/lib/wrappers/postgres.nim b/lib/wrappers/postgres.nim index f9a10dccb..7cb816f68 100644 --- a/lib/wrappers/postgres.nim +++ b/lib/wrappers/postgres.nim @@ -15,7 +15,7 @@ # connection-protocol. # -{.deadCodeElim: on.} +{.deadCodeElim: on.} # dce option deprecated when defined(windows): const diff --git a/lib/wrappers/sqlite3.nim b/lib/wrappers/sqlite3.nim index a12945832..0276a0a65 100644 --- a/lib/wrappers/sqlite3.nim +++ b/lib/wrappers/sqlite3.nim @@ -7,7 +7,7 @@ # distribution, for details about the copyright. # -{.deadCodeElim: on.} +{.deadCodeElim: on.} # dce option deprecated when defined(windows): when defined(nimOldDlls): const Lib = "sqlite3.dll" |