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

This commit is contained in:
Araq 2012-12-02 11:13:05 +01:00
commit 6ce15652d3
16 changed files with 252 additions and 133 deletions

View file

@ -586,7 +586,8 @@ proc StrTableRawInsert(data: var TSymSeq, n: PSym) =
var h: THash = n.name.h and high(data) var h: THash = n.name.h and high(data)
while data[h] != nil: while data[h] != nil:
if data[h] == n: if data[h] == n:
InternalError(n.info, "StrTableRawInsert: " & n.name.s) # allowed for 'export' feature:
#InternalError(n.info, "StrTableRawInsert: " & n.name.s)
return return
h = nextTry(h, high(data)) h = nextTry(h, high(data))
assert(data[h] == nil) assert(data[h] == nil)
@ -617,7 +618,7 @@ proc StrTableAdd(t: var TStrTable, n: PSym) =
StrTableRawInsert(t.data, n) StrTableRawInsert(t.data, n)
inc(t.counter) inc(t.counter)
proc StrTableIncl*(t: var TStrTable, n: PSym): bool = proc StrTableIncl*(t: var TStrTable, n: PSym): bool {.discardable.} =
# returns true if n is already in the string table: # returns true if n is already in the string table:
# It is essential that `n` is written nevertheless! # It is essential that `n` is written nevertheless!
# This way the newest redefinition is picked by the semantic analyses! # This way the newest redefinition is picked by the semantic analyses!

View file

