diff --git a/compiler/astalgo.nim b/compiler/astalgo.nim index da0de3e942..9a2bebab4c 100755 --- a/compiler/astalgo.nim +++ b/compiler/astalgo.nim @@ -585,8 +585,9 @@ proc StrTableContains(t: TStrTable, n: PSym): bool = proc StrTableRawInsert(data: var TSymSeq, n: PSym) = var h: THash = n.name.h and high(data) while data[h] != nil: - if data[h] == n: - InternalError(n.info, "StrTableRawInsert: " & n.name.s) + if data[h] == n: + # allowed for 'export' feature: + #InternalError(n.info, "StrTableRawInsert: " & n.name.s) return h = nextTry(h, high(data)) assert(data[h] == nil) @@ -617,23 +618,23 @@ proc StrTableAdd(t: var TStrTable, n: PSym) = StrTableRawInsert(t.data, n) 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: # It is essential that `n` is written nevertheless! # This way the newest redefinition is picked by the semantic analyses! assert n.name != nil var h: THash = n.name.h and high(t.data) - while true: + while true: var it = t.data[h] - if it == nil: break - if it.name.id == n.name.id: + if it == nil: break + if it.name.id == n.name.id: t.data[h] = n # overwrite it with newer definition! return true # found it h = nextTry(h, high(t.data)) - if mustRehash(len(t.data), t.counter): + if mustRehash(len(t.data), t.counter): StrTableEnlarge(t) StrTableRawInsert(t.data, n) - else: + else: assert(t.data[h] == nil) t.data[h] = n inc(t.counter) diff --git a/compiler/importer.nim b/compiler/importer.nim index b3821746c2..774bcea6ac 100755 --- a/compiler/importer.nim +++ b/compiler/importer.nim @@ -15,7 +15,6 @@ import proc evalImport*(c: PContext, n: PNode): PNode proc evalFrom*(c: PContext, n: PNode): PNode -proc importAllSymbols*(c: PContext, fromMod: PSym) proc getModuleName*(n: PNode): string = # This returns a short relative module name without the nim extension @@ -29,9 +28,11 @@ proc getModuleName*(n: PNode): string = of nkSym: result = n.sym.name.s else: - localError(n.info, errGenerated, - "invalide module name: '$1'" % renderTree(n)) - result = "" + # hacky way to implement 'x / y /../ z': + result = renderTree(n, {renderNoComments}).replace(" ") + #localError(n.info, errGenerated, + # "invalide module name: '$1'" % renderTree(n)) + #result = "" proc checkModuleName*(n: PNode): string = # This returns the full canonical path for a given module import @@ -40,42 +41,43 @@ proc checkModuleName*(n: PNode): string = if result.len == 0: LocalError(n.info, errCannotOpenFile, modulename) -proc rawImportSymbol(c: PContext, s: PSym) = +proc rawImportSymbol(c: PContext, s: PSym) = # This does not handle stubs, because otherwise loading on demand would be # 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: var check = StrTableGet(c.tab.stack[importTablePos], s.name) - if check != nil and check.id != copy.id: - if s.kind notin OverloadableSyms: + if check != nil and check.id != s.id: + if s.kind notin OverloadableSyms: # s and check need to be qualified: - Incl(c.AmbiguousSymbols, copy.id) + Incl(c.AmbiguousSymbols, s.id) Incl(c.AmbiguousSymbols, check.id) - StrTableAdd(c.tab.stack[importTablePos], copy) - if s.kind == skType: + # 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: var etyp = s.typ - if etyp.kind in {tyBool, tyEnum} and sfPure notin s.flags: - for j in countup(0, sonsLen(etyp.n) - 1): + if etyp.kind in {tyBool, tyEnum} and sfPure notin s.flags: + for j in countup(0, sonsLen(etyp.n) - 1): var e = etyp.n.sons[j].sym - if (e.Kind != skEnumField): + if e.Kind != skEnumField: InternalError(s.info, "rawImportSymbol") # BUGFIX: because of aliases for enums the symbol may already # have been put into the symbol table # BUGFIX: but only iff they are the same symbols! var it: TIdentIter check = InitIdentIter(it, c.tab.stack[importTablePos], e.name) - while check != nil: - if check.id == e.id: + while check != nil: + if check.id == e.id: e = nil - break + break check = NextIdentIter(it, c.tab.stack[importTablePos]) - if e != nil: + if e != nil: rawImportSymbol(c, e) else: # rodgen assures that converters and patterns are no stubs if s.kind == skConverter: addConverter(c, s) if hasPattern(s): addPattern(c, s) - + proc importSymbol(c: PContext, n: PNode, fromMod: PSym) = let ident = lookups.considerAcc(n) let s = StrTableGet(fromMod.tab, ident) @@ -96,17 +98,6 @@ proc importSymbol(c: PContext, n: PNode, fromMod: PSym) = rawImportSymbol(c, e) e = NextIdentIter(it, fromMod.tab) 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) = var i: TTabIter @@ -116,12 +107,34 @@ proc importAllSymbolsExcept(c: PContext, fromMod: PSym, exceptSet: TIntSet) = if s.kind != skEnumField: if s.Kind notin ExportableSymKinds: InternalError(s.info, "importAllSymbols: " & $s.kind) - if s.name.id notin exceptSet: + if exceptSet.empty or s.name.id notin exceptSet: rawImportSymbol(c, s) 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 = result = n + var emptySet: TIntSet for i in countup(0, sonsLen(n) - 1): var f = checkModuleName(n.sons[i]) if f.len > 0: @@ -130,7 +143,8 @@ proc evalImport(c: PContext, n: PNode): PNode = Message(n.sons[i].info, warnDeprecated, m.name.s) # ``addDecl`` needs to be done before ``importAllSymbols``! 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 = result = n @@ -155,3 +169,4 @@ proc evalImportExcept*(c: PContext, n: PNode): PNode = let ident = lookups.considerAcc(n.sons[i]) exceptSet.incl(ident.id) importAllSymbolsExcept(c, m, exceptSet) + importForwarded(c, m.ast, exceptSet) diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index c5e3fdc1eb..7b37987eec 100755 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -1659,6 +1659,26 @@ proc fixImmediateParams(n: PNode): PNode = 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.. = 20.0: 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 # later-est. -proc poll*(irc: var TIRC, ev: var TIRCEvent, +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. @@ -316,46 +343,32 @@ proc poll*(irc: var TIRC, ev: var TIRCEvent, 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. ## ## If latency is unknown, returns -1.0. 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. return irc.status == SockConnected -proc getNick*(irc: var TIRC): string = +proc getNick*(irc: PIRC): string = ## Returns the current nickname of the client. return irc.nick # -- 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) = # 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) - - # 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) - + 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 @@ -363,42 +376,48 @@ proc handleRead(s: PAsyncSocket, irc: PAsyncIRC) = if ret: if line == "": var ev: TIRCEvent - irc[].close() + irc.close() ev.typ = EvDisconnected - irc[].handleEvent(irc[], ev) + 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 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""" + 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) + 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)) -discard """proc handleTask(h: PObject) = - var irc = PAsyncIRC(h) - var ev: TIRCEvent - if PAsyncIRC(h)[].processOther(ev): - irc.handleEvent(irc[], ev, irc.userArg)""" + 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, nick = "NimrodBot", @@ -406,7 +425,7 @@ proc asyncIRC*(address: string, port: TPort = 6667.TPort, realname = "NimrodBot", serverPass = "", joinChans: seq[string] = @[], msgLimit: bool = true, - ircEvent: proc (irc: var TAsyncIRC, ev: TIRCEvent) {.closure.} + ircEvent: proc (irc: PAsyncIRC, ev: TIRCEvent) {.closure.} ): PAsyncIRC = ## 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.messageBuffer = @[] result.handleEvent = ircEvent - -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) + result.asyncSock = AsyncSocket() when isMainModule: #var m = parseMessage("ERROR :Closing Link: dom96.co.cc (Ping timeout: 252 seconds)") @@ -456,6 +463,8 @@ when isMainModule: var event: TIRCEvent if client.poll(event): case event.typ + of EvConnected: + nil of EvDisconnected: break of EvMsg: diff --git a/lib/pure/scgi.nim b/lib/pure/scgi.nim index 44a579a7d7..0f3b44e000 100755 --- a/lib/pure/scgi.nim +++ b/lib/pure/scgi.nim @@ -23,6 +23,8 @@ ## ## run(handleRequest) ## +## **Warning:** The API of this module is unstable, and therefore is subject +## to change. import sockets, strutils, os, strtabs, asyncio diff --git a/tests/compile/mexporta.nim b/tests/compile/mexporta.nim new file mode 100644 index 0000000000..b7d4ddec9b --- /dev/null +++ b/tests/compile/mexporta.nim @@ -0,0 +1,8 @@ +# module A +import mexportb +export mexportb.TMyObject, mexportb.xyz + +export mexportb.q + +proc `$`*(x: TMyObject): string = "my object" + diff --git a/tests/compile/mexportb.nim b/tests/compile/mexportb.nim new file mode 100644 index 0000000000..10d89f3880 --- /dev/null +++ b/tests/compile/mexportb.nim @@ -0,0 +1,7 @@ +# module B +type TMyObject* = object + +const xyz* = 13 + +proc q*(x: int): int = 6 +proc q*(x: string): string = "8" diff --git a/tests/compile/texport.nim b/tests/compile/texport.nim new file mode 100644 index 0000000000..99228dfcec --- /dev/null +++ b/tests/compile/texport.nim @@ -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") + diff --git a/tests/compile/twalker.nim b/tests/compile/twalker.nim index 3fdd8769ba..89e6c2b9d5 100755 --- a/tests/compile/twalker.nim +++ b/tests/compile/twalker.nim @@ -1,7 +1,7 @@ # iterate over all files with a given filter: import - os, times + "../../lib/pure/os.nim", ../../ lib / pure / times proc main(filter: string) = for filename in walkFiles(filter): diff --git a/todo.txt b/todo.txt index 804f6e5f68..61b178a230 100755 --- a/todo.txt +++ b/todo.txt @@ -1,7 +1,7 @@ version 0.9.2 ============= -- 'export' feature +- fix tfShared and tfNotNil - test&finish first class iterators: * nested iterators * test generic iterators @@ -50,7 +50,7 @@ Concurrency provide a ``syncgc`` pragma to trigger compiler injection --> more general: an ``injectLoop`` pragma - '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 diff --git a/web/news.txt b/web/news.txt index 6cd120e39a..fc54bbce21 100755 --- a/web/news.txt +++ b/web/news.txt @@ -51,6 +51,9 @@ Language Additions that ``nil`` is not allowed. However currently the compiler performs no advanced static checking for this; for now it's merely for documentation 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