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

This commit is contained in:
Zahary Karadjov
2012-03-31 18:51:11 +03:00
36 changed files with 1017 additions and 109 deletions

View File

@@ -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!

View File

@@ -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

View File

@@ -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)

View File

@@ -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)

View File

@@ -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]])

View File

@@ -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)))

View File

@@ -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")

View File

@@ -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

View File

@@ -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 =

View File

@@ -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

View File

@@ -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)

View File

@@ -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)

View File

@@ -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]

View File

@@ -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)

View File

@@ -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])

View File

@@ -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)

View File

@@ -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
------------------------

View File

@@ -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 --------------------------------------------------------

View File

@@ -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``.

View File

@@ -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()

View File

@@ -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):

View File

@@ -103,4 +103,3 @@ proc c_getenv(env: CString): CString {.importc: "getenv", noDecl.}
proc c_putenv(env: CString): cint {.importc: "putenv", noDecl.}
{.pop}

106
lib/system/embedded.nim Normal file
View File

@@ -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

View File

@@ -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.}

View File

@@ -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

View File

@@ -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

47
tests/benchmark.nim Normal file
View File

@@ -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))

View File

@@ -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))

View File

@@ -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)

View File

@@ -0,0 +1,7 @@
var funcs: seq[proc (): int] = @[]
for i in 0..10:
funcs.add((proc (): int = return i * i))
echo(funcs[3]())

447
tests/compile/tircbot.nim Normal file
View File

@@ -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()

View File

@@ -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

View File

@@ -1,6 +1,6 @@
discard """
line: 7
errormsg: "a type has no value"
errormsg: "type mismatch"
"""
type a = enum b,c,d

View File

@@ -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

View File

@@ -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.

View File

@@ -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