@ -15,7 +15,6 @@ import
proc evalImport*(c: PContext, n: PNode): PNode proc evalImport*(c: PContext, n: PNode): PNode
proc evalFrom*(c: PContext, n: PNode): PNode proc evalFrom*(c: PContext, n: PNode): PNode
proc importAllSymbols*(c: PContext, fromMod: PSym)
proc getModuleName*(n: PNode): string = proc getModuleName*(n: PNode): string =
# This returns a short relative module name without the nim extension # This returns a short relative module name without the nim extension
@ -29,9 +28,11 @@ proc getModuleName*(n: PNode): string =
of nkSym: of nkSym:
result = n.sym.name.s result = n.sym.name.s
else: else:
localError(n.info, errGenerated, # hacky way to implement 'x / y /../ z':
"invalide module name: '$1'" % renderTree(n)) result = renderTree(n, {renderNoComments}).replace(" ")
result = "" #localError(n.info, errGenerated,
# "invalide module name: '$1'" % renderTree(n))
#result = ""
proc checkModuleName*(n: PNode): string = proc checkModuleName*(n: PNode): string =
# This returns the full canonical path for a given module import # This returns the full canonical path for a given module import
@ -43,21 +44,22 @@ proc checkModuleName*(n: PNode): string =
proc rawImportSymbol(c: PContext, s: PSym) = proc rawImportSymbol(c: PContext, s: PSym) =
# This does not handle stubs, because otherwise loading on demand would be # This does not handle stubs, because otherwise loading on demand would be
# pointless in practice. So importing stubs is fine here! # pointless in practice. So importing stubs is fine here!
var copy = s # do not copy symbols when importing!
# check if we have already a symbol of the same name: # check if we have already a symbol of the same name:
var check = StrTableGet(c.tab.stack[importTablePos], s.name) var check = StrTableGet(c.tab.stack[importTablePos], s.name)
if check != nil and check.id != copy.id: if check != nil and check.id != s.id:
if s.kind notin OverloadableSyms: if s.kind notin OverloadableSyms:
# s and check need to be qualified: # s and check need to be qualified:
Incl(c.AmbiguousSymbols, copy.id) Incl(c.AmbiguousSymbols, s.id)
Incl(c.AmbiguousSymbols, check.id) Incl(c.AmbiguousSymbols, check.id)
StrTableAdd(c.tab.stack[importTablePos], copy) # thanks to 'export' feature, it could be we import the same symbol from
# multiple sources, so we need to call 'StrTableAdd' here:
StrTableAdd(c.tab.stack[importTablePos], s)
if s.kind == skType: if s.kind == skType:
var etyp = s.typ var etyp = s.typ
if etyp.kind in {tyBool, tyEnum} and sfPure notin s.flags: if etyp.kind in {tyBool, tyEnum} and sfPure notin s.flags:
for j in countup(0, sonsLen(etyp.n) - 1): for j in countup(0, sonsLen(etyp.n) - 1):
var e = etyp.n.sons[j].sym var e = etyp.n.sons[j].sym
if (e.Kind != skEnumField): if e.Kind != skEnumField:
InternalError(s.info, "rawImportSymbol") InternalError(s.info, "rawImportSymbol")
# BUGFIX: because of aliases for enums the symbol may already # BUGFIX: because of aliases for enums the symbol may already
# have been put into the symbol table # have been put into the symbol table
@ -97,17 +99,6 @@ proc importSymbol(c: PContext, n: PNode, fromMod: PSym) =
e = NextIdentIter(it, fromMod.tab) e = NextIdentIter(it, fromMod.tab)
else: rawImportSymbol(c, s) else: rawImportSymbol(c, s)
proc importAllSymbols(c: PContext, fromMod: PSym) =
var i: TTabIter
var s = InitTabIter(i, fromMod.tab)
while s != nil:
if s.kind != skModule:
if s.kind != skEnumField:
if s.Kind notin ExportableSymKinds:
InternalError(s.info, "importAllSymbols: " & $s.kind)
rawImportSymbol(c, s) # this is correct!
s = NextIter(i, fromMod.tab)
proc importAllSymbolsExcept(c: PContext, fromMod: PSym, exceptSet: TIntSet) = proc importAllSymbolsExcept(c: PContext, fromMod: PSym, exceptSet: TIntSet) =
var i: TTabIter var i: TTabIter
var s = InitTabIter(i, fromMod.tab) var s = InitTabIter(i, fromMod.tab)
@ -116,12 +107,34 @@ proc importAllSymbolsExcept(c: PContext, fromMod: PSym, exceptSet: TIntSet) =
if s.kind != skEnumField: if s.kind != skEnumField:
if s.Kind notin ExportableSymKinds: if s.Kind notin ExportableSymKinds:
InternalError(s.info, "importAllSymbols: " & $s.kind) InternalError(s.info, "importAllSymbols: " & $s.kind)
if s.name.id notin exceptSet: if exceptSet.empty or s.name.id notin exceptSet:
rawImportSymbol(c, s) rawImportSymbol(c, s)
s = NextIter(i, fromMod.tab) s = NextIter(i, fromMod.tab)
proc importAllSymbols*(c: PContext, fromMod: PSym) =
var exceptSet: TIntSet
importAllSymbolsExcept(c, fromMod, exceptSet)
proc importForwarded(c: PContext, n: PNode, exceptSet: TIntSet) =
if n.isNil: return
case n.kind
of nkExportStmt:
for a in n:
assert a.kind == nkSym
let s = a.sym
if s.kind == skModule:
importAllSymbolsExcept(c, s, exceptSet)
elif exceptSet.empty or s.name.id notin exceptSet:
rawImportSymbol(c, s)
of nkExportExceptStmt:
localError(n.info, errGenerated, "'export except' not implemented")
else:
for i in 0 ..safeLen(n)-1:
importForwarded(c, n.sons[i], exceptSet)
proc evalImport(c: PContext, n: PNode): PNode = proc evalImport(c: PContext, n: PNode): PNode =
result = n result = n
var emptySet: TIntSet
for i in countup(0, sonsLen(n) - 1): for i in countup(0, sonsLen(n) - 1):
var f = checkModuleName(n.sons[i]) var f = checkModuleName(n.sons[i])
if f.len > 0: if f.len > 0:
@ -130,7 +143,8 @@ proc evalImport(c: PContext, n: PNode): PNode =
Message(n.sons[i].info, warnDeprecated, m.name.s) Message(n.sons[i].info, warnDeprecated, m.name.s)
# ``addDecl`` needs to be done before ``importAllSymbols``! # ``addDecl`` needs to be done before ``importAllSymbols``!
addDecl(c, m) # add symbol to symbol table of module addDecl(c, m) # add symbol to symbol table of module
importAllSymbols(c, m) importAllSymbolsExcept(c, m, emptySet)
importForwarded(c, m.ast, emptySet)
proc evalFrom(c: PContext, n: PNode): PNode = proc evalFrom(c: PContext, n: PNode): PNode =
result = n result = n
@ -155,3 +169,4 @@ proc evalImportExcept*(c: PContext, n: PNode): PNode =
let ident = lookups.considerAcc(n.sons[i]) let ident = lookups.considerAcc(n.sons[i])
exceptSet.incl(ident.id) exceptSet.incl(ident.id)
importAllSymbolsExcept(c, m, exceptSet) importAllSymbolsExcept(c, m, exceptSet)
importForwarded(c, m.ast, exceptSet)

