Merge branch 'devel' of https://github.com/Araq/Nimrod into devel
This commit is contained in:
commit
e662013ee0
8 changed files with 89 additions and 555 deletions
|
|
@ -833,8 +833,8 @@ proc genSwap(p: PProc, n: PNode) =
|
||||||
"local $1 = $2; $2 = $3; $3 = $1;$n", [
|
"local $1 = $2; $2 = $3; $3 = $1;$n", [
|
||||||
tmp, a.address, b.address])
|
tmp, a.address, b.address])
|
||||||
tmp = tmp2
|
tmp = tmp2
|
||||||
appf(p.body, "var $1 = $2; $2 = $3; $3 = $1" |
|
appf(p.body, "var $1 = $2; $2 = $3; $3 = $1;" |
|
||||||
"local $1 = $2; $2 = $3; $3 = $1", [tmp, a.res, b.res])
|
"local $1 = $2; $2 = $3; $3 = $1;", [tmp, a.res, b.res])
|
||||||
|
|
||||||
proc getFieldPosition(f: PNode): int =
|
proc getFieldPosition(f: PNode): int =
|
||||||
case f.kind
|
case f.kind
|
||||||
|
|
@ -881,14 +881,15 @@ proc genArrayAddr(p: PProc, n: PNode, r: var TCompRes) =
|
||||||
a, b: TCompRes
|
a, b: TCompRes
|
||||||
first: BiggestInt
|
first: BiggestInt
|
||||||
r.typ = etyBaseIndex
|
r.typ = etyBaseIndex
|
||||||
gen(p, n.sons[0], a)
|
let m = if n.kind == nkHiddenAddr: n.sons[0] else: n
|
||||||
gen(p, n.sons[1], b)
|
gen(p, m.sons[0], a)
|
||||||
|
gen(p, m.sons[1], b)
|
||||||
internalAssert a.typ != etyBaseIndex and b.typ != etyBaseIndex
|
internalAssert a.typ != etyBaseIndex and b.typ != etyBaseIndex
|
||||||
r.address = a.res
|
r.address = a.res
|
||||||
var typ = skipTypes(n.sons[0].typ, abstractPtrs)
|
var typ = skipTypes(m.sons[0].typ, abstractPtrs)
|
||||||
if typ.kind in {tyArray, tyArrayConstr}: first = firstOrd(typ.sons[0])
|
if typ.kind in {tyArray, tyArrayConstr}: first = firstOrd(typ.sons[0])
|
||||||
else: first = 0
|
else: first = 0
|
||||||
if optBoundsCheck in p.options and not isConstExpr(n.sons[1]):
|
if optBoundsCheck in p.options and not isConstExpr(m.sons[1]):
|
||||||
useMagic(p, "chckIndx")
|
useMagic(p, "chckIndx")
|
||||||
r.res = ropef("chckIndx($1, $2, $3.length)-$2",
|
r.res = ropef("chckIndx($1, $2, $3.length)-$2",
|
||||||
[b.res, toRope(first), a.res])
|
[b.res, toRope(first), a.res])
|
||||||
|
|
@ -1351,7 +1352,7 @@ proc genMagic(p: PProc, n: PNode, r: var TCompRes) =
|
||||||
of mEcho: genEcho(p, n, r)
|
of mEcho: genEcho(p, n, r)
|
||||||
of mSlurp, mStaticExec:
|
of mSlurp, mStaticExec:
|
||||||
localError(n.info, errXMustBeCompileTime, n.sons[0].sym.name.s)
|
localError(n.info, errXMustBeCompileTime, n.sons[0].sym.name.s)
|
||||||
of mCopyStr: binaryExpr(p, n, r, "", "($1.slice($2,-1))")
|
of mCopyStr: binaryExpr(p, n, r, "", "($1.slice($2))")
|
||||||
of mCopyStrLast: ternaryExpr(p, n, r, "", "($1.slice($2, ($3)+1).concat(0))")
|
of mCopyStrLast: ternaryExpr(p, n, r, "", "($1.slice($2, ($3)+1).concat(0))")
|
||||||
of mNewString: unaryExpr(p, n, r, "mnewString", "mnewString($1)")
|
of mNewString: unaryExpr(p, n, r, "mnewString", "mnewString($1)")
|
||||||
of mNewStringOfCap: unaryExpr(p, n, r, "mnewString", "mnewString(0)")
|
of mNewStringOfCap: unaryExpr(p, n, r, "mnewString", "mnewString(0)")
|
||||||
|
|
|
||||||
|
|
@ -81,6 +81,8 @@ proc notFoundError*(c: PContext, n: PNode, errors: seq[string]) =
|
||||||
if c.inCompilesContext > 0:
|
if c.inCompilesContext > 0:
|
||||||
# fail fast:
|
# fail fast:
|
||||||
globalError(n.info, errTypeMismatch, "")
|
globalError(n.info, errTypeMismatch, "")
|
||||||
|
if errors.len == 0:
|
||||||
|
localError(n.info, errExprXCannotBeCalled, n[0].renderTree)
|
||||||
var result = msgKindToString(errTypeMismatch)
|
var result = msgKindToString(errTypeMismatch)
|
||||||
add(result, describeArgs(c, n, 1))
|
add(result, describeArgs(c, n, 1))
|
||||||
add(result, ')')
|
add(result, ')')
|
||||||
|
|
|
||||||
503
lib/pure/irc.nim
503
lib/pure/irc.nim
|
|
@ -1,503 +0,0 @@
|
||||||
#
|
|
||||||
#
|
|
||||||
# Nimrod's Runtime Library
|
|
||||||
# (c) Copyright 2012 Dominik Picheta
|
|
||||||
# See the file "copying.txt", included in this
|
|
||||||
# distribution, for details about the copyright.
|
|
||||||
#
|
|
||||||
|
|
||||||
## This module implements an asynchronous IRC client.
|
|
||||||
##
|
|
||||||
## Currently this module requires at least some knowledge of the IRC protocol.
|
|
||||||
## It provides a function for sending raw messages to the IRC server, together
|
|
||||||
## with some basic functions like sending a message to a channel.
|
|
||||||
## It automizes the process of keeping the connection alive, so you don't
|
|
||||||
## need to reply to PING messages. In fact, the server is also PING'ed to check
|
|
||||||
## the amount of lag.
|
|
||||||
##
|
|
||||||
## .. code-block:: Nimrod
|
|
||||||
##
|
|
||||||
## var client = irc("picheta.me", joinChans = @["#bots"])
|
|
||||||
## client.connect()
|
|
||||||
## while True:
|
|
||||||
## var event: TIRCEvent
|
|
||||||
## if client.poll(event):
|
|
||||||
## case event.typ
|
|
||||||
## of EvConnected: nil
|
|
||||||
## of EvDisconnected:
|
|
||||||
## client.reconnect()
|
|
||||||
## of EvMsg:
|
|
||||||
## # Write your message reading code here.
|
|
||||||
##
|
|
||||||
## **Warning:** The API of this module is unstable, and therefore is subject
|
|
||||||
## to change.
|
|
||||||
|
|
||||||
include "system/inclrtl"
|
|
||||||
|
|
||||||
import sockets, strutils, parseutils, times, asyncio, os
|
|
||||||
|
|
||||||
type
|
|
||||||
TIRC* = object of TObject
|
|
||||||
address: string
|
|
||||||
port: TPort
|
|
||||||
nick, user, realname, serverPass: string
|
|
||||||
case isAsync: bool
|
|
||||||
of true:
|
|
||||||
handleEvent: proc (irc: PAsyncIRC, ev: TIRCEvent) {.closure, gcsafe.}
|
|
||||||
asyncSock: PAsyncSocket
|
|
||||||
myDispatcher: PDispatcher
|
|
||||||
of false:
|
|
||||||
dummyA: pointer
|
|
||||||
dummyB: pointer # workaround a Nimrod API issue
|
|
||||||
dummyC: pointer
|
|
||||||
sock: TSocket
|
|
||||||
status: TInfo
|
|
||||||
lastPing: float
|
|
||||||
lastPong: float
|
|
||||||
lag: float
|
|
||||||
channelsToJoin: seq[string]
|
|
||||||
msgLimit: bool
|
|
||||||
messageBuffer: seq[tuple[timeToSend: float, m: string]]
|
|
||||||
lastReconnect: float
|
|
||||||
|
|
||||||
PIRC* = ref TIRC
|
|
||||||
|
|
||||||
PAsyncIRC* = ref TAsyncIRC
|
|
||||||
TAsyncIRC* = object of TIRC
|
|
||||||
|
|
||||||
TIRCMType* = enum
|
|
||||||
MUnknown,
|
|
||||||
MNumeric,
|
|
||||||
MPrivMsg,
|
|
||||||
MJoin,
|
|
||||||
MPart,
|
|
||||||
MMode,
|
|
||||||
MTopic,
|
|
||||||
MInvite,
|
|
||||||
MKick,
|
|
||||||
MQuit,
|
|
||||||
MNick,
|
|
||||||
MNotice,
|
|
||||||
MPing,
|
|
||||||
MPong,
|
|
||||||
MError
|
|
||||||
|
|
||||||
TIRCEventType* = enum
|
|
||||||
EvMsg, EvConnected, EvDisconnected
|
|
||||||
TIRCEvent* = object ## IRC Event
|
|
||||||
case typ*: TIRCEventType
|
|
||||||
of EvConnected:
|
|
||||||
## Connected to server.
|
|
||||||
## Only occurs with AsyncIRC.
|
|
||||||
nil
|
|
||||||
of EvDisconnected:
|
|
||||||
## Disconnected from the server
|
|
||||||
nil
|
|
||||||
of EvMsg: ## Message from the server
|
|
||||||
cmd*: TIRCMType ## Command (e.g. PRIVMSG)
|
|
||||||
nick*, user*, host*, servername*: string
|
|
||||||
numeric*: string ## Only applies to ``MNumeric``
|
|
||||||
params*: seq[string] ## Parameters of the IRC message
|
|
||||||
origin*: string ## The channel/user that this msg originated from
|
|
||||||
raw*: string ## Raw IRC message
|
|
||||||
timestamp*: TTime ## UNIX epoch time the message was received
|
|
||||||
|
|
||||||
proc send*(irc: PIRC, message: string, sendImmediately = false) =
|
|
||||||
## Sends ``message`` as a raw command. It adds ``\c\L`` for you.
|
|
||||||
var sendMsg = true
|
|
||||||
if irc.msgLimit and not sendImmediately:
|
|
||||||
var timeToSend = epochTime()
|
|
||||||
if irc.messageBuffer.len() >= 3:
|
|
||||||
timeToSend = (irc.messageBuffer[irc.messageBuffer.len()-1][0] + 2.0)
|
|
||||||
|
|
||||||
irc.messageBuffer.add((timeToSend, message))
|
|
||||||
sendMsg = false
|
|
||||||
|
|
||||||
if sendMsg:
|
|
||||||
try:
|
|
||||||
if irc.isAsync:
|
|
||||||
irc.asyncSock.send(message & "\c\L")
|
|
||||||
else:
|
|
||||||
irc.sock.send(message & "\c\L")
|
|
||||||
except EOS:
|
|
||||||
# Assuming disconnection of every EOS could be bad,
|
|
||||||
# but I can't exactly check for EBrokenPipe.
|
|
||||||
irc.status = SockClosed
|
|
||||||
|
|
||||||
proc privmsg*(irc: PIRC, target, message: string) =
|
|
||||||
## Sends ``message`` to ``target``. ``Target`` can be a channel, or a user.
|
|
||||||
irc.send("PRIVMSG $1 :$2" % [target, message])
|
|
||||||
|
|
||||||
proc notice*(irc: PIRC, target, message: string) =
|
|
||||||
## Sends ``notice`` to ``target``. ``Target`` can be a channel, or a user.
|
|
||||||
irc.send("NOTICE $1 :$2" % [target, message])
|
|
||||||
|
|
||||||
proc join*(irc: PIRC, channel: string, key = "") =
|
|
||||||
## Joins ``channel``.
|
|
||||||
##
|
|
||||||
## If key is not ``""``, then channel is assumed to be key protected and this
|
|
||||||
## function will join the channel using ``key``.
|
|
||||||
if key == "":
|
|
||||||
irc.send("JOIN " & channel)
|
|
||||||
else:
|
|
||||||
irc.send("JOIN " & channel & " " & key)
|
|
||||||
|
|
||||||
proc part*(irc: PIRC, channel, message: string) =
|
|
||||||
## Leaves ``channel`` with ``message``.
|
|
||||||
irc.send("PART " & channel & " :" & message)
|
|
||||||
|
|
||||||
proc close*(irc: PIRC) =
|
|
||||||
## Closes connection to an IRC server.
|
|
||||||
##
|
|
||||||
## **Warning:** This procedure does not send a ``QUIT`` message to the server.
|
|
||||||
irc.status = SockClosed
|
|
||||||
if irc.isAsync:
|
|
||||||
irc.asyncSock.close()
|
|
||||||
else:
|
|
||||||
irc.sock.close()
|
|
||||||
|
|
||||||
proc isNumber(s: string): bool =
|
|
||||||
## Checks if `s` contains only numbers.
|
|
||||||
var i = 0
|
|
||||||
while s[i] in {'0'..'9'}: inc(i)
|
|
||||||
result = i == s.len and s.len > 0
|
|
||||||
|
|
||||||
proc parseMessage(msg: string): TIRCEvent =
|
|
||||||
result.typ = EvMsg
|
|
||||||
result.cmd = MUnknown
|
|
||||||
result.raw = msg
|
|
||||||
result.timestamp = times.getTime()
|
|
||||||
var i = 0
|
|
||||||
# Process the prefix
|
|
||||||
if msg[i] == ':':
|
|
||||||
inc(i) # Skip `:`
|
|
||||||
var nick = ""
|
|
||||||
i.inc msg.parseUntil(nick, {'!', ' '}, i)
|
|
||||||
result.nick = ""
|
|
||||||
result.serverName = ""
|
|
||||||
if msg[i] == '!':
|
|
||||||
result.nick = nick
|
|
||||||
inc(i) # Skip `!`
|
|
||||||
i.inc msg.parseUntil(result.user, {'@'}, i)
|
|
||||||
inc(i) # Skip `@`
|
|
||||||
i.inc msg.parseUntil(result.host, {' '}, i)
|
|
||||||
inc(i) # Skip ` `
|
|
||||||
else:
|
|
||||||
result.serverName = nick
|
|
||||||
inc(i) # Skip ` `
|
|
||||||
|
|
||||||
# Process command
|
|
||||||
var cmd = ""
|
|
||||||
i.inc msg.parseUntil(cmd, {' '}, i)
|
|
||||||
|
|
||||||
if cmd.isNumber:
|
|
||||||
result.cmd = MNumeric
|
|
||||||
result.numeric = cmd
|
|
||||||
else:
|
|
||||||
case cmd
|
|
||||||
of "PRIVMSG": result.cmd = MPrivMsg
|
|
||||||
of "JOIN": result.cmd = MJoin
|
|
||||||
of "PART": result.cmd = MPart
|
|
||||||
of "PONG": result.cmd = MPong
|
|
||||||
of "PING": result.cmd = MPing
|
|
||||||
of "MODE": result.cmd = MMode
|
|
||||||
of "TOPIC": result.cmd = MTopic
|
|
||||||
of "INVITE": result.cmd = MInvite
|
|
||||||
of "KICK": result.cmd = MKick
|
|
||||||
of "QUIT": result.cmd = MQuit
|
|
||||||
of "NICK": result.cmd = MNick
|
|
||||||
of "NOTICE": result.cmd = MNotice
|
|
||||||
of "ERROR": result.cmd = MError
|
|
||||||
else: result.cmd = MUnknown
|
|
||||||
|
|
||||||
# Don't skip space here. It is skipped in the following While loop.
|
|
||||||
|
|
||||||
# Params
|
|
||||||
result.params = @[]
|
|
||||||
var param = ""
|
|
||||||
while msg[i] != '\0' and msg[i] != ':':
|
|
||||||
inc(i) # Skip ` `.
|
|
||||||
i.inc msg.parseUntil(param, {' ', ':', '\0'}, i)
|
|
||||||
if param != "":
|
|
||||||
result.params.add(param)
|
|
||||||
param.setlen(0)
|
|
||||||
|
|
||||||
if msg[i] == ':':
|
|
||||||
inc(i) # Skip `:`.
|
|
||||||
result.params.add(msg[i..msg.len-1])
|
|
||||||
|
|
||||||
proc connect*(irc: PIRC) =
|
|
||||||
## Connects to an IRC server as specified by ``irc``.
|
|
||||||
assert(irc.address != "")
|
|
||||||
assert(irc.port != TPort(0))
|
|
||||||
|
|
||||||
irc.sock.connect(irc.address, irc.port)
|
|
||||||
|
|
||||||
irc.status = SockConnected
|
|
||||||
|
|
||||||
# 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)
|
|
||||||
|
|
||||||
proc reconnect*(irc: PIRC, timeout = 5000) =
|
|
||||||
## Reconnects to an IRC server.
|
|
||||||
##
|
|
||||||
## ``Timeout`` specifies the time to wait in miliseconds between multiple
|
|
||||||
## consecutive reconnections.
|
|
||||||
##
|
|
||||||
## This should be used when an ``EvDisconnected`` event occurs.
|
|
||||||
let secSinceReconnect = int(epochTime() - irc.lastReconnect)
|
|
||||||
if secSinceReconnect < timeout:
|
|
||||||
sleep(timeout - secSinceReconnect)
|
|
||||||
irc.sock = socket()
|
|
||||||
if irc.sock == InvalidSocket: osError(osLastError())
|
|
||||||
irc.connect()
|
|
||||||
irc.lastReconnect = epochTime()
|
|
||||||
|
|
||||||
proc irc*(address: string, port: TPort = 6667.TPort,
|
|
||||||
nick = "NimrodBot",
|
|
||||||
user = "NimrodBot",
|
|
||||||
realname = "NimrodBot", serverPass = "",
|
|
||||||
joinChans: seq[string] = @[],
|
|
||||||
msgLimit: bool = true): PIRC =
|
|
||||||
## Creates a ``TIRC`` object.
|
|
||||||
new(result)
|
|
||||||
result.address = address
|
|
||||||
result.port = port
|
|
||||||
result.nick = nick
|
|
||||||
result.user = user
|
|
||||||
result.realname = realname
|
|
||||||
result.serverPass = serverPass
|
|
||||||
result.lastPing = epochTime()
|
|
||||||
result.lastPong = -1.0
|
|
||||||
result.lag = -1.0
|
|
||||||
result.channelsToJoin = joinChans
|
|
||||||
result.msgLimit = msgLimit
|
|
||||||
result.messageBuffer = @[]
|
|
||||||
result.status = SockIdle
|
|
||||||
result.sock = socket()
|
|
||||||
if result.sock == InvalidSocket: osError(osLastError())
|
|
||||||
|
|
||||||
proc processLine(irc: PIRC, line: string): TIRCEvent =
|
|
||||||
if line.len == 0:
|
|
||||||
irc.close()
|
|
||||||
result.typ = EvDisconnected
|
|
||||||
else:
|
|
||||||
result = parseMessage(line)
|
|
||||||
# Get the origin
|
|
||||||
result.origin = result.params[0]
|
|
||||||
if result.origin == irc.nick and
|
|
||||||
result.nick != "": result.origin = result.nick
|
|
||||||
|
|
||||||
if result.cmd == MError:
|
|
||||||
irc.close()
|
|
||||||
result.typ = EvDisconnected
|
|
||||||
return
|
|
||||||
|
|
||||||
if result.cmd == MPing:
|
|
||||||
irc.send("PONG " & result.params[0])
|
|
||||||
if result.cmd == MPong:
|
|
||||||
irc.lag = epochTime() - parseFloat(result.params[result.params.high])
|
|
||||||
irc.lastPong = epochTime()
|
|
||||||
if result.cmd == MNumeric:
|
|
||||||
if result.numeric == "001":
|
|
||||||
# Check the nickname.
|
|
||||||
if irc.nick != result.params[0]:
|
|
||||||
assert ' ' notin result.params[0]
|
|
||||||
irc.nick = result.params[0]
|
|
||||||
for chan in items(irc.channelsToJoin):
|
|
||||||
irc.join(chan)
|
|
||||||
if result.cmd == MNick:
|
|
||||||
if result.nick == irc.nick:
|
|
||||||
irc.nick = result.params[0]
|
|
||||||
|
|
||||||
proc processOther(irc: PIRC, ev: var TIRCEvent): bool =
|
|
||||||
result = false
|
|
||||||
if epochTime() - irc.lastPing >= 20.0:
|
|
||||||
irc.lastPing = epochTime()
|
|
||||||
irc.send("PING :" & formatFloat(irc.lastPing), true)
|
|
||||||
|
|
||||||
if epochTime() - irc.lastPong >= 120.0 and irc.lastPong != -1.0:
|
|
||||||
irc.close()
|
|
||||||
ev.typ = EvDisconnected # TODO: EvTimeout?
|
|
||||||
return true
|
|
||||||
|
|
||||||
for i in 0..irc.messageBuffer.len-1:
|
|
||||||
if epochTime() >= irc.messageBuffer[0][0]:
|
|
||||||
irc.send(irc.messageBuffer[0].m, true)
|
|
||||||
irc.messageBuffer.delete(0)
|
|
||||||
else:
|
|
||||||
break # messageBuffer is guaranteed to be from the quickest to the
|
|
||||||
# later-est.
|
|
||||||
|
|
||||||
proc poll*(irc: PIRC, ev: var TIRCEvent,
|
|
||||||
timeout: int = 500): bool =
|
|
||||||
## This function parses a single message from the IRC server and returns
|
|
||||||
## a TIRCEvent.
|
|
||||||
##
|
|
||||||
## This function should be called often as it also handles pinging
|
|
||||||
## the server.
|
|
||||||
##
|
|
||||||
## This function provides a somewhat asynchronous IRC implementation, although
|
|
||||||
## it should only be used for simple things for example an IRC bot which does
|
|
||||||
## not need to be running many time critical tasks in the background. If you
|
|
||||||
## require this, use the asyncio implementation.
|
|
||||||
|
|
||||||
if not (irc.status == SockConnected):
|
|
||||||
# Do not close the socket here, it is already closed!
|
|
||||||
ev.typ = EvDisconnected
|
|
||||||
var line = TaintedString""
|
|
||||||
var socks = @[irc.sock]
|
|
||||||
var ret = socks.select(timeout)
|
|
||||||
if ret == -1: osError(osLastError())
|
|
||||||
if socks.len() != 0 and ret != 0:
|
|
||||||
irc.sock.readLine(line)
|
|
||||||
ev = irc.processLine(line.string)
|
|
||||||
result = true
|
|
||||||
|
|
||||||
if processOther(irc, ev): result = true
|
|
||||||
|
|
||||||
proc getLag*(irc: PIRC): float =
|
|
||||||
## Returns the latency between this client and the IRC server in seconds.
|
|
||||||
##
|
|
||||||
## If latency is unknown, returns -1.0.
|
|
||||||
return irc.lag
|
|
||||||
|
|
||||||
proc isConnected*(irc: PIRC): bool =
|
|
||||||
## Returns whether this IRC client is connected to an IRC server.
|
|
||||||
return irc.status == SockConnected
|
|
||||||
|
|
||||||
proc getNick*(irc: PIRC): string =
|
|
||||||
## Returns the current nickname of the client.
|
|
||||||
return irc.nick
|
|
||||||
|
|
||||||
# -- Asyncio dispatcher
|
|
||||||
|
|
||||||
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)
|
|
||||||
irc.status = SockConnected
|
|
||||||
|
|
||||||
var ev: TIRCEvent
|
|
||||||
ev.typ = EvConnected
|
|
||||||
irc.handleEvent(irc, ev)
|
|
||||||
|
|
||||||
proc handleRead(s: PAsyncSocket, irc: PAsyncIRC) =
|
|
||||||
var line = "".TaintedString
|
|
||||||
var ret = s.readLine(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)
|
|
||||||
|
|
||||||
proc handleTask(s: PAsyncSocket, irc: PAsyncIRC) =
|
|
||||||
var ev: TIRCEvent
|
|
||||||
if irc.processOther(ev):
|
|
||||||
irc.handleEvent(irc, ev)
|
|
||||||
|
|
||||||
proc register*(d: PDispatcher, irc: PAsyncIRC) =
|
|
||||||
## Registers ``irc`` with dispatcher ``d``.
|
|
||||||
irc.asyncSock.handleConnect =
|
|
||||||
proc (s: PAsyncSocket) =
|
|
||||||
handleConnect(s, irc)
|
|
||||||
irc.asyncSock.handleRead =
|
|
||||||
proc (s: PAsyncSocket) =
|
|
||||||
handleRead(s, irc)
|
|
||||||
irc.asyncSock.handleTask =
|
|
||||||
proc (s: PAsyncSocket) =
|
|
||||||
handleTask(s, irc)
|
|
||||||
d.register(irc.asyncSock)
|
|
||||||
irc.myDispatcher = d
|
|
||||||
|
|
||||||
proc connect*(irc: PAsyncIRC) =
|
|
||||||
## Equivalent of connect for ``TIRC`` but specifically created for asyncio.
|
|
||||||
assert(irc.address != "")
|
|
||||||
assert(irc.port != TPort(0))
|
|
||||||
|
|
||||||
irc.asyncSock.connect(irc.address, irc.port)
|
|
||||||
|
|
||||||
proc reconnect*(irc: PAsyncIRC, timeout = 5000) =
|
|
||||||
## Reconnects to an IRC server.
|
|
||||||
##
|
|
||||||
## ``Timeout`` specifies the time to wait in miliseconds between multiple
|
|
||||||
## consecutive reconnections.
|
|
||||||
##
|
|
||||||
## This should be used when an ``EvDisconnected`` event occurs.
|
|
||||||
##
|
|
||||||
## When successfully reconnected an ``EvConnected`` event will occur.
|
|
||||||
let secSinceReconnect = int(epochTime() - irc.lastReconnect)
|
|
||||||
if secSinceReconnect < timeout:
|
|
||||||
sleep(timeout - secSinceReconnect)
|
|
||||||
irc.asyncSock = AsyncSocket()
|
|
||||||
irc.myDispatcher.register(irc)
|
|
||||||
irc.connect()
|
|
||||||
irc.lastReconnect = epochTime()
|
|
||||||
|
|
||||||
proc asyncIRC*(address: string, port: TPort = 6667.TPort,
|
|
||||||
nick = "NimrodBot",
|
|
||||||
user = "NimrodBot",
|
|
||||||
realname = "NimrodBot", serverPass = "",
|
|
||||||
joinChans: seq[string] = @[],
|
|
||||||
msgLimit: bool = true,
|
|
||||||
ircEvent: proc (irc: PAsyncIRC, ev: TIRCEvent) {.closure,gcsafe.}
|
|
||||||
): PAsyncIRC =
|
|
||||||
## 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
|
|
||||||
## requires one task to be run, i.e. this should not be used if you want a
|
|
||||||
## synchronous IRC client implementation, use ``irc`` for that.
|
|
||||||
|
|
||||||
new(result)
|
|
||||||
result.isAsync = true
|
|
||||||
result.address = address
|
|
||||||
result.port = port
|
|
||||||
result.nick = nick
|
|
||||||
result.user = user
|
|
||||||
result.realname = realname
|
|
||||||
result.serverPass = serverPass
|
|
||||||
result.lastPing = epochTime()
|
|
||||||
result.lastPong = -1.0
|
|
||||||
result.lag = -1.0
|
|
||||||
result.channelsToJoin = joinChans
|
|
||||||
result.msgLimit = msgLimit
|
|
||||||
result.messageBuffer = @[]
|
|
||||||
result.handleEvent = ircEvent
|
|
||||||
result.asyncSock = AsyncSocket()
|
|
||||||
|
|
||||||
when isMainModule:
|
|
||||||
#var m = parseMessage("ERROR :Closing Link: dom96.co.cc (Ping timeout: 252 seconds)")
|
|
||||||
#echo(repr(m))
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
var client = irc("amber.tenthbit.net", nick="TestBot1234",
|
|
||||||
joinChans = @["#flood"])
|
|
||||||
client.connect()
|
|
||||||
while true:
|
|
||||||
var event: TIRCEvent
|
|
||||||
if client.poll(event):
|
|
||||||
case event.typ
|
|
||||||
of EvConnected:
|
|
||||||
discard
|
|
||||||
of EvDisconnected:
|
|
||||||
break
|
|
||||||
of EvMsg:
|
|
||||||
if event.cmd == MPrivMsg:
|
|
||||||
var msg = event.params[event.params.high]
|
|
||||||
if msg == "|test": client.privmsg(event.origin, "hello")
|
|
||||||
if msg == "|excessFlood":
|
|
||||||
for i in 0..10:
|
|
||||||
client.privmsg(event.origin, "TEST" & $i)
|
|
||||||
|
|
||||||
#echo( repr(event) )
|
|
||||||
#echo("Lag: ", formatFloat(client.getLag()))
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -11,7 +11,7 @@
|
||||||
|
|
||||||
{.deadCodeElim: on.}
|
{.deadCodeElim: on.}
|
||||||
import rawsockets, os, strutils, unsigned, parseutils, times
|
import rawsockets, os, strutils, unsigned, parseutils, times
|
||||||
export TPort, `$`
|
export TPort, `$`, `==`
|
||||||
|
|
||||||
const useWinVersion = defined(Windows) or defined(nimdoc)
|
const useWinVersion = defined(Windows) or defined(nimdoc)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -365,8 +365,9 @@ when defined(windows):
|
||||||
template getFilename(f: expr): expr = $f.cFilename
|
template getFilename(f: expr): expr = $f.cFilename
|
||||||
|
|
||||||
proc skipFindData(f: TWIN32_FIND_DATA): bool {.inline.} =
|
proc skipFindData(f: TWIN32_FIND_DATA): bool {.inline.} =
|
||||||
|
# Note - takes advantage of null delimiter in the cstring
|
||||||
const dot = ord('.')
|
const dot = ord('.')
|
||||||
result = f.cFileName[0].int == dot and(f.cFileName[1].int == 0 or
|
result = f.cFileName[0].int == dot and (f.cFileName[1].int == 0 or
|
||||||
f.cFileName[1].int == dot and f.cFileName[2].int == 0)
|
f.cFileName[1].int == dot and f.cFileName[2].int == 0)
|
||||||
|
|
||||||
proc existsFile*(filename: string): bool {.rtl, extern: "nos$1",
|
proc existsFile*(filename: string): bool {.rtl, extern: "nos$1",
|
||||||
|
|
@ -1861,12 +1862,12 @@ proc expandTilde*(path: string): string =
|
||||||
|
|
||||||
when defined(Windows):
|
when defined(Windows):
|
||||||
type
|
type
|
||||||
DeviceId = int32
|
DeviceId* = int32
|
||||||
FileId = int64
|
FileId* = int64
|
||||||
else:
|
else:
|
||||||
type
|
type
|
||||||
DeviceId = TDev
|
DeviceId* = TDev
|
||||||
FileId = TIno
|
FileId* = TIno
|
||||||
|
|
||||||
type
|
type
|
||||||
FileInfo* = object
|
FileInfo* = object
|
||||||
|
|
@ -1909,6 +1910,7 @@ template rawToFormalFileInfo(rawInfo, formalInfo): expr =
|
||||||
if (rawInfo.dwFileAttributes and FILE_ATTRIBUTE_REPARSE_POINT) != 0'i32:
|
if (rawInfo.dwFileAttributes and FILE_ATTRIBUTE_REPARSE_POINT) != 0'i32:
|
||||||
formalInfo.kind = succ(result.kind)
|
formalInfo.kind = succ(result.kind)
|
||||||
|
|
||||||
|
|
||||||
else:
|
else:
|
||||||
template checkAndIncludeMode(rawMode, formalMode: expr) =
|
template checkAndIncludeMode(rawMode, formalMode: expr) =
|
||||||
if (rawInfo.st_mode and rawMode) != 0'i32:
|
if (rawInfo.st_mode and rawMode) != 0'i32:
|
||||||
|
|
@ -1995,4 +1997,26 @@ proc getFileInfo*(path: string, followSymlink = true): FileInfo =
|
||||||
osError(osLastError())
|
osError(osLastError())
|
||||||
rawToFormalFileInfo(rawInfo, result)
|
rawToFormalFileInfo(rawInfo, result)
|
||||||
|
|
||||||
|
proc isHidden*(path: string): bool =
|
||||||
|
## Determines whether a given path is hidden or not. Returns false if the
|
||||||
|
## file doesn't exist. The given path must be accessible from the current
|
||||||
|
## working directory of the program.
|
||||||
|
##
|
||||||
|
## On Windows, a file is hidden if the file's 'hidden' attribute is set.
|
||||||
|
## On Unix-like systems, a file is hidden if it starts with a '.' (period)
|
||||||
|
## and is not *just* '.' or '..' ' ."
|
||||||
|
when defined(Windows):
|
||||||
|
wrapUnary(attributes, getFileAttributesW, path)
|
||||||
|
if attributes != -1'i32:
|
||||||
|
result = (attributes and FILE_ATTRIBUTE_HIDDEN) != 0'i32
|
||||||
|
else:
|
||||||
|
if fileExists(path):
|
||||||
|
let
|
||||||
|
fileName = extractFilename(path)
|
||||||
|
nameLen = len(fileName)
|
||||||
|
if nameLen == 2:
|
||||||
|
result = (fileName[0] == '.') and (fileName[1] != '.')
|
||||||
|
elif nameLen > 2:
|
||||||
|
result = (fileName[0] == '.') and (fileName[3] != '.')
|
||||||
|
|
||||||
{.pop.}
|
{.pop.}
|
||||||
|
|
|
||||||
|
|
@ -63,44 +63,44 @@ elif defined(windows):
|
||||||
elif defined(JS):
|
elif defined(JS):
|
||||||
type
|
type
|
||||||
TTime* {.final, importc.} = object
|
TTime* {.final, importc.} = object
|
||||||
getDay: proc (): int {.tags: [], raises: [].}
|
getDay: proc (): int {.tags: [], raises: [], gcsafe.}
|
||||||
getFullYear: proc (): int {.tags: [], raises: [].}
|
getFullYear: proc (): int {.tags: [], raises: [], gcsafe.}
|
||||||
getHours: proc (): int {.tags: [], raises: [].}
|
getHours: proc (): int {.tags: [], raises: [], gcsafe.}
|
||||||
getMilliseconds: proc (): int {.tags: [], raises: [].}
|
getMilliseconds: proc (): int {.tags: [], raises: [], gcsafe.}
|
||||||
getMinutes: proc (): int {.tags: [], raises: [].}
|
getMinutes: proc (): int {.tags: [], raises: [], gcsafe.}
|
||||||
getMonth: proc (): int {.tags: [], raises: [].}
|
getMonth: proc (): int {.tags: [], raises: [], gcsafe.}
|
||||||
getSeconds: proc (): int {.tags: [], raises: [].}
|
getSeconds: proc (): int {.tags: [], raises: [], gcsafe.}
|
||||||
getTime: proc (): int {.tags: [], raises: [].}
|
getTime: proc (): int {.tags: [], raises: [], gcsafe.}
|
||||||
getTimezoneOffset: proc (): int {.tags: [], raises: [].}
|
getTimezoneOffset: proc (): int {.tags: [], raises: [], gcsafe.}
|
||||||
getDate: proc (): int {.tags: [], raises: [].}
|
getDate: proc (): int {.tags: [], raises: [], gcsafe.}
|
||||||
getUTCDate: proc (): int {.tags: [], raises: [].}
|
getUTCDate: proc (): int {.tags: [], raises: [], gcsafe.}
|
||||||
getUTCFullYear: proc (): int {.tags: [], raises: [].}
|
getUTCFullYear: proc (): int {.tags: [], raises: [], gcsafe.}
|
||||||
getUTCHours: proc (): int {.tags: [], raises: [].}
|
getUTCHours: proc (): int {.tags: [], raises: [], gcsafe.}
|
||||||
getUTCMilliseconds: proc (): int {.tags: [], raises: [].}
|
getUTCMilliseconds: proc (): int {.tags: [], raises: [], gcsafe.}
|
||||||
getUTCMinutes: proc (): int {.tags: [], raises: [].}
|
getUTCMinutes: proc (): int {.tags: [], raises: [], gcsafe.}
|
||||||
getUTCMonth: proc (): int {.tags: [], raises: [].}
|
getUTCMonth: proc (): int {.tags: [], raises: [], gcsafe.}
|
||||||
getUTCSeconds: proc (): int {.tags: [], raises: [].}
|
getUTCSeconds: proc (): int {.tags: [], raises: [], gcsafe.}
|
||||||
getUTCDay: proc (): int {.tags: [], raises: [].}
|
getUTCDay: proc (): int {.tags: [], raises: [], gcsafe.}
|
||||||
getYear: proc (): int {.tags: [], raises: [].}
|
getYear: proc (): int {.tags: [], raises: [], gcsafe.}
|
||||||
parse: proc (s: cstring): TTime {.tags: [], raises: [].}
|
parse: proc (s: cstring): TTime {.tags: [], raises: [], gcsafe.}
|
||||||
setDate: proc (x: int) {.tags: [], raises: [].}
|
setDate: proc (x: int) {.tags: [], raises: [], gcsafe.}
|
||||||
setFullYear: proc (x: int) {.tags: [], raises: [].}
|
setFullYear: proc (x: int) {.tags: [], raises: [], gcsafe.}
|
||||||
setHours: proc (x: int) {.tags: [], raises: [].}
|
setHours: proc (x: int) {.tags: [], raises: [], gcsafe.}
|
||||||
setMilliseconds: proc (x: int) {.tags: [], raises: [].}
|
setMilliseconds: proc (x: int) {.tags: [], raises: [], gcsafe.}
|
||||||
setMinutes: proc (x: int) {.tags: [], raises: [].}
|
setMinutes: proc (x: int) {.tags: [], raises: [], gcsafe.}
|
||||||
setMonth: proc (x: int) {.tags: [], raises: [].}
|
setMonth: proc (x: int) {.tags: [], raises: [], gcsafe.}
|
||||||
setSeconds: proc (x: int) {.tags: [], raises: [].}
|
setSeconds: proc (x: int) {.tags: [], raises: [], gcsafe.}
|
||||||
setTime: proc (x: int) {.tags: [], raises: [].}
|
setTime: proc (x: int) {.tags: [], raises: [], gcsafe.}
|
||||||
setUTCDate: proc (x: int) {.tags: [], raises: [].}
|
setUTCDate: proc (x: int) {.tags: [], raises: [], gcsafe.}
|
||||||
setUTCFullYear: proc (x: int) {.tags: [], raises: [].}
|
setUTCFullYear: proc (x: int) {.tags: [], raises: [], gcsafe.}
|
||||||
setUTCHours: proc (x: int) {.tags: [], raises: [].}
|
setUTCHours: proc (x: int) {.tags: [], raises: [], gcsafe.}
|
||||||
setUTCMilliseconds: proc (x: int) {.tags: [], raises: [].}
|
setUTCMilliseconds: proc (x: int) {.tags: [], raises: [], gcsafe.}
|
||||||
setUTCMinutes: proc (x: int) {.tags: [], raises: [].}
|
setUTCMinutes: proc (x: int) {.tags: [], raises: [], gcsafe.}
|
||||||
setUTCMonth: proc (x: int) {.tags: [], raises: [].}
|
setUTCMonth: proc (x: int) {.tags: [], raises: [], gcsafe.}
|
||||||
setUTCSeconds: proc (x: int) {.tags: [], raises: [].}
|
setUTCSeconds: proc (x: int) {.tags: [], raises: [], gcsafe.}
|
||||||
setYear: proc (x: int) {.tags: [], raises: [].}
|
setYear: proc (x: int) {.tags: [], raises: [], gcsafe.}
|
||||||
toGMTString: proc (): cstring {.tags: [], raises: [].}
|
toGMTString: proc (): cstring {.tags: [], raises: [], gcsafe.}
|
||||||
toLocaleString: proc (): cstring {.tags: [], raises: [].}
|
toLocaleString: proc (): cstring {.tags: [], raises: [], gcsafe.}
|
||||||
|
|
||||||
type
|
type
|
||||||
TTimeInfo* = object of TObject ## represents a time in different parts
|
TTimeInfo* = object of TObject ## represents a time in different parts
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
discard """
|
discard """
|
||||||
line: 7
|
line: 7
|
||||||
errormsg: "type mismatch"
|
errormsg: "expression 'items' cannot be called"
|
||||||
"""
|
"""
|
||||||
|
|
||||||
type a = enum b,c,d
|
type a = enum b,c,d
|
||||||
|
|
|
||||||
10
tests/misc/tissue710.nim
Normal file
10
tests/misc/tissue710.nim
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
discard """
|
||||||
|
file: "tissue710.nim"
|
||||||
|
line: 8
|
||||||
|
errorMsg: "expression '||' cannot be called"
|
||||||
|
"""
|
||||||
|
var sum = 0
|
||||||
|
for x in 3..1000:
|
||||||
|
if (x mod 3 == 0) || (x mod 5 == 0):
|
||||||
|
sum += x
|
||||||
|
echo(sum)
|
||||||
Loading…
Add table
Add a link
Reference in a new issue