diff options
Diffstat (limited to 'lib/pure')
40 files changed, 1316 insertions, 1316 deletions
diff --git a/lib/pure/actors.nim b/lib/pure/actors.nim index da9037285..f0791f954 100644 --- a/lib/pure/actors.nim +++ b/lib/pure/actors.nim @@ -41,7 +41,7 @@ type Actor[In, Out] = object{.pure, final.} i: Channel[Task[In, Out]] t: TThread[ptr Actor[In, Out]] - + PActor*[In, Out] = ptr Actor[In, Out] ## an actor {.deprecated: [TTask: Task, TActor: Actor].} @@ -83,7 +83,7 @@ proc send*[In, Out, X, Y](receiver: PActor[In, Out], msg: In, shallowCopy(t.data, msg) send(receiver.i, t) -proc send*[In, Out](receiver: PActor[In, Out], msg: In, +proc send*[In, Out](receiver: PActor[In, Out], msg: In, sender: ptr Channel[Out] = nil) = ## sends a message to `receiver`'s inbox. var t: Task[In, Out] @@ -138,7 +138,7 @@ proc createActorPool*[In, Out](a: var ActorPool[In, Out], poolSize = 4) = proc sync*[In, Out](a: var ActorPool[In, Out], polling=50) = ## waits for every actor of `a` to finish with its work. Currently this is - ## implemented as polling every `polling` ms and has a slight chance + ## implemented as polling every `polling` ms and has a slight chance ## of failing since we check for every actor to be in `ready` state and not ## for messages still in ether. This will change in a later ## version, however. @@ -146,7 +146,7 @@ proc sync*[In, Out](a: var ActorPool[In, Out], polling=50) = while true: var wait = false for i in 0..high(a.actors): - if not a.actors[i].i.ready: + if not a.actors[i].i.ready: wait = true allReadyCount = 0 break @@ -222,7 +222,7 @@ proc spawn*[In](p: var ActorPool[In, void], input: In, var t: Task[In, void] setupTask() schedule() - + when not defined(testing) and isMainModule: var a: ActorPool[int, void] diff --git a/lib/pure/asyncfile.nim b/lib/pure/asyncfile.nim index ece9b4dfb..c7b9fac18 100644 --- a/lib/pure/asyncfile.nim +++ b/lib/pure/asyncfile.nim @@ -195,10 +195,10 @@ proc read*(f: AsyncFile, size: int): Future[string] = readBuffer.setLen(res) f.offset.inc(res) retFuture.complete(readBuffer) - + if not cb(f.fd): addRead(f.fd, cb) - + return retFuture proc readLine*(f: AsyncFile): Future[string] {.async.} = @@ -222,7 +222,7 @@ proc getFilePos*(f: AsyncFile): int64 = proc setFilePos*(f: AsyncFile, pos: int64) = ## Sets the position of the file pointer that is used for read/write - ## operations. The file's first byte has the index zero. + ## operations. The file's first byte has the index zero. f.offset = pos when not defined(windows) and not defined(nimdoc): let ret = lseek(f.fd.cint, pos, SEEK_SET) @@ -291,7 +291,7 @@ proc write*(f: AsyncFile, data: string): Future[void] = retFuture.complete() else: var written = 0 - + proc cb(fd: AsyncFD): bool = result = true let remainderSize = data.len-written @@ -309,7 +309,7 @@ proc write*(f: AsyncFile, data: string): Future[void] = result = false # We still have data to write. else: retFuture.complete() - + if not cb(f.fd): addWrite(f.fd, cb) return retFuture diff --git a/lib/pure/base64.nim b/lib/pure/base64.nim index 41d19dc0f..deab39c7c 100644 --- a/lib/pure/base64.nim +++ b/lib/pure/base64.nim @@ -9,11 +9,11 @@ ## This module implements a base64 encoder and decoder. -const +const cb64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/" -template encodeInternal(s: expr, lineLen: int, newLine: string): stmt {.immediate.} = - ## encodes `s` into base64 representation. After `lineLen` characters, a +template encodeInternal(s: expr, lineLen: int, newLine: string): stmt {.immediate.} = + ## encodes `s` into base64 representation. After `lineLen` characters, a ## `newline` is added. var total = ((len(s) + 2) div 3) * 4 var numLines = (total + lineLen - 1) div lineLen @@ -29,13 +29,13 @@ template encodeInternal(s: expr, lineLen: int, newLine: string): stmt {.immediat var c = ord(s[i+2]) result[r] = cb64[a shr 2] result[r+1] = cb64[((a and 3) shl 4) or ((b and 0xF0) shr 4)] - result[r+2] = cb64[((b and 0x0F) shl 2) or ((c and 0xC0) shr 6)] - result[r+3] = cb64[c and 0x3F] + result[r+2] = cb64[((b and 0x0F) shl 2) or ((c and 0xC0) shr 6)] + result[r+3] = cb64[c and 0x3F] inc(r, 4) inc(i, 3) inc(currLine, 4) - if currLine >= lineLen and i != s.len-2: - for x in items(newLine): + if currLine >= lineLen and i != s.len-2: + for x in items(newLine): result[r] = x inc(r) currLine = 0 @@ -45,7 +45,7 @@ template encodeInternal(s: expr, lineLen: int, newLine: string): stmt {.immediat var b = ord(s[i+1]) result[r] = cb64[a shr 2] result[r+1] = cb64[((a and 3) shl 4) or ((b and 0xF0) shr 4)] - result[r+2] = cb64[((b and 0x0F) shl 2)] + result[r+2] = cb64[((b and 0x0F) shl 2)] result[r+3] = '=' if r+4 != result.len: setLen(result, r+4) @@ -61,17 +61,17 @@ template encodeInternal(s: expr, lineLen: int, newLine: string): stmt {.immediat #assert(r == result.len) discard -proc encode*[T:SomeInteger|char](s: openarray[T], lineLen = 75, newLine="\13\10"): string = - ## encodes `s` into base64 representation. After `lineLen` characters, a +proc encode*[T:SomeInteger|char](s: openarray[T], lineLen = 75, newLine="\13\10"): string = + ## encodes `s` into base64 representation. After `lineLen` characters, a ## `newline` is added. encodeInternal(s, lineLen, newLine) - -proc encode*(s: string, lineLen = 75, newLine="\13\10"): string = - ## encodes `s` into base64 representation. After `lineLen` characters, a + +proc encode*(s: string, lineLen = 75, newLine="\13\10"): string = + ## encodes `s` into base64 representation. After `lineLen` characters, a ## `newline` is added. encodeInternal(s, lineLen, newLine) - -proc decodeByte(b: char): int {.inline.} = + +proc decodeByte(b: char): int {.inline.} = case b of '+': result = ord('>') of '0'..'9': result = ord(b) + 4 @@ -79,7 +79,7 @@ proc decodeByte(b: char): int {.inline.} = of 'a'..'z': result = ord(b) - 71 else: result = 63 -proc decode*(s: string): string = +proc decode*(s: string): string = ## decodes a string in base64 representation back into its original form. ## Whitespace is skipped. const Whitespace = {' ', '\t', '\v', '\r', '\l', '\f'} @@ -96,7 +96,7 @@ proc decode*(s: string): string = var b = s[i+1].decodeByte var c = s[i+2].decodeByte var d = s[i+3].decodeByte - + result[r] = chr((a shl 2) and 0xff or ((b shr 4) and 0x03)) result[r+1] = chr((b shl 4) and 0xff or ((c shr 2) and 0x0F)) result[r+2] = chr((c shl 6) and 0xff or (d and 0x3F)) @@ -105,19 +105,19 @@ proc decode*(s: string): string = else: break assert i == s.len # adjust the length: - if i > 0 and s[i-1] == '=': + if i > 0 and s[i-1] == '=': dec(r) if i > 1 and s[i-2] == '=': dec(r) setLen(result, r) - + when isMainModule: assert encode("leasure.") == "bGVhc3VyZS4=" assert encode("easure.") == "ZWFzdXJlLg==" assert encode("asure.") == "YXN1cmUu" assert encode("sure.") == "c3VyZS4=" - + const longText = """Man is distinguished, not only by his reason, but by this - singular passion from other animals, which is a lust of the mind, + singular passion from other animals, which is a lust of the mind, that by a perseverance of delight in the continued and indefatigable generation of knowledge, exceeds the short vehemence of any carnal pleasure.""" diff --git a/lib/pure/basic2d.nim b/lib/pure/basic2d.nim index 1392fdeba..ad8f8653d 100644 --- a/lib/pure/basic2d.nim +++ b/lib/pure/basic2d.nim @@ -13,26 +13,26 @@ import strutils ## Basic 2d support with vectors, points, matrices and some basic utilities. ## Vectors are implemented as direction vectors, ie. when transformed with a matrix -## the translation part of matrix is ignored. +## the translation part of matrix is ignored. ## Operators `+` , `-` , `*` , `/` , `+=` , `-=` , `*=` and `/=` are implemented for vectors and scalars. ## ## Quick start example: -## +## ## # Create a matrix which first rotates, then scales and at last translates -## +## ## var m:Matrix2d=rotate(DEG90) & scale(2.0) & move(100.0,200.0) -## +## ## # Create a 2d point at (100,0) and a vector (5,2) -## -## var pt:Point2d=point2d(100.0,0.0) -## +## +## var pt:Point2d=point2d(100.0,0.0) +## ## var vec:Vector2d=vector2d(5.0,2.0) -## -## +## +## ## pt &= m # transforms pt in place -## +## ## var pt2:Point2d=pt & m #concatenates pt with m and returns a new point -## +## ## var vec2:Vector2d=vec & m #concatenates vec with m and returns a new vector @@ -64,12 +64,12 @@ type ## not used for geometric transformations in 2d. ax*,ay*,bx*,by*,tx*,ty*:float Point2d* = object - ## Implements a non-homogeneous 2d point stored as + ## Implements a non-homogeneous 2d point stored as ## an `x` coordinate and an `y` coordinate. x*,y*:float - Vector2d* = object - ## Implements a 2d **direction vector** stored as - ## an `x` coordinate and an `y` coordinate. Direction vector means, + Vector2d* = object + ## Implements a 2d **direction vector** stored as + ## an `x` coordinate and an `y` coordinate. Direction vector means, ## that when transforming a vector with a matrix, the translational ## part of the matrix is ignored. x*,y*:float @@ -78,7 +78,7 @@ type # Some forward declarations... proc matrix2d*(ax,ay,bx,by,tx,ty:float):Matrix2d {.noInit.} - ## Creates a new matrix. + ## Creates a new matrix. ## `ax`,`ay` is the local x axis ## `bx`,`by` is the local y axis ## `tx`,`ty` is the translation @@ -99,7 +99,7 @@ let YAXIS*:Vector2d=vector2d(0.0,1.0) ## Quick acces to an 2d y-axis unit vector - + # *************************************** # Private utils # *************************************** @@ -114,13 +114,13 @@ proc safeArccos(v:float):float= return arccos(clamp(v,-1.0,1.0)) -template makeBinOpVector(s:expr)= +template makeBinOpVector(s:expr)= ## implements binary operators + , - , * and / for vectors proc s*(a,b:Vector2d):Vector2d {.inline,noInit.} = vector2d(s(a.x,b.x),s(a.y,b.y)) proc s*(a:Vector2d,b:float):Vector2d {.inline,noInit.} = vector2d(s(a.x,b),s(a.y,b)) proc s*(a:float,b:Vector2d):Vector2d {.inline,noInit.} = vector2d(s(a,b.x),s(a,b.y)) - -template makeBinOpAssignVector(s:expr)= + +template makeBinOpAssignVector(s:expr)= ## implements inplace binary operators += , -= , /= and *= for vectors proc s*(a:var Vector2d,b:Vector2d) {.inline.} = s(a.x,b.x) ; s(a.y,b.y) proc s*(a:var Vector2d,b:float) {.inline.} = s(a.x,b) ; s(a.y,b) @@ -144,7 +144,7 @@ proc matrix2d*(ax,ay,bx,by,tx,ty:float):Matrix2d = proc `&`*(a,b:Matrix2d):Matrix2d {.noInit.} = #concatenate matrices ## Concatenates matrices returning a new matrix. - + # | a.AX a.AY 0 | | b.AX b.AY 0 | # | a.BX a.BY 0 | * | b.BX b.BY 0 | # | a.TX a.TY 1 | | b.TX b.TY 1 | @@ -153,7 +153,7 @@ proc `&`*(a,b:Matrix2d):Matrix2d {.noInit.} = #concatenate matrices a.ax * b.ay + a.ay * b.by, a.bx * b.ax + a.by * b.bx, a.bx * b.ay + a.by * b.by, - a.tx * b.ax + a.ty * b.bx + b.tx, + a.tx * b.ax + a.ty * b.bx + b.tx, a.tx * b.ay + a.ty * b.by + b.ty) @@ -169,13 +169,13 @@ proc stretch*(sx,sy:float):Matrix2d {.noInit.} = ## Returns new a stretch matrix, which is a ## scale matrix with non uniform scale in x and y. result.setElements(sx,0,0,sy,0,0) - + proc stretch*(sx,sy:float,org:Point2d):Matrix2d {.noInit.} = ## Returns a new stretch matrix, which is a ## scale matrix with non uniform scale in x and y. ## `org` is used as stretch origin. result.setElements(sx,0,0,sy,org.x-sx*org.x,org.y-sy*org.y) - + proc move*(dx,dy:float):Matrix2d {.noInit.} = ## Returns a new translation matrix. result.setElements(1,0,0,1,dx,dy) @@ -187,7 +187,7 @@ proc move*(v:Vector2d):Matrix2d {.noInit.} = proc rotate*(rad:float):Matrix2d {.noInit.} = ## Returns a new rotation matrix, which ## represents a rotation by `rad` radians - let + let s=sin(rad) c=cos(rad) result.setElements(c,s,-s,c,0,0) @@ -200,7 +200,7 @@ proc rotate*(rad:float,org:Point2d):Matrix2d {.noInit.} = s=sin(rad) c=cos(rad) result.setElements(c,s,-s,c,org.x+s*org.y-c*org.x,org.y-c*org.y-s*org.x) - + proc mirror*(v:Vector2d):Matrix2d {.noInit.} = ## Returns a new mirror matrix, mirroring ## around the line that passes through origo and @@ -211,7 +211,7 @@ proc mirror*(v:Vector2d):Matrix2d {.noInit.} = nd=1.0/(sqx+sqy) #used to normalize invector xy2=v.x*v.y*2.0*nd sqd=nd*(sqx-sqy) - + if nd==Inf or nd==NegInf: return IDMATRIX #mirroring around a zero vector is arbitrary=>just use identity @@ -230,7 +230,7 @@ proc mirror*(org:Point2d,v:Vector2d):Matrix2d {.noInit.} = nd=1.0/(sqx+sqy) #used to normalize invector xy2=v.x*v.y*2.0*nd sqd=nd*(sqx-sqy) - + if nd==Inf or nd==NegInf: return IDMATRIX #mirroring around a zero vector is arbitrary=>just use identity @@ -238,47 +238,47 @@ proc mirror*(org:Point2d,v:Vector2d):Matrix2d {.noInit.} = sqd,xy2, xy2,-sqd, org.x-org.y*xy2-org.x*sqd,org.y-org.x*xy2+org.y*sqd) - + proc skew*(xskew,yskew:float):Matrix2d {.noInit.} = - ## Returns a new skew matrix, which has its + ## Returns a new skew matrix, which has its ## x axis rotated `xskew` radians from the local x axis, and ## y axis rotated `yskew` radians from the local y axis result.setElements(cos(yskew),sin(yskew),-sin(xskew),cos(xskew),0,0) - + proc `$`* (t:Matrix2d):string {.noInit.} = ## Returns a string representation of the matrix return rtos(t.ax) & "," & rtos(t.ay) & - "," & rtos(t.bx) & "," & rtos(t.by) & + "," & rtos(t.bx) & "," & rtos(t.by) & "," & rtos(t.tx) & "," & rtos(t.ty) proc isUniform*(t:Matrix2d,tol=1.0e-6):bool= - ## Checks if the transform is uniform, that is + ## Checks if the transform is uniform, that is ## perpendicular axes of equal length, which means (for example) ## it cannot transform a circle into an ellipse. - ## `tol` is used as tolerance for both equal length comparison + ## `tol` is used as tolerance for both equal length comparison ## and perp. comparison. - + #dot product=0 means perpendicular coord. system: - if abs(t.ax*t.bx+t.ay*t.by)<=tol: + if abs(t.ax*t.bx+t.ay*t.by)<=tol: #subtract squared lengths of axes to check if uniform scaling: if abs((t.ax*t.ax+t.ay*t.ay)-(t.bx*t.bx+t.by*t.by))<=tol: return true return false - + proc determinant*(t:Matrix2d):float= ## Computes the determinant of the matrix. - + #NOTE: equivalent with perp.dot product for two 2d vectors - return t.ax*t.by-t.bx*t.ay + return t.ax*t.by-t.bx*t.ay proc isMirroring* (m:Matrix2d):bool= ## Checks if the `m` is a mirroring matrix, ## which means it will reverse direction of a curve transformed with it return m.determinant<0.0 - + proc inverse*(m:Matrix2d):Matrix2d {.noInit.} = ## Returns a new matrix, which is the inverse of the matrix ## If the matrix is not invertible (determinant=0), an EDivByZero @@ -286,7 +286,7 @@ proc inverse*(m:Matrix2d):Matrix2d {.noInit.} = let d=m.determinant if d==0.0: raise newException(DivByZeroError,"Cannot invert a zero determinant matrix") - + result.setElements( m.by/d,-m.ay/d, -m.bx/d,m.ax/d, @@ -296,14 +296,14 @@ proc inverse*(m:Matrix2d):Matrix2d {.noInit.} = proc equals*(m1:Matrix2d,m2:Matrix2d,tol=1.0e-6):bool= ## Checks if all elements of `m1`and `m2` is equal within ## a given tolerance `tol`. - return + return abs(m1.ax-m2.ax)<=tol and abs(m1.ay-m2.ay)<=tol and abs(m1.bx-m2.bx)<=tol and abs(m1.by-m2.by)<=tol and abs(m1.tx-m2.tx)<=tol and abs(m1.ty-m2.ty)<=tol - + proc `=~`*(m1,m2:Matrix2d):bool= ## Checks if `m1`and `m2` is approximately equal, using a ## tolerance of 1e-6. @@ -350,16 +350,16 @@ proc slopeVector2d*(slope:float,len:float):Vector2d {.noInit.} = proc len*(v:Vector2d):float {.inline.}= ## Returns the length of the vector. sqrt(v.x*v.x+v.y*v.y) - + proc `len=`*(v:var Vector2d,newlen:float) {.noInit.} = ## Sets the length of the vector, keeping its angle. let fac=newlen/v.len - + if newlen==0.0: v.x=0.0 v.y=0.0 return - + if fac==Inf or fac==NegInf: #to short for float accuracy #do as good as possible: @@ -368,30 +368,30 @@ proc `len=`*(v:var Vector2d,newlen:float) {.noInit.} = else: v.x*=fac v.y*=fac - + proc sqrLen*(v:Vector2d):float {.inline.}= ## Computes the squared length of the vector, which is ## faster than computing the absolute length. v.x*v.x+v.y*v.y - + proc angle*(v:Vector2d):float= - ## Returns the angle of the vector. + ## Returns the angle of the vector. ## (The counter clockwise plane angle between posetive x axis and `v`) result=arctan2(v.y,v.x) if result<0.0: result+=DEG360 - + proc `$` *(v:Vector2d):string= ## String representation of `v` result=rtos(v.x) result.add(",") result.add(rtos(v.y)) - - + + proc `&` *(v:Vector2d,m:Matrix2d):Vector2d {.noInit.} = ## Concatenate vector `v` with a transformation matrix. ## Transforming a vector ignores the translational part ## of the matrix. - + # | AX AY 0 | # | X Y 1 | * | BX BY 0 | # | 0 0 1 | @@ -403,7 +403,7 @@ proc `&=`*(v:var Vector2d,m:Matrix2d) {.inline.}= ## Applies transformation `m` onto `v` in place. ## Transforming a vector ignores the translational part ## of the matrix. - + # | AX AY 0 | # | X Y 1 | * | BX BY 0 | # | 0 0 1 | @@ -412,31 +412,31 @@ proc `&=`*(v:var Vector2d,m:Matrix2d) {.inline.}= v.x=newx -proc tryNormalize*(v:var Vector2d):bool= +proc tryNormalize*(v:var Vector2d):bool= ## Modifies `v` to have a length of 1.0, keeping its angle. - ## If `v` has zero length (and thus no angle), it is left unmodified and + ## If `v` has zero length (and thus no angle), it is left unmodified and ## false is returned, otherwise true is returned. let mag=v.len if mag==0.0: return false - + v.x/=mag v.y/=mag return true -proc normalize*(v:var Vector2d) {.inline.}= +proc normalize*(v:var Vector2d) {.inline.}= ## Modifies `v` to have a length of 1.0, keeping its angle. ## If `v` has zero length, an EDivByZero will be raised. if not tryNormalize(v): raise newException(DivByZeroError,"Cannot normalize zero length vector") - + proc transformNorm*(v:var Vector2d,t:Matrix2d)= ## Applies a normal direction transformation `t` onto `v` in place. - ## The resulting vector is *not* normalized. Transforming a vector ignores the - ## translational part of the matrix. If the matrix is not invertible + ## The resulting vector is *not* normalized. Transforming a vector ignores the + ## translational part of the matrix. If the matrix is not invertible ## (determinant=0), an EDivByZero will be raised. # transforming a normal is done by transforming @@ -469,16 +469,16 @@ proc transformInv*(v:var Vector2d,t:Matrix2d)= proc transformNormInv*(v:var Vector2d,t:Matrix2d)= ## Applies an inverse normal direction transformation `t` onto `v` in place. - ## This is faster than creating an inverse - ## matrix and transformNorm(...) it. Transforming a vector ignores the + ## This is faster than creating an inverse + ## matrix and transformNorm(...) it. Transforming a vector ignores the ## translational part of the matrix. # normal inverse transform is done by transforming # by the inverse of the transpose of the inverse of the org. matrix # which is equivalent with transforming with the transpose. # | | | AX AY 0 |^-1|^T|^-1 | AX BX 0 | - # | X Y 1 | * | | | BX BY 0 | | | = | X Y 1 | * | AY BY 0 | - # | | | 0 0 1 | | | | 0 0 1 | + # | X Y 1 | * | | | BX BY 0 | | | = | X Y 1 | * | AY BY 0 | + # | | | 0 0 1 | | | | 0 0 1 | # This can be heavily reduced to: let newx=t.ay*v.y+t.ax*v.x v.y=t.by*v.y+t.bx*v.x @@ -489,19 +489,19 @@ proc rotate90*(v:var Vector2d) {.inline.}= ## without using any trigonometrics. swap(v.x,v.y) v.x= -v.x - + proc rotate180*(v:var Vector2d){.inline.}= ## Quickly rotates vector `v` 180 degrees counter clockwise, ## without using any trigonometrics. v.x= -v.x v.y= -v.y - + proc rotate270*(v:var Vector2d) {.inline.}= ## Quickly rotates vector `v` 270 degrees counter clockwise, ## without using any trigonometrics. swap(v.x,v.y) v.y= -v.y - + proc rotate*(v:var Vector2d,rad:float) = ## Rotates vector `v` `rad` radians in place. let @@ -510,18 +510,18 @@ proc rotate*(v:var Vector2d,rad:float) = newx=c*v.x-s*v.y v.y=c*v.y+s*v.x v.x=newx - + proc scale*(v:var Vector2d,fac:float){.inline.}= ## Scales vector `v` `rad` radians in place. v.x*=fac v.y*=fac - + proc stretch*(v:var Vector2d,facx,facy:float){.inline.}= ## Stretches vector `v` `facx` times horizontally, ## and `facy` times vertically. v.x*=facx v.y*=facy - + proc mirror*(v:var Vector2d,mirrvec:Vector2d)= ## Mirrors vector `v` using `mirrvec` as mirror direction. let @@ -530,20 +530,20 @@ proc mirror*(v:var Vector2d,mirrvec:Vector2d)= nd=1.0/(sqx+sqy) #used to normalize invector xy2=mirrvec.x*mirrvec.y*2.0*nd sqd=nd*(sqx-sqy) - + if nd==Inf or nd==NegInf: return #mirroring around a zero vector is arbitrary=>keep as is is fastest - + let newx=xy2*v.y+sqd*v.x v.y=v.x*xy2-sqd*v.y v.x=newx - - + + proc `-` *(v:Vector2d):Vector2d= ## Negates a vector result.x= -v.x result.y= -v.y - + # declare templated binary operators makeBinOpVector(`+`) makeBinOpVector(`-`) @@ -556,27 +556,27 @@ makeBinOpAssignVector(`/=`) proc dot*(v1,v2:Vector2d):float= - ## Computes the dot product of two vectors. + ## Computes the dot product of two vectors. ## Returns 0.0 if the vectors are perpendicular. return v1.x*v2.x+v1.y*v2.y - + proc cross*(v1,v2:Vector2d):float= ## Computes the cross product of two vectors, also called ## the 'perpendicular dot product' in 2d. Returns 0.0 if the vectors ## are parallel. return v1.x*v2.y-v1.y*v2.x - + proc equals*(v1,v2:Vector2d,tol=1.0e-6):bool= ## Checks if two vectors approximately equals with a tolerance. return abs(v2.x-v1.x)<=tol and abs(v2.y-v1.y)<=tol - + proc `=~` *(v1,v2:Vector2d):bool= - ## Checks if two vectors approximately equals with a + ## Checks if two vectors approximately equals with a ## hardcoded tolerance 1e-6 equals(v1,v2) - + proc angleTo*(v1,v2:Vector2d):float= - ## Returns the smallest of the two possible angles + ## Returns the smallest of the two possible angles ## between `v1` and `v2` in radians. var nv1=v1 @@ -584,7 +584,7 @@ proc angleTo*(v1,v2:Vector2d):float= if not nv1.tryNormalize or not nv2.tryNormalize: return 0.0 # zero length vector has zero angle to any other vector return safeArccos(dot(nv1,nv2)) - + proc angleCCW*(v1,v2:Vector2d):float= ## Returns the counter clockwise plane angle from `v1` to `v2`, ## in range 0 - 2*PI @@ -592,7 +592,7 @@ proc angleCCW*(v1,v2:Vector2d):float= if v1.cross(v2)>=0.0: return a return DEG360-a - + proc angleCW*(v1,v2:Vector2d):float= ## Returns the clockwise plane angle from `v1` to `v2`, ## in range 0 - 2*PI @@ -612,32 +612,32 @@ proc turnAngle*(v1,v2:Vector2d):float= proc bisect*(v1,v2:Vector2d):Vector2d {.noInit.}= ## Computes the bisector between v1 and v2 as a normalized vector. ## If one of the input vectors has zero length, a normalized version - ## of the other is returned. If both input vectors has zero length, + ## of the other is returned. If both input vectors has zero length, ## an arbitrary normalized vector is returned. var vmag1=v1.len vmag2=v2.len - + # zero length vector equals arbitrary vector, just change to magnitude to one to # avoid zero division - if vmag1==0.0: + if vmag1==0.0: if vmag2==0: #both are zero length return any normalized vector return XAXIS vmag1=1.0 - if vmag2==0.0: vmag2=1.0 - + if vmag2==0.0: vmag2=1.0 + let x1=v1.x/vmag1 y1=v1.y/vmag1 x2=v2.x/vmag2 y2=v2.y/vmag2 - + result.x=(x1 + x2) * 0.5 result.y=(y1 + y2) * 0.5 - + if not result.tryNormalize(): # This can happen if vectors are colinear. In this special case - # there are actually two bisectors, we select just + # there are actually two bisectors, we select just # one of them (x1,y1 rotated 90 degrees ccw). result.x = -y1 result.y = x1 @@ -651,13 +651,13 @@ proc bisect*(v1,v2:Vector2d):Vector2d {.noInit.}= proc point2d*(x,y:float):Point2d = result.x=x result.y=y - + proc sqrDist*(a,b:Point2d):float= ## Computes the squared distance between `a` and `b` let dx=b.x-a.x let dy=b.y-a.y result=dx*dx+dy*dy - + proc dist*(a,b:Point2d):float {.inline.}= ## Computes the absolute distance between `a` and `b` result=sqrt(sqrDist(a,b)) @@ -675,11 +675,11 @@ proc `$` *(p:Point2d):string= result=rtos(p.x) result.add(",") result.add(rtos(p.y)) - + proc `&`*(p:Point2d,t:Matrix2d):Point2d {.noInit,inline.} = ## Concatenates a point `p` with a transform `t`, ## resulting in a new, transformed point. - + # | AX AY 0 | # | X Y 1 | * | BX BY 0 | # | TX TY 1 | @@ -697,21 +697,21 @@ proc transformInv*(p:var Point2d,t:Matrix2d){.inline.}= ## Applies the inverse of transformation `t` onto `p` in place. ## If the matrix is not invertable (determinant=0) , EDivByZero will ## be raised. - + # | AX AY 0 | ^-1 # | X Y 1 | * | BX BY 0 | # | TX TY 1 | let d=t.determinant if d==0.0: raise newException(DivByZeroError,"Cannot invert a zero determinant matrix") - let + let newx= (t.bx*t.ty-t.by*t.tx+p.x*t.by-p.y*t.bx)/d p.y = -(t.ax*t.ty-t.ay*t.tx+p.x*t.ay-p.y*t.ax)/d p.x=newx - - + + proc `+`*(p:Point2d,v:Vector2d):Point2d {.noInit,inline.} = - ## Adds a vector `v` to a point `p`, resulting + ## Adds a vector `v` to a point `p`, resulting ## in a new point. result.x=p.x+v.x result.y=p.y+v.y @@ -722,7 +722,7 @@ proc `+=`*(p:var Point2d,v:Vector2d) {.noInit,inline.} = p.y+=v.y proc `-`*(p:Point2d,v:Vector2d):Point2d {.noInit,inline.} = - ## Subtracts a vector `v` from a point `p`, resulting + ## Subtracts a vector `v` from a point `p`, resulting ## in a new point. result.x=p.x-v.x result.y=p.y-v.y @@ -736,13 +736,13 @@ proc `-=`*(p:var Point2d,v:Vector2d) {.noInit,inline.} = ## Subtracts a vector `v` from a point `p` in place. p.x-=v.x p.y-=v.y - + proc equals(p1,p2:Point2d,tol=1.0e-6):bool {.inline.}= ## Checks if two points approximately equals with a tolerance. return abs(p2.x-p1.x)<=tol and abs(p2.y-p1.y)<=tol proc `=~`*(p1,p2:Point2d):bool {.inline.}= - ## Checks if two vectors approximately equals with a + ## Checks if two vectors approximately equals with a ## hardcoded tolerance 1e-6 equals(p1,p2) @@ -759,7 +759,7 @@ proc rotate*(p:var Point2d,rad:float)= newx=p.x*c-p.y*s p.y=p.y*c+p.x*s p.x=newx - + proc rotate*(p:var Point2d,rad:float,org:Point2d)= ## Rotates a point in place `rad` radians using `org` as ## center of rotation. @@ -769,25 +769,25 @@ proc rotate*(p:var Point2d,rad:float,org:Point2d)= newx=(p.x - org.x) * c - (p.y - org.y) * s + org.x p.y=(p.y - org.y) * c + (p.x - org.x) * s + org.y p.x=newx - + proc scale*(p:var Point2d,fac:float) {.inline.}= ## Scales a point in place `fac` times with world origo as origin. p.x*=fac p.y*=fac - + proc scale*(p:var Point2d,fac:float,org:Point2d){.inline.}= ## Scales the point in place `fac` times with `org` as origin. p.x=(p.x - org.x) * fac + org.x p.y=(p.y - org.y) * fac + org.y proc stretch*(p:var Point2d,facx,facy:float){.inline.}= - ## Scales a point in place non uniformly `facx` and `facy` times with + ## Scales a point in place non uniformly `facx` and `facy` times with ## world origo as origin. p.x*=facx p.y*=facy proc stretch*(p:var Point2d,facx,facy:float,org:Point2d){.inline.}= - ## Scales the point in place non uniformly `facx` and `facy` times with + ## Scales the point in place non uniformly `facx` and `facy` times with ## `org` as origin. p.x=(p.x - org.x) * facx + org.x p.y=(p.y - org.y) * facy + org.y @@ -814,21 +814,21 @@ proc area*(a,b,c:Point2d):float= return abs(sgnArea(a,b,c)) proc closestPoint*(p:Point2d,pts:varargs[Point2d]):Point2d= - ## Returns a point selected from `pts`, that has the closest + ## Returns a point selected from `pts`, that has the closest ## euclidean distance to `p` assert(pts.len>0) # must have at least one point - - var + + var bestidx=0 bestdist=p.sqrDist(pts[0]) curdist:float - + for idx in 1..high(pts): curdist=p.sqrDist(pts[idx]) if curdist<bestdist: bestidx=idx bestdist=curdist - + result=pts[bestidx] @@ -843,7 +843,7 @@ proc normAngle*(ang:float):float= return ang return ang mod DEG360 - + proc degToRad*(deg:float):float {.inline.}= ## converts `deg` degrees to radians deg / RAD2DEGCONST @@ -852,4 +852,4 @@ proc radToDeg*(rad:float):float {.inline.}= ## converts `rad` radians to degrees rad * RAD2DEGCONST - + diff --git a/lib/pure/collections/LockFreeHash.nim b/lib/pure/collections/LockFreeHash.nim index 1758c5b1a..1d4471b21 100644 --- a/lib/pure/collections/LockFreeHash.nim +++ b/lib/pure/collections/LockFreeHash.nim @@ -6,32 +6,32 @@ import unsigned, math, hashes ## Memory Utility Functions proc newHeap*[T](): ptr T = - result = cast[ptr T](alloc0(sizeof(T))) + result = cast[ptr T](alloc0(sizeof(T))) proc copyNew*[T](x: var T): ptr T = - var - size = sizeof(T) - mem = alloc(size) - copyMem(mem, x.addr, size) + var + size = sizeof(T) + mem = alloc(size) + copyMem(mem, x.addr, size) return cast[ptr T](mem) -proc copyTo*[T](val: var T, dest: int) = - copyMem(pointer(dest), val.addr, sizeof(T)) +proc copyTo*[T](val: var T, dest: int) = + copyMem(pointer(dest), val.addr, sizeof(T)) -proc allocType*[T](): pointer = alloc(sizeof(T)) +proc allocType*[T](): pointer = alloc(sizeof(T)) proc newShared*[T](): ptr T = - result = cast[ptr T](allocShared0(sizeof(T))) + result = cast[ptr T](allocShared0(sizeof(T))) proc copyShared*[T](x: var T): ptr T = - var - size = sizeof(T) - mem = allocShared(size) - copyMem(mem, x.addr, size) + var + size = sizeof(T) + mem = allocShared(size) + copyMem(mem, x.addr, size) return cast[ptr T](mem) #------------------------------------------------------------------------------ -## Pointer arithmetic +## Pointer arithmetic proc `+`*(p: pointer, i: int): pointer {.inline.} = cast[pointer](cast[int](p) + i) @@ -41,11 +41,11 @@ const reProbeLimit = 12 minCopyWork = 4096 intSize = sizeof(int) - + when sizeof(int) == 4: # 32bit - type + type Raw = range[0..1073741823] ## The range of uint values that can be stored directly in a value slot ## when on a 32 bit platform @@ -56,40 +56,40 @@ elif sizeof(int) == 8: # 64bit ## The range of uint values that can be stored directly in a value slot ## when on a 64 bit platform {.deprecated: [TRaw: Raw].} -else: +else: {.error: "unsupported platform".} - -type + +type Entry = tuple key: int value: int EntryArr = ptr array[0..10_000_000, Entry] - + PConcTable[K,V] = ptr object {.pure.} len: int used: int active: int copyIdx: int copyDone: int - next: PConcTable[K,V] + next: PConcTable[K,V] data: EntryArr {.deprecated: [TEntry: Entry, TEntryArr: EntryArr.} proc setVal[K,V](table: var PConcTable[K,V], key: int, val: int, - expVal: int, match: bool): int + expVal: int, match: bool): int #------------------------------------------------------------------------------ # Create a new table proc newLFTable*[K,V](size: int = minTableSize): PConcTable[K,V] = - let - dataLen = max(nextPowerOfTwo(size), minTableSize) - dataSize = dataLen*sizeof(Entry) - dataMem = allocShared0(dataSize) + let + dataLen = max(nextPowerOfTwo(size), minTableSize) + dataSize = dataLen*sizeof(Entry) + dataMem = allocShared0(dataSize) tableSize = 7 * intSize tableMem = allocShared0(tableSize) - table = cast[PConcTable[K,V]](tableMem) + table = cast[PConcTable[K,V]](tableMem) table.len = dataLen table.used = 0 table.active = 0 @@ -99,14 +99,14 @@ proc newLFTable*[K,V](size: int = minTableSize): PConcTable[K,V] = table.data = cast[EntryArr](dataMem) result = table -#------------------------------------------------------------------------------ +#------------------------------------------------------------------------------ # Delete a table proc deleteConcTable[K,V](tbl: PConcTable[K,V]) = - deallocShared(tbl.data) + deallocShared(tbl.data) deallocShared(tbl) -#------------------------------------------------------------------------------ +#------------------------------------------------------------------------------ proc `[]`[K,V](table: var PConcTable[K,V], i: int): var Entry {.inline.} = table.data[i] @@ -119,15 +119,15 @@ proc pack[T](x: T): int {.inline.} = result = (cast[int](x) shl 2) #echo("packKey ",cast[int](x) , " -> ", result) -# Pop the flags off returning a 4 byte aligned ptr to our Key or Val +# Pop the flags off returning a 4 byte aligned ptr to our Key or Val proc pop(x: int): int {.inline.} = result = x and 0xFFFFFFFC'i32 -# Pop the raw value off of our Key or Val +# Pop the raw value off of our Key or Val proc popRaw(x: int): int {.inline.} = - result = x shr 2 + result = x shr 2 -# Pop the flags off returning a 4 byte aligned ptr to our Key or Val +# Pop the flags off returning a 4 byte aligned ptr to our Key or Val proc popPtr[V](x: int): ptr V {.inline.} = result = cast[ptr V](pop(x)) #echo("popPtr " & $x & " -> " & $cast[int](result)) @@ -136,34 +136,34 @@ proc popPtr[V](x: int): ptr V {.inline.} = # K or V is no longer valid use new table const Ghost = 0xFFFFFFFC proc isGhost(x: int): bool {.inline.} = - result = x == 0xFFFFFFFC + result = x == 0xFFFFFFFC -# Tombstone -# applied to V = K is dead -proc isTomb(x: int): bool {.inline.} = +# Tombstone +# applied to V = K is dead +proc isTomb(x: int): bool {.inline.} = result = (x and 0x00000002) != 0 proc setTomb(x: int): int {.inline.} = result = x or 0x00000002 # Prime -# K or V is in new table copied from old -proc isPrime(x: int): bool {.inline.} = +# K or V is in new table copied from old +proc isPrime(x: int): bool {.inline.} = result = (x and 0x00000001) != 0 proc setPrime(x: int): int {.inline.} = result = x or 0x00000001 -#------------------------------------------------------------------------------ +#------------------------------------------------------------------------------ ##This is for i32 only need to override for i64 -proc hashInt(x: int):int {.inline.} = - var h = uint32(x) #shr 2'u32 +proc hashInt(x: int):int {.inline.} = + var h = uint32(x) #shr 2'u32 h = h xor (h shr 16'u32) h *= 0x85ebca6b'u32 h = h xor (h shr 13'u32) h *= 0xc2b2ae35'u32 - h = h xor (h shr 16'u32) + h = h xor (h shr 16'u32) result = int(h) #------------------------------------------------------------------------------ @@ -175,31 +175,31 @@ proc resize[K,V](self: PConcTable[K,V]): PConcTable[K,V] = #echo("A new table already exists, copy in progress") return next var - oldLen = atomic_load_n(self.len.addr, ATOMIC_RELAXED) + oldLen = atomic_load_n(self.len.addr, ATOMIC_RELAXED) newTable = newLFTable[K,V](oldLen*2) success = atomic_compare_exchange_n(self.next.addr, next.addr, newTable, false, ATOMIC_RELAXED, ATOMIC_RELAXED) if not success: echo("someone beat us to it! delete table we just created and return his " & $cast[int](next)) - deleteConcTable(newTable) - return next + deleteConcTable(newTable) + return next else: echo("Created New Table! " & $cast[int](newTable) & " Size = " & $newTable.len) return newTable - + #------------------------------------------------------------------------------ -#proc keyEQ[K](key1: ptr K, key2: ptr K): bool {.inline.} = -proc keyEQ[K](key1: int, key2: int): bool {.inline.} = +#proc keyEQ[K](key1: ptr K, key2: ptr K): bool {.inline.} = +proc keyEQ[K](key1: int, key2: int): bool {.inline.} = result = false when K is Raw: - if key1 == key2: + if key1 == key2: result = true else: - var + var p1 = popPtr[K](key1) p2 = popPtr[K](key2) - if p1 != nil and p2 != nil: + if p1 != nil and p2 != nil: if cast[int](p1) == cast[int](p2): return true if p1[] == p2[]: @@ -214,53 +214,53 @@ proc keyEQ[K](key1: int, key2: int): bool {.inline.} = proc copySlot[K,V](idx: int, oldTbl: var PConcTable[K,V], newTbl: var PConcTable[K,V]): bool = #echo("Copy idx " & $idx) - var + var oldVal = 0 - oldkey = 0 + oldkey = 0 ok = false result = false #Block the key so no other threads waste time here while not ok: - ok = atomic_compare_exchange_n(oldTbl[idx].key.addr, oldKey.addr, + ok = atomic_compare_exchange_n(oldTbl[idx].key.addr, oldKey.addr, setTomb(oldKey), false, ATOMIC_RELAXED, ATOMIC_RELAXED) - #echo("oldKey was = " & $oldKey & " set it to tomb " & $setTomb(oldKey)) - #Prevent new values from appearing in the old table by priming + #echo("oldKey was = " & $oldKey & " set it to tomb " & $setTomb(oldKey)) + #Prevent new values from appearing in the old table by priming oldVal = atomic_load_n(oldTbl[idx].value.addr, ATOMIC_RELAXED) while not isPrime(oldVal): - var box = if oldVal == 0 or isTomb(oldVal) : oldVal.setTomb.setPrime - else: oldVal.setPrime - if atomic_compare_exchange_n(oldTbl[idx].value.addr, oldVal.addr, + var box = if oldVal == 0 or isTomb(oldVal) : oldVal.setTomb.setPrime + else: oldVal.setPrime + if atomic_compare_exchange_n(oldTbl[idx].value.addr, oldVal.addr, box, false, ATOMIC_RELAXED, ATOMIC_RELAXED): - if isPrime(box) and isTomb(box): + if isPrime(box) and isTomb(box): return true oldVal = box break #echo("oldVal was = ", oldVal, " set it to prime ", box) - if isPrime(oldVal) and isTomb(oldVal): + if isPrime(oldVal) and isTomb(oldVal): #when not (K is Raw): - # deallocShared(popPtr[K](oldKey)) + # deallocShared(popPtr[K](oldKey)) return false - if isTomb(oldVal): + if isTomb(oldVal): echo("oldVal is Tomb!!!, should not happen") - if pop(oldVal) != 0: + if pop(oldVal) != 0: result = setVal(newTbl, pop(oldKey), pop(oldVal), 0, true) == 0 - if result: - #echo("Copied a Slot! idx= " & $idx & " key= " & $oldKey & " val= " & $oldVal) - else: - #echo("copy slot failed") + if result: + #echo("Copied a Slot! idx= " & $idx & " key= " & $oldKey & " val= " & $oldVal) + else: + #echo("copy slot failed") # Our copy is done so we disable the old slot while not ok: - ok = atomic_compare_exchange_n(oldTbl[idx].value.addr, oldVal.addr, + ok = atomic_compare_exchange_n(oldTbl[idx].value.addr, oldVal.addr, oldVal.setTomb.setPrime , false, ATOMIC_RELAXED, ATOMIC_RELAXED) - #echo("disabled old slot") - #echo"---------------------" + #echo("disabled old slot") + #echo"---------------------" #------------------------------------------------------------------------------ proc promote[K,V](table: var PConcTable[K,V]) = var newData = atomic_load_n(table.next.data.addr, ATOMIC_RELAXED) - newLen = atomic_load_n(table.next.len.addr, ATOMIC_RELAXED) + newLen = atomic_load_n(table.next.len.addr, ATOMIC_RELAXED) newUsed = atomic_load_n(table.next.used.addr, ATOMIC_RELAXED) deallocShared(table.data) @@ -270,52 +270,52 @@ proc promote[K,V](table: var PConcTable[K,V]) = atomic_store_n(table.copyIdx.addr, 0, ATOMIC_RELAXED) atomic_store_n(table.copyDone.addr, 0, ATOMIC_RELAXED) deallocShared(table.next) - atomic_store_n(table.next.addr, nil, ATOMIC_RELAXED) + atomic_store_n(table.next.addr, nil, ATOMIC_RELAXED) echo("new table swapped!") #------------------------------------------------------------------------------ - -proc checkAndPromote[K,V](table: var PConcTable[K,V], workDone: int): bool = - var + +proc checkAndPromote[K,V](table: var PConcTable[K,V], workDone: int): bool = + var oldLen = atomic_load_n(table.len.addr, ATOMIC_RELAXED) copyDone = atomic_load_n(table.copyDone.addr, ATOMIC_RELAXED) ok: bool - result = false + result = false if workDone > 0: #echo("len to copy =" & $oldLen) - #echo("copyDone + workDone = " & $copyDone & " + " & $workDone) + #echo("copyDone + workDone = " & $copyDone & " + " & $workDone) while not ok: - ok = atomic_compare_exchange_n(table.copyDone.addr, copyDone.addr, + ok = atomic_compare_exchange_n(table.copyDone.addr, copyDone.addr, copyDone + workDone, false, ATOMIC_RELAXED, ATOMIC_RELAXED) - #if ok: echo("set copyDone") - # If the copy is done we can promote this table + #if ok: echo("set copyDone") + # If the copy is done we can promote this table if copyDone + workDone >= oldLen: # Swap new data - #echo("work is done!") + #echo("work is done!") table.promote result = true - + #------------------------------------------------------------------------------ proc copySlotAndCheck[K,V](table: var PConcTable[K,V], idx: int): PConcTable[K,V] = var newTable = cast[PConcTable[K,V]](atomic_load_n(table.next.addr, ATOMIC_RELAXED)) - result = newTable - if newTable != nil and copySlot(idx, table, newTable): - #echo("copied a single slot, idx = " & $idx) + result = newTable + if newTable != nil and copySlot(idx, table, newTable): + #echo("copied a single slot, idx = " & $idx) if checkAndPromote(table, 1): return table - + #------------------------------------------------------------------------------ - + proc helpCopy[K,V](table: var PConcTable[K,V]): PConcTable[K,V] = var - newTable = cast[PConcTable[K,V]](atomic_load_n(table.next.addr, ATOMIC_RELAXED)) - result = newTable - if newTable != nil: - var - oldLen = atomic_load_n(table.len.addr, ATOMIC_RELAXED) + newTable = cast[PConcTable[K,V]](atomic_load_n(table.next.addr, ATOMIC_RELAXED)) + result = newTable + if newTable != nil: + var + oldLen = atomic_load_n(table.len.addr, ATOMIC_RELAXED) copyDone = atomic_load_n(table.copyDone.addr, ATOMIC_RELAXED) copyIdx = 0 work = min(oldLen, minCopyWork) @@ -324,54 +324,54 @@ proc helpCopy[K,V](table: var PConcTable[K,V]): PConcTable[K,V] = if copyDone < oldLen: var ok: bool while not ok: - ok = atomic_compare_exchange_n(table.copyIdx.addr, copyIdx.addr, + ok = atomic_compare_exchange_n(table.copyIdx.addr, copyIdx.addr, copyIdx + work, false, ATOMIC_RELAXED, ATOMIC_RELAXED) - #echo("copy idx = ", copyIdx) + #echo("copy idx = ", copyIdx) for i in 0..work-1: - var idx = (copyIdx + i) and (oldLen - 1) + var idx = (copyIdx + i) and (oldLen - 1) if copySlot(idx, table, newTable): workDone += 1 if workDone > 0: #echo("did work ", workDone, " on thread ", cast[int](myThreadID[pointer]())) if checkAndPromote(table, workDone): return table - # In case a thread finished all the work then got stalled before promotion + # In case a thread finished all the work then got stalled before promotion if checkAndPromote(table, 0): return table - - + + #------------------------------------------------------------------------------ proc setVal[K,V](table: var PConcTable[K,V], key: int, val: int, expVal: int, match: bool): int = - #echo("-try set- in table ", " key = ", (popPtr[K](key)[]), " val = ", val) - when K is Raw: - var idx = hashInt(key) + #echo("-try set- in table ", " key = ", (popPtr[K](key)[]), " val = ", val) + when K is Raw: + var idx = hashInt(key) else: - var idx = popPtr[K](key)[].hash - var - nextTable: PConcTable[K,V] + var idx = popPtr[K](key)[].hash + var + nextTable: PConcTable[K,V] probes = 1 - # spin until we find a key slot or build and jump to next table - while true: - idx = idx and (table.len - 1) + # spin until we find a key slot or build and jump to next table + while true: + idx = idx and (table.len - 1) #echo("try set idx = " & $idx & "for" & $key) var - probedKey = 0 - openKey = atomic_compare_exchange_n(table[idx].key.addr, probedKey.addr, - key, false, ATOMIC_RELAXED, ATOMIC_RELAXED) + probedKey = 0 + openKey = atomic_compare_exchange_n(table[idx].key.addr, probedKey.addr, + key, false, ATOMIC_RELAXED, ATOMIC_RELAXED) if openKey: if val.isTomb: #echo("val was tomb, bail, no reason to set an open slot to tomb") return val - #increment used slots - #echo("found an open slot, total used = " & + #increment used slots + #echo("found an open slot, total used = " & #$atomic_add_fetch(table.used.addr, 1, ATOMIC_RELAXED)) discard atomic_add_fetch(table.used.addr, 1, ATOMIC_RELAXED) - break # We found an open slot - #echo("set idx ", idx, " key = ", key, " probed = ", probedKey) + break # We found an open slot + #echo("set idx ", idx, " key = ", key, " probed = ", probedKey) if keyEQ[K](probedKey, key): - #echo("we found the matching slot") - break # We found a matching slot + #echo("we found the matching slot") + break # We found a matching slot if (not(expVal != 0 and match)) and (probes >= reProbeLimit or key.isTomb): if key.isTomb: echo("Key is Tombstone") #if probes >= reProbeLimit: echo("Too much probing " & $probes) @@ -379,22 +379,22 @@ proc setVal[K,V](table: var PConcTable[K,V], key: int, val: int, #create next bigger table nextTable = resize(table) #help do some copying - #echo("help copy old table to new") - nextTable = helpCopy(table) + #echo("help copy old table to new") + nextTable = helpCopy(table) #now setVal in the new table instead - #echo("jumping to next table to set val") - return setVal(nextTable, key, val, expVal, match) + #echo("jumping to next table to set val") + return setVal(nextTable, key, val, expVal, match) else: idx += 1 probes += 1 # Done spinning for a new slot - var oldVal = atomic_load_n(table[idx].value.addr, ATOMIC_RELAXED) + var oldVal = atomic_load_n(table[idx].value.addr, ATOMIC_RELAXED) if val == oldVal: - #echo("this val is alredy in the slot") + #echo("this val is alredy in the slot") return oldVal - nextTable = atomic_load_n(table.next.addr, ATOMIC_SEQ_CST) - if nextTable == nil and - ((oldVal == 0 and + nextTable = atomic_load_n(table.next.addr, ATOMIC_SEQ_CST) + if nextTable == nil and + ((oldVal == 0 and (probes >= reProbeLimit or table.used / table.len > 0.8)) or (isPrime(oldVal))): if table.used / table.len > 0.8: echo("resize because usage ratio = " & @@ -402,7 +402,7 @@ proc setVal[K,V](table: var PConcTable[K,V], key: int, val: int, if isPrime(oldVal): echo("old val isPrime, should be a rare mem ordering event") nextTable = resize(table) if nextTable != nil: - #echo("tomb old slot then set in new table") + #echo("tomb old slot then set in new table") nextTable = copySlotAndCheck(table,idx) return setVal(nextTable, key, val, expVal, match) # Finally ready to add new val to table @@ -410,7 +410,7 @@ proc setVal[K,V](table: var PConcTable[K,V], key: int, val: int, if match and oldVal != expVal: #echo("set failed, no match oldVal= " & $oldVal & " expVal= " & $expVal) return oldVal - if atomic_compare_exchange_n(table[idx].value.addr, oldVal.addr, + if atomic_compare_exchange_n(table[idx].value.addr, oldVal.addr, val, false, ATOMIC_RELEASE, ATOMIC_RELAXED): #echo("val set at table " & $cast[int](table)) if expVal != 0: @@ -427,48 +427,48 @@ proc setVal[K,V](table: var PConcTable[K,V], key: int, val: int, #------------------------------------------------------------------------------ -proc getVal[K,V](table: var PConcTable[K,V], key: int): int = +proc getVal[K,V](table: var PConcTable[K,V], key: int): int = #echo("-try get- key = " & $key) - when K is Raw: + when K is Raw: var idx = hashInt(key) else: - var idx = popPtr[K](key)[].hash - #echo("get idx ", idx) - var + var idx = popPtr[K](key)[].hash + #echo("get idx ", idx) + var probes = 0 - val: int + val: int while true: - idx = idx and (table.len - 1) - var + idx = idx and (table.len - 1) + var newTable: PConcTable[K,V] # = atomic_load_n(table.next.addr, ATOMIC_ACQUIRE) - probedKey = atomic_load_n(table[idx].key.addr, ATOMIC_SEQ_CST) + probedKey = atomic_load_n(table[idx].key.addr, ATOMIC_SEQ_CST) if keyEQ[K](probedKey, key): #echo("found key after ", probes+1) val = atomic_load_n(table[idx].value.addr, ATOMIC_ACQUIRE) if not isPrime(val): if isTomb(val): - #echo("val was tomb but not prime") + #echo("val was tomb but not prime") return 0 else: - #echo("-GotIt- idx = ", idx, " key = ", key, " val ", val ) + #echo("-GotIt- idx = ", idx, " key = ", key, " val ", val ) return val else: newTable = copySlotAndCheck(table, idx) - return getVal(newTable, key) + return getVal(newTable, key) else: - #echo("probe ", probes, " idx = ", idx, " key = ", key, " found ", probedKey ) + #echo("probe ", probes, " idx = ", idx, " key = ", key, " found ", probedKey ) if probes >= reProbeLimit*4 or key.isTomb: if newTable == nil: #echo("too many probes and no new table ", key, " ", idx ) return 0 - else: + else: newTable = helpCopy(table) return getVal(newTable, key) idx += 1 probes += 1 #------------------------------------------------------------------------------ - + #proc set*(table: var PConcTable[Raw,Raw], key: Raw, val: Raw) = # discard setVal(table, pack(key), pack(key), 0, false) @@ -476,33 +476,34 @@ proc getVal[K,V](table: var PConcTable[K,V], key: int): int = # discard setVal(table, pack(key), cast[int](val), 0, false) proc set*[K,V](table: var PConcTable[K,V], key: var K, val: var V) = - when not (K is Raw): + when not (K is Raw): var newKey = cast[int](copyShared(key)) - else: + else: var newKey = pack(key) - when not (V is Raw): + when not (V is Raw): var newVal = cast[int](copyShared(val)) - else: + else: var newVal = pack(val) var oldPtr = pop(setVal(table, newKey, newVal, 0, false)) #echo("oldPtr = ", cast[int](oldPtr), " newPtr = ", cast[int](newPtr)) - when not (V is Raw): - if newVal != oldPtr and oldPtr != 0: + when not (V is Raw): + if newVal != oldPtr and oldPtr != 0: deallocShared(cast[ptr V](oldPtr)) - - + + proc get*[K,V](table: var PConcTable[K,V], key: var K): V = when not (V is Raw): when not (K is Raw): return popPtr[V](getVal(table, cast[int](key.addr)))[] - else: + else: return popPtr[V](getVal(table, pack(key)))[] else: when not (K is Raw): return popRaw(getVal(table, cast[int](key.addr))) - else: - return popRaw(getVal(table, pack(key))) + else: + return popRaw(getVal(table, pack(key))) + @@ -512,7 +513,6 @@ proc get*[K,V](table: var PConcTable[K,V], key: var K): V = - #proc `[]`[K,V](table: var PConcTable[K,V], key: K): PEntry[K,V] {.inline.} = # getVal(table, key) @@ -528,16 +528,16 @@ proc get*[K,V](table: var PConcTable[K,V], key: var K): V = #Tests ---------------------------- when not defined(testing) and isMainModule: import locks, times, mersenne - - const + + const numTests = 100000 numThreads = 10 - + type TestObj = tuple - thr: int + thr: int f0: int f1: int @@ -545,63 +545,63 @@ when not defined(testing) and isMainModule: PDataArr = array[0..numTests-1, Data] Dict = PConcTable[string,TestObj] {.deprecated: [TTestObj: TestObj, TData: Data].} - - var + + var thr: array[0..numThreads-1, Thread[Dict]] - - table = newLFTable[string,TestObj](8) + + table = newLFTable[string,TestObj](8) rand = newMersenneTwister(2525) - proc createSampleData(len: int): PDataArr = - #result = cast[PDataArr](allocShared0(sizeof(Data)*numTests)) + proc createSampleData(len: int): PDataArr = + #result = cast[PDataArr](allocShared0(sizeof(Data)*numTests)) for i in 0..len-1: result[i].k = "mark" & $(i+1) - #echo("mark" & $(i+1), " ", hash("mark" & $(i+1))) + #echo("mark" & $(i+1), " ", hash("mark" & $(i+1))) result[i].v.thr = 0 - result[i].v.f0 = i+1 - result[i].v.f1 = 0 + result[i].v.f0 = i+1 + result[i].v.f1 = 0 #echo("key = " & $(i+1) & " Val ptr = " & $cast[int](result[i].v.addr)) - proc threadProc(tp: Dict) {.thread.} = - var t = cpuTime(); + proc threadProc(tp: Dict) {.thread.} = + var t = cpuTime(); for i in 1..numTests: var key = "mark" & $(i) - var got = table.get(key) + var got = table.get(key) got.thr = cast[int](myThreadID[pointer]()) - got.f1 = got.f1 + 1 + got.f1 = got.f1 + 1 table.set(key, got) t = cpuTime() - t - echo t - + echo t + var testData = createSampleData(numTests) for i in 0..numTests-1: table.set(testData[i].k, testData[i].v) - + var i = 0 while i < numThreads: createThread(thr[i], threadProc, table) i += 1 - joinThreads(thr) + joinThreads(thr) + - var fails = 0 - for i in 0..numTests-1: - var got = table.get(testData[i].k) + for i in 0..numTests-1: + var got = table.get(testData[i].k) if got.f0 != i+1 or got.f1 != numThreads: fails += 1 echo(got) echo("Failed read or write = ", fails) - + #for i in 1..numTests: # echo(i, " = ", hashInt(i) and 8191) diff --git a/lib/pure/collections/critbits.nim b/lib/pure/collections/critbits.nim index 7e3f23851..424bcdcca 100644 --- a/lib/pure/collections/critbits.nim +++ b/lib/pure/collections/critbits.nim @@ -17,11 +17,11 @@ type otherbits: char case isLeaf: bool of false: child: array[0..1, ref NodeObj[T]] - of true: + of true: key: string when T isnot void: val: T - + Node[T] = ref NodeObj[T] CritBitTree*[T] = object ## The crit bit tree can either be used ## as a mapping from strings to @@ -66,7 +66,7 @@ proc rawInsert[T](c: var CritBitTree[T], key: string): Node[T] = let ch = if it.byte < key.len: key[it.byte] else: '\0' let dir = (1 + (ch.ord or it.otherBits.ord)) shr 8 it = it.child[dir] - + var newOtherBits = 0 var newByte = 0 block blockX: @@ -84,7 +84,7 @@ proc rawInsert[T](c: var CritBitTree[T], key: string): Node[T] = newOtherBits = newOtherBits xor 255 let ch = it.key[newByte] let dir = (1 + (ord(ch) or newOtherBits)) shr 8 - + var inner: Node[T] new inner new result @@ -93,7 +93,7 @@ proc rawInsert[T](c: var CritBitTree[T], key: string): Node[T] = inner.otherBits = chr(newOtherBits) inner.byte = newByte inner.child[1 - dir] = result - + var wherep = addr(c.root) while true: var p = wherep[] @@ -176,7 +176,7 @@ iterator leaves[T](n: Node[T]): Node[T] = # XXX actually we could compute the necessary stack size in advance: # it's roughly log2(c.count). var stack = @[n] - while stack.len > 0: + while stack.len > 0: var it = stack.pop while not it.isLeaf: stack.add(it.child[1]) @@ -205,7 +205,7 @@ iterator items*[T](c: CritBitTree[T]): string = iterator pairs*[T](c: CritBitTree[T]): tuple[key: string, val: T] = ## yields all (key, value)-pairs of `c`. for x in leaves(c.root): yield (x.key, x.val) - + iterator mpairs*[T](c: var CritBitTree[T]): tuple[key: string, val: var T] = ## yields all (key, value)-pairs of `c`. The yielded values can be modified. for x in leaves(c.root): yield (x.key, x.val) @@ -251,7 +251,7 @@ iterator pairsWithPrefix*[T](c: CritBitTree[T], ## yields all (key, value)-pairs of `c` starting with `prefix`. let top = allprefixedAux(c, prefix) for x in leaves(top): yield (x.key, x.val) - + iterator mpairsWithPrefix*[T](c: var CritBitTree[T], prefix: string): tuple[key: string, val: var T] = ## yields all (key, value)-pairs of `c` starting with `prefix`. diff --git a/lib/pure/collections/lists.nim b/lib/pure/collections/lists.nim index 535d5e21d..f847ddd58 100644 --- a/lib/pure/collections/lists.nim +++ b/lib/pure/collections/lists.nim @@ -27,18 +27,18 @@ type SinglyLinkedList*[T] = object ## a singly linked list head*, tail*: SinglyLinkedNode[T] - + DoublyLinkedList*[T] = object ## a doubly linked list head*, tail*: DoublyLinkedNode[T] SinglyLinkedRing*[T] = object ## a singly linked ring head*, tail*: SinglyLinkedNode[T] - + DoublyLinkedRing*[T] = object ## a doubly linked ring head*: DoublyLinkedNode[T] {.deprecated: [TDoublyLinkedNode: DoublyLinkedNodeObj, - PDoublyLinkedNode: DoublyLinkedNode, + PDoublyLinkedNode: DoublyLinkedNode, TSinglyLinkedNode: SinglyLinkedNodeObj, PSinglyLinkedNode: SinglyLinkedNode, TDoublyLinkedList: DoublyLinkedList, @@ -106,19 +106,19 @@ template findImpl() {.dirty.} = for x in nodes(L): if x.value == value: return x -iterator items*[T](L: DoublyLinkedList[T]): T = +iterator items*[T](L: DoublyLinkedList[T]): T = ## yields every value of `L`. itemsListImpl() -iterator items*[T](L: SinglyLinkedList[T]): T = +iterator items*[T](L: SinglyLinkedList[T]): T = ## yields every value of `L`. itemsListImpl() -iterator items*[T](L: SinglyLinkedRing[T]): T = +iterator items*[T](L: SinglyLinkedRing[T]): T = ## yields every value of `L`. itemsRingImpl() -iterator items*[T](L: DoublyLinkedRing[T]): T = +iterator items*[T](L: DoublyLinkedRing[T]): T = ## yields every value of `L`. itemsRingImpl() @@ -138,22 +138,22 @@ iterator mitems*[T](L: var DoublyLinkedRing[T]): var T = ## yields every value of `L` so that you can modify it. itemsRingImpl() -iterator nodes*[T](L: SinglyLinkedList[T]): SinglyLinkedNode[T] = +iterator nodes*[T](L: SinglyLinkedList[T]): SinglyLinkedNode[T] = ## iterates over every node of `x`. Removing the current node from the ## list during traversal is supported. nodesListImpl() -iterator nodes*[T](L: DoublyLinkedList[T]): DoublyLinkedNode[T] = +iterator nodes*[T](L: DoublyLinkedList[T]): DoublyLinkedNode[T] = ## iterates over every node of `x`. Removing the current node from the ## list during traversal is supported. nodesListImpl() -iterator nodes*[T](L: SinglyLinkedRing[T]): SinglyLinkedNode[T] = +iterator nodes*[T](L: SinglyLinkedRing[T]): SinglyLinkedNode[T] = ## iterates over every node of `x`. Removing the current node from the ## list during traversal is supported. nodesRingImpl() -iterator nodes*[T](L: DoublyLinkedRing[T]): DoublyLinkedNode[T] = +iterator nodes*[T](L: DoublyLinkedRing[T]): DoublyLinkedNode[T] = ## iterates over every node of `x`. Removing the current node from the ## list during traversal is supported. nodesRingImpl() @@ -165,87 +165,87 @@ template dollarImpl() {.dirty.} = result.add($x.value) result.add("]") -proc `$`*[T](L: SinglyLinkedList[T]): string = +proc `$`*[T](L: SinglyLinkedList[T]): string = ## turns a list into its string representation. dollarImpl() -proc `$`*[T](L: DoublyLinkedList[T]): string = +proc `$`*[T](L: DoublyLinkedList[T]): string = ## turns a list into its string representation. dollarImpl() -proc `$`*[T](L: SinglyLinkedRing[T]): string = +proc `$`*[T](L: SinglyLinkedRing[T]): string = ## turns a list into its string representation. dollarImpl() -proc `$`*[T](L: DoublyLinkedRing[T]): string = +proc `$`*[T](L: DoublyLinkedRing[T]): string = ## turns a list into its string representation. dollarImpl() -proc find*[T](L: SinglyLinkedList[T], value: T): SinglyLinkedNode[T] = +proc find*[T](L: SinglyLinkedList[T], value: T): SinglyLinkedNode[T] = ## searches in the list for a value. Returns nil if the value does not ## exist. findImpl() -proc find*[T](L: DoublyLinkedList[T], value: T): DoublyLinkedNode[T] = +proc find*[T](L: DoublyLinkedList[T], value: T): DoublyLinkedNode[T] = ## searches in the list for a value. Returns nil if the value does not ## exist. findImpl() -proc find*[T](L: SinglyLinkedRing[T], value: T): SinglyLinkedNode[T] = +proc find*[T](L: SinglyLinkedRing[T], value: T): SinglyLinkedNode[T] = ## searches in the list for a value. Returns nil if the value does not ## exist. findImpl() -proc find*[T](L: DoublyLinkedRing[T], value: T): DoublyLinkedNode[T] = +proc find*[T](L: DoublyLinkedRing[T], value: T): DoublyLinkedNode[T] = ## searches in the list for a value. Returns nil if the value does not ## exist. findImpl() -proc contains*[T](L: SinglyLinkedList[T], value: T): bool {.inline.} = +proc contains*[T](L: SinglyLinkedList[T], value: T): bool {.inline.} = ## searches in the list for a value. Returns false if the value does not ## exist, true otherwise. result = find(L, value) != nil -proc contains*[T](L: DoublyLinkedList[T], value: T): bool {.inline.} = +proc contains*[T](L: DoublyLinkedList[T], value: T): bool {.inline.} = ## searches in the list for a value. Returns false if the value does not ## exist, true otherwise. result = find(L, value) != nil -proc contains*[T](L: SinglyLinkedRing[T], value: T): bool {.inline.} = +proc contains*[T](L: SinglyLinkedRing[T], value: T): bool {.inline.} = ## searches in the list for a value. Returns false if the value does not ## exist, true otherwise. result = find(L, value) != nil -proc contains*[T](L: DoublyLinkedRing[T], value: T): bool {.inline.} = +proc contains*[T](L: DoublyLinkedRing[T], value: T): bool {.inline.} = ## searches in the list for a value. Returns false if the value does not ## exist, true otherwise. result = find(L, value) != nil -proc prepend*[T](L: var SinglyLinkedList[T], - n: SinglyLinkedNode[T]) {.inline.} = +proc prepend*[T](L: var SinglyLinkedList[T], + n: SinglyLinkedNode[T]) {.inline.} = ## prepends a node to `L`. Efficiency: O(1). n.next = L.head L.head = n -proc prepend*[T](L: var SinglyLinkedList[T], value: T) {.inline.} = +proc prepend*[T](L: var SinglyLinkedList[T], value: T) {.inline.} = ## prepends a node to `L`. Efficiency: O(1). prepend(L, newSinglyLinkedNode(value)) - -proc append*[T](L: var DoublyLinkedList[T], n: DoublyLinkedNode[T]) = + +proc append*[T](L: var DoublyLinkedList[T], n: DoublyLinkedNode[T]) = ## appends a node `n` to `L`. Efficiency: O(1). n.next = nil n.prev = L.tail - if L.tail != nil: + if L.tail != nil: assert(L.tail.next == nil) L.tail.next = n L.tail = n if L.head == nil: L.head = n -proc append*[T](L: var DoublyLinkedList[T], value: T) = +proc append*[T](L: var DoublyLinkedList[T], value: T) = ## appends a value to `L`. Efficiency: O(1). append(L, newDoublyLinkedNode(value)) -proc prepend*[T](L: var DoublyLinkedList[T], n: DoublyLinkedNode[T]) = +proc prepend*[T](L: var DoublyLinkedList[T], n: DoublyLinkedNode[T]) = ## prepends a node `n` to `L`. Efficiency: O(1). n.prev = nil n.next = L.head @@ -255,11 +255,11 @@ proc prepend*[T](L: var DoublyLinkedList[T], n: DoublyLinkedNode[T]) = L.head = n if L.tail == nil: L.tail = n -proc prepend*[T](L: var DoublyLinkedList[T], value: T) = +proc prepend*[T](L: var DoublyLinkedList[T], value: T) = ## prepends a value to `L`. Efficiency: O(1). prepend(L, newDoublyLinkedNode(value)) - -proc remove*[T](L: var DoublyLinkedList[T], n: DoublyLinkedNode[T]) = + +proc remove*[T](L: var DoublyLinkedList[T], n: DoublyLinkedNode[T]) = ## removes `n` from `L`. Efficiency: O(1). if n == L.tail: L.tail = n.prev if n == L.head: L.head = n.next @@ -267,7 +267,7 @@ proc remove*[T](L: var DoublyLinkedList[T], n: DoublyLinkedNode[T]) = if n.prev != nil: n.prev.next = n.next -proc append*[T](L: var SinglyLinkedRing[T], n: SinglyLinkedNode[T]) = +proc append*[T](L: var SinglyLinkedRing[T], n: SinglyLinkedNode[T]) = ## appends a node `n` to `L`. Efficiency: O(1). if L.head != nil: n.next = L.head @@ -279,11 +279,11 @@ proc append*[T](L: var SinglyLinkedRing[T], n: SinglyLinkedNode[T]) = L.head = n L.tail = n -proc append*[T](L: var SinglyLinkedRing[T], value: T) = +proc append*[T](L: var SinglyLinkedRing[T], value: T) = ## appends a value to `L`. Efficiency: O(1). append(L, newSinglyLinkedNode(value)) -proc prepend*[T](L: var SinglyLinkedRing[T], n: SinglyLinkedNode[T]) = +proc prepend*[T](L: var SinglyLinkedRing[T], n: SinglyLinkedNode[T]) = ## prepends a node `n` to `L`. Efficiency: O(1). if L.head != nil: n.next = L.head @@ -294,11 +294,11 @@ proc prepend*[T](L: var SinglyLinkedRing[T], n: SinglyLinkedNode[T]) = L.tail = n L.head = n -proc prepend*[T](L: var SinglyLinkedRing[T], value: T) = +proc prepend*[T](L: var SinglyLinkedRing[T], value: T) = ## prepends a value to `L`. Efficiency: O(1). prepend(L, newSinglyLinkedNode(value)) -proc append*[T](L: var DoublyLinkedRing[T], n: DoublyLinkedNode[T]) = +proc append*[T](L: var DoublyLinkedRing[T], n: DoublyLinkedNode[T]) = ## appends a node `n` to `L`. Efficiency: O(1). if L.head != nil: n.next = L.head @@ -310,13 +310,13 @@ proc append*[T](L: var DoublyLinkedRing[T], n: DoublyLinkedNode[T]) = n.next = n L.head = n -proc append*[T](L: var DoublyLinkedRing[T], value: T) = +proc append*[T](L: var DoublyLinkedRing[T], value: T) = ## appends a value to `L`. Efficiency: O(1). append(L, newDoublyLinkedNode(value)) -proc prepend*[T](L: var DoublyLinkedRing[T], n: DoublyLinkedNode[T]) = +proc prepend*[T](L: var DoublyLinkedRing[T], n: DoublyLinkedNode[T]) = ## prepends a node `n` to `L`. Efficiency: O(1). - if L.head != nil: + if L.head != nil: n.next = L.head n.prev = L.head.prev L.head.prev.next = n @@ -326,17 +326,17 @@ proc prepend*[T](L: var DoublyLinkedRing[T], n: DoublyLinkedNode[T]) = n.next = n L.head = n -proc prepend*[T](L: var DoublyLinkedRing[T], value: T) = +proc prepend*[T](L: var DoublyLinkedRing[T], value: T) = ## prepends a value to `L`. Efficiency: O(1). prepend(L, newDoublyLinkedNode(value)) - -proc remove*[T](L: var DoublyLinkedRing[T], n: DoublyLinkedNode[T]) = + +proc remove*[T](L: var DoublyLinkedRing[T], n: DoublyLinkedNode[T]) = ## removes `n` from `L`. Efficiency: O(1). n.next.prev = n.prev n.prev.next = n.next - if n == L.head: + if n == L.head: var p = L.head.prev - if p == L.head: + if p == L.head: # only one element left: L.head = nil else: diff --git a/lib/pure/collections/queues.nim b/lib/pure/collections/queues.nim index af5e7b6cd..c35a2dc29 100644 --- a/lib/pure/collections/queues.nim +++ b/lib/pure/collections/queues.nim @@ -77,7 +77,7 @@ proc dequeue*[T](q: var Queue[T]): T = result = q.data[q.rd] q.rd = (q.rd + 1) and q.mask -proc `$`*[T](q: Queue[T]): string = +proc `$`*[T](q: Queue[T]): string = ## turns a queue into its string representation. result = "[" for x in items(q): @@ -95,7 +95,7 @@ when isMainModule: q.add(6) var second = q.dequeue q.add(789) - + assert first == 123 assert second == 9 assert($q == "[4, 56, 6, 789]") diff --git a/lib/pure/colors.nim b/lib/pure/colors.nim index f24cc0072..7328f7c24 100644 --- a/lib/pure/colors.nim +++ b/lib/pure/colors.nim @@ -6,7 +6,7 @@ # distribution, for details about the copyright. # -## This module implements color handling for Nimrod. It is used by +## This module implements color handling for Nimrod. It is used by ## the ``graphics`` module. import strutils @@ -18,15 +18,15 @@ type proc `==` *(a, b: Color): bool {.borrow.} ## compares two colors. - + template extract(a: Color, r, g, b: expr) {.immediate.}= var r = a.int shr 16 and 0xff var g = a.int shr 8 and 0xff var b = a.int and 0xff - + template rawRGB(r, g, b: int): expr = Color(r shl 16 or g shl 8 or b) - + template colorOp(op: expr) {.immediate.} = extract(a, ar, ag, ab) extract(b, br, bg, bb) @@ -39,24 +39,24 @@ proc satPlus(a, b: int): int {.inline.} = proc satMinus(a, b: int): int {.inline.} = result = a -% b if result < 0: result = 0 - + proc `+`*(a, b: Color): Color = ## adds two colors: This uses saturated artithmetic, so that each color ## component cannot overflow (255 is used as a maximum). colorOp(satPlus) - + proc `-`*(a, b: Color): Color = ## subtracts two colors: This uses saturated artithmetic, so that each color ## component cannot overflow (255 is used as a maximum). colorOp(satMinus) - + proc extractRGB*(a: Color): tuple[r, g, b: range[0..255]] = ## extracts the red/green/blue components of the color `a`. result.r = a.int shr 16 and 0xff result.g = a.int shr 8 and 0xff result.b = a.int and 0xff - -proc intensity*(a: Color, f: float): Color = + +proc intensity*(a: Color, f: float): Color = ## returns `a` with intensity `f`. `f` should be a float from 0.0 (completely ## dark) to 1.0 (full color intensity). var r = toInt(toFloat(a.int shr 16 and 0xff) * f) @@ -66,7 +66,7 @@ proc intensity*(a: Color, f: float): Color = if g >% 255: g = 255 if b >% 255: b = 255 result = rawRGB(r, g, b) - + template mix*(a, b: Color, fn: expr): expr = ## uses `fn` to mix the colors `a` and `b`. `fn` is invoked for each component ## R, G, and B. This is a template because `fn` should be inlined and the @@ -79,7 +79,7 @@ template mix*(a, b: Color, fn: expr): expr = if y >% 255: y = if y < 0: 0 else: 255 y - + (bind extract)(a, ar, ag, ab) (bind extract)(b, br, bg, bb) (bind rawRGB)(><fn(ar, br), ><fn(ag, bg), ><fn(ab, bb)) @@ -226,7 +226,7 @@ const colWhiteSmoke* = Color(0xF5F5F5) colYellow* = Color(0xFFFF00) colYellowGreen* = Color(0x9ACD32) - + colorNames = [ ("aliceblue", colAliceBlue), ("antiquewhite", colAntiqueWhite), @@ -369,24 +369,24 @@ const ("yellow", colYellow), ("yellowgreen", colYellowGreen)] -proc `$`*(c: Color): string = +proc `$`*(c: Color): string = ## converts a color into its textual representation. Example: ``#00FF00``. result = '#' & toHex(int(c), 6) -proc binaryStrSearch(x: openArray[tuple[name: string, col: Color]], - y: string): int = +proc binaryStrSearch(x: openArray[tuple[name: string, col: Color]], + y: string): int = var a = 0 var b = len(x) - 1 - while a <= b: + while a <= b: var mid = (a + b) div 2 var c = cmp(x[mid].name, y) if c < 0: a = mid + 1 elif c > 0: b = mid - 1 else: return mid result = - 1 - -proc parseColor*(name: string): Color = - ## parses `name` to a color value. If no valid color could be + +proc parseColor*(name: string): Color = + ## parses `name` to a color value. If no valid color could be ## parsed ``EInvalidValue`` is raised. if name[0] == '#': result = Color(parseHexInt(name)) @@ -396,10 +396,10 @@ proc parseColor*(name: string): Color = result = colorNames[idx][1] proc isColor*(name: string): bool = - ## returns true if `name` is a known color name or a hexadecimal color + ## returns true if `name` is a known color name or a hexadecimal color ## prefixed with ``#``. - if name[0] == '#': - for i in 1 .. name.len-1: + if name[0] == '#': + for i in 1 .. name.len-1: if name[i] notin {'0'..'9', 'a'..'f', 'A'..'F'}: return false result = true else: diff --git a/lib/pure/complex.nim b/lib/pure/complex.nim index 8577bf7a1..ccde5ee0a 100644 --- a/lib/pure/complex.nim +++ b/lib/pure/complex.nim @@ -17,7 +17,7 @@ import math - + const EPS = 1.0e-7 ## Epsilon used for float comparisons. @@ -248,7 +248,7 @@ proc pow*(x, y: Complex): Complex = var r = y.re*theta + y.im*ln(rho) result.re = s*cos(r) result.im = s*sin(r) - + proc sin*(z: Complex): Complex = ## Returns the sine of `z`. @@ -387,7 +387,7 @@ when isMainModule: var one = (1.0,0.0) var tt = (10.0, 20.0) var ipi = (0.0, -PI) - + assert( a == a ) assert( (a-a) == z ) assert( (a+b) == z ) @@ -403,7 +403,7 @@ when isMainModule: assert( conjugate(a) == (1.0, -2.0) ) assert( sqrt(m1) == i ) assert( exp(ipi) =~ m1 ) - + assert( pow(a,b) =~ (-3.72999124927876, -1.68815826725068) ) assert( pow(z,a) =~ (0.0, 0.0) ) assert( pow(z,z) =~ (1.0, 0.0) ) diff --git a/lib/pure/concurrency/cpuinfo.nim b/lib/pure/concurrency/cpuinfo.nim index 6f2bc4491..8c87c77df 100644 --- a/lib/pure/concurrency/cpuinfo.nim +++ b/lib/pure/concurrency/cpuinfo.nim @@ -18,7 +18,7 @@ when not defined(windows): when defined(linux): import linux - + when defined(freebsd) or defined(macosx): {.emit:"#include <sys/types.h>".} diff --git a/lib/pure/cookies.nim b/lib/pure/cookies.nim index 9983c4a04..8090cd49d 100644 --- a/lib/pure/cookies.nim +++ b/lib/pure/cookies.nim @@ -11,7 +11,7 @@ import strtabs, times -proc parseCookies*(s: string): StringTableRef = +proc parseCookies*(s: string): StringTableRef = ## parses cookies into a string table. result = newStringTable(modeCaseInsensitive) var i = 0 @@ -31,7 +31,7 @@ proc parseCookies*(s: string): StringTableRef = proc setCookie*(key, value: string, domain = "", path = "", expires = "", noName = false, secure = false, httpOnly = false): string = - ## Creates a command in the format of + ## Creates a command in the format of ## ``Set-Cookie: key=value; Domain=...; ...`` result = "" if not noName: result.add("Set-Cookie: ") @@ -45,10 +45,10 @@ proc setCookie*(key, value: string, domain = "", path = "", proc setCookie*(key, value: string, expires: TimeInfo, domain = "", path = "", noName = false, secure = false, httpOnly = false): string = - ## Creates a command in the format of + ## Creates a command in the format of ## ``Set-Cookie: key=value; Domain=...; ...`` ## - ## **Note:** UTC is assumed as the timezone for ``expires``. + ## **Note:** UTC is assumed as the timezone for ``expires``. return setCookie(key, value, domain, path, format(expires, "ddd',' dd MMM yyyy HH:mm:ss 'UTC'"), noname, secure, httpOnly) @@ -61,7 +61,7 @@ when isMainModule: echo cookie let start = "Set-Cookie: test=value; Expires=" assert cookie[0..start.high] == start - + let table = parseCookies("uid=1; kp=2") assert table["uid"] == "1" assert table["kp"] == "2" diff --git a/lib/pure/encodings.nim b/lib/pure/encodings.nim index e427b585d..2d305ea42 100644 --- a/lib/pure/encodings.nim +++ b/lib/pure/encodings.nim @@ -7,7 +7,7 @@ # distribution, for details about the copyright. # -## Converts between different character encodings. On UNIX, this uses +## Converts between different character encodings. On UNIX, this uses ## the `iconv`:idx: library, on Windows the Windows API. import os, parseutils, strutils @@ -41,173 +41,173 @@ when defined(windows): inc j result = i == a.len and j == b.len - const + const winEncodings = [ (1, "OEMCP"), # current OEM codepage - (037, "IBM037"), # IBM EBCDIC US-Canada - (437, "IBM437"), # OEM United States - (500, "IBM500"), # IBM EBCDIC International - (708, "ASMO-708"), # Arabic (ASMO 708) - (709, "ASMO_449"), # Arabic (ASMO-449+, BCON V4) - (710, ""), # Arabic - Transparent Arabic - (720, "DOS-720"), # Arabic (Transparent ASMO); Arabic (DOS) - (737, "ibm737"), # OEM Greek (formerly 437G); Greek (DOS) - (775, "ibm775"), # OEM Baltic; Baltic (DOS) - (850, "ibm850"), # OEM Multilingual Latin 1; Western European (DOS) - (852, "ibm852"), # OEM Latin 2; Central European (DOS) - (855, "IBM855"), # OEM Cyrillic (primarily Russian) - (857, "ibm857"), # OEM Turkish; Turkish (DOS) - (858, "IBM00858"), # OEM Multilingual Latin 1 + Euro symbol - (860, "IBM860"), # OEM Portuguese; Portuguese (DOS) - (861, "ibm861"), # OEM Icelandic; Icelandic (DOS) - (862, "DOS-862"), # OEM Hebrew; Hebrew (DOS) - (863, "IBM863"), # OEM French Canadian; French Canadian (DOS) - (864, "IBM864"), # OEM Arabic; Arabic (864) - (865, "IBM865"), # OEM Nordic; Nordic (DOS) - (866, "cp866"), # OEM Russian; Cyrillic (DOS) - (869, "ibm869"), # OEM Modern Greek; Greek, Modern (DOS) - (870, "IBM870"), # IBM EBCDIC Multilingual/ROECE (Latin 2); IBM EBCDIC Multilingual Latin 2 - (874, "windows-874"), # ANSI/OEM Thai (same as 28605, ISO 8859-15); Thai (Windows) - (875, "cp875"), # IBM EBCDIC Greek Modern - (932, "shift_jis"), # ANSI/OEM Japanese; Japanese (Shift-JIS) - (936, "gb2312"), # ANSI/OEM Simplified Chinese (PRC, Singapore); Chinese Simplified (GB2312) - (949, "ks_c_5601-1987"), # ANSI/OEM Korean (Unified Hangul Code) - (950, "big5"), # ANSI/OEM Traditional Chinese (Taiwan; Hong Kong SAR, PRC); Chinese Traditional (Big5) - (1026, "IBM1026"), # IBM EBCDIC Turkish (Latin 5) - (1047, "IBM01047"), # IBM EBCDIC Latin 1/Open System - (1140, "IBM01140"), # IBM EBCDIC US-Canada (037 + Euro symbol); IBM EBCDIC (US-Canada-Euro) - (1141, "IBM01141"), # IBM EBCDIC Germany (20273 + Euro symbol); IBM EBCDIC (Germany-Euro) - (1142, "IBM01142"), # IBM EBCDIC Denmark-Norway (20277 + Euro symbol); IBM EBCDIC (Denmark-Norway-Euro) - (1143, "IBM01143"), # IBM EBCDIC Finland-Sweden (20278 + Euro symbol); IBM EBCDIC (Finland-Sweden-Euro) - (1144, "IBM01144"), # IBM EBCDIC Italy (20280 + Euro symbol); IBM EBCDIC (Italy-Euro) - (1145, "IBM01145"), # IBM EBCDIC Latin America-Spain (20284 + Euro symbol); IBM EBCDIC (Spain-Euro) - (1146, "IBM01146"), # IBM EBCDIC United Kingdom (20285 + Euro symbol); IBM EBCDIC (UK-Euro) - (1147, "IBM01147"), # IBM EBCDIC France (20297 + Euro symbol); IBM EBCDIC (France-Euro) - (1148, "IBM01148"), # IBM EBCDIC International (500 + Euro symbol); IBM EBCDIC (International-Euro) - (1149, "IBM01149"), # IBM EBCDIC Icelandic (20871 + Euro symbol); IBM EBCDIC (Icelandic-Euro) - (1200, "utf-16"), # Unicode UTF-16, little endian byte order (BMP of ISO 10646); available only to managed applications - (1201, "unicodeFFFE"), # Unicode UTF-16, big endian byte order; available only to managed applications - (1250, "windows-1250"), # ANSI Central European; Central European (Windows) - (1251, "windows-1251"), # ANSI Cyrillic; Cyrillic (Windows) - (1252, "windows-1252"), # ANSI Latin 1; Western European (Windows) - (1253, "windows-1253"), # ANSI Greek; Greek (Windows) - (1254, "windows-1254"), # ANSI Turkish; Turkish (Windows) - (1255, "windows-1255"), # ANSI Hebrew; Hebrew (Windows) - (1256, "windows-1256"), # ANSI Arabic; Arabic (Windows) - (1257, "windows-1257"), # ANSI Baltic; Baltic (Windows) - (1258, "windows-1258"), # ANSI/OEM Vietnamese; Vietnamese (Windows) - - (1250, "cp-1250"), # ANSI Central European; Central European (Windows) - (1251, "cp-1251"), # ANSI Cyrillic; Cyrillic (Windows) - (1252, "cp-1252"), # ANSI Latin 1; Western European (Windows) - (1253, "cp-1253"), # ANSI Greek; Greek (Windows) - (1254, "cp-1254"), # ANSI Turkish; Turkish (Windows) - (1255, "cp-1255"), # ANSI Hebrew; Hebrew (Windows) - (1256, "cp-1256"), # ANSI Arabic; Arabic (Windows) - (1257, "cp-1257"), # ANSI Baltic; Baltic (Windows) - (1258, "cp-1258"), # ANSI/OEM Vietnamese; Vietnamese (Windows) - - (1361, "Johab"), # Korean (Johab) - (10000, "macintosh"), # MAC Roman; Western European (Mac) - (10001, "x-mac-japanese"), # Japanese (Mac) - (10002, "x-mac-chinesetrad"), # MAC Traditional Chinese (Big5); Chinese Traditional (Mac) - (10003, "x-mac-korean"), # Korean (Mac) - (10004, "x-mac-arabic"), # Arabic (Mac) - (10005, "x-mac-hebrew"), # Hebrew (Mac) - (10006, "x-mac-greek"), # Greek (Mac) - (10007, "x-mac-cyrillic"), # Cyrillic (Mac) - (10008, "x-mac-chinesesimp"), # MAC Simplified Chinese (GB 2312); Chinese Simplified (Mac) - (10010, "x-mac-romanian"), # Romanian (Mac) - (10017, "x-mac-ukrainian"), # Ukrainian (Mac) - (10021, "x-mac-thai"), # Thai (Mac) - (10029, "x-mac-ce"), # MAC Latin 2; Central European (Mac) - (10079, "x-mac-icelandic"), # Icelandic (Mac) - (10081, "x-mac-turkish"), # Turkish (Mac) - (10082, "x-mac-croatian"), # Croatian (Mac) - (12000, "utf-32"), # Unicode UTF-32, little endian byte order; available only to managed applications - (12001, "utf-32BE"), # Unicode UTF-32, big endian byte order; available only to managed applications - (20000, "x-Chinese_CNS"), # CNS Taiwan; Chinese Traditional (CNS) - (20001, "x-cp20001"), # TCA Taiwan - (20002, "x_Chinese-Eten"), # Eten Taiwan; Chinese Traditional (Eten) - (20003, "x-cp20003"), # IBM5550 Taiwan - (20004, "x-cp20004"), # TeleText Taiwan - (20005, "x-cp20005"), # Wang Taiwan - (20105, "x-IA5"), # IA5 (IRV International Alphabet No. 5, 7-bit); Western European (IA5) - (20106, "x-IA5-German"), # IA5 German (7-bit) - (20107, "x-IA5-Swedish"), # IA5 Swedish (7-bit) - (20108, "x-IA5-Norwegian"), # IA5 Norwegian (7-bit) - (20127, "us-ascii"), # US-ASCII (7-bit) - (20261, "x-cp20261"), # T.61 - (20269, "x-cp20269"), # ISO 6937 Non-Spacing Accent - (20273, "IBM273"), # IBM EBCDIC Germany - (20277, "IBM277"), # IBM EBCDIC Denmark-Norway - (20278, "IBM278"), # IBM EBCDIC Finland-Sweden - (20280, "IBM280"), # IBM EBCDIC Italy - (20284, "IBM284"), # IBM EBCDIC Latin America-Spain - (20285, "IBM285"), # IBM EBCDIC United Kingdom - (20290, "IBM290"), # IBM EBCDIC Japanese Katakana Extended - (20297, "IBM297"), # IBM EBCDIC France - (20420, "IBM420"), # IBM EBCDIC Arabic - (20423, "IBM423"), # IBM EBCDIC Greek - (20424, "IBM424"), # IBM EBCDIC Hebrew - (20833, "x-EBCDIC-KoreanExtended"), # IBM EBCDIC Korean Extended - (20838, "IBM-Thai"), # IBM EBCDIC Thai - (20866, "koi8-r"), # Russian (KOI8-R); Cyrillic (KOI8-R) - (20871, "IBM871"), # IBM EBCDIC Icelandic - (20880, "IBM880"), # IBM EBCDIC Cyrillic Russian - (20905, "IBM905"), # IBM EBCDIC Turkish - (20924, "IBM00924"), # IBM EBCDIC Latin 1/Open System (1047 + Euro symbol) - (20932, "EUC-JP"), # Japanese (JIS 0208-1990 and 0121-1990) - (20936, "x-cp20936"), # Simplified Chinese (GB2312); Chinese Simplified (GB2312-80) - (20949, "x-cp20949"), # Korean Wansung - (21025, "cp1025"), # IBM EBCDIC Cyrillic Serbian-Bulgarian - (21027, ""), # (deprecated) - (21866, "koi8-u"), # Ukrainian (KOI8-U); Cyrillic (KOI8-U) - (28591, "iso-8859-1"), # ISO 8859-1 Latin 1; Western European (ISO) - (28592, "iso-8859-2"), # ISO 8859-2 Central European; Central European (ISO) - (28593, "iso-8859-3"), # ISO 8859-3 Latin 3 - (28594, "iso-8859-4"), # ISO 8859-4 Baltic - (28595, "iso-8859-5"), # ISO 8859-5 Cyrillic - (28596, "iso-8859-6"), # ISO 8859-6 Arabic - (28597, "iso-8859-7"), # ISO 8859-7 Greek - (28598, "iso-8859-8"), # ISO 8859-8 Hebrew; Hebrew (ISO-Visual) - (28599, "iso-8859-9"), # ISO 8859-9 Turkish - (28603, "iso-8859-13"), # ISO 8859-13 Estonian - (28605, "iso-8859-15"), # ISO 8859-15 Latin 9 - (29001, "x-Europa"), # Europa 3 - (38598, "iso-8859-8-i"), # ISO 8859-8 Hebrew; Hebrew (ISO-Logical) - (50220, "iso-2022-jp"), # ISO 2022 Japanese with no halfwidth Katakana; Japanese (JIS) - (50221, "csISO2022JP"), # ISO 2022 Japanese with halfwidth Katakana; Japanese (JIS-Allow 1 byte Kana) - (50222, "iso-2022-jp"), # ISO 2022 Japanese JIS X 0201-1989; Japanese (JIS-Allow 1 byte Kana - SO/SI) - (50225, "iso-2022-kr"), # ISO 2022 Korean - (50227, "x-cp50227"), # ISO 2022 Simplified Chinese; Chinese Simplified (ISO 2022) - (50229, ""), # ISO 2022 Traditional Chinese - (50930, ""), # EBCDIC Japanese (Katakana) Extended - (50931, ""), # EBCDIC US-Canada and Japanese - (50933, ""), # EBCDIC Korean Extended and Korean - (50935, ""), # EBCDIC Simplified Chinese Extended and Simplified Chinese - (50936, ""), # EBCDIC Simplified Chinese - (50937, ""), # EBCDIC US-Canada and Traditional Chinese - (50939, ""), # EBCDIC Japanese (Latin) Extended and Japanese - (51932, "euc-jp"), # EUC Japanese - (51936, "EUC-CN"), # EUC Simplified Chinese; Chinese Simplified (EUC) - (51949, "euc-kr"), # EUC Korean - (51950, ""), # EUC Traditional Chinese - (52936, "hz-gb-2312"), # HZ-GB2312 Simplified Chinese; Chinese Simplified (HZ) - (54936, "GB18030"), # Windows XP and later: GB18030 Simplified Chinese (4 byte); Chinese Simplified (GB18030) - (57002, "x-iscii-de"), # ISCII Devanagari - (57003, "x-iscii-be"), # ISCII Bengali - (57004, "x-iscii-ta"), # ISCII Tamil - (57005, "x-iscii-te"), # ISCII Telugu - (57006, "x-iscii-as"), # ISCII Assamese - (57007, "x-iscii-or"), # ISCII Oriya - (57008, "x-iscii-ka"), # ISCII Kannada - (57009, "x-iscii-ma"), # ISCII Malayalam - (57010, "x-iscii-gu"), # ISCII Gujarati - (57011, "x-iscii-pa"), # ISCII Punjabi - (65000, "utf-7"), # Unicode (UTF-7) - (65001, "utf-8")] # Unicode (UTF-8) - + (037, "IBM037"), # IBM EBCDIC US-Canada + (437, "IBM437"), # OEM United States + (500, "IBM500"), # IBM EBCDIC International + (708, "ASMO-708"), # Arabic (ASMO 708) + (709, "ASMO_449"), # Arabic (ASMO-449+, BCON V4) + (710, ""), # Arabic - Transparent Arabic + (720, "DOS-720"), # Arabic (Transparent ASMO); Arabic (DOS) + (737, "ibm737"), # OEM Greek (formerly 437G); Greek (DOS) + (775, "ibm775"), # OEM Baltic; Baltic (DOS) + (850, "ibm850"), # OEM Multilingual Latin 1; Western European (DOS) + (852, "ibm852"), # OEM Latin 2; Central European (DOS) + (855, "IBM855"), # OEM Cyrillic (primarily Russian) + (857, "ibm857"), # OEM Turkish; Turkish (DOS) + (858, "IBM00858"), # OEM Multilingual Latin 1 + Euro symbol + (860, "IBM860"), # OEM Portuguese; Portuguese (DOS) + (861, "ibm861"), # OEM Icelandic; Icelandic (DOS) + (862, "DOS-862"), # OEM Hebrew; Hebrew (DOS) + (863, "IBM863"), # OEM French Canadian; French Canadian (DOS) + (864, "IBM864"), # OEM Arabic; Arabic (864) + (865, "IBM865"), # OEM Nordic; Nordic (DOS) + (866, "cp866"), # OEM Russian; Cyrillic (DOS) + (869, "ibm869"), # OEM Modern Greek; Greek, Modern (DOS) + (870, "IBM870"), # IBM EBCDIC Multilingual/ROECE (Latin 2); IBM EBCDIC Multilingual Latin 2 + (874, "windows-874"), # ANSI/OEM Thai (same as 28605, ISO 8859-15); Thai (Windows) + (875, "cp875"), # IBM EBCDIC Greek Modern + (932, "shift_jis"), # ANSI/OEM Japanese; Japanese (Shift-JIS) + (936, "gb2312"), # ANSI/OEM Simplified Chinese (PRC, Singapore); Chinese Simplified (GB2312) + (949, "ks_c_5601-1987"), # ANSI/OEM Korean (Unified Hangul Code) + (950, "big5"), # ANSI/OEM Traditional Chinese (Taiwan; Hong Kong SAR, PRC); Chinese Traditional (Big5) + (1026, "IBM1026"), # IBM EBCDIC Turkish (Latin 5) + (1047, "IBM01047"), # IBM EBCDIC Latin 1/Open System + (1140, "IBM01140"), # IBM EBCDIC US-Canada (037 + Euro symbol); IBM EBCDIC (US-Canada-Euro) + (1141, "IBM01141"), # IBM EBCDIC Germany (20273 + Euro symbol); IBM EBCDIC (Germany-Euro) + (1142, "IBM01142"), # IBM EBCDIC Denmark-Norway (20277 + Euro symbol); IBM EBCDIC (Denmark-Norway-Euro) + (1143, "IBM01143"), # IBM EBCDIC Finland-Sweden (20278 + Euro symbol); IBM EBCDIC (Finland-Sweden-Euro) + (1144, "IBM01144"), # IBM EBCDIC Italy (20280 + Euro symbol); IBM EBCDIC (Italy-Euro) + (1145, "IBM01145"), # IBM EBCDIC Latin America-Spain (20284 + Euro symbol); IBM EBCDIC (Spain-Euro) + (1146, "IBM01146"), # IBM EBCDIC United Kingdom (20285 + Euro symbol); IBM EBCDIC (UK-Euro) + (1147, "IBM01147"), # IBM EBCDIC France (20297 + Euro symbol); IBM EBCDIC (France-Euro) + (1148, "IBM01148"), # IBM EBCDIC International (500 + Euro symbol); IBM EBCDIC (International-Euro) + (1149, "IBM01149"), # IBM EBCDIC Icelandic (20871 + Euro symbol); IBM EBCDIC (Icelandic-Euro) + (1200, "utf-16"), # Unicode UTF-16, little endian byte order (BMP of ISO 10646); available only to managed applications + (1201, "unicodeFFFE"), # Unicode UTF-16, big endian byte order; available only to managed applications + (1250, "windows-1250"), # ANSI Central European; Central European (Windows) + (1251, "windows-1251"), # ANSI Cyrillic; Cyrillic (Windows) + (1252, "windows-1252"), # ANSI Latin 1; Western European (Windows) + (1253, "windows-1253"), # ANSI Greek; Greek (Windows) + (1254, "windows-1254"), # ANSI Turkish; Turkish (Windows) + (1255, "windows-1255"), # ANSI Hebrew; Hebrew (Windows) + (1256, "windows-1256"), # ANSI Arabic; Arabic (Windows) + (1257, "windows-1257"), # ANSI Baltic; Baltic (Windows) + (1258, "windows-1258"), # ANSI/OEM Vietnamese; Vietnamese (Windows) + + (1250, "cp-1250"), # ANSI Central European; Central European (Windows) + (1251, "cp-1251"), # ANSI Cyrillic; Cyrillic (Windows) + (1252, "cp-1252"), # ANSI Latin 1; Western European (Windows) + (1253, "cp-1253"), # ANSI Greek; Greek (Windows) + (1254, "cp-1254"), # ANSI Turkish; Turkish (Windows) + (1255, "cp-1255"), # ANSI Hebrew; Hebrew (Windows) + (1256, "cp-1256"), # ANSI Arabic; Arabic (Windows) + (1257, "cp-1257"), # ANSI Baltic; Baltic (Windows) + (1258, "cp-1258"), # ANSI/OEM Vietnamese; Vietnamese (Windows) + + (1361, "Johab"), # Korean (Johab) + (10000, "macintosh"), # MAC Roman; Western European (Mac) + (10001, "x-mac-japanese"), # Japanese (Mac) + (10002, "x-mac-chinesetrad"), # MAC Traditional Chinese (Big5); Chinese Traditional (Mac) + (10003, "x-mac-korean"), # Korean (Mac) + (10004, "x-mac-arabic"), # Arabic (Mac) + (10005, "x-mac-hebrew"), # Hebrew (Mac) + (10006, "x-mac-greek"), # Greek (Mac) + (10007, "x-mac-cyrillic"), # Cyrillic (Mac) + (10008, "x-mac-chinesesimp"), # MAC Simplified Chinese (GB 2312); Chinese Simplified (Mac) + (10010, "x-mac-romanian"), # Romanian (Mac) + (10017, "x-mac-ukrainian"), # Ukrainian (Mac) + (10021, "x-mac-thai"), # Thai (Mac) + (10029, "x-mac-ce"), # MAC Latin 2; Central European (Mac) + (10079, "x-mac-icelandic"), # Icelandic (Mac) + (10081, "x-mac-turkish"), # Turkish (Mac) + (10082, "x-mac-croatian"), # Croatian (Mac) + (12000, "utf-32"), # Unicode UTF-32, little endian byte order; available only to managed applications + (12001, "utf-32BE"), # Unicode UTF-32, big endian byte order; available only to managed applications + (20000, "x-Chinese_CNS"), # CNS Taiwan; Chinese Traditional (CNS) + (20001, "x-cp20001"), # TCA Taiwan + (20002, "x_Chinese-Eten"), # Eten Taiwan; Chinese Traditional (Eten) + (20003, "x-cp20003"), # IBM5550 Taiwan + (20004, "x-cp20004"), # TeleText Taiwan + (20005, "x-cp20005"), # Wang Taiwan + (20105, "x-IA5"), # IA5 (IRV International Alphabet No. 5, 7-bit); Western European (IA5) + (20106, "x-IA5-German"), # IA5 German (7-bit) + (20107, "x-IA5-Swedish"), # IA5 Swedish (7-bit) + (20108, "x-IA5-Norwegian"), # IA5 Norwegian (7-bit) + (20127, "us-ascii"), # US-ASCII (7-bit) + (20261, "x-cp20261"), # T.61 + (20269, "x-cp20269"), # ISO 6937 Non-Spacing Accent + (20273, "IBM273"), # IBM EBCDIC Germany + (20277, "IBM277"), # IBM EBCDIC Denmark-Norway + (20278, "IBM278"), # IBM EBCDIC Finland-Sweden + (20280, "IBM280"), # IBM EBCDIC Italy + (20284, "IBM284"), # IBM EBCDIC Latin America-Spain + (20285, "IBM285"), # IBM EBCDIC United Kingdom + (20290, "IBM290"), # IBM EBCDIC Japanese Katakana Extended + (20297, "IBM297"), # IBM EBCDIC France + (20420, "IBM420"), # IBM EBCDIC Arabic + (20423, "IBM423"), # IBM EBCDIC Greek + (20424, "IBM424"), # IBM EBCDIC Hebrew + (20833, "x-EBCDIC-KoreanExtended"), # IBM EBCDIC Korean Extended + (20838, "IBM-Thai"), # IBM EBCDIC Thai + (20866, "koi8-r"), # Russian (KOI8-R); Cyrillic (KOI8-R) + (20871, "IBM871"), # IBM EBCDIC Icelandic + (20880, "IBM880"), # IBM EBCDIC Cyrillic Russian + (20905, "IBM905"), # IBM EBCDIC Turkish + (20924, "IBM00924"), # IBM EBCDIC Latin 1/Open System (1047 + Euro symbol) + (20932, "EUC-JP"), # Japanese (JIS 0208-1990 and 0121-1990) + (20936, "x-cp20936"), # Simplified Chinese (GB2312); Chinese Simplified (GB2312-80) + (20949, "x-cp20949"), # Korean Wansung + (21025, "cp1025"), # IBM EBCDIC Cyrillic Serbian-Bulgarian + (21027, ""), # (deprecated) + (21866, "koi8-u"), # Ukrainian (KOI8-U); Cyrillic (KOI8-U) + (28591, "iso-8859-1"), # ISO 8859-1 Latin 1; Western European (ISO) + (28592, "iso-8859-2"), # ISO 8859-2 Central European; Central European (ISO) + (28593, "iso-8859-3"), # ISO 8859-3 Latin 3 + (28594, "iso-8859-4"), # ISO 8859-4 Baltic + (28595, "iso-8859-5"), # ISO 8859-5 Cyrillic + (28596, "iso-8859-6"), # ISO 8859-6 Arabic + (28597, "iso-8859-7"), # ISO 8859-7 Greek + (28598, "iso-8859-8"), # ISO 8859-8 Hebrew; Hebrew (ISO-Visual) + (28599, "iso-8859-9"), # ISO 8859-9 Turkish + (28603, "iso-8859-13"), # ISO 8859-13 Estonian + (28605, "iso-8859-15"), # ISO 8859-15 Latin 9 + (29001, "x-Europa"), # Europa 3 + (38598, "iso-8859-8-i"), # ISO 8859-8 Hebrew; Hebrew (ISO-Logical) + (50220, "iso-2022-jp"), # ISO 2022 Japanese with no halfwidth Katakana; Japanese (JIS) + (50221, "csISO2022JP"), # ISO 2022 Japanese with halfwidth Katakana; Japanese (JIS-Allow 1 byte Kana) + (50222, "iso-2022-jp"), # ISO 2022 Japanese JIS X 0201-1989; Japanese (JIS-Allow 1 byte Kana - SO/SI) + (50225, "iso-2022-kr"), # ISO 2022 Korean + (50227, "x-cp50227"), # ISO 2022 Simplified Chinese; Chinese Simplified (ISO 2022) + (50229, ""), # ISO 2022 Traditional Chinese + (50930, ""), # EBCDIC Japanese (Katakana) Extended + (50931, ""), # EBCDIC US-Canada and Japanese + (50933, ""), # EBCDIC Korean Extended and Korean + (50935, ""), # EBCDIC Simplified Chinese Extended and Simplified Chinese + (50936, ""), # EBCDIC Simplified Chinese + (50937, ""), # EBCDIC US-Canada and Traditional Chinese + (50939, ""), # EBCDIC Japanese (Latin) Extended and Japanese + (51932, "euc-jp"), # EUC Japanese + (51936, "EUC-CN"), # EUC Simplified Chinese; Chinese Simplified (EUC) + (51949, "euc-kr"), # EUC Korean + (51950, ""), # EUC Traditional Chinese + (52936, "hz-gb-2312"), # HZ-GB2312 Simplified Chinese; Chinese Simplified (HZ) + (54936, "GB18030"), # Windows XP and later: GB18030 Simplified Chinese (4 byte); Chinese Simplified (GB18030) + (57002, "x-iscii-de"), # ISCII Devanagari + (57003, "x-iscii-be"), # ISCII Bengali + (57004, "x-iscii-ta"), # ISCII Tamil + (57005, "x-iscii-te"), # ISCII Telugu + (57006, "x-iscii-as"), # ISCII Assamese + (57007, "x-iscii-or"), # ISCII Oriya + (57008, "x-iscii-ka"), # ISCII Kannada + (57009, "x-iscii-ma"), # ISCII Malayalam + (57010, "x-iscii-gu"), # ISCII Gujarati + (57011, "x-iscii-pa"), # ISCII Punjabi + (65000, "utf-7"), # Unicode (UTF-7) + (65001, "utf-8")] # Unicode (UTF-8) + when false: # not needed yet: type @@ -219,22 +219,22 @@ when defined(windows): proc getCPInfo(codePage: CodePage, lpCPInfo: var CpInfo): int32 {. stdcall, importc: "GetCPInfo", dynlib: "kernel32".} - + proc nameToCodePage(name: string): CodePage = var nameAsInt: int if parseInt(name, nameAsInt) == 0: nameAsInt = -1 for no, na in items(winEncodings): if no == nameAsInt or eqEncodingNames(na, name): return CodePage(no) result = CodePage(-1) - + proc codePageToName(c: CodePage): string = for no, na in items(winEncodings): if no == int(c): return if na.len != 0: na else: $no result = "" - + proc getACP(): CodePage {.stdcall, importc: "GetACP", dynlib: "kernel32".} - + proc multiByteToWideChar( codePage: CodePage, dwFlags: int32, @@ -254,7 +254,7 @@ when defined(windows): lpDefaultChar: cstring=nil, lpUsedDefaultChar: pointer=nil): cint {. stdcall, importc: "WideCharToMultiByte", dynlib: "kernel32".} - + else: when defined(haiku): const iconvDll = "(libc.so.6|libiconv.so|libtextencoding.so)" @@ -292,31 +292,31 @@ else: proc iconv(c: EncodingConverter, inbuf: pointer, inbytesLeft: pointer, outbuf: var cstring, outbytesLeft: var int): int {. importc: prefix & "iconv", cdecl, dynlib: iconvDll.} - + proc getCurrentEncoding*(): string = ## retrieves the current encoding. On Unix, always "UTF-8" is returned. when defined(windows): result = codePageToName(getACP()) else: result = "UTF-8" - + proc open*(destEncoding = "UTF-8", srcEncoding = "CP1252"): EncodingConverter = ## opens a converter that can convert from `srcEncoding` to `destEncoding`. ## Raises `EIO` if it cannot fulfill the request. when not defined(windows): result = iconvOpen(destEncoding, srcEncoding) if result == nil: - raise newException(EncodingError, - "cannot create encoding converter from " & + raise newException(EncodingError, + "cannot create encoding converter from " & srcEncoding & " to " & destEncoding) else: result.dest = nameToCodePage(destEncoding) result.src = nameToCodePage(srcEncoding) if int(result.dest) == -1: - raise newException(EncodingError, + raise newException(EncodingError, "cannot find encoding " & destEncoding) if int(result.src) == -1: - raise newException(EncodingError, + raise newException(EncodingError, "cannot find encoding " & srcEncoding) proc close*(c: EncodingConverter) = @@ -328,7 +328,7 @@ when defined(windows): proc convert*(c: EncodingConverter, s: string): string = ## converts `s` to `destEncoding` that was given to the converter `c`. It ## assumed that `s` is in `srcEncoding`. - + # special case: empty string: needed because MultiByteToWideChar # return 0 in case of error: if s.len == 0: return "" @@ -336,21 +336,21 @@ when defined(windows): var cap = s.len + s.len shr 2 result = newStringOfCap(cap*2) # convert to utf-16 LE - var m = multiByteToWideChar(codePage = c.src, dwFlags = 0'i32, + var m = multiByteToWideChar(codePage = c.src, dwFlags = 0'i32, lpMultiByteStr = cstring(s), cbMultiByte = cint(s.len), lpWideCharStr = cstring(result), cchWideChar = cint(cap)) - if m == 0: + if m == 0: # try again; ask for capacity: - cap = multiByteToWideChar(codePage = c.src, dwFlags = 0'i32, + cap = multiByteToWideChar(codePage = c.src, dwFlags = 0'i32, lpMultiByteStr = cstring(s), cbMultiByte = cint(s.len), lpWideCharStr = nil, cchWideChar = cint(0)) # and do the conversion properly: result = newStringOfCap(cap*2) - m = multiByteToWideChar(codePage = c.src, dwFlags = 0'i32, + m = multiByteToWideChar(codePage = c.src, dwFlags = 0'i32, lpMultiByteStr = cstring(s), cbMultiByte = cint(s.len), lpWideCharStr = cstring(result), @@ -361,7 +361,7 @@ when defined(windows): setLen(result, m*2) else: assert(false) # cannot happen - + # if already utf-16 LE, no further need to do something: if int(c.dest) == 1200: return # otherwise the fun starts again: @@ -428,7 +428,7 @@ else: outLen = len(result) - offset else: raiseOSError(lerr.OSErrorCode) - # iconv has a buffer that needs flushing, specially if the last char is + # iconv has a buffer that needs flushing, specially if the last char is # not '\0' discard iconv(c, nil, nil, dst, outLen) if iconvres == cint(-1) and errno == E2BIG: @@ -441,7 +441,7 @@ else: # trim output buffer setLen(result, len(result) - outLen) -proc convert*(s: string, destEncoding = "UTF-8", +proc convert*(s: string, destEncoding = "UTF-8", srcEncoding = "CP1252"): string = ## converts `s` to `destEncoding`. It assumed that `s` is in `srcEncoding`. ## This opens a converter, uses it and closes it again and is thus more diff --git a/lib/pure/endians.nim b/lib/pure/endians.nim index 6e33d4624..5a23169d4 100644 --- a/lib/pure/endians.nim +++ b/lib/pure/endians.nim @@ -24,7 +24,7 @@ proc swapEndian64*(outp, inp: pointer) = o[6] = i[1] o[7] = i[0] -proc swapEndian32*(outp, inp: pointer) = +proc swapEndian32*(outp, inp: pointer) = ## copies `inp` to `outp` swapping bytes. Both buffers are supposed to ## contain at least 4 bytes. var i = cast[cstring](inp) @@ -34,7 +34,7 @@ proc swapEndian32*(outp, inp: pointer) = o[2] = i[1] o[3] = i[0] -proc swapEndian16*(outp, inp: pointer) = +proc swapEndian16*(outp, inp: pointer) = ## copies `inp` to `outp` swapping bytes. Both buffers are supposed to ## contain at least 2 bytes. var @@ -50,7 +50,7 @@ when system.cpuEndian == bigEndian: proc bigEndian64*(outp, inp: pointer) {.inline.} = copyMem(outp, inp, 8) proc bigEndian32*(outp, inp: pointer) {.inline.} = copyMem(outp, inp, 4) proc bigEndian16*(outp, inp: pointer) {.inline.} = copyMem(outp, inp, 2) -else: +else: proc littleEndian64*(outp, inp: pointer) {.inline.} = copyMem(outp, inp, 8) proc littleEndian32*(outp, inp: pointer) {.inline.} = copyMem(outp, inp, 4) proc littleEndian16*(outp, inp: pointer){.inline.} = copyMem(outp, inp, 2) diff --git a/lib/pure/etcpriv.nim b/lib/pure/etcpriv.nim index e7a525e4d..5b785b051 100644 --- a/lib/pure/etcpriv.nim +++ b/lib/pure/etcpriv.nim @@ -18,6 +18,6 @@ const magicIdentSeparatorRuneByteWidth* = 3 # Used by pure/hashes.nim, and the compiler parsing proc isMagicIdentSeparatorRune*(cs: cstring, i: int): bool {. inline } = - result = cs[i] == '\226' and + result = cs[i] == '\226' and cs[i + 1] == '\128' and cs[i + 2] == '\147' # en-dash # 145 = nb-hyphen diff --git a/lib/pure/events.nim b/lib/pure/events.nim index 44e9ed286..01b64c545 100644 --- a/lib/pure/events.nim +++ b/lib/pure/events.nim @@ -10,7 +10,7 @@ ## :Author: Alex Mitchell ## ## This module implements an event system that is not dependent on external -## graphical toolkits. It was originally called ``NimEE`` because +## graphical toolkits. It was originally called ``NimEE`` because ## it was inspired by Python's PyEE module. There are two ways you can use ## events: one is a python-inspired way; the other is more of a C-style way. ## @@ -23,7 +23,7 @@ ## # Python way ## ee.on("EventName", handleevent) ## ee.emit("EventName", genericargs) -## +## ## # C/Java way ## # Declare a type ## type @@ -45,7 +45,7 @@ type {.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 = @[] @@ -61,7 +61,7 @@ proc removeHandler*(handler: var EventHandler, fn: proc(e: EventArgs) {.closure. if fn == handler.handlers[i]: handler.handlers.del(i) break - + proc containsHandler*(handler: var EventHandler, fn: proc(e: EventArgs) {.closure.}): bool = ## Checks if a callback is registered to this event handler. return handler.handlers.contains(fn) @@ -86,8 +86,8 @@ proc on*(emitter: var EventEmitter, event: string, fn: proc(e: EventArgs) {.clos emitter.s.add(eh) else: addHandler(emitter.s[i], fn) - -proc emit*(emitter: var EventEmitter, eventhandler: var EventHandler, + +proc emit*(emitter: var EventEmitter, eventhandler: var EventHandler, args: EventArgs) = ## Fires an event handler with specified event arguments. for fn in items(eventhandler.handlers): fn(args) diff --git a/lib/pure/fsmonitor.nim b/lib/pure/fsmonitor.nim index 229df80b5..787acb5d4 100644 --- a/lib/pure/fsmonitor.nim +++ b/lib/pure/fsmonitor.nim @@ -30,7 +30,7 @@ type fd: cint handleEvent: proc (m: FSMonitor, ev: MonitorEvent) {.closure.} targets: Table[cint, string] - + MonitorEventType* = enum ## Monitor event type MonitorAccess, ## File was accessed. MonitorAttrib, ## Metadata changed. @@ -44,7 +44,7 @@ type MonitorMoved, ## File was moved. MonitorOpen, ## File was opened. MonitorAll ## Filter for all event types. - + MonitorEvent* = object case kind*: MonitorEventType ## Type of the event. of MonitorMoveSelf, MonitorMoved: @@ -77,7 +77,7 @@ proc add*(monitor: FSMonitor, target: string, ## Adds ``target`` which may be a directory or a file to the list of ## watched paths of ``monitor``. ## You can specify the events to report using the ``filters`` parameter. - + var INFilter = -1 for f in filters: case f @@ -93,7 +93,7 @@ proc add*(monitor: FSMonitor, target: string, of MonitorMoved: INFilter = INFilter and IN_MOVED_FROM and IN_MOVED_TO of MonitorOpen: INFilter = INFilter and IN_OPEN of MonitorAll: INFilter = INFilter and IN_ALL_EVENTS - + result = inotifyAddWatch(monitor.fd, target, INFilter.uint32) if result < 0: raiseOSError(osLastError()) @@ -125,13 +125,13 @@ proc getEvent(m: FSMonitor, fd: cint): seq[MonitorEvent] = mev.name = $cstr else: mev.name = "" - - if (event.mask.int and IN_MOVED_FROM) != 0: + + if (event.mask.int and IN_MOVED_FROM) != 0: # Moved from event, add to m's collection movedFrom.add(event.cookie.cint, (mev.wd, mev.name)) inc(i, sizeof(INotifyEvent) + event.len.int) continue - elif (event.mask.int and IN_MOVED_TO) != 0: + elif (event.mask.int and IN_MOVED_TO) != 0: mev.kind = MonitorMoved assert movedFrom.hasKey(event.cookie.cint) # Find the MovedFrom event. @@ -141,23 +141,23 @@ proc getEvent(m: FSMonitor, fd: cint): seq[MonitorEvent] = movedFrom.del(event.cookie.cint) elif (event.mask.int and IN_ACCESS) != 0: mev.kind = MonitorAccess elif (event.mask.int and IN_ATTRIB) != 0: mev.kind = MonitorAttrib - elif (event.mask.int and IN_CLOSE_WRITE) != 0: + elif (event.mask.int and IN_CLOSE_WRITE) != 0: mev.kind = MonitorCloseWrite - elif (event.mask.int and IN_CLOSE_NOWRITE) != 0: + elif (event.mask.int and IN_CLOSE_NOWRITE) != 0: mev.kind = MonitorCloseNoWrite elif (event.mask.int and IN_CREATE) != 0: mev.kind = MonitorCreate - elif (event.mask.int and IN_DELETE) != 0: + elif (event.mask.int and IN_DELETE) != 0: mev.kind = MonitorDelete - elif (event.mask.int and IN_DELETE_SELF) != 0: + elif (event.mask.int and IN_DELETE_SELF) != 0: mev.kind = MonitorDeleteSelf elif (event.mask.int and IN_MODIFY) != 0: mev.kind = MonitorModify - elif (event.mask.int and IN_MOVE_SELF) != 0: + elif (event.mask.int and IN_MOVE_SELF) != 0: mev.kind = MonitorMoveSelf elif (event.mask.int and IN_OPEN) != 0: mev.kind = MonitorOpen - + if mev.kind != MonitorMoved: mev.fullname = "" - + result.add(mev) inc(i, sizeof(INotifyEvent) + event.len.int) @@ -211,7 +211,7 @@ when not defined(testing) and isMainModule: echo("Name is ", ev.name) else: echo("Name ", ev.name, " fullname ", ev.fullName)) - + while true: if not disp.poll(): break main() diff --git a/lib/pure/httpserver.nim b/lib/pure/httpserver.nim index 981891227..71ba04991 100644 --- a/lib/pure/httpserver.nim +++ b/lib/pure/httpserver.nim @@ -142,7 +142,7 @@ when false: if meth == reqPost: # get from client and post to CGI program: var buf = alloc(contentLength) - if recv(client, buf, contentLength) != contentLength: + if recv(client, buf, contentLength) != contentLength: dealloc(buf) raiseOSError() var inp = process.inputStream @@ -177,7 +177,7 @@ when false: else: path = "." & data[1] # path starts with "/", by adding "." in front of it we serve files from cwd - + if cmpIgnoreCase(data[0], "GET") == 0: if q >= 0: cgi = true @@ -218,12 +218,12 @@ type headers*: StringTableRef ## headers with which the client made the request body*: string ## only set with POST requests ip*: string ## ip address of the requesting client - + 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 ## acquired that can be accessed later by the ``port`` proc. @@ -262,7 +262,7 @@ proc next*(s: var Server) = var data = "" s.client.readLine(data) if data == "": - # Socket disconnected + # Socket disconnected s.client.close() next(s) return @@ -283,9 +283,9 @@ proc next*(s: var Server) = s.client.close() next(s) return - + var i = skipWhitespace(data) - if skipIgnoreCase(data, "GET") > 0: + if skipIgnoreCase(data, "GET") > 0: s.reqMethod = "GET" inc(i, 3) elif skipIgnoreCase(data, "POST") > 0: @@ -296,7 +296,7 @@ proc next*(s: var Server) = s.client.close() next(s) return - + if s.reqMethod == "POST": # Check for Expect header if s.headers.hasKey("Expect"): @@ -304,7 +304,7 @@ proc next*(s: var Server) = s.client.sendStatus("100 Continue") else: s.client.sendStatus("417 Expectation Failed") - + # Read the body # - Check for Content-length header if s.headers.hasKey("Content-Length"): @@ -340,13 +340,13 @@ proc next*(s: var Server) = s.client.close() next(s) return - + var L = skipWhitespace(data, i) inc(i, L) # XXX we ignore "HTTP/1.1" etc. for now here var query = 0 var last = i - while last < data.len and data[last] notin Whitespace: + while last < data.len and data[last] notin Whitespace: if data[last] == '?' and query == 0: query = last inc(last) if query > 0: @@ -360,7 +360,7 @@ proc close*(s: Server) = ## closes the server (and the socket the server uses). close(s.socket) -proc run*(handleRequest: proc (client: Socket, +proc run*(handleRequest: proc (client: Socket, path, query: string): bool {.closure.}, port = Port(80)) = ## encapsulates the server object and main loop @@ -388,7 +388,7 @@ proc nextAsync(s: PAsyncHTTPServer) = var data = "" s.client.readLine(data) if data == "": - # Socket disconnected + # Socket disconnected s.client.close() return var header = "" @@ -408,9 +408,9 @@ proc nextAsync(s: PAsyncHTTPServer) = else: s.client.close() return - + var i = skipWhitespace(data) - if skipIgnoreCase(data, "GET") > 0: + if skipIgnoreCase(data, "GET") > 0: s.reqMethod = "GET" inc(i, 3) elif skipIgnoreCase(data, "POST") > 0: @@ -420,7 +420,7 @@ proc nextAsync(s: PAsyncHTTPServer) = unimplemented(s.client) s.client.close() return - + if s.reqMethod == "POST": # Check for Expect header if s.headers.hasKey("Expect"): @@ -428,7 +428,7 @@ proc nextAsync(s: PAsyncHTTPServer) = s.client.sendStatus("100 Continue") else: s.client.sendStatus("417 Expectation Failed") - + # Read the body # - Check for Content-length header if s.headers.hasKey("Content-Length"): @@ -460,13 +460,13 @@ proc nextAsync(s: PAsyncHTTPServer) = badRequest(s.client) s.client.close() return - + var L = skipWhitespace(data, i) inc(i, L) # XXX we ignore "HTTP/1.1" etc. for now here var query = 0 var last = i - while last < data.len and data[last] notin Whitespace: + while last < data.len and data[last] notin Whitespace: if data[last] == '?' and query == 0: query = last inc(last) if query > 0: @@ -476,7 +476,7 @@ proc nextAsync(s: PAsyncHTTPServer) = s.query = "" s.path = data.substr(i, last-1) -proc asyncHTTPServer*(handleRequest: proc (server: PAsyncHTTPServer, client: Socket, +proc asyncHTTPServer*(handleRequest: proc (server: PAsyncHTTPServer, client: Socket, path, query: string): bool {.closure, gcsafe.}, port = Port(80), address = "", reuseAddr = false): PAsyncHTTPServer = @@ -492,14 +492,14 @@ proc asyncHTTPServer*(handleRequest: proc (server: PAsyncHTTPServer, client: Soc if quit: capturedRet.asyncSocket.close() if reuseAddr: capturedRet.asyncSocket.setSockOpt(OptReuseAddr, true) - + capturedRet.asyncSocket.bindAddr(port, address) capturedRet.asyncSocket.listen() if port == Port(0): capturedRet.port = getSockName(capturedRet.asyncSocket) else: capturedRet.port = port - + capturedRet.client = invalidSocket capturedRet.reqMethod = "" capturedRet.body = "" @@ -524,11 +524,11 @@ when not defined(testing) and isMainModule: echo("httpserver running on port ", s.port) while true: next(s) - + inc(counter) s.client.send("Hello, Andreas, for the $#th time. $# ? $#" % [ $counter, s.path, s.query] & wwwNL) - + close(s.client) close(s) diff --git a/lib/pure/matchers.nim b/lib/pure/matchers.nim index d55963c15..5c28f65a0 100644 --- a/lib/pure/matchers.nim +++ b/lib/pure/matchers.nim @@ -18,15 +18,15 @@ include "system/inclrtl" import parseutils, strutils proc validEmailAddress*(s: string): bool {.noSideEffect, - rtl, extern: "nsuValidEmailAddress".} = - ## returns true if `s` seems to be a valid e-mail address. + rtl, extern: "nsuValidEmailAddress".} = + ## returns true if `s` seems to be a valid e-mail address. ## The checking also uses a domain list. const chars = Letters + Digits + {'!','#','$','%','&', '\'','*','+','/','=','?','^','_','`','{','}','|','~','-','.'} var i = 0 if s[i] notin chars or s[i] == '.': return false - while s[i] in chars: + while s[i] in chars: if s[i] == '.' and s[i+1] == '.': return false inc(i) if s[i] != '@': return false @@ -34,9 +34,9 @@ proc validEmailAddress*(s: string): bool {.noSideEffect, if 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) + while s[i] in {'0'..'9', 'a'..'z', '-', '.'}: inc(i) if s[i] != '\0': 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) @@ -59,6 +59,6 @@ proc parseInt*(s: string, value: var int, validRange: Slice[int]) {. when isMainModule: doAssert "wuseldusel@codehome.com".validEmailAddress - + {.pop.} diff --git a/lib/pure/memfiles.nim b/lib/pure/memfiles.nim index 00929eaa2..27b989597 100644 --- a/lib/pure/memfiles.nim +++ b/lib/pure/memfiles.nim @@ -33,7 +33,7 @@ type when defined(windows): fHandle: int - mapHandle: int + mapHandle: int else: handle: cint @@ -131,7 +131,7 @@ proc open*(filename: string, mode: FileMode = fmRead, fail(osLastError(), "error opening file") if newFileSize != -1: - var + var sizeHigh = int32(newFileSize shr 32) sizeLow = int32(newFileSize and 0xffffffff) @@ -177,7 +177,7 @@ proc open*(filename: string, mode: FileMode = fmRead, rollback() if result.handle != 0: discard close(result.handle) raiseOSError(errCode) - + var flags = if readonly: O_RDONLY else: O_RDWR if newFileSize != -1: @@ -221,7 +221,7 @@ proc open*(filename: string, mode: FileMode = fmRead, proc close*(f: var MemFile) = ## closes the memory mapped file `f`. All changes are written back to the ## file system, if `f` was opened with write access. - + var error = false var lastErr: OSErrorCode @@ -245,7 +245,7 @@ proc close*(f: var MemFile) = f.mapHandle = 0 else: f.handle = 0 - + if error: raiseOSError(lastErr) type MemSlice* = object ## represent slice of a MemFile for iteration over delimited lines/records diff --git a/lib/pure/mersenne.nim b/lib/pure/mersenne.nim index 74112e304..c8090dc6a 100644 --- a/lib/pure/mersenne.nim +++ b/lib/pure/mersenne.nim @@ -7,7 +7,7 @@ type {.deprecated: [TMersenneTwister: MersenneTwister].} -proc newMersenneTwister*(seed: int): MersenneTwister = +proc newMersenneTwister*(seed: int): MersenneTwister = result.index = 0 result.mt[0]= uint32(seed) for i in 1..623'u32: diff --git a/lib/pure/numeric.nim b/lib/pure/numeric.nim index 9b298c0a0..71adf19b3 100644 --- a/lib/pure/numeric.nim +++ b/lib/pure/numeric.nim @@ -11,23 +11,23 @@ type OneVarFunction* = proc (x: float): float {.deprecated: [TOneVarFunction: OneVarFunction].} -proc brent*(xmin,xmax:float, function:OneVarFunction, tol:float,maxiter=1000): +proc brent*(xmin,xmax:float, function:OneVarFunction, tol:float,maxiter=1000): tuple[rootx, rooty: float, success: bool]= - ## Searches `function` for a root between `xmin` and `xmax` + ## Searches `function` for a root between `xmin` and `xmax` ## using brents method. If the function value at `xmin`and `xmax` has the ## same sign, `rootx`/`rooty` is set too the extrema value closest to x-axis ## and succes is set to false. ## Otherwise there exists at least one root and success is set to true. ## This root is searched for at most `maxiter` iterations. - ## If `tol` tolerance is reached within `maxiter` iterations + ## If `tol` tolerance is reached within `maxiter` iterations ## the root refinement stops and success=true. # see http://en.wikipedia.org/wiki/Brent%27s_method - var + var a=xmin b=xmax c=a - d=1.0e308 + d=1.0e308 fa=function(a) fb=function(b) fc=fa @@ -42,19 +42,19 @@ proc brent*(xmin,xmax:float, function:OneVarFunction, tol:float,maxiter=1000): return (a,fa,false) else: return (b,fb,false) - + if abs(fa)<abs(fb): swap(fa,fb) swap(a,b) - + while fb!=0.0 and abs(a-b)>tol: if fa!=fc and fb!=fc: # inverse quadratic interpolation s = a * fb * fc / (fa - fb) / (fa - fc) + b * fa * fc / (fb - fa) / (fb - fc) + c * fa * fb / (fc - fa) / (fc - fb) else: #secant rule s = b - fb * (b - a) / (fb - fa) tmp2 = (3.0 * a + b) / 4.0 - if not((s > tmp2 and s < b) or (s < tmp2 and s > b)) or - (mflag and abs(s - b) >= (abs(b - c) / 2.0)) or + if not((s > tmp2 and s < b) or (s < tmp2 and s > b)) or + (mflag and abs(s - b) >= (abs(b - c) / 2.0)) or (not mflag and abs(s - b) >= abs(c - d) / 2.0): s=(a+b)/2.0 mflag=true @@ -80,5 +80,5 @@ proc brent*(xmin,xmax:float, function:OneVarFunction, tol:float,maxiter=1000): inc i if i>maxiter: break - + return (b,fb,true) diff --git a/lib/pure/oids.nim b/lib/pure/oids.nim index ac90dd16b..174922223 100644 --- a/lib/pure/oids.nim +++ b/lib/pure/oids.nim @@ -8,7 +8,7 @@ # ## Nim OID support. An OID is a global ID that consists of a timestamp, -## a unique counter and a random value. This combination should suffice to +## a unique counter and a random value. This combination should suffice to ## produce a globally distributed unique ID. This implementation was extracted ## from the Mongodb interface and it thus binary compatible with a Mongo OID. ## @@ -19,13 +19,13 @@ import times, endians type Oid* = object ## an OID - time: int32 ## - fuzz: int32 ## - count: int32 ## + time: int32 ## + fuzz: int32 ## + count: int32 ## {.deprecated: [Toid: Oid].} -proc hexbyte*(hex: char): int = +proc hexbyte*(hex: char): int = case hex of '0'..'9': result = (ord(hex) - ord('0')) of 'a'..'f': result = (ord(hex) - ord('a') + 10) @@ -40,7 +40,7 @@ proc parseOid*(str: cstring): Oid = bytes[i] = chr((hexbyte(str[2 * i]) shl 4) or hexbyte(str[2 * i + 1])) inc(i) -proc oidToString*(oid: Oid, str: cstring) = +proc oidToString*(oid: Oid, str: cstring) = const hex = "0123456789abcdef" # work around a compiler bug: var str = str @@ -59,7 +59,7 @@ proc `$`*(oid: Oid): string = oidToString(oid, result) var - incr: int + incr: int fuzz: int32 proc genOid*(): Oid = @@ -69,10 +69,10 @@ proc genOid*(): Oid = proc srand(seed: cint) {.importc: "srand", header: "<stdlib.h>", nodecl.} var t = gettime(nil) - + var i = int32(incr) atomicInc(incr) - + if fuzz == 0: # racy, but fine semantically: srand(t) diff --git a/lib/pure/parsecfg.nim b/lib/pure/parsecfg.nim index bb64c8134..f680d4fef 100644 --- a/lib/pure/parsecfg.nim +++ b/lib/pure/parsecfg.nim @@ -7,17 +7,17 @@ # distribution, for details about the copyright. # -## The ``parsecfg`` module implements a high performance configuration file -## parser. The configuration file's syntax is similar to the Windows ``.ini`` -## format, but much more powerful, as it is not a line based parser. String -## literals, raw string literals and triple quoted string literals are supported +## The ``parsecfg`` module implements a high performance configuration file +## parser. The configuration file's syntax is similar to the Windows ``.ini`` +## format, but much more powerful, as it is not a line based parser. String +## literals, raw string literals and triple quoted string literals are supported ## as in the Nim programming language. ## This is an example of how a configuration file may look like: ## ## .. include:: doc/mytest.cfg ## :literal: -## The file ``examples/parsecfgex.nim`` demonstrates how to use the +## The file ``examples/parsecfgex.nim`` demonstrates how to use the ## configuration file parser: ## ## .. code-block:: nim @@ -36,14 +36,14 @@ type cfgKeyValuePair, ## a ``key=value`` pair has been detected cfgOption, ## a ``--key=value`` command line option cfgError ## an error occurred during parsing - + CfgEvent* = object of RootObj ## describes a parsing event case kind*: CfgEventKind ## the kind of the event of cfgEof: nil - of cfgSectionStart: - section*: string ## `section` contains the name of the + of cfgSectionStart: + section*: string ## `section` contains the name of the ## parsed section start (syntax: ``[section]``) - of cfgKeyValuePair, cfgOption: + of cfgKeyValuePair, cfgOption: key*, value*: string ## contains the (key, value) pair if an option ## of the form ``--key: value`` or an ordinary ## ``key= value`` pair has been parsed. @@ -52,14 +52,14 @@ type of cfgError: ## the parser encountered an error: `msg` msg*: string ## contains the error message. No exceptions ## are thrown if a parse error occurs. - - TokKind = enum - tkInvalid, tkEof, + + TokKind = enum + tkInvalid, tkEof, tkSymbol, tkEquals, tkColon, tkBracketLe, tkBracketRi, tkDashDash Token = object # a token kind: TokKind # the type of the token literal: string # the parsed (string) literal - + CfgParser* = object of BaseLexer ## the parser object. tok: Token filename: string @@ -69,12 +69,12 @@ type # implementation -const - SymChars = {'a'..'z', 'A'..'Z', '0'..'9', '_', '\x80'..'\xFF', '.', '/', '\\'} - +const + SymChars = {'a'..'z', 'A'..'Z', '0'..'9', '_', '\x80'..'\xFF', '.', '/', '\\'} + proc rawGetTok(c: var CfgParser, tok: var Token) {.gcsafe.} -proc open*(c: var CfgParser, input: Stream, filename: string, +proc open*(c: var CfgParser, input: Stream, filename: string, lineOffset = 0) {.rtl, extern: "npc$1".} = ## initializes the parser with an input stream. `Filename` is only used ## for nice error messages. `lineOffset` can be used to influence the line @@ -85,7 +85,7 @@ proc open*(c: var CfgParser, input: Stream, filename: string, c.tok.literal = "" inc(c.lineNumber, lineOffset) rawGetTok(c, c.tok) - + proc close*(c: var CfgParser) {.rtl, extern: "npc$1".} = ## closes the parser `c` and its associated input stream. lexbase.close(c) @@ -102,260 +102,260 @@ proc getFilename*(c: CfgParser): string {.rtl, extern: "npc$1".} = ## get the filename of the file that the parser processes. result = c.filename -proc handleHexChar(c: var CfgParser, xi: var int) = +proc handleHexChar(c: var CfgParser, xi: var int) = case c.buf[c.bufpos] - of '0'..'9': + of '0'..'9': xi = (xi shl 4) or (ord(c.buf[c.bufpos]) - ord('0')) inc(c.bufpos) - of 'a'..'f': + of 'a'..'f': xi = (xi shl 4) or (ord(c.buf[c.bufpos]) - ord('a') + 10) inc(c.bufpos) - of 'A'..'F': + of 'A'..'F': xi = (xi shl 4) or (ord(c.buf[c.bufpos]) - ord('A') + 10) inc(c.bufpos) - else: + else: discard -proc handleDecChars(c: var CfgParser, xi: var int) = - while c.buf[c.bufpos] in {'0'..'9'}: +proc handleDecChars(c: var CfgParser, xi: var int) = + while c.buf[c.bufpos] in {'0'..'9'}: xi = (xi * 10) + (ord(c.buf[c.bufpos]) - ord('0')) inc(c.bufpos) -proc getEscapedChar(c: var CfgParser, tok: var Token) = +proc getEscapedChar(c: var CfgParser, tok: var Token) = inc(c.bufpos) # skip '\' case c.buf[c.bufpos] - of 'n', 'N': + of 'n', 'N': add(tok.literal, "\n") inc(c.bufpos) - of 'r', 'R', 'c', 'C': + of 'r', 'R', 'c', 'C': add(tok.literal, '\c') inc(c.bufpos) - of 'l', 'L': + of 'l', 'L': add(tok.literal, '\L') inc(c.bufpos) - of 'f', 'F': + of 'f', 'F': add(tok.literal, '\f') inc(c.bufpos) - of 'e', 'E': + of 'e', 'E': add(tok.literal, '\e') inc(c.bufpos) - of 'a', 'A': + of 'a', 'A': add(tok.literal, '\a') inc(c.bufpos) - of 'b', 'B': + of 'b', 'B': add(tok.literal, '\b') inc(c.bufpos) - of 'v', 'V': + of 'v', 'V': add(tok.literal, '\v') inc(c.bufpos) - of 't', 'T': + of 't', 'T': add(tok.literal, '\t') inc(c.bufpos) - of '\'', '"': + of '\'', '"': add(tok.literal, c.buf[c.bufpos]) inc(c.bufpos) - of '\\': + of '\\': add(tok.literal, '\\') inc(c.bufpos) - of 'x', 'X': + of 'x', 'X': inc(c.bufpos) var xi = 0 handleHexChar(c, xi) handleHexChar(c, xi) add(tok.literal, chr(xi)) - of '0'..'9': + of '0'..'9': var xi = 0 handleDecChars(c, xi) if (xi <= 255): add(tok.literal, chr(xi)) else: tok.kind = tkInvalid else: tok.kind = tkInvalid - -proc handleCRLF(c: var CfgParser, pos: int): int = + +proc handleCRLF(c: var CfgParser, pos: int): int = case c.buf[pos] of '\c': result = lexbase.handleCR(c, pos) of '\L': result = lexbase.handleLF(c, pos) else: result = pos - -proc getString(c: var CfgParser, tok: var Token, rawMode: bool) = + +proc getString(c: var CfgParser, tok: var Token, rawMode: bool) = var pos = c.bufpos + 1 # skip " var buf = c.buf # put `buf` in a register tok.kind = tkSymbol - if (buf[pos] == '"') and (buf[pos + 1] == '"'): + if (buf[pos] == '"') and (buf[pos + 1] == '"'): # long string literal: inc(pos, 2) # skip "" # skip leading newline: pos = handleCRLF(c, pos) buf = c.buf - while true: + while true: case buf[pos] - of '"': - if (buf[pos + 1] == '"') and (buf[pos + 2] == '"'): break + of '"': + if (buf[pos + 1] == '"') and (buf[pos + 2] == '"'): break add(tok.literal, '"') inc(pos) - of '\c', '\L': + of '\c', '\L': pos = handleCRLF(c, pos) buf = c.buf add(tok.literal, "\n") - of lexbase.EndOfFile: + of lexbase.EndOfFile: tok.kind = tkInvalid - break - else: + break + else: add(tok.literal, buf[pos]) inc(pos) c.bufpos = pos + 3 # skip the three """ - else: + else: # ordinary string literal - while true: + while true: var ch = buf[pos] - if ch == '"': + if ch == '"': inc(pos) # skip '"' - break - if ch in {'\c', '\L', lexbase.EndOfFile}: + break + if ch in {'\c', '\L', lexbase.EndOfFile}: tok.kind = tkInvalid - break - if (ch == '\\') and not rawMode: + break + if (ch == '\\') and not rawMode: c.bufpos = pos getEscapedChar(c, tok) pos = c.bufpos - else: + else: add(tok.literal, ch) inc(pos) c.bufpos = pos -proc getSymbol(c: var CfgParser, tok: var Token) = +proc getSymbol(c: var CfgParser, tok: var Token) = var pos = c.bufpos var buf = c.buf - while true: + while true: add(tok.literal, buf[pos]) inc(pos) - if not (buf[pos] in SymChars): break + if not (buf[pos] in SymChars): break c.bufpos = pos tok.kind = tkSymbol -proc skip(c: var CfgParser) = +proc skip(c: var CfgParser) = var pos = c.bufpos var buf = c.buf - while true: + while true: case buf[pos] - of ' ', '\t': + of ' ', '\t': inc(pos) - of '#', ';': + of '#', ';': while not (buf[pos] in {'\c', '\L', lexbase.EndOfFile}): inc(pos) - of '\c', '\L': + of '\c', '\L': pos = handleCRLF(c, pos) buf = c.buf - else: + else: break # EndOfFile also leaves the loop c.bufpos = pos -proc rawGetTok(c: var CfgParser, tok: var Token) = +proc rawGetTok(c: var CfgParser, tok: var Token) = tok.kind = tkInvalid setLen(tok.literal, 0) skip(c) case c.buf[c.bufpos] - of '=': + of '=': tok.kind = tkEquals inc(c.bufpos) tok.literal = "=" - of '-': + of '-': inc(c.bufpos) if c.buf[c.bufpos] == '-': inc(c.bufpos) tok.kind = tkDashDash tok.literal = "--" - of ':': + of ':': tok.kind = tkColon inc(c.bufpos) tok.literal = ":" - of 'r', 'R': - if c.buf[c.bufpos + 1] == '\"': + of 'r', 'R': + if c.buf[c.bufpos + 1] == '\"': inc(c.bufpos) getString(c, tok, true) - else: + else: getSymbol(c, tok) - of '[': + of '[': tok.kind = tkBracketLe inc(c.bufpos) tok.literal = "]" - of ']': + of ']': tok.kind = tkBracketRi inc(c.bufpos) tok.literal = "]" - of '"': + of '"': getString(c, tok, false) - of lexbase.EndOfFile: + of lexbase.EndOfFile: tok.kind = tkEof tok.literal = "[EOF]" else: getSymbol(c, tok) - + proc errorStr*(c: CfgParser, msg: string): string {.rtl, extern: "npc$1".} = ## returns a properly formated error message containing current line and ## column information. - result = `%`("$1($2, $3) Error: $4", + result = `%`("$1($2, $3) Error: $4", [c.filename, $getLine(c), $getColumn(c), msg]) - + proc warningStr*(c: CfgParser, msg: string): string {.rtl, extern: "npc$1".} = ## returns a properly formated warning message containing current line and ## column information. - result = `%`("$1($2, $3) Warning: $4", + result = `%`("$1($2, $3) Warning: $4", [c.filename, $getLine(c), $getColumn(c), msg]) proc ignoreMsg*(c: CfgParser, e: CfgEvent): string {.rtl, extern: "npc$1".} = ## returns a properly formated warning message containing that ## an entry is ignored. - case e.kind + case e.kind of cfgSectionStart: result = c.warningStr("section ignored: " & e.section) of cfgKeyValuePair: result = c.warningStr("key ignored: " & e.key) - of cfgOption: + of cfgOption: result = c.warningStr("command ignored: " & e.key & ": " & e.value) of cfgError: result = e.msg of cfgEof: result = "" -proc getKeyValPair(c: var CfgParser, kind: CfgEventKind): CfgEvent = - if c.tok.kind == tkSymbol: +proc getKeyValPair(c: var CfgParser, kind: CfgEventKind): CfgEvent = + if c.tok.kind == tkSymbol: result.kind = kind result.key = c.tok.literal result.value = "" rawGetTok(c, c.tok) - if c.tok.kind in {tkEquals, tkColon}: + if c.tok.kind in {tkEquals, tkColon}: rawGetTok(c, c.tok) - if c.tok.kind == tkSymbol: + if c.tok.kind == tkSymbol: result.value = c.tok.literal - else: + else: reset result result.kind = cfgError result.msg = errorStr(c, "symbol expected, but found: " & c.tok.literal) rawGetTok(c, c.tok) - else: + else: result.kind = cfgError result.msg = errorStr(c, "symbol expected, but found: " & c.tok.literal) rawGetTok(c, c.tok) proc next*(c: var CfgParser): CfgEvent {.rtl, extern: "npc$1".} = ## retrieves the first/next event. This controls the parser. - case c.tok.kind - of tkEof: + case c.tok.kind + of tkEof: result.kind = cfgEof - of tkDashDash: + of tkDashDash: rawGetTok(c, c.tok) result = getKeyValPair(c, cfgOption) - of tkSymbol: + of tkSymbol: result = getKeyValPair(c, cfgKeyValuePair) - of tkBracketLe: + of tkBracketLe: rawGetTok(c, c.tok) - if c.tok.kind == tkSymbol: + if c.tok.kind == tkSymbol: result.kind = cfgSectionStart result.section = c.tok.literal - else: + else: result.kind = cfgError result.msg = errorStr(c, "symbol expected, but found: " & c.tok.literal) rawGetTok(c, c.tok) - if c.tok.kind == tkBracketRi: + if c.tok.kind == tkBracketRi: rawGetTok(c, c.tok) else: reset(result) result.kind = cfgError result.msg = errorStr(c, "']' expected, but found: " & c.tok.literal) - of tkInvalid, tkEquals, tkColon, tkBracketRi: + of tkInvalid, tkEquals, tkColon, tkBracketRi: result.kind = cfgError result.msg = errorStr(c, "invalid token: " & c.tok.literal) rawGetTok(c, c.tok) diff --git a/lib/pure/parsecsv.nim b/lib/pure/parsecsv.nim index 117d75cfa..af51e1201 100644 --- a/lib/pure/parsecsv.nim +++ b/lib/pure/parsecsv.nim @@ -8,7 +8,7 @@ # ## This module implements a simple high performance `CSV`:idx: -## (`comma separated value`:idx:) parser. +## (`comma separated value`:idx:) parser. ## ## Example: How to use the parser ## ============================== @@ -43,7 +43,7 @@ type {.deprecated: [TCsvRow: CsvRow, TCsvParser: CsvParser, EInvalidCsv: CsvError].} -proc raiseEInvalidCsv(filename: string, line, col: int, +proc raiseEInvalidCsv(filename: string, line, col: int, msg: string) {.noreturn.} = var e: ref CsvError new(e) @@ -60,13 +60,13 @@ proc open*(my: var CsvParser, input: Stream, filename: string, ## for nice error messages. The parser's behaviour can be controlled by ## the diverse optional parameters: ## - `separator`: character used to separate fields - ## - `quote`: Used to quote fields containing special characters like + ## - `quote`: Used to quote fields containing special characters like ## `separator`, `quote` or new-line characters. '\0' disables the parsing ## of quotes. - ## - `escape`: removes any special meaning from the following character; + ## - `escape`: removes any special meaning from the following character; ## '\0' disables escaping; if escaping is disabled and `quote` is not '\0', ## two `quote` characters are parsed one literal `quote` character. - ## - `skipInitialSpace`: If true, whitespace immediately following the + ## - `skipInitialSpace`: If true, whitespace immediately following the ## `separator` is ignored. lexbase.open(my, input) my.filename = filename @@ -77,13 +77,13 @@ proc open*(my: var CsvParser, input: Stream, filename: string, my.row = @[] my.currRow = 0 -proc parseField(my: var CsvParser, a: var string) = +proc parseField(my: var CsvParser, a: var string) = var pos = my.bufpos var buf = my.buf if my.skipWhite: while buf[pos] in {' ', '\t'}: inc(pos) setLen(a, 0) # reuse memory - if buf[pos] == my.quote and my.quote != '\0': + if buf[pos] == my.quote and my.quote != '\0': inc(pos) while true: var c = buf[pos] @@ -91,7 +91,7 @@ proc parseField(my: var CsvParser, a: var string) = my.bufpos = pos # can continue after exception? error(my, pos, my.quote & " expected") break - elif c == my.quote: + elif c == my.quote: if my.esc == '\0' and buf[pos+1] == my.quote: add(a, my.quote) inc(pos, 2) @@ -103,11 +103,11 @@ proc parseField(my: var CsvParser, a: var string) = inc(pos, 2) else: case c - of '\c': + of '\c': pos = handleCR(my, pos) buf = my.buf add(a, "\n") - of '\l': + of '\l': pos = handleLF(my, pos) buf = my.buf add(a, "\n") @@ -123,11 +123,11 @@ proc parseField(my: var CsvParser, a: var string) = inc(pos) my.bufpos = pos -proc processedRows*(my: var CsvParser): int = +proc processedRows*(my: var CsvParser): int = ## returns number of the processed rows return my.currRow -proc readRow*(my: var CsvParser, columns = 0): bool = +proc readRow*(my: var CsvParser, columns = 0): bool = ## reads the next row; if `columns` > 0, it expects the row to have ## exactly this many columns. Returns false if the end of the file ## has been encountered else true. @@ -140,13 +140,13 @@ proc readRow*(my: var CsvParser, columns = 0): bool = my.row[col] = "" parseField(my, my.row[col]) inc(col) - if my.buf[my.bufpos] == my.sep: + if my.buf[my.bufpos] == my.sep: inc(my.bufpos) else: case my.buf[my.bufpos] - of '\c', '\l': + of '\c', '\l': # skip empty lines: - while true: + while true: case my.buf[my.bufpos] of '\c': my.bufpos = handleCR(my, my.bufpos) of '\l': my.bufpos = handleLF(my, my.bufpos) @@ -154,15 +154,15 @@ proc readRow*(my: var CsvParser, columns = 0): bool = of '\0': discard else: error(my, my.bufpos, my.sep & " expected") break - + setLen(my.row, col) result = col > 0 - if result and col != columns and columns > 0: - error(my, oldpos+1, $columns & " columns expected, but found " & + if result and col != columns and columns > 0: + error(my, oldpos+1, $columns & " columns expected, but found " & $col & " columns") inc(my.currRow) - -proc close*(my: var CsvParser) {.inline.} = + +proc close*(my: var CsvParser) {.inline.} = ## closes the parser `my` and its associated input stream. lexbase.close(my) diff --git a/lib/pure/parsesql.nim b/lib/pure/parsesql.nim index cfa60094d..d6fafec08 100644 --- a/lib/pure/parsesql.nim +++ b/lib/pure/parsesql.nim @@ -7,7 +7,7 @@ # distribution, for details about the copyright. # -## The ``parsesql`` module implements a high performance SQL file +## The ``parsesql`` module implements a high performance SQL file ## parser. It parses PostgreSQL syntax and the SQL ANSI standard. import @@ -37,11 +37,11 @@ type tkBracketLe, ## '[' tkBracketRi, ## ']' tkDot ## '.' - + Token = object # a token kind: TokKind # the type of the token literal: string # the parsed (string) literal - + SqlLexer* = object of BaseLexer ## the parser object. filename: string @@ -55,82 +55,82 @@ const ";", ":", ",", "(", ")", "[", "]", "." ] -proc open(L: var SqlLexer, input: Stream, filename: string) = +proc open(L: var SqlLexer, input: Stream, filename: string) = lexbase.open(L, input) L.filename = filename - -proc close(L: var SqlLexer) = + +proc close(L: var SqlLexer) = lexbase.close(L) -proc getColumn(L: SqlLexer): int = +proc getColumn(L: SqlLexer): int = ## get the current column the parser has arrived at. result = getColNumber(L, L.bufpos) -proc getLine(L: SqlLexer): int = +proc getLine(L: SqlLexer): int = result = L.lineNumber -proc handleHexChar(c: var SqlLexer, xi: var int) = +proc handleHexChar(c: var SqlLexer, xi: var int) = case c.buf[c.bufpos] - of '0'..'9': + of '0'..'9': xi = (xi shl 4) or (ord(c.buf[c.bufpos]) - ord('0')) inc(c.bufpos) - of 'a'..'f': + of 'a'..'f': xi = (xi shl 4) or (ord(c.buf[c.bufpos]) - ord('a') + 10) inc(c.bufpos) - of 'A'..'F': + of 'A'..'F': xi = (xi shl 4) or (ord(c.buf[c.bufpos]) - ord('A') + 10) inc(c.bufpos) - else: + else: discard -proc handleOctChar(c: var SqlLexer, xi: var int) = +proc handleOctChar(c: var SqlLexer, xi: var int) = if c.buf[c.bufpos] in {'0'..'7'}: xi = (xi shl 3) or (ord(c.buf[c.bufpos]) - ord('0')) inc(c.bufpos) -proc getEscapedChar(c: var SqlLexer, tok: var Token) = +proc getEscapedChar(c: var SqlLexer, tok: var Token) = inc(c.bufpos) case c.buf[c.bufpos] - of 'n', 'N': + of 'n', 'N': add(tok.literal, '\L') inc(c.bufpos) - of 'r', 'R', 'c', 'C': + of 'r', 'R', 'c', 'C': add(tok.literal, '\c') inc(c.bufpos) - of 'l', 'L': + of 'l', 'L': add(tok.literal, '\L') inc(c.bufpos) - of 'f', 'F': + of 'f', 'F': add(tok.literal, '\f') inc(c.bufpos) - of 'e', 'E': + of 'e', 'E': add(tok.literal, '\e') inc(c.bufpos) - of 'a', 'A': + of 'a', 'A': add(tok.literal, '\a') inc(c.bufpos) - of 'b', 'B': + of 'b', 'B': add(tok.literal, '\b') inc(c.bufpos) - of 'v', 'V': + of 'v', 'V': add(tok.literal, '\v') inc(c.bufpos) - of 't', 'T': + of 't', 'T': add(tok.literal, '\t') inc(c.bufpos) - of '\'', '\"': + of '\'', '\"': add(tok.literal, c.buf[c.bufpos]) inc(c.bufpos) - of '\\': + of '\\': add(tok.literal, '\\') inc(c.bufpos) - of 'x', 'X': + of 'x', 'X': inc(c.bufpos) var xi = 0 handleHexChar(c, xi) handleHexChar(c, xi) add(tok.literal, chr(xi)) - of '0'..'7': + of '0'..'7': var xi = 0 handleOctChar(c, xi) handleOctChar(c, xi) @@ -138,20 +138,20 @@ proc getEscapedChar(c: var SqlLexer, tok: var Token) = if (xi <= 255): add(tok.literal, chr(xi)) else: tok.kind = tkInvalid else: tok.kind = tkInvalid - -proc handleCRLF(c: var SqlLexer, pos: int): int = + +proc handleCRLF(c: var SqlLexer, pos: int): int = case c.buf[pos] of '\c': result = lexbase.handleCR(c, pos) of '\L': result = lexbase.handleLF(c, pos) else: result = pos -proc skip(c: var SqlLexer) = +proc skip(c: var SqlLexer) = var pos = c.bufpos var buf = c.buf var nested = 0 - while true: + while true: case buf[pos] - of ' ', '\t': + of ' ', '\t': inc(pos) of '-': if buf[pos+1] == '-': @@ -164,7 +164,7 @@ proc skip(c: var SqlLexer) = while true: case buf[pos] of '\0': break - of '\c', '\L': + of '\c', '\L': pos = handleCRLF(c, pos) buf = c.buf of '*': @@ -182,20 +182,20 @@ proc skip(c: var SqlLexer) = inc(pos) else: inc(pos) else: break - of '\c', '\L': + of '\c', '\L': pos = handleCRLF(c, pos) buf = c.buf - else: + else: break # EndOfFile also leaves the loop c.bufpos = pos - -proc getString(c: var SqlLexer, tok: var Token, kind: TokKind) = + +proc getString(c: var SqlLexer, tok: var Token, kind: TokKind) = var pos = c.bufpos + 1 var buf = c.buf tok.kind = kind block parseLoop: while true: - while true: + while true: var ch = buf[pos] if ch == '\'': if buf[pos+1] == '\'': @@ -203,15 +203,15 @@ proc getString(c: var SqlLexer, tok: var Token, kind: TokKind) = add(tok.literal, '\'') else: inc(pos) - break - elif ch in {'\c', '\L', lexbase.EndOfFile}: + break + elif ch in {'\c', '\L', lexbase.EndOfFile}: tok.kind = tkInvalid break parseLoop - elif (ch == '\\') and kind == tkEscapeConstant: + elif (ch == '\\') and kind == tkEscapeConstant: c.bufpos = pos getEscapedChar(c, tok) pos = c.bufpos - else: + else: add(tok.literal, ch) inc(pos) c.bufpos = pos @@ -227,7 +227,7 @@ proc getString(c: var SqlLexer, tok: var Token, kind: TokKind) = else: break parseLoop c.bufpos = pos -proc getDollarString(c: var SqlLexer, tok: var Token) = +proc getDollarString(c: var SqlLexer, tok: var Token) = var pos = c.bufpos + 1 var buf = c.buf tok.kind = tkDollarQuotedConstant @@ -241,7 +241,7 @@ proc getDollarString(c: var SqlLexer, tok: var Token) = return while true: case buf[pos] - of '\c', '\L': + of '\c', '\L': pos = handleCRLF(c, pos) buf = c.buf add(tok.literal, "\L") @@ -263,10 +263,10 @@ proc getDollarString(c: var SqlLexer, tok: var Token) = inc(pos) c.bufpos = pos -proc getSymbol(c: var SqlLexer, tok: var Token) = +proc getSymbol(c: var SqlLexer, tok: var Token) = var pos = c.bufpos var buf = c.buf - while true: + while true: add(tok.literal, buf[pos]) inc(pos) if buf[pos] notin {'a'..'z','A'..'Z','0'..'9','_','$', '\128'..'\255'}: @@ -274,7 +274,7 @@ proc getSymbol(c: var SqlLexer, tok: var Token) = c.bufpos = pos tok.kind = tkIdentifier -proc getQuotedIdentifier(c: var SqlLexer, tok: var Token) = +proc getQuotedIdentifier(c: var SqlLexer, tok: var Token) = var pos = c.bufpos + 1 var buf = c.buf tok.kind = tkQuotedIdentifier @@ -287,7 +287,7 @@ proc getQuotedIdentifier(c: var SqlLexer, tok: var Token) = else: inc(pos) break - elif ch in {'\c', '\L', lexbase.EndOfFile}: + elif ch in {'\c', '\L', lexbase.EndOfFile}: tok.kind = tkInvalid break else: @@ -300,15 +300,15 @@ proc getBitHexString(c: var SqlLexer, tok: var Token, validChars: set[char]) = var buf = c.buf block parseLoop: while true: - while true: + while true: var ch = buf[pos] if ch in validChars: add(tok.literal, ch) - inc(pos) + inc(pos) elif ch == '\'': inc(pos) break - else: + else: tok.kind = tkInvalid break parseLoop c.bufpos = pos @@ -353,7 +353,7 @@ proc getNumeric(c: var SqlLexer, tok: var Token) = inc(pos) else: tok.kind = tkInvalid - c.bufpos = pos + c.bufpos = pos proc getOperator(c: var SqlLexer, tok: var Token) = const operators = {'+', '-', '*', '/', '<', '>', '=', '~', '!', '@', '#', '%', @@ -381,12 +381,12 @@ proc getOperator(c: var SqlLexer, tok: var Token) = inc(pos) c.bufpos = pos -proc getTok(c: var SqlLexer, tok: var Token) = +proc getTok(c: var SqlLexer, tok: var Token) = tok.kind = tkInvalid setLen(tok.literal, 0) skip(c) case c.buf[c.bufpos] - of ';': + of ';': tok.kind = tkSemicolon inc(c.bufpos) add(tok.literal, ';') @@ -394,15 +394,15 @@ proc getTok(c: var SqlLexer, tok: var Token) = tok.kind = tkComma inc(c.bufpos) add(tok.literal, ',') - of ':': + of ':': tok.kind = tkColon inc(c.bufpos) add(tok.literal, ':') - of 'e', 'E': - if c.buf[c.bufpos + 1] == '\'': + of 'e', 'E': + if c.buf[c.bufpos + 1] == '\'': inc(c.bufpos) getString(c, tok, tkEscapeConstant) - else: + else: getSymbol(c, tok) of 'b', 'B': if c.buf[c.bufpos + 1] == '\'': @@ -417,11 +417,11 @@ proc getTok(c: var SqlLexer, tok: var Token) = else: getSymbol(c, tok) of '$': getDollarString(c, tok) - of '[': + of '[': tok.kind = tkBracketLe inc(c.bufpos) add(tok.literal, '[') - of ']': + of ']': tok.kind = tkBracketRi inc(c.bufpos) add(tok.literal, ']') @@ -433,7 +433,7 @@ proc getTok(c: var SqlLexer, tok: var Token) = tok.kind = tkParRi inc(c.bufpos) add(tok.literal, ')') - of '.': + of '.': if c.buf[c.bufpos + 1] in Digits: getNumeric(c, tok) else: @@ -443,7 +443,7 @@ proc getTok(c: var SqlLexer, tok: var Token) = of '0'..'9': getNumeric(c, tok) of '\'': getString(c, tok, tkStringConstant) of '"': getQuotedIdentifier(c, tok) - of lexbase.EndOfFile: + of lexbase.EndOfFile: tok.kind = tkEof tok.literal = "[EOF]" of 'a', 'c', 'd', 'f'..'w', 'y', 'z', 'A', 'C', 'D', 'F'..'W', 'Y', 'Z', '_', @@ -455,8 +455,8 @@ proc getTok(c: var SqlLexer, tok: var Token) = else: add(tok.literal, c.buf[c.bufpos]) inc(c.bufpos) - -proc errorStr(L: SqlLexer, msg: string): string = + +proc errorStr(L: SqlLexer, msg: string): string = result = "$1($2, $3) Error: $4" % [L.filename, $getLine(L), $getColumn(L), msg] @@ -496,7 +496,7 @@ type nkPrimaryKey, nkForeignKey, nkNotNull, - + nkStmtList, nkDot, nkDotDot, @@ -530,13 +530,13 @@ type nkValueList, nkWhere, nkCreateTable, - nkCreateTableIfNotExists, + nkCreateTableIfNotExists, nkCreateType, nkCreateTypeIfNotExists, nkCreateIndex, nkCreateIndexIfNotExists, nkEnumDef - + type SqlParseError* = object of ValueError ## Invalid SQL encountered SqlNode* = ref SqlNodeObj ## an SQL abstract syntax tree node @@ -563,11 +563,11 @@ proc newNode(k: SqlNodeKind, s: string): SqlNode = new(result) result.kind = k result.strVal = s - + proc len*(n: SqlNode): int = if isNil(n.sons): result = 0 else: result = n.sons.len - + proc add*(father, n: SqlNode) = if isNil(father.sons): father.sons = @[] add(father.sons, n) @@ -641,11 +641,11 @@ proc parseDataType(p: var SqlParser): SqlNode = getTok(p) eat(p, tkParRi) -proc getPrecedence(p: SqlParser): int = +proc getPrecedence(p: SqlParser): int = if isOpr(p, "*") or isOpr(p, "/") or isOpr(p, "%"): result = 6 elif isOpr(p, "+") or isOpr(p, "-"): - result = 5 + result = 5 elif isOpr(p, "=") or isOpr(p, "<") or isOpr(p, ">") or isOpr(p, ">=") or isOpr(p, "<=") or isOpr(p, "<>") or isOpr(p, "!=") or isKeyw(p, "is") or isKeyw(p, "like"): @@ -664,7 +664,7 @@ proc parseExpr(p: var SqlParser): SqlNode proc identOrLiteral(p: var SqlParser): SqlNode = case p.tok.kind - of tkIdentifier, tkQuotedIdentifier: + of tkIdentifier, tkQuotedIdentifier: result = newNode(nkIdent, p.tok.literal) getTok(p) of tkStringConstant, tkEscapeConstant, tkDollarQuotedConstant: @@ -686,21 +686,21 @@ proc identOrLiteral(p: var SqlParser): SqlNode = getTok(p) result = parseExpr(p) eat(p, tkParRi) - else: + else: sqlError(p, "expression expected") getTok(p) # we must consume a token here to prevend endless loops! -proc primary(p: var SqlParser): SqlNode = - if p.tok.kind == tkOperator or isKeyw(p, "not"): +proc primary(p: var SqlParser): SqlNode = + if p.tok.kind == tkOperator or isKeyw(p, "not"): result = newNode(nkPrefix) result.add(newNode(nkIdent, p.tok.literal)) getTok(p) result.add(primary(p)) return result = identOrLiteral(p) - while true: + while true: case p.tok.kind - of tkParLe: + of tkParLe: var a = result result = newNode(nkCall) result.add(a) @@ -710,7 +710,7 @@ proc primary(p: var SqlParser): SqlNode = if p.tok.kind == tkComma: getTok(p) else: break eat(p, tkParRi) - of tkDot: + of tkDot: getTok(p) var a = result if p.tok.kind == tkDot: @@ -727,14 +727,14 @@ proc primary(p: var SqlParser): SqlNode = sqlError(p, "identifier expected") getTok(p) else: break - -proc lowestExprAux(p: var SqlParser, v: var SqlNode, limit: int): int = + +proc lowestExprAux(p: var SqlParser, v: var SqlNode, limit: int): int = var v2, node, opNode: SqlNode v = primary(p) # expand while operators have priorities higher than 'limit' var opPred = getPrecedence(p) result = opPred - while opPred > limit: + while opPred > limit: node = newNode(nkInfix) opNode = newNode(nkIdent, p.tok.literal) getTok(p) @@ -744,8 +744,8 @@ proc lowestExprAux(p: var SqlParser, v: var SqlNode, limit: int): int = node.add(v2) v = node opPred = getPrecedence(p) - -proc parseExpr(p: var SqlParser): SqlNode = + +proc parseExpr(p: var SqlParser): SqlNode = discard lowestExprAux(p, result, - 1) proc parseTableName(p: var SqlParser): SqlNode = @@ -765,7 +765,7 @@ proc parseColumnReference(p: var SqlParser): SqlNode = result.add(parseTableName(p)) eat(p, tkParRi) -proc parseCheck(p: var SqlParser): SqlNode = +proc parseCheck(p: var SqlParser): SqlNode = getTok(p) result = newNode(nkCheck) result.add(parseExpr(p)) @@ -817,9 +817,9 @@ proc parseColumnDef(p: var SqlParser): SqlNode = result.add(newNode(nkIdent, p.tok.literal)) getTok(p) result.add(parseDataType(p)) - parseColumnConstraints(p, result) + parseColumnConstraints(p, result) -proc parseIfNotExists(p: var SqlParser, k: SqlNodeKind): SqlNode = +proc parseIfNotExists(p: var SqlParser, k: SqlNodeKind): SqlNode = getTok(p) if isKeyw(p, "if"): getTok(p) @@ -880,7 +880,7 @@ proc parseTableDef(p: var SqlParser): SqlNode = result.add(parseTableConstraint(p)) if p.tok.kind != tkComma: break eat(p, tkParRi) - + proc parseTypeDef(p: var SqlParser): SqlNode = result = parseIfNotExists(p, nkCreateType) expectIdent(p) @@ -966,7 +966,7 @@ proc parseUpdate(p: var SqlParser): SqlNode = result.add(parseWhere(p)) else: result.add(nil) - + proc parseDelete(p: var SqlParser): SqlNode = getTok(p) result = newNode(nkDelete) @@ -1018,14 +1018,14 @@ proc parseSelect(p: var SqlParser): SqlNode = while true: getTok(p) h.add(parseExpr(p)) - if p.tok.kind != tkComma: break + if p.tok.kind != tkComma: break result.add(h) if isKeyw(p, "union"): result.add(newNode(nkUnion)) getTok(p) elif isKeyw(p, "intersect"): result.add(newNode(nkIntersect)) - getTok(p) + getTok(p) elif isKeyw(p, "except"): result.add(newNode(nkExcept)) getTok(p) @@ -1083,7 +1083,7 @@ proc open(p: var SqlParser, input: Stream, filename: string) = p.tok.kind = tkInvalid p.tok.literal = "" getTok(p) - + proc parse(p: var SqlParser): SqlNode = ## parses the content of `p`'s input stream and returns the SQL AST. ## Syntax errors raise an `EInvalidSql` exception. @@ -1094,13 +1094,13 @@ proc parse(p: var SqlParser): SqlNode = result.add(s) if result.len == 1: result = result.sons[0] - + proc close(p: var SqlParser) = ## closes the parser `p`. The associated input stream is closed too. close(SqlLexer(p)) proc parseSQL*(input: Stream, filename: string): SqlNode = - ## parses the SQL from `input` into an AST and returns the AST. + ## parses the SQL from `input` into an AST and returns the AST. ## `filename` is only used for error messages. ## Syntax errors raise an `EInvalidSql` exception. var p: SqlParser @@ -1114,7 +1114,7 @@ proc ra(n: SqlNode, s: var string, indent: int) proc rs(n: SqlNode, s: var string, indent: int, prefix = "(", suffix = ")", - sep = ", ") = + sep = ", ") = if n.len > 0: s.add(prefix) for i in 0 .. n.len-1: @@ -1162,7 +1162,7 @@ proc ra(n: SqlNode, s: var string, indent: int) = ra(n.sons[1], s, indent) s.add(')') of nkInfix: - s.add('(') + s.add('(') ra(n.sons[1], s, indent) s.add(' ') ra(n.sons[0], s, indent) @@ -1207,13 +1207,13 @@ proc ra(n: SqlNode, s: var string, indent: int) = s.add("insert into ") ra(n.sons[0], s, indent) ra(n.sons[1], s, indent) - if n.sons[2].kind == nkDefault: + if n.sons[2].kind == nkDefault: s.add("default values") else: s.add("\nvalues ") ra(n.sons[2], s, indent) s.add(';') - of nkUpdate: + of nkUpdate: s.add("update ") ra(n.sons[0], s, indent) s.add(" set ") @@ -1225,7 +1225,7 @@ proc ra(n: SqlNode, s: var string, indent: int) = ra(it, s, indent) ra(n.sons[L-1], s, indent) s.add(';') - of nkDelete: + of nkDelete: s.add("delete from ") ra(n.sons[0], s, indent) ra(n.sons[1], s, indent) @@ -1237,12 +1237,12 @@ proc ra(n: SqlNode, s: var string, indent: int) = rs(n.sons[0], s, indent, "", "", ", ") for i in 1 .. n.len-1: ra(n.sons[i], s, indent) s.add(';') - of nkSelectColumns: + of nkSelectColumns: assert(false) of nkAsgn: ra(n.sons[0], s, indent) s.add(" = ") - ra(n.sons[1], s, indent) + ra(n.sons[1], s, indent) of nkFrom: s.add("\nfrom ") rs(n, s, indent, "", "", ", ") @@ -1306,23 +1306,23 @@ proc ra(n: SqlNode, s: var string, indent: int) = s.add("enum ") rs(n, s, indent) -# What I want: +# What I want: # -#select(columns = [T1.all, T2.name], +#select(columns = [T1.all, T2.name], # fromm = [T1, T2], # where = T1.name ==. T2.name, # orderby = [name]): -# -#for row in dbQuery(db, """select x, y, z -# from a, b +# +#for row in dbQuery(db, """select x, y, z +# from a, b # where a.name = b.name"""): -# +# #select x, y, z: # fromm: Table1, Table2 # where: x.name == y.name #db.select(fromm = [t1, t2], where = t1.name == t2.name): -#for x, y, z in db.select(fromm = a, b where = a.name == b.name): +#for x, y, z in db.select(fromm = a, b where = a.name == b.name): # writeLine x, y, z proc renderSQL*(n: SqlNode): string = @@ -1338,7 +1338,7 @@ when not defined(testing) and isMainModule: happiness happiness ); CREATE INDEX table1_attr1 ON table1(attr1); - + SELECT * FROM myTab WHERE col1 = 'happy'; """), "stdin"))) diff --git a/lib/pure/parseurl.nim b/lib/pure/parseurl.nim index 56bf10768..6d58e8a73 100644 --- a/lib/pure/parseurl.nim +++ b/lib/pure/parseurl.nim @@ -19,7 +19,7 @@ import strutils type Url* = tuple[ ## represents a *Uniform Resource Locator* (URL) ## any optional component is "" if it does not exist - scheme, username, password, + scheme, username, password, hostname, port, path, query, anchor: string] {.deprecated: [TUrl: Url].} @@ -31,7 +31,7 @@ proc parseUrl*(url: string): Url {.deprecated.} = var hostname, port, path, query, anchor: string = "" var temp = "" - + if url[i] != '/': # url isn't a relative path while true: # Scheme @@ -48,7 +48,7 @@ proc parseUrl*(url: string): Url {.deprecated.} = password = username.substr(colon+1) username = username.substr(0, colon-1) temp.setLen(0) - inc(i) #Skip the @ + inc(i) #Skip the @ # hostname(subdomain, domain, port) if url[i] == '/' or url[i] == '\0': hostname = temp @@ -56,10 +56,10 @@ proc parseUrl*(url: string): Url {.deprecated.} = if colon >= 0: port = hostname.substr(colon+1) hostname = hostname.substr(0, colon-1) - + temp.setLen(0) break - + temp.add(url[i]) inc(i) @@ -75,7 +75,7 @@ proc parseUrl*(url: string): Url {.deprecated.} = else: path = temp temp.setLen(0) - + if url[i] == '\0': if temp[0] == '?': query = temp @@ -84,10 +84,10 @@ proc parseUrl*(url: string): Url {.deprecated.} = else: path = temp break - + temp.add(url[i]) inc(i) - + return (scheme, username, password, hostname, port, path, query, anchor) proc `$`*(u: Url): string {.deprecated.} = @@ -103,12 +103,12 @@ proc `$`*(u: Url): string {.deprecated.} = result.add(u.password) result.add("@") result.add(u.hostname) - if u.port.len > 0: + if u.port.len > 0: result.add(":") result.add(u.port) - if u.path.len > 0: + if u.path.len > 0: result.add("/") result.add(u.path) result.add(u.query) result.add(u.anchor) - + diff --git a/lib/pure/poly.nim b/lib/pure/poly.nim index 58dcdc1ad..c52300400 100644 --- a/lib/pure/poly.nim +++ b/lib/pure/poly.nim @@ -11,7 +11,7 @@ import math import strutils import numeric -type +type Poly* = object cofs:seq[float] @@ -40,7 +40,7 @@ proc `[]` *(p:Poly;idx:int):float= if idx<0 or idx>p.degree: return 0.0 return p.cofs[idx] - + proc `[]=` *(p:var Poly;idx:int,v:float)= ## Sets an coefficient of the polynomial by index. ## p[2] set the quadric term, p[3] the cubic etc. @@ -55,15 +55,15 @@ proc `[]=` *(p:var Poly;idx:int,v:float)= p.cofs[q]=0.0 #new-grown coefficients set to zero p.cofs[idx]=v - - + + iterator items*(p:Poly):float= ## Iterates through the coefficients of the polynomial. var i=p.degree while i>=0: yield p[i] - dec i - + dec i + proc clean*(p:var Poly;zerotol=0.0)= ## Removes leading zero coefficients of the polynomial. ## An optional tolerance can be given for what's considered zero. @@ -77,19 +77,19 @@ proc clean*(p:var Poly;zerotol=0.0)= if relen: p.cofs.setLen(n+1) -proc `$` *(p:Poly):string = +proc `$` *(p:Poly):string = ## Gets a somewhat reasonable string representation of the polynomial ## The format should be compatible with most online function plotters, ## for example directly in google search result="" var first=true #might skip + sign if first coefficient - + for idx in countdown(p.degree,0): let a=p[idx] - + if a==0.0: continue - + if a>= 0.0 and not first: result.add('+') first=false @@ -103,14 +103,14 @@ proc `$` *(p:Poly):string = if result=="": result="0" - + proc derivative*(p: Poly): Poly= ## Returns a new polynomial, which is the derivative of `p` newSeq[float](result.cofs,p.degree) for idx in 0..high(result.cofs): result.cofs[idx]=p.cofs[idx+1]*float(idx+1) - + proc diff*(p:Poly,x:float):float= ## Evaluates the differentiation of a polynomial with ## respect to `x` quickly using a modifed Horners method @@ -128,7 +128,7 @@ proc integral*(p:Poly):Poly= result.cofs[0]=0.0 #constant arbitrary term, use 0.0 for i in 1..high(result.cofs): result.cofs[i]=p.cofs[i-1]/float(i) - + proc integrate*(p:Poly;xmin,xmax:float):float= ## Computes the definite integral of `p` between `xmin` and `xmax` @@ -145,9 +145,9 @@ proc integrate*(p:Poly;xmin,xmax:float):float= s1 = s1*xmin+fac s2 = s2*xmax+fac dec n - + result=s2*xmax-s1*xmin - + proc initPoly*(cofs:varargs[float]):Poly= ## Initializes a polynomial with given coefficients. ## The most significant coefficient is first, so to create x^2-2x+3: @@ -158,7 +158,7 @@ proc initPoly*(cofs:varargs[float]):Poly= # reverse order of coefficients so indexing matches degree of # coefficient... result.cofs= @[] - for idx in countdown(cofs.len-1,0): + for idx in countdown(cofs.len-1,0): result.cofs.add(cofs[idx]) result.clean #remove leading zero terms @@ -167,49 +167,49 @@ proc initPoly*(cofs:varargs[float]):Poly= proc divMod*(p,d:Poly;q,r:var Poly)= ## Divides `p` with `d`, and stores the quotinent in `q` and ## the remainder in `d` - var + var pdeg=p.degree ddeg=d.degree power=p.degree-d.degree ratio:float - + r.cofs = p.cofs #initial remainder=numerator if power<0: #denominator is larger than numerator q.cofs= @ [0.0] #quotinent is 0.0 return # keep remainder as numerator - + q.cofs=newSeq[float](power+1) - + for i in countdown(pdeg,ddeg): ratio=r.cofs[i]/d.cofs[ddeg] - + q.cofs[i-ddeg]=ratio r.cofs[i]=0.0 - + for j in countup(0,<ddeg): var idx=i-ddeg+j r.cofs[idx] = r.cofs[idx] - d.cofs[j]*ratio - + r.clean # drop zero coefficients in remainder proc `+` *(p1:Poly,p2:Poly):Poly= ## Adds two polynomials var n=max(p1.cofs.len,p2.cofs.len) newSeq(result.cofs,n) - + for idx in countup(0,n-1): result[idx]=p1[idx]+p2[idx] - + result.clean # drop zero coefficients in remainder - + proc `*` *(p1:Poly,p2:Poly):Poly= ## Multiplies the polynomial `p1` with `p2` - var + var d1=p1.degree d2=p2.degree n=d1+d2 idx:int - + newSeq(result.cofs,n) for i1 in countup(0,d1): @@ -225,38 +225,38 @@ proc `*` *(p:Poly,f:float):Poly= for i in 0..high(p.cofs): result[i]=p.cofs[i]*f result.clean - + proc `*` *(f:float,p:Poly):Poly= ## Multiplies a real number with a polynomial return p*f - + proc `-`*(p:Poly):Poly= ## Negates a polynomial result=p for i in countup(0,<result.cofs.len): result.cofs[i]= -result.cofs[i] - + proc `-` *(p1:Poly,p2:Poly):Poly= ## Subtract `p1` with `p2` var n=max(p1.cofs.len,p2.cofs.len) newSeq(result.cofs,n) - + for idx in countup(0,n-1): result[idx]=p1[idx]-p2[idx] - + result.clean # drop zero coefficients in remainder - + proc `/`*(p:Poly,f:float):Poly= ## Divides polynomial `p` with a real number `f` newSeq(result.cofs,p.cofs.len) for i in 0..high(p.cofs): result[i]=p.cofs[i]/f result.clean - + proc `/` *(p,q:Poly):Poly= ## Divides polynomial `p` with polynomial `q` var dummy:Poly - p.divMod(q,result,dummy) + p.divMod(q,result,dummy) proc `mod` *(p,q:Poly):Poly= ## Computes the polynomial modulo operation, @@ -277,20 +277,20 @@ proc solveQuadric*(a,b,c:float;zerotol=0.0):seq[float]= ## Solves the quadric equation `ax^2+bx+c`, with a possible ## tolerance `zerotol` to find roots of curves just 'touching' ## the x axis. Returns sequence with 0,1 or 2 solutions. - + var p,q,d:float - + p=b/(2.0*a) - + if p==Inf or p==NegInf: #linear equation.. var linrt= -c/b if linrt==Inf or linrt==NegInf: #constant only return @[] return @[linrt] - + q=c/a d=p*p-q - + if d<0.0: #check for inside zerotol range for neg. roots var err=a*p*p-b*p+c #evaluate error at parabola center axis @@ -309,12 +309,12 @@ proc getRangeForRoots(p:Poly):tuple[xmin,xmax:float]= var deg=p.degree var d=p[deg] var bound1,bound2:float - + for i in countup(0,deg): var c=abs(p.cofs[i]/d) bound1=max(bound1,c+1.0) bound2=bound2+c - + bound2=max(1.0,bound2) result.xmax=min(bound1,bound2) result.xmin= -result.xmax @@ -327,13 +327,13 @@ proc addRoot(p:Poly,res:var seq[float],xp0,xp1,tol,zerotol,mergetol:float,maxite var br=brent(xp0,xp1, proc(x:float):float=p.eval(x),tol) if br.success: if res.len==0 or br.rootx>=res[high(res)]+mergetol: #dont add equal roots. - res.add(br.rootx) + res.add(br.rootx) else: #this might be a 'touching' case, check function value against #zero tolerance if abs(br.rooty)<=zerotol: if res.len==0 or br.rootx>=res[high(res)]+mergetol: #dont add equal roots. - res.add(br.rootx) + res.add(br.rootx) proc roots*(p:Poly,tol=1.0e-9,zerotol=1.0e-6,mergetol=1.0e-12,maxiter=1000):seq[float]= diff --git a/lib/pure/rationals.nim b/lib/pure/rationals.nim index 8e1dbe464..60d09c71a 100644 --- a/lib/pure/rationals.nim +++ b/lib/pure/rationals.nim @@ -216,7 +216,7 @@ proc hash*[T](x: Rational[T]): Hash = h = h !& hash(copy.num) h = h !& hash(copy.den) result = !$h - + when isMainModule: var z = Rational[int](num: 0, den: 1) diff --git a/lib/pure/redis.nim b/lib/pure/redis.nim index b738e4094..e3f18a496 100644 --- a/lib/pure/redis.nim +++ b/lib/pure/redis.nim @@ -11,7 +11,7 @@ ## redis-server instance, send commands and receive replies. ## ## **Beware**: Most (if not all) functions that return a ``RedisString`` may -## return ``redisNil``, and functions which return a ``RedisList`` +## return ``redisNil``, and functions which return a ``RedisList`` ## may return ``nil``. import sockets, os, strutils, parseutils @@ -19,7 +19,7 @@ import sockets, os, strutils, parseutils const redisNil* = "\0\0" -type +type Pipeline = ref object enabled: bool buffer: string @@ -34,7 +34,7 @@ type socket: Socket connected: bool pipeline: Pipeline - + RedisStatus* = string RedisInteger* = BiggestInt RedisString* = string ## Bulk reply @@ -59,10 +59,10 @@ proc open*(host = "localhost", port = 6379.Port): Redis = if result.socket == invalidSocket: raiseOSError(osLastError()) result.socket.connect(host, port) - result.pipeline = newPipeline() + result.pipeline = newPipeline() proc raiseInvalidReply(expected, got: char) = - raise newException(ReplyError, + raise newException(ReplyError, "Expected '$1' at the beginning of a status reply got '$2'" % [$expected, $got]) @@ -90,16 +90,16 @@ proc parseStatus(r: Redis, line: string = ""): RedisStatus = raise newException(RedisError, strip(line)) if line[0] != '+': raiseInvalidReply('+', line[0]) - + return line.substr(1) # Strip '+' proc readStatus(r:Redis): RedisStatus = r.readSocket("PIPELINED") return r.parseStatus(line) - + proc parseInteger(r: Redis, line: string = ""): RedisInteger = if r.pipeline.enabled: return -1 - + #if line == "+QUEUED": # inside of multi # return -1 @@ -110,10 +110,10 @@ proc parseInteger(r: Redis, line: string = ""): RedisInteger = raise newException(RedisError, strip(line)) if line[0] != ':': raiseInvalidReply(':', line[0]) - + # Strip ':' if parseBiggestInt(line, result, 1) == 0: - raise newException(ReplyError, "Unable to parse integer.") + raise newException(ReplyError, "Unable to parse integer.") proc readInteger(r: Redis): RedisInteger = r.readSocket(-1) @@ -126,19 +126,19 @@ proc recv(sock: Socket, size: int): TaintedString = proc parseSingleString(r: Redis, line:string, allowMBNil = false): RedisString = if r.pipeline.enabled: return "" - + # Error. if line[0] == '-': raise newException(RedisError, strip(line)) - + # Some commands return a /bulk/ value or a /multi-bulk/ nil. Odd. if allowMBNil: if line == "*-1": return redisNil - + if line[0] != '$': raiseInvalidReply('$', line[0]) - + var numBytes = parseInt(line.substr(1)) if numBytes == -1: return redisNil @@ -168,7 +168,7 @@ proc parseArrayLines(r: Redis, countLine:string): RedisList = proc readArrayLines(r: Redis): RedisList = r.readSocket(nil) - return r.parseArrayLines(line) + return r.parseArrayLines(line) proc parseBulkString(r: Redis, allowMBNil = false, line:string = ""): RedisString = if r.pipeline.enabled: return "" @@ -191,7 +191,7 @@ proc readNext(r: Redis): RedisList = of ':': @[$(r.parseInteger(line))] of '$': @[r.parseBulkString(true,line)] of '*': r.parseArrayLines(line) - else: + else: raise newException(ReplyError, "readNext failed on line: " & line) nil r.pipeline.expected -= 1 @@ -202,10 +202,10 @@ proc flushPipeline*(r: Redis, wasMulti = false): RedisList = if r.pipeline.buffer.len > 0: r.socket.send(r.pipeline.buffer) r.pipeline.buffer = "" - + r.pipeline.enabled = false result = @[] - + var tot = r.pipeline.expected for i in 0..tot-1: @@ -232,7 +232,7 @@ proc sendCommand(r: Redis, cmd: string, args: varargs[string]) = for i in items(args): request.add("$" & $i.len() & "\c\L") request.add(i & "\c\L") - + if r.pipeline.enabled: r.pipeline.buffer.add(request) r.pipeline.expected += 1 @@ -249,7 +249,7 @@ proc sendCommand(r: Redis, cmd: string, arg1: string, for i in items(args): request.add("$" & $i.len() & "\c\L") request.add(i & "\c\L") - + if r.pipeline.enabled: r.pipeline.expected += 1 r.pipeline.buffer.add(request) @@ -275,7 +275,7 @@ proc expire*(r: Redis, key: string, seconds: int): bool = return r.readInteger() == 1 proc expireAt*(r: Redis, key: string, timestamp: int): bool = - ## Set the expiration for a key as a UNIX timestamp. Returns `false` + ## Set the expiration for a key as a UNIX timestamp. Returns `false` ## if the key could not be found or the timeout could not be set. r.sendCommand("EXPIREAT", key, $timestamp) return r.readInteger() == 1 @@ -301,7 +301,7 @@ proc scan*(r: Redis, cursor: var BiggestInt, pattern: string): RedisList = cursor = strutils.parseBiggestInt(reply[0]) return reply[1..high(reply)] -proc scan*(r: Redis, cursor: var BiggestInt, pattern: string, count: int): RedisList = +proc scan*(r: Redis, cursor: var BiggestInt, pattern: string, count: int): RedisList = ## Find all keys matching the given pattern and yield it to client in portions ## using cursor as a client query identifier. r.sendCommand("SCAN", $cursor, ["MATCH", pattern, "COUNT", $count]) @@ -315,11 +315,11 @@ proc move*(r: Redis, key: string, db: int): bool = return r.readInteger() == 1 proc persist*(r: Redis, key: string): bool = - ## Remove the expiration from a key. + ## Remove the expiration from a key. ## Returns `true` when the timeout was removed. r.sendCommand("PERSIST", key) return r.readInteger() == 1 - + proc randomKey*(r: Redis): RedisString = ## Return a random key from the keyspace r.sendCommand("RANDOMKEY") @@ -327,11 +327,11 @@ proc randomKey*(r: Redis): RedisString = proc rename*(r: Redis, key, newkey: string): RedisStatus = ## Rename a key. - ## + ## ## **WARNING:** Overwrites `newkey` if it exists! r.sendCommand("RENAME", key, newkey) raiseNoOK(r.readStatus(), r.pipeline.enabled) - + proc renameNX*(r: Redis, key, newkey: string): bool = ## Same as ``rename`` but doesn't continue if `newkey` exists. ## Returns `true` if key was renamed. @@ -342,12 +342,12 @@ proc ttl*(r: Redis, key: string): RedisInteger = ## Get the time to live for a key r.sendCommand("TTL", key) return r.readInteger() - + proc keyType*(r: Redis, key: string): RedisStatus = ## Determine the type stored at key r.sendCommand("TYPE", key) return r.readStatus() - + # Strings @@ -360,12 +360,12 @@ proc decr*(r: Redis, key: string): RedisInteger = ## Decrement the integer value of a key by one r.sendCommand("DECR", key) return r.readInteger() - + proc decrBy*(r: Redis, key: string, decrement: int): RedisInteger = ## Decrement the integer value of a key by the given number r.sendCommand("DECRBY", key, $decrement) return r.readInteger() - + proc get*(r: Redis, key: string): RedisString = ## Get the value of a key. Returns `redisNil` when `key` doesn't exist. r.sendCommand("GET", key) @@ -397,7 +397,7 @@ proc incrBy*(r: Redis, key: string, increment: int): RedisInteger = r.sendCommand("INCRBY", key, $increment) return r.readInteger() -proc setk*(r: Redis, key, value: string) = +proc setk*(r: Redis, key, value: string) = ## Set the string value of a key. ## ## NOTE: This function had to be renamed due to a clash with the `set` type. @@ -410,18 +410,18 @@ proc setNX*(r: Redis, key, value: string): bool = r.sendCommand("SETNX", key, value) return r.readInteger() == 1 -proc setBit*(r: Redis, key: string, offset: int, +proc setBit*(r: Redis, key: string, offset: int, value: string): RedisInteger = ## Sets or clears the bit at offset in the string value stored at key r.sendCommand("SETBIT", key, $offset, value) return r.readInteger() - + proc setEx*(r: Redis, key: string, seconds: int, value: string): RedisStatus = ## Set the value and expiration of a key r.sendCommand("SETEX", key, $seconds, value) raiseNoOK(r.readStatus(), r.pipeline.enabled) -proc setRange*(r: Redis, key: string, offset: int, +proc setRange*(r: Redis, key: string, offset: int, value: string): RedisInteger = ## Overwrite part of a string at key starting at the specified offset r.sendCommand("SETRANGE", key, $offset, value) @@ -474,7 +474,7 @@ proc hMGet*(r: Redis, key: string, fields: varargs[string]): RedisList = r.sendCommand("HMGET", key, fields) return r.readArray() -proc hMSet*(r: Redis, key: string, +proc hMSet*(r: Redis, key: string, fieldValues: openArray[tuple[field, value: string]]) = ## Set multiple hash fields to multiple values var args = @[key] @@ -488,7 +488,7 @@ proc hSet*(r: Redis, key, field, value: string): RedisInteger = ## Set the string value of a hash field r.sendCommand("HSET", key, field, value) return r.readInteger() - + proc hSetNX*(r: Redis, key, field, value: string): RedisInteger = ## Set the value of a hash field, only if the field does **not** exist r.sendCommand("HSETNX", key, field, value) @@ -498,11 +498,11 @@ proc hVals*(r: Redis, key: string): RedisList = ## Get all the values in a hash r.sendCommand("HVALS", key) return r.readArray() - + # Lists proc bLPop*(r: Redis, keys: varargs[string], timeout: int): RedisList = - ## Remove and get the *first* element in a list, or block until + ## Remove and get the *first* element in a list, or block until ## one is available var args: seq[string] = @[] for i in items(keys): args.add(i) @@ -511,7 +511,7 @@ proc bLPop*(r: Redis, keys: varargs[string], timeout: int): RedisList = return r.readArray() proc bRPop*(r: Redis, keys: varargs[string], timeout: int): RedisList = - ## Remove and get the *last* element in a list, or block until one + ## Remove and get the *last* element in a list, or block until one ## is available. var args: seq[string] = @[] for i in items(keys): args.add(i) @@ -539,7 +539,7 @@ proc lInsert*(r: Redis, key: string, before: bool, pivot, value: string): var pos = if before: "BEFORE" else: "AFTER" r.sendCommand("LINSERT", key, pos, pivot, value) return r.readInteger() - + proc lLen*(r: Redis, key: string): RedisInteger = ## Get the length of a list r.sendCommand("LLEN", key) @@ -553,7 +553,7 @@ proc lPop*(r: Redis, key: string): RedisString = proc lPush*(r: Redis, key, value: string, create: bool = true): RedisInteger = ## Prepend a value to a list. Returns the length of the list after the push. ## The ``create`` param specifies whether a list should be created if it - ## doesn't exist at ``key``. More specifically if ``create`` is true, `LPUSH` + ## doesn't exist at ``key``. More specifically if ``create`` is true, `LPUSH` ## will be used, otherwise `LPUSHX`. if create: r.sendCommand("LPUSH", key, value) @@ -562,7 +562,7 @@ proc lPush*(r: Redis, key, value: string, create: bool = true): RedisInteger = return r.readInteger() proc lRange*(r: Redis, key: string, start, stop: int): RedisList = - ## Get a range of elements from a list. Returns `nil` when `key` + ## Get a range of elements from a list. Returns `nil` when `key` ## doesn't exist. r.sendCommand("LRANGE", key, $start, $stop) return r.readArray() @@ -587,16 +587,16 @@ proc rPop*(r: Redis, key: string): RedisString = ## Remove and get the last element in a list r.sendCommand("RPOP", key) return r.readBulkString() - + proc rPopLPush*(r: Redis, source, destination: string): RedisString = ## Remove the last element in a list, append it to another list and return it r.sendCommand("RPOPLPUSH", source, destination) return r.readBulkString() - + proc rPush*(r: Redis, key, value: string, create: bool = true): RedisInteger = ## Append a value to a list. Returns the length of the list after the push. ## The ``create`` param specifies whether a list should be created if it - ## doesn't exist at ``key``. More specifically if ``create`` is true, `RPUSH` + ## doesn't exist at ``key``. More specifically if ``create`` is true, `RPUSH` ## will be used, otherwise `RPUSHX`. if create: r.sendCommand("RPUSH", key, value) @@ -676,7 +676,7 @@ proc sunion*(r: Redis, keys: varargs[string]): RedisList = proc sunionstore*(r: Redis, destination: string, key: varargs[string]): RedisInteger = - ## Add multiple sets and store the resulting set in a key + ## Add multiple sets and store the resulting set in a key r.sendCommand("SUNIONSTORE", destination, key) return r.readInteger() @@ -710,16 +710,16 @@ proc zinterstore*(r: Redis, destination: string, numkeys: string, ## a new key var args = @[destination, numkeys] for i in items(keys): args.add(i) - + if weights.len != 0: args.add("WITHSCORE") for i in items(weights): args.add(i) if aggregate.len != 0: args.add("AGGREGATE") args.add(aggregate) - + r.sendCommand("ZINTERSTORE", args) - + return r.readInteger() proc zrange*(r: Redis, key: string, start: string, stop: string, @@ -731,18 +731,18 @@ proc zrange*(r: Redis, key: string, start: string, stop: string, r.sendCommand("ZRANGE", "WITHSCORES", key, start, stop) return r.readArray() -proc zrangebyscore*(r: Redis, key: string, min: string, max: string, +proc zrangebyscore*(r: Redis, key: string, min: string, max: string, withScore: bool = false, limit: bool = false, limitOffset: int = 0, limitCount: int = 0): RedisList = ## Return a range of members in a sorted set, by score var args = @[key, min, max] - + if withScore: args.add("WITHSCORE") - if limit: + if limit: args.add("LIMIT") args.add($limitOffset) args.add($limitCount) - + r.sendCommand("ZRANGEBYSCORE", args) return r.readArray() @@ -770,26 +770,26 @@ proc zremrangebyscore*(r: Redis, key: string, min: string, proc zrevrange*(r: Redis, key: string, start: string, stop: string, withScore: bool): RedisList = - ## Return a range of members in a sorted set, by index, + ## Return a range of members in a sorted set, by index, ## with scores ordered from high to low if withScore: r.sendCommand("ZREVRANGE", "WITHSCORE", key, start, stop) else: r.sendCommand("ZREVRANGE", key, start, stop) return r.readArray() -proc zrevrangebyscore*(r: Redis, key: string, min: string, max: string, +proc zrevrangebyscore*(r: Redis, key: string, min: string, max: string, withScore: bool = false, limit: bool = false, limitOffset: int = 0, limitCount: int = 0): RedisList = ## Return a range of members in a sorted set, by score, with ## scores ordered from high to low var args = @[key, min, max] - + if withScore: args.add("WITHSCORE") - if limit: + if limit: args.add("LIMIT") args.add($limitOffset) args.add($limitCount) - + r.sendCommand("ZREVRANGEBYSCORE", args) return r.readArray() @@ -807,24 +807,24 @@ proc zscore*(r: Redis, key: string, member: string): RedisString = proc zunionstore*(r: Redis, destination: string, numkeys: string, keys: openArray[string], weights: openArray[string] = [], aggregate: string = ""): RedisInteger = - ## Add multiple sorted sets and store the resulting sorted set in a new key + ## Add multiple sorted sets and store the resulting sorted set in a new key var args = @[destination, numkeys] for i in items(keys): args.add(i) - + if weights.len != 0: args.add("WEIGHTS") for i in items(weights): args.add(i) if aggregate.len != 0: args.add("AGGREGATE") args.add(aggregate) - + r.sendCommand("ZUNIONSTORE", args) - + return r.readInteger() # HyperLogLog -proc pfadd*(r: Redis, key: string, elements: varargs[string]): RedisInteger = +proc pfadd*(r: Redis, key: string, elements: varargs[string]): RedisInteger = ## Add variable number of elements into special 'HyperLogLog' set type r.sendCommand("PFADD", key, elements) return r.readInteger() @@ -864,7 +864,7 @@ proc subscribe*(r: Redis, channel: openarray[string]): ???? = return ??? proc unsubscribe*(r: Redis, [channel: openarray[string], : string): ???? = - ## Stop listening for messages posted to the given channels + ## Stop listening for messages posted to the given channels r.socket.send("UNSUBSCRIBE $# $#\c\L" % [[channel.join(), ]) return ??? @@ -879,12 +879,12 @@ proc discardMulti*(r: Redis) = proc exec*(r: Redis): RedisList = ## Execute all commands issued after MULTI - r.sendCommand("EXEC") + r.sendCommand("EXEC") r.pipeline.enabled = false # Will reply with +OK for MULTI/EXEC and +QUEUED for every command # between, then with the results return r.flushPipeline(true) - + proc multi*(r: Redis) = ## Mark the start of a transaction block @@ -898,7 +898,7 @@ proc unwatch*(r: Redis) = raiseNoOK(r.readStatus(), r.pipeline.enabled) proc watch*(r: Redis, key: varargs[string]) = - ## Watch the given keys to determine execution of the MULTI/EXEC block + ## Watch the given keys to determine execution of the MULTI/EXEC block r.sendCommand("WATCH", key) raiseNoOK(r.readStatus(), r.pipeline.enabled) @@ -925,7 +925,7 @@ proc quit*(r: Redis) = raiseNoOK(r.readStatus(), r.pipeline.enabled) proc select*(r: Redis, index: int): RedisStatus = - ## Change the selected database for the current connection + ## Change the selected database for the current connection r.sendCommand("SELECT", $index) return r.readStatus() @@ -1016,7 +1016,7 @@ proc slaveof*(r: Redis, host: string, port: string) = iterator hPairs*(r: Redis, key: string): tuple[key, value: string] = ## Iterator for keys and values in a hash. - var + var contents = r.hGetAll(key) k = "" for i in items(contents): @@ -1031,9 +1031,9 @@ proc someTests(r: Redis, how: SendMode):seq[string] = if how == pipelined: r.startPipelining() - elif how == multiple: + elif how == multiple: r.multi() - + r.setk("nim:test", "Testing something.") r.setk("nim:utf8", "こんにちは") r.setk("nim:esc", "\\ths ągt\\") @@ -1054,7 +1054,7 @@ proc someTests(r: Redis, how: SendMode):seq[string] = for i in items(p): if not isNil(i): - list.add(i) + list.add(i) list.add(r.debugObject("mylist")) @@ -1079,7 +1079,7 @@ proc assertListsIdentical(listA, listB: seq[string]) = for item in listA: assert(item == listB[i]) i = i + 1 - + when not defined(testing) and isMainModule: when false: var r = open() diff --git a/lib/pure/romans.nim b/lib/pure/romans.nim index 0c182843a..18c04ef58 100644 --- a/lib/pure/romans.nim +++ b/lib/pure/romans.nim @@ -11,7 +11,7 @@ ## See http://en.wikipedia.org/wiki/Roman_numerals for reference. const - RomanNumeralDigits* = {'I', 'i', 'V', 'v', 'X', 'x', 'L', 'l', 'C', 'c', + RomanNumeralDigits* = {'I', 'i', 'V', 'v', 'X', 'x', 'L', 'l', 'C', 'c', 'D', 'd', 'M', 'm'} ## set of all characters a Roman numeral may consist of proc romanToDecimal*(romanVal: string): int = @@ -28,7 +28,7 @@ proc romanToDecimal*(romanVal: string): int = of 'C', 'c': val = 100 of 'D', 'd': val = 500 of 'M', 'm': val = 1000 - else: + else: raise newException(EInvalidValue, "invalid roman numeral: " & $romanVal) if val >= prevVal: inc(result, val) diff --git a/lib/pure/ropes.nim b/lib/pure/ropes.nim index 5c7fedfe3..df7071642 100644 --- a/lib/pure/ropes.nim +++ b/lib/pure/ropes.nim @@ -29,7 +29,7 @@ const var cacheEnabled = false -type +type Rope* = ref RopeObj ## empty rope is represented by nil RopeObj {.acyclic.} = object left, right: Rope @@ -59,7 +59,7 @@ proc newRope(data: string): Rope = result.length = len(data) result.data = data -var +var cache {.threadvar.}: Rope # the root of the cache tree N {.threadvar.}: Rope # dummy rope needed for splay algorithm @@ -81,7 +81,7 @@ proc splay(s: string, tree: Rope, cmpres: var int): Rope = t.left = y.right y.right = t t = y - if t.left == nil: break + if t.left == nil: break r.left = t r = t t = t.left @@ -91,7 +91,7 @@ proc splay(s: string, tree: Rope, cmpres: var int): Rope = t.right = y.left y.left = t t = y - if t.right == nil: break + if t.right == nil: break le.right = t le = t t = t.right @@ -109,7 +109,7 @@ proc insertInCache(s: string, tree: Rope): Rope = if t == nil: result = newRope(s) when countCacheMisses: inc(misses) - return + return var cmp: int t = splay(s, t, cmp) if cmp == 0: @@ -197,7 +197,7 @@ proc add*(a: var Rope, b: Rope) {.rtl, extern: "nro$1Rope".} = proc add*(a: var Rope, b: string) {.rtl, extern: "nro$1Str".} = ## adds `b` to the rope `a`. a = a & b - + proc `[]`*(r: Rope, i: int): char {.rtl, extern: "nroCharAt".} = ## returns the character at position `i` in the rope `r`. This is quite ## expensive! Worst-case: O(n). If ``i >= r.len``, ``\0`` is returned. @@ -250,7 +250,7 @@ when false: proc compiledArg(idx: int): Rope = new(result) result.length = -idx - + proc compileFrmt(frmt: string): Rope = var i = 0 var length = len(frmt) @@ -272,7 +272,7 @@ when false: while true: j = j * 10 + ord(frmt[i]) - ord('0') inc(i) - if frmt[i] notin {'0'..'9'}: break + if frmt[i] notin {'0'..'9'}: break add(s, compiledArg(j)) of '{': inc(i) @@ -345,7 +345,7 @@ const proc equalsFile*(r: Rope, f: File): bool {.rtl, extern: "nro$1File".} = ## returns true if the contents of the file `f` equal `r`. - var + var buf: array[bufSize, char] bpos = buf.len blen = buf.len @@ -363,7 +363,7 @@ proc equalsFile*(r: Rope, f: File): bool {.rtl, extern: "nro$1File".} = return let n = min(blen - bpos, slen - spos) # TODO There's gotta be a better way of comparing here... - if not equalMem(addr(buf[bpos]), + if not equalMem(addr(buf[bpos]), cast[pointer](cast[int](cstring(s))+spos), n): result = false return diff --git a/lib/pure/scgi.nim b/lib/pure/scgi.nim index 3de422c87..1d54b4591 100644 --- a/lib/pure/scgi.nim +++ b/lib/pure/scgi.nim @@ -8,13 +8,13 @@ # ## This module implements helper procs for SCGI applications. Example: -## +## ## .. code-block:: Nim ## ## import strtabs, sockets, scgi ## ## var counter = 0 -## proc handleRequest(client: Socket, input: string, +## proc handleRequest(client: Socket, input: string, ## headers: StringTableRef): bool {.procvar.} = ## inc(counter) ## client.writeStatusOkTextContent() @@ -37,19 +37,19 @@ import sockets, strutils, os, strtabs, asyncio type ScgiError* = object of IOError ## the exception that is raised, if a SCGI error occurs -proc raiseScgiError*(msg: string) {.noreturn.} = +proc raiseScgiError*(msg: string) {.noreturn.} = ## raises an ScgiError exception with message `msg`. var e: ref ScgiError new(e) e.msg = msg raise e -proc parseWord(inp: string, outp: var string, start: int): int = +proc parseWord(inp: string, outp: var string, start: int): int = result = start while inp[result] != '\0': inc(result) outp = substr(inp, start, result-1) -proc parseHeaders(s: string, L: int): StringTableRef = +proc parseHeaders(s: string, L: int): StringTableRef = result = newStringTable() var i = 0 while i < L: @@ -59,12 +59,12 @@ proc parseHeaders(s: string, L: int): StringTableRef = result[key] = val if s[i] == ',': inc(i) else: raiseScgiError("',' after netstring expected") - -proc recvChar(s: Socket): char = + +proc recvChar(s: Socket): char = var c: char - if recv(s, addr(c), sizeof(c)) == sizeof(c): + if recv(s, addr(c), sizeof(c)) == sizeof(c): result = c - + type ScgiState* = object of RootObj ## SCGI state object server: Socket @@ -72,22 +72,22 @@ type client*: Socket ## the client socket to send data to headers*: StringTableRef ## the parsed headers input*: string ## the input buffer - - + + # Async - + ClientMode = enum ClientReadChar, ClientReadHeaders, ClientReadContent - + AsyncClient = ref object c: AsyncSocket mode: ClientMode dataLen: int headers: StringTableRef ## the parsed headers input: string ## the input buffer - + AsyncScgiStateObj = object - handleRequest: proc (client: AsyncSocket, + handleRequest: proc (client: AsyncSocket, input: string, headers: StringTableRef) {.closure, gcsafe.} asyncServer: AsyncSocket @@ -98,19 +98,19 @@ type PAsyncScgiState: AsyncScgiState, scgiError: raiseScgiError].} proc recvBuffer(s: var ScgiState, L: int) = - if L > s.bufLen: + if L > s.bufLen: s.bufLen = L s.input = newString(L) - if L > 0 and recv(s.client, cstring(s.input), L) != L: + if L > 0 and recv(s.client, cstring(s.input), L) != L: raiseScgiError("could not read all data") setLen(s.input, L) - + proc open*(s: var ScgiState, port = Port(4000), address = "127.0.0.1", - reuseAddr = false) = + reuseAddr = false) = ## opens a connection. s.bufLen = 4000 s.input = newString(s.bufLen) # will be reused - + s.server = socket() if s.server == invalidSocket: raiseOSError(osLastError()) new(s.client) # Initialise s.client for `next` @@ -120,12 +120,12 @@ proc open*(s: var ScgiState, port = Port(4000), address = "127.0.0.1", s.server.setSockOpt(OptReuseAddr, true) bindAddr(s.server, port, address) listen(s.server) - -proc close*(s: var ScgiState) = + +proc close*(s: var ScgiState) = ## closes the connection. s.server.close() -proc next*(s: var ScgiState, timeout: int = -1): bool = +proc next*(s: var ScgiState, timeout: int = -1): bool = ## proceed to the first/next request. Waits ``timeout`` milliseconds for a ## request, if ``timeout`` is `-1` then this function will never time out. ## Returns `true` if a new request has been processed. @@ -139,18 +139,18 @@ proc next*(s: var ScgiState, timeout: int = -1): bool = if d == '\0': s.client.close() return false - if d notin strutils.Digits: + if d notin strutils.Digits: if d != ':': raiseScgiError("':' after length expected") break - L = L * 10 + ord(d) - ord('0') + L = L * 10 + ord(d) - ord('0') recvBuffer(s, L+1) s.headers = parseHeaders(s.input, L) if s.headers["SCGI"] != "1": raiseScgiError("SCGI Version 1 expected") L = parseInt(s.headers["CONTENT_LENGTH"]) recvBuffer(s, L) return true - -proc writeStatusOkTextContent*(c: Socket, contentType = "text/html") = + +proc writeStatusOkTextContent*(c: Socket, contentType = "text/html") = ## sends the following string to the socket `c`:: ## ## Status: 200 OK\r\LContent-Type: text/html\r\L\r\L @@ -159,9 +159,9 @@ proc writeStatusOkTextContent*(c: Socket, contentType = "text/html") = c.send("Status: 200 OK\r\L" & "Content-Type: $1\r\L\r\L" % contentType) -proc run*(handleRequest: proc (client: Socket, input: string, +proc run*(handleRequest: proc (client: Socket, input: string, headers: StringTableRef): bool {.nimcall,gcsafe.}, - port = Port(4000)) = + port = Port(4000)) = ## encapsulates the SCGI object and main loop. var s: ScgiState s.open(port) @@ -197,7 +197,7 @@ proc checkCloseSocket(client: AsyncClient) = s.close() s.delHandleWrite() else: client.c.close() - + proc handleClientRead(client: AsyncClient, s: AsyncScgiState) = case client.mode of ClientReadChar: @@ -223,7 +223,7 @@ proc handleClientRead(client: AsyncClient, s: AsyncScgiState) = client.headers = parseHeaders(client.input, client.input.len-1) if client.headers["SCGI"] != "1": raiseScgiError("SCGI Version 1 expected") client.input = "" # For next part - + let contentLen = parseInt(client.headers["CONTENT_LENGTH"]) if contentLen > 0: client.mode = ClientReadContent @@ -250,12 +250,12 @@ proc handleAccept(sock: AsyncSocket, s: AsyncScgiState) = accept(s.asyncServer, client) var asyncClient = AsyncClient(c: client, mode: ClientReadChar, dataLen: 0, headers: newStringTable(), input: "") - client.handleRead = + client.handleRead = proc (sock: AsyncSocket) = handleClientRead(asyncClient, s) s.disp.register(client) -proc open*(handleRequest: proc (client: AsyncSocket, +proc open*(handleRequest: proc (client: AsyncSocket, input: string, headers: StringTableRef) {. closure, gcsafe.}, port = Port(4000), address = "127.0.0.1", @@ -286,7 +286,7 @@ proc close*(s: AsyncScgiState) = when false: var counter = 0 - proc handleRequest(client: Socket, input: string, + proc handleRequest(client: Socket, input: string, headers: StringTableRef): bool {.procvar.} = inc(counter) client.writeStatusOkTextContent() diff --git a/lib/pure/smtp.nim b/lib/pure/smtp.nim index c1bc259a5..b2adac2f3 100644 --- a/lib/pure/smtp.nim +++ b/lib/pure/smtp.nim @@ -7,25 +7,25 @@ # distribution, for details about the copyright. # -## This module implements the SMTP client protocol as specified by RFC 5321, +## This module implements the SMTP client protocol as specified by RFC 5321, ## this can be used to send mail to any SMTP Server. -## -## This module also implements the protocol used to format messages, +## +## This module also implements the protocol used to format messages, ## as specified by RFC 2822. -## +## ## Example gmail use: -## -## +## +## ## .. code-block:: Nim -## var msg = createMessage("Hello from Nim's SMTP", -## "Hello!.\n Is this awesome or what?", +## var msg = createMessage("Hello from Nim's SMTP", +## "Hello!.\n Is this awesome or what?", ## @["foo@gmail.com"]) ## var smtp = connect("smtp.gmail.com", 465, true, true) ## smtp.auth("username", "password") ## smtp.sendmail("username@gmail.com", @["foo@gmail.com"], $msg) -## -## -## For SSL support this module relies on OpenSSL. If you want to +## +## +## For SSL support this module relies on OpenSSL. If you want to ## enable SSL, compile with ``-d:ssl``. import net, strutils, strtabs, base64, os @@ -35,14 +35,14 @@ type Smtp* = object sock: Socket debug: bool - + Message* = object msgTo: seq[string] msgCc: seq[string] msgSubject: string msgOtherHeaders: StringTableRef msgBody: string - + ReplyError* = object of IOError AsyncSmtp* = ref object @@ -84,7 +84,7 @@ when not defined(ssl): else: let defaultSSLContext = newContext(verifyMode = CVerifyNone) -proc connect*(address: string, port = Port(25), +proc connect*(address: string, port = Port(25), ssl = false, debug = false, sslContext = defaultSSLContext): Smtp = ## Establishes a connection with a SMTP server. @@ -94,17 +94,17 @@ proc connect*(address: string, port = Port(25), when compiledWithSsl: sslContext.wrapSocket(result.sock) else: - raise newException(ESystem, + raise newException(ESystem, "SMTP module compiled without SSL support") result.sock.connect(address, port) result.debug = debug - + result.checkReply("220") result.debugSend("HELO " & address & "\c\L") result.checkReply("250") proc auth*(smtp: var Smtp, username, password: string) = - ## Sends an AUTH command to the server to login as the `username` + ## Sends an AUTH command to the server to login as the `username` ## using `password`. ## May fail with ReplyError. @@ -113,13 +113,13 @@ proc auth*(smtp: var Smtp, username, password: string) = # i.e "334 VXNlcm5hbWU6" smtp.debugSend(encode(username) & "\c\L") smtp.checkReply("334") # TODO: Same as above, only "Password:" (I think?) - + smtp.debugSend(encode(password) & "\c\L") smtp.checkReply("235") # Check whether the authentification was successful. proc sendmail*(smtp: var Smtp, fromaddr: string, toaddrs: seq[string], msg: string) = - ## Sends `msg` from `fromaddr` to `toaddr`. + ## Sends `msg` from `fromaddr` to `toaddr`. ## Messages may be formed using ``createMessage`` by converting the ## Message into a string. @@ -128,7 +128,7 @@ proc sendmail*(smtp: var Smtp, fromaddr: string, for address in items(toaddrs): smtp.debugSend("RCPT TO:<" & address & ">\c\L") smtp.checkReply("250") - + # Send the message smtp.debugSend("DATA " & "\c\L") smtp.checkReply("354") @@ -175,7 +175,7 @@ proc `$`*(msg: Message): string = result.add("\c\L") result.add(msg.msgBody) - + proc newAsyncSmtp*(address: string, port: Port, useSsl = false, sslContext = defaultSslContext): AsyncSmtp = ## Creates a new ``AsyncSmtp`` instance. @@ -189,7 +189,7 @@ proc newAsyncSmtp*(address: string, port: Port, useSsl = false, when compiledWithSsl: sslContext.wrapSocket(result.sock) else: - raise newException(ESystem, + raise newException(ESystem, "SMTP module compiled without SSL support") proc quitExcpt(smtp: AsyncSmtp, msg: string): Future[void] = @@ -216,7 +216,7 @@ proc connect*(smtp: AsyncSmtp) {.async.} = await smtp.checkReply("250") proc auth*(smtp: AsyncSmtp, username, password: string) {.async.} = - ## Sends an AUTH command to the server to login as the `username` + ## Sends an AUTH command to the server to login as the `username` ## using `password`. ## May fail with ReplyError. @@ -225,7 +225,7 @@ proc auth*(smtp: AsyncSmtp, username, password: string) {.async.} = # i.e "334 VXNlcm5hbWU6" await smtp.sock.send(encode(username) & "\c\L") await smtp.checkReply("334") # TODO: Same as above, only "Password:" (I think?) - + await smtp.sock.send(encode(password) & "\c\L") await smtp.checkReply("235") # Check whether the authentification was successful. @@ -240,7 +240,7 @@ proc sendMail*(smtp: AsyncSmtp, fromAddr: string, for address in items(toAddrs): await smtp.sock.send("RCPT TO:<" & address & ">\c\L") await smtp.checkReply("250") - + # Send the message await smtp.sock.send("DATA " & "\c\L") await smtp.checkReply("354") @@ -254,24 +254,24 @@ proc close*(smtp: AsyncSmtp) {.async.} = smtp.sock.close() when not defined(testing) and isMainModule: - #var msg = createMessage("Test subject!", + #var msg = createMessage("Test subject!", # "Hello, my name is dom96.\n What\'s yours?", @["dominik@localhost"]) #echo(msg) #var smtp = connect("localhost", 25, False, True) #smtp.sendmail("root@localhost", @["dominik@localhost"], $msg) - + #echo(decode("a17sm3701420wbe.12")) proc main() {.async.} = var client = newAsyncSmtp("smtp.gmail.com", Port(465), true) await client.connect() await client.auth("johndoe", "foo") - var msg = createMessage("Hello from Nim's SMTP!", - "Hello!!!!.\n Is this awesome or what?", + var msg = createMessage("Hello from Nim's SMTP!", + "Hello!!!!.\n Is this awesome or what?", @["blah@gmail.com"]) echo(msg) await client.sendMail("blah@gmail.com", @["blah@gmail.com"], $msg) await client.close() - + waitFor main() diff --git a/lib/pure/sockets.nim b/lib/pure/sockets.nim index 18b2ab1e9..29f0bf87d 100644 --- a/lib/pure/sockets.nim +++ b/lib/pure/sockets.nim @@ -51,17 +51,17 @@ else: # Note: The enumerations are mapped to Window's constants. -when defined(ssl): +when defined(ssl): type SSLError* = object of Exception SSLCVerifyMode* = enum CVerifyNone, CVerifyPeer - + SSLProtVersion* = enum protSSLv2, protSSLv3, protTLSv1, protSSLv23 - + SSLContext* = distinct SSLCTX SSLAcceptResult* = enum @@ -93,11 +93,11 @@ type sslPeekChar: char of false: nil nonblocking: bool - + Socket* = ref SocketImpl - + Port* = distinct uint16 ## port type - + Domain* = enum ## domain, which specifies the protocol family of the ## created socket. Other domains than those that are listed ## here are unsupported. @@ -112,7 +112,7 @@ type SOCK_SEQPACKET = 5 ## reliable sequenced packet service Protocol* = enum ## third argument to `socket` proc - IPPROTO_TCP = 6, ## Transmission control protocol. + IPPROTO_TCP = 6, ## Transmission control protocol. IPPROTO_UDP = 17, ## User datagram protocol. IPPROTO_IP, ## Internet protocol. Unsupported on Windows. IPPROTO_IPV6, ## Internet Protocol Version 6. Unsupported on Windows. @@ -178,7 +178,7 @@ proc `==`*(a, b: Port): bool {.borrow.} proc `$`*(p: Port): string {.borrow.} ## returns the port number as a string -proc ntohl*(x: int32): int32 = +proc ntohl*(x: int32): int32 = ## Converts 32-bit integers from network to host byte order. ## On machines where the host byte order is the same as network byte order, ## this is a no-op; otherwise, it performs a 4-byte swap operation. @@ -206,7 +206,7 @@ proc htons*(x: int16): int16 = ## On machines where the host byte order is the same as network byte ## order, this is a no-op; otherwise, it performs a 2-byte swap operation. result = sockets.ntohs(x) - + when defined(Posix): proc toInt(domain: Domain): cint = case domain @@ -234,19 +234,19 @@ when defined(Posix): else: discard else: - proc toInt(domain: Domain): cint = + proc toInt(domain: Domain): cint = result = toU16(ord(domain)) proc toInt(typ: SockType): cint = result = cint(ord(typ)) - + proc toInt(p: Protocol): cint = result = cint(ord(p)) proc socket*(domain: Domain = AF_INET, typ: SockType = SOCK_STREAM, protocol: Protocol = IPPROTO_TCP, buffered = true): Socket = ## Creates a new socket; returns `InvalidSocket` if an error occurs. - + # TODO: Perhaps this should just raise EOS when an error occurs. when defined(Windows): result = newTSocket(winlean.socket(ord(domain), ord(typ), ord(protocol)), buffered) @@ -277,27 +277,27 @@ when defined(ssl): raise newException(system.IOError, "Certificate file could not be found: " & certFile) if keyFile != "" and not existsFile(keyFile): raise newException(system.IOError, "Key file could not be found: " & keyFile) - + if certFile != "": var ret = SSLCTXUseCertificateChainFile(ctx, certFile) if ret != 1: raiseSslError() - + # TODO: Password? www.rtfm.com/openssl-examples/part1.pdf if keyFile != "": if SSL_CTX_use_PrivateKey_file(ctx, keyFile, SSL_FILETYPE_PEM) != 1: raiseSslError() - + if SSL_CTX_check_private_key(ctx) != 1: raiseSslError("Verification of private key file failed.") proc newContext*(protVersion = protSSLv23, verifyMode = CVerifyPeer, certFile = "", keyFile = ""): SSLContext = ## Creates an SSL context. - ## - ## Protocol version specifies the protocol to use. SSLv2, SSLv3, TLSv1 are - ## are available with the addition of ``ProtSSLv23`` which allows for + ## + ## Protocol version specifies the protocol to use. SSLv2, SSLv3, TLSv1 are + ## are available with the addition of ``ProtSSLv23`` which allows for ## compatibility with all of them. ## ## There are currently only two options for verify mode; @@ -322,7 +322,7 @@ when defined(ssl): newCTX = SSL_CTX_new(SSLv3_method()) of protTLSv1: newCTX = SSL_CTX_new(TLSv1_method()) - + if newCTX.SSLCTXSetCipherList("ALL") != 1: raiseSslError() case verifyMode @@ -343,7 +343,7 @@ when defined(ssl): ## ## **Disclaimer**: This code is not well tested, may be very unsafe and ## prone to security vulnerabilities. - + socket.isSSL = true socket.sslContext = ctx socket.sslHandle = SSLNew(SSLCTX(socket.sslContext)) @@ -351,7 +351,7 @@ when defined(ssl): socket.sslHasPeekChar = false if socket.sslHandle == nil: raiseSslError() - + if SSLSetFd(socket.sslHandle, socket.fd) != 1: raiseSslError() @@ -382,7 +382,7 @@ proc raiseSocketError*(socket: Socket, err: int = -1, async = false) = of SSL_ERROR_SYSCALL, SSL_ERROR_SSL: raiseSslError() else: raiseSslError("Unknown Error") - + if err == -1 and not (when defined(ssl): socket.isSSL else: false): let lastError = osLastError() if async: @@ -397,15 +397,15 @@ proc raiseSocketError*(socket: Socket, err: int = -1, async = false) = else: raiseOSError(lastError) proc listen*(socket: Socket, backlog = SOMAXCONN) {.tags: [ReadIOEffect].} = - ## Marks ``socket`` as accepting connections. - ## ``Backlog`` specifies the maximum length of the + ## Marks ``socket`` as accepting connections. + ## ``Backlog`` specifies the maximum length of the ## queue of pending connections. if listen(socket.fd, cint(backlog)) < 0'i32: raiseOSError(osLastError()) proc invalidIp4(s: string) {.noreturn, noinline.} = raise newException(ValueError, "invalid ip4 address: " & s) -proc parseIp4*(s: string): BiggestInt = +proc parseIp4*(s: string): BiggestInt = ## parses an IP version 4 in dotted decimal form like "a.b.c.d". ## ## This is equivalent to `inet_ntoa`:idx:. @@ -469,8 +469,8 @@ proc bindAddr*(socket: Socket, port = Port(0), address = "") {. gaiNim(address, port, hints, aiList) if bindSocket(socket.fd, aiList.ai_addr, aiList.ai_addrlen.SockLen) < 0'i32: raiseOSError(osLastError()) - -proc getSockName*(socket: Socket): Port = + +proc getSockName*(socket: Socket): Port = ## returns the socket's associated port number. var name: Sockaddr_in when defined(Windows): @@ -485,14 +485,14 @@ proc getSockName*(socket: Socket): Port = raiseOSError(osLastError()) result = Port(sockets.ntohs(name.sin_port)) -template acceptAddrPlain(noClientRet, successRet: expr, +template acceptAddrPlain(noClientRet, successRet: expr, sslImplementation: stmt): stmt {.immediate.} = assert(client != nil) var sockAddress: Sockaddr_in var addrLen = sizeof(sockAddress).SockLen var sock = accept(server.fd, cast[ptr SockAddr](addr(sockAddress)), addr(addrLen)) - + if sock == osInvalidSocket: let err = osLastError() when defined(windows): @@ -537,7 +537,7 @@ proc acceptAddr*(server: Socket, client: var Socket, address: var string) {. ## The resulting client will inherit any properties of the server socket. For ## example: whether the socket is buffered or not. ## - ## **Note**: ``client`` must be initialised (with ``new``), this function + ## **Note**: ``client`` must be initialised (with ``new``), this function ## makes no effort to initialise the ``client`` variable. ## ## **Warning:** When using SSL with non-blocking sockets, it is best to use @@ -546,7 +546,7 @@ proc acceptAddr*(server: Socket, client: var Socket, address: var string) {. when defined(ssl): if server.isSSL: # We must wrap the client sock in a ssl context. - + server.sslContext.wrapSocket(client) let ret = SSLAccept(client.sslHandle) while ret <= 0: @@ -572,9 +572,9 @@ when defined(ssl): proc acceptAddrSSL*(server: Socket, client: var Socket, address: var string): SSLAcceptResult {. tags: [ReadIOEffect].} = - ## This procedure should only be used for non-blocking **SSL** sockets. + ## This procedure should only be used for non-blocking **SSL** sockets. ## It will immediately return with one of the following values: - ## + ## ## ``AcceptSuccess`` will be returned when a client has been successfully ## accepted and the handshake has been successfully performed between ## ``server`` and the newly connected client. @@ -591,7 +591,7 @@ when defined(ssl): if server.isSSL: client.setBlocking(false) # We must wrap the client sock in a ssl context. - + if not client.isSSL or client.sslHandle == nil: server.sslContext.wrapSocket(client) let ret = SSLAccept(client.sslHandle) @@ -623,10 +623,10 @@ when defined(ssl): proc accept*(server: Socket, client: var Socket) {.tags: [ReadIOEffect].} = ## Equivalent to ``acceptAddr`` but doesn't return the address, only the ## socket. - ## + ## ## **Note**: ``client`` must be initialised (with ``new``), this function ## makes no effort to initialise the ``client`` variable. - + var addrDummy = "" acceptAddr(server, client, addrDummy) @@ -662,7 +662,7 @@ proc close*(socket: Socket) = socket.sslHandle = nil proc getServByName*(name, proto: string): Servent {.tags: [ReadIOEffect].} = - ## Searches the database from the beginning and finds the first entry for + ## Searches the database from the beginning and finds the first entry for ## which the service name specified by ``name`` matches the s_name member ## and the protocol name specified by ``proto`` matches the s_proto member. ## @@ -676,10 +676,10 @@ proc getServByName*(name, proto: string): Servent {.tags: [ReadIOEffect].} = result.aliases = cstringArrayToSeq(s.s_aliases) result.port = Port(s.s_port) result.proto = $s.s_proto - -proc getServByPort*(port: Port, proto: string): Servent {.tags: [ReadIOEffect].} = - ## Searches the database from the beginning and finds the first entry for - ## which the port specified by ``port`` matches the s_port member and the + +proc getServByPort*(port: Port, proto: string): Servent {.tags: [ReadIOEffect].} = + ## Searches the database from the beginning and finds the first entry for + ## which the port specified by ``port`` matches the s_port member and the ## protocol name specified by ``proto`` matches the s_proto member. ## ## On posix this will search through the ``/etc/services`` file. @@ -697,20 +697,20 @@ proc getHostByAddr*(ip: string): Hostent {.tags: [ReadIOEffect].} = ## This function will lookup the hostname of an IP Address. var myaddr: InAddr myaddr.s_addr = inet_addr(ip) - + when defined(windows): var s = winlean.gethostbyaddr(addr(myaddr), sizeof(myaddr).cuint, cint(sockets.AF_INET)) if s == nil: raiseOSError(osLastError()) else: - var s = posix.gethostbyaddr(addr(myaddr), sizeof(myaddr).Socklen, + var s = posix.gethostbyaddr(addr(myaddr), sizeof(myaddr).Socklen, cint(posix.AF_INET)) if s == nil: raiseOSError(osLastError(), $hstrerror(h_errno)) - + result.name = $s.h_name result.aliases = cstringArrayToSeq(s.h_aliases) - when defined(windows): + when defined(windows): result.addrtype = Domain(s.h_addrtype) else: if s.h_addrtype == posix.AF_INET: @@ -722,7 +722,7 @@ proc getHostByAddr*(ip: string): Hostent {.tags: [ReadIOEffect].} = result.addrList = cstringArrayToSeq(s.h_addr_list) result.length = int(s.h_length) -proc getHostByName*(name: string): Hostent {.tags: [ReadIOEffect].} = +proc getHostByName*(name: string): Hostent {.tags: [ReadIOEffect].} = ## This function will lookup the IP address of a hostname. when defined(Windows): var s = winlean.gethostbyname(name) @@ -731,7 +731,7 @@ proc getHostByName*(name: string): Hostent {.tags: [ReadIOEffect].} = if s == nil: raiseOSError(osLastError()) result.name = $s.h_name result.aliases = cstringArrayToSeq(s.h_aliases) - when defined(windows): + when defined(windows): result.addrtype = Domain(s.h_addrtype) else: if s.h_addrtype == posix.AF_INET: @@ -744,11 +744,11 @@ proc getHostByName*(name: string): Hostent {.tags: [ReadIOEffect].} = result.length = int(s.h_length) proc getSockOptInt*(socket: Socket, level, optname: int): int {. - tags: [ReadIOEffect].} = + tags: [ReadIOEffect].} = ## getsockopt for integer options. var res: cint var size = sizeof(res).SockLen - if getsockopt(socket.fd, cint(level), cint(optname), + if getsockopt(socket.fd, cint(level), cint(optname), addr(res), addr(size)) < 0'i32: raiseOSError(osLastError()) result = int(res) @@ -757,7 +757,7 @@ proc setSockOptInt*(socket: Socket, level, optname, optval: int) {. tags: [WriteIOEffect].} = ## setsockopt for integer options. var value = cint(optval) - if setsockopt(socket.fd, cint(level), cint(optname), addr(value), + if setsockopt(socket.fd, cint(level), cint(optname), addr(value), sizeof(value).SockLen) < 0'i32: raiseOSError(osLastError()) @@ -776,7 +776,7 @@ proc getSockOpt*(socket: Socket, opt: SOBool, level = SOL_SOCKET): bool {. ## Retrieves option ``opt`` as a boolean value. var res: cint var size = sizeof(res).SockLen - if getsockopt(socket.fd, cint(level), toCInt(opt), + if getsockopt(socket.fd, cint(level), toCInt(opt), addr(res), addr(size)) < 0'i32: raiseOSError(osLastError()) result = res != 0 @@ -785,11 +785,11 @@ proc setSockOpt*(socket: Socket, opt: SOBool, value: bool, level = SOL_SOCKET) { tags: [WriteIOEffect].} = ## Sets option ``opt`` to a boolean value specified by ``value``. var valuei = cint(if value: 1 else: 0) - if setsockopt(socket.fd, cint(level), toCInt(opt), addr(valuei), + if setsockopt(socket.fd, cint(level), toCInt(opt), addr(valuei), sizeof(valuei).SockLen) < 0'i32: raiseOSError(osLastError()) -proc connect*(socket: Socket, address: string, port = Port(0), +proc connect*(socket: Socket, address: string, port = Port(0), af: Domain = AF_INET) {.tags: [ReadIOEffect].} = ## Connects socket to ``address``:``port``. ``Address`` can be an IP address or a ## host name. If ``address`` is a host name, this function will try each IP @@ -816,7 +816,7 @@ proc connect*(socket: Socket, address: string, port = Port(0), freeaddrinfo(aiList) if not success: raiseOSError(lastError) - + when defined(ssl): if socket.isSSL: let ret = SSLConnect(socket.sslHandle) @@ -825,7 +825,7 @@ proc connect*(socket: Socket, address: string, port = Port(0), case err of SSL_ERROR_ZERO_RETURN: raiseSslError("TLS/SSL connection failed to initiate, socket closed prematurely.") - of SSL_ERROR_WANT_READ, SSL_ERROR_WANT_WRITE, SSL_ERROR_WANT_CONNECT, + of SSL_ERROR_WANT_READ, SSL_ERROR_WANT_WRITE, SSL_ERROR_WANT_CONNECT, SSL_ERROR_WANT_ACCEPT: raiseSslError("The operation did not complete. Perhaps you should use connectAsync?") of SSL_ERROR_WANT_X509_LOOKUP: @@ -834,7 +834,7 @@ proc connect*(socket: Socket, address: string, port = Port(0), raiseSslError() else: raiseSslError("Unknown error") - + when false: var s: TSockAddrIn s.sin_addr.s_addr = inet_addr(address) @@ -842,7 +842,7 @@ proc connect*(socket: Socket, address: string, port = Port(0), when defined(windows): s.sin_family = toU16(ord(af)) else: - case af + case af of AF_UNIX: s.sin_family = posix.AF_UNIX of AF_INET: s.sin_family = posix.AF_INET of AF_INET6: s.sin_family = posix.AF_INET6 @@ -886,7 +886,7 @@ proc connectAsync*(socket: Socket, name: string, port = Port(0), if lastError.int32 == EINTR or lastError.int32 == EINPROGRESS: success = true break - + it = it.ai_next freeaddrinfo(aiList) @@ -942,12 +942,12 @@ proc timeValFromMilliseconds(timeout = 500): Timeval = result.tv_sec = seconds.int32 result.tv_usec = ((timeout - seconds * 1000) * 1000).int32 -proc createFdSet(fd: var TFdSet, s: seq[Socket], m: var int) = +proc createFdSet(fd: var TFdSet, s: seq[Socket], m: var int) = FD_ZERO(fd) - for i in items(s): + for i in items(s): m = max(m, int(i.fd)) FD_SET(i.fd, fd) - + proc pruneSocketSet(s: var seq[Socket], fd: var TFdSet) = var i = 0 var L = s.len @@ -982,13 +982,13 @@ proc checkBuffer(readfds: var seq[Socket]): int = if result > 0: readfds = res -proc select*(readfds, writefds, exceptfds: var seq[Socket], - timeout = 500): int {.tags: [ReadIOEffect].} = +proc select*(readfds, writefds, exceptfds: var seq[Socket], + timeout = 500): int {.tags: [ReadIOEffect].} = ## Traditional select function. This function will return the number of ## sockets that are ready to be read from, written to, or which have errors. - ## If there are none; 0 is returned. + ## If there are none; 0 is returned. ## ``Timeout`` is in milliseconds and -1 can be specified for no timeout. - ## + ## ## Sockets which are **not** ready for reading, writing or which don't have ## errors waiting on them are removed from the ``readfds``, ``writefds``, ## ``exceptfds`` sequences respectively. @@ -997,44 +997,44 @@ proc select*(readfds, writefds, exceptfds: var seq[Socket], return buffersFilled var tv {.noInit.}: Timeval = timeValFromMilliseconds(timeout) - + var rd, wr, ex: TFdSet var m = 0 createFdSet((rd), readfds, m) createFdSet((wr), writefds, m) createFdSet((ex), exceptfds, m) - + if timeout != -1: result = int(select(cint(m+1), addr(rd), addr(wr), addr(ex), addr(tv))) else: result = int(select(cint(m+1), addr(rd), addr(wr), addr(ex), nil)) - + pruneSocketSet(readfds, (rd)) pruneSocketSet(writefds, (wr)) pruneSocketSet(exceptfds, (ex)) -proc select*(readfds, writefds: var seq[Socket], +proc select*(readfds, writefds: var seq[Socket], timeout = 500): int {.tags: [ReadIOEffect].} = ## Variant of select with only a read and write list. let buffersFilled = checkBuffer(readfds) if buffersFilled > 0: return buffersFilled var tv {.noInit.}: Timeval = timeValFromMilliseconds(timeout) - + var rd, wr: TFdSet var m = 0 createFdSet((rd), readfds, m) createFdSet((wr), writefds, m) - + if timeout != -1: result = int(select(cint(m+1), addr(rd), addr(wr), nil, addr(tv))) else: result = int(select(cint(m+1), addr(rd), addr(wr), nil, nil)) - + pruneSocketSet(readfds, (rd)) pruneSocketSet(writefds, (wr)) -proc selectWrite*(writefds: var seq[Socket], +proc selectWrite*(writefds: var seq[Socket], timeout = 500): int {.tags: [ReadIOEffect].} = ## When a socket in ``writefds`` is ready to be written to then a non-zero ## value will be returned specifying the count of the sockets which can be @@ -1044,16 +1044,16 @@ proc selectWrite*(writefds: var seq[Socket], ## ``timeout`` is specified in milliseconds and ``-1`` can be specified for ## an unlimited time. var tv {.noInit.}: Timeval = timeValFromMilliseconds(timeout) - + var wr: TFdSet var m = 0 createFdSet((wr), writefds, m) - + if timeout != -1: result = int(select(cint(m+1), nil, addr(wr), nil, addr(tv))) else: result = int(select(cint(m+1), nil, addr(wr), nil, nil)) - + pruneSocketSet(writefds, (wr)) proc select*(readfds: var seq[Socket], timeout = 500): int = @@ -1062,16 +1062,16 @@ proc select*(readfds: var seq[Socket], timeout = 500): int = if buffersFilled > 0: return buffersFilled var tv {.noInit.}: Timeval = timeValFromMilliseconds(timeout) - + var rd: TFdSet var m = 0 createFdSet((rd), readfds, m) - + if timeout != -1: result = int(select(cint(m+1), addr(rd), nil, nil, addr(tv))) else: result = int(select(cint(m+1), addr(rd), nil, nil, nil)) - + pruneSocketSet(readfds, (rd)) proc readIntoBuf(socket: Socket, flags: int32): int = @@ -1107,12 +1107,12 @@ proc recv*(socket: Socket, data: pointer, size: int): int {.tags: [ReadIOEffect] if socket.isBuffered: if socket.bufLen == 0: retRead(0'i32, 0) - + var read = 0 while read < size: if socket.currPos >= socket.bufLen: retRead(0'i32, read) - + let chunk = min(socket.bufLen-socket.currPos, size-read) var d = cast[cstring](data) copyMem(addr(d[read]), addr(socket.buffer[socket.currPos]), chunk) @@ -1155,7 +1155,7 @@ proc waitFor(socket: Socket, waited: var float, timeout, size: int, else: if timeout - int(waited * 1000.0) < 1: raise newException(TimeoutError, "Call to '" & funcName & "' timed out.") - + when defined(ssl): if socket.isSSL: if socket.hasDataBuffered: @@ -1164,7 +1164,7 @@ proc waitFor(socket: Socket, waited: var float, timeout, size: int, let sslPending = SSLPending(socket.sslHandle) if sslPending != 0: return sslPending - + var s = @[socket] var startTime = epochTime() let selRet = select(s, timeout - int(waited * 1000.0)) @@ -1176,8 +1176,8 @@ proc waitFor(socket: Socket, waited: var float, timeout, size: int, proc recv*(socket: Socket, data: pointer, size: int, timeout: int): int {. tags: [ReadIOEffect, TimeEffect].} = ## overload with a ``timeout`` parameter in milliseconds. - var waited = 0.0 # number of seconds already waited - + var waited = 0.0 # number of seconds already waited + var read = 0 while read < size: let avail = waitFor(socket, waited, timeout, size-read, "recv") @@ -1187,7 +1187,7 @@ proc recv*(socket: Socket, data: pointer, size: int, timeout: int): int {. if result < 0: return result inc(read, result) - + result = read proc recv*(socket: Socket, data: var string, size: int, timeout = -1): int = @@ -1231,7 +1231,7 @@ proc peekChar(socket: Socket, c: var char): int {.tags: [ReadIOEffect].} = var res = socket.readIntoBuf(0'i32) if res <= 0: result = res - + c = socket.buffer[socket.currPos] else: when defined(ssl): @@ -1239,7 +1239,7 @@ proc peekChar(socket: Socket, c: var char): int {.tags: [ReadIOEffect].} = if not socket.sslHasPeekChar: result = SSLRead(socket.sslHandle, addr(socket.sslPeekChar), 1) socket.sslHasPeekChar = true - + c = socket.sslPeekChar return result = recv(socket.fd, addr(c), 1, MSG_PEEK) @@ -1251,11 +1251,11 @@ proc recvLine*(socket: Socket, line: var TaintedString, timeout = -1): bool {. ## If a full line is received ``\r\L`` is not ## added to ``line``, however if solely ``\r\L`` is received then ``line`` ## will be set to it. - ## + ## ## ``True`` is returned if data is available. ``False`` suggests an ## error, EOS exceptions are not raised and ``False`` is simply returned ## instead. - ## + ## ## If the socket is disconnected, ``line`` will be set to ``""`` and ``True`` ## will be returned. ## @@ -1264,7 +1264,7 @@ proc recvLine*(socket: Socket, line: var TaintedString, timeout = -1): bool {. ## ## **Deprecated since version 0.9.2**: This function has been deprecated in ## favour of readLine. - + template addNLIfEmpty(): stmt = if line.len == 0: line.add("\c\L") @@ -1286,7 +1286,7 @@ proc recvLine*(socket: Socket, line: var TaintedString, timeout = -1): bool {. elif n <= 0: return false addNLIfEmpty() return true - elif c == '\L': + elif c == '\L': addNLIfEmpty() return true add(line.string, c) @@ -1298,14 +1298,14 @@ proc readLine*(socket: Socket, line: var TaintedString, timeout = -1) {. ## If a full line is read ``\r\L`` is not ## added to ``line``, however if solely ``\r\L`` is read then ``line`` ## will be set to it. - ## + ## ## If the socket is disconnected, ``line`` will be set to ``""``. ## ## An EOS exception will be raised in the case of a socket error. ## ## A timeout can be specified in milliseconds, if data is not received within ## the specified time an ETimeout exception will be raised. - + template addNLIfEmpty(): stmt = if line.len == 0: line.add("\c\L") @@ -1327,12 +1327,12 @@ proc readLine*(socket: Socket, line: var TaintedString, timeout = -1) {. elif n <= 0: socket.raiseSocketError() addNLIfEmpty() return - elif c == '\L': + elif c == '\L': addNLIfEmpty() return add(line.string, c) -proc recvLineAsync*(socket: Socket, +proc recvLineAsync*(socket: Socket, line: var TaintedString): RecvLineResult {.tags: [ReadIOEffect], deprecated.} = ## Similar to ``recvLine`` but designed for non-blocking sockets. ## @@ -1350,21 +1350,21 @@ proc recvLineAsync*(socket: Socket, while true: var c: char var n = recv(socket, addr(c), 1) - if n < 0: + if n < 0: return (if line.len == 0: RecvFail else: RecvPartialLine) - elif n == 0: + elif n == 0: return (if line.len == 0: RecvDisconnected else: RecvPartialLine) if c == '\r': n = peekChar(socket, c) if n > 0 and c == '\L': discard recv(socket, addr(c), 1) - elif n <= 0: + elif n <= 0: return (if line.len == 0: RecvFail else: RecvPartialLine) return RecvFullLine elif c == '\L': return RecvFullLine add(line.string, c) -proc readLineAsync*(socket: Socket, +proc readLineAsync*(socket: Socket, line: var TaintedString): ReadLineResult {.tags: [ReadIOEffect].} = ## Similar to ``recvLine`` but designed for non-blocking sockets. ## @@ -1376,24 +1376,24 @@ proc readLineAsync*(socket: Socket, ## * If no data could be retrieved; ``ReadNone`` is returned. ## * If call to ``recv`` failed; **an EOS exception is raised.** setLen(line.string, 0) - + template errorOrNone = socket.raiseSocketError(async = true) return ReadNone - + while true: var c: char var n = recv(socket, addr(c), 1) #echo(n) if n < 0: if line.len == 0: errorOrNone else: return ReadPartialLine - elif n == 0: + elif n == 0: return (if line.len == 0: ReadDisconnected else: ReadPartialLine) if c == '\r': n = peekChar(socket, c) if n > 0 and c == '\L': discard recv(socket, addr(c), 1) - elif n <= 0: + elif n <= 0: if line.len == 0: errorOrNone else: return ReadPartialLine return ReadFullLine elif c == '\L': return ReadFullLine @@ -1424,7 +1424,7 @@ proc recv*(socket: Socket): TaintedString {.tags: [ReadIOEffect], deprecated.} = var bytesRead = recv(socket, cstring(buf), bufSize-1) # Error if bytesRead == -1: OSError(osLastError()) - + buf[bytesRead] = '\0' # might not be necessary setLen(buf, bytesRead) add(result.string, buf) @@ -1442,13 +1442,13 @@ proc recvTimeout*(socket: Socket, timeout: int): TaintedString {. var s = @[socket] if s.select(timeout) != 1: raise newException(TimeoutError, "Call to recv() timed out.") - + return socket.recv {.pop.} proc recvAsync*(socket: Socket, s: var TaintedString): bool {. tags: [ReadIOEffect], deprecated.} = - ## receives all the data from a non-blocking socket. If socket is non-blocking + ## receives all the data from a non-blocking socket. If socket is non-blocking ## and there are no messages available, `False` will be returned. ## Other socket errors will result in an ``EOS`` error. ## If socket is not a connectionless socket and socket is not connected @@ -1478,7 +1478,7 @@ proc recvAsync*(socket: Socket, s: var TaintedString): bool {. of SSL_ERROR_SYSCALL, SSL_ERROR_SSL: raiseSslError() else: raiseSslError("Unknown Error") - + if bytesRead == -1 and not (when defined(ssl): socket.isSSL else: false): let err = osLastError() when defined(windows): @@ -1510,7 +1510,7 @@ proc recvFrom*(socket: Socket, data: var string, length: int, ## so when ``socket`` is buffered the non-buffered implementation will be ## used. Therefore if ``socket`` contains something in its buffer this ## function will make no effort to return it. - + # TODO: Buffered sockets data.setLen(length) var sockAddress: Sockaddr_in @@ -1524,7 +1524,7 @@ proc recvFrom*(socket: Socket, data: var string, length: int, port = ntohs(sockAddress.sin_port).Port proc recvFromAsync*(socket: Socket, data: var string, length: int, - address: var string, port: var Port, + address: var string, port: var Port, flags = 0'i32): bool {.tags: [ReadIOEffect].} = ## Variant of ``recvFrom`` for non-blocking sockets. Unlike ``recvFrom``, ## this function will raise an EOS error whenever a socket error occurs. @@ -1573,11 +1573,11 @@ proc send*(socket: Socket, data: pointer, size: int): int {. when defined(ssl): if socket.isSSL: return SSLWrite(socket.sslHandle, cast[cstring](data), size) - + when defined(windows) or defined(macosx): result = send(socket.fd, data, size.cint, 0'i32) else: - when defined(solaris): + when defined(solaris): const MSG_NOSIGNAL = 0 result = send(socket.fd, data, size, int32(MSG_NOSIGNAL)) @@ -1590,7 +1590,7 @@ proc send*(socket: Socket, data: string) {.tags: [WriteIOEffect].} = when defined(ssl): if socket.isSSL: raiseSslError() - + raiseOSError(osLastError()) if sent != data.len: @@ -1633,7 +1633,7 @@ proc sendAsync*(socket: Socket, data: string): int {.tags: [WriteIOEffect].} = if err.int32 == EAGAIN or err.int32 == EWOULDBLOCK: return 0 else: raiseOSError(err) - + proc trySend*(socket: Socket, data: string): bool {.tags: [WriteIOEffect].} = ## safe alternative to ``send``. Does not raise an EOS when an error occurs, @@ -1644,7 +1644,7 @@ proc sendTo*(socket: Socket, address: string, port: Port, data: pointer, size: int, af: Domain = AF_INET, flags = 0'i32): int {. tags: [WriteIOEffect].} = ## low-level sendTo proc. This proc sends ``data`` to the specified ``address``, - ## which may be an IP address or a hostname, if a hostname is specified + ## which may be an IP address or a hostname, if a hostname is specified ## this function will try each IP of that hostname. ## ## **Note:** This proc is not available for SSL sockets. @@ -1654,7 +1654,7 @@ proc sendTo*(socket: Socket, address: string, port: Port, data: pointer, hints.ai_socktype = toInt(SOCK_STREAM) hints.ai_protocol = toInt(IPPROTO_TCP) gaiNim(address, port, hints, aiList) - + # try all possibilities: var success = false var it = aiList @@ -1668,7 +1668,7 @@ proc sendTo*(socket: Socket, address: string, port: Port, data: pointer, freeaddrinfo(aiList) -proc sendTo*(socket: Socket, address: string, port: Port, +proc sendTo*(socket: Socket, address: string, port: Port, data: string): int {.tags: [WriteIOEffect].} = ## Friendlier version of the low-level ``sendTo``. result = socket.sendTo(address, port, cstring(data), data.len) @@ -1677,10 +1677,10 @@ when defined(Windows): const IOCPARM_MASK = 127 IOC_IN = int(-2147483648) - FIONBIO = IOC_IN.int32 or ((sizeof(int32) and IOCPARM_MASK) shl 16) or + FIONBIO = IOC_IN.int32 or ((sizeof(int32) and IOCPARM_MASK) shl 16) or (102 shl 8) or 126 - proc ioctlsocket(s: SocketHandle, cmd: clong, + proc ioctlsocket(s: SocketHandle, cmd: clong, argptr: ptr clong): cint {. stdcall, importc:"ioctlsocket", dynlib: "ws2_32.dll".} @@ -1713,7 +1713,7 @@ proc connect*(socket: Socket, address: string, port = Port(0), timeout: int, ## the connection to the server to be made. let originalStatus = not socket.nonblocking socket.setBlocking(false) - + socket.connectAsync(address, port, af) var s: seq[Socket] = @[socket] if selectWrite(s, timeout) != 1: diff --git a/lib/pure/subexes.nim b/lib/pure/subexes.nim index 46824645d..2d1adc0eb 100644 --- a/lib/pure/subexes.nim +++ b/lib/pure/subexes.nim @@ -77,7 +77,7 @@ proc getFormatArg(p: var FormatParser, a: openArray[string]): int = result = if not negative: j-1 else: a.len-j of 'a'..'z', 'A'..'Z', '\128'..'\255', '_': var name = "" - while f[i] in PatternChars: + while f[i] in PatternChars: name.add(f[i]) inc(i) result = findNormalized(name, a)+1 @@ -131,7 +131,7 @@ proc scanBranch(p: var FormatParser, a: openArray[string], while true: case f[i] of ']': break - of '|': + of '|': inc i elsePart = i inc c @@ -172,7 +172,7 @@ proc scanSlice(p: var FormatParser, a: openarray[string]): tuple[x, y: int] = var slice = false var i = p.i var f = p.f - + if f[i] == '{': inc i else: raiseInvalidFormat("'{' expected") if f[i] == '.' and f[i+1] == '.': @@ -193,12 +193,12 @@ proc scanSlice(p: var FormatParser, a: openarray[string]): tuple[x, y: int] = if f[i] != '}': raiseInvalidFormat("'}' expected") inc i p.i = i - + proc scanDollar(p: var FormatParser, a: openarray[string], s: var string) = var i = p.i var f = p.f case f[i] - of '$': + of '$': emitChar p, s, '$' inc i of '*': @@ -232,7 +232,7 @@ proc scanDollar(p: var FormatParser, a: openarray[string], s: var string) = # $' '~{1..3} # insert space followed by 1..3 if not empty inc i - call: + call: let (x, y) = scanSlice(p, a) var L = 0 for j in x..y: inc L, a[j].len @@ -258,7 +258,7 @@ proc scanDollar(p: var FormatParser, a: openarray[string], s: var string) = of 'i': inc i callNoLineLenTracking: scanQuote(p, indent, true) - + call: let (x, y) = scanSlice(p, a) if maxLen < 1: emitStrLinear(p, s, indent) @@ -266,7 +266,7 @@ proc scanDollar(p: var FormatParser, a: openarray[string], s: var string) = emitStr p, s, a[x] for j in x+1..y: emitStr p, s, sep - if items >= maxLen: + if items >= maxLen: emitStrLinear p, s, indent items = 0 emitStr p, s, a[j] @@ -274,7 +274,7 @@ proc scanDollar(p: var FormatParser, a: openarray[string], s: var string) = of 'c': inc i callNoLineLenTracking: scanQuote(p, indent, true) - + call: let (x, y) = scanSlice(p, a) if p.lineLen + a[x].len > maxLen: emitStrLinear(p, s, indent) @@ -283,7 +283,7 @@ proc scanDollar(p: var FormatParser, a: openarray[string], s: var string) = emitStr p, s, sep if p.lineLen + a[j].len > maxLen: emitStrLinear(p, s, indent) emitStr p, s, a[j] - + else: raiseInvalidFormat("unit 'c' (chars) or 'i' (items) expected") break StringJoin @@ -294,7 +294,7 @@ proc scanDollar(p: var FormatParser, a: openarray[string], s: var string) = emitStr p, s, sep emitStr p, s, a[j] else: - call: + call: var x = getFormatArg(p, a) emitStr p, s, a[x] p.i = i @@ -375,38 +375,38 @@ when isMainModule: doAssert "$1($', '{2..})" % ["f", "a", "b"] == "f(a, b)" doAssert "$[$1($', '{2..})|''''|fg'$3']1" % ["7", "a", "b"] == "fg$3" - + doAssert "$[$#($', '{#..})|''''|$3]1" % ["0", "a", "b"] == "0(a, b)" doAssert "$' '~{..}" % "" == "" doAssert "$' '~{..}" % "P0" == " P0" doAssert "${$1}" % "1" == "1" doAssert "${$$-1} $$1" % "1" == "1 $1" - + doAssert "$#($', '10c'\n '{#..})" % ["doAssert", "longishA", "longish"] == """doAssert( - longishA, + longishA, longish)""" - + assert "type MyEnum* = enum\n $', '2i'\n '{..}" % ["fieldA", "fieldB", "FiledClkad", "fieldD", "fieldE", "longishFieldName"] == strutils.unindent """ type MyEnum* = enum - fieldA, fieldB, - FiledClkad, fieldD, + fieldA, fieldB, + FiledClkad, fieldD, fieldE, longishFieldName""" - + doAssert subex"$1($', '{2..})" % ["f", "a", "b", "c"] == "f(a, b, c)" - + doAssert subex"$1 $[files|file|files]{1} copied" % ["1"] == "1 file copied" - + doAssert subex"$['''|'|''''|']']#" % "0" == "'|" - + assert subex("type\n Enum = enum\n $', '40c'\n '{..}") % [ "fieldNameA", "fieldNameB", "fieldNameC", "fieldNameD"] == strutils.unindent """ type Enum = enum - fieldNameA, fieldNameB, fieldNameC, + fieldNameA, fieldNameB, fieldNameC, fieldNameD""" - - + + diff --git a/lib/pure/unicode.nim b/lib/pure/unicode.nim index 5e20db32b..396957f6c 100644 --- a/lib/pure/unicode.nim +++ b/lib/pure/unicode.nim @@ -1285,7 +1285,7 @@ proc cmpRunesIgnoreCase*(a, b: string): int {.rtl, extern: "nuc$1", procvar.} = result = a.len - b.len proc reversed*(s: string): string = - ## Returns the reverse of ``s``, interpreting it as Unicode characters. + ## Returns the reverse of ``s``, interpreting it as Unicode characters. ## Unicode combining characters are correctly interpreted as well: ## ## .. code-block:: nim diff --git a/lib/pure/unidecode/unidecode.nim b/lib/pure/unidecode/unidecode.nim index a83b9be0f..9d8843f06 100644 --- a/lib/pure/unidecode/unidecode.nim +++ b/lib/pure/unidecode/unidecode.nim @@ -7,19 +7,19 @@ # distribution, for details about the copyright. # -## This module is based on Python's Unidecode module by Tomaz Solc, -## which in turn is based on the ``Text::Unidecode`` Perl module by -## Sean M. Burke +## This module is based on Python's Unidecode module by Tomaz Solc, +## which in turn is based on the ``Text::Unidecode`` Perl module by +## Sean M. Burke ## (http://search.cpan.org/~sburke/Text-Unidecode-0.04/lib/Text/Unidecode.pm ). ## ## It provides a single proc that does Unicode to ASCII transliterations: ## It finds the sequence of ASCII characters that is the closest approximation ## to the Unicode string. ## -## For example, the closest to string "Äußerst" in ASCII is "Ausserst". Some -## information is lost in this transformation, of course, since several Unicode +## For example, the closest to string "Äußerst" in ASCII is "Ausserst". Some +## information is lost in this transformation, of course, since several Unicode ## strings can be transformed in the same ASCII representation. So this is a -## strictly one-way transformation. However a human reader will probably +## strictly one-way transformation. However a human reader will probably ## still be able to guess what original string was meant from the context. ## ## This module needs the data file "unidecode.dat" to work: You can either @@ -31,7 +31,7 @@ import unicode when defined(embedUnidecodeTable): import strutils - + const translationTable = splitLines(slurp"unidecode/unidecode.dat") else: # shared is fine for threading: @@ -49,12 +49,12 @@ proc loadUnidecodeTable*(datafile = "unidecode.dat") = translationTable[i] = line.string inc(i) -proc unidecode*(s: string): string = +proc unidecode*(s: string): string = ## Finds the sequence of ASCII characters that is the closest approximation ## to the UTF-8 string `s`. ## - ## Example: - ## + ## Example: + ## ## ..code-block:: nim ## ## unidecode("\x53\x17\x4E\xB0") @@ -63,7 +63,7 @@ proc unidecode*(s: string): string = ## assert(not isNil(translationTable)) result = "" - for r in runes(s): + for r in runes(s): var c = int(r) if c <=% 127: add(result, chr(c)) elif c <% translationTable.len: add(result, translationTable[c-128]) diff --git a/lib/pure/xmldomparser.nim b/lib/pure/xmldomparser.nim index 050362435..7c7f7b99c 100644 --- a/lib/pure/xmldomparser.nim +++ b/lib/pure/xmldomparser.nim @@ -30,19 +30,19 @@ proc getNS(prefix: string): string = if ":" in key: if key.split(':')[1] == prefix: return value - + if key == "xmlns": defaultNS.add(value) - + # Don't return the default namespaces # in the loop, because then they would have a precedence # over normal namespaces if defaultNS.len() > 0: return defaultNS[0] # Return the first found default namespace # if none are specified for this prefix - + return "" - + proc parseText(x: var XmlParser, doc: var PDocument): PText = result = doc.createTextNode(x.charData()) @@ -58,19 +58,19 @@ proc parseElement(x: var XmlParser, doc: var PDocument): PElement = n.appendChild(parseElement(x, doc)) else: n = doc.createElementNS("", x.elementName) - + of xmlElementEnd: if x.elementName == n.nodeName: # n.normalize() # Remove any whitespace etc. - + var ns: string if x.elementName.contains(':'): ns = getNS(x.elementName.split(':')[0]) else: ns = getNS("") - + n.namespaceURI = ns - + # Remove any namespaces this element declared var count = 0 # Variable which keeps the index # We need to edit it.. @@ -82,15 +82,15 @@ proc parseElement(x: var XmlParser, doc: var PDocument): PElement = return n else: #The wrong element is ended - raise newException(EMismatchedTag, "Mismatched tag at line " & + raise newException(EMismatchedTag, "Mismatched tag at line " & $x.getLine() & " column " & $x.getColumn) - + of xmlCharData: n.appendChild(parseText(x, doc)) of xmlAttribute: if x.attrKey == "xmlns" or x.attrKey.startsWith("xmlns:"): nsList.add((x.attrKey, x.attrValue, n)) - + if x.attrKey.contains(':'): var ns = getNS(x.attrKey) n.setAttributeNS(ns, x.attrKey, x.attrValue) @@ -103,7 +103,7 @@ proc parseElement(x: var XmlParser, doc: var PDocument): PElement = n.appendChild(doc.createComment(x.charData())) of xmlPI: n.appendChild(doc.createProcessingInstruction(x.piName(), x.piRest())) - + of xmlWhitespace, xmlElementClose, xmlEntity, xmlSpecial: discard " Unused \'events\'" @@ -111,19 +111,19 @@ proc parseElement(x: var XmlParser, doc: var PDocument): PElement = raise newException(EParserError, "Unexpected XML Parser event") x.next() - raise newException(EMismatchedTag, + raise newException(EMismatchedTag, "Mismatched tag at line " & $x.getLine() & " column " & $x.getColumn) proc loadXMLStream*(stream: Stream): PDocument = - ## Loads and parses XML from a stream specified by ``stream``, and returns + ## Loads and parses XML from a stream specified by ``stream``, and returns ## a ``PDocument`` var x: XmlParser open(x, stream, nil, {reportComments}) - + var xmlDoc: PDocument var dom: PDOMImplementation = getDOM() - + while true: x.next() case x.kind() @@ -140,16 +140,16 @@ proc loadXMLStream*(stream: Stream): PDocument = return xmlDoc proc loadXML*(xml: string): PDocument = - ## Loads and parses XML from a string specified by ``xml``, and returns + ## Loads and parses XML from a string specified by ``xml``, and returns ## a ``PDocument`` var s = newStringStream(xml) return loadXMLStream(s) - - + + proc loadXMLFile*(path: string): PDocument = - ## Loads and parses XML from a file specified by ``path``, and returns + ## Loads and parses XML from a file specified by ``path``, and returns ## a ``PDocument`` - + var s = newFileStream(path, fmRead) if s == nil: raise newException(IOError, "Unable to read file " & path) return loadXMLStream(s) @@ -164,5 +164,5 @@ when not defined(testing) and isMainModule: if i.namespaceURI != nil: echo(i.nodeName, "=", i.namespaceURI) - + echo($xml) diff --git a/lib/pure/xmlparser.nim b/lib/pure/xmlparser.nim index 840cae734..56b122000 100644 --- a/lib/pure/xmlparser.nim +++ b/lib/pure/xmlparser.nim @@ -18,24 +18,24 @@ type {.deprecated: [EInvalidXml: XmlError].} -proc raiseInvalidXml(errors: seq[string]) = +proc raiseInvalidXml(errors: seq[string]) = var e: ref XmlError new(e) e.msg = errors[0] e.errors = errors raise e -proc addNode(father, son: XmlNode) = +proc addNode(father, son: XmlNode) = if son != nil: add(father, son) proc parse(x: var XmlParser, errors: var seq[string]): XmlNode -proc untilElementEnd(x: var XmlParser, result: XmlNode, +proc untilElementEnd(x: var XmlParser, result: XmlNode, errors: var seq[string]) = while true: case x.kind - of xmlElementEnd: - if x.elementName == result.tag: + of xmlElementEnd: + if x.elementName == result.tag: next(x) else: errors.add(errorMsg(x, "</" & result.tag & "> expected")) @@ -49,7 +49,7 @@ proc untilElementEnd(x: var XmlParser, result: XmlNode, proc parse(x: var XmlParser, errors: var seq[string]): XmlNode = case x.kind - of xmlComment: + of xmlComment: result = newComment(x.charData) next(x) of xmlCharData, xmlWhitespace: @@ -67,11 +67,11 @@ proc parse(x: var XmlParser, errors: var seq[string]): XmlNode = untilElementEnd(x, result, errors) of xmlElementEnd: errors.add(errorMsg(x, "unexpected ending tag: " & x.elementName)) - of xmlElementOpen: + of xmlElementOpen: result = newElement(x.elementName) next(x) result.attrs = newStringTable() - while true: + while true: case x.kind of xmlAttribute: result.attrs[x.attrKey] = x.attrValue @@ -91,7 +91,7 @@ proc parse(x: var XmlParser, errors: var seq[string]): XmlNode = of xmlAttribute, xmlElementClose: errors.add(errorMsg(x, "<some_tag> expected")) next(x) - of xmlCData: + of xmlCData: result = newCData(x.charData) next(x) of xmlEntity: @@ -100,8 +100,8 @@ proc parse(x: var XmlParser, errors: var seq[string]): XmlNode = next(x) of xmlEof: discard -proc parseXml*(s: Stream, filename: string, - errors: var seq[string]): XmlNode = +proc parseXml*(s: Stream, filename: string, + errors: var seq[string]): XmlNode = ## parses the XML from stream `s` and returns a ``PXmlNode``. Every ## occurred parsing error is added to the `errors` sequence. var x: XmlParser @@ -109,7 +109,7 @@ proc parseXml*(s: Stream, filename: string, while true: x.next() case x.kind - of xmlElementOpen, xmlElementStart: + of xmlElementOpen, xmlElementStart: result = parse(x, errors) break of xmlComment, xmlWhitespace, xmlSpecial, xmlPI: discard # just skip it @@ -120,7 +120,7 @@ proc parseXml*(s: Stream, filename: string, break close(x) -proc parseXml*(s: Stream): XmlNode = +proc parseXml*(s: Stream): XmlNode = ## parses the XTML from stream `s` and returns a ``PXmlNode``. All parsing ## errors are turned into an ``EInvalidXML`` exception. var errors: seq[string] = @[] @@ -128,7 +128,7 @@ proc parseXml*(s: Stream): XmlNode = if errors.len > 0: raiseInvalidXml(errors) proc loadXml*(path: string, errors: var seq[string]): XmlNode = - ## Loads and parses XML from file specified by ``path``, and returns + ## Loads and parses XML from file specified by ``path``, and returns ## a ``PXmlNode``. Every occurred parsing error is added to the `errors` ## sequence. var s = newFileStream(path, fmRead) @@ -136,9 +136,9 @@ proc loadXml*(path: string, errors: var seq[string]): XmlNode = result = parseXml(s, path, errors) proc loadXml*(path: string): XmlNode = - ## Loads and parses XML from file specified by ``path``, and returns + ## Loads and parses XML from file specified by ``path``, and returns ## a ``PXmlNode``. All parsing errors are turned into an ``EInvalidXML`` - ## exception. + ## exception. var errors: seq[string] = @[] result = loadXml(path, errors) if errors.len > 0: raiseInvalidXml(errors) @@ -146,14 +146,14 @@ proc loadXml*(path: string): XmlNode = when not defined(testing) and isMainModule: import os - var errors: seq[string] = @[] + var errors: seq[string] = @[] var x = loadXml(paramStr(1), errors) for e in items(errors): echo e - + var f: File if open(f, "xmltest.txt", fmWrite): f.write($x) f.close() else: quit("cannot write test.txt") - + |