View file

@ -1659,6 +1659,26 @@ proc fixImmediateParams(n: PNode): PNode =
result = n result = n
proc semExport(c: PContext, n: PNode): PNode =
var x = newNodeI(n.kind, n.info)
#let L = if n.kind == nkExportExceptStmt: L = 1 else: n.len
for i in 0.. <n.len:
let a = n.sons[i]
var o: TOverloadIter
var s = initOverloadIter(o, c, a)
if s == nil:
localError(a.info, errGenerated, "invalid expr for 'export': " &
renderTree(a))
while s != nil:
if s.kind in ExportableSymKinds+{skModule}:
x.add(newSymNode(s, a.info))
s = nextOverloadIter(o, c, a)
if c.module.ast.isNil:
c.module.ast = newNodeI(nkStmtList, n.info)
assert c.module.ast.kind == nkStmtList
c.module.ast.add x
result = n
proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode = proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
result = n result = n
if gCmd == cmdIdeTools: suggestExpr(c, n) if gCmd == cmdIdeTools: suggestExpr(c, n)
@ -1861,6 +1881,9 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
of nkIncludeStmt: of nkIncludeStmt:
if not isTopLevel(c): LocalError(n.info, errXOnlyAtModuleScope, "include") if not isTopLevel(c): LocalError(n.info, errXOnlyAtModuleScope, "include")
result = evalInclude(c, n) result = evalInclude(c, n)
of nkExportStmt, nkExportExceptStmt:
if not isTopLevel(c): LocalError(n.info, errXOnlyAtModuleScope, "export")
result = semExport(c, n)
of nkPragmaBlock: of nkPragmaBlock:
result = semPragmaBlock(c, n) result = semPragmaBlock(c, n)
of nkStaticStmt: of nkStaticStmt:

View file

@ -3656,7 +3656,7 @@ don't need to instantiate the code multiple times, because types then can be
manipulated using the unified internal symbol representation. In such context manipulated using the unified internal symbol representation. In such context
typedesc acts as any other type. You can create variables, store typedesc typedesc acts as any other type. You can create variables, store typedesc
values inside containers and so on. For example, here is how we can create values inside containers and so on. For example, here is how we can create
a type-safe wrapper for the unsafe `printf` function form C: a type-safe wrapper for the unsafe `printf` function from C:
.. code-block:: nimrod .. code-block:: nimrod
macro safePrintF(formatString: string{lit}, args: vararg[expr]): expr = macro safePrintF(formatString: string{lit}, args: vararg[expr]): expr =
@ -3744,7 +3744,7 @@ This is best illustrated by an example:
Import statement Import statement
~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~
After the import statement a list of module names can follow or a single After the `import`:idx: statement a list of module names can follow or a single
module name followed by an ``except`` to prevent some symbols to be imported: module name followed by an ``except`` to prevent some symbols to be imported:
.. code-block:: nimrod .. code-block:: nimrod
@ -3754,6 +3754,33 @@ module name followed by an ``except`` to prevent some symbols to be imported:
echo "$1" % "abc" echo "$1" % "abc"
Export statement
~~~~~~~~~~~~~~~~
An `export`:idx: statement can be used for symbol fowarding so that client
modules don't need to import a module's dependencies:
.. code-block:: nimrod
# module B
type TMyObject* = object
.. code-block:: nimrod
# module A
import B
export B.TMyObject
proc `$`*(x: TMyObject): string = "my object"
.. code-block:: nimrod
# module C
import A
# B.TMyObject has been imported implicitly here:
var x: TMyObject
echo($x)
Scope rules Scope rules
----------- -----------
Identifiers are valid from the point of their declaration until the end of Identifiers are valid from the point of their declaration until the end of

