Merge branch 'devel' of https://github.com/Araq/Nimrod into new_spawn

Conflicts:
	lib/system.nim
This commit is contained in:
Araq 2014-06-26 17:19:28 +02:00
commit 85a1d896c2
40 changed files with 385 additions and 114 deletions

View file

@ -150,6 +150,15 @@ proc sort*[T](a: var openArray[T],
## # overload:
## sort(myStrArray, system.cmp)
##
## You can inline adhoc comparison procs with the `do notation
## <manual.html#do-notation>`_. Example:
##
## .. code-block:: nimrod
##
## people.sort do (x, y: Person) -> int:
## result = cmp(x.surname, y.surname)
## if result == 0:
## result = cmp(x.name, y.name)
var n = a.len
var b: seq[T]
newSeq(b, n div 2)

View file

@ -27,9 +27,11 @@ export TPort
# TODO: Discarded void PFutures need to checked for exception.
# TODO: Exceptions are currently uncatchable due to the limitation that
# you cannot have yield in a try stmt. Perhaps I can get the macro to put
# a user's try except around ``future.read``.
# TODO: ``except`` statement (without `try`) does not work.
# TODO: Multiple exception names in a ``except`` don't work.
# TODO: The effect system (raises: []) has trouble with my try transformation.
# TODO: Can't await in a 'except' body
# -- Futures
@ -922,14 +924,17 @@ proc getName(node: PNimrodNode): string {.compileTime.} =
return $node[1].ident
of nnkIdent:
return $node.ident
of nnkEmpty:
return "anonymous"
else:
assert false
error("Unknown name.")
macro async*(prc: stmt): stmt {.immediate.} =
## Macro which processes async procedures into the appropriate
## iterators and yield statements.
expectKind(prc, nnkProcDef)
if prc.kind notin {nnkProcDef, nnkLambda}:
error("Cannot transform this node kind into an async proc." &
" Proc definition or lambda node expected.")
hint("Processing " & prc[0].getName & " as an async proc.")
@ -941,7 +946,9 @@ macro async*(prc: stmt): stmt {.immediate.} =
if $returnType[0] != "PFuture":
error("Expected return type of 'PFuture' got '" & $returnType[0] & "'")
let subtypeIsVoid = returnType.kind == nnkEmpty
let subtypeIsVoid = returnType.kind == nnkEmpty or
(returnType.kind == nnkBracketExpr and
returnType[1].kind == nnkIdent and returnType[1].ident == !"void")
var outerProcBody = newNimNode(nnkStmtList)
@ -990,17 +997,19 @@ macro async*(prc: stmt): stmt {.immediate.} =
# Remove the 'async' pragma.
for i in 0 .. <result[4].len:
if result[4][i].ident == !"async":
if result[4][i].kind == nnkIdent and result[4][i].ident == !"async":
result[4].del(i)
if subtypeIsVoid:
# Add discardable pragma.
result[4].add(newIdentNode("discardable"))
if prc.kind == nnkProcDef: # TODO: This is a workaround for #1287
result[4].add(newIdentNode("discardable"))
if returnType.kind == nnkEmpty:
# Add PFuture[void]
result[3][0] = parseExpr("PFuture[void]")
result[6] = outerProcBody
#echo(treeRepr(result))
#echo(toStrLit(result))
proc recvLine*(socket: TAsyncFD): PFuture[string] {.async.} =

View file

@ -14,12 +14,13 @@
import strtabs, asyncnet, asyncdispatch, parseutils, parseurl, strutils
type
TRequest* = object
client: PAsyncSocket # TODO: Separate this into a Response object?
client*: PAsyncSocket # TODO: Separate this into a Response object?
reqMethod*: string
headers*: PStringTable
protocol*: tuple[orig: string, major, minor: int]
url*: TURL
hostname*: string ## The hostname of the client that made the request.
body*: string # TODO
PAsyncHttpServer* = ref object
socket: PAsyncSocket
@ -169,6 +170,10 @@ proc serve*(server: PAsyncHttpServer, port: TPort,
var fut = await server.socket.acceptAddr()
processClient(fut.client, fut.address, callback)
proc close*(server: PAsyncHttpServer) =
## Terminates the async http server instance.
server.socket.close()
when isMainModule:
var server = newAsyncHttpServer()
proc cb(req: TRequest) {.async.} =

View file

@ -59,7 +59,7 @@ proc enqueue*[T](q: var TQueue[T], item: T) =
proc dequeue*[T](q: var TQueue[T]): T =
## removes and returns the first element of the queue `q`.
assert q.len > 0
assert q.count > 0
dec q.count
result = q.data[q.rd]
q.rd = (q.rd + 1) and q.mask

View file

@ -37,7 +37,8 @@
## ## Piggyback on the already available string hash proc.
## ##
## ## Without this proc nothing works!
## result = hash(x.firstName & x.lastName)
## result = x.firstName.hash !& x.lastName.hash
## result = !$result
##
## var
## salaries = initTable[Person, int]()
@ -145,6 +146,14 @@ proc mget*[A, B](t: var TTable[A, B], key: A): var B =
if index >= 0: result = t.data[index].val
else: raise newException(EInvalidKey, "key not found: " & $key)
iterator allValues*[A, B](t: TTable[A, B]; key: A): B =
## iterates over any value in the table `t` that belongs to the given `key`.
var h: THash = hash(key) and high(t.data)
while t.data[h].slot != seEmpty:
if t.data[h].key == key and t.data[h].slot == seFilled:
yield t.data[h].val
h = nextTry(h, high(t.data))
proc hasKey*[A, B](t: TTable[A, B], key: A): bool =
## returns true iff `key` is in the table `t`.
result = rawGet(t, key) >= 0
@ -313,8 +322,7 @@ proc newTable*[A, B](initialSize=64): PTable[A, B] =
new(result)
result[] = initTable[A, B](initialSize)
proc newTable*[A, B](pairs: openArray[tuple[key: A,
val: B]]): PTable[A, B] =
proc newTable*[A, B](pairs: openArray[tuple[key: A, val: B]]): PTable[A, B] =
## creates a new hash table that contains the given `pairs`.
new(result)
result[] = toTable[A, B](pairs)
@ -841,7 +849,8 @@ when isMainModule:
## Piggyback on the already available string hash proc.
##
## Without this proc nothing works!
result = hash(x.firstName & x.lastName)
result = x.firstName.hash !& x.lastName.hash
result = !$result
var
salaries = initTable[Person, int]()

View file

@ -119,8 +119,8 @@ proc getEvent(m: PFSMonitor, fd: cint): seq[TMonitorEvent] =
var mev: TMonitorEvent
mev.wd = event.wd
if event.len.int != 0:
mev.name = newString(event.len.int)
copyMem(addr(mev.name[0]), addr event.name, event.len.int-1)
let cstr = event.name.addr.cstring
mev.name = $cstr
else:
mev.name = ""
@ -211,4 +211,4 @@ when isMainModule:
while true:
if not disp.poll(): break

View file

@ -8,7 +8,34 @@
#
## This module implements efficient computations of hash values for diverse
## Nimrod types.
## Nimrod types. All the procs are based on these two building blocks: the `!&
## proc <#!&>`_ used to start or mix a hash value, and the `!$ proc <#!$>`_
## used to *finish* the hash value. If you want to implement hash procs for
## your custom types you will end up writing the following kind of skeleton of
## code:
##
## .. code-block:: nimrod
## proc hash(x: Something): THash =
## ## Computes a THash from `x`.
## var h: THash = 0
## # Iterate over parts of `x`.
## for xAtom in x:
## # Mix the atom with the partial hash.
## h = h !& xAtom
## # Finish the hash.
## result = !$h
##
## If your custom types contain fields for which there already is a hash proc,
## like for example objects made up of ``strings``, you can simply hash
## together the hash value of the individual fields:
##
## .. code-block:: nimrod
## proc hash(x: Something): THash =
## ## Computes a THash from `x`.
## var h: THash = 0
## h = h &! hash(x.foo)
## h = h &! hash(x.bar)
## result = !$h
import
strutils

View file

@ -619,6 +619,44 @@ proc `%`*(elements: openArray[PJsonNode]): PJsonNode =
newSeq(result.elems, elements.len)
for i, p in pairs(elements): result.elems[i] = p
proc `==`* (a,b: PJsonNode): bool =
## Check two nodes for equality
if a.kind != b.kind: false
else:
case a.kind
of JString:
a.str == b.str
of JInt:
a.num == b.num
of JFloat:
a.fnum == b.fnum
of JBool:
a.bval == b.bval
of JNull:
true
of JArray:
a.elems == b.elems
of JObject:
a.fields == b.fields
proc hash* (n:PJsonNode): THash =
## Compute the hash for a JSON node
case n.kind
of JArray:
result = hash(n.elems)
of JObject:
result = hash(n.fields)
of JInt:
result = hash(n.num)
of JFloat:
result = hash(n.fnum)
of JBool:
result = hash(n.bval.int)
of JString:
result = hash(n.str)
of JNull:
result = hash(0)
proc len*(n: PJsonNode): int =
## If `n` is a `JArray`, it returns the number of elements.
## If `n` is a `JObject`, it returns the number of pairs.
@ -631,7 +669,7 @@ proc len*(n: PJsonNode): int =
proc `[]`*(node: PJsonNode, name: string): PJsonNode =
## Gets a field from a `JObject`, which must not be nil.
## If the value at `name` does not exist, returns nil
assert(node != nil)
assert(not isNil(node))
assert(node.kind == JObject)
for key, item in items(node.fields):
if key == name:
@ -641,8 +679,8 @@ proc `[]`*(node: PJsonNode, name: string): PJsonNode =
proc `[]`*(node: PJsonNode, index: int): PJsonNode =
## Gets the node at `index` in an Array. Result is undefined if `index`
## is out of bounds
assert(not isNil(node))
assert(node.kind == JArray)
assert(node != nil)
return node.elems[index]
proc hasKey*(node: PJsonNode, key: string): bool =
@ -675,14 +713,12 @@ proc `[]=`*(obj: PJsonNode, key: string, val: PJsonNode) =
return
obj.fields.add((key, val))
proc `{}`*(node: PJsonNode, names: varargs[string]): PJsonNode =
proc `{}`*(node: PJsonNode, key: string): PJsonNode =
## Transverses the node and gets the given value. If any of the
## names does not exist, returns nil
result = node
for name in names:
result = result[name]
if isNil(result):
return nil
if isNil(node): return nil
result = result[key]
proc `{}=`*(node: PJsonNode, names: varargs[string], value: PJsonNode) =
## Transverses the node and tries to set the value at the given location
@ -1021,7 +1057,7 @@ when isMainModule:
let testJson = parseJson"""{ "a": [1, 2, 3, 4], "b": "asd" }"""
# nil passthrough
assert(testJson{"doesnt_exist", "anything"} == nil)
assert(testJson{"doesnt_exist"}{"anything"}.isNil)
testJson{["c", "d"]} = %true
assert(testJson["c"]["d"].bval)

View file

@ -11,7 +11,7 @@
{.deadCodeElim: on.}
import rawsockets, os, strutils, unsigned, parseutils, times
export TPort
export TPort, `$`
const useWinVersion = defined(Windows) or defined(nimdoc)

View file

@ -39,7 +39,6 @@ export
MSG_PEEK
type
TPort* = distinct uint16 ## port type
TDomain* = enum ## domain, which specifies the protocol family of the

View file

@ -90,6 +90,15 @@ proc defined*(x: expr): bool {.magic: "Defined", noSideEffect.}
## when not defined(strutils.toUpper):
## # provide our own toUpper proc here, because strutils is
## # missing it.
##
## You can also check external symbols introduced through the compiler's
## `-d:x switch <nimrodc.html#compile-time-symbols>`_ to enable build time
## conditionals:
##
## .. code-block:: Nimrod
## when not defined(release):
## # Do here programmer friendly expensive sanity checks.
## # Put here the normal code
when defined(useNimRtl):
{.deadCodeElim: on.}
@ -1769,9 +1778,38 @@ iterator fields*[S:tuple|object, T:tuple|object](x: S, y: T): tuple[a,b: expr] {
## in the loop body.
iterator fieldPairs*[T: tuple|object](x: T): TObject {.
magic: "FieldPairs", noSideEffect.}
## iterates over every field of `x`. Warning: This really transforms
## the 'for' and unrolls the loop. The current implementation also has a bug
## that affects symbol binding in the loop body.
## Iterates over every field of `x` returning their name and value.
##
## When you iterate over objects with different field types you have to use
## the compile time ``when`` instead of a runtime ``if`` to select the code
## you want to run for each type. To perform the comparison use the `is
## operator <manual.html#is-operator>`_. Example:
##
## .. code-block:: Nimrod
##
## type
## Custom = object
## foo: string
## bar: bool
##
## proc `$`(x: Custom): string =
## result = "Custom:"
## for name, value in x.fieldPairs:
## when value is bool:
## result.add("\n\t" & name & " is " & $value)
## else:
## if value.isNil:
## result.add("\n\t" & name & " (nil)")
## else:
## result.add("\n\t" & name & " '" & value & "'")
##
## Another way to do the same without ``when`` is to leave the task of
## picking the appropriate code to a secondary proc which you overload for
## each field type and pass the `value` to.
##
## Warning: This really transforms the 'for' and unrolls the loop. The
## current implementation also has a bug that affects symbol binding in the
## loop body.
iterator fieldPairs*[S: tuple|object, T: tuple|object](x: S, y: T): tuple[
a, b: expr] {.
magic: "FieldPairs", noSideEffect.}
@ -2785,10 +2823,15 @@ when true:
THide(raiseAssert)(msg)
template assert*(cond: bool, msg = "") =
## provides a means to implement `programming by contracts`:idx: in Nimrod.
## Raises ``EAssertionFailure`` with `msg` if `cond` is false.
##
## Provides a means to implement `programming by contracts`:idx: in Nimrod.
## ``assert`` evaluates expression ``cond`` and if ``cond`` is false, it
## raises an ``EAssertionFailure`` exception. However, the compiler may
## not generate any code at all for ``assert`` if it is advised to do so.
## raises an ``EAssertionFailure`` exception. However, the compiler may not
## generate any code at all for ``assert`` if it is advised to do so through
## the ``-d:release`` or ``--assertions:off`` `command line switches
## <nimrodc.html#command-line-switches>`_.
##
## Use ``assert`` for debugging purposes only.
bind instantiationInfo
mixin failedAssertImpl

View file

@ -67,6 +67,28 @@ proc chckObjAsgn(a, b: PNimType) {.compilerproc, inline.} =
if a != b:
sysFatal(EInvalidObjectAssignment, "invalid object assignment")
type ObjCheckCache = array[0..1, PNimType]
proc isObjSlowPath(obj, subclass: PNimType;
cache: var ObjCheckCache): bool {.noinline.} =
# checks if obj is of type subclass:
var x = obj.base
while x != subclass:
if x == nil:
cache[0] = obj
return false
x = x.base
cache[1] = obj
return true
proc isObjWithCache(obj, subclass: PNimType;
cache: var ObjCheckCache): bool {.compilerProc, inline.} =
if obj == subclass: return true
if obj.base == subclass: return true
if cache[0] == obj: return false
if cache[1] == obj: return true
return isObjSlowPath(obj, subclass, cache)
proc isObj(obj, subclass: PNimType): bool {.compilerproc.} =
# checks if obj is of type subclass:
var x = obj