Merge branch 'master' of github.com:Araq/Nimrod

This commit is contained in:
Araq 2012-09-13 18:48:14 +02:00
commit 4a435a8fb4
6 changed files with 299 additions and 233 deletions

View file

@ -370,23 +370,22 @@ proc typeRel(c: var TCandidate, f, a: PType): TTypeRelation =
of tyVar: of tyVar:
if a.kind == f.kind: result = typeRel(c, base(f), base(a)) if a.kind == f.kind: result = typeRel(c, base(f), base(a))
else: result = typeRel(c, base(f), a) else: result = typeRel(c, base(f), a)
of tyArray, tyArrayConstr: of tyArray, tyArrayConstr:
# tyArrayConstr cannot happen really, but # tyArrayConstr cannot happen really, but
# we wanna be safe here # we wanna be safe here
case a.kind case a.kind
of tyArray: of tyArray, tyArrayConstr:
result = minRel(typeRel(c, f.sons[0], a.sons[0]), var fRange = f.sons[0]
typeRel(c, f.sons[1], a.sons[1])) if fRange.kind == tyGenericParam:
if result < isGeneric: result = isNone var prev = PType(idTableGet(c.bindings, fRange))
of tyArrayConstr: if prev == nil:
put(c.bindings, fRange, a.sons[0])
fRange = a
else:
fRange = prev
result = typeRel(c, f.sons[1], a.sons[1]) result = typeRel(c, f.sons[1], a.sons[1])
if result < isGeneric: if result < isGeneric: result = isNone
result = isNone elif lengthOrd(fRange) != lengthOrd(a): result = isNone
else:
if (result != isGeneric) and (lengthOrd(f) != lengthOrd(a)):
result = isNone
elif f.sons[0].kind in GenericTypes:
result = minRel(result, typeRel(c, f.sons[0], a.sons[0]))
else: nil else: nil
of tyOpenArray, tyVarargs: of tyOpenArray, tyVarargs:
case a.Kind case a.Kind

View file

