Merge branch 'devel' of github.com:nim-lang/Nim into devel

This commit is contained in:
Araq 2017-11-21 01:42:58 +01:00
commit 1bbab827c4
115 changed files with 1279 additions and 1542 deletions

View file

@ -113,7 +113,9 @@ type
type
NimIdent* = object of RootObj
## represents a Nim identifier in the AST
## represents a Nim identifier in the AST. **Note**: This is only
## rarely useful, for identifier construction from a string
## use ``ident"abc"``.
NimSymObj = object # hidden
NimSym* = ref NimSymObj
@ -129,7 +131,11 @@ const
nnkCallKinds* = {nnkCall, nnkInfix, nnkPrefix, nnkPostfix, nnkCommand,
nnkCallStrLit}
proc `!`*(s: string): NimIdent {.magic: "StrToIdent", noSideEffect.}
proc `!`*(s: string): NimIdent {.magic: "StrToIdent", noSideEffect, deprecated.}
## constructs an identifier from the string `s`
## **Deprecated since version 0.18.0**: Use ``toNimIdent`` instead.
proc toNimIdent*(s: string): NimIdent {.magic: "StrToIdent", noSideEffect.}
## constructs an identifier from the string `s`
proc `$`*(i: NimIdent): string {.magic: "IdentToStr", noSideEffect.}
@ -237,7 +243,7 @@ proc `ident=`*(n: NimNode, val: NimIdent) {.magic: "NSetIdent", noSideEffect.}
proc `strVal=`*(n: NimNode, val: string) {.magic: "NSetStrVal", noSideEffect.}
proc newNimNode*(kind: NimNodeKind,
lineInfoFrom: NimNode=nil): NimNode
lineInfoFrom: NimNode = nil): NimNode
{.magic: "NNewNimNode", noSideEffect.}
## Creates a new AST node of the specified kind.
##
@ -290,7 +296,7 @@ proc newIdentNode*(i: NimIdent): NimNode {.compileTime.} =
proc newIdentNode*(i: string): NimNode {.compileTime.} =
## creates an identifier node from `i`
result = newNimNode(nnkIdent)
result.ident = !i
result.ident = toNimIdent i
type
@ -400,7 +406,7 @@ proc quote*(bl: typed, op = "``"): NimNode {.magic: "QuoteAst", noSideEffect.}
##
## .. code-block:: nim
##
## macro check(ex: expr): stmt =
## macro check(ex: untyped): typed =
## # this is a simplified version of the check macro from the
## # unittest module.
##
@ -477,7 +483,6 @@ proc newLit*(c: char): NimNode {.compileTime.} =
result = newNimNode(nnkCharLit)
result.intVal = ord(c)
proc newLit*(i: int): NimNode {.compileTime.} =
## produces a new integer literal node.
result = newNimNode(nnkIntLit)
@ -581,7 +586,7 @@ proc newLit*[T](arg: seq[T]): NimNode {.compileTime.} =
proc newLit*(arg: tuple): NimNode {.compileTime.} =
result = nnkPar.newTree
for a,b in arg.fieldPairs:
result.add nnkExprColonExpr.newTree( newIdentNode(a), newLit(b) )
result.add nnkExprColonExpr.newTree(newIdentNode(a), newLit(b))
proc newLit*(s: string): NimNode {.compileTime.} =
## produces a new string literal node.
@ -615,7 +620,7 @@ proc treeRepr*(n: NimNode): string {.compileTime, benign.} =
of nnkCharLit..nnkInt64Lit: res.add(" " & $n.intVal)
of nnkFloatLit..nnkFloat64Lit: res.add(" " & $n.floatVal)
of nnkStrLit..nnkTripleStrLit: res.add(" " & $n.strVal)
of nnkIdent: res.add(" !\"" & $n.ident & '"')
of nnkIdent: res.add(" ident\"" & $n.ident & '"')
of nnkSym: res.add(" \"" & $n.symbol & '"')
of nnkNone: assert false
else:
@ -640,7 +645,7 @@ proc lispRepr*(n: NimNode): string {.compileTime, benign.} =
of nnkCharLit..nnkInt64Lit: add(result, $n.intVal)
of nnkFloatLit..nnkFloat64Lit: add(result, $n.floatVal)
of nnkStrLit..nnkTripleStrLit: add(result, $n.strVal)
of nnkIdent: add(result, "!\"" & $n.ident & '"')
of nnkIdent: add(result, "ident\"" & $n.ident & '"')
of nnkSym: add(result, $n.symbol)
of nnkNone: assert false
else:
@ -664,7 +669,7 @@ proc astGenRepr*(n: NimNode): string {.compileTime, benign.} =
## .. code-block:: nim
## nnkStmtList.newTree(
## nnkCommand.newTree(
## newIdentNode(!"echo"),
## newIdentNode("echo"),
## newLit("Hello world")
## )
## )
@ -718,7 +723,7 @@ proc astGenRepr*(n: NimNode): string {.compileTime, benign.} =
of nnkIntLit..nnkInt64Lit: res.add($n.intVal)
of nnkFloatLit..nnkFloat64Lit: res.add($n.floatVal)
of nnkStrLit..nnkTripleStrLit: res.add($n.strVal.escape())
of nnkIdent: res.add("!" & ($n.ident).escape())
of nnkIdent: res.add(($n.ident).escape())
of nnkSym: res.add(($n.symbol).escape())
of nnkNone: assert false
else:
@ -898,6 +903,48 @@ proc newIfStmt*(branches: varargs[tuple[cond, body: NimNode]]):
for i in branches:
result.add(newNimNode(nnkElifBranch).add(i.cond, i.body))
proc newEnum*(name: NimNode, fields: openArray[NimNode],
public, pure: bool): NimNode {.compileTime.} =
## Creates a new enum. `name` must be an ident. Fields are allowed to be
## either idents or EnumFieldDef
##
## .. code-block:: nim
##
## newEnum(
## name = ident("Colors"),
## fields = [ident("Blue"), ident("Red")],
## public = true, pure = false)
##
## # type Colors* = Blue Red
##
expectKind name, nnkIdent
doAssert len(fields) > 0, "Enum must contain at least one field"
for field in fields:
expectKind field, {nnkIdent, nnkEnumFieldDef}
let enumBody = newNimNode(nnkEnumTy).add(newEmptyNode()).add(fields)
var typeDefArgs = [name, newEmptyNode(), enumBody]
if public:
let postNode = newNimNode(nnkPostfix).add(
newIdentNode("*"), typeDefArgs[0])
typeDefArgs[0] = postNode
if pure:
let pragmaNode = newNimNode(nnkPragmaExpr).add(
typeDefArgs[0],
add(newNimNode(nnkPragma), newIdentNode("pure")))
typeDefArgs[0] = pragmaNode
let
typeDef = add(newNimNode(nnkTypeDef), typeDefArgs)
typeSect = add(newNimNode(nnkTypeSection), typeDef)
return typeSect
proc copyChildrenTo*(src, dest: NimNode) {.compileTime.}=
## Copy all children from `src` to `dest`
@ -1015,7 +1062,7 @@ template findChild*(n: NimNode; cond: untyped): NimNode {.dirty.} =
##
## .. code-block:: nim
## var res = findChild(n, it.kind == nnkPostfix and
## it.basename.ident == !"foo")
## it.basename.ident == toNimIdent"foo")
block:
var res: NimNode
for it in n.children:
@ -1049,7 +1096,7 @@ proc basename*(a: NimNode): NimNode =
proc `basename=`*(a: NimNode; val: string) {.compileTime.}=
case a.kind
of nnkIdent: macros.`ident=`(a, !val)
of nnkIdent: macros.`ident=`(a, toNimIdent val)
of nnkPostfix, nnkPrefix: a[1] = ident(val)
else:
quit "Do not know how to get basename of (" & treeRepr(a) & ")\n" & repr(a)
@ -1110,7 +1157,7 @@ proc eqIdent*(node: NimNode; s: string): bool {.compileTime.} =
## other ways like ``node.ident`` are much more error-prone, unfortunately.
case node.kind
of nnkIdent:
result = node.ident == !s
result = node.ident == toNimIdent s
of nnkSym:
result = eqIdent($node.symbol, s)
of nnkOpenSymChoice, nnkClosedSymChoice:

View file