View file

@ -35,6 +35,9 @@ import sockets, os
## that in the future this type's fields will not be exported therefore breaking ## that in the future this type's fields will not be exported therefore breaking
## your code. ## your code.
## ##
## **Warning:** The API of this module is unstable, and therefore is subject
## to change.
##
## Asynchronous sockets ## Asynchronous sockets
## ==================== ## ====================
## ##

View file

@ -190,6 +190,11 @@ proc `$`*(s: TIntSet): string =
## The `$` operator for int sets. ## The `$` operator for int sets.
dollarImpl() dollarImpl()
proc empty*(s: TIntSet): bool {.inline.} =
## returns true if `s` is empty. This is safe to call even before
## the set has been initialized with `initIntSet`.
result = s.counter == 0
when isMainModule: when isMainModule:
var x = initIntSet() var x = initIntSet()
x.incl(1) x.incl(1)

View file

@ -26,6 +26,10 @@ import sockets, strutils, parseutils, times, os, asyncio
## var ftp = FTPClient("example.org", user = "user", pass = "pass") ## var ftp = FTPClient("example.org", user = "user", pass = "pass")
## ftp.connect() ## ftp.connect()
## ftp.retrFile("file.ext", "file.ext") ## ftp.retrFile("file.ext", "file.ext")
##
## **Warning:** The API of this module is unstable, and therefore is subject
## to change.
type type
TFTPClient* = object of TObject TFTPClient* = object of TObject

View file

@ -22,6 +22,8 @@
## ##
## run(handleRequest, TPort(80)) ## run(handleRequest, TPort(80))
## ##
## **Warning:** The API of this module is unstable, and therefore is subject
## to change.
import parseutils, strutils, os, osproc, strtabs, streams, sockets, asyncio import parseutils, strutils, os, osproc, strtabs, streams, sockets, asyncio

View file