@ -119,6 +119,8 @@ type
handleAccept*: proc (s: PAsyncSocket) {.closure.} handleAccept*: proc (s: PAsyncSocket) {.closure.}
handleTask*: proc (s: PAsyncSocket) {.closure.}
lineBuffer: TaintedString ## Temporary storage for ``recvLine`` lineBuffer: TaintedString ## Temporary storage for ``recvLine``
sslNeedAccept: bool sslNeedAccept: bool
proto: TProtocol proto: TProtocol
@ -145,6 +147,7 @@ proc newAsyncSocket(): PAsyncSocket =
result.handleRead = (proc (s: PAsyncSocket) = nil) result.handleRead = (proc (s: PAsyncSocket) = nil)
result.handleConnect = (proc (s: PAsyncSocket) = nil) result.handleConnect = (proc (s: PAsyncSocket) = nil)
result.handleAccept = (proc (s: PAsyncSocket) = nil) result.handleAccept = (proc (s: PAsyncSocket) = nil)
result.handleTask = (proc (s: PAsyncSocket) = nil)
result.lineBuffer = "".TaintedString result.lineBuffer = "".TaintedString
@ -196,6 +199,13 @@ when defined(ssl):
# handshake will set socket's ``sslNoHandshake`` field. # handshake will set socket's ``sslNoHandshake`` field.
discard PAsyncSocket(h).socket.handshake() discard PAsyncSocket(h).socket.handshake()
proc asyncSockTask(h: PObject) =
when defined(ssl):
h.asyncSockDoHandshake()
PAsyncSocket(h).handleTask(PAsyncSocket(h))
proc toDelegate(sock: PAsyncSocket): PDelegate = proc toDelegate(sock: PAsyncSocket): PDelegate =
result = newDelegate() result = newDelegate()
result.deleVal = sock result.deleVal = sock
@ -204,6 +214,7 @@ proc toDelegate(sock: PAsyncSocket): PDelegate =
result.mode = fmReadWrite result.mode = fmReadWrite
result.handleRead = asyncSockHandleRead result.handleRead = asyncSockHandleRead
result.handleWrite = asyncSockHandleWrite result.handleWrite = asyncSockHandleWrite
result.task = asyncSockTask
# TODO: Errors? # TODO: Errors?
#result.handleError = (proc (h: PObject) = assert(false)) #result.handleError = (proc (h: PObject) = assert(false))
@ -215,10 +226,7 @@ proc toDelegate(sock: PAsyncSocket): PDelegate =
if sock.info notin {SockIdle, SockClosed}: if sock.info notin {SockIdle, SockClosed}:
sock.deleg.open = true sock.deleg.open = true
else: else:
sock.deleg.open = false sock.deleg.open = false
when defined(ssl):
result.task = asyncSockDoHandshake
proc connect*(sock: PAsyncSocket, name: string, port = TPort(0), proc connect*(sock: PAsyncSocket, name: string, port = TPort(0),
af: TDomain = AF_INET) = af: TDomain = AF_INET) =
@ -257,6 +265,7 @@ proc acceptAddr*(server: PAsyncSocket, client: var PAsyncSocket,
## ##
## **Note**: ``client`` needs to be initialised. ## **Note**: ``client`` needs to be initialised.
assert(client != nil) assert(client != nil)
client = newAsyncSocket()
var c: TSocket var c: TSocket
new(c) new(c)
when defined(ssl): when defined(ssl):

View file

@ -44,10 +44,8 @@ type
PAsyncIRC* = ref TAsyncIRC PAsyncIRC* = ref TAsyncIRC
TAsyncIRC* = object of TIRC TAsyncIRC* = object of TIRC
userArg: PObject handleEvent: proc (irc: var TAsyncIRC, ev: TIRCEvent) {.closure.}
handleEvent: proc (irc: var TAsyncIRC, ev: TIRCEvent, asyncSock: PAsyncSocket
userArg: PObject) {.nimcall.}
lineBuffer: TaintedString
TIRCMType* = enum TIRCMType* = enum
MUnknown, MUnknown,
@ -320,12 +318,16 @@ proc connect*(irc: PAsyncIRC) =
assert(irc.address != "") assert(irc.address != "")
assert(irc.port != TPort(0)) assert(irc.port != TPort(0))
irc.sock = socket() irc.asyncSock = AsyncSocket()
irc.sock.setBlocking(false) irc.asyncSock.connect(irc.address, irc.port)
irc.sock.connectAsync(irc.address, irc.port)
irc.status = SockConnecting
proc handleConnect(h: PObject) = proc handleConnect(s: PAsyncSocket, irc: PAsyncIRC) =
# Greet the server :)
if irc.serverPass != "": irc[].send("PASS " & irc.serverPass, true)
irc[].send("NICK " & irc.nick, true)
irc[].send("USER $1 * 0 :$2" % [irc.user, irc.realname], true)
discard """proc handleConnect(h: PObject) =
var irc = PAsyncIRC(h) var irc = PAsyncIRC(h)
# Greet the server :) # Greet the server :)
@ -334,8 +336,22 @@ proc handleConnect(h: PObject) =
irc[].send("USER $1 * 0 :$2" % [irc.user, irc.realname], true) irc[].send("USER $1 * 0 :$2" % [irc.user, irc.realname], true)
irc.status = SockConnected irc.status = SockConnected
"""
proc handleRead(h: PObject) = proc handleRead(s: PAsyncSocket, irc: PAsyncIRC) =
var line = "".TaintedString
var ret = s.recvLine(line)
if ret:
if line == "":
var ev: TIRCEvent
irc[].close()
ev.typ = EvDisconnected
irc.handleEvent(irc[], ev)
else:
var ev = irc[].processLine(line.string)
irc.handleEvent(irc[], ev)
discard """proc handleRead(h: PObject) =
var irc = PAsyncIRC(h) var irc = PAsyncIRC(h)
var line = "".TaintedString var line = "".TaintedString
var ret = irc.sock.recvLineAsync(line) var ret = irc.sock.recvLineAsync(line)
@ -352,13 +368,18 @@ proc handleRead(h: PObject) =
irc[].close() irc[].close()
ev.typ = EvDisconnected ev.typ = EvDisconnected
irc.handleEvent(irc[], ev, irc.userArg) irc.handleEvent(irc[], ev, irc.userArg)
of RecvFail: nil of RecvFail: nil"""
proc handleTask(h: PObject) = proc handleTask(s: PAsyncSocket, irc: PAsyncIRC) =
var ev: TIRCEvent
if irc[].processOther(ev):
irc.handleEvent(irc[], ev)
discard """proc handleTask(h: PObject) =
var irc = PAsyncIRC(h) var irc = PAsyncIRC(h)
var ev: TIRCEvent var ev: TIRCEvent
if PAsyncIRC(h)[].processOther(ev): if PAsyncIRC(h)[].processOther(ev):
irc.handleEvent(irc[], ev, irc.userArg) irc.handleEvent(irc[], ev, irc.userArg)"""
proc asyncIRC*(address: string, port: TPort = 6667.TPort, proc asyncIRC*(address: string, port: TPort = 6667.TPort,
nick = "NimrodBot", nick = "NimrodBot",
@ -366,9 +387,8 @@ proc asyncIRC*(address: string, port: TPort = 6667.TPort,
realname = "NimrodBot", serverPass = "", realname = "NimrodBot", serverPass = "",
joinChans: seq[string] = @[], joinChans: seq[string] = @[],
msgLimit: bool = true, msgLimit: bool = true,
ircEvent: proc (irc: var TAsyncIRC, ev: TIRCEvent, ircEvent: proc (irc: var TAsyncIRC, ev: TIRCEvent) {.closure.}
userArg: PObject) {.nimcall.}, ): PAsyncIRC =
userArg: PObject = nil): PAsyncIRC =
## Use this function if you want to use asyncio's dispatcher. ## Use this function if you want to use asyncio's dispatcher.
## ##
## **Note:** Do **NOT** use this if you're writing a simple IRC bot which only ## **Note:** Do **NOT** use this if you're writing a simple IRC bot which only
@ -389,28 +409,25 @@ proc asyncIRC*(address: string, port: TPort = 6667.TPort,
result.msgLimit = msgLimit result.msgLimit = msgLimit
result.messageBuffer = @[] result.messageBuffer = @[]
result.handleEvent = ircEvent result.handleEvent = ircEvent
result.userArg = userArg
result.lineBuffer = ""
proc register*(d: PDispatcher, irc: PAsyncIRC) = proc register*(d: PDispatcher, irc: PAsyncIRC) =
## Registers ``irc`` with dispatcher ``d``. ## Registers ``irc`` with dispatcher ``d``.
var dele = newDelegate() irc.asyncSock.handleConnect =
dele.deleVal = irc proc (s: PAsyncSocket) =
dele.getSocket = (proc (h: PObject): tuple[info: TInfo, sock: TSocket] = handleConnect(s, irc)
if PAsyncIRC(h).status == SockConnecting or irc.asyncSock.handleRead =
PAsyncIRC(h).status == SockConnected: proc (s: PAsyncSocket) =
return (PAsyncIRC(h).status, PAsyncIRC(h).sock) handleRead(s, irc)
else: return (SockIdle, PAsyncIRC(h).sock)) irc.asyncSock.handleTask =
dele.handleConnect = handleConnect proc (s: PAsyncSocket) =
dele.handleRead = handleRead handleTask(s, irc)
dele.task = handleTask d.register(irc.asyncSock)
d.register(dele)
when isMainModule: when isMainModule:
#var m = parseMessage("ERROR :Closing Link: dom96.co.cc (Ping timeout: 252 seconds)") #var m = parseMessage("ERROR :Closing Link: dom96.co.cc (Ping timeout: 252 seconds)")
#echo(repr(m)) #echo(repr(m))
#discard """
var client = irc("amber.tenthbit.net", nick="TestBot1234", var client = irc("amber.tenthbit.net", nick="TestBot1234",
joinChans = @["#flood"]) joinChans = @["#flood"])
@ -431,5 +448,5 @@ when isMainModule:
#echo( repr(event) ) #echo( repr(event) )
#echo("Lag: ", formatFloat(client.getLag())) #echo("Lag: ", formatFloat(client.getLag()))
#"""

View file

@ -1,187 +1,215 @@
# #
# #
# Nimrod's Runtime Library # Nimrod's Runtime Library
# (c) Copyright 2012 Nimrod Contributors # (c) Copyright 2012 Nimrod Contributors
# #
# See the file "copying.txt", included in this # See the file "copying.txt", included in this
# distribution, for details about the copyright. # distribution, for details about the copyright.
# #
## :Author: Zahary Karadjov (zah@github) ## :Author: Zahary Karadjov
## ##
## This module implements the standard unit testing facilities such as ## This module implements the standard unit testing facilities such as
## suites, fixtures and test cases as well as facilities for combinatorial ## suites, fixtures and test cases as well as facilities for combinatorial
## and randomzied test case generation (not yet available) ## and randomzied test case generation (not yet available)
## and object mocking (not yet available) ## and object mocking (not yet available)
## ##
## It is loosely based on C++'s boost.test and Haskell's QuickTest ## It is loosely based on C++'s boost.test and Haskell's QuickTest
import import
macros, terminal, os macros, terminal, os
type type
TTestStatus* = enum OK, FAILED TTestStatus* = enum OK, FAILED
TOutputLevel* = enum PRINT_ALL, PRINT_FAILURES, PRINT_NONE TOutputLevel* = enum PRINT_ALL, PRINT_FAILURES, PRINT_NONE
var var
# XXX: These better be thread-local # XXX: These better be thread-local
AbortOnError*: bool AbortOnError*: bool
OutputLevel*: TOutputLevel OutputLevel*: TOutputLevel
ColorOutput*: bool ColorOutput*: bool
checkpoints: seq[string] = @[]
template TestSetupIMPL*: stmt {.immediate, dirty.} = nil
template TestTeardownIMPL*: stmt {.immediate, dirty.} = nil
proc shouldRun(testName: string): bool =
result = true
template suite*(name: expr, body: stmt): stmt {.immediate, dirty.} =
block:
template setup*(setupBody: stmt): stmt {.immediate, dirty.} =
template TestSetupIMPL: stmt {.immediate, dirty.} = setupBody
template teardown*(teardownBody: stmt): stmt {.immediate, dirty.} =
template TestTeardownIMPL: stmt {.immediate, dirty.} = teardownBody
body
proc testDone(name: string, s: TTestStatus) =
if s == FAILED:
program_result += 1
if OutputLevel != PRINT_NONE and (OutputLevel == PRINT_ALL or s == FAILED):
var color = (if s == OK: fgGreen else: fgRed)
if ColorOutput:
styledEcho styleBright, color, "[", $s, "] ", fgWhite, name, "\n"
else:
echo "[", $s, "] ", name, "\n"
template test*(name: expr, body: stmt): stmt {.immediate, dirty.} =
bind shouldRun, checkpoints, testDone
if shouldRun(name):
checkpoints = @[]
var TestStatusIMPL {.inject.} = OK
try:
TestSetupIMPL()
body
except:
checkpoint("Unhandled exception: " & getCurrentExceptionMsg())
fail()
finally:
TestTeardownIMPL()
testDone name, TestStatusIMPL
proc checkpoint*(msg: string) =
checkpoints.add(msg)
# TODO: add support for something like SCOPED_TRACE from Google Test
template fail* =
bind checkpoints
for msg in items(checkpoints):
echo msg
if AbortOnError: quit(1)
TestStatusIMPL = FAILED
checkpoints = @[]
macro check*(conditions: stmt): stmt {.immediate.} =
let conditions = callsite()
proc standardRewrite(e: PNimrodNode): PNimrodNode = checkpoints: seq[string] = @[]
template rewrite(Exp, lineInfoLit: expr, expLit: string): stmt =
if not Exp: template TestSetupIMPL*: stmt {.immediate, dirty.} = nil
checkpoint(lineInfoLit & ": Check failed: " & expLit) template TestTeardownIMPL*: stmt {.immediate, dirty.} = nil
fail()
proc shouldRun(testName: string): bool =
result = getAst(rewrite(e, e.lineinfo, e.toStrLit)) result = true
case conditions.kind template suite*(name: expr, body: stmt): stmt {.immediate, dirty.} =
of nnkCall, nnkCommand, nnkMacroStmt: block:
case conditions[1].kind template setup*(setupBody: stmt): stmt {.immediate, dirty.} =
of nnkInfix: template TestSetupIMPL: stmt {.immediate, dirty.} = setupBody
proc rewriteBinaryOp(op: PNimrodNode): PNimrodNode =
template rewrite(op, left, right, lineInfoLit: expr, opLit, template teardown*(teardownBody: stmt): stmt {.immediate, dirty.} =
leftLit, rightLit: string, printLhs, printRhs: bool): stmt = template TestTeardownIMPL: stmt {.immediate, dirty.} = teardownBody
block:
var body
lhs = left
rhs = right proc testDone(name: string, s: TTestStatus) =
if s == FAILED:
if not `op`(lhs, rhs): program_result += 1
checkpoint(lineInfoLit & ": Check failed: " & opLit)
when printLhs: checkpoint(" " & leftLit & " was " & $lhs) if OutputLevel != PRINT_NONE and (OutputLevel == PRINT_ALL or s == FAILED):
when printRhs: checkpoint(" " & rightLit & " was " & $rhs) var color = (if s == OK: fgGreen else: fgRed)
fail()
if ColorOutput:
result = getAst(rewrite( styledEcho styleBright, color, "[", $s, "] ", fgWhite, name, "\n"
op[0], op[1], op[2], else:
op.lineinfo, echo "[", $s, "] ", name, "\n"
op.toStrLit,
op[1].toStrLit, template test*(name: expr, body: stmt): stmt {.immediate, dirty.} =
op[2].toStrLit, bind shouldRun, checkpoints, testDone
op[1].kind notin nnkLiterals,
op[2].kind notin nnkLiterals)) if shouldRun(name):
checkpoints = @[]
result = rewriteBinaryOp(conditions[1]) var TestStatusIMPL {.inject.} = OK
of nnkCall, nnkCommand: try:
# TODO: We can print out the call arguments in case of failure TestSetupIMPL()
result = standardRewrite(conditions[1]) body
of nnkStmtList: except:
result = newNimNode(nnkStmtList) checkpoint("Unhandled exception: " & getCurrentExceptionMsg())
for i in countup(0, conditions[1].len - 1): fail()
result.add(newCall(!"check", conditions[1][i]))
finally:
else: TestTeardownIMPL()
result = standardRewrite(conditions[1]) testDone name, TestStatusIMPL
else: proc checkpoint*(msg: string) =
var ast = conditions.treeRepr checkpoints.add(msg)
error conditions.lineinfo & ": Malformed check statement:\n" & ast # TODO: add support for something like SCOPED_TRACE from Google Test
template require*(conditions: stmt): stmt {.immediate, dirty.} = template fail* =
block: bind checkpoints
const AbortOnError {.inject.} = true for msg in items(checkpoints):
check conditions echo msg
macro expect*(exp: stmt): stmt {.immediate.} = if AbortOnError: quit(1)
TestStatusIMPL = FAILED
checkpoints = @[]
macro check*(conditions: stmt): stmt {.immediate.} =
let conditions = callsite()
case conditions.kind
of nnkCall, nnkCommand, nnkMacroStmt:
case conditions[1].kind
of nnkInfix:
proc rewriteBinaryOp(op: PNimrodNode): PNimrodNode =
template rewrite(op, left, right, lineInfoLit: expr, opLit,
leftLit, rightLit: string, printLhs, printRhs: bool): stmt =
block:
var
lhs = left
rhs = right
if not `op`(lhs, rhs):
checkpoint(lineInfoLit & ": Check failed: " & opLit)
when printLhs: checkpoint(" " & leftLit & " was " & $lhs)
when printRhs: checkpoint(" " & rightLit & " was " & $rhs)
fail()
result = getAst(rewrite(
op[0], op[1], op[2],
op.lineinfo,
op.toStrLit,
op[1].toStrLit,
op[2].toStrLit,
op[1].kind notin nnkLiterals,
op[2].kind notin nnkLiterals))
result = rewriteBinaryOp(conditions[1])
of nnkCall, nnkCommand:
proc rewriteCall(op: PNimrodNode): PNimrodNode =
template rewrite(call, lineInfoLit: expr, expLit: string,
argAssgs, argPrintOuts: stmt): stmt =
block:
argAssgs
if not call:
checkpoint(lineInfoLit & ": Check failed: " & expLit)
argPrintOuts
fail()
template asgn(a, value: expr): stmt =
let a = value
template print(name, value: expr): stmt =
checkpoint(name & " was " & $value)
var
argsAsgns = newNimNode(nnkStmtList)
argsPrintOuts = newNimNode(nnkStmtList)
opStr = op.toStrLit
for i in 1 .. <op.len:
if op[i].kind notin nnkLiterals:
# TODO: print only types that are printable
var arg = newIdentNode(":param" & ($i))
argsAsgns.add getAst(asgn(arg, op[i]))
argsPrintOuts.add getAst(print(op[i].toStrLit, arg))
op[i] = arg
result = getAst(rewrite(op, op.lineinfo, opStr, argsAsgns, argsPrintOuts))
result = rewriteCall(conditions[1])
of nnkStmtList:
result = newNimNode(nnkStmtList)
for i in countup(0, conditions[1].len - 1):
result.add(newCall(!"check", conditions[1][i]))
else:
template rewrite(Exp, lineInfoLit: expr, expLit: string): stmt =
if not Exp:
checkpoint(lineInfoLit & ": Check failed: " & expLit)
fail()
let e = conditions[1]
result = getAst(rewrite(e, e.lineinfo, e.toStrLit))
else:
var ast = conditions.treeRepr
error conditions.lineinfo & ": Malformed check statement:\n" & ast
template require*(conditions: stmt): stmt {.immediate, dirty.} =
block:
const AbortOnError {.inject.} = true
check conditions
macro expect*(exp: stmt): stmt {.immediate.} =
let exp = callsite() let exp = callsite()
template expectBody(errorTypes, lineInfoLit: expr, template expectBody(errorTypes, lineInfoLit: expr,
body: stmt): PNimrodNode {.dirty.} = body: stmt): PNimrodNode {.dirty.} =
try: try:
body body
checkpoint(lineInfoLit & ": Expect Failed, no exception was thrown.") checkpoint(lineInfoLit & ": Expect Failed, no exception was thrown.")
fail() fail()
except errorTypes: except errorTypes:
nil nil
var expectCall = exp[0] var expectCall = exp[0]
var body = exp[1] var body = exp[1]
var errorTypes = newNimNode(nnkBracket) var errorTypes = newNimNode(nnkBracket)
for i in countup(1, expectCall.len - 1): for i in countup(1, expectCall.len - 1):
errorTypes.add(expectCall[i]) errorTypes.add(expectCall[i])
result = getAst(expectBody(errorTypes, exp.lineinfo, body)) result = getAst(expectBody(errorTypes, exp.lineinfo, body))
## Reading settings ## Reading settings
var envOutLvl = os.getEnv("NIMTEST_OUTPUT_LVL").string var envOutLvl = os.getEnv("NIMTEST_OUTPUT_LVL").string
if envOutLvl.len > 0: if envOutLvl.len > 0:
for opt in countup(low(TOutputLevel), high(TOutputLevel)): for opt in countup(low(TOutputLevel), high(TOutputLevel)):
if $opt == envOutLvl: if $opt == envOutLvl:
OutputLevel = opt OutputLevel = opt
break break
AbortOnError = existsEnv("NIMTEST_ABORT_ON_ERROR") AbortOnError = existsEnv("NIMTEST_ABORT_ON_ERROR")
ColorOutput = not existsEnv("NIMTEST_NO_COLOR") ColorOutput = not existsEnv("NIMTEST_NO_COLOR")

View file

@ -1,4 +1,4 @@
var s = @[]
proc `*` *(a, b: seq[int]): seq[int] = proc `*` *(a, b: seq[int]): seq[int] =
# allocate a new sequence: # allocate a new sequence:

View file

@ -0,0 +1,13 @@
discard """
msg: "type mismatch: got (array[0..2, float], array[0..1, float])"
"""
proc `+`*[R, T] (v1, v2: array[R, T]): array[R, T] =
for i in low(v1)..high(v1):
result[i] = v1[i] + v2[i]
var
v1: array[0..2, float] = [3.0, 1.2, 3.0]
v2: array[0..1, float] = [2.0, 1.0]
v3 = v1 + v2