@ -3,12 +3,12 @@ export nativesockets
{.warning: "rawsockets module is deprecated, use nativesockets instead".}
template newRawSocket*(domain, sockType, protocol: cint): expr =
template newRawSocket*(domain, sockType, protocol: cint): untyped =
{.warning: "newRawSocket is deprecated, use newNativeSocket instead".}
newNativeSocket(domain, sockType, protocol)
template newRawSocket*(domain: Domain = AF_INET,
sockType: SockType = SOCK_STREAM,
protocol: Protocol = IPPROTO_TCP): expr =
protocol: Protocol = IPPROTO_TCP): untyped =
{.warning: "newRawSocket is deprecated, use newNativeSocket instead".}
newNativeSocket(domain, sockType, protocol)

View file

@ -58,15 +58,18 @@ type
socket: AsyncSocket
reuseAddr: bool
reusePort: bool
maxBody: int ## The maximum content-length that will be read for the body.
{.deprecated: [TRequest: Request, PAsyncHttpServer: AsyncHttpServer,
THttpCode: HttpCode, THttpVersion: HttpVersion].}
proc newAsyncHttpServer*(reuseAddr = true, reusePort = false): AsyncHttpServer =
proc newAsyncHttpServer*(reuseAddr = true, reusePort = false,
maxBody = 8388608): AsyncHttpServer =
## Creates a new ``AsyncHttpServer`` instance.
new result
result.reuseAddr = reuseAddr
result.reusePort = reusePort
result.maxBody = maxBody
proc addHeaders(msg: var string, headers: HttpHeaders) =
for k, v in headers:
@ -122,145 +125,161 @@ proc parseProtocol(protocol: string): tuple[orig: string, major, minor: int] =
raise newException(ValueError, "Invalid request protocol. Got: " &
protocol)
result.orig = protocol
i.inc protocol.parseInt(result.major, i)
i.inc protocol.parseSaturatedNatural(result.major, i)
i.inc # Skip .
i.inc protocol.parseInt(result.minor, i)
i.inc protocol.parseSaturatedNatural(result.minor, i)
proc sendStatus(client: AsyncSocket, status: string): Future[void] =
client.send("HTTP/1.1 " & status & "\c\L\c\L")
proc processClient(client: AsyncSocket, address: string,
callback: proc (request: Request):
proc processRequest(server: AsyncHttpServer, req: FutureVar[Request],
client: AsyncSocket,
address: string, lineFut: FutureVar[string],
callback: proc (request: Request):
Future[void] {.closure, gcsafe.}) {.async.} =
var request: Request
request.url = initUri()
request.headers = newHttpHeaders()
var lineFut = newFutureVar[string]("asynchttpserver.processClient")
lineFut.mget() = newStringOfCap(80)
var key, value = ""
while not client.isClosed:
# GET /path HTTP/1.1
# Header: val
# \n
request.headers.clear()
request.body = ""
request.hostname.shallowCopy(address)
assert client != nil
request.client = client
# Alias `request` to `req.mget()` so we don't have to write `mget` everywhere.
template request(): Request =
req.mget()
# We should skip at least one empty line before the request
# https://tools.ietf.org/html/rfc7230#section-3.5
for i in 0..1:
lineFut.mget().setLen(0)
lineFut.clean()
await client.recvLineInto(lineFut, maxLength=maxLine) # TODO: Timeouts.
# GET /path HTTP/1.1
# Header: val
# \n
request.headers.clear()
request.body = ""
request.hostname.shallowCopy(address)
assert client != nil
request.client = client
if lineFut.mget == "":
client.close()
return
# We should skip at least one empty line before the request
# https://tools.ietf.org/html/rfc7230#section-3.5
for i in 0..1:
lineFut.mget().setLen(0)
lineFut.clean()
await client.recvLineInto(lineFut, maxLength=maxLine) # TODO: Timeouts.
if lineFut.mget.len > maxLine:
await request.respondError(Http413)
client.close()
return
if lineFut.mget != "\c\L":
break
# First line - GET /path HTTP/1.1
var i = 0
for linePart in lineFut.mget.split(' '):
case i
of 0:
try:
# TODO: this is likely slow.
request.reqMethod = parseEnum[HttpMethod]("http" & linePart)
except ValueError:
asyncCheck request.respondError(Http400)
continue
of 1:
try:
parseUri(linePart, request.url)
except ValueError:
asyncCheck request.respondError(Http400)
continue
of 2:
try:
request.protocol = parseProtocol(linePart)
except ValueError:
asyncCheck request.respondError(Http400)
continue
else:
await request.respondError(Http400)
continue
inc i
# Headers
while true:
i = 0
lineFut.mget.setLen(0)
lineFut.clean()
await client.recvLineInto(lineFut, maxLength=maxLine)
if lineFut.mget == "":
client.close(); return
if lineFut.mget.len > maxLine:
await request.respondError(Http413)
client.close(); return
if lineFut.mget == "\c\L": break
let (key, value) = parseHeader(lineFut.mget)
request.headers[key] = value
# Ensure the client isn't trying to DoS us.
if request.headers.len > headerLimit:
await client.sendStatus("400 Bad Request")
request.client.close()
return
if request.reqMethod == HttpPost:
# Check for Expect header
if request.headers.hasKey("Expect"):
if "100-continue" in request.headers["Expect"]:
await client.sendStatus("100 Continue")
else:
await client.sendStatus("417 Expectation Failed")
# Read the body
# - Check for Content-length header
if request.headers.hasKey("Content-Length"):
var contentLength = 0
if parseInt(request.headers["Content-Length"],
contentLength) == 0:
await request.respond(Http400, "Bad Request. Invalid Content-Length.")
continue
else:
request.body = await client.recv(contentLength)
if request.body.len != contentLength:
await request.respond(Http400, "Bad Request. Content-Length does not match actual.")
continue
elif request.reqMethod == HttpPost:
await request.respond(Http411, "Content-Length required.")
continue
# Call the user's callback.
await callback(request)
if "upgrade" in request.headers.getOrDefault("connection"):
if lineFut.mget == "":
client.close()
return
# Persistent connections
if (request.protocol == HttpVer11 and
request.headers.getOrDefault("connection").normalize != "close") or
(request.protocol == HttpVer10 and
request.headers.getOrDefault("connection").normalize == "keep-alive"):
# In HTTP 1.1 we assume that connection is persistent. Unless connection
# header states otherwise.
# In HTTP 1.0 we assume that the connection should not be persistent.
# Unless the connection header states otherwise.
discard
else:
request.client.close()
if lineFut.mget.len > maxLine:
await request.respondError(Http413)
client.close()
return
if lineFut.mget != "\c\L":
break
# First line - GET /path HTTP/1.1
var i = 0
for linePart in lineFut.mget.split(' '):
case i
of 0:
try:
# TODO: this is likely slow.
request.reqMethod = parseEnum[HttpMethod]("http" & linePart)
except ValueError:
asyncCheck request.respondError(Http400)
return
of 1:
try:
parseUri(linePart, request.url)
except ValueError:
asyncCheck request.respondError(Http400)
return
of 2:
try:
request.protocol = parseProtocol(linePart)
except ValueError:
asyncCheck request.respondError(Http400)
return
else:
await request.respondError(Http400)
return
inc i
# Headers
while true:
i = 0
lineFut.mget.setLen(0)
lineFut.clean()
await client.recvLineInto(lineFut, maxLength=maxLine)
if lineFut.mget == "":
client.close(); return
if lineFut.mget.len > maxLine:
await request.respondError(Http413)
client.close(); return
if lineFut.mget == "\c\L": break
let (key, value) = parseHeader(lineFut.mget)
request.headers[key] = value
# Ensure the client isn't trying to DoS us.
if request.headers.len > headerLimit:
await client.sendStatus("400 Bad Request")
request.client.close()
return
if request.reqMethod == HttpPost:
# Check for Expect header
if request.headers.hasKey("Expect"):
if "100-continue" in request.headers["Expect"]:
await client.sendStatus("100 Continue")
else:
await client.sendStatus("417 Expectation Failed")
# Read the body
# - Check for Content-length header
if request.headers.hasKey("Content-Length"):
var contentLength = 0
if parseSaturatedNatural(request.headers["Content-Length"], contentLength) == 0:
await request.respond(Http400, "Bad Request. Invalid Content-Length.")
return
else:
if contentLength > server.maxBody:
await request.respondError(Http413)
return
request.body = await client.recv(contentLength)
if request.body.len != contentLength:
await request.respond(Http400, "Bad Request. Content-Length does not match actual.")
return
elif request.reqMethod == HttpPost:
await request.respond(Http411, "Content-Length required.")
return
# Call the user's callback.
await callback(request)
if "upgrade" in request.headers.getOrDefault("connection"):
return
# Persistent connections
if (request.protocol == HttpVer11 and
cmpIgnoreCase(request.headers.getOrDefault("connection"), "close") != 0) or
(request.protocol == HttpVer10 and
cmpIgnoreCase(request.headers.getOrDefault("connection"), "keep-alive") == 0):
# In HTTP 1.1 we assume that connection is persistent. Unless connection
# header states otherwise.
# In HTTP 1.0 we assume that the connection should not be persistent.
# Unless the connection header states otherwise.
discard
else:
request.client.close()
return
proc processClient(server: AsyncHttpServer, client: AsyncSocket, address: string,
callback: proc (request: Request):
Future[void] {.closure, gcsafe.}) {.async.} =
var request = newFutureVar[Request]("asynchttpserver.processClient")
request.mget().url = initUri()
request.mget().headers = newHttpHeaders()
var lineFut = newFutureVar[string]("asynchttpserver.processClient")
lineFut.mget() = newStringOfCap(80)
while not client.isClosed:
try:
await processRequest(server, request, client, address, lineFut, callback)
except:
asyncCheck request.mget().respondError(Http500)
proc serve*(server: AsyncHttpServer, port: Port,
callback: proc (request: Request): Future[void] {.closure,gcsafe.},
address = "") {.async.} =
@ -280,7 +299,7 @@ proc serve*(server: AsyncHttpServer, port: Port,
# TODO: Causes compiler crash.
#var (address, client) = await server.socket.acceptAddr()
var fut = await server.socket.acceptAddr()
asyncCheck processClient(fut.client, fut.address, callback)
asyncCheck processClient(server, fut.client, fut.address, callback)
#echo(f.isNil)
#echo(f.repr)

View file

@ -472,27 +472,13 @@ proc stripAwait(node: NimNode): NimNode =
proc splitParamType(paramType: NimNode, async: bool): NimNode =
result = paramType
if paramType.kind == nnkInfix and $paramType[0].ident in ["|", "or"]:
let firstType = paramType[1]
let firstTypeName = $firstType.ident
let secondType = paramType[2]
let secondTypeName = $secondType.ident
let firstAsync = "async" in ($paramType[1].ident).normalize
let secondAsync = "async" in ($paramType[2].ident).normalize
# Make sure that at least one has the name `async`, otherwise we shouldn't
# touch it.
if not ("async" in firstTypeName.normalize or
"async" in secondTypeName.normalize):
return
if async:
if firstTypeName.normalize.startsWith("async"):
result = paramType[1]
elif secondTypeName.normalize.startsWith("async"):
result = paramType[2]
else:
if not firstTypeName.normalize.startsWith("async"):
result = paramType[1]
elif not secondTypeName.normalize.startsWith("async"):
result = paramType[2]
if firstAsync:
result = paramType[if async: 1 else: 2]
elif secondAsync:
result = paramType[if async: 2 else: 1]
proc stripReturnType(returnType: NimNode): NimNode =
# Strip out the 'Future' from 'Future[T]'.
@ -535,4 +521,4 @@ macro multisync*(prc: untyped): untyped =
let (sync, asyncPrc) = splitProc(prc)
result = newStmtList()
result.add(asyncSingleProc(asyncPrc))
result.add(sync)
result.add(sync)

View file

@ -21,24 +21,18 @@ proc openDefaultBrowser*(url: string) =
## opens `url` with the user's default browser. This does not block.
##
## Under Windows, ``ShellExecute`` is used. Under Mac OS X the ``open``
## command is used. Under Unix, it is checked if ``gnome-open`` exists and
## used if it does. Next attempt is ``kde-open``, then ``xdg-open``.
## Otherwise the environment variable ``BROWSER`` is used to determine the
## default browser to use.
## command is used. Under Unix, it is checked if ``xdg-open`` exists and
## used if it does. Otherwise the environment variable ``BROWSER`` is
## used to determine the default browser to use.
when defined(windows):
when useWinUnicode:
var o = newWideCString("open")
var u = newWideCString(url)
discard shellExecuteW(0'i32, o, u, nil, nil, SW_SHOWNORMAL)
else:
discard shellExecuteA(0'i32, "open", url, nil, nil, SW_SHOWNORMAL)
var o = newWideCString("open")
var u = newWideCString(url)
discard shellExecuteW(0'i32, o, u, nil, nil, SW_SHOWNORMAL)
elif defined(macosx):
discard execShellCmd("open " & quoteShell(url))
else:
const attempts = ["gnome-open ", "kde-open ", "xdg-open "]
var u = quoteShell(url)
for a in items(attempts):
if execShellCmd(a & u) == 0: return
if execShellCmd("xdg-open " & u) == 0: return
for b in getEnv("BROWSER").string.split(PathSep):
try:
# we use ``startProcess`` here because we don't want to block!

View file

@ -20,6 +20,8 @@
include "system/inclrtl"
import macros
when not defined(nimhygiene):
{.pragma: dirty.}
@ -704,6 +706,52 @@ template newSeqWith*(len: int, init: untyped): untyped =
result[i] = init
result
proc mapLitsImpl(constructor: NimNode; op: NimNode; nested: bool;
filter = nnkLiterals): NimNode =
if constructor.kind in filter:
result = newNimNode(nnkCall, lineInfoFrom=constructor)
result.add op
result.add constructor
else:
result = newNimNode(constructor.kind, lineInfoFrom=constructor)
for v in constructor:
if nested or v.kind in filter:
result.add mapLitsImpl(v, op, nested, filter)
else:
result.add v
macro mapLiterals*(constructor, op: untyped;
nested = true): untyped =
## applies ``op`` to each of the **atomic** literals like ``3``
## or ``"abc"`` in the specified ``constructor`` AST. This can
## be used to map every array element to some target type:
##
## Example:
##
## .. code-block::
## let x = mapLiterals([0.1, 1.2, 2.3, 3.4], int)
## doAssert x is array[4, int]
##
## Short notation for:
##
## .. code-block::
## let x = [int(0.1), int(1.2), int(2.3), int(3.4)]
##
## If ``nested`` is true, the literals are replaced everywhere
## in the ``constructor`` AST, otherwise only the first level
## is considered:
##
## .. code-block::
## mapLiterals((1, ("abc"), 2), float, nested=false)
##
## Produces::
##
## (float(1), ("abc"), float(2))
##
## There are no constraints for the ``constructor`` AST, it
## works for nested tuples of arrays of sets etc.
result = mapLitsImpl(constructor, op, nested.boolVal)
when isMainModule:
import strutils
block: # concat test
@ -992,5 +1040,11 @@ when isMainModule:
seq2D[0][1] = true
doAssert seq2D == @[@[true, true], @[true, false], @[false, false], @[false, false]]
block: # mapLiterals tests
let x = mapLiterals([0.1, 1.2, 2.3, 3.4], int)
doAssert x is array[4, int]
doAssert mapLiterals((1, ("abc"), 2), float, nested=false) == (float(1), "abc", float(2))
doAssert mapLiterals(([1], ("abc"), 2), `$`, nested=true) == (["1"], "abc", "2")
when not defined(testing):
echo "Finished doc tests"

View file

@ -87,10 +87,10 @@ proc newSharedString*(s: string): SharedString =
result.len = len
when declared(atomicLoadN):
template load(x): expr = atomicLoadN(addr x, ATOMIC_SEQ_CST)
template load(x): untyped = atomicLoadN(addr x, ATOMIC_SEQ_CST)
else:
# XXX Fixme
template load(x): expr = x
template load(x): untyped = x
proc add*(s: var SharedString; t: cstring; len: Natural) =
if len == 0: return

View file

@ -13,6 +13,15 @@ import strtabs, times
proc parseCookies*(s: string): StringTableRef =
## parses cookies into a string table.
##
## The proc is meant to parse the Cookie header set by a client, not the
## "Set-Cookie" header set by servers.
##
## Example:
##
## .. code-block::Nim
## doAssert parseCookies("a=1; foo=bar") == {"a": 1, "foo": "bar"}.newStringTable
result = newStringTable(modeCaseInsensitive)
var i = 0
while true:

View file

@ -197,4 +197,4 @@ macro dump*(x: typed): untyped =
let s = x.toStrLit
let r = quote do:
debugEcho `s`, " = ", `x`
return r
return r

View file

@ -1,211 +0,0 @@
#
#
# Nim's Runtime Library
# (c) Copyright 2012 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
## The ``gentabs`` module implements an efficient hash table that is a
## key-value mapping. The keys are required to be strings, but the values
## may be any Nim or user defined type. This module supports matching
## of keys in case-sensitive, case-insensitive and style-insensitive modes.
##
## **Warning:** This module is deprecated, new code shouldn't use it!
{.deprecated.}
import
os, hashes, strutils
type
GenTableMode* = enum ## describes the table's key matching mode
modeCaseSensitive, ## case sensitive matching of keys
modeCaseInsensitive, ## case insensitive matching of keys
modeStyleInsensitive ## style sensitive matching of keys
GenKeyValuePair[T] = tuple[key: string, val: T]
GenKeyValuePairSeq[T] = seq[GenKeyValuePair[T]]
GenTable*[T] = object of RootObj
counter: int
data: GenKeyValuePairSeq[T]
mode: GenTableMode
PGenTable*[T] = ref GenTable[T] ## use this type to declare hash tables
{.deprecated: [TGenTableMode: GenTableMode, TGenKeyValuePair: GenKeyValuePair,
TGenKeyValuePairSeq: GenKeyValuePairSeq, TGenTable: GenTable].}
const
growthFactor = 2
startSize = 64
proc len*[T](tbl: PGenTable[T]): int {.inline.} =
## returns the number of keys in `tbl`.
result = tbl.counter
iterator pairs*[T](tbl: PGenTable[T]): tuple[key: string, value: T] =
## iterates over any (key, value) pair in the table `tbl`.
for h in 0..high(tbl.data):
if not isNil(tbl.data[h].key):
yield (tbl.data[h].key, tbl.data[h].val)
proc myhash[T](tbl: PGenTable[T], key: string): Hash =
case tbl.mode
of modeCaseSensitive: result = hashes.hash(key)
of modeCaseInsensitive: result = hashes.hashIgnoreCase(key)
of modeStyleInsensitive: result = hashes.hashIgnoreStyle(key)
proc myCmp[T](tbl: PGenTable[T], a, b: string): bool =
case tbl.mode
of modeCaseSensitive: result = cmp(a, b) == 0
of modeCaseInsensitive: result = cmpIgnoreCase(a, b) == 0
of modeStyleInsensitive: result = cmpIgnoreStyle(a, b) == 0
proc mustRehash(length, counter: int): bool =
assert(length > counter)
result = (length * 2 < counter * 3) or (length - counter < 4)
proc newGenTable*[T](mode: GenTableMode): PGenTable[T] =
## creates a new generic hash table that is empty.
new(result)
result.mode = mode
result.counter = 0
newSeq(result.data, startSize)
proc nextTry(h, maxHash: Hash): Hash {.inline.} =
result = ((5 * h) + 1) and maxHash
proc rawGet[T](tbl: PGenTable[T], key: string): int =
var h: Hash
h = myhash(tbl, key) and high(tbl.data) # start with real hash value
while not isNil(tbl.data[h].key):
if myCmp(tbl, tbl.data[h].key, key):
return h
h = nextTry(h, high(tbl.data))
result = - 1
proc rawInsert[T](tbl: PGenTable[T], data: var GenKeyValuePairSeq[T],
key: string, val: T) =
var h: Hash
h = myhash(tbl, key) and high(data)
while not isNil(data[h].key):
h = nextTry(h, high(data))
data[h].key = key
data[h].val = val
proc enlarge[T](tbl: PGenTable[T]) =
var n: GenKeyValuePairSeq[T]
newSeq(n, len(tbl.data) * growthFactor)
for i in countup(0, high(tbl.data)):
if not isNil(tbl.data[i].key):
rawInsert[T](tbl, n, tbl.data[i].key, tbl.data[i].val)
swap(tbl.data, n)
proc hasKey*[T](tbl: PGenTable[T], key: string): bool =
## returns true iff `key` is in the table `tbl`.
result = rawGet(tbl, key) >= 0
proc `[]`*[T](tbl: PGenTable[T], key: string): T =
## retrieves the value at ``tbl[key]``. If `key` is not in `tbl`,
## default(T) is returned and no exception is raised. One can check
## with ``hasKey`` whether the key exists.
var index = rawGet(tbl, key)
if index >= 0: result = tbl.data[index].val
proc `[]=`*[T](tbl: PGenTable[T], key: string, val: T) =
## puts a (key, value)-pair into `tbl`.
var index = rawGet(tbl, key)
if index >= 0:
tbl.data[index].val = val
else:
if mustRehash(len(tbl.data), tbl.counter): enlarge(tbl)
rawInsert(tbl, tbl.data, key, val)
inc(tbl.counter)
when isMainModule:
#
# Verify tables of integer values (string keys)
#
var x = newGenTable[int](modeCaseInsensitive)
x["one"] = 1
x["two"] = 2
x["three"] = 3
x["four"] = 4
x["five"] = 5
assert(len(x) == 5) # length procedure works
assert(x["one"] == 1) # case-sensitive lookup works
assert(x["ONE"] == 1) # case-insensitive should work for this table
assert(x["one"]+x["two"] == 3) # make sure we're getting back ints
assert(x.hasKey("one")) # hasKey should return 'true' for a key
# of "one"...
assert(not x.hasKey("NOPE")) # ...but key "NOPE" is not in the table.
for k,v in pairs(x): # make sure the 'pairs' iterator works
assert(x[k]==v)
#
# Verify a table of user-defined types
#
type
MyType = tuple[first, second: string] # a pair of strings
{.deprecated: [TMyType: MyType].}
var y = newGenTable[MyType](modeCaseInsensitive) # hash table where each
# value is MyType tuple
#var junk: MyType = ("OK", "Here")
#echo junk.first, " ", junk.second
y["Hello"] = ("Hello", "World")
y["Goodbye"] = ("Goodbye", "Everyone")
#y["Hello"] = MyType( ("Hello", "World") )
#y["Goodbye"] = MyType( ("Goodbye", "Everyone") )
assert( not isNil(y["Hello"].first) )
assert( y["Hello"].first == "Hello" )
assert( y["Hello"].second == "World" )
#
# Verify table of tables
#
var z: PGenTable[ PGenTable[int] ] # hash table where each value is
# a hash table of ints
z = newGenTable[PGenTable[int]](modeCaseInsensitive)
z["first"] = newGenTable[int](modeCaseInsensitive)
z["first"]["one"] = 1
z["first"]["two"] = 2
z["first"]["three"] = 3
z["second"] = newGenTable[int](modeCaseInsensitive)
z["second"]["red"] = 10
z["second"]["blue"] = 20
assert(len(z) == 2) # length of outer table
assert(len(z["first"]) == 3) # length of "first" table
assert(len(z["second"]) == 2) # length of "second" table
assert( z["first"]["one"] == 1) # retrieve from first inner table
assert( z["second"]["red"] == 10) # retrieve from second inner table
when false:
# disabled: depends on hash order:
var output = ""
for k, v in pairs(z):
output.add( "$# ($#) ->\L" % [k,$len(v)] )
for k2,v2 in pairs(v):
output.add( " $# <-> $#\L" % [k2,$v2] )
let expected = unindent """
first (3) ->
two <-> 2
three <-> 3
one <-> 1
second (2) ->
red <-> 10
blue <-> 20
"""
assert output == expected

View file

@ -43,6 +43,7 @@ const bsdPlatform = defined(macosx) or defined(freebsd) or
defined(dragonfly)
when defined(nimdoc):
type SocketHandle = int
type
Selector*[T] = ref object
## An object which holds descriptors to be checked for read/write status

View file

@ -1849,8 +1849,8 @@ when isMainModule:
discard parseJson"""{ invalid"""
except:
discard
# memory diff should less than 2M
doAssert(abs(getOccupiedMem() - startMemory) < 2 * 1024 * 1024)
# memory diff should less than 4M
doAssert(abs(getOccupiedMem() - startMemory) < 4 * 1024 * 1024)
# test `$`

View file

@ -1,87 +0,0 @@
#
#
# Nim's Runtime Library
# (c) Copyright 2013 Robert Persson
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
## **Warning:** This module will be moved out of the stdlib and into a
## Nimble package, don't use it.
type OneVarFunction* = proc (x: float): float
{.deprecated: [TOneVarFunction: OneVarFunction].}
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`
## 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
## the root refinement stops and success=true.
# see http://en.wikipedia.org/wiki/Brent%27s_method
var
a=xmin
b=xmax
c=a
d=1.0e308
fa=function(a)
fb=function(b)
fc=fa
s=0.0
fs=0.0
mflag:bool
i=0
tmp2:float
if fa*fb>=0:
if abs(fa)<abs(fb):
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
(not mflag and abs(s - b) >= abs(c - d) / 2.0):
s=(a+b)/2.0
mflag=true
else:
if (mflag and (abs(b - c) < tol)) or (not mflag and (abs(c - d) < tol)):
s=(a+b)/2.0
mflag=true
else:
mflag=false
fs = function(s)
d = c
c = b
fc = fb
if fa * fs<0.0:
b=s
fb=fs
else:
a=s
fa=fs
if abs(fa)<abs(fb):
swap(a,b)
swap(fa,fb)
inc i
if i>maxiter:
break
return (b,fb,true)

View file

@ -1195,14 +1195,15 @@ when defined(nimdoc):
## Returns the number of `command line arguments`:idx: given to the
## application.
##
## If your binary was called without parameters this will return zero. You
## can later query each individual paramater with `paramStr() <#paramStr>`_
## Unlike `argc`:idx: in C, if your binary was called without parameters this
## will return zero.
## You can query each individual paramater with `paramStr() <#paramStr>`_
## or retrieve all of them in one go with `commandLineParams()
## <#commandLineParams>`_.
##
## **Availability**: On Posix there is no portable way to get the command
## line from a DLL and thus the proc isn't defined in this environment. You
## can test for its availability with `declared() <system.html#declared>`_.
## **Availability**: When generating a dynamic library (see --app:lib) on
## Posix this proc is not defined.
## Test for availability using `declared() <system.html#declared>`_.
## Example:
##
## .. code-block:: nim
@ -1219,13 +1220,14 @@ when defined(nimdoc):
## `paramCount() <#paramCount>`_ with this proc you can call the
## convenience `commandLineParams() <#commandLineParams>`_.
##
## It is possible to call ``paramStr(0)`` but this will return OS specific
## Similarly to `argv`:idx: in C,
## it is possible to call ``paramStr(0)`` but this will return OS specific
## contents (usually the name of the invoked executable). You should avoid
## this and call `getAppFilename() <#getAppFilename>`_ instead.
##
## **Availability**: On Posix there is no portable way to get the command
## line from a DLL and thus the proc isn't defined in this environment. You
## can test for its availability with `declared() <system.html#declared>`_.
## **Availability**: When generating a dynamic library (see --app:lib) on
## Posix this proc is not defined.
## Test for availability using `declared() <system.html#declared>`_.
## Example:
##
## .. code-block:: nim

View file

@ -841,7 +841,7 @@ elif not defined(useNimRtl):
var attr: Tposix_spawnattr
var fops: Tposix_spawn_file_actions
template chck(e: expr) =
template chck(e: untyped) =
if e != 0'i32: raiseOSError(osLastError())
chck posix_spawn_file_actions_init(fops)

View file

@ -252,6 +252,31 @@ proc parseInt*(s: string, number: var int, start = 0): int {.
elif result != 0:
number = int(res)
proc parseSaturatedNatural*(s: string, b: var int, start = 0): int =
## parses a natural number into ``b``. This cannot raise an overflow
## error. Instead of an ``Overflow`` exception ``high(int)`` is returned.
## The number of processed character is returned.
## This is usually what you really want to use instead of `parseInt`:idx:.
## Example:
##
## .. code-block:: nim
## var res = 0
## discard parseSaturatedNatural("848", res)
## doAssert res == 848
var i = start
if s[i] == '+': inc(i)
if s[i] in {'0'..'9'}:
b = 0
while s[i] in {'0'..'9'}:
let c = ord(s[i]) - ord('0')
if b <= (high(int) - c) div 10:
b = b * 10 + c
else:
b = high(int)
inc(i)
while s[i] == '_': inc(i) # underscores are allowed and ignored
result = i - start
# overflowChecks doesn't work with BiggestUInt
proc rawParseUInt(s: string, b: var BiggestUInt, start = 0): int =
var
@ -393,16 +418,43 @@ when isMainModule:
let input = "$test{} $this is ${an{ example}} "
let expected = @[(ikVar, "test"), (ikStr, "{} "), (ikVar, "this"),
(ikStr, " is "), (ikExpr, "an{ example}"), (ikStr, " ")]
assert toSeq(interpolatedFragments(input)) == expected
doAssert toSeq(interpolatedFragments(input)) == expected
var value = 0
discard parseHex("0x38", value)
assert value == 56
doAssert value == 56
discard parseHex("0x34", value)
assert value == 56 * 256 + 52
doAssert value == 56 * 256 + 52
value = -1
discard parseHex("0x38", value)
assert value == -200
doAssert value == -200
value = -1
doAssert(parseSaturatedNatural("848", value) == 3)
doAssert value == 848
value = -1
discard parseSaturatedNatural("84899999999999999999324234243143142342135435342532453", value)
doAssert value == high(int)
value = -1
discard parseSaturatedNatural("9223372036854775808", value)
doAssert value == high(int)
value = -1
discard parseSaturatedNatural("9223372036854775807", value)
doAssert value == high(int)
value = -1
discard parseSaturatedNatural("18446744073709551616", value)
doAssert value == high(int)
value = -1
discard parseSaturatedNatural("18446744073709551615", value)
doAssert value == high(int)
value = -1
doAssert(parseSaturatedNatural("1_000_000", value) == 9)
doAssert value == 1_000_000
{.pop.}

View file

@ -1,371 +0,0 @@
#
#
# Nim's Runtime Library
# (c) Copyright 2013 Robert Persson
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
## **Warning:** This module will be moved out of the stdlib and into a
## Nimble package, don't use it.
import math
import strutils
import numeric
type
Poly* = object
cofs:seq[float]
{.deprecated: [TPoly: Poly].}
proc degree*(p:Poly):int=
## Returns the degree of the polynomial,
## that is the number of coefficients-1
return p.cofs.len-1
proc eval*(p:Poly,x:float):float=
## Evaluates a polynomial function value for `x`
## quickly using Horners method
var n=p.degree
result=p.cofs[n]
dec n
while n>=0:
result = result*x+p.cofs[n]
dec n
proc `[]` *(p:Poly;idx:int):float=
## Gets a coefficient of the polynomial.
## p[2] will returns the quadric term, p[3] the cubic etc.
## Out of bounds index will return 0.0.
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.
## If index is out of range for the coefficients,
## the polynomial grows to the smallest needed degree.
assert(idx>=0)
if idx>p.degree: #polynomial must grow
var oldlen=p.cofs.len
p.cofs.setLen(idx+1)
for q in oldlen.. <high(p.cofs):
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
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.
var n=p.degree
var relen=false
while n>0 and abs(p[n])<=zerotol: # >0 => keep at least one coefficient
dec n
relen=true
if relen: p.cofs.setLen(n+1)
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
if a!=1.0 or idx==0:
result.add(formatFloat(a,ffDefault,0))
if idx>=2:
result.add("x^" & $idx)
elif idx==1:
result.add("x")
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
var n=p.degree
result=p[n]*float(n)
dec n
while n>=1:
result = result*x+p[n]*float(n)
dec n
proc integral*(p:Poly):Poly=
## Returns a new polynomial which is the indefinite
## integral of `p`. The constant term is set to 0.0
newSeq(result.cofs,p.cofs.len+1)
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`
## quickly using a modified version of Horners method
var
n=p.degree
s1=p[n]/float(n+1)
s2=s1
fac:float
dec n
while n>=0:
fac=p[n]/float(n+1)
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:
## intiPoly(1.0,-2.0,3.0)
if len(cofs)<=0:
result.cofs= @[0.0] #need at least one coefficient
else:
# reverse order of coefficients so indexing matches degree of
# coefficient...
result.cofs= @[]
for idx in countdown(cofs.len-1,0):
result.cofs.add(cofs[idx])
result.clean #remove leading zero terms
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
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
d1=p1.degree
d2=p2.degree
n=d1+d2
idx:int
newSeq(result.cofs,n)
for i1 in countup(0,d1):
for i2 in countup(0,d2):
idx=i1+i2
result[idx]=result[idx]+p1[i1]*p2[i2]
result.clean
proc `*` *(p:Poly,f:float):Poly=
## Multiplies the polynomial `p` with a real number
newSeq(result.cofs,p.cofs.len)
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)
proc `mod` *(p,q:Poly):Poly=
## Computes the polynomial modulo operation,
## that is the remainder of `p`/`q`
var dummy:Poly
p.divMod(q,dummy,result)
proc normalize*(p:var Poly)=
## Multiplies the polynomial inplace by a term so that
## the leading term is 1.0.
## This might lead to an unstable polynomial
## if the leading term is zero.
p=p/p[p.degree]
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
if(err<=zerotol): return @[-p]
return @[]
else:
var sr=sqrt(d)
result= @[-sr-p,sr-p]
proc getRangeForRoots(p:Poly):tuple[xmin,xmax:float]=
## helper function for `roots` function
## quickly computes a range, guaranteed to contain
## all the real roots of the polynomial
# see http://www.mathsisfun.com/algebra/polynomials-bounds-zeros.html
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
proc addRoot(p:Poly,res:var seq[float],xp0,xp1,tol,zerotol,mergetol:float,maxiter:int)=
## helper function for `roots` function
## try to do a numeric search for a single root in range xp0-xp1,
## adding it to `res` (allocating `res` if nil)
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)
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)
proc roots*(p:Poly,tol=1.0e-9,zerotol=1.0e-6,mergetol=1.0e-12,maxiter=1000):seq[float]=
## Computes the real roots of the polynomial `p`
## `tol` is the tolerance used to break searching for each root when reached.
## `zerotol` is the tolerance, which is 'close enough' to zero to be considered a root
## and is used to find roots for curves that only 'touch' the x-axis.
## `mergetol` is the tolerance, of which two x-values are considered being the same root.
## `maxiter` can be used to limit the number of iterations for each root.
## Returns a (possibly empty) sorted sequence with the solutions.
var deg=p.degree
if deg<=0: #constant only => no roots
return @[]
elif p.degree==1: #linear
var linrt= -p.cofs[0]/p.cofs[1]
if linrt==Inf or linrt==NegInf:
return @[] #constant only => no roots
return @[linrt]
elif p.degree==2:
return solveQuadric(p.cofs[2],p.cofs[1],p.cofs[0],zerotol)
else:
# degree >=3 , find min/max points of polynomial with recursive
# derivative and do a numerical search for root between each min/max
var range=p.getRangeForRoots()
var minmax=p.derivative.roots(tol,zerotol,mergetol)
result= @[]
if minmax!=nil: #ie. we have minimas/maximas in this function
for x in minmax.items:
addRoot(p,result,range.xmin,x,tol,zerotol,mergetol,maxiter)
range.xmin=x
addRoot(p,result,range.xmin,range.xmax,tol,zerotol,mergetol,maxiter)

View file

@ -1,59 +0,0 @@
#
#
# Nim's Runtime Library
# (c) Copyright 2011 Philippe Lhoste
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
## Module for converting an integer to a Roman numeral.
## See http://en.wikipedia.org/wiki/Roman_numerals for reference.
##
## **Warning:** This module will be moved out of the stdlib and into a
## Nimble package, don't use it.
const
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 =
## Converts a Roman numeral to its int representation.
result = 0
var prevVal = 0
for i in countdown(romanVal.len - 1, 0):
var val = 0
case romanVal[i]
of 'I', 'i': val = 1
of 'V', 'v': val = 5
of 'X', 'x': val = 10
of 'L', 'l': val = 50
of 'C', 'c': val = 100
of 'D', 'd': val = 500
of 'M', 'm': val = 1000
else:
raise newException(EInvalidValue, "invalid roman numeral: " & $romanVal)
if val >= prevVal:
inc(result, val)
else:
dec(result, val)
prevVal = val
proc decimalToRoman*(number: range[1..3_999]): string =
## Converts a number to a Roman numeral.
const romanComposites = [
("M", 1000), ("CM", 900),
("D", 500), ("CD", 400), ("C", 100),
("XC", 90), ("L", 50), ("XL", 40), ("X", 10), ("IX", 9),
("V", 5), ("IV", 4), ("I", 1)]
result = ""
var decVal: int = number
for key, val in items(romanComposites):
while decVal >= val:
dec(decVal, val)
result.add(key)
when isMainModule:
for i in 1 .. 3_999:
assert i == i.decimalToRoman.romanToDecimal

View file

@ -1927,7 +1927,7 @@ type
{.deprecated: [TFloatFormat: FloatFormatMode].}
proc formatBiggestFloat*(f: BiggestFloat, format: FloatFormatMode = ffDefault,
precision: range[0..32] = 16;
precision: range[-1..32] = 16;
decimalSep = '.'): string {.
noSideEffect, rtl, extern: "nsu$1".} =
## Converts a floating point value `f` to a string.
@ -1939,7 +1939,7 @@ proc formatBiggestFloat*(f: BiggestFloat, format: FloatFormatMode = ffDefault,
## `precision`'s default value is the maximum number of meaningful digits
## after the decimal point for Nim's ``biggestFloat`` type.
##
## If ``precision == 0``, it tries to format it nicely.
## If ``precision == -1``, it tries to format it nicely.
when defined(js):
var res: cstring
case format
@ -1961,7 +1961,7 @@ proc formatBiggestFloat*(f: BiggestFloat, format: FloatFormatMode = ffDefault,
buf {.noinit.}: array[0..2500, char]
L: cint
frmtstr[0] = '%'
if precision > 0:
if precision >= 0:
frmtstr[1] = '#'
frmtstr[2] = '.'
frmtstr[3] = '*'
@ -1984,7 +1984,7 @@ proc formatBiggestFloat*(f: BiggestFloat, format: FloatFormatMode = ffDefault,
# but nothing else is possible:
if buf[i] in {'.', ','}: result[i] = decimalsep
else: result[i] = buf[i]
when defined(vcc):
when defined(windows):
# VS pre 2015 violates the C standard: "The exponent always contains at
# least two digits, and only as many more digits as necessary to
# represent the exponent." [C11 §7.21.6.1]
@ -1995,7 +1995,7 @@ proc formatBiggestFloat*(f: BiggestFloat, format: FloatFormatMode = ffDefault,
result.setLen(result.len - 1)
proc formatFloat*(f: float, format: FloatFormatMode = ffDefault,
precision: range[0..32] = 16; decimalSep = '.'): string {.
precision: range[-1..32] = 16; decimalSep = '.'): string {.
noSideEffect, rtl, extern: "nsu$1".} =
## Converts a floating point value `f` to a string.
##
@ -2006,16 +2006,16 @@ proc formatFloat*(f: float, format: FloatFormatMode = ffDefault,
## `precision`'s default value is the maximum number of meaningful digits
## after the decimal point for Nim's ``float`` type.
##
## If ``precision == 0``, it tries to format it nicely.
## If ``precision == -1``, it tries to format it nicely.
##
## Examples:
##
## .. code-block:: nim
##
## let x = 123.456
## echo x.formatFloat() # 123.4560000000000
## echo x.formatFloat(ffDecimal, 4) # 123.4560
## echo x.formatFloat(ffScientific, 2) # 1.23e+02
## doAssert x.formatFloat() == "123.4560000000000"
## doAssert x.formatFloat(ffDecimal, 4) == "123.4560"
## doAssert x.formatFloat(ffScientific, 2) == "1.23e+02"
##
result = formatBiggestFloat(f, format, precision, decimalSep)
@ -2471,12 +2471,14 @@ when isMainModule:
outp = " this is a\nlong text\n--\nmuchlongerthan10chars\nand here\nit goes"
doAssert wordWrap(inp, 10, false) == outp
doAssert formatBiggestFloat(1234.567, ffDecimal, -1) == "1234.567000"
doAssert formatBiggestFloat(1234.567, ffDecimal, 0) == "1235."
doAssert formatBiggestFloat(1234.567, ffDecimal, 1) == "1234.6"
doAssert formatBiggestFloat(0.00000000001, ffDecimal, 11) == "0.00000000001"
doAssert formatBiggestFloat(0.00000000001, ffScientific, 1, ',') in
["1,0e-11", "1,0e-011"]
# bug #6589
doAssert formatFloat(123.456, ffScientific, precision=0) in
["1.234560e+02", "1.234560e+002"]
doAssert formatFloat(123.456, ffScientific, precision = -1) == "1.234560e+02"
doAssert "$# $3 $# $#" % ["a", "b", "c"] == "a c b c"
doAssert "${1}12 ${-1}$2" % ["a", "b"] == "a12 bb"

View file

@ -21,7 +21,7 @@ proc name*(t: typedesc): string {.magic: "TypeTrait".}
##
## proc `$`*(T: typedesc): string = name(T)
##
## template test(x): stmt =
## template test(x): typed =
## echo "type: ", type(x), ", value: ", x
##
## test 42

View file

@ -308,11 +308,16 @@ proc `$`*(u: Uri): string =
result.add(":")
result.add(u.password)
result.add("@")
result.add(u.hostname)
if u.hostname.endswith('/'):
result.add(u.hostname[0..^2])
else:
result.add(u.hostname)
if u.port.len > 0:
result.add(":")
result.add(u.port)
if u.path.len > 0:
if u.hostname.len > 0 and u.path[0] != '/':
result.add('/')
result.add(u.path)
if u.query.len > 0:
result.add("?")
@ -483,6 +488,34 @@ when isMainModule:
let foo = parseUri("http://localhost:9515") / "status"
doAssert $foo == "http://localhost:9515/status"
# bug #6649 #6652
block:
var foo = parseUri("http://example.com")
foo.hostname = "example.com"
foo.path = "baz"
doAssert $foo == "http://example.com/baz"
foo.hostname = "example.com/"
foo.path = "baz"
doAssert $foo == "http://example.com/baz"
foo.hostname = "example.com"
foo.path = "/baz"
doAssert $foo == "http://example.com/baz"
foo.hostname = "example.com/"
foo.path = "/baz"
doAssert $foo == "http://example.com/baz"
foo.hostname = "example.com/"
foo.port = "8000"
foo.path = "baz"
doAssert $foo == "http://example.com:8000/baz"
foo = parseUri("file:/dir/file")
foo.path = "relative"
doAssert $foo == "file:relative"
# isAbsolute tests
block:
doAssert "www.google.com".parseUri().isAbsolute() == false
@ -524,4 +557,4 @@ when isMainModule:
doAssert "https://example.com/about/staff.html?".parseUri().isAbsolute == true
doAssert "https://example.com/about/staff.html?parameters".parseUri().isAbsolute == true
echo("All good!")
echo("All good!")

View file

@ -456,6 +456,13 @@ type
WriteIOEffect* = object of IOEffect ## Effect describing a write IO operation.
ExecIOEffect* = object of IOEffect ## Effect describing an executing IO operation.
StackTraceEntry* = object ## In debug mode exceptions store the stack trace that led
## to them. A StackTraceEntry is a single entry of the
## stack trace.
procname*: cstring ## name of the proc that is currently executing
line*: int ## line number of the proc that is currently executing
filename*: cstring ## filename of the proc that is currently executing
Exception* {.compilerproc.} = object of RootObj ## \
## Base exception class.
##
@ -468,7 +475,10 @@ type
msg* {.exportc: "message".}: string ## the exception's message. Not
## providing an exception message
## is bad style.
trace: string
when defined(js):
trace: string
else:
trace: seq[StackTraceEntry]
up: ref Exception # used for stacking exceptions. Not exported!
SystemError* = object of Exception ## \
@ -895,7 +905,7 @@ proc `div` *(x, y: int8): int8 {.magic: "DivI", noSideEffect.}
proc `div` *(x, y: int16): int16 {.magic: "DivI", noSideEffect.}
proc `div` *(x, y: int32): int32 {.magic: "DivI", noSideEffect.}
## computes the integer division. This is roughly the same as
## ``floor(x/y)``.
## ``trunc(x/y)``.
##
## .. code-block:: Nim
## 1 div 2 == 0
@ -1097,7 +1107,7 @@ proc `*`*[T: SomeUnsignedInt](x, y: T): T {.magic: "MulU", noSideEffect.}
proc `div`*[T: SomeUnsignedInt](x, y: T): T {.magic: "DivU", noSideEffect.}
## computes the integer division. This is roughly the same as
## ``floor(x/y)``.
## ``trunc(x/y)``.
##
## .. code-block:: Nim
## (7 div 5) == 1
@ -2015,19 +2025,19 @@ when defined(nimNewRoof):
yield res
inc(res)
iterator `..`*(a, b: int64): int64 {.inline.} =
## A special version of ``..`` for ``int64`` only.
var res = a
while res <= b:
yield res
inc(res)
template dotdotImpl(t) {.dirty.} =
iterator `..`*(a, b: t): t {.inline.} =
## A type specialized version of ``..`` for convenience so that
## mixing integer types work better.
var res = a
while res <= b:
yield res
inc(res)
iterator `..`*(a, b: int32): int32 {.inline.} =
## A special version of ``..`` for ``int32`` only.
var res = a
while res <= b:
yield res
inc(res)
dotdotImpl(int64)
dotdotImpl(int32)
dotdotImpl(uint64)
dotdotImpl(uint32)
else:
iterator countup*[S, T](a: S, b: T, step = 1): T {.inline.} =
@ -2763,9 +2773,9 @@ when defined(nimvarargstyped):
## pretends to be free of side effects, so that it can be used for debugging
## routines marked as `noSideEffect <manual.html#pragmas-nosideeffect-pragma>`_.
else:
proc echo*(x: varargs[expr, `$`]) {.magic: "Echo", tags: [WriteIOEffect],
proc echo*(x: varargs[untyped, `$`]) {.magic: "Echo", tags: [WriteIOEffect],
benign, sideEffect.}
proc debugEcho*(x: varargs[expr, `$`]) {.magic: "Echo", noSideEffect,
proc debugEcho*(x: varargs[untyped, `$`]) {.magic: "Echo", noSideEffect,
tags: [], raises: [].}
template newException*(exceptn: typedesc, message: string;
@ -3706,7 +3716,7 @@ proc instantiationInfo*(index = -1, fullPaths = false): tuple[
## .. code-block:: nim
## import strutils
##
## template testException(exception, code: expr): stmt =
## template testException(exception, code: untyped): typed =
## try:
## let pos = instantiationInfo()
## discard(code)
@ -3844,11 +3854,11 @@ type
{.deprecated: [PNimrodNode: NimNode].}
when false:
template eval*(blk: stmt): stmt =
template eval*(blk: typed): typed =
## executes a block of code at compile time just as if it was a macro
## optionally, the block can return an AST tree that will replace the
## eval expression
macro payload: stmt {.gensym.} = blk
macro payload: typed {.gensym.} = blk
payload()
when hasAlloc:

View file

@ -213,12 +213,20 @@ proc atomicDec*(memLoc: var int, x: int = 1): int =
result = memLoc
when defined(vcc):
proc interlockedCompareExchange64(p: pointer; exchange, comparand: int64): int64
{.importc: "_InterlockedCompareExchange64", header: "<intrin.h>".}
proc interlockedCompareExchange32(p: pointer; exchange, comparand: int32): int32
{.importc: "_InterlockedCompareExchange", header: "<intrin.h>".}
proc interlockedCompareExchange8(p: pointer; exchange, comparand: byte): byte
{.importc: "_InterlockedCompareExchange64", header: "<intrin.h>".}
when defined(cpp):
proc interlockedCompareExchange64(p: pointer; exchange, comparand: int64): int64
{.importcpp: "_InterlockedCompareExchange64(static_cast<NI64 volatile *>(#), #, #)", header: "<intrin.h>".}
proc interlockedCompareExchange32(p: pointer; exchange, comparand: int32): int32
{.importcpp: "_InterlockedCompareExchange(static_cast<NI volatile *>(#), #, #)", header: "<intrin.h>".}
proc interlockedCompareExchange8(p: pointer; exchange, comparand: byte): byte
{.importcpp: "_InterlockedCompareExchange8(static_cast<char volatile *>(#), #, #)", header: "<intrin.h>".}
else:
proc interlockedCompareExchange64(p: pointer; exchange, comparand: int64): int64
{.importc: "_InterlockedCompareExchange64", header: "<intrin.h>".}
proc interlockedCompareExchange32(p: pointer; exchange, comparand: int32): int32
{.importc: "_InterlockedCompareExchange", header: "<intrin.h>".}
proc interlockedCompareExchange8(p: pointer; exchange, comparand: byte): byte
{.importc: "_InterlockedCompareExchange8", header: "<intrin.h>".}
proc cas*[T: bool|int|ptr](p: ptr T; oldValue, newValue: T): bool =
when sizeof(T) == 8:

View file

@ -370,7 +370,7 @@ proc commandPrompt() =
if dbgUser.len == 0: dbgUser.len = oldLen
# now look what we have to do:
var i = scanWord(addr dbgUser.data, dbgTemp, 0)
template `?`(x: expr): expr = dbgTemp == cstring(x)
template `?`(x: untyped): untyped = dbgTemp == cstring(x)
if ?"s" or ?"step":
dbgState = dbStepInto
again = false

View file

@ -155,6 +155,52 @@ when not hasThreadSupport:
var
tempFrames: array[0..127, PFrame] # should not be alloc'd on stack
const
reraisedFromBegin = -10
reraisedFromEnd = -100
template reraisedFrom(z): untyped =
StackTraceEntry(procname: nil, line: z, filename: nil)
proc auxWriteStackTrace(f: PFrame; s: var seq[StackTraceEntry]) =
var
it = f
i = 0
while it != nil:
inc(i)
it = it.prev
var last = i-1
if s.isNil:
s = newSeq[StackTraceEntry](i)
else:
last = s.len + i - 1
s.setLen(last+1)
it = f
while it != nil:
s[last] = StackTraceEntry(procname: it.procname,
line: it.line,
filename: it.filename)
it = it.prev
dec last
template addFrameEntry(s, f: untyped) =
var oldLen = s.len
add(s, f.filename)
if f.line > 0:
add(s, '(')
add(s, $f.line)
add(s, ')')
for k in 1..max(1, 25-(s.len-oldLen)): add(s, ' ')
add(s, f.procname)
add(s, "\n")
proc `$`(s: seq[StackTraceEntry]): string =
result = newStringOfCap(2000)
for i in 0 .. s.len-1:
if s[i].line == reraisedFromBegin: result.add "[[reraised from:\n"
elif s[i].line == reraisedFromEnd: result.add "]]\n"
else: addFrameEntry(result, s[i])
proc auxWriteStackTrace(f: PFrame, s: var string) =
when hasThreadSupport:
var
@ -194,17 +240,9 @@ proc auxWriteStackTrace(f: PFrame, s: var string) =
if tempFrames[j] == nil:
add(s, "(")
add(s, $skipped)
add(s, " calls omitted) ...")
add(s, " calls omitted) ...\n")
else:
var oldLen = s.len
add(s, tempFrames[j].filename)
if tempFrames[j].line > 0:
add(s, '(')
add(s, $tempFrames[j].line)
add(s, ')')
for k in 1..max(1, 25-(s.len-oldLen)): add(s, ' ')
add(s, tempFrames[j].procname)
add(s, "\n")
addFrameEntry(s, tempFrames[j])
proc stackTraceAvailable*(): bool
@ -221,6 +259,13 @@ when hasSomeStackTrace:
auxWriteStackTraceWithBacktrace(s)
else:
add(s, "No stack traceback available\n")
proc rawWriteStackTrace(s: var seq[StackTraceEntry]) =
when NimStackTrace:
auxWriteStackTrace(framePtr, s)
else:
s = nil
proc stackTraceAvailable(): bool =
when NimStackTrace:
if framePtr == nil:
@ -240,12 +285,6 @@ proc quitOrDebug() {.inline.} =
else:
endbStep() # call the debugger
when false:
proc rawRaise*(e: ref Exception) =
## undocumented. Do not use.
pushCurrentException(e)
c_longjmp(excHandler.context, 1)
var onUnhandledException*: (proc (errorMsg: string) {.
nimcall.}) ## set this error \
## handler to override the existing behaviour on an unhandled exception.
@ -282,7 +321,7 @@ proc raiseExceptionAux(e: ref Exception) =
when hasSomeStackTrace:
var buf = newStringOfCap(2000)
if isNil(e.trace): rawWriteStackTrace(buf)
else: add(buf, e.trace)
else: add(buf, $e.trace)
add(buf, "Error: unhandled exception: ")
if not isNil(e.msg): add(buf, e.msg)
add(buf, " [")
@ -318,12 +357,11 @@ proc raiseException(e: ref Exception, ename: cstring) {.compilerRtl.} =
if e.name.isNil: e.name = ename
when hasSomeStackTrace:
if e.trace.isNil:
e.trace = ""
rawWriteStackTrace(e.trace)
elif framePtr != nil:
e.trace.add "[[reraised from:\n"
e.trace.add reraisedFrom(reraisedFromBegin)
auxWriteStackTrace(framePtr, e.trace)
e.trace.add "]]\n"
e.trace.add reraisedFrom(reraisedFromEnd)
raiseExceptionAux(e)
proc reraiseException() {.compilerRtl.} =
@ -349,10 +387,15 @@ proc getStackTrace(): string =
proc getStackTrace(e: ref Exception): string =
if not isNil(e) and not isNil(e.trace):
result = e.trace
result = $e.trace
else:
result = ""
proc getStackTraceEntries*(e: ref Exception): seq[StackTraceEntry] =
## Returns the attached stack trace to the exception ``e`` as
## a ``seq``. This is not yet available for the JS backend.
shallowCopy(result, e.trace)
when defined(nimRequiresNimFrame):
proc stackOverflow() {.noinline.} =
writeStackTrace()

View file

@ -173,7 +173,7 @@ proc internRefcount(p: pointer): int {.exportc: "getRefcount".} =
when BitsPerPage mod (sizeof(int)*8) != 0:
{.error: "(BitsPerPage mod BitsPerUnit) should be zero!".}
template color(c): expr = c.refCount and colorMask
template color(c): untyped = c.refCount and colorMask
template setColor(c, col) =
c.refcount = c.refcount and not colorMask or col

View file

@ -153,12 +153,12 @@ proc preferSpawn*(): bool =
## it is not necessary to call this directly; use 'spawnX' instead.
result = gSomeReady.event
proc spawn*(call: stmt) {.magic: "Spawn".}
proc spawn*(call: typed) {.magic: "Spawn".}
## always spawns a new task, so that the 'call' is never executed on
## the calling thread. 'call' has to be proc call 'p(...)' where 'p'
## is gcsafe and has 'void' as the return type.
template spawnX*(call: stmt) =
template spawnX*(call: typed) =
## spawns a new task if a CPU core is ready, otherwise executes the
## call in the calling thread. Usually it is advised to
## use 'spawn' in order to not block the producer for an unknown

View file

@ -322,37 +322,40 @@ proc nimIntToStr(x: int): string {.compilerRtl.} =
result.add x
proc add*(result: var string; x: float) =
var buf: array[0..64, char]
when defined(nimNoArrayToCstringConversion):
var n: int = c_sprintf(addr buf, "%.16g", x)
when nimvm:
result.add $x
else:
var n: int = c_sprintf(buf, "%.16g", x)
var hasDot = false
for i in 0..n-1:
if buf[i] == ',':
buf[i] = '.'
hasDot = true
elif buf[i] in {'a'..'z', 'A'..'Z', '.'}:
hasDot = true
if not hasDot:
buf[n] = '.'
buf[n+1] = '0'
buf[n+2] = '\0'
# On Windows nice numbers like '1.#INF', '-1.#INF' or '1.#NAN'
# of '-1.#IND' are produced.
# We want to get rid of these here:
if buf[n-1] in {'n', 'N', 'D', 'd'}:
result.add "nan"
elif buf[n-1] == 'F':
if buf[0] == '-':
result.add "-inf"
var buf: array[0..64, char]
when defined(nimNoArrayToCstringConversion):
var n: int = c_sprintf(addr buf, "%.16g", x)
else:
result.add "inf"
else:
var i = 0
while buf[i] != '\0':
result.add buf[i]
inc i
var n: int = c_sprintf(buf, "%.16g", x)
var hasDot = false
for i in 0..n-1:
if buf[i] == ',':
buf[i] = '.'
hasDot = true
elif buf[i] in {'a'..'z', 'A'..'Z', '.'}:
hasDot = true
if not hasDot:
buf[n] = '.'
buf[n+1] = '0'
buf[n+2] = '\0'
# On Windows nice numbers like '1.#INF', '-1.#INF' or '1.#NAN'
# of '-1.#IND' are produced.
# We want to get rid of these here:
if buf[n-1] in {'n', 'N', 'D', 'd'}:
result.add "nan"
elif buf[n-1] == 'F':
if buf[0] == '-':
result.add "-inf"
else:
result.add "inf"
else:
var i = 0
while buf[i] != '\0':
result.add buf[i]
inc i
proc nimFloatToStr(f: float): string {.compilerproc.} =
result = newStringOfCap(8)