@ -16,15 +16,21 @@
## the amount of lag. ## the amount of lag.
## ##
## .. code-block:: Nimrod ## .. code-block:: Nimrod
## var client = irc("irc.server.net", joinChans = @["#channel"]) ##
## var client = irc("picheta.me", joinChans = @["#bots"])
## client.connect() ## client.connect()
## while True: ## while True:
## var event: TIRCEvent ## var event: TIRCEvent
## if client.poll(event): ## if client.poll(event):
## case event.typ ## case event.typ
## of EvDisconnected: break ## of EvConnected: nil
## of EvDisconnected:
## client.reconnect()
## of EvMsg: ## of EvMsg:
## # Where all the magic happens. ## # Write your message reading code here.
##
## **Warning:** The API of this module is unstable, and therefore is subject
## to change.
import sockets, strutils, parseutils, times, asyncio import sockets, strutils, parseutils, times, asyncio
@ -34,11 +40,15 @@ type
port: TPort port: TPort
nick, user, realname, serverPass: string nick, user, realname, serverPass: string
case isAsync: bool case isAsync: bool
of false:
sock: TSocket
of true: of true:
handleEvent: proc (irc: var TAsyncIRC, ev: TIRCEvent) {.closure.} handleEvent: proc (irc: PAsyncIRC, ev: TIRCEvent) {.closure.}
asyncSock: PAsyncSocket asyncSock: PAsyncSocket
myDispatcher: PDispatcher
of false:
dummyA: pointer
dummyB: pointer # workaround a Nimrod API issue
dummyC: pointer
sock: TSocket
status: TInfo status: TInfo
lastPing: float lastPing: float
lastPong: float lastPong: float
@ -47,6 +57,8 @@ type
msgLimit: bool msgLimit: bool
messageBuffer: seq[tuple[timeToSend: float, m: string]] messageBuffer: seq[tuple[timeToSend: float, m: string]]
PIRC* = ref TIRC
PAsyncIRC* = ref TAsyncIRC PAsyncIRC* = ref TAsyncIRC
TAsyncIRC* = object of TIRC TAsyncIRC* = object of TIRC
@ -68,9 +80,13 @@ type
MError MError
TIRCEventType* = enum TIRCEventType* = enum
EvMsg, EvDisconnected EvMsg, EvConnected, EvDisconnected
TIRCEvent* = object ## IRC Event TIRCEvent* = object ## IRC Event
case typ*: TIRCEventType case typ*: TIRCEventType
of EvConnected:
## Connected to server.
## Only occurs with AsyncIRC.
nil
of EvDisconnected: of EvDisconnected:
## Disconnected from the server ## Disconnected from the server
nil nil
@ -82,7 +98,7 @@ type
origin*: string ## The channel/user that this msg originated from origin*: string ## The channel/user that this msg originated from
raw*: string ## Raw IRC message raw*: string ## Raw IRC message
proc send*(irc: var TIRC, message: string, sendImmediately = false) = proc send*(irc: PIRC, message: string, sendImmediately = false) =
## Sends ``message`` as a raw command. It adds ``\c\L`` for you. ## Sends ``message`` as a raw command. It adds ``\c\L`` for you.
var sendMsg = true var sendMsg = true
if irc.msgLimit and not sendImmediately: if irc.msgLimit and not sendImmediately:
@ -104,15 +120,15 @@ proc send*(irc: var TIRC, message: string, sendImmediately = false) =
# but I can't exactly check for EBrokenPipe. # but I can't exactly check for EBrokenPipe.
irc.status = SockClosed irc.status = SockClosed
proc privmsg*(irc: var TIRC, target, message: string) = proc privmsg*(irc: PIRC, target, message: string) =
## Sends ``message`` to ``target``. ``Target`` can be a channel, or a user. ## Sends ``message`` to ``target``. ``Target`` can be a channel, or a user.
irc.send("PRIVMSG $1 :$2" % [target, message]) irc.send("PRIVMSG $1 :$2" % [target, message])
proc notice*(irc: var TIRC, target, message: string) = proc notice*(irc: PIRC, target, message: string) =
## Sends ``notice`` to ``target``. ``Target`` can be a channel, or a user. ## Sends ``notice`` to ``target``. ``Target`` can be a channel, or a user.
irc.send("NOTICE $1 :$2" % [target, message]) irc.send("NOTICE $1 :$2" % [target, message])
proc join*(irc: var TIRC, channel: string, key = "") = proc join*(irc: PIRC, channel: string, key = "") =
## Joins ``channel``. ## Joins ``channel``.
## ##
## If key is not ``""``, then channel is assumed to be key protected and this ## If key is not ``""``, then channel is assumed to be key protected and this
@ -122,16 +138,19 @@ proc join*(irc: var TIRC, channel: string, key = "") =
else: else:
irc.send("JOIN " & channel & " " & key) irc.send("JOIN " & channel & " " & key)
proc part*(irc: var TIRC, channel, message: string) = proc part*(irc: PIRC, channel, message: string) =
## Leaves ``channel`` with ``message``. ## Leaves ``channel`` with ``message``.
irc.send("PART " & channel & " :" & message) irc.send("PART " & channel & " :" & message)
proc close*(irc: var TIRC) = proc close*(irc: PIRC) =
## Closes connection to an IRC server. ## Closes connection to an IRC server.
## ##
## **Warning:** This procedure does not send a ``QUIT`` message to the server. ## **Warning:** This procedure does not send a ``QUIT`` message to the server.
irc.status = SockClosed irc.status = SockClosed
irc.sock.close() if irc.isAsync:
irc.asyncSock.close()
else:
irc.sock.close()
proc isNumber(s: string): bool = proc isNumber(s: string): bool =
## Checks if `s` contains only numbers. ## Checks if `s` contains only numbers.
@ -202,12 +221,11 @@ proc parseMessage(msg: string): TIRCEvent =
inc(i) # Skip `:`. inc(i) # Skip `:`.
result.params.add(msg[i..msg.len-1]) result.params.add(msg[i..msg.len-1])
proc connect*(irc: var TIRC) = proc connect*(irc: PIRC) =
## Connects to an IRC server as specified by ``irc``. ## Connects to an IRC server as specified by ``irc``.
assert(irc.address != "") assert(irc.address != "")
assert(irc.port != TPort(0)) assert(irc.port != TPort(0))
irc.sock = socket()
irc.sock.connect(irc.address, irc.port) irc.sock.connect(irc.address, irc.port)
irc.status = SockConnected irc.status = SockConnected
@ -217,13 +235,21 @@ proc connect*(irc: var TIRC) =
irc.send("NICK " & irc.nick, true) irc.send("NICK " & irc.nick, true)
irc.send("USER $1 * 0 :$2" % [irc.user, irc.realname], true) irc.send("USER $1 * 0 :$2" % [irc.user, irc.realname], true)
proc reconnect*(irc: PIRC) =
## Reconnects to an IRC server.
##
## This should be used when an ``EvDisconnected`` event occurs.
irc.sock = socket()
irc.connect()
proc irc*(address: string, port: TPort = 6667.TPort, proc irc*(address: string, port: TPort = 6667.TPort,
nick = "NimrodBot", nick = "NimrodBot",
user = "NimrodBot", user = "NimrodBot",
realname = "NimrodBot", serverPass = "", realname = "NimrodBot", serverPass = "",
joinChans: seq[string] = @[], joinChans: seq[string] = @[],
msgLimit: bool = true): TIRC = msgLimit: bool = true): PIRC =
## Creates a ``TIRC`` object. ## Creates a ``TIRC`` object.
new(result)
result.address = address result.address = address
result.port = port result.port = port
result.nick = nick result.nick = nick
@ -237,8 +263,9 @@ proc irc*(address: string, port: TPort = 6667.TPort,
result.msgLimit = msgLimit result.msgLimit = msgLimit
result.messageBuffer = @[] result.messageBuffer = @[]
result.status = SockIdle result.status = SockIdle
result.sock = socket()
proc processLine(irc: var TIRC, line: string): TIRCEvent = proc processLine(irc: PIRC, line: string): TIRCEvent =
if line.len == 0: if line.len == 0:
irc.close() irc.close()
result.typ = EvDisconnected result.typ = EvDisconnected
@ -271,7 +298,7 @@ proc processLine(irc: var TIRC, line: string): TIRCEvent =
if result.nick == irc.nick: if result.nick == irc.nick:
irc.nick = result.params[0] irc.nick = result.params[0]
proc processOther(irc: var TIRC, ev: var TIRCEvent): bool = proc processOther(irc: PIRC, ev: var TIRCEvent): bool =
result = false result = false
if epochTime() - irc.lastPing >= 20.0: if epochTime() - irc.lastPing >= 20.0:
irc.lastPing = epochTime() irc.lastPing = epochTime()
@ -290,7 +317,7 @@ proc processOther(irc: var TIRC, ev: var TIRCEvent): bool =
break # messageBuffer is guaranteed to be from the quickest to the break # messageBuffer is guaranteed to be from the quickest to the
# later-est. # later-est.
proc poll*(irc: var TIRC, ev: var TIRCEvent, proc poll*(irc: PIRC, ev: var TIRCEvent,
timeout: int = 500): bool = timeout: int = 500): bool =
## This function parses a single message from the IRC server and returns ## This function parses a single message from the IRC server and returns
## a TIRCEvent. ## a TIRCEvent.
@ -316,46 +343,32 @@ proc poll*(irc: var TIRC, ev: var TIRCEvent,
if processOther(irc, ev): result = true if processOther(irc, ev): result = true
proc getLag*(irc: var TIRC): float = proc getLag*(irc: PIRC): float =
## Returns the latency between this client and the IRC server in seconds. ## Returns the latency between this client and the IRC server in seconds.
## ##
## If latency is unknown, returns -1.0. ## If latency is unknown, returns -1.0.
return irc.lag return irc.lag
proc isConnected*(irc: var TIRC): bool = proc isConnected*(irc: PIRC): bool =
## Returns whether this IRC client is connected to an IRC server. ## Returns whether this IRC client is connected to an IRC server.
return irc.status == SockConnected return irc.status == SockConnected
proc getNick*(irc: var TIRC): string = proc getNick*(irc: PIRC): string =
## Returns the current nickname of the client. ## Returns the current nickname of the client.
return irc.nick return irc.nick
# -- Asyncio dispatcher # -- Asyncio dispatcher
proc connect*(irc: PAsyncIRC) =
## Equivalent of connect for ``TIRC`` but specifically created for asyncio.
assert(irc.address != "")
assert(irc.port != TPort(0))
irc.asyncSock = AsyncSocket()
irc.asyncSock.connect(irc.address, irc.port)
proc handleConnect(s: PAsyncSocket, irc: PAsyncIRC) = proc handleConnect(s: PAsyncSocket, irc: PAsyncIRC) =
# Greet the server :) # Greet the server :)
if irc.serverPass != "": irc[].send("PASS " & irc.serverPass, true) if irc.serverPass != "": irc.send("PASS " & irc.serverPass, true)
irc[].send("NICK " & irc.nick, true) irc.send("NICK " & irc.nick, true)
irc[].send("USER $1 * 0 :$2" % [irc.user, irc.realname], true) irc.send("USER $1 * 0 :$2" % [irc.user, irc.realname], true)
discard """proc handleConnect(h: PObject) =
var irc = PAsyncIRC(h)
# 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 irc.status = SockConnected
"""
var ev: TIRCEvent
ev.typ = EvConnected
irc.handleEvent(irc, ev)
proc handleRead(s: PAsyncSocket, irc: PAsyncIRC) = proc handleRead(s: PAsyncSocket, irc: PAsyncIRC) =
var line = "".TaintedString var line = "".TaintedString
@ -363,42 +376,48 @@ proc handleRead(s: PAsyncSocket, irc: PAsyncIRC) =
if ret: if ret:
if line == "": if line == "":
var ev: TIRCEvent var ev: TIRCEvent
irc[].close() irc.close()
ev.typ = EvDisconnected ev.typ = EvDisconnected
irc[].handleEvent(irc[], ev) irc.handleEvent(irc, ev)
else: else:
var ev = irc[].processLine(line.string) var ev = irc.processLine(line.string)
irc[].handleEvent(irc[], ev) irc.handleEvent(irc, ev)
discard """proc handleRead(h: PObject) =
var irc = PAsyncIRC(h)
var line = "".TaintedString
var ret = irc.sock.recvLineAsync(line)
case ret
of RecvFullLine:
var ev = irc[].processLine(irc.lineBuffer.string & line.string)
irc.handleEvent(irc[], ev, irc.userArg)
irc.lineBuffer = "".TaintedString
of RecvPartialLine:
if line.string != "":
string(irc.lineBuffer).add(line.string)
of RecvDisconnected:
var ev: TIRCEvent
irc[].close()
ev.typ = EvDisconnected
irc.handleEvent(irc[], ev, irc.userArg)
of RecvFail: nil"""
proc handleTask(s: PAsyncSocket, irc: PAsyncIRC) = proc handleTask(s: PAsyncSocket, irc: PAsyncIRC) =
var ev: TIRCEvent var ev: TIRCEvent
if irc[].processOther(ev): if irc.processOther(ev):
irc.handleEvent(irc[], ev) irc.handleEvent(irc, ev)
discard """proc handleTask(h: PObject) = proc register*(d: PDispatcher, irc: PAsyncIRC) =
var irc = PAsyncIRC(h) ## Registers ``irc`` with dispatcher ``d``.
var ev: TIRCEvent irc.asyncSock.handleConnect =
if PAsyncIRC(h)[].processOther(ev): proc (s: PAsyncSocket) =
irc.handleEvent(irc[], ev, irc.userArg)""" 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) =
## Reconnects to an IRC server.
##
## This should be used when an ``EvDisconnected`` event occurs.
##
## When successfully reconnected an ``EvConnected`` event will occur.
irc.asyncSock = AsyncSocket()
irc.myDispatcher.register(irc)
irc.connect()
proc asyncIRC*(address: string, port: TPort = 6667.TPort, proc asyncIRC*(address: string, port: TPort = 6667.TPort,
nick = "NimrodBot", nick = "NimrodBot",
@ -406,7 +425,7 @@ 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) {.closure.} ircEvent: proc (irc: PAsyncIRC, ev: TIRCEvent) {.closure.}
): PAsyncIRC = ): PAsyncIRC =
## Use this function if you want to use asyncio's dispatcher. ## Use this function if you want to use asyncio's dispatcher.
## ##
@ -429,19 +448,7 @@ 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.asyncSock = AsyncSocket()
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)
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)")
@ -456,6 +463,8 @@ when isMainModule:
var event: TIRCEvent var event: TIRCEvent
if client.poll(event): if client.poll(event):
case event.typ case event.typ
of EvConnected:
nil
of EvDisconnected: of EvDisconnected:
break break
of EvMsg: of EvMsg:

