diff options
36 files changed, 1017 insertions, 109 deletions
diff --git a/compiler/ast.nim b/compiler/ast.nim index a9407c91e..897501ee5 100755 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -227,9 +227,6 @@ type sfInnerProc, # proc is an inner proc sfThread, # proc will run as a thread # variable is a thread variable - sfInline # forced-inline procs - sfImmediate, # macro or template is immediately expanded without - # considering any possible overloads sfCompileTime, # proc can be evaluated at compile time sfMerge, # proc can be merged with itself sfDeadCodeElim, # dead code elimination for the module is turned on @@ -246,6 +243,8 @@ const sfFakeConst* = sfDeadCodeElim # const cannot be put into a data section sfDispatcher* = sfDeadCodeElim # copied method symbol is the dispatcher sfNoInit* = sfMainModule # don't generate code to init the variable + sfImmediate* = sfDeadCodeElim # macro or template is immediately expanded + # without considering any possible overloads type TTypeKind* = enum # order is important! diff --git a/compiler/astalgo.nim b/compiler/astalgo.nim index 9da0d3a20..861642594 100755 --- a/compiler/astalgo.nim +++ b/compiler/astalgo.nim @@ -146,6 +146,14 @@ proc IITablePut*(t: var TIITable, key, val: int) # implementation +proc skipConv*(n: PNode): PNode = + case n.kind + of nkObjUpConv, nkObjDownConv, nkChckRange, nkChckRangeF, nkChckRange64: + result = n.sons[0] + of nkHiddenStdConv, nkHiddenSubConv, nkConv: + result = n.sons[1] + else: result = n + proc SameValue*(a, b: PNode): bool = result = false case a.kind diff --git a/compiler/ccgexprs.nim b/compiler/ccgexprs.nim index 81d122859..d03142208 100755 --- a/compiler/ccgexprs.nim +++ b/compiler/ccgexprs.nim @@ -139,9 +139,9 @@ proc getStorageLoc(n: PNode): TStorageLoc = case n.kind of nkSym: case n.sym.kind - of skParam, skForVar, skTemp: + of skParam, skTemp: result = OnStack - of skVar, skResult, skLet: + of skVar, skForVar, skResult, skLet: if sfGlobal in n.sym.flags: result = OnHeap else: result = OnStack of skConst: @@ -1652,7 +1652,7 @@ proc expr(p: BProc, e: PNode, d: var TLoc) = genComplexConst(p, sym, d) of skEnumField: putIntoDest(p, d, e.typ, toRope(sym.position)) - of skVar, skResult, skLet: + of skVar, skForVar, skResult, skLet: if sfGlobal in sym.flags: genVarPrototype(p.module, sym) if sym.loc.r == nil or sym.loc.t == nil: InternalError(e.info, "expr: var not init " & sym.name.s) @@ -1664,7 +1664,7 @@ proc expr(p: BProc, e: PNode, d: var TLoc) = putLocIntoDest(p, d, sym.loc) else: putLocIntoDest(p, d, sym.loc) - of skForVar, skTemp: + of skTemp: if sym.loc.r == nil or sym.loc.t == nil: InternalError(e.info, "expr: temp not init " & sym.name.s) putLocIntoDest(p, d, sym.loc) diff --git a/compiler/ccgstmts.nim b/compiler/ccgstmts.nim index d0112f9d7..9c0d52221 100755 --- a/compiler/ccgstmts.nim +++ b/compiler/ccgstmts.nim @@ -23,7 +23,7 @@ proc genVarTuple(p: BProc, n: PNode) = for i in countup(0, L-3): var v = n.sons[i].sym if sfCompileTime in v.flags: continue - if sfGlobal in v.flags and v.kind != skForVar: + if sfGlobal in v.flags: assignGlobalVar(p, v) genObjectInit(p, cpsInit, v.typ, v.loc, true) else: @@ -49,7 +49,7 @@ proc genSingleVar(p: BProc, a: PNode) = var v = a.sons[0].sym if sfCompileTime in v.flags: return var immediateAsgn = a.sons[2].kind != nkEmpty - if sfGlobal in v.flags and v.kind != skForVar: + if sfGlobal in v.flags: assignGlobalVar(p, v) genObjectInit(p, cpsInit, v.typ, v.loc, true) else: @@ -725,7 +725,10 @@ proc genStmts(p: BProc, t: PNode) = genLineDir(p, t) initLocExpr(p, t, a) of nkAsgn: genAsgn(p, t, fastAsgn=false) - of nkFastAsgn: genAsgn(p, t, fastAsgn=true) + of nkFastAsgn: + # transf is overly aggressive with 'nkFastAsgn', so we work around here. + # See tests/run/tcnstseq3 for an example that would fail otherwise. + genAsgn(p, t, fastAsgn=p.prc != nil) of nkDiscardStmt: genLineDir(p, t) initLocExpr(p, t.sons[0], a) diff --git a/compiler/ccgtrav.nim b/compiler/ccgtrav.nim index 995ed2973..8aaf5370f 100644 --- a/compiler/ccgtrav.nim +++ b/compiler/ccgtrav.nim @@ -70,6 +70,7 @@ proc genTraverseProc(c: var TTraversalClosure, accessor: PRope, typ: PType) = genTraverseProc(c, accessor.parentObj, typ.sons[i]) if typ.n != nil: genTraverseProc(c, accessor, typ.n) of tyTuple: + let typ = GetUniqueType(typ) if typ.n != nil: genTraverseProc(c, accessor, typ.n) else: @@ -110,7 +111,11 @@ proc genTraverseProc(m: BModule, typ: PType, reason: TTypeInfoReason): PRope = if typ.kind == tySequence: genTraverseProcSeq(c, "a".toRope, typ) else: - genTraverseProc(c, "(*a)".toRope, typ.sons[0]) + if skipTypes(typ.sons[0], abstractInst).kind in {tyArrayConstr, tyArray}: + # C's arrays are broken beyond repair: + genTraverseProc(c, "a".toRope, typ.sons[0]) + else: + genTraverseProc(c, "(*a)".toRope, typ.sons[0]) let generatedProc = ropef("$1 {$n$2$3$4}$n", [header, p.s[cpsLocals], p.s[cpsInit], p.s[cpsStmts]]) diff --git a/compiler/ccgtypes.nim b/compiler/ccgtypes.nim index 78b02c691..6195ff2f4 100755 --- a/compiler/ccgtypes.nim +++ b/compiler/ccgtypes.nim @@ -39,10 +39,10 @@ proc mangleName(s: PSym): PRope = case s.kind of skProc, skMethod, skConverter, skConst: result = toRope("@") - of skVar, skResult, skLet: + of skVar, skForVar, skResult, skLet: if sfGlobal in s.flags: result = toRope("@") else: result = toRope("%") - of skForVar, skTemp, skParam, skType, skEnumField, skModule: + of skTemp, skParam, skType, skEnumField, skModule: result = toRope("%") else: InternalError(s.info, "mangleName") app(result, toRope(mangle(s.name.s))) diff --git a/compiler/cgen.nim b/compiler/cgen.nim index 0c1d721c0..a56053f79 100755 --- a/compiler/cgen.nim +++ b/compiler/cgen.nim @@ -779,6 +779,9 @@ proc genMainProc(m: BModule) = PosixCMain = "int main(int argc, char** args, char** env) {$n" & " cmdLine = args;$n" & " cmdCount = argc;$n" & " gEnv = env;$n" & " NimMain();$n" & " return nim_program_result;$n" & "}$n" + StandaloneCMain = "int main(void) {$n" & + " NimMain();$n" & + " return 0;$n" & "}$n" WinNimMain = "N_CDECL(void, NimMain)(void) {$n" & CommonMainBody & "}$n" WinCMain = "N_STDCALL(int, WinMain)(HINSTANCE hCurInstance, $n" & @@ -808,7 +811,10 @@ proc genMainProc(m: BModule) = elif optGenDynLib in gGlobalOptions: nimMain = posixNimDllMain otherMain = posixCDllMain - else: + elif platform.targetOS == osStandalone: + nimMain = PosixNimMain + otherMain = StandaloneCMain + else: nimMain = PosixNimMain otherMain = PosixCMain if gBreakpoints != nil: discard cgsym(m, "dbgRegisterBreakpoint") diff --git a/compiler/cgendata.nim b/compiler/cgendata.nim index d93c107ac..c58c64250 100644 --- a/compiler/cgendata.nim +++ b/compiler/cgendata.nim @@ -36,7 +36,7 @@ type cfsDynLibInit, # section for init of dynamic library binding cfsDynLibDeinit # section for deinitialization of dynamic # libraries - TCTypeKind* = enum # describes the type kind of a C type + TCTypeKind* = enum # describes the type kind of a C type ctVoid, ctChar, ctBool, ctUInt, ctUInt8, ctUInt16, ctUInt32, ctUInt64, ctInt, ctInt8, ctInt16, ctInt32, ctInt64, ctFloat, ctFloat32, ctFloat64, ctFloat128, ctArray, ctStruct, ctPtr, ctNimStr, ctNimSeq, ctProc, ctCString diff --git a/compiler/lambdalifting.nim b/compiler/lambdalifting.nim index a22252a30..dc6469563 100644 --- a/compiler/lambdalifting.nim +++ b/compiler/lambdalifting.nim @@ -11,8 +11,7 @@ # included from transf.nim const - declarativeDefs = {nkProcDef, nkMethodDef, nkIteratorDef, - nkConverterDef} + declarativeDefs = {nkProcDef, nkMethodDef, nkIteratorDef, nkConverterDef} procDefs = nkLambdaKinds + declarativeDefs proc indirectAccess(a, b: PSym, info: TLineInfo): PNode = diff --git a/compiler/platform.nim b/compiler/platform.nim index 01190c9c9..f4cf3b882 100755 --- a/compiler/platform.nim +++ b/compiler/platform.nim @@ -21,7 +21,8 @@ type # conditionals to condsyms (end of module). osNone, osDos, osWindows, osOs2, osLinux, osMorphos, osSkyos, osSolaris, osIrix, osNetbsd, osFreebsd, osOpenbsd, osAix, osPalmos, osQnx, osAmiga, - osAtari, osNetware, osMacos, osMacosx, osEcmaScript, osNimrodVM + osAtari, osNetware, osMacos, osMacosx, osEcmaScript, osNimrodVM, + osStandalone type TInfoOSProp* = enum @@ -139,14 +140,18 @@ const exeExt: "", extSep: ".", props: {}), (name: "NimrodVM", parDir: "..", dllFrmt: "lib$1.so", altDirSep: "/", objExt: ".o", newLine: "\x0A", pathSep: ":", dirSep: "/", - scriptExt: ".sh", curDir: ".", exeExt: "", extSep: ".", props: {})] + scriptExt: ".sh", curDir: ".", exeExt: "", extSep: ".", props: {}), + (name: "Standalone", parDir: "..", dllFrmt: "lib$1.so", altDirSep: "/", + objExt: ".o", newLine: "\x0A", pathSep: ":", dirSep: "/", + scriptExt: ".sh", curDir: ".", exeExt: ".elf", extSep: ".", + props: {})] type TSystemCPU* = enum # Also add CPU for in initialization section and # alias conditionals to condsyms (end of module). cpuNone, cpuI386, cpuM68k, cpuAlpha, cpuPowerpc, cpuPowerpc64, cpuSparc, cpuVm, cpuIa64, cpuAmd64, cpuMips, cpuArm, - cpuEcmaScript, cpuNimrodVM + cpuEcmaScript, cpuNimrodVM, cpuAVR type TEndian* = enum @@ -169,7 +174,8 @@ const (name: "mips", intSize: 32, endian: bigEndian, floatSize: 64, bit: 32), (name: "arm", intSize: 32, endian: littleEndian, floatSize: 64, bit: 32), (name: "ecmascript", intSize: 32, endian: bigEndian,floatSize: 64,bit: 32), - (name: "nimrodvm", intSize: 32, endian: bigEndian, floatSize: 64, bit: 32)] + (name: "nimrodvm", intSize: 32, endian: bigEndian, floatSize: 64, bit: 32), + (name: "avr", intSize: 16, endian: littleEndian, floatSize: 32, bit: 16)] var targetCPU*, hostCPU*: TSystemCPU diff --git a/compiler/rodread.nim b/compiler/rodread.nim index 3385ce942..c3123161b 100755 --- a/compiler/rodread.nim +++ b/compiler/rodread.nim @@ -535,7 +535,7 @@ proc cmdChangeTriggersRecompilation(old, new: TCommands): bool = proc processRodFile(r: PRodReader, crc: TCrc32) = var w: string - d, L, inclCrc: int + d, inclCrc: int while r.s[r.pos] != '\0': var section = rdWord(r) if r.reason != rrNone: @@ -573,20 +573,17 @@ proc processRodFile(r: PRodReader, crc: TCrc32) = of "FILES": inc(r.pos, 2) # skip "(\10" inc(r.line) - L = 0 - while r.s[r.pos] > '\x0A' and r.s[r.pos] != ')': - setlen(r.files, L + 1) + while r.s[r.pos] != ')': var relativePath = decodeStr(r.s, r.pos) var resolvedPath = relativePath.findModule - r.files[L] = if resolvedPath.len > 0: resolvedPath else: relativePath + r.files.add(if resolvedPath.len > 0: resolvedPath else: relativePath) inc(r.pos) # skip #10 inc(r.line) - inc(L) if r.s[r.pos] == ')': inc(r.pos) of "INCLUDES": inc(r.pos, 2) # skip "(\10" inc(r.line) - while r.s[r.pos] > '\x0A' and r.s[r.pos] != ')': + while r.s[r.pos] != ')': w = r.files[decodeVInt(r.s, r.pos)] inc(r.pos) # skip ' ' inclCrc = decodeVInt(r.s, r.pos) @@ -597,13 +594,10 @@ proc processRodFile(r: PRodReader, crc: TCrc32) = inc(r.pos) inc(r.line) if r.s[r.pos] == ')': inc(r.pos) - of "DEPS": + of "DEPS": inc(r.pos) # skip ':' - L = 0 while r.s[r.pos] > '\x0A': - setlen(r.modDeps, L + 1) - r.modDeps[L] = r.files[decodeVInt(r.s, r.pos)] - inc(L) + r.modDeps.add r.files[decodeVInt(r.s, r.pos)] if r.s[r.pos] == ' ': inc(r.pos) of "INTERF": r.interfIdx = r.pos + 2 @@ -629,9 +623,11 @@ proc processRodFile(r: PRodReader, crc: TCrc32) = of "INIT": r.initIdx = r.pos + 2 # "(\10" skipSection(r) - else: - MsgWriteln("skipping section: " & section & - " at " & $r.pos & " in " & r.filename) + else: + InternalError("invalid section: '" & section & + "' at " & $r.line & " in " & r.filename) + #MsgWriteln("skipping section: " & section & + # " at " & $r.line & " in " & r.filename) skipSection(r) if r.s[r.pos] == '\x0A': inc(r.pos) diff --git a/compiler/semmagic.nim b/compiler/semmagic.nim index 932f36c2f..4f120e2ab 100644 --- a/compiler/semmagic.nim +++ b/compiler/semmagic.nim @@ -22,7 +22,7 @@ proc semSlurp(c: PContext, n: PNode, flags: TExprFlags): PNode = result = newStrNode(nkStrLit, content) result.typ = getSysType(tyString) result.info = n.info - c.slurpedFiles.add(filename) + c.slurpedFiles.add(a.strVal) except EIO: GlobalError(a.info, errCannotOpenFile, a.strVal) diff --git a/compiler/semthreads.nim b/compiler/semthreads.nim index 1295723cf..09668a912 100755 --- a/compiler/semthreads.nim +++ b/compiler/semthreads.nim @@ -107,14 +107,14 @@ proc analyseSym(c: PProcCtx, n: PNode): TThreadOwner = result = c.mapping[v.id] if result != toUndefined: return case v.kind - of skVar, skLet, skResult: + of skVar, skForVar, skLet, skResult: result = toNil if sfGlobal in v.flags: if sfThread in v.flags: result = toMine elif containsGarbageCollectedRef(v.typ): result = toTheirs - of skTemp, skForVar: result = toNil + of skTemp: result = toNil of skConst: result = toMine of skParam: result = c.mapping[v.id] diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim index 40176ad50..38cf19406 100755 --- a/compiler/semtypes.nim +++ b/compiler/semtypes.nim @@ -251,43 +251,38 @@ proc semIdentWithPragma(c: PContext, kind: TSymKind, n: PNode, result = semIdentVis(c, kind, n, allowed) proc checkForOverlap(c: PContext, t, ex: PNode, branchIndex: int) = + let ex = ex.skipConv for i in countup(1, branchIndex - 1): for j in countup(0, sonsLen(t.sons[i]) - 2): - if overlap(t.sons[i].sons[j], ex): + if overlap(t.sons[i].sons[j].skipConv, ex): LocalError(ex.info, errDuplicateCaseLabel) -proc semBranchExpr(c: PContext, t, e: PNode): PNode = - result = semConstExpr(c, e) +proc semBranchRange(c: PContext, t, a, b: PNode, covered: var biggestInt): PNode = checkMinSonsLen(t, 1) - result = fitNode(c, t.sons[0].typ, result) - #if cmpTypes(t.sons[0].typ, result.typ) <= isConvertible: - # typeMismatch(result, t.sons[0].typ, result.typ) + let ac = semConstExpr(c, a) + let bc = semConstExpr(c, b) + let at = fitNode(c, t.sons[0].typ, ac) + let bt = fitNode(c, t.sons[0].typ, bc) + + result = newNodeI(nkRange, a.info) + result.add(at) + result.add(bt) + if emptyRange(ac, bc): GlobalError(b.info, errRangeIsEmpty) + covered = covered + getOrdValue(bc) - getOrdValue(ac) + 1 proc SemCaseBranchRange(c: PContext, t, b: PNode, covered: var biggestInt): PNode = checkSonsLen(b, 3) - result = newNodeI(nkRange, b.info) - result.add(semBranchExpr(c, t, b.sons[1])) - result.add(semBranchExpr(c, t, b.sons[2])) - if emptyRange(result[0], result[1]): GlobalError(b.info, errRangeIsEmpty) - covered = covered + getOrdValue(result[1]) - getOrdValue(result[0]) + 1 + result = semBranchRange(c, t, b.sons[1], b.sons[2], covered) proc semCaseBranchSetElem(c: PContext, t, b: PNode, covered: var biggestInt): PNode = if isRange(b): checkSonsLen(b, 3) - result = newNodeI(nkRange, b.info) - result.add(semBranchExpr(c, t, b.sons[1])) - result.add(semBranchExpr(c, t, b.sons[2])) - if emptyRange(result[0], result[1]): GlobalError(b.info, errRangeIsEmpty) - covered = covered + getOrdValue(result[1]) - getOrdValue(result[0]) + 1 + result = semBranchRange(c, t, b.sons[1], b.sons[2], covered) elif b.kind == nkRange: checkSonsLen(b, 2) - result = newNodeI(nkRange, b.info) - result.add(semBranchExpr(c, t, b.sons[0])) - result.add(semBranchExpr(c, t, b.sons[1])) - if emptyRange(result[0], result[1]): GlobalError(b.info, errRangeIsEmpty) - covered = covered + getOrdValue(result[1]) - getOrdValue(result[0]) + 1 + result = semBranchRange(c, t, b.sons[0], b.sons[1], covered) else: result = fitNode(c, t.sons[0].typ, b) inc(covered) diff --git a/compiler/transf.nim b/compiler/transf.nim index 3c685eeb4..ff42ff592 100755 --- a/compiler/transf.nim +++ b/compiler/transf.nim @@ -267,14 +267,6 @@ proc transformLoopBody(c: PTransf, n: PNode): PTransNode = discard c.blockSyms.pop() else: result = transform(c, n) - -proc skipConv(n: PNode): PNode = - case n.kind - of nkObjUpConv, nkObjDownConv, nkChckRange, nkChckRangeF, nkChckRange64: - result = n.sons[0] - of nkHiddenStdConv, nkHiddenSubConv, nkConv: - result = n.sons[1] - else: result = n proc newTupleAccess(tup: PNode, i: int): PNode = result = newNodeIT(nkBracketExpr, tup.info, tup.typ.sons[i]) diff --git a/compiler/types.nim b/compiler/types.nim index 6b25a388f..3f7c2c820 100755 --- a/compiler/types.nim +++ b/compiler/types.nim @@ -869,9 +869,11 @@ proc typeAllowedAux(marker: var TIntSet, typ: PType, kind: TSymKind): bool = of tyArray: result = t.sons[1].kind == tyEmpty or typeAllowedAux(marker, t.sons[1], skVar) - of tyPtr, tyRef: + of tyRef: if kind == skConst: return false result = typeAllowedAux(marker, t.sons[0], skVar) + of tyPtr: + result = typeAllowedAux(marker, t.sons[0], skVar) of tyArrayConstr, tyTuple, tySet, tyConst, tyMutable, tyIter, tyProxy: for i in countup(0, sonsLen(t) - 1): result = typeAllowedAux(marker, t.sons[i], kind) diff --git a/doc/lib.txt b/doc/lib.txt index acdee3447..65f5d81ed 100755 --- a/doc/lib.txt +++ b/doc/lib.txt @@ -255,6 +255,10 @@ XML Processing * `htmlparser <htmlparser.html>`_ This module parses an HTML document and creates its XML tree representation. +* `htmlgen <htmlgen.html>`_ + This module implements a simple XML and HTML code + generator. Each commonly used HTML tag has a corresponding macro + that generates a string with its HTML representation. Cryptography and Hashing ------------------------ diff --git a/koch.nim b/koch.nim index add08625a..9eec6bd76 100755 --- a/koch.nim +++ b/koch.nim @@ -141,7 +141,7 @@ proc boot(args: string) = return copyExe(output, (i+1).thVersion) copyExe(output, finalDest) - echo "[Warning] executables are still not equal" + when not defined(windows): echo "[Warning] executables are still not equal" # -------------- clean -------------------------------------------------------- diff --git a/lib/pure/irc.nim b/lib/pure/irc.nim index 8946a9d8f..81dff024e 100644 --- a/lib/pure/irc.nim +++ b/lib/pure/irc.nim @@ -142,6 +142,8 @@ proc parseMessage(msg: string): TIRCEvent = inc(i) # Skip `:` var nick = "" i.inc msg.parseUntil(nick, {'!', ' '}, i) + result.nick = "" + result.serverName = "" if msg[i] == '!': result.nick = nick inc(i) # Skip `!` @@ -237,7 +239,8 @@ proc processLine(irc: var TIRC, line: string): TIRCEvent = result = parseMessage(line) # Get the origin result.origin = result.params[0] - if result.origin == irc.nick: result.origin = result.nick + if result.origin == irc.nick and + result.nick != "": result.origin = result.nick if result.cmd == MError: irc.close() @@ -386,6 +389,7 @@ proc asyncIRC*(address: string, port: TPort = 6667.TPort, result.messageBuffer = @[] result.handleEvent = ircEvent result.userArg = userArg + result.lineBuffer = "" proc register*(d: PDispatcher, irc: PAsyncIRC) = ## Registers ``irc`` with dispatcher ``d``. diff --git a/lib/pure/sockets.nim b/lib/pure/sockets.nim index 5721d79fe..31973c6ce 100755 --- a/lib/pure/sockets.nim +++ b/lib/pure/sockets.nim @@ -8,8 +8,12 @@ # ## This module implements a simple portable type-safe sockets layer. +## +## Most procedures raise EOS on error. + import os, parseutils +from times import epochTime when defined(Windows): import winlean @@ -59,6 +63,8 @@ type TRecvLineResult* = enum ## result for recvLineAsync RecvFullLine, RecvPartialLine, RecvDisconnected, RecvFail + ETimeout* = object of ESynch + const InvalidSocket* = TSocket(-1'i32) ## invalid socket number @@ -377,12 +383,14 @@ proc connect*(socket: TSocket, name: string, port = TPort(0), ## host name. If ``name`` is a host name, this function will try each IP ## of that host name. ``htons`` is already performed on ``port`` so you must ## not do it. + var hints: TAddrInfo var aiList: ptr TAddrInfo = nil hints.ai_family = toInt(af) hints.ai_socktype = toInt(SOCK_STREAM) hints.ai_protocol = toInt(IPPROTO_TCP) gaiNim(name, port, hints, aiList) + # try all possibilities: var success = false var it = aiList @@ -445,7 +453,6 @@ proc connectAsync*(socket: TSocket, name: string, port = TPort(0), freeaddrinfo(aiList) if not success: OSError() - proc timeValFromMilliseconds(timeout = 500): TTimeVal = if timeout != -1: var seconds = timeout div 1000 @@ -545,7 +552,33 @@ proc select*(readfds: var seq[TSocket], timeout = 500): int = result = int(select(cint(m+1), addr(rd), nil, nil, nil)) pruneSocketSet(readfds, (rd)) + +proc recv*(socket: TSocket, data: pointer, size: int): int = + ## receives data from a socket + result = recv(cint(socket), data, size, 0'i32) +template waitFor(): stmt = + if timeout - int(waited * 1000.0) < 1: + raise newException(ETimeout, "Call to recv() timed out.") + var s = @[socket] + var startTime = epochTime() + if select(s, timeout - int(waited * 1000.0)) != 1: + raise newException(ETimeout, "Call to recv() timed out.") + waited += (epochTime() - startTime) + +proc recv*(socket: TSocket, data: var string, size: int, timeout: int): int = + ## overload with a ``timeout`` parameter in miliseconds. + var waited = 0.0 # number of seconds already waited + + var read = 0 + while read < size: + waitFor() + result = recv(cint(socket), addr(data[read]), 1, 0'i32) + if result < 0: + return + inc(read) + + result = read proc recvLine*(socket: TSocket, line: var TaintedString): bool = ## returns false if no further data is available. `Line` must be initialized @@ -567,6 +600,29 @@ proc recvLine*(socket: TSocket, line: var TaintedString): bool = elif c == '\L': return true add(line.string, c) +proc recvLine*(socket: TSocket, line: var TaintedString, timeout: int): bool = + ## variant with a ``timeout`` parameter, the timeout parameter specifies + ## how many miliseconds to wait for data. + + var waited = 0.0 # number of seconds already waited + + setLen(line.string, 0) + while true: + var c: char + waitFor() + var n = recv(cint(socket), addr(c), 1, 0'i32) + if n < 0: return + elif n == 0: return true + if c == '\r': + waitFor() + n = recv(cint(socket), addr(c), 1, MSG_PEEK) + if n > 0 and c == '\L': + discard recv(cint(socket), addr(c), 1, 0'i32) + elif n <= 0: return false + return true + elif c == '\L': return true + add(line.string, c) + proc recvLineAsync*(socket: TSocket, line: var TaintedString): TRecvLineResult = ## similar to ``recvLine`` but for non-blocking sockets. ## The values of the returned enum should be pretty self explanatory: @@ -592,10 +648,6 @@ proc recvLineAsync*(socket: TSocket, line: var TaintedString): TRecvLineResult = elif c == '\L': return RecvFullLine add(line.string, c) -proc recv*(socket: TSocket, data: pointer, size: int): int = - ## receives data from a socket - result = recv(cint(socket), data, size, 0'i32) - proc recv*(socket: TSocket): TaintedString = ## receives all the data from the socket. ## Socket errors will result in an ``EOS`` error. @@ -625,6 +677,16 @@ proc recv*(socket: TSocket): TaintedString = add(result.string, buf) if bytesRead != bufSize-1: break +proc recvTimeout*(socket: TSocket, timeout: int): TaintedString = + ## overloaded variant to support a ``timeout`` parameter, the ``timeout`` + ## parameter specifies the amount of miliseconds to wait for data on the + ## socket. + var s = @[socket] + if s.select(timeout) != 1: + raise newException(ETimeout, "Call to recv() timed out.") + + return socket.recv + proc recvAsync*(socket: TSocket, s: var TaintedString): bool = ## receives all the data from a non-blocking socket. If socket is non-blocking ## and there are no messages available, `False` will be returned. @@ -723,6 +785,21 @@ proc setBlocking*(s: TSocket, blocking: bool) = if fcntl(cint(s), F_SETFL, mode) == -1: OSError() +proc connect*(socket: TSocket, timeout: int, name: string, port = TPort(0), + af: TDomain = AF_INET) = + ## Overload for ``connect`` to support timeouts. The ``timeout`` parameter + ## specifies the time in miliseconds of how long to wait for a connection + ## to be made. + ## + ## **Warning:** If ``socket`` is non-blocking and timeout is not ``-1`` then + ## this function may set blocking mode on ``socket`` to true. + socket.setBlocking(true) + + socket.connectAsync(name, port, af) + var s: seq[TSocket] = @[socket] + if selectWrite(s, timeout) != 1: + raise newException(ETimeout, "Call to connect() timed out.") + when defined(Windows): var wsa: TWSADATA if WSAStartup(0x0101'i16, wsa) != 0: OSError() diff --git a/lib/system.nim b/lib/system.nim index e1b82b6bb..78912be7e 100755 --- a/lib/system.nim +++ b/lib/system.nim @@ -851,7 +851,7 @@ template sysAssert(cond: bool, msg: string) = include "system/inclrtl" -when not defined(ecmascript) and not defined(nimrodVm): +when not defined(ecmascript) and not defined(nimrodVm) and not defined(avr): include "system/cgprocs" proc add *[T](x: var seq[T], y: T) {.magic: "AppendSeqElem", noSideEffect.} @@ -1604,16 +1604,17 @@ when not defined(EcmaScript) and not defined(NimrodVM): {.push stack_trace: off.} proc initGC() - when not defined(boehmgc): + when not defined(boehmgc) and not defined(useMalloc): proc initAllocator() {.inline.} - proc initStackBottom() {.inline.} = - # WARNING: This is very fragile! An array size of 8 does not work on my - # Linux 64bit system. Very strange, but we are at the will of GCC's - # optimizer... - var locals {.volatile.}: pointer - locals = addr(locals) - setStackBottom(locals) + when not defined(nogc): + proc initStackBottom() {.inline.} = + # WARNING: This is very fragile! An array size of 8 does not work on my + # Linux 64bit system. Very strange, but we are at the will of GCC's + # optimizer... + var locals {.volatile.}: pointer + locals = addr(locals) + setStackBottom(locals) var strDesc: TNimType @@ -1868,7 +1869,7 @@ when not defined(EcmaScript) and not defined(NimrodVM): when hasThreadSupport: include "system/syslocks" include "system/threads" - else: + elif not defined(nogc): initStackBottom() initGC() @@ -1881,21 +1882,23 @@ when not defined(EcmaScript) and not defined(NimrodVM): ## for debug builds. {.push stack_trace: off.} - include "system/excpt" + when hostCPU == "avr": + include "system/embedded" + else: + include "system/excpt" + # we cannot compile this with stack tracing on # as it would recurse endlessly! include "system/arithm" {.pop.} # stack trace {.pop.} # stack trace - include "system/dyncalls" + when hostOS != "standalone": include "system/dyncalls" include "system/sets" const GenericSeqSize = (2 * sizeof(int)) - proc reprAny(p: pointer, typ: PNimType): string {.compilerRtl.} - proc getDiscriminant(aa: Pointer, n: ptr TNimNode): int = sysAssert(n.kind == nkCase, "getDiscriminant: node != nkCase") var d: int @@ -1918,7 +1921,7 @@ when not defined(EcmaScript) and not defined(NimrodVM): include "system/mmdisp" {.push stack_trace: off.} - include "system/sysstr" + when hostCPU != "avr": include "system/sysstr" {.pop.} include "system/sysio" @@ -1938,18 +1941,19 @@ when not defined(EcmaScript) and not defined(NimrodVM): var res = TaintedString(newStringOfCap(80)) while f.readLine(res): yield TaintedString(res) - include "system/assign" - include "system/repr" + when hostCPU != "avr": + include "system/assign" + include "system/repr" - proc getCurrentException*(): ref E_Base {.compilerRtl, inl.} = - ## retrieves the current exception; if there is none, nil is returned. - result = currException + proc getCurrentException*(): ref E_Base {.compilerRtl, inl.} = + ## retrieves the current exception; if there is none, nil is returned. + result = currException - proc getCurrentExceptionMsg*(): string {.inline.} = - ## retrieves the error message that was attached to the current - ## exception; if there is none, "" is returned. - var e = getCurrentException() - return if e == nil: "" else: e.msg + proc getCurrentExceptionMsg*(): string {.inline.} = + ## retrieves the error message that was attached to the current + ## exception; if there is none, "" is returned. + var e = getCurrentException() + return if e == nil: "" else: e.msg {.push stack_trace: off.} when defined(endb): diff --git a/lib/system/ansi_c.nim b/lib/system/ansi_c.nim index 9722e1396..e328f7099 100755 --- a/lib/system/ansi_c.nim +++ b/lib/system/ansi_c.nim @@ -103,4 +103,3 @@ proc c_getenv(env: CString): CString {.importc: "getenv", noDecl.} proc c_putenv(env: CString): cint {.importc: "putenv", noDecl.} {.pop} - diff --git a/lib/system/embedded.nim b/lib/system/embedded.nim new file mode 100644 index 000000000..f17432561 --- /dev/null +++ b/lib/system/embedded.nim @@ -0,0 +1,106 @@ +# +# +# Nimrod's Runtime Library +# (c) Copyright 2012 Andreas Rumpf +# +# See the file "copying.txt", included in this +# distribution, for details about the copyright. +# + + +# Bare-bones implementation of some things for embedded targets. + +proc writeToStdErr(msg: CString) = write(stdout, msg) + +proc chckIndx(i, a, b: int): int {.inline, compilerproc.} +proc chckRange(i, a, b: int): int {.inline, compilerproc.} +proc chckRangeF(x, a, b: float): float {.inline, compilerproc.} +proc chckNil(p: pointer) {.inline, compilerproc.} + +proc pushFrame(s: PFrame) {.compilerRtl, inl.} = nil +proc popFrame {.compilerRtl, inl.} = nil + +proc setFrame(s: PFrame) {.compilerRtl, inl.} = nil +proc pushSafePoint(s: PSafePoint) {.compilerRtl, inl.} = nil +proc popSafePoint {.compilerRtl, inl.} = nil +proc pushCurrentException(e: ref E_Base) {.compilerRtl, inl.} = nil +proc popCurrentException {.compilerRtl, inl.} = nil + +# some platforms have native support for stack traces: +const + nativeStackTraceSupported = false + hasSomeStackTrace = false + +proc quitOrDebug() {.inline.} = + quit(1) + +proc raiseException(e: ref E_Base, ename: CString) {.compilerRtl.} = + writeToStdErr(ename) + +proc reraiseException() {.compilerRtl.} = + writeToStdErr("reraise not supported") + +proc WriteStackTrace() = nil + +proc setControlCHook(hook: proc () {.noconv.}) = + # ugly cast, but should work on all architectures: + type TSignalHandler = proc (sig: cint) {.noconv.} + c_signal(SIGINT, cast[TSignalHandler](hook)) + +proc raiseRangeError(val: biggestInt) {.compilerproc, noreturn, noinline.} = + writeToStdErr("value out of range") + +proc raiseIndexError() {.compilerproc, noreturn, noinline.} = + writeToStdErr("index out of bounds") + +proc raiseFieldError(f: string) {.compilerproc, noreturn, noinline.} = + writeToStdErr("field is not accessible") + +proc chckIndx(i, a, b: int): int = + if i >= a and i <= b: + return i + else: + raiseIndexError() + +proc chckRange(i, a, b: int): int = + if i >= a and i <= b: + return i + else: + raiseRangeError(i) + +proc chckRange64(i, a, b: int64): int64 {.compilerproc.} = + if i >= a and i <= b: + return i + else: + raiseRangeError(i) + +proc chckRangeF(x, a, b: float): float = + if x >= a and x <= b: + return x + else: + raise newException(EOutOfRange, "value " & $x & " out of range") + +proc chckNil(p: pointer) = + if p == nil: c_raise(SIGSEGV) + +proc chckObj(obj, subclass: PNimType) {.compilerproc.} = + # checks if obj is of type subclass: + var x = obj + if x == subclass: return # optimized fast path + while x != subclass: + if x == nil: + raise newException(EInvalidObjectConversion, "invalid object conversion") + x = x.base + +proc chckObjAsgn(a, b: PNimType) {.compilerproc, inline.} = + if a != b: + raise newException(EInvalidObjectAssignment, "invalid object assignment") + +proc isObj(obj, subclass: PNimType): bool {.compilerproc.} = + # checks if obj is of type subclass: + var x = obj + if x == subclass: return true # optimized fast path + while x != subclass: + if x == nil: return false + x = x.base + return true diff --git a/lib/system/excpt.nim b/lib/system/excpt.nim index 8ffca90fb..2df7fe4ad 100755 --- a/lib/system/excpt.nim +++ b/lib/system/excpt.nim @@ -27,7 +27,7 @@ else: proc writeToStdErr(msg: CString) = discard MessageBoxA(0, msg, nil, 0) -proc registerSignalHandler() {.compilerproc.} +proc registerSignalHandler() proc chckIndx(i, a, b: int): int {.inline, compilerproc.} proc chckRange(i, a, b: int): int {.inline, compilerproc.} diff --git a/lib/system/mmdisp.nim b/lib/system/mmdisp.nim index e8ad23970..1abf3fbbf 100755 --- a/lib/system/mmdisp.nim +++ b/lib/system/mmdisp.nim @@ -181,6 +181,78 @@ when defined(boehmgc): proc deallocOsPages() {.inline.} = nil include "system/cellsets" +elif defined(nogc) and defined(useMalloc): + + when not defined(useNimRtl): + proc alloc(size: int): pointer = + result = cmalloc(size) + if result == nil: raiseOutOfMem() + proc alloc0(size: int): pointer = + result = alloc(size) + zeroMem(result, size) + proc realloc(p: Pointer, newsize: int): pointer = + result = crealloc(p, newsize) + if result == nil: raiseOutOfMem() + proc dealloc(p: Pointer) = cfree(p) + + proc allocShared(size: int): pointer = + result = cmalloc(size) + if result == nil: raiseOutOfMem() + proc allocShared0(size: int): pointer = + result = alloc(size) + zeroMem(result, size) + proc reallocShared(p: Pointer, newsize: int): pointer = + result = crealloc(p, newsize) + if result == nil: raiseOutOfMem() + proc deallocShared(p: Pointer) = cfree(p) + + proc GC_disable() = nil + proc GC_enable() = nil + proc GC_fullCollect() = nil + proc GC_setStrategy(strategy: TGC_Strategy) = nil + proc GC_enableMarkAndSweep() = nil + proc GC_disableMarkAndSweep() = nil + proc GC_getStatistics(): string = return "" + + proc getOccupiedMem(): int = nil + proc getFreeMem(): int = nil + proc getTotalMem(): int = nil + + proc setStackBottom(theStackBottom: pointer) = nil + + proc initGC() = nil + + proc newObj(typ: PNimType, size: int): pointer {.compilerproc.} = + result = alloc(size) + proc newSeq(typ: PNimType, len: int): pointer {.compilerproc.} = + result = newObj(typ, addInt(mulInt(len, typ.base.size), GenericSeqSize)) + cast[PGenericSeq](result).len = len + cast[PGenericSeq](result).reserved = len + + proc growObj(old: pointer, newsize: int): pointer = + result = realloc(old, newsize) + + proc nimGCref(p: pointer) {.compilerproc, inline.} = nil + proc nimGCunref(p: pointer) {.compilerproc, inline.} = nil + + proc unsureAsgnRef(dest: ppointer, src: pointer) {.compilerproc, inline.} = + dest[] = src + proc asgnRef(dest: ppointer, src: pointer) {.compilerproc, inline.} = + dest[] = src + proc asgnRefNoCycle(dest: ppointer, src: pointer) {.compilerproc, inline.} = + dest[] = src + + type + TMemRegion = object {.final, pure.} + + proc Alloc(r: var TMemRegion, size: int): pointer = + result = alloc(size) + proc Alloc0(r: var TMemRegion, size: int): pointer = + result = alloc0(size) + proc Dealloc(r: var TMemRegion, p: Pointer) = Dealloc(p) + proc deallocOsPages(r: var TMemRegion) {.inline.} = nil + proc deallocOsPages() {.inline.} = nil + elif defined(nogc): # Even though we don't want the GC, we cannot simply use C's memory manager # because Nimrod's runtime wants ``realloc`` to zero out the additional diff --git a/lib/system/repr.nim b/lib/system/repr.nim index 2dec8136c..ec02f5e08 100755 --- a/lib/system/repr.nim +++ b/lib/system/repr.nim @@ -9,6 +9,9 @@ # The generic ``repr`` procedure. It is an invaluable debugging tool. +when not defined(useNimRtl): + proc reprAny(p: pointer, typ: PNimType): string {.compilerRtl.} + proc reprInt(x: int64): string {.compilerproc.} = return $x proc reprFloat(x: float): string {.compilerproc.} = return $x diff --git a/tests/benchmark.nim b/tests/benchmark.nim new file mode 100644 index 000000000..8dd9cc2e2 --- /dev/null +++ b/tests/benchmark.nim @@ -0,0 +1,47 @@ +# +# +# Nimrod Benchmark tool +# (c) Copyright 2012 Dominik Picheta +# +# See the file "copying.txt", included in this +# distribution, for details about the copyright. +# + +## This program runs benchmarks +import osproc, os, times, json + +type + TBenchResult = tuple[file: string, success: bool, time: float] + +proc compileBench(file: string) = + ## Compiles ``file``. + doAssert(execCmdEx("nimrod c -d:release " & file).exitCode == QuitSuccess) + +proc runBench(file: string): TBenchResult = + ## Runs ``file`` and returns info on how long it took to run. + var start = epochTime() + if execCmdEx(file.addFileExt(ExeExt)).exitCode == QuitSuccess: + var t = epochTime() - start + result = (file, true, t) + else: result = (file, false, -1.0) + +proc genOutput(benches: seq[TBenchResult]): PJsonNode = + result = newJObject() + for i in benches: + if i.success: + result[i.file.extractFilename] = newJFloat(i.time) + else: + result[i.file.extractFilename] = newJNull() + +proc doBench(): seq[TBenchResult] = + result = @[] + for i in walkFiles("tests/benchmarks/*.nim"): + echo(i.extractFilename) + compileBench(i) + result.add(runBench(i)) + +when isMainModule: + var b = doBench() + var output = genOutput(b) + writeFile("benchmarkResults.json", pretty(output)) + \ No newline at end of file diff --git a/tests/benchmarks/fannkuch.nim b/tests/benchmarks/fannkuch.nim new file mode 100644 index 000000000..15f78f50c --- /dev/null +++ b/tests/benchmarks/fannkuch.nim @@ -0,0 +1,69 @@ +import os +import strutils + +proc fannkuch (n: int): int = + var + count: seq[int] + maxFlips = 0 + m = n-1 + r = n + check = 0 + perm1: seq[int] + perm: seq[int] + + newSeq (count, n+1) + newSeq (perm1, n) + newSeq (perm, n) + for i in 0 .. n-1: + count[i] = i+1 + perm1[i] = i + perm[i] = i + count[n] = n+1 + + while True: + if check < 30: + for i in items (perm1): + write (stdout, $(i+1)) + echo ("") + inc (check) + + while r != 1: + count[r-1] = r + dec (r) + + if perm1[0] != 0 and perm1[m] != m: + # perm = perm1 + # The above line is between 3 and 4 times slower than the loop below! + for i in 0 .. n-1: + perm[i] = perm1[i] + var flipsCount = 0 + var k = perm[0] + while k != 0: + for i in 0 .. (k div 2): + swap (perm[i], perm[k-i]) + inc (flipsCount) + k = perm[0] + + if flipsCount > maxFlips: + maxFlips = flipsCount + + block makePerm: + while r != n: + var tmp = perm1[0] + # # perm1.delete (0) + # # perm1.insert (tmp, r) + # # The above is about twice as slow as the following: + # moveMem (addr (perm1[0]), addr (perm1[1]), r * sizeof (int)) + # The call to moveMem is about 50% slower than the loop below! + for i in 0 .. r-1: + perm1[i] = perm1[i+1] + perm1[r] = tmp + + dec (count[r]) + if count[r] > 0: + break makePerm + inc (r) + return maxFlips + +var n = 10 +echo ("Pfannkuchen(" & $n & ") = " & $fannkuch (n)) \ No newline at end of file diff --git a/tests/benchmarks/quicksort.nim b/tests/benchmarks/quicksort.nim new file mode 100644 index 000000000..599e3674c --- /dev/null +++ b/tests/benchmarks/quicksort.nim @@ -0,0 +1,54 @@ +import os +import strutils + +# Generate some pseudo-random data +var seed: tuple[s1, s2, s3: int32] = (2'i32, 8'i32, 16'i32) + +proc random(): int32 = + seed = (((((((seed[0] and 0x0007_FFFF'i32) shl 13'i32) xor seed[0]) shr + 19'i32) and 0x0000_1FFF'i32) xor + ((seed[0] and 0x000F_FFFE'i32) shl 12'i32)), + + ((((((seed[1] and 0x3FFF_FFFF'i32) shl 2'i32) xor seed[1]) shr + 25'i32) and 0x0000_007F'i32) xor + ((seed[1] and 0x0FFF_FFF8'i32) shl 4'i32)), + + ((((((seed[2] and 0x1FFF_FFFF'i32) shl 3'i32) xor seed[2]) shr + 11'i32) and 0x001F_FFFF'i32) xor + ((seed[2] and 0x0000_7FF0'i32) shl 17'i32))) + return seed[0] xor seed[1] xor seed[2] + +var n = 9999999 + +var data: seq[int32] +newSeq (data, n) +for i in 0 .. data.high(): + data[i] = random() + + +proc `$` (d: seq[int32]): string = + result = "[ " + for i in items (d): + result.addSep (", ", 2) + result.add ($(i and 0xFFFF_FFFF'i64)) + result.add (" ]") + +# Sort the data +proc sort (start, stop: int) = + if stop <= start+1: + return + + var j = start + for i in start..stop-2: + if data[i] <% data[stop-1]: + swap (data[i], data[j]) + inc (j) + swap (data[j], data[stop-1]) + + sort (start, j) + sort (j+1, stop) + +sort (0, data.len) +echo (data[n div 2 - 1] and 0xFFFF_FFFF'i64, ", ", + data[n div 2] and 0xFFFF_FFFF'i64, ", ", + data[n div 2 + 1] and 0xFFFF_FFFF'i64) \ No newline at end of file diff --git a/tests/compile/tglobalforvar.nim b/tests/compile/tglobalforvar.nim new file mode 100644 index 000000000..9f61ebcab --- /dev/null +++ b/tests/compile/tglobalforvar.nim @@ -0,0 +1,7 @@ + +var funcs: seq[proc (): int] = @[] +for i in 0..10: + funcs.add((proc (): int = return i * i)) + +echo(funcs[3]()) + diff --git a/tests/compile/tircbot.nim b/tests/compile/tircbot.nim new file mode 100644 index 000000000..91be18092 --- /dev/null +++ b/tests/compile/tircbot.nim @@ -0,0 +1,447 @@ +import irc, sockets, asyncio, json, os, strutils, times, redis + +type + TDb* = object + r*: TRedis + lastPing: float + + TBuildResult* = enum + bUnknown, bFail, bSuccess + + TTestResult* = enum + tUnknown, tFail, tSuccess + + TEntry* = tuple[c: TCommit, p: seq[TPlatform]] + + TCommit* = object + commitMsg*, username*, hash*: string + date*: TTime + + TPlatform* = object + buildResult*: TBuildResult + testResult*: TTestResult + failReason*, platform*: string + total*, passed*, skipped*, failed*: biggestInt + csources*: bool + +const + listName = "commits" + failOnExisting = False + +proc open*(host = "localhost", port: TPort): TDb = + result.r = redis.open(host, port) + result.lastPing = epochTime() + +proc customHSet(database: TDb, name, field, value: string) = + if database.r.hSet(name, field, value).int == 0: + if failOnExisting: + assert(false) + else: + echo("[Warning:REDIS] ", field, " already exists in ", name) + +proc updateProperty*(database: TDb, commitHash, platform, property, + value: string) = + var name = platform & ":" & commitHash + if database.r.hSet(name, property, value).int == 0: + echo("[INFO:REDIS] '$1' field updated in hash" % [property]) + else: + echo("[INFO:REDIS] '$1' new field added to hash" % [property]) + +proc globalProperty*(database: TDb, commitHash, property, value: string) = + if database.r.hSet(commitHash, property, value).int == 0: + echo("[INFO:REDIS] '$1' field updated in hash" % [property]) + else: + echo("[INFO:REDIS] '$1' new field added to hash" % [property]) + +proc addCommit*(database: TDb, commitHash, commitMsg, user: string) = + # Add the commit hash to the `commits` list. + discard database.r.lPush(listName, commitHash) + # Add the commit message, current date and username as a property + globalProperty(database, commitHash, "commitMsg", commitMsg) + globalProperty(database, commitHash, "date", $int(getTime())) + globalProperty(database, commitHash, "username", user) + +proc keepAlive*(database: var TDb) = + ## Keep the connection alive. Ping redis in this case. This functions does + ## not guarantee that redis will be pinged. + var t = epochTime() + if t - database.lastPing >= 60.0: + echo("PING -> redis") + assert(database.r.ping() == "PONG") + database.lastPing = t + +proc getCommits*(database: TDb, + plStr: var seq[string]): seq[TEntry] = + result = @[] + var commitsRaw = database.r.lrange("commits", 0, -1) + for c in items(commitsRaw): + var commit: TCommit + commit.hash = c + for key, value in database.r.hPairs(c): + case normalize(key) + of "commitmsg": commit.commitMsg = value + of "date": commit.date = TTime(parseInt(value)) + of "username": commit.username = value + else: + echo(key) + assert(false) + + var platformsRaw = database.r.lrange(c & ":platforms", 0, -1) + var platforms: seq[TPlatform] = @[] + for p in items(platformsRaw): + var platform: TPlatform + for key, value in database.r.hPairs(p & ":" & c): + case normalize(key) + of "buildresult": + platform.buildResult = parseInt(value).TBuildResult + of "testresult": + platform.testResult = parseInt(value).TTestResult + of "failreason": + platform.failReason = value + of "total": + platform.total = parseBiggestInt(value) + of "passed": + platform.passed = parseBiggestInt(value) + of "skipped": + platform.skipped = parseBiggestInt(value) + of "failed": + platform.failed = parseBiggestInt(value) + of "csources": + platform.csources = if value == "t": true else: false + else: + echo(normalize(key)) + assert(false) + + platform.platform = p + + platforms.add(platform) + if p notin plStr: + plStr.add(p) + result.add((commit, platforms)) + +proc commitExists*(database: TDb, commit: string, starts = false): bool = + # TODO: Consider making the 'commits' list a set. + for c in items(database.r.lrange("commits", 0, -1)): + if starts: + if c.startsWith(commit): return true + else: + if c == commit: return true + return false + +proc platformExists*(database: TDb, commit: string, platform: string): bool = + for p in items(database.r.lrange(commit & ":" & "platforms", 0, -1)): + if p == platform: return true + +proc expandHash*(database: TDb, commit: string): string = + for c in items(database.r.lrange("commits", 0, -1)): + if c.startsWith(commit): return c + assert false + +proc isNewest*(database: TDb, commit: string): bool = + return database.r.lIndex("commits", 0) == commit + +proc getNewest*(database: TDb): string = + return database.r.lIndex("commits", 0) + +proc addPlatform*(database: TDb, commit: string, platform: string) = + assert database.commitExists(commit) + assert (not database.platformExists(commit, platform)) + var name = platform & ":" & commit + if database.r.exists(name): + if failOnExisting: quit("[FAIL] " & name & " already exists!", 1) + else: echo("[Warning] " & name & " already exists!") + + discard database.r.lPush(commit & ":" & "platforms", platform) + +proc `[]`*(p: seq[TPlatform], name: string): TPlatform = + for platform in items(p): + if platform.platform == name: + return platform + raise newException(EInvalidValue, name & " platforms not found in commits.") + +proc contains*(p: seq[TPlatform], s: string): bool = + for i in items(p): + if i.platform == s: + return True + + +type + PState = ref TState + TState = object of TObject + dispatcher: PDispatcher + sock: PAsyncSocket + ircClient: PAsyncIRC + hubPort: TPort + database: TDb + dbConnected: bool + + TSeenType = enum + PSeenJoin, PSeenPart, PSeenMsg, PSeenNick, PSeenQuit + + TSeen = object + nick: string + channel: string + timestamp: TTime + case kind*: TSeenType + of PSeenJoin: nil + of PSeenPart, PSeenQuit, PSeenMsg: + msg: string + of PSeenNick: + newNick: string + +const + ircServer = "irc.freenode.net" + joinChans = @["#nimrod"] + botNickname = "NimBot" + +proc setSeen(d: TDb, s: TSeen) = + discard d.r.del("seen:" & s.nick) + + var hashToSet = @[("type", $s.kind.int), ("channel", s.channel), + ("timestamp", $s.timestamp.int)] + case s.kind + of PSeenJoin: nil + of PSeenPart, PSeenMsg, PSeenQuit: + hashToSet.add(("msg", s.msg)) + of PSeenNick: + hashToSet.add(("newnick", s.newNick)) + + d.r.hMSet("seen:" & s.nick, hashToSet) + +proc getSeen(d: TDb, nick: string, s: var TSeen): bool = + if d.r.exists("seen:" & nick): + result = true + s.nick = nick + # Get the type first + s.kind = d.r.hGet("seen:" & nick, "type").parseInt.TSeenType + + for key, value in d.r.hPairs("seen:" & nick): + case normalize(key) + of "type": + #s.kind = value.parseInt.TSeenType + of "channel": + s.channel = value + of "timestamp": + s.timestamp = TTime(value.parseInt) + of "msg": + s.msg = value + of "newnick": + s.newNick = value + +template createSeen(typ: TSeenType, n, c: string): stmt = + var seenNick: TSeen + seenNick.kind = typ + seenNick.nick = n + seenNick.channel = c + seenNick.timestamp = getTime() + +proc parseReply(line: string, expect: string): Bool = + var jsonDoc = parseJson(line) + return jsonDoc["reply"].str == expect + +proc limitCommitMsg(m: string): string = + ## Limits the message to 300 chars and adds ellipsis. + var m1 = m + if NewLines in m1: + m1 = m1.splitLines()[0] + + if m1.len >= 300: + m1 = m1[0..300] + + if m1.len >= 300 or NewLines in m: m1.add("... ") + + if NewLines in m: m1.add($m.splitLines().len & " more lines") + + return m1 + +proc handleWebMessage(state: PState, line: string) = + echo("Got message from hub: " & line) + var json = parseJson(line) + if json.existsKey("payload"): + for i in 0..min(4, json["payload"]["commits"].len-1): + var commit = json["payload"]["commits"][i] + # Create the message + var message = "" + message.add(json["payload"]["repository"]["owner"]["name"].str & "/" & + json["payload"]["repository"]["name"].str & " ") + message.add(commit["id"].str[0..6] & " ") + message.add(commit["author"]["name"].str & " ") + message.add("[+" & $commit["added"].len & " ") + message.add("±" & $commit["modified"].len & " ") + message.add("-" & $commit["removed"].len & "]: ") + message.add(limitCommitMsg(commit["message"].str)) + + # Send message to #nimrod. + state.ircClient[].privmsg(joinChans[0], message) + elif json.existsKey("redisinfo"): + assert json["redisinfo"].existsKey("port") + let redisPort = json["redisinfo"]["port"].num + state.dbConnected = true + +proc hubConnect(state: PState) +proc handleConnect(s: PAsyncSocket, userArg: PObject) = + let state = PState(userArg) + try: + # Send greeting + var obj = newJObject() + obj["name"] = newJString("irc") + obj["platform"] = newJString("?") + state.sock.send($obj & "\c\L") + + # Wait for reply. + var line = "" + sleep(1500) + if state.sock.recvLine(line): + assert(line != "") + doAssert parseReply(line, "OK") + echo("The hub accepted me!") + else: + raise newException(EInvalidValue, + "Hub didn't accept me. Waited 1.5 seconds.") + + # ask for the redis info + var riobj = newJObject() + riobj["do"] = newJString("redisinfo") + state.sock.send($riobj & "\c\L") + + except EOS: + echo(getCurrentExceptionMsg()) + s.close() + echo("Waiting 5 seconds...") + sleep(5000) + state.hubConnect() + +proc handleRead(s: PAsyncSocket, userArg: PObject) = + let state = PState(userArg) + var line = "" + if state.sock.recvLine(line): + if line != "": + # Handle the message + state.handleWebMessage(line) + else: + echo("Disconnected from hub: ", OSErrorMsg()) + s.close() + echo("Reconnecting...") + state.hubConnect() + else: + echo(OSErrorMsg()) + +proc hubConnect(state: PState) = + state.sock = AsyncSocket() + state.sock.connect("127.0.0.1", state.hubPort) + state.sock.userArg = state + state.sock.handleConnect = handleConnect + state.sock.handleRead = handleRead + + state.dispatcher.register(state.sock) + +proc handleIrc(irc: var TAsyncIRC, event: TIRCEvent, userArg: PObject) = + let state = PState(userArg) + case event.typ + of EvDisconnected: + while not state.ircClient[].isConnected: + try: + state.ircClient.connect() + except: + echo("Error reconnecting: ", getCurrentExceptionMsg()) + + echo("Waiting 5 seconds...") + sleep(5000) + echo("Reconnected successfully!") + of EvMsg: + echo("< ", event.raw) + case event.cmd + of MPrivMsg: + let msg = event.params[event.params.len-1] + let words = msg.split(' ') + template pm(msg: string): stmt = + state.ircClient[].privmsg(event.origin, msg) + case words[0] + of "!ping": pm("pong") + of "!lag": + if state.ircClient[].getLag != -1.0: + var lag = state.ircClient[].getLag + lag = lag * 1000.0 + pm($int(lag) & "ms between me and the server.") + else: + pm("Unknown.") + of "!seen": + if words.len > 1: + let nick = words[1] + if nick == botNickname: + pm("Yes, I see myself.") + echo(nick) + var seenInfo: TSeen + if state.database.getSeen(nick, seenInfo): + var mSend = "" + case seenInfo.kind + of PSeenMsg: + pm("$1 was last seen on $2 in $3 saying: $4" % + [seenInfo.nick, $seenInfo.timestamp, + seenInfo.channel, seenInfo.msg]) + of PSeenJoin: + pm("$1 was last seen on $2 joining $3" % + [seenInfo.nick, $seenInfo.timestamp, seenInfo.channel]) + of PSeenPart: + pm("$1 was last seen on $2 leaving $3 with message: $4" % + [seenInfo.nick, $seenInfo.timestamp, seenInfo.channel, + seenInfo.msg]) + of PSeenQuit: + pm("$1 was last seen on $2 quitting with message: $3" % + [seenInfo.nick, $seenInfo.timestamp, seenInfo.msg]) + of PSeenNick: + pm("$1 was last seen on $2 changing nick to $3" % + [seenInfo.nick, $seenInfo.timestamp, seenInfo.newNick]) + + else: + pm("I have not seen " & nick) + else: + pm("Syntax: !seen <nick>") + + # TODO: ... commands + + # -- Seen + # Log this as activity. + createSeen(PSeenMsg, event.nick, event.origin) + seenNick.msg = msg + state.database.setSeen(seenNick) + of MJoin: + createSeen(PSeenJoin, event.nick, event.origin) + state.database.setSeen(seenNick) + of MPart: + createSeen(PSeenPart, event.nick, event.origin) + let msg = event.params[event.params.high] + seenNick.msg = msg + state.database.setSeen(seenNick) + of MQuit: + createSeen(PSeenQuit, event.nick, event.origin) + let msg = event.params[event.params.high] + seenNick.msg = msg + state.database.setSeen(seenNick) + of MNick: + createSeen(PSeenNick, event.nick, "#nimrod") + seenNick.newNick = event.params[0] + state.database.setSeen(seenNick) + else: + nil # TODO: ? + +proc open(port: TPort = TPort(5123)): PState = + new(result) + result.dispatcher = newDispatcher() + + result.hubPort = port + result.hubConnect() + + # Connect to the irc server. + result.ircClient = AsyncIrc(ircServer, nick = botNickname, user = botNickname, + joinChans = joinChans, ircEvent = handleIrc, userArg = result) + result.ircClient.connect() + result.dispatcher.register(result.ircClient) + + result.dbConnected = false + +var state = tircbot.open() # Connect to the website and the IRC server. + +while state.dispatcher.poll(): + if state.dbConnected: + state.database.keepAlive() diff --git a/tests/reject/t99bott.nim b/tests/reject/t99bott.nim index 7d11ba4b0..7ebfd61e9 100755 --- a/tests/reject/t99bott.nim +++ b/tests/reject/t99bott.nim @@ -1,7 +1,7 @@ discard """ file: "t99bott.nim" line: 26 - errormsg: "cannot evaluate 'GetBottleNumber(bn)'" + errormsg: "constant expression expected" disabled: false """ ## 99 Bottles of Beer diff --git a/tests/reject/tenumitems.nim b/tests/reject/tenumitems.nim index a0497fd59..b6eee5ba8 100644 --- a/tests/reject/tenumitems.nim +++ b/tests/reject/tenumitems.nim @@ -1,6 +1,6 @@ discard """ line: 7 - errormsg: "a type has no value" + errormsg: "type mismatch" """ type a = enum b,c,d diff --git a/todo.txt b/todo.txt index 9921e06d7..9772f4001 100755 --- a/todo.txt +++ b/todo.txt @@ -1,7 +1,7 @@ version 0.9.0 ============= -- bootstrapping fails with --symbolFiles:on again! +- make GC realtime capable: GC_step(ms: int) - ``=`` should be overloadable; requires specialization for ``=`` - fix remaining generics bugs - fix remaining closure bugs: @@ -49,6 +49,8 @@ Bugs without ``-d:release`` leaks memory? - bug: object {.pure, final.} does not work again! - bug: tsortdev does not run with native GC? +- bug: pragma statements in combination with symbol files are evaluated twice + but this can lead to compilation errors version 0.9.XX @@ -98,6 +100,7 @@ version 0.9.XX Library ------- +- provide more up to date OpenGL headers - wrappers for mongodb; poppler; libharu - suffix trees - locale support; i18n module diff --git a/web/index.txt b/web/index.txt index fcdccee49..3e939f492 100755 --- a/web/index.txt +++ b/web/index.txt @@ -42,11 +42,11 @@ priority). Nimrod is efficient =================== -* Native code generation (currently via compilation to C), not dependant on a +* Native code generation (currently via compilation to C), not dependent on a virtual machine: **Nimrod produces small executables without dependencies for easy redistribution.** -* A fast non-recursive incremental and generational garbage collector that - should be well suited for soft real-time systems (like games). +* A fast **non-tracing** garbage collector that should be well suited for soft + real-time systems (like games). * System programming features: Ability to manage your own memory and access the hardware directly. Pointers to garbage collected memory are distinguished from pointers to manually managed memory. diff --git a/web/news.txt b/web/news.txt index 633ad3d08..897961c25 100755 --- a/web/news.txt +++ b/web/news.txt @@ -17,6 +17,7 @@ Bugfixes Library Additions ----------------- +- Added the (already existing) module ``htmlgen`` to the documentation. - Added ``system.shallow`` that can be used to speed up string and sequence assignments. - Added ``system.eval`` that can execute an anonymous block of code at |