diff options
Diffstat (limited to 'lib/pure')
-rw-r--r-- | lib/pure/algorithm.nim | 14 | ||||
-rw-r--r-- | lib/pure/asyncdispatch.nim | 2 | ||||
-rw-r--r-- | lib/pure/httpclient.nim | 16 | ||||
-rw-r--r-- | lib/pure/json.nim | 63 | ||||
-rw-r--r-- | lib/pure/math.nim | 3 | ||||
-rw-r--r-- | lib/pure/net.nim | 10 | ||||
-rw-r--r-- | lib/pure/os.nim | 4 | ||||
-rw-r--r-- | lib/pure/osproc.nim | 26 | ||||
-rw-r--r-- | lib/pure/rawsockets.nim | 4 | ||||
-rw-r--r-- | lib/pure/scgi.nim | 2 | ||||
-rw-r--r-- | lib/pure/sexp.nim | 697 | ||||
-rw-r--r-- | lib/pure/sockets.nim | 18 | ||||
-rw-r--r-- | lib/pure/times.nim | 17 |
13 files changed, 815 insertions, 61 deletions
diff --git a/lib/pure/algorithm.nim b/lib/pure/algorithm.nim index f7ccb9234..46d049f14 100644 --- a/lib/pure/algorithm.nim +++ b/lib/pure/algorithm.nim @@ -24,6 +24,17 @@ proc `*`*(x: int, order: SortOrder): int {.inline.} = var y = order.ord - 1 result = (x xor y) - y +proc fill*[T](a: var openArray[T], first, last: Natural, value: T) = + ## fills the array ``a[first..last]`` with `value`. + var x = first + while x <= last: + a[x] = value + inc(x) + +proc fill*[T](a: var openArray[T], value: T) = + ## fills the array `a` with `value`. + fill(a, 0, a.high, value) + proc reverse*[T](a: var openArray[T], first, last: Natural) = ## reverses the array ``a[first..last]``. var x = first @@ -210,8 +221,7 @@ template sortedByIt*(seq1, op: expr): expr = ## p2: Person = (name: "p2", age: 20) ## p3: Person = (name: "p3", age: 30) ## p4: Person = (name: "p4", age: 30) - ## - ## people = @[p1,p2,p4,p3] + ## people = @[p1,p2,p4,p3] ## ## echo people.sortedByIt(it.name) ## diff --git a/lib/pure/asyncdispatch.nim b/lib/pure/asyncdispatch.nim index a4d7a1632..8849a8be3 100644 --- a/lib/pure/asyncdispatch.nim +++ b/lib/pure/asyncdispatch.nim @@ -1154,7 +1154,7 @@ else: proc sleepAsync*(ms: int): Future[void] = ## Suspends the execution of the current async procedure for the next - ## ``ms`` miliseconds. + ## ``ms`` milliseconds. var retFuture = newFuture[void]("sleepAsync") let p = getGlobalDispatcher() p.timers.add((epochTime() + (ms / 1000), retFuture)) diff --git a/lib/pure/httpclient.nim b/lib/pure/httpclient.nim index 9c27ecdab..e083d44ea 100644 --- a/lib/pure/httpclient.nim +++ b/lib/pure/httpclient.nim @@ -64,7 +64,7 @@ ## ======== ## Currently all functions support an optional timeout, by default the timeout is set to ## `-1` which means that the function will never time out. The timeout is -## measured in miliseconds, once it is set any call on a socket which may +## measured in milliseconds, once it is set any call on a socket which may ## block will be susceptible to this timeout, however please remember that the ## function as a whole can take longer than the specified timeout, only ## individual internal calls on the socket are affected. In practice this means @@ -386,7 +386,7 @@ proc request*(url: string, httpMethod: string, extraHeaders = "", ## | Requests ``url`` with the custom method string specified by the ## | ``httpMethod`` parameter. ## | Extra headers can be specified and must be separated by ``\c\L`` - ## | An optional timeout can be specified in miliseconds, if reading from the + ## | An optional timeout can be specified in milliseconds, if reading from the ## server takes longer than specified an ETimeout exception will be raised. var r = if proxy == nil: parseUri(url) else: proxy.url var headers = substr(httpMethod, len("http")) @@ -440,7 +440,7 @@ proc request*(url: string, httpMethod = httpGET, extraHeaders = "", userAgent = defUserAgent, proxy: Proxy = nil): Response = ## | Requests ``url`` with the specified ``httpMethod``. ## | Extra headers can be specified and must be separated by ``\c\L`` - ## | An optional timeout can be specified in miliseconds, if reading from the + ## | An optional timeout can be specified in milliseconds, if reading from the ## server takes longer than specified an ETimeout exception will be raised. result = request(url, $httpMethod, extraHeaders, body, sslContext, timeout, userAgent, proxy) @@ -467,7 +467,7 @@ proc get*(url: string, extraHeaders = "", maxRedirects = 5, ## | GETs the ``url`` and returns a ``Response`` object ## | This proc also handles redirection ## | Extra headers can be specified and must be separated by ``\c\L``. - ## | An optional timeout can be specified in miliseconds, if reading from the + ## | An optional timeout can be specified in milliseconds, if reading from the ## server takes longer than specified an ETimeout exception will be raised. result = request(url, httpGET, extraHeaders, "", sslContext, timeout, userAgent, proxy) @@ -486,7 +486,7 @@ proc getContent*(url: string, extraHeaders = "", maxRedirects = 5, ## | GETs the body and returns it as a string. ## | Raises exceptions for the status codes ``4xx`` and ``5xx`` ## | Extra headers can be specified and must be separated by ``\c\L``. - ## | An optional timeout can be specified in miliseconds, if reading from the + ## | An optional timeout can be specified in milliseconds, if reading from the ## server takes longer than specified an ETimeout exception will be raised. var r = get(url, extraHeaders, maxRedirects, sslContext, timeout, userAgent, proxy) @@ -505,7 +505,7 @@ proc post*(url: string, extraHeaders = "", body = "", ## | This proc adds the necessary Content-Length header. ## | This proc also handles redirection. ## | Extra headers can be specified and must be separated by ``\c\L``. - ## | An optional timeout can be specified in miliseconds, if reading from the + ## | An optional timeout can be specified in milliseconds, if reading from the ## server takes longer than specified an ETimeout exception will be raised. ## | The optional ``multipart`` parameter can be used to create ## ``multipart/form-data`` POSTs comfortably. @@ -542,7 +542,7 @@ proc postContent*(url: string, extraHeaders = "", body = "", ## | POSTs ``body`` to ``url`` and returns the response's body as a string ## | Raises exceptions for the status codes ``4xx`` and ``5xx`` ## | Extra headers can be specified and must be separated by ``\c\L``. - ## | An optional timeout can be specified in miliseconds, if reading from the + ## | An optional timeout can be specified in milliseconds, if reading from the ## server takes longer than specified an ETimeout exception will be raised. ## | The optional ``multipart`` parameter can be used to create ## ``multipart/form-data`` POSTs comfortably. @@ -558,7 +558,7 @@ proc downloadFile*(url: string, outputFilename: string, timeout = -1, userAgent = defUserAgent, proxy: Proxy = nil) = ## | Downloads ``url`` and saves it to ``outputFilename`` - ## | An optional timeout can be specified in miliseconds, if reading from the + ## | An optional timeout can be specified in milliseconds, if reading from the ## server takes longer than specified an ETimeout exception will be raised. var f: File if open(f, outputFilename, fmWrite): diff --git a/lib/pure/json.nim b/lib/pure/json.nim index 5d824d6f8..7d22e897b 100644 --- a/lib/pure/json.nim +++ b/lib/pure/json.nim @@ -761,7 +761,7 @@ proc len*(n: JsonNode): int = of JObject: result = n.fields.len else: discard -proc `[]`*(node: JsonNode, name: string): JsonNode = +proc `[]`*(node: JsonNode, name: string): JsonNode {.inline.} = ## Gets a field from a `JObject`, which must not be nil. ## If the value at `name` does not exist, returns nil assert(not isNil(node)) @@ -771,7 +771,7 @@ proc `[]`*(node: JsonNode, name: string): JsonNode = return item return nil -proc `[]`*(node: JsonNode, index: int): JsonNode = +proc `[]`*(node: JsonNode, index: int): JsonNode {.inline.} = ## Gets the node at `index` in an Array. Result is undefined if `index` ## is out of bounds assert(not isNil(node)) @@ -799,7 +799,7 @@ proc add*(obj: JsonNode, key: string, val: JsonNode) = assert obj.kind == JObject obj.fields.add((key, val)) -proc `[]=`*(obj: JsonNode, key: string, val: JsonNode) = +proc `[]=`*(obj: JsonNode, key: string, val: JsonNode) {.inline.} = ## Sets a field from a `JObject`. Performs a check for duplicate keys. assert(obj.kind == JObject) for i in 0..obj.fields.len-1: @@ -815,7 +815,7 @@ proc `{}`*(node: JsonNode, keys: varargs[string]): JsonNode = result = node for key in keys: if isNil(result) or result.kind!=JObject: - return nil + return nil result=result[key] proc `{}=`*(node: JsonNode, keys: varargs[string], value: JsonNode) = @@ -949,10 +949,49 @@ proc pretty*(node: JsonNode, indent = 2): string = result = "" toPretty(result, node, indent) +proc toUgly*(result: var string, node: JsonNode) = + ## Converts `node` to its JSON Representation, without + ## regard for human readability. Meant to improve ``$`` string + ## conversion performance. + ## + ## This provides higher efficiency than the ``toPretty`` procedure as it + ## does **not** attempt to format the resulting JSON to make it human readable. + var comma = false + case node.kind: + of JArray: + result.add "[" + for child in node.elems: + if comma: result.add "," + else: comma = true + result.toUgly child + result.add "]" + of JObject: + result.add "{" + for key, value in items(node.fields): + if comma: result.add "," + else: comma = true + result.add "\"" + result.add key.escapeJson() + result.add "\":" + result.toUgly value + result.add "}" + of JString: + result.add "\"" + result.add node.str.escapeJson() + result.add "\"" + of JInt: + result.add($node.num) + of JFloat: + result.add($node.fnum) + of JBool: + result.add(if node.bval: "true" else: "false") + of JNull: + result.add "null" + proc `$`*(node: JsonNode): string = ## Converts `node` to its JSON Representation on one line. - result = "" - toPretty(result, node, 0, false) + result = newStringOfCap(node.len shl 1) + toUgly(result, node) iterator items*(node: JsonNode): JsonNode = ## Iterator for the items of `node`. `node` has to be a JArray. @@ -1153,7 +1192,7 @@ when false: when isMainModule: #var node = parse("{ \"test\": null }") #echo(node.existsKey("test56")) - + var parsed = parseFile("tests/testdata/jsontest.json") var parsed2 = parseFile("tests/testdata/jsontest2.json") @@ -1192,17 +1231,17 @@ when isMainModule: except: assert(false, "EInvalidIndex thrown for valid index") - assert(testJson{"b"}.str=="asd", "Couldn't fetch a singly nested key with {}") - assert(isNil(testJson{"nonexistent"}), "Non-existent keys should return nil") + assert(testJson{"b"}.str=="asd", "Couldn't fetch a singly nested key with {}") + assert(isNil(testJson{"nonexistent"}), "Non-existent keys should return nil") assert(parsed2{"repository", "description"}.str=="IRC Library for Haskell", "Couldn't fetch via multiply nested key using {}") assert(isNil(testJson{"a", "b"}), "Indexing through a list should return nil") assert(isNil(testJson{"a", "b"}), "Indexing through a list should return nil") assert(testJson{"a"}==parseJson"[1, 2, 3, 4]", "Didn't return a non-JObject when there was one to be found") assert(isNil(parseJson("[1, 2, 3]"){"foo"}), "Indexing directly into a list should return nil") - + # Generator: var j = %* [{"name": "John", "age": 30}, {"name": "Susan", "age": 31}] - assert j == %[%{"name": %"John", "age": %30}, %{"name": %"Susan", "age": %31}] + assert j == %[%{"name": %"John", "age": %30}, %{"name": %"Susan", "age": %31}] var j2 = %* [ @@ -1230,7 +1269,7 @@ when isMainModule: } ] assert j3 == %[%{"name": %"John", "age": %30}, %{"name": %"Susan", "age": %31}] - + when not defined(testing): discard """ while true: diff --git a/lib/pure/math.nim b/lib/pure/math.nim index cb58ea39b..a9e9010f6 100644 --- a/lib/pure/math.nim +++ b/lib/pure/math.nim @@ -352,6 +352,9 @@ proc `^`*[T](x, y: T): T = proc gcd*[T](x, y: T): T = ## Computes the greatest common divisor of ``x`` and ``y``. + ## Note that for floats, the result cannot always be interpreted as + ## "greatest decimal `z` such that ``z*N == x and z*M == y`` + ## where N and M are positive integers." var (x,y) = (x,y) while y != 0: x = x mod y diff --git a/lib/pure/net.nim b/lib/pure/net.nim index ffbc6e320..cf37c271e 100644 --- a/lib/pure/net.nim +++ b/lib/pure/net.nim @@ -704,7 +704,7 @@ 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 miliseconds. + ## overload with a ``timeout`` parameter in milliseconds. var waited = 0.0 # number of seconds already waited var read = 0 @@ -729,7 +729,7 @@ proc recv*(socket: Socket, data: var string, size: int, timeout = -1, ## This function will throw an EOS exception when an error occurs. A value ## lower than 0 is never returned. ## - ## A timeout may be specified in miliseconds, if enough data is not received + ## A timeout may be specified in milliseconds, if enough data is not received ## within the time specified an ETimeout exception will be raised. ## ## **Note**: ``data`` must be initialised. @@ -777,7 +777,7 @@ proc readLine*(socket: Socket, line: var TaintedString, timeout = -1, ## ## An EOS exception will be raised in the case of a socket error. ## - ## A timeout can be specified in miliseconds, if data is not received within + ## A timeout can be specified in milliseconds, if data is not received within ## the specified time an ETimeout exception will be raised. ## ## **Warning**: Only the ``SafeDisconn`` flag is currently supported. @@ -844,7 +844,7 @@ proc recvFrom*(socket: Socket, data: var string, length: int, proc skip*(socket: Socket, size: int, timeout = -1) = ## Skips ``size`` amount of bytes. ## - ## An optional timeout can be specified in miliseconds, if skipping the + ## An optional timeout can be specified in milliseconds, if skipping the ## bytes takes longer than specified an ETimeout exception will be raised. ## ## Returns the number of skipped bytes. @@ -967,7 +967,7 @@ proc connect*(socket: Socket, address: string, port = Port(0), timeout: int, af: Domain = AF_INET) {.tags: [ReadIOEffect, WriteIOEffect].} = ## Connects to server as specified by ``address`` on port specified by ``port``. ## - ## The ``timeout`` paremeter specifies the time in miliseconds to allow for + ## The ``timeout`` paremeter specifies the time in milliseconds to allow for ## the connection to the server to be made. socket.fd.setBlocking(false) diff --git a/lib/pure/os.nim b/lib/pure/os.nim index f53abe81d..3a5bcbfa1 100644 --- a/lib/pure/os.nim +++ b/lib/pure/os.nim @@ -2033,10 +2033,10 @@ proc getFileInfo*(path: string, followSymlink = true): FileInfo = else: var rawInfo: TStat if followSymlink: - if lstat(path, rawInfo) < 0'i32: + if stat(path, rawInfo) < 0'i32: raiseOSError(osLastError()) else: - if stat(path, rawInfo) < 0'i32: + if lstat(path, rawInfo) < 0'i32: raiseOSError(osLastError()) rawToFormalFileInfo(rawInfo, result) diff --git a/lib/pure/osproc.nim b/lib/pure/osproc.nim index dce0673ba..dc6f21174 100644 --- a/lib/pure/osproc.nim +++ b/lib/pure/osproc.nim @@ -195,43 +195,43 @@ proc peekExitCode*(p: Process): int {.tags: [].} proc inputStream*(p: Process): Stream {.rtl, extern: "nosp$1", tags: [].} ## returns ``p``'s input stream for writing to. ## - ## **Warning**: The returned `PStream` should not be closed manually as it - ## is closed when closing the PProcess ``p``. + ## **Warning**: The returned `Stream` should not be closed manually as it + ## is closed when closing the Process ``p``. proc outputStream*(p: Process): Stream {.rtl, extern: "nosp$1", tags: [].} ## returns ``p``'s output stream for reading from. ## - ## **Warning**: The returned `PStream` should not be closed manually as it - ## is closed when closing the PProcess ``p``. + ## **Warning**: The returned `Stream` should not be closed manually as it + ## is closed when closing the Process ``p``. proc errorStream*(p: Process): Stream {.rtl, extern: "nosp$1", tags: [].} ## returns ``p``'s error stream for reading from. ## - ## **Warning**: The returned `PStream` should not be closed manually as it - ## is closed when closing the PProcess ``p``. + ## **Warning**: The returned `Stream` should not be closed manually as it + ## is closed when closing the Process ``p``. proc inputHandle*(p: Process): FileHandle {.rtl, extern: "nosp$1", tags: [].} = ## returns ``p``'s input file handle for writing to. ## - ## **Warning**: The returned `TFileHandle` should not be closed manually as - ## it is closed when closing the PProcess ``p``. + ## **Warning**: The returned `FileHandle` should not be closed manually as + ## it is closed when closing the Process ``p``. result = p.inHandle proc outputHandle*(p: Process): FileHandle {.rtl, extern: "nosp$1", tags: [].} = ## returns ``p``'s output file handle for reading from. ## - ## **Warning**: The returned `TFileHandle` should not be closed manually as - ## it is closed when closing the PProcess ``p``. + ## **Warning**: The returned `FileHandle` should not be closed manually as + ## it is closed when closing the Process ``p``. result = p.outHandle proc errorHandle*(p: Process): FileHandle {.rtl, extern: "nosp$1", tags: [].} = ## returns ``p``'s error file handle for reading from. ## - ## **Warning**: The returned `TFileHandle` should not be closed manually as - ## it is closed when closing the PProcess ``p``. + ## **Warning**: The returned `FileHandle` should not be closed manually as + ## it is closed when closing the Process ``p``. result = p.errHandle proc countProcessors*(): int {.rtl, extern: "nosp$1".} = @@ -303,7 +303,7 @@ proc execProcesses*(cmds: openArray[string], close(p) proc select*(readfds: var seq[Process], timeout = 500): int - ## `select` with a sensible Nim interface. `timeout` is in miliseconds. + ## `select` with a sensible Nim interface. `timeout` is in milliseconds. ## Specify -1 for no timeout. Returns the number of processes that are ## ready to read from. The processes that are ready to be read from are ## removed from `readfds`. diff --git a/lib/pure/rawsockets.nim b/lib/pure/rawsockets.nim index a30c23ada..d08c5b769 100644 --- a/lib/pure/rawsockets.nim +++ b/lib/pure/rawsockets.nim @@ -392,7 +392,7 @@ proc select*(readfds: var seq[SocketHandle], timeout = 500): int = ## 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. - ## ``Timeout`` is in miliseconds and -1 can be specified for no timeout. + ## ``Timeout`` is in milliseconds and -1 can be specified for no timeout. ## ## A socket is removed from the specific ``seq`` when it has data waiting to ## be read/written to or has errors (``exceptfds``). @@ -416,7 +416,7 @@ proc selectWrite*(writefds: var seq[SocketHandle], ## written to. The sockets which can be written to will also be removed ## from ``writefds``. ## - ## ``timeout`` is specified in miliseconds and ``-1`` can be specified for + ## ``timeout`` is specified in milliseconds and ``-1`` can be specified for ## an unlimited time. var tv {.noInit.}: Timeval = timeValFromMilliseconds(timeout) diff --git a/lib/pure/scgi.nim b/lib/pure/scgi.nim index f3e2b583c..3de422c87 100644 --- a/lib/pure/scgi.nim +++ b/lib/pure/scgi.nim @@ -126,7 +126,7 @@ proc close*(s: var ScgiState) = s.server.close() proc next*(s: var ScgiState, timeout: int = -1): bool = - ## proceed to the first/next request. Waits ``timeout`` miliseconds for a + ## 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. var rsocks = @[s.server] diff --git a/lib/pure/sexp.nim b/lib/pure/sexp.nim new file mode 100644 index 000000000..3c9fbc150 --- /dev/null +++ b/lib/pure/sexp.nim @@ -0,0 +1,697 @@ +# +# +# Nim's Runtime Library +# (c) Copyright 2015 Andreas Rumpf, Dominik Picheta +# +# See the file "copying.txt", included in this +# distribution, for details about the copyright. +# + +import + hashes, strutils, lexbase, streams, unicode, macros + +type + SexpEventKind* = enum ## enumeration of all events that may occur when parsing + sexpError, ## an error occurred during parsing + sexpEof, ## end of file reached + sexpString, ## a string literal + sexpSymbol, ## a symbol + sexpInt, ## an integer literal + sexpFloat, ## a float literal + sexpNil, ## the value ``nil`` + sexpDot, ## the dot to separate car/cdr + sexpListStart, ## start of a list: the ``(`` token + sexpListEnd, ## end of a list: the ``)`` token + + TTokKind = enum # must be synchronized with SexpEventKind! + tkError, + tkEof, + tkString, + tkSymbol, + tkInt, + tkFloat, + tkNil, + tkDot, + tkParensLe, + tkParensRi + tkSpace + + SexpError* = enum ## enumeration that lists all errors that can occur + errNone, ## no error + errInvalidToken, ## invalid token + errParensRiExpected, ## ``)`` expected + errQuoteExpected, ## ``"`` expected + errEofExpected, ## EOF expected + + SexpParser* = object of BaseLexer ## the parser object. + a: string + tok: TTokKind + kind: SexpEventKind + err: SexpError + +const + errorMessages: array [SexpError, string] = [ + "no error", + "invalid token", + "')' expected", + "'\"' or \"'\" expected", + "EOF expected", + ] + tokToStr: array [TTokKind, string] = [ + "invalid token", + "EOF", + "string literal", + "symbol", + "int literal", + "float literal", + "nil", + ".", + "(", ")", "space" + ] + +proc close*(my: var SexpParser) {.inline.} = + ## closes the parser `my` and its associated input stream. + lexbase.close(my) + +proc str*(my: SexpParser): string {.inline.} = + ## returns the character data for the events: ``sexpInt``, ``sexpFloat``, + ## ``sexpString`` + assert(my.kind in {sexpInt, sexpFloat, sexpString}) + result = my.a + +proc getInt*(my: SexpParser): BiggestInt {.inline.} = + ## returns the number for the event: ``sexpInt`` + assert(my.kind == sexpInt) + result = parseBiggestInt(my.a) + +proc getFloat*(my: SexpParser): float {.inline.} = + ## returns the number for the event: ``sexpFloat`` + assert(my.kind == sexpFloat) + result = parseFloat(my.a) + +proc kind*(my: SexpParser): SexpEventKind {.inline.} = + ## returns the current event type for the SEXP parser + result = my.kind + +proc getColumn*(my: SexpParser): int {.inline.} = + ## get the current column the parser has arrived at. + result = getColNumber(my, my.bufpos) + +proc getLine*(my: SexpParser): int {.inline.} = + ## get the current line the parser has arrived at. + result = my.lineNumber + +proc errorMsg*(my: SexpParser): string = + ## returns a helpful error message for the event ``sexpError`` + assert(my.kind == sexpError) + result = "($1, $2) Error: $3" % [$getLine(my), $getColumn(my), errorMessages[my.err]] + +proc errorMsgExpected*(my: SexpParser, e: string): string = + ## returns an error message "`e` expected" in the same format as the + ## other error messages + result = "($1, $2) Error: $3" % [$getLine(my), $getColumn(my), e & " expected"] + +proc handleHexChar(c: char, x: var int): bool = + result = true # Success + case c + of '0'..'9': x = (x shl 4) or (ord(c) - ord('0')) + of 'a'..'f': x = (x shl 4) or (ord(c) - ord('a') + 10) + of 'A'..'F': x = (x shl 4) or (ord(c) - ord('A') + 10) + else: result = false # error + +proc parseString(my: var SexpParser): TTokKind = + result = tkString + var pos = my.bufpos + 1 + var buf = my.buf + while true: + case buf[pos] + of '\0': + my.err = errQuoteExpected + result = tkError + break + of '"': + inc(pos) + break + of '\\': + case buf[pos+1] + of '\\', '"', '\'', '/': + add(my.a, buf[pos+1]) + inc(pos, 2) + of 'b': + add(my.a, '\b') + inc(pos, 2) + of 'f': + add(my.a, '\f') + inc(pos, 2) + of 'n': + add(my.a, '\L') + inc(pos, 2) + of 'r': + add(my.a, '\C') + inc(pos, 2) + of 't': + add(my.a, '\t') + inc(pos, 2) + of 'u': + inc(pos, 2) + var r: int + if handleHexChar(buf[pos], r): inc(pos) + if handleHexChar(buf[pos], r): inc(pos) + if handleHexChar(buf[pos], r): inc(pos) + if handleHexChar(buf[pos], r): inc(pos) + add(my.a, toUTF8(Rune(r))) + else: + # don't bother with the error + add(my.a, buf[pos]) + inc(pos) + of '\c': + pos = lexbase.handleCR(my, pos) + buf = my.buf + add(my.a, '\c') + of '\L': + pos = lexbase.handleLF(my, pos) + buf = my.buf + add(my.a, '\L') + else: + add(my.a, buf[pos]) + inc(pos) + my.bufpos = pos # store back + +proc parseNumber(my: var SexpParser) = + var pos = my.bufpos + var buf = my.buf + if buf[pos] == '-': + add(my.a, '-') + inc(pos) + if buf[pos] == '.': + add(my.a, "0.") + inc(pos) + else: + while buf[pos] in Digits: + add(my.a, buf[pos]) + inc(pos) + if buf[pos] == '.': + add(my.a, '.') + inc(pos) + # digits after the dot: + while buf[pos] in Digits: + add(my.a, buf[pos]) + inc(pos) + if buf[pos] in {'E', 'e'}: + add(my.a, buf[pos]) + inc(pos) + if buf[pos] in {'+', '-'}: + add(my.a, buf[pos]) + inc(pos) + while buf[pos] in Digits: + add(my.a, buf[pos]) + inc(pos) + my.bufpos = pos + +proc parseSymbol(my: var SexpParser) = + var pos = my.bufpos + var buf = my.buf + if buf[pos] in IdentStartChars: + while buf[pos] in IdentChars: + add(my.a, buf[pos]) + inc(pos) + my.bufpos = pos + +proc getTok(my: var SexpParser): TTokKind = + setLen(my.a, 0) + case my.buf[my.bufpos] + of '-', '0'..'9': # numbers that start with a . are not parsed + # correctly. + parseNumber(my) + if {'.', 'e', 'E'} in my.a: + result = tkFloat + else: + result = tkInt + of '"': #" # gotta fix nim-mode + result = parseString(my) + of '(': + inc(my.bufpos) + result = tkParensLe + of ')': + inc(my.bufpos) + result = tkParensRi + of '\0': + result = tkEof + of 'a'..'z', 'A'..'Z', '_': + parseSymbol(my) + if my.a == "nil": + result = tkNil + else: + result = tkSymbol + of ' ': + result = tkSpace + inc(my.bufpos) + of '.': + result = tkDot + inc(my.bufpos) + else: + inc(my.bufpos) + result = tkError + my.tok = result + +# ------------- higher level interface --------------------------------------- + +type + SexpNodeKind* = enum ## possible SEXP node types + SNil, + SInt, + SFloat, + SString, + SSymbol, + SList, + SCons + + SexpNode* = ref SexpNodeObj ## SEXP node + SexpNodeObj* {.acyclic.} = object + case kind*: SexpNodeKind + of SString: + str*: string + of SSymbol: + symbol*: string + of SInt: + num*: BiggestInt + of SFloat: + fnum*: float + of SList: + elems*: seq[SexpNode] + of SCons: + car: SexpNode + cdr: SexpNode + of SNil: + discard + + Cons = tuple[car: SexpNode, cdr: SexpNode] + + SexpParsingError* = object of ValueError ## is raised for a SEXP error + +proc raiseParseErr*(p: SexpParser, msg: string) {.noinline, noreturn.} = + ## raises an `ESexpParsingError` exception. + raise newException(SexpParsingError, errorMsgExpected(p, msg)) + +proc newSString*(s: string): SexpNode {.procvar.}= + ## Creates a new `SString SexpNode`. + new(result) + result.kind = SString + result.str = s + +proc newSStringMove(s: string): SexpNode = + new(result) + result.kind = SString + shallowCopy(result.str, s) + +proc newSInt*(n: BiggestInt): SexpNode {.procvar.} = + ## Creates a new `SInt SexpNode`. + new(result) + result.kind = SInt + result.num = n + +proc newSFloat*(n: float): SexpNode {.procvar.} = + ## Creates a new `SFloat SexpNode`. + new(result) + result.kind = SFloat + result.fnum = n + +proc newSNil*(): SexpNode {.procvar.} = + ## Creates a new `SNil SexpNode`. + new(result) + +proc newSCons*(car, cdr: SexpNode): SexpNode {.procvar.} = + ## Creates a new `SCons SexpNode` + new(result) + result.kind = SCons + result.car = car + result.cdr = cdr + +proc newSList*(): SexpNode {.procvar.} = + ## Creates a new `SList SexpNode` + new(result) + result.kind = SList + result.elems = @[] + +proc newSSymbol*(s: string): SexpNode {.procvar.} = + new(result) + result.kind = SSymbol + result.symbol = s + +proc newSSymbolMove(s: string): SexpNode = + new(result) + result.kind = SSymbol + shallowCopy(result.symbol, s) + +proc getStr*(n: SexpNode, default: string = ""): string = + ## Retrieves the string value of a `SString SexpNode`. + ## + ## Returns ``default`` if ``n`` is not a ``SString``. + if n.kind != SString: return default + else: return n.str + +proc getNum*(n: SexpNode, default: BiggestInt = 0): BiggestInt = + ## Retrieves the int value of a `SInt SexpNode`. + ## + ## Returns ``default`` if ``n`` is not a ``SInt``. + if n.kind != SInt: return default + else: return n.num + +proc getFNum*(n: SexpNode, default: float = 0.0): float = + ## Retrieves the float value of a `SFloat SexpNode`. + ## + ## Returns ``default`` if ``n`` is not a ``SFloat``. + if n.kind != SFloat: return default + else: return n.fnum + +proc getSymbol*(n: SexpNode, default: string = ""): string = + ## Retrieves the int value of a `SList SexpNode`. + ## + ## Returns ``default`` if ``n`` is not a ``SList``. + if n.kind != SSymbol: return default + else: return n.symbol + +proc getElems*(n: SexpNode, default: seq[SexpNode] = @[]): seq[SexpNode] = + ## Retrieves the int value of a `SList SexpNode`. + ## + ## Returns ``default`` if ``n`` is not a ``SList``. + if n.kind == SNil: return @[] + elif n.kind != SList: return default + else: return n.elems + +proc getCons*(n: SexpNode, defaults: Cons = (newSNil(), newSNil())): Cons = + ## Retrieves the cons value of a `SList SexpNode`. + ## + ## Returns ``default`` if ``n`` is not a ``SList``. + if n.kind == SCons: return (n.car, n.cdr) + elif n.kind == SList: return (n.elems[0], n.elems[1]) + else: return defaults + +proc sexp*(s: string): SexpNode = + ## Generic constructor for SEXP data. Creates a new `SString SexpNode`. + new(result) + result.kind = SString + result.str = s + +proc sexp*(n: BiggestInt): SexpNode = + ## Generic constructor for SEXP data. Creates a new `SInt SexpNode`. + new(result) + result.kind = SInt + result.num = n + +proc sexp*(n: float): SexpNode = + ## Generic constructor for SEXP data. Creates a new `SFloat SexpNode`. + new(result) + result.kind = SFloat + result.fnum = n + +proc sexp*(b: bool): SexpNode = + ## Generic constructor for SEXP data. Creates a new `SSymbol + ## SexpNode` with value t or `SNil SexpNode`. + new(result) + if b: + result.kind = SSymbol + result.symbol = "t" + else: + result.kind = SNil + +proc sexp*(elements: openArray[SexpNode]): SexpNode = + ## Generic constructor for SEXP data. Creates a new `SList SexpNode` + new(result) + result.kind = SList + newSeq(result.elems, elements.len) + for i, p in pairs(elements): result.elems[i] = p + +proc sexp*(s: SexpNode): SexpNode = + result = s + +proc toSexp(x: NimNode): NimNode {.compiletime.} = + case x.kind + of nnkBracket: + result = newNimNode(nnkBracket) + for i in 0 .. <x.len: + result.add(toSexp(x[i])) + + else: + result = x + + result = prefix(result, "sexp") + +macro convertSexp*(x: expr): expr = + ## Convert an expression to a SexpNode directly, without having to specify + ## `%` for every element. + result = toSexp(x) + +proc `==`* (a,b: SexpNode): bool = + ## Check two nodes for equality + if a.isNil: + if b.isNil: return true + return false + elif b.isNil or a.kind != b.kind: + return false + else: + return case a.kind + of SString: + a.str == b.str + of SInt: + a.num == b.num + of SFloat: + a.fnum == b.fnum + of SNil: + true + of SList: + a.elems == b.elems + of SSymbol: + a.symbol == b.symbol + of SCons: + a.car == b.car and a.cdr == b.cdr + +proc hash* (n:SexpNode): THash = + ## Compute the hash for a SEXP node + case n.kind + of SList: + result = hash(n.elems) + of SInt: + result = hash(n.num) + of SFloat: + result = hash(n.fnum) + of SString: + result = hash(n.str) + of SNil: + result = hash(0) + of SSymbol: + result = hash(n.symbol) + of SCons: + result = hash(n.car) !& hash(n.cdr) + +proc len*(n: SexpNode): int = + ## If `n` is a `SList`, it returns the number of elements. + ## If `n` is a `JObject`, it returns the number of pairs. + ## Else it returns 0. + case n.kind + of SList: result = n.elems.len + else: discard + +proc `[]`*(node: SexpNode, index: int): SexpNode = + ## Gets the node at `index` in a List. Result is undefined if `index` + ## is out of bounds + assert(not isNil(node)) + assert(node.kind == SList) + return node.elems[index] + +proc add*(father, child: SexpNode) = + ## Adds `child` to a SList node `father`. + assert father.kind == SList + father.elems.add(child) + +# ------------- pretty printing ---------------------------------------------- + +proc indent(s: var string, i: int) = + s.add(spaces(i)) + +proc newIndent(curr, indent: int, ml: bool): int = + if ml: return curr + indent + else: return indent + +proc nl(s: var string, ml: bool) = + if ml: s.add("\n") + +proc escapeJson*(s: string): string = + ## Converts a string `s` to its JSON representation. + result = newStringOfCap(s.len + s.len shr 3) + result.add("\"") + for x in runes(s): + var r = int(x) + if r >= 32 and r <= 127: + var c = chr(r) + case c + of '"': result.add("\\\"") #" # gotta fix nim-mode + of '\\': result.add("\\\\") + else: result.add(c) + else: + result.add("\\u") + result.add(toHex(r, 4)) + result.add("\"") + +proc copy*(p: SexpNode): SexpNode = + ## Performs a deep copy of `a`. + case p.kind + of SString: + result = newSString(p.str) + of SInt: + result = newSInt(p.num) + of SFloat: + result = newSFloat(p.fnum) + of SNil: + result = newSNil() + of SSymbol: + result = newSSymbol(p.symbol) + of SList: + result = newSList() + for i in items(p.elems): + result.elems.add(copy(i)) + of SCons: + result = newSCons(copy(p.car), copy(p.cdr)) + +proc toPretty(result: var string, node: SexpNode, indent = 2, ml = true, + lstArr = false, currIndent = 0) = + case node.kind + of SString: + if lstArr: result.indent(currIndent) + result.add(escapeJson(node.str)) + of SInt: + if lstArr: result.indent(currIndent) + result.add($node.num) + of SFloat: + if lstArr: result.indent(currIndent) + result.add($node.fnum) + of SNil: + if lstArr: result.indent(currIndent) + result.add("nil") + of SSymbol: + if lstArr: result.indent(currIndent) + result.add($node.symbol) + of SList: + if lstArr: result.indent(currIndent) + if len(node.elems) != 0: + result.add("(") + result.nl(ml) + for i in 0..len(node.elems)-1: + if i > 0: + result.add(" ") + result.nl(ml) # New Line + toPretty(result, node.elems[i], indent, ml, + true, newIndent(currIndent, indent, ml)) + result.nl(ml) + result.indent(currIndent) + result.add(")") + else: result.add("nil") + of SCons: + if lstArr: result.indent(currIndent) + result.add("(") + toPretty(result, node.car, indent, ml, + true, newIndent(currIndent, indent, ml)) + result.add(" . ") + toPretty(result, node.cdr, indent, ml, + true, newIndent(currIndent, indent, ml)) + result.add(")") + +proc pretty*(node: SexpNode, indent = 2): string = + ## Converts `node` to its Sexp Representation, with indentation and + ## on multiple lines. + result = "" + toPretty(result, node, indent) + +proc `$`*(node: SexpNode): string = + ## Converts `node` to its SEXP Representation on one line. + result = "" + toPretty(result, node, 0, false) + +iterator items*(node: SexpNode): SexpNode = + ## Iterator for the items of `node`. `node` has to be a SList. + assert node.kind == SList + for i in items(node.elems): + yield i + +iterator mitems*(node: var SexpNode): var SexpNode = + ## Iterator for the items of `node`. `node` has to be a SList. Items can be + ## modified. + assert node.kind == SList + for i in mitems(node.elems): + yield i + +proc eat(p: var SexpParser, tok: TTokKind) = + if p.tok == tok: discard getTok(p) + else: raiseParseErr(p, tokToStr[tok]) + +proc parseSexp(p: var SexpParser): SexpNode = + ## Parses SEXP from a SEXP Parser `p`. + case p.tok + of tkString: + # we capture 'p.a' here, so we need to give it a fresh buffer afterwards: + result = newSStringMove(p.a) + p.a = "" + discard getTok(p) + of tkInt: + result = newSInt(parseBiggestInt(p.a)) + discard getTok(p) + of tkFloat: + result = newSFloat(parseFloat(p.a)) + discard getTok(p) + of tkNil: + result = newSNil() + discard getTok(p) + of tkSymbol: + result = newSSymbolMove(p.a) + p.a = "" + discard getTok(p) + of tkParensLe: + result = newSList() + discard getTok(p) + while p.tok notin {tkParensRi, tkDot}: + result.add(parseSexp(p)) + if p.tok != tkSpace: break + discard getTok(p) + if p.tok == tkDot: + eat(p, tkDot) + eat(p, tkSpace) + result.add(parseSexp(p)) + result = newSCons(result[0], result[1]) + eat(p, tkParensRi) + of tkSpace, tkDot, tkError, tkParensRi, tkEof: + raiseParseErr(p, "(") + +proc open*(my: var SexpParser, input: Stream) = + ## initializes the parser with an input stream. + lexbase.open(my, input) + my.kind = sexpError + my.a = "" + +proc parseSexp*(s: Stream): SexpNode = + ## Parses from a buffer `s` into a `SexpNode`. + var p: SexpParser + p.open(s) + discard getTok(p) # read first token + result = p.parseSexp() + p.close() + +proc parseSexp*(buffer: string): SexpNode = + ## Parses Sexp from `buffer`. + result = parseSexp(newStringStream(buffer)) + +when isMainModule: + let testSexp = parseSexp("""(1 (98 2) nil (2) foobar "foo" 9.234)""") + assert(testSexp[0].getNum == 1) + assert(testSexp[1][0].getNum == 98) + assert(testSexp[2].getElems == @[]) + assert(testSexp[4].getSymbol == "foobar") + assert(testSexp[5].getStr == "foo") + + let alist = parseSexp("""((1 . 2) (2 . "foo"))""") + assert(alist[0].getCons.car.getNum == 1) + assert(alist[0].getCons.cdr.getNum == 2) + assert(alist[1].getCons.cdr.getStr == "foo") + + # Generator: + var j = convertSexp([true, false, "foobar", [1, 2, "baz"]]) + assert($j == """(t nil "foobar" (1 2 "baz"))""") diff --git a/lib/pure/sockets.nim b/lib/pure/sockets.nim index 3afb545c8..64e2cdcd3 100644 --- a/lib/pure/sockets.nim +++ b/lib/pure/sockets.nim @@ -986,7 +986,7 @@ proc select*(readfds, writefds, exceptfds: var seq[Socket], ## 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. - ## ``Timeout`` is in miliseconds and -1 can be specified for no timeout. + ## ``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``, @@ -1040,7 +1040,7 @@ proc selectWrite*(writefds: var seq[Socket], ## written to. The sockets which **cannot** be written to will also be removed ## from ``writefds``. ## - ## ``timeout`` is specified in miliseconds and ``-1`` can be specified for + ## ``timeout`` is specified in milliseconds and ``-1`` can be specified for ## an unlimited time. var tv {.noInit.}: Timeval = timeValFromMilliseconds(timeout) @@ -1174,7 +1174,7 @@ 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 miliseconds. + ## overload with a ``timeout`` parameter in milliseconds. var waited = 0.0 # number of seconds already waited var read = 0 @@ -1197,7 +1197,7 @@ proc recv*(socket: Socket, data: var string, size: int, timeout = -1): int = ## This function will throw an EOS exception when an error occurs. A value ## lower than 0 is never returned. ## - ## A timeout may be specified in miliseconds, if enough data is not received + ## A timeout may be specified in milliseconds, if enough data is not received ## within the time specified an ETimeout exception will be raised. ## ## **Note**: ``data`` must be initialised. @@ -1258,7 +1258,7 @@ proc recvLine*(socket: Socket, line: var TaintedString, timeout = -1): bool {. ## If the socket is disconnected, ``line`` will be set to ``""`` and ``True`` ## will be returned. ## - ## A timeout can be specified in miliseconds, if data is not received within + ## A timeout can be specified in milliseconds, if data is not received within ## the specified time an ETimeout exception will be raised. ## ## **Deprecated since version 0.9.2**: This function has been deprecated in @@ -1302,7 +1302,7 @@ proc readLine*(socket: Socket, line: var TaintedString, timeout = -1) {. ## ## An EOS exception will be raised in the case of a socket error. ## - ## A timeout can be specified in miliseconds, if data is not received within + ## 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 = @@ -1433,7 +1433,7 @@ proc recv*(socket: Socket): TaintedString {.tags: [ReadIOEffect], deprecated.} = proc recvTimeout*(socket: Socket, timeout: int): TaintedString {. tags: [ReadIOEffect], deprecated.} = ## overloaded variant to support a ``timeout`` parameter, the ``timeout`` - ## parameter specifies the amount of miliseconds to wait for data on the + ## parameter specifies the amount of milliseconds to wait for data on the ## socket. ## ## **Deprecated since version 0.9.2**: This function is not safe for use. @@ -1554,7 +1554,7 @@ proc skip*(socket: Socket) {.tags: [ReadIOEffect], deprecated.} = proc skip*(socket: Socket, size: int, timeout = -1) = ## Skips ``size`` amount of bytes. ## - ## An optional timeout can be specified in miliseconds, if skipping the + ## An optional timeout can be specified in milliseconds, if skipping the ## bytes takes longer than specified an ETimeout exception will be raised. ## ## Returns the number of skipped bytes. @@ -1708,7 +1708,7 @@ proc connect*(socket: Socket, address: string, port = Port(0), timeout: int, af: Domain = AF_INET) {.tags: [ReadIOEffect, WriteIOEffect].} = ## Connects to server as specified by ``address`` on port specified by ``port``. ## - ## The ``timeout`` paremeter specifies the time in miliseconds to allow for + ## The ``timeout`` paremeter specifies the time in milliseconds to allow for ## the connection to the server to be made. let originalStatus = not socket.nonblocking socket.setBlocking(false) diff --git a/lib/pure/times.nim b/lib/pure/times.nim index 1b9fa4599..b8836c15b 100644 --- a/lib/pure/times.nim +++ b/lib/pure/times.nim @@ -134,7 +134,7 @@ type ## everything should be positive or everything negative. Zero is ## fine too. Mixed signs will lead to unexpected results. TimeInterval* = object ## a time interval - miliseconds*: int ## The number of miliseconds + milliseconds*: int ## The number of milliseconds seconds*: int ## The number of seconds minutes*: int ## The number of minutes hours*: int ## The number of hours @@ -145,6 +145,11 @@ type {.deprecated: [TMonth: Month, TWeekDay: WeekDay, TTime: Time, TTimeInterval: TimeInterval, TTimeInfo: TimeInfo].} +proc miliseconds*(t: TimeInterval): int {.deprecated.} = t.milliseconds + +proc `miliseconds=`*(t:var TimeInterval, milliseconds: int) {.deprecated.} = + t.milliseconds = milliseconds + proc getTime*(): Time {.tags: [TimeEffect], benign.} ## gets the current calendar time as a UNIX epoch value (number of seconds ## elapsed since 1970) with integer precission. Use epochTime for higher @@ -208,13 +213,13 @@ proc getTimezone*(): int {.tags: [TimeEffect], raises: [], benign.} ## returns the offset of the local (non-DST) timezone in seconds west of UTC. proc getStartMilsecs*(): int {.deprecated, tags: [TimeEffect], benign.} - ## get the miliseconds from the start of the program. **Deprecated since + ## get the milliseconds from the start of the program. **Deprecated since ## version 0.8.10.** Use ``epochTime`` or ``cpuTime`` instead. -proc initInterval*(miliseconds, seconds, minutes, hours, days, months, +proc initInterval*(milliseconds, seconds, minutes, hours, days, months, years: int = 0): TimeInterval = ## creates a new ``TimeInterval``. - result.miliseconds = miliseconds + result.milliseconds = milliseconds result.seconds = seconds result.minutes = minutes result.hours = hours @@ -264,7 +269,7 @@ proc toSeconds(a: TimeInfo, interval: TimeInterval): float = result += float(newinterv.hours * 60 * 60) result += float(newinterv.minutes * 60) result += float(newinterv.seconds) - result += newinterv.miliseconds / 1000 + result += newinterv.milliseconds / 1000 proc `+`*(a: TimeInfo, interval: TimeInterval): TimeInfo = ## adds ``interval`` time. @@ -530,7 +535,7 @@ elif defined(JS): startMilsecs = getTime() proc getStartMilsecs(): int = - ## get the miliseconds from the start of the program + ## get the milliseconds from the start of the program return int(getTime() - startMilsecs) proc valueOf(time: Time): float {.importcpp: "getTime", tags:[]} |