View file

@ -23,6 +23,8 @@
## ##
## run(handleRequest) ## run(handleRequest)
## ##
## **Warning:** The API of this module is unstable, and therefore is subject
## to change.
import sockets, strutils, os, strtabs, asyncio import sockets, strutils, os, strtabs, asyncio

View file

@ -0,0 +1,8 @@
# module A
import mexportb
export mexportb.TMyObject, mexportb.xyz
export mexportb.q
proc `$`*(x: TMyObject): string = "my object"

View file

@ -0,0 +1,7 @@
# module B
type TMyObject* = object
const xyz* = 13
proc q*(x: int): int = 6
proc q*(x: string): string = "8"

10
tests/compile/texport.nim Normal file
View file

@ -0,0 +1,10 @@
discard """
output: "my object68"
"""
import mexporta
# B.TMyObject has been imported implicitly here:
var x: TMyObject
echo($x, q(0), q"0")

View file

@ -1,7 +1,7 @@
# iterate over all files with a given filter: # iterate over all files with a given filter:
import import
os, times "../../lib/pure/os.nim", ../../ lib / pure / times
proc main(filter: string) = proc main(filter: string) =
for filename in walkFiles(filter): for filename in walkFiles(filter):

View file

@ -1,7 +1,7 @@
version 0.9.2 version 0.9.2
============= =============
- 'export' feature - fix tfShared and tfNotNil
- test&finish first class iterators: - test&finish first class iterators:
* nested iterators * nested iterators
* test generic iterators * test generic iterators
@ -50,7 +50,7 @@ Concurrency
provide a ``syncgc`` pragma to trigger compiler injection --> more general: provide a ``syncgc`` pragma to trigger compiler injection --> more general:
an ``injectLoop`` pragma an ``injectLoop`` pragma
- 'writes: []' effect; track reads/writes for shared types - 'writes: []' effect; track reads/writes for shared types
- use the effect system to for static deadlock prevention - use the effect system for static deadlock prevention
version 0.9.XX version 0.9.XX

View file

@ -51,6 +51,9 @@ Language Additions
that ``nil`` is not allowed. However currently the compiler performs no that ``nil`` is not allowed. However currently the compiler performs no
advanced static checking for this; for now it's merely for documentation advanced static checking for this; for now it's merely for documentation
purposes. purposes.
- An ``export`` statement has been added to the language: It can be used for
symbol forwarding so client modules don't have to import a module's
dependencies explicitly.
2012-09-23 Version 0.9.0 released 2012-09-23 Version 0.9.0 released