diff options
author | Andreas Rumpf <rumpf_a@web.de> | 2017-02-17 10:48:01 +0100 |
---|---|---|
committer | Andreas Rumpf <rumpf_a@web.de> | 2017-02-17 10:48:01 +0100 |
commit | ba29ca0c63e50a06baf5935235b85e4ddc28fc96 (patch) | |
tree | abfeee6348d6419ba37fd4b938b5916dea66c6e9 | |
parent | 1189ae9b99eff08dc34f2d5338c65ca2096ca297 (diff) | |
parent | e9767d8809a0ff4df83ffff1836937db3c2885bf (diff) | |
download | Nim-ba29ca0c63e50a06baf5935235b85e4ddc28fc96.tar.gz |
Merge branch 'devel' into faster-nimsuggest
52 files changed, 1052 insertions, 285 deletions
diff --git a/appveyor.yml b/appveyor.yml index 87640b192..ea98b8507 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -7,10 +7,7 @@ cache: # - i686-4.9.2-release-win32-dwarf-rt_v4-rev4.7z matrix: - fast_finish: true # set this flag to immediately finish build once one of the jobs fails. - allow_failures: - - platform: x64 -# - platform: x86 + fast_finish: true environment: matrix: diff --git a/compiler/ast.nim b/compiler/ast.nim index 8fbec64cf..4ea68dc99 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -1541,8 +1541,7 @@ proc skipGenericOwner*(s: PSym): PSym = ## Generic instantiations are owned by their originating generic ## symbol. This proc skips such owners and goes straight to the owner ## of the generic itself (the module or the enclosing proc). - result = if s.kind in skProcKinds and {sfGenSym, sfFromGeneric} * s.flags == - {sfFromGeneric}: + result = if s.kind in skProcKinds and sfFromGeneric in s.flags: s.owner.owner else: s.owner diff --git a/compiler/astalgo.nim b/compiler/astalgo.nim index affbdffd9..226d5ee42 100644 --- a/compiler/astalgo.nim +++ b/compiler/astalgo.nim @@ -388,6 +388,7 @@ proc debugTree(n: PNode, indent: int, maxRecDepth: int; [istr, makeYamlString($n.kind)] addf(result, ",$N$1\"info\": $2", [istr, lineInfoToStr(n.info)]) if maxRecDepth != 0: + addf(result, ",$N$1\"flags\": $2", [istr, rope($n.flags)]) case n.kind of nkCharLit..nkUInt64Lit: addf(result, ",$N$1\"intVal\": $2", [istr, rope(n.intVal)]) diff --git a/compiler/ccgexprs.nim b/compiler/ccgexprs.nim index 95a7beada..6f7e83c18 100644 --- a/compiler/ccgexprs.nim +++ b/compiler/ccgexprs.nim @@ -220,7 +220,7 @@ proc genOptAsgnTuple(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) = optAsgnLoc(src, t, field), newflags) proc genOptAsgnObject(p: BProc, dest, src: TLoc, flags: TAssignmentFlags, - t: PNode) = + t: PNode, typ: PType) = if t == nil: return let newflags = if src.s == OnStatic: @@ -232,10 +232,11 @@ proc genOptAsgnObject(p: BProc, dest, src: TLoc, flags: TAssignmentFlags, case t.kind of nkSym: let field = t.sym + if field.loc.r == nil: fillObjectFields(p.module, typ) genAssignment(p, optAsgnLoc(dest, field.typ, field.loc.r), optAsgnLoc(src, field.typ, field.loc.r), newflags) of nkRecList: - for child in items(t): genOptAsgnObject(p, dest, src, newflags, child) + for child in items(t): genOptAsgnObject(p, dest, src, newflags, child, typ) else: discard proc genGenericAsgn(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) = @@ -315,9 +316,9 @@ proc genAssignment(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) = genGenericAsgn(p, dest, src, flags) elif needsComplexAssignment(ty): if ty.sons[0].isNil and asgnComplexity(ty.n) <= 4: - discard getTypeDesc(p.module, dest.t) + discard getTypeDesc(p.module, ty) internalAssert ty.n != nil - genOptAsgnObject(p, dest, src, flags, ty.n) + genOptAsgnObject(p, dest, src, flags, ty.n, ty) else: genGenericAsgn(p, dest, src, flags) else: diff --git a/compiler/ccgstmts.nim b/compiler/ccgstmts.nim index 45d675f64..02119d63e 100644 --- a/compiler/ccgstmts.nim +++ b/compiler/ccgstmts.nim @@ -1103,8 +1103,8 @@ proc asgnFieldDiscriminant(p: BProc, e: PNode) = genAssignment(p, a, tmp, {}) proc genAsgn(p: BProc, e: PNode, fastAsgn: bool) = - genLineDir(p, e) if e.sons[0].kind == nkSym and sfGoto in e.sons[0].sym.flags: + genLineDir(p, e) genGotoVar(p, e.sons[1]) elif not fieldDiscriminantCheckNeeded(p, e): var a: TLoc @@ -1114,8 +1114,11 @@ proc genAsgn(p: BProc, e: PNode, fastAsgn: bool) = initLocExpr(p, e.sons[0], a) if fastAsgn: incl(a.flags, lfNoDeepCopy) assert(a.t != nil) - loadInto(p, e.sons[0], e.sons[1], a) + let ri = e.sons[1] + genLineDir(p, ri) + loadInto(p, e.sons[0], ri, a) else: + genLineDir(p, e) asgnFieldDiscriminant(p, e) proc genStmts(p: BProc, t: PNode) = diff --git a/compiler/ccgthreadvars.nim b/compiler/ccgthreadvars.nim index 3efddbf30..505b69eab 100644 --- a/compiler/ccgthreadvars.nim +++ b/compiler/ccgthreadvars.nim @@ -57,8 +57,8 @@ proc generateThreadLocalStorage(m: BModule) = proc generateThreadVarsSize(m: BModule) = if nimtv != nil: - let externc = if gCmd != cmdCompileToCpp and - sfCompileToCpp in m.module.flags: "extern \"C\"" + let externc = if gCmd == cmdCompileToCpp or + sfCompileToCpp in m.module.flags: "extern \"C\" " else: "" addf(m.s[cfsProcs], "$#NI NimThreadVarsSize(){return (NI)sizeof(NimThreadVars);}$n", diff --git a/compiler/cgen.nim b/compiler/cgen.nim index 62ed9ad6e..63a7cda0e 100644 --- a/compiler/cgen.nim +++ b/compiler/cgen.nim @@ -715,10 +715,13 @@ proc genProcAux(m: BModule, prc: PSym) = add(generatedProc, ~"}$N") add(m.s[cfsProcs], generatedProc) -proc crossesCppBoundary(m: BModule; sym: PSym): bool {.inline.} = - result = sfCompileToCpp in m.module.flags and +proc requiresExternC(m: BModule; sym: PSym): bool {.inline.} = + result = (sfCompileToCpp in m.module.flags and sfCompileToCpp notin sym.getModule().flags and - gCmd != cmdCompileToCpp + gCmd != cmdCompileToCpp) or ( + sym.flags * {sfImportc, sfInfixCall, sfCompilerProc} == {sfImportc} and + sym.magic == mNone and + gCmd == cmdCompileToCpp) proc genProcPrototype(m: BModule, sym: PSym) = useHeader(m, sym) @@ -732,7 +735,7 @@ proc genProcPrototype(m: BModule, sym: PSym) = var header = genProcHeader(m, sym) if sfNoReturn in sym.flags and hasDeclspec in extccomp.CC[cCompiler].props: header = "__declspec(noreturn) " & header - if sym.typ.callConv != ccInline and crossesCppBoundary(m, sym): + if sym.typ.callConv != ccInline and requiresExternC(m, sym): header = "extern \"C\" " & header if sfPure in sym.flags and hasAttribute in CC[cCompiler].props: header.add(" __attribute__((naked))") diff --git a/compiler/extccomp.nim b/compiler/extccomp.nim index 0f283b208..1b068385a 100644 --- a/compiler/extccomp.nim +++ b/compiler/extccomp.nim @@ -566,16 +566,18 @@ proc needsExeExt(): bool {.inline.} = result = (optGenScript in gGlobalOptions and targetOS == osWindows) or (platform.hostOS == osWindows) -proc getCompilerExe(compiler: TSystemCC): string = - result = if gCmd == cmdCompileToCpp: CC[compiler].cppCompiler - else: CC[compiler].compilerExe +proc getCompilerExe(compiler: TSystemCC; cfile: string): string = + result = if gCmd == cmdCompileToCpp and not cfile.endsWith(".c"): + CC[compiler].cppCompiler + else: + CC[compiler].compilerExe if result.len == 0: rawMessage(errCompilerDoesntSupportTarget, CC[compiler].name) proc getLinkerExe(compiler: TSystemCC): string = result = if CC[compiler].linkerExe.len > 0: CC[compiler].linkerExe elif gMixedMode and gCmd != cmdCompileToCpp: CC[compiler].cppCompiler - else: compiler.getCompilerExe + else: compiler.getCompilerExe("") proc getCompileCFileCmd*(cfile: Cfile): string = var c = cCompiler @@ -596,7 +598,7 @@ proc getCompileCFileCmd*(cfile: Cfile): string = var options = cFileSpecificOptions(cfile.cname) var exe = getConfigVar(c, ".exe") - if exe.len == 0: exe = c.getCompilerExe + if exe.len == 0: exe = c.getCompilerExe(cfile.cname) if needsExeExt(): exe = addFileExt(exe, "exe") if optGenDynLib in gGlobalOptions and @@ -614,7 +616,7 @@ proc getCompileCFileCmd*(cfile: Cfile): string = compilePattern = joinPath(ccompilerpath, exe) else: includeCmd = "" - compilePattern = c.getCompilerExe + compilePattern = c.getCompilerExe(cfile.cname) var cf = if noAbsolutePaths(): extractFilename(cfile.cname) else: cfile.cname diff --git a/compiler/sem.nim b/compiler/sem.nim index dbf237635..fc7736b07 100644 --- a/compiler/sem.nim +++ b/compiler/sem.nim @@ -178,6 +178,11 @@ proc newSymG*(kind: TSymKind, n: PNode, c: PContext): PSym = if result.kind != kind: localError(n.info, "cannot use symbol of kind '" & $result.kind & "' as a '" & $kind & "'") + if sfGenSym in result.flags and result.kind notin {skTemplate, skMacro, skParam}: + # declarative context, so produce a fresh gensym: + result = copySym(result) + result.ast = n.sym.ast + put(c.p, n.sym, result) # when there is a nested proc inside a template, semtmpl # will assign a wrong owner during the first pass over the # template; we must fix it here: see #909 diff --git a/compiler/semdata.nim b/compiler/semdata.nim index 20fd1d9be..77a530a15 100644 --- a/compiler/semdata.nim +++ b/compiler/semdata.nim @@ -39,6 +39,7 @@ type next*: PProcCon # used for stacking procedure contexts wasForwarded*: bool # whether the current proc has a separate header bracketExpr*: PNode # current bracket expression (for ^ support) + mapping*: TIdTable TInstantiationPair* = object genericSym*: PSym @@ -147,6 +148,35 @@ proc lastOptionEntry*(c: PContext): POptionEntry = proc popProcCon*(c: PContext) {.inline.} = c.p = c.p.next +proc put*(p: PProcCon; key, val: PSym) = + if p.mapping.data == nil: initIdTable(p.mapping) + #echo "put into table ", key.info + p.mapping.idTablePut(key, val) + +proc get*(p: PProcCon; key: PSym): PSym = + if p.mapping.data == nil: return nil + result = PSym(p.mapping.idTableGet(key)) + +proc getGenSym*(c: PContext; s: PSym): PSym = + if sfGenSym notin s.flags: return s + var it = c.p + while it != nil: + result = get(it, s) + if result != nil: + #echo "got from table ", result.name.s, " ", result.info + return result + it = it.next + result = s + +proc considerGenSyms*(c: PContext; n: PNode) = + if n.kind == nkSym: + let s = getGenSym(c, n.sym) + if n.sym != s: + n.sym = s + else: + for i in 0..<n.safeLen: + considerGenSyms(c, n.sons[i]) + proc newOptionEntry*(): POptionEntry = new(result) result.options = gOptions diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index 57674735a..542d7b4e3 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -922,7 +922,8 @@ proc readTypeParameter(c: PContext, typ: PType, return newSymNode(copySym(tParam.sym).linkTo(foundTyp), info) #echo "came here: returned nil" -proc semSym(c: PContext, n: PNode, s: PSym, flags: TExprFlags): PNode = +proc semSym(c: PContext, n: PNode, sym: PSym, flags: TExprFlags): PNode = + let s = getGenSym(c, sym) case s.kind of skConst: markUsed(n.info, s) @@ -1093,11 +1094,11 @@ proc builtinFieldAccess(c: PContext, n: PNode, flags: TExprFlags): PNode = return readTypeParameter(c, ty, i, n.info) of tyObject, tyTuple: if ty.n != nil and ty.n.kind == nkRecList: - for field in ty.n: - if field.sym.name == i: - n.typ = newTypeWithSons(c, tyFieldAccessor, @[ty, field.sym.typ]) - n.typ.n = copyTree(n) - return n + let field = lookupInRecord(ty.n, i) + if field != nil: + n.typ = newTypeWithSons(c, tyFieldAccessor, @[ty, field.typ]) + n.typ.n = copyTree(n) + return n else: # echo "TYPE FIELD ACCESS" # debug ty diff --git a/compiler/semfold.nim b/compiler/semfold.nim index 05e398c9a..749deded1 100644 --- a/compiler/semfold.nim +++ b/compiler/semfold.nim @@ -628,7 +628,10 @@ proc getConstExpr(m: PSym, n: PNode): PNode = of {skProc, skMethod}: result = n of skType: - result = newSymNodeTypeDesc(s, n.info) + # XXX gensym'ed symbols can come here and cannot be resolved. This is + # dirty, but correct. + if s.typ != nil: + result = newSymNodeTypeDesc(s, n.info) of skGenericParam: if s.typ.kind == tyStatic: if s.typ.n != nil: @@ -656,7 +659,7 @@ proc getConstExpr(m: PSym, n: PNode): PNode = localError(a.info, errCannotEvalXBecauseIncompletelyDefined, "sizeof") result = nil - elif skipTypes(a.typ, typedescInst).kind in + elif skipTypes(a.typ, typedescInst+{tyRange}).kind in IntegralTypes+NilableTypes+{tySet}: #{tyArray,tyObject,tyTuple}: result = newIntNodeT(getSize(a.typ), n) diff --git a/compiler/seminst.nim b/compiler/seminst.nim index 9c57be023..acaade494 100644 --- a/compiler/seminst.nim +++ b/compiler/seminst.nim @@ -106,15 +106,18 @@ proc freshGenSyms(n: PNode, owner, orig: PSym, symMap: var TIdTable) = #if n.kind == nkSym and sfGenSym in n.sym.flags: # if n.sym.owner != orig: # echo "symbol ", n.sym.name.s, " orig ", orig, " owner ", n.sym.owner - if n.kind == nkSym and {sfGenSym, sfFromGeneric} * n.sym.flags == {sfGenSym}: # and + if n.kind == nkSym and sfGenSym in n.sym.flags: # and # (n.sym.owner == orig or n.sym.owner.kind in {skPackage}): let s = n.sym var x = PSym(idTableGet(symMap, s)) - if x == nil: + if x != nil: + n.sym = x + elif s.owner.kind == skPackage: + #echo "copied this ", s.name.s x = copySym(s, false) x.owner = owner idTablePut(symMap, s, x) - n.sym = x + n.sym = x else: for i in 0 .. <safeLen(n): freshGenSyms(n.sons[i], owner, orig, symMap) diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim index 5a15f9f5a..6a3793d73 100644 --- a/compiler/semstmts.nim +++ b/compiler/semstmts.nim @@ -1360,6 +1360,7 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind, proc determineType(c: PContext, s: PSym) = if s.typ != nil: return #if s.magic != mNone: return + #if s.ast.isNil: return discard semProcAux(c, s.ast, s.kind, {}, stepDetermineType) proc semIterator(c: PContext, n: PNode): PNode = diff --git a/compiler/semtempl.nim b/compiler/semtempl.nim index a69fe477b..8819f17cc 100644 --- a/compiler/semtempl.nim +++ b/compiler/semtempl.nim @@ -172,7 +172,7 @@ proc newGenSym(kind: TSymKind, n: PNode, c: var TemplCtx): PSym = result = newSym(kind, considerQuotedIdent(n), c.owner, n.info) incl(result.flags, sfGenSym) incl(result.flags, sfShadowed) - if c.scopeN == 0: incl(result.flags, sfFromGeneric) + #if c.scopeN == 0: incl(result.flags, sfFromGeneric) proc addLocalDecl(c: var TemplCtx, n: var PNode, k: TSymKind) = # locals default to 'gensym': diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim index 17c065b49..440edd226 100644 --- a/compiler/semtypes.nim +++ b/compiler/semtypes.nim @@ -297,7 +297,7 @@ proc semOrdinal(c: PContext, n: PNode, prev: PType): PType = proc semTypeIdent(c: PContext, n: PNode): PSym = if n.kind == nkSym: - result = n.sym + result = getGenSym(c, n.sym) else: when defined(nimfix): result = pickSym(c, n, skType) @@ -1319,8 +1319,9 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType = prev.id = s.typ.id result = prev of nkSym: - if n.sym.kind == skType and n.sym.typ != nil: - var t = n.sym.typ + let s = getGenSym(c, n.sym) + if s.kind == skType and s.typ != nil: + var t = s.typ let alias = maybeAliasType(c, t, prev) if alias != nil: result = alias @@ -1332,7 +1333,7 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType = markUsed(n.info, n.sym) styleCheckUse(n.info, n.sym) else: - if n.sym.kind != skError: localError(n.info, errTypeExpected) + if s.kind != skError: localError(n.info, errTypeExpected) result = newOrPrevType(tyError, prev, c) of nkObjectTy: result = semObjectNode(c, n, prev) of nkTupleTy: result = semTuple(c, n, prev) diff --git a/compiler/sigmatch.nim b/compiler/sigmatch.nim index 6d5e6f742..117b66cd8 100644 --- a/compiler/sigmatch.nim +++ b/compiler/sigmatch.nim @@ -1583,12 +1583,14 @@ proc prepareOperand(c: PContext; formal: PType; a: PNode): PNode = result = c.semOperand(c, a, flags) else: result = a + considerGenSyms(c, result) proc prepareOperand(c: PContext; a: PNode): PNode = if a.typ.isNil: result = c.semOperand(c, a, {efDetermineType}) else: result = a + considerGenSyms(c, result) proc prepareNamedParam(a: PNode) = if a.sons[0].kind != nkIdent: diff --git a/compiler/transf.nim b/compiler/transf.nim index 13c6dd8fe..2103d48bf 100644 --- a/compiler/transf.nim +++ b/compiler/transf.nim @@ -869,7 +869,12 @@ proc transform(c: PTransf, n: PNode): PTransNode = else: result = transformSons(c, n) of nkIdentDefs, nkConstDef: - result = transformSons(c, n) + when true: + result = transformSons(c, n) + else: + result = n.PTransNode + let L = n.len-1 + result[L] = transform(c, n.sons[L]) # XXX comment handling really sucks: if importantComments(): PNode(result).comment = n.comment diff --git a/compiler/trees.nim b/compiler/trees.nim index 7208f7d7b..79a460aa0 100644 --- a/compiler/trees.nim +++ b/compiler/trees.nim @@ -38,7 +38,7 @@ proc exprStructuralEquivalent*(a, b: PNode; strictSymEquality=false): bool = # don't go nuts here: same symbol as string is enough: result = a.sym.name.id == b.sym.name.id of nkIdent: result = a.ident.id == b.ident.id - of nkCharLit..nkInt64Lit: result = a.intVal == b.intVal + of nkCharLit..nkUInt64Lit: result = a.intVal == b.intVal of nkFloatLit..nkFloat64Lit: result = a.floatVal == b.floatVal of nkStrLit..nkTripleStrLit: result = a.strVal == b.strVal of nkEmpty, nkNilLit, nkType: result = true diff --git a/doc/backends.txt b/doc/backends.txt index 5846cce9b..6446103ed 100644 --- a/doc/backends.txt +++ b/doc/backends.txt @@ -461,3 +461,11 @@ can then attach a GC to this thread via It is **not** safe to disable the garbage collector and enable it after the call from your background thread even if the code you are calling is short lived. + +Before the thread exits, you should tear down the thread's GC to prevent memory +leaks by calling + +.. code-block:: nim + + system.tearDownForeignThreadGc() + diff --git a/doc/tut1.rst b/doc/tut1.rst index 47cafc7fa..e79214dee 100644 --- a/doc/tut1.rst +++ b/doc/tut1.rst @@ -361,7 +361,7 @@ iterator: .. code-block:: nim echo "Counting to ten: " for i in countup(1, 10): - echo $i + echo i # --> Outputs 1 2 3 4 5 6 7 8 9 10 on different lines The built-in `$ <system.html#$>`_ operator turns an integer (``int``) and many @@ -374,7 +374,7 @@ Each value is ``echo``-ed. This code does the same: echo "Counting to 10: " var i = 1 while i <= 10: - echo $i + echo i inc(i) # increment i by 1 # --> Outputs 1 2 3 4 5 6 7 8 9 10 on different lines @@ -383,7 +383,7 @@ Counting down can be achieved as easily (but is less often needed): .. code-block:: nim echo "Counting down from 10 to 1: " for i in countdown(10, 1): - echo $i + echo i # --> Outputs 10 9 8 7 6 5 4 3 2 1 on different lines Since counting up occurs so often in programs, Nim also has a `.. @@ -827,7 +827,7 @@ Let's return to the boring counting example: .. code-block:: nim echo "Counting to ten: " for i in countup(1, 10): - echo $i + echo i Can a `countup <system.html#countup>`_ proc be written that supports this loop? Lets try: @@ -1035,15 +1035,15 @@ there is a difference between the ``$`` and ``repr`` outputs: myString = "nim" myInteger = 42 myFloat = 3.14 - echo $myBool, ":", repr(myBool) + echo myBool, ":", repr(myBool) # --> true:true - echo $myCharacter, ":", repr(myCharacter) + echo myCharacter, ":", repr(myCharacter) # --> n:'n' - echo $myString, ":", repr(myString) + echo myString, ":", repr(myString) # --> nim:0x10fa8c050"nim" - echo $myInteger, ":", repr(myInteger) + echo myInteger, ":", repr(myInteger) # --> 42:42 - echo $myFloat, ":", repr(myFloat) + echo myFloat, ":", repr(myFloat) # --> 3.1400000000000001e+00:3.1400000000000001e+00 @@ -1075,7 +1075,7 @@ at runtime by 0, the second by 1 and so on. Example: north, east, south, west var x = south # `x` is of type `Direction`; its value is `south` - echo $x # writes "south" to `stdout` + echo x # writes "south" to `stdout` All comparison operators can be used with enumeration types. @@ -1289,7 +1289,7 @@ value. Here the ``for`` statement is looping over the results from the .. code-block:: nim for i in @[3, 4, 5]: - echo $i + echo i # --> 3 # --> 4 # --> 5 diff --git a/koch.nim b/koch.nim index 825b360a0..109dcc3f1 100644 --- a/koch.nim +++ b/koch.nim @@ -223,8 +223,9 @@ proc bundleWinTools() = copyExe("tools/finish".exe, "finish".exe) removeFile("tools/finish".exe) nimexec("c -o:bin/vccexe.exe tools/vccenv/vccexe") - nimexec(r"c --cc:vcc --app:gui -o:bin\downloader.exe -d:ssl --noNimblePath " & - r"--path:..\ui tools\downloader.nim") + when false: + nimexec(r"c --cc:vcc --app:gui -o:bin\downloader.exe -d:ssl --noNimblePath " & + r"--path:..\ui tools\downloader.nim") proc zip(args: string) = bundleNimbleSrc() diff --git a/lib/impure/db_postgres.nim b/lib/impure/db_postgres.nim index 60bd1f081..fc587b5df 100644 --- a/lib/impure/db_postgres.nim +++ b/lib/impure/db_postgres.nim @@ -69,16 +69,14 @@ import db_common export db_common type - DbConn* = PPGconn ## encapsulates a database connection - Row* = seq[string] ## a row of a dataset. NULL database values will be - ## converted to nil. - InstantRow* = tuple[res: PPGresult, line: int32] ## a handle that can be - ## used to get a row's - ## column text on demand + DbConn* = PPGconn ## encapsulates a database connection + Row* = seq[string] ## a row of a dataset. NULL database values will be + ## converted to nil. + InstantRow* = object ## a handle that can be + res: PPGresult ## used to get a row's + line: int ## column text on demand SqlPrepared* = distinct string ## a identifier for the prepared queries - -{.deprecated: [TRow: Row, TDbConn: DbConn, - TSqlPrepared: SqlPrepared].} +{.deprecated: [TRow: Row, TDbConn: DbConn, TSqlPrepared: SqlPrepared].} proc dbError*(db: DbConn) {.noreturn.} = ## raises a DbError exception. @@ -213,7 +211,7 @@ iterator instantRows*(db: DbConn, query: SqlQuery, ## on demand using []. Returned handle is valid only within iterator body. var res = setupQuery(db, query, args) for i in 0..pqNtuples(res)-1: - yield (res: res, line: i) + yield InstantRow(res: res, line: i) pqClear(res) iterator instantRows*(db: DbConn, stmtName: SqlPrepared, @@ -223,16 +221,170 @@ iterator instantRows*(db: DbConn, stmtName: SqlPrepared, ## on demand using []. Returned handle is valid only within iterator body. var res = setupQuery(db, stmtName, args) for i in 0..pqNtuples(res)-1: - yield (res: res, line: i) + yield InstantRow(res: res, line: i) + pqClear(res) + +proc getColumnType(res: PPGresult, col: int) : DbType = + ## returns DbType for given column in the row + ## defined in pg_type.h file in the postgres source code + ## Wire representation for types: http://www.npgsql.org/dev/types.html + var oid = pqftype(res, int32(col)) + ## The integer returned is the internal OID number of the type + case oid + of 16: return DbType(kind: DbTypeKind.dbBool, name: "bool") + of 17: return DbType(kind: DbTypeKind.dbBlob, name: "bytea") + + of 21: return DbType(kind: DbTypeKind.dbInt, name: "int2", size: 2) + of 23: return DbType(kind: DbTypeKind.dbInt, name: "int4", size: 4) + of 20: return DbType(kind: DbTypeKind.dbInt, name: "int8", size: 8) + of 1560: return DbType(kind: DbTypeKind.dbBit, name: "bit") + of 1562: return DbType(kind: DbTypeKind.dbInt, name: "varbit") + + of 18: return DbType(kind: DbTypeKind.dbFixedChar, name: "char") + of 19: return DbType(kind: DbTypeKind.dbFixedChar, name: "name") + of 1042: return DbType(kind: DbTypeKind.dbFixedChar, name: "bpchar") + + of 25: return DbType(kind: DbTypeKind.dbVarchar, name: "text") + of 1043: return DbType(kind: DbTypeKind.dbVarChar, name: "varchar") + of 2275: return DbType(kind: DbTypeKind.dbVarchar, name: "cstring") + + of 700: return DbType(kind: DbTypeKind.dbFloat, name: "float4") + of 701: return DbType(kind: DbTypeKind.dbFloat, name: "float8") + + of 790: return DbType(kind: DbTypeKind.dbDecimal, name: "money") + of 1700: return DbType(kind: DbTypeKind.dbDecimal, name: "numeric") + + of 704: return DbType(kind: DbTypeKind.dbTimeInterval, name: "tinterval") + of 702: return DbType(kind: DbTypeKind.dbTimestamp, name: "abstime") + of 703: return DbType(kind: DbTypeKind.dbTimeInterval, name: "reltime") + of 1082: return DbType(kind: DbTypeKind.dbDate, name: "date") + of 1083: return DbType(kind: DbTypeKind.dbTime, name: "time") + of 1114: return DbType(kind: DbTypeKind.dbTimestamp, name: "timestamp") + of 1184: return DbType(kind: DbTypeKind.dbTimestamp, name: "timestamptz") + of 1186: return DbType(kind: DbTypeKind.dbTimeInterval, name: "interval") + of 1266: return DbType(kind: DbTypeKind.dbTime, name: "timetz") + + of 114: return DbType(kind: DbTypeKind.dbJson, name: "json") + of 142: return DbType(kind: DbTypeKind.dbXml, name: "xml") + of 3802: return DbType(kind: DbTypeKind.dbJson, name: "jsonb") + + of 600: return DbType(kind: DbTypeKind.dbPoint, name: "point") + of 601: return DbType(kind: DbTypeKind.dbLseg, name: "lseg") + of 602: return DbType(kind: DbTypeKind.dbPath, name: "path") + of 603: return DbType(kind: DbTypeKind.dbBox, name: "box") + of 604: return DbType(kind: DbTypeKind.dbPolygon, name: "polygon") + of 628: return DbType(kind: DbTypeKind.dbLine, name: "line") + of 718: return DbType(kind: DbTypeKind.dbCircle, name: "circle") + + of 650: return DbType(kind: DbTypeKind.dbInet, name: "cidr") + of 829: return DbType(kind: DbTypeKind.dbMacAddress, name: "macaddr") + of 869: return DbType(kind: DbTypeKind.dbInet, name: "inet") + + of 2950: return DbType(kind: DbTypeKind.dbVarchar, name: "uuid") + of 3614: return DbType(kind: DbTypeKind.dbVarchar, name: "tsvector") + of 3615: return DbType(kind: DbTypeKind.dbVarchar, name: "tsquery") + of 2970: return DbType(kind: DbTypeKind.dbVarchar, name: "txid_snapshot") + + of 27: return DbType(kind: DbTypeKind.dbComposite, name: "tid") + of 1790: return DbType(kind: DbTypeKind.dbComposite, name: "refcursor") + of 2249: return DbType(kind: DbTypeKind.dbComposite, name: "record") + of 3904: return DbType(kind: DbTypeKind.dbComposite, name: "int4range") + of 3906: return DbType(kind: DbTypeKind.dbComposite, name: "numrange") + of 3908: return DbType(kind: DbTypeKind.dbComposite, name: "tsrange") + of 3910: return DbType(kind: DbTypeKind.dbComposite, name: "tstzrange") + of 3912: return DbType(kind: DbTypeKind.dbComposite, name: "daterange") + of 3926: return DbType(kind: DbTypeKind.dbComposite, name: "int8range") + + of 22: return DbType(kind: DbTypeKind.dbArray, name: "int2vector") + of 30: return DbType(kind: DbTypeKind.dbArray, name: "oidvector") + of 143: return DbType(kind: DbTypeKind.dbArray, name: "xml[]") + of 199: return DbType(kind: DbTypeKind.dbArray, name: "json[]") + of 629: return DbType(kind: DbTypeKind.dbArray, name: "line[]") + of 651: return DbType(kind: DbTypeKind.dbArray, name: "cidr[]") + of 719: return DbType(kind: DbTypeKind.dbArray, name: "circle[]") + of 791: return DbType(kind: DbTypeKind.dbArray, name: "money[]") + of 1000: return DbType(kind: DbTypeKind.dbArray, name: "bool[]") + of 1001: return DbType(kind: DbTypeKind.dbArray, name: "bytea[]") + of 1002: return DbType(kind: DbTypeKind.dbArray, name: "char[]") + of 1003: return DbType(kind: DbTypeKind.dbArray, name: "name[]") + of 1005: return DbType(kind: DbTypeKind.dbArray, name: "int2[]") + of 1006: return DbType(kind: DbTypeKind.dbArray, name: "int2vector[]") + of 1007: return DbType(kind: DbTypeKind.dbArray, name: "int4[]") + of 1008: return DbType(kind: DbTypeKind.dbArray, name: "regproc[]") + of 1009: return DbType(kind: DbTypeKind.dbArray, name: "text[]") + of 1028: return DbType(kind: DbTypeKind.dbArray, name: "oid[]") + of 1010: return DbType(kind: DbTypeKind.dbArray, name: "tid[]") + of 1011: return DbType(kind: DbTypeKind.dbArray, name: "xid[]") + of 1012: return DbType(kind: DbTypeKind.dbArray, name: "cid[]") + of 1013: return DbType(kind: DbTypeKind.dbArray, name: "oidvector[]") + of 1014: return DbType(kind: DbTypeKind.dbArray, name: "bpchar[]") + of 1015: return DbType(kind: DbTypeKind.dbArray, name: "varchar[]") + of 1016: return DbType(kind: DbTypeKind.dbArray, name: "int8[]") + of 1017: return DbType(kind: DbTypeKind.dbArray, name: "point[]") + of 1018: return DbType(kind: DbTypeKind.dbArray, name: "lseg[]") + of 1019: return DbType(kind: DbTypeKind.dbArray, name: "path[]") + of 1020: return DbType(kind: DbTypeKind.dbArray, name: "box[]") + of 1021: return DbType(kind: DbTypeKind.dbArray, name: "float4[]") + of 1022: return DbType(kind: DbTypeKind.dbArray, name: "float8[]") + of 1023: return DbType(kind: DbTypeKind.dbArray, name: "abstime[]") + of 1024: return DbType(kind: DbTypeKind.dbArray, name: "reltime[]") + of 1025: return DbType(kind: DbTypeKind.dbArray, name: "tinterval[]") + of 1027: return DbType(kind: DbTypeKind.dbArray, name: "polygon[]") + of 1040: return DbType(kind: DbTypeKind.dbArray, name: "macaddr[]") + of 1041: return DbType(kind: DbTypeKind.dbArray, name: "inet[]") + of 1263: return DbType(kind: DbTypeKind.dbArray, name: "cstring[]") + of 1115: return DbType(kind: DbTypeKind.dbArray, name: "timestamp[]") + of 1182: return DbType(kind: DbTypeKind.dbArray, name: "date[]") + of 1183: return DbType(kind: DbTypeKind.dbArray, name: "time[]") + of 1185: return DbType(kind: DbTypeKind.dbArray, name: "timestamptz[]") + of 1187: return DbType(kind: DbTypeKind.dbArray, name: "interval[]") + of 1231: return DbType(kind: DbTypeKind.dbArray, name: "numeric[]") + of 1270: return DbType(kind: DbTypeKind.dbArray, name: "timetz[]") + of 1561: return DbType(kind: DbTypeKind.dbArray, name: "bit[]") + of 1563: return DbType(kind: DbTypeKind.dbArray, name: "varbit[]") + of 2201: return DbType(kind: DbTypeKind.dbArray, name: "refcursor[]") + of 2951: return DbType(kind: DbTypeKind.dbArray, name: "uuid[]") + of 3643: return DbType(kind: DbTypeKind.dbArray, name: "tsvector[]") + of 3645: return DbType(kind: DbTypeKind.dbArray, name: "tsquery[]") + of 3807: return DbType(kind: DbTypeKind.dbArray, name: "jsonb[]") + of 2949: return DbType(kind: DbTypeKind.dbArray, name: "txid_snapshot[]") + of 3905: return DbType(kind: DbTypeKind.dbArray, name: "int4range[]") + of 3907: return DbType(kind: DbTypeKind.dbArray, name: "numrange[]") + of 3909: return DbType(kind: DbTypeKind.dbArray, name: "tsrange[]") + of 3911: return DbType(kind: DbTypeKind.dbArray, name: "tstzrange[]") + of 3913: return DbType(kind: DbTypeKind.dbArray, name: "daterange[]") + of 3927: return DbType(kind: DbTypeKind.dbArray, name: "int8range[]") + of 2287: return DbType(kind: DbTypeKind.dbArray, name: "record[]") + + of 705: return DbType(kind: DbTypeKind.dbUnknown, name: "unknown") + else: return DbType(kind: DbTypeKind.dbUnknown, name: $oid) ## Query the system table pg_type to determine exactly which type is referenced. + +proc setColumnInfo(columns: var DbColumns; res: PPGresult; L: int32) = + setLen(columns, L) + for i in 0..<L: + columns[i].name = $pqfname(res, i) + columns[i].typ = getColumnType(res, i) + columns[i].tableName = $(pqftable(res, i)) ## Returns the OID of the table from which the given column was fetched. + ## Query the system table pg_class to determine exactly which table is referenced. + #columns[i].primaryKey = libpq does not have a function for that + #columns[i].foreignKey = libpq does not have a function for that + +iterator instantRows*(db: DbConn; columns: var DbColumns; query: SqlQuery; + args: varargs[string, `$`]): InstantRow + {.tags: [ReadDbEffect].} = + var res = setupQuery(db, query, args) + setColumnInfo(columns, res, pqnfields(res)) + for i in 0..<pqntuples(res): + yield InstantRow(res: res, line: i) pqClear(res) -proc `[]`*(row: InstantRow, col: int32): string {.inline.} = +proc `[]`*(row: InstantRow; col: int): string {.inline.} = ## returns text for given column of the row - $pqgetvalue(row.res, row.line, col) + $pqgetvalue(row.res, int32(row.line), int32(col)) -proc len*(row: InstantRow): int32 {.inline.} = +proc len*(row: InstantRow): int {.inline.} = ## returns number of columns in the row - pqNfields(row.res) + int(pqNfields(row.res)) proc getRow*(db: DbConn, query: SqlQuery, args: varargs[string, `$`]): Row {.tags: [ReadDbEffect].} = diff --git a/lib/impure/re.nim b/lib/impure/re.nim index 9d5d2bdd0..e00f91de1 100644 --- a/lib/impure/re.nim +++ b/lib/impure/re.nim @@ -290,7 +290,7 @@ proc find*(buf: cstring, pattern: Regex, matches: var openArray[string], for i in 1..int(res)-1: var a = rawMatches[i * 2] var b = rawMatches[i * 2 + 1] - if a >= 0'i32: matches[i-1] = bufSubstr(buf, int(a), int(b)-1) + if a >= 0'i32: matches[i-1] = bufSubstr(buf, int(a), int(b)) else: matches[i-1] = nil return rawMatches[0] diff --git a/lib/pure/algorithm.nim b/lib/pure/algorithm.nim index 739cdc16b..bc39f153b 100644 --- a/lib/pure/algorithm.nim +++ b/lib/pure/algorithm.nim @@ -46,7 +46,7 @@ proc reverse*[T](a: var openArray[T], first, last: Natural) = proc reverse*[T](a: var openArray[T]) = ## reverses the array `a`. - reverse(a, 0, a.high) + reverse(a, 0, max(0, a.high)) proc reversed*[T](a: openArray[T], first: Natural, last: int): seq[T] = ## returns the reverse of the array `a[first..last]`. diff --git a/lib/pure/collections/heapqueue.nim b/lib/pure/collections/heapqueue.nim index abe20e556..f86ba1d3f 100644 --- a/lib/pure/collections/heapqueue.nim +++ b/lib/pure/collections/heapqueue.nim @@ -77,8 +77,10 @@ proc pop*[T](heap: var HeapQueue[T]): T = proc del*[T](heap: var HeapQueue[T], index: int) = ## Removes element at `index`, maintaining the heap invariant. swap(seq[T](heap)[^1], seq[T](heap)[index]) - seq[T](heap).setLen(heap.len - 1) - heap.siftup(index) + let newLen = heap.len - 1 + seq[T](heap).setLen(newLen) + if index < newLen: + heap.siftup(index) proc replace*[T](heap: var HeapQueue[T], item: T): T = ## Pop and return the current smallest value, and add the new item. @@ -101,16 +103,19 @@ proc pushpop*[T](heap: var HeapQueue[T], item: T): T = return item when isMainModule: + proc toSortedSeq[T](h: HeapQueue[T]): seq[T] = + var tmp = h + result = @[] + while tmp.len > 0: + result.add(pop(tmp)) + block: # Simple sanity test var heap = newHeapQueue[int]() let data = [1, 3, 5, 7, 9, 2, 4, 6, 8, 0] for item in data: push(heap, item) doAssert(heap[0] == 0) - var sort = newSeq[int]() - while heap.len > 0: - sort.add(pop(heap)) - doAssert(sort == @[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]) + doAssert(heap.toSortedSeq == @[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]) block: # Test del var heap = newHeapQueue[int]() @@ -121,11 +126,27 @@ when isMainModule: doAssert(heap[0] == 1) heap.del(seq[int](heap).find(7)) + doAssert(heap.toSortedSeq == @[1, 2, 3, 4, 5, 6, 8, 9]) + heap.del(seq[int](heap).find(5)) + doAssert(heap.toSortedSeq == @[1, 2, 3, 4, 6, 8, 9]) + heap.del(seq[int](heap).find(6)) + doAssert(heap.toSortedSeq == @[1, 2, 3, 4, 8, 9]) + heap.del(seq[int](heap).find(2)) + doAssert(heap.toSortedSeq == @[1, 3, 4, 8, 9]) + + block: # Test del last + var heap = newHeapQueue[int]() + let data = [1, 2, 3] + for item in data: push(heap, item) + + heap.del(2) + doAssert(heap.toSortedSeq == @[1, 2]) - var sort = newSeq[int]() - while heap.len > 0: - sort.add(pop(heap)) - doAssert(sort == @[1, 3, 4, 8, 9]) + heap.del(1) + doAssert(heap.toSortedSeq == @[1]) + + heap.del(0) + doAssert(heap.toSortedSeq == @[]) diff --git a/lib/pure/distros.nim b/lib/pure/distros.nim index ff30f6134..896497630 100644 --- a/lib/pure/distros.nim +++ b/lib/pure/distros.nim @@ -225,7 +225,7 @@ proc foreignDepInstallCmd*(foreignPackageName: string): (string, bool) = else: result = ("<your package manager here> install " & p, true) else: - result = ("brew install " & p, true) + result = ("brew install " & p, false) proc foreignDep*(foreignPackageName: string) = ## Registers 'foreignPackageName' to the internal list of foreign deps. diff --git a/lib/pure/hashes.nim b/lib/pure/hashes.nim index 11af81149..17d1c6442 100644 --- a/lib/pure/hashes.nim +++ b/lib/pure/hashes.nim @@ -127,6 +127,15 @@ proc hash*(x: string): Hash = h = h !& ord(x[i]) result = !$h +proc hash*(x: cstring): Hash = + ## efficient hashing of null-terminated strings + var h: Hash = 0 + var i = 0 + while x[i] != 0.char: + h = h !& ord(x[i]) + inc i + result = !$h + proc hash*(sBuf: string, sPos, ePos: int): Hash = ## efficient hashing of a string buffer, from starting ## position `sPos` to ending position `ePos` @@ -239,6 +248,7 @@ proc hash*[A](x: set[A]): Hash = when isMainModule: doAssert( hash("aa bb aaaa1234") == hash("aa bb aaaa1234", 0, 13) ) + doAssert( hash("aa bb aaaa1234") == hash(cstring("aa bb aaaa1234")) ) doAssert( hashIgnoreCase("aa bb aaaa1234") == hash("aa bb aaaa1234") ) doAssert( hashIgnoreStyle("aa bb aaaa1234") == hashIgnoreCase("aa bb aaaa1234") ) let xx = @['H','e','l','l','o'] diff --git a/lib/pure/httpcore.nim b/lib/pure/httpcore.nim index d7f720f66..aa8f1958d 100644 --- a/lib/pure/httpcore.nim +++ b/lib/pure/httpcore.nim @@ -206,6 +206,8 @@ proc parseHeader*(line: string): tuple[key: string, value: seq[string]] = inc(i) # skip : if i < len(line): i += parseList(line, result.value, i) + elif result.key.len > 0: + result.value = @[""] else: result.value = @[] @@ -318,4 +320,6 @@ when isMainModule: let (key, value) = parseHeader("foobar: ") test = newHttpHeaders() test[key] = value - doAssert test["foobar"] == "" \ No newline at end of file + doAssert test["foobar"] == "" + + doAssert parseHeader("foobar:") == ("foobar", @[""]) \ No newline at end of file diff --git a/lib/pure/os.nim b/lib/pure/os.nim index 4cd3870c7..92e295820 100644 --- a/lib/pure/os.nim +++ b/lib/pure/os.nim @@ -1002,8 +1002,8 @@ iterator walkDir*(dir: string; relative=false): tuple[kind: PathComponent, path: iterator walkDirRec*(dir: string, filter={pcFile, pcDir}): string {. tags: [ReadDirEffect].} = - ## walks over the directory `dir` and yields for each file in `dir`. The - ## full path for each file is returned. + ## Recursively walks over the directory `dir` and yields for each file in `dir`. + ## The full path for each file is returned. Directories are not returned. ## **Warning**: ## Modifying the directory structure while the iterator ## is traversing may result in undefined behavior! diff --git a/lib/pure/parseutils.nim b/lib/pure/parseutils.nim index fb7d72182..8d53a0360 100644 --- a/lib/pure/parseutils.nim +++ b/lib/pure/parseutils.nim @@ -250,18 +250,18 @@ proc parseInt*(s: string, number: var int, start = 0): int {. elif result != 0: number = int(res) -# overflowChecks doesn't work with uint64 -proc rawParseUInt(s: string, b: var uint64, start = 0): int = +# overflowChecks doesn't work with BiggestUInt +proc rawParseUInt(s: string, b: var BiggestUInt, start = 0): int = var - res = 0'u64 - prev = 0'u64 + res = 0.BiggestUInt + prev = 0.BiggestUInt i = start if s[i] == '+': inc(i) # Allow if s[i] in {'0'..'9'}: b = 0 while s[i] in {'0'..'9'}: prev = res - res = res * 10 + (ord(s[i]) - ord('0')).uint64 + res = res * 10 + (ord(s[i]) - ord('0')).BiggestUInt if prev > res: return 0 # overflowChecks emulation inc(i) @@ -269,13 +269,13 @@ proc rawParseUInt(s: string, b: var uint64, start = 0): int = b = res result = i - start -proc parseBiggestUInt*(s: string, number: var uint64, start = 0): int {. +proc parseBiggestUInt*(s: string, number: var BiggestUInt, start = 0): int {. rtl, extern: "npuParseBiggestUInt", noSideEffect.} = ## parses an unsigned integer starting at `start` and stores the value ## into `number`. ## Result is the number of processed chars or 0 if there is no integer ## or overflow detected. - var res: uint64 + var res: BiggestUInt # use 'res' for exception safety (don't write to 'number' in case of an # overflow exception): result = rawParseUInt(s, res, start) @@ -287,12 +287,12 @@ proc parseUInt*(s: string, number: var uint, start = 0): int {. ## into `number`. ## Result is the number of processed chars or 0 if there is no integer or ## overflow detected. - var res: uint64 + var res: BiggestUInt result = parseBiggestUInt(s, res, start) - if (sizeof(uint) <= 4) and - (res > 0xFFFF_FFFF'u64): - raise newException(OverflowError, "overflow") - elif result != 0: + when sizeof(BiggestUInt) > sizeof(uint) and sizeof(uint) <= 4: + if res > 0xFFFF_FFFF'u64: + raise newException(OverflowError, "overflow") + if result != 0: number = uint(res) proc parseBiggestFloat*(s: string, number: var BiggestFloat, start = 0): int {. diff --git a/lib/pure/strutils.nim b/lib/pure/strutils.nim index 235e66f6a..9c205a54f 100644 --- a/lib/pure/strutils.nim +++ b/lib/pure/strutils.nim @@ -939,7 +939,7 @@ proc parseUInt*(s: string): uint {.noSideEffect, procvar, if L != s.len or L == 0: raise newException(ValueError, "invalid unsigned integer: " & s) -proc parseBiggestUInt*(s: string): uint64 {.noSideEffect, procvar, +proc parseBiggestUInt*(s: string): BiggestUInt {.noSideEffect, procvar, rtl, extern: "nsuParseBiggestUInt".} = ## Parses a decimal unsigned integer value contained in `s`. ## diff --git a/lib/system.nim b/lib/system.nim index 13ca6eaf7..bab5369f1 100644 --- a/lib/system.nim +++ b/lib/system.nim @@ -1512,6 +1512,17 @@ type ## compiler supports. Currently this is ``float64``, but it is ## platform-dependant in general. +when defined(JS): + type BiggestUInt* = uint32 + ## is an alias for the biggest unsigned integer type the Nim compiler + ## supports. Currently this is ``uint32`` for JS and ``uint64`` for other + ## targets. +else: + type BiggestUInt* = uint64 + ## is an alias for the biggest unsigned integer type the Nim compiler + ## supports. Currently this is ``uint32`` for JS and ``uint64`` for other + ## targets. + {.deprecated: [TAddress: ByteAddress].} when defined(windows): diff --git a/lib/system/gc_common.nim b/lib/system/gc_common.nim index 269514ceb..6ab6bd920 100644 --- a/lib/system/gc_common.nim +++ b/lib/system/gc_common.nim @@ -128,13 +128,7 @@ iterator items(stack: ptr GcStack): ptr GcStack = yield s s = s.next -# There will be problems with GC in foreign threads if `threads` option is off or TLS emulation is enabled -const allowForeignThreadGc = compileOption("threads") and not compileOption("tlsEmulation") - -when allowForeignThreadGc: - var - localGcInitialized {.rtlThreadVar.}: bool - +when declared(threadType): proc setupForeignThreadGc*() {.gcsafe.} = ## Call this if you registered a callback that will be run from a thread not ## under your control. This has a cheap thread-local guard, so the GC for @@ -143,16 +137,33 @@ when allowForeignThreadGc: ## ## This function is available only when ``--threads:on`` and ``--tlsEmulation:off`` ## switches are used - if not localGcInitialized: - localGcInitialized = true + if threadType == ThreadType.None: initAllocator() var stackTop {.volatile.}: pointer setStackBottom(addr(stackTop)) initGC() + threadType = ThreadType.ForeignThread + + proc tearDownForeignThreadGc*() {.gcsafe.} = + ## Call this to tear down the GC, previously initialized by ``setupForeignThreadGc``. + ## If GC has not been previously initialized, or has already been torn down, the + ## call does nothing. + ## + ## This function is available only when ``--threads:on`` and ``--tlsEmulation:off`` + ## switches are used + if threadType != ThreadType.ForeignThread: + return + when declared(deallocOsPages): deallocOsPages() + threadType = ThreadType.None + when declared(gch): zeroMem(addr gch, sizeof(gch)) + else: template setupForeignThreadGc*() = {.error: "setupForeignThreadGc is available only when ``--threads:on`` and ``--tlsEmulation:off`` are used".} + template tearDownForeignThreadGc*() = + {.error: "tearDownForeignThreadGc is available only when ``--threads:on`` and ``--tlsEmulation:off`` are used".} + # ----------------- stack management -------------------------------------- # inspired from Smart Eiffel diff --git a/lib/system/sysio.nim b/lib/system/sysio.nim index 9f4944eb0..7444661e3 100644 --- a/lib/system/sysio.nim +++ b/lib/system/sysio.nim @@ -67,7 +67,7 @@ proc checkErr(f: File) = {.push stackTrace:off, profiler:off.} proc readBuffer(f: File, buffer: pointer, len: Natural): int = result = c_fread(buffer, 1, len, f) - checkErr(f) + if result != len: checkErr(f) proc readBytes(f: File, a: var openArray[int8|uint8], start, len: Natural): int = result = readBuffer(f, addr(a[start]), len) @@ -118,8 +118,9 @@ const proc close*(f: File) = discard c_fclose(f) proc readChar(f: File): char = let x = c_fgetc(f) - if x == -1: raiseEOF() - checkErr(f) + if x < 0: + checkErr(f) + raiseEOF() result = char(x) proc flushFile*(f: File) = discard c_fflush(f) @@ -140,7 +141,7 @@ proc readLine(f: File, line: var TaintedString): bool = # fgets doesn't append an \L c_memset(addr line.string[pos], '\L'.ord, sp) var fgetsSuccess = c_fgets(addr line.string[pos], sp, f) != nil - checkErr(f) + if not fgetsSuccess: checkErr(f) let m = c_memchr(addr line.string[pos], '\L'.ord, sp) if m != nil: # \l found: Could be our own or the one by fgets, in any case, we're done @@ -170,21 +171,23 @@ proc readLine(f: File): TaintedString = proc write(f: File, i: int) = when sizeof(int) == 8: - c_fprintf(f, "%lld", i) + if c_fprintf(f, "%lld", i) < 0: checkErr(f) else: - c_fprintf(f, "%ld", i) + if c_fprintf(f, "%ld", i) < 0: checkErr(f) proc write(f: File, i: BiggestInt) = when sizeof(BiggestInt) == 8: - c_fprintf(f, "%lld", i) + if c_fprintf(f, "%lld", i) < 0: checkErr(f) else: - c_fprintf(f, "%ld", i) + if c_fprintf(f, "%ld", i) < 0: checkErr(f) proc write(f: File, b: bool) = if b: write(f, "true") else: write(f, "false") -proc write(f: File, r: float32) = c_fprintf(f, "%g", r) -proc write(f: File, r: BiggestFloat) = c_fprintf(f, "%g", r) +proc write(f: File, r: float32) = + if c_fprintf(f, "%g", r) < 0: checkErr(f) +proc write(f: File, r: BiggestFloat) = + if c_fprintf(f, "%g", r) < 0: checkErr(f) proc write(f: File, c: char) = discard c_putc(ord(c), f) proc write(f: File, a: varargs[string, `$`]) = @@ -212,7 +215,10 @@ proc rawFileSize(file: File): int = discard c_fseek(file, clong(oldPos), 0) proc endOfFile(f: File): bool = - result = c_feof(f) != 0 + var c = c_fgetc(f) + discard c_ungetc(c, f) + return c < 0'i32 + #result = c_feof(f) != 0 proc readAllFile(file: File, len: int): string = # We acquire the filesize beforehand and hope it doesn't change. diff --git a/lib/system/threads.nim b/lib/system/threads.nim index e8b34bf2e..6e58638e9 100644 --- a/lib/system/threads.nim +++ b/lib/system/threads.nim @@ -285,7 +285,19 @@ when useStackMaskHack: when not defined(useNimRtl): when not useStackMaskHack: #when not defined(createNimRtl): initStackBottom() - when declared(initGC): initGC() + when declared(initGC): + initGC() + when not emulatedThreadVars: + type ThreadType {.pure.} = enum + None = 0, + NimThread = 1, + ForeignThread = 2 + var + threadType {.rtlThreadVar.}: ThreadType + + threadType = ThreadType.NimThread + + when emulatedThreadVars: if nimThreadVarsSize() > sizeof(ThreadLocalStorage): @@ -442,6 +454,8 @@ proc threadProcWrapStackFrame[TArg](thrd: ptr Thread[TArg]) = # init the GC for refc/markandsweep setStackBottom(addr(p)) initGC() + when declared(threadType): + threadType = ThreadType.NimThread when declared(registerThread): thrd.stackBottom = addr(thrd) registerThread(thrd) diff --git a/readme.md b/readme.md index 6eb85b0a3..865724b71 100644 --- a/readme.md +++ b/readme.md @@ -1,146 +1,175 @@ -# <img src="https://raw.githubusercontent.com/nim-lang/assets/master/Art/logo-crown.png" width="36"> Nim [](https://travis-ci.org/nim-lang/Nim) +# ![Logo][image-nim-logo] Nim [![Build Status][badge-nim-travisci]][nim-travisci] -This repo contains the Nim compiler, Nim's stdlib, tools and -documentation. For more information about Nim, including downloads -and documentation for the latest release, check out -[Nim's website](http://nim-lang.org). +This repository contains the Nim compiler, Nim's stdlib, tools and documentation. +For more information about Nim, including downloads and documentation for +the latest release, check out [Nim's website][nim-site]. -## Compiling -Compiling the Nim compiler is quite straightforward. Because -the Nim compiler itself is written in the Nim programming language -the C source of an older version of the compiler are needed to bootstrap the -latest version. The C sources are available in a separate -repo [here](http://github.com/nim-lang/csources). +## Community +[![Join the IRC chat][badge-nim-irc]][nim-irc] +[![Join the Gitter chat][badge-nim-gitter]][nim-gitter] +[![Get help][badge-nim-forum-gethelp]][nim-forum] +[![View Nim posts on Stack Overflow][badge-nim-stackoverflow]][nim-stackoverflow-newest] +[![Follow @nim_lang on Twitter][badge-nim-twitter]][nim-twitter] + +* The [forum][nim-forum] - the best place to ask questions and to discuss Nim. +* [#nim IRC Channel (Freenode)][nim-irc] - a place to discuss Nim in real-time. + Also where most development decisions get made. +* [Gitter][nim-gitter] - an additional place to discuss Nim in real-time. There + is a bridge between Gitter and the IRC channel. +* [Stack Overflow][nim-stackoverflow] - a popular Q/A site for programming related + topics that includes posts about Nim. -The compiler currently supports the following platform and architecture -combinations: +## Compiling +The compiler currently officially supports the following platform and +architecture combinations: * Windows (Windows XP or greater) - x86 and x86_64 * Linux (most, if not all, distributions) - x86, x86_64, ppc64 and armv6l - * Mac OS X 10.04 or higher - x86, x86_64 and ppc64 + * Mac OS X (10.04 or greater) - x86, x86_64 and ppc64 -In reality a lot more are supported, however they are not tested regularly. +More platforms are supported, however they are not tested regularly and they +may not be as stable as the above-listed platforms. -To build from source you will need: +Compiling the Nim compiler is quite straightforward if you follow these steps: - * gcc 3.x or later recommended. Other alternatives which may work - are: clang, Visual C++, Intel's C++ compiler - * git or wget +First, the C source of an older version of the Nim compiler is needed to +bootstrap the latest version because the Nim compiler itself is written in the +Nim programming language. Those C sources are available within the +[``nim-lang/csources``][csources-repo] repository. -**Note:** When installing ``gcc`` on Ubuntu (and likely other distros) ensure -that the ``build-essentials`` package is installed also. +Next, to build from source you will need: -If you are on a fairly modern *nix system, the following steps should work: + * A C compiler such as ``gcc`` 3.x/later or an alternative such as ``clang``, + ``Visual C++`` or ``Intel C++``. It is recommended to use ``gcc`` 3.x or + later. + * Either ``git`` or ``wget`` to download the needed source repositories. + * The ``build-essentials`` package when using ``gcc`` on Ubuntu (and likely + other distros as well). + +Then, if you are on a \*nix system or Windows, the following steps should compile +Nim from source using ``gcc``, ``git`` and the ``koch`` build tool (in the place +of ``sh build.sh`` you should substitute ``build.bat`` on x86 Windows or +``build64.bat`` on x86_64 Windows): ``` $ git clone https://github.com/nim-lang/Nim.git $ cd Nim -$ git clone --depth 1 https://github.com/nim-lang/csources -$ cd csources && sh build.sh -$ cd .. +$ git clone --depth 1 https://github.com/nim-lang/csources.git +$ cd csources +$ sh build.sh +$ cd ../ $ bin/nim c koch $ ./koch boot -d:release ``` -You should then add the ``bin`` directory to your PATH, to make it easily -executable on your system. +Finally, once you have finished the build steps (on Windows, Mac or Linux) you +should add the ``bin`` directory to your PATH. -The above steps can be performed on Windows in a similar fashion, the -``build.bat`` and ``build64.bat`` (for x86_64 systems) are provided to be used -instead of ``build.sh``. +## Koch +``koch`` is the build tool used to build various parts of Nim and to generate +documentation and the website, among other things. The ``koch`` tool can also +be used to run the Nim test suite. -The ``koch`` tool is the Nim build tool, more ``koch`` related options are -documented in [doc/koch.rst](doc/koch.rst). +Assuming that you added Nim's ``bin`` directory to your PATH, you may execute +the tests using ``./koch tests``. The tests take a while to run, but you +can run a subset of tests by specifying a category (for example +``./koch tests cat async``). +For more information on the ``koch`` build tool please see the documentation +within the [doc/koch.rst](doc/koch.rst) file. ## Nimble -[Nimble](https://github.com/nim-lang/nimble) is Nim's package manager. For the -source based installations, where you added Nim's ``bin`` directory to your PATH, -the easiest way of installing Nimble is via: - -``` -$ koch nimble -``` - -## Community -[](https://webchat.freenode.net/?channels=nim) -[](https://gitter.im/nim-lang/Nim) -[](http://forum.nim-lang.org) -[](http://stackoverflow.com/questions/tagged/nim?sort=newest&pageSize=15) -[](https://twitter.com/nim_lang) - -* The [forum](http://forum.nim-lang.org/) - the best place to ask questions and to discuss Nim. -* [IRC (Freenode#nim)](https://webchat.freenode.net/?channels=nim) - a place to discuss - Nim in real-time, this is also where most development decision get made! -* [Gitter](https://gitter.im/nim-lang/Nim) allows to discuss Nim from your browser, one click to join. - There is a bridge between Gitter and IRC channels. -* [Stackoverflow](http://stackoverflow.com/questions/tagged/nim) +``nimble`` is Nim's package manager and it can be acquired from the +[``nim-lang/nimble``][nimble-repo] repository. Assuming that you added Nim's +``bin`` directory to your PATH, you may install Nimble from source by running +``koch nimble`` within the root of the cloned repository. ## Contributing +[![Contribute to Nim via Gratipay][badge-nim-gratipay]][nim-gratipay] +[![Setup a bounty via Bountysource][badge-nim-bountysource]][nim-bountysource] +[![Donate Bitcoins][badge-nim-bitcoin]][nim-bitcoin] -[](https://gratipay.com/nim/) -[](https://www.bountysource.com/teams/nim) - -We welcome everyone's contributions to Nim. No matter how small or large -the contribution is, anything from small spelling fixes to large modules -intended to be included in the standard library are accepted. Before -you get started, you should know the following about this repositories -structure: +We welcome everyone's contributions to Nim independent of how small or how large +they are. Anything from small spelling fixes to large modules intended to +be included in the standard library are welcome and appreciated. Before you get +started contributing, you should familiarize yourself with the repository structure: * ``bin/``, ``build/`` - these directories are empty, but are used when Nim is built. -* ``compiler/`` - the compiler source code, all the Nim source code files in this - directory implement the compiler. This also includes nimfix, and plugins - which live in ``compiler/nimfix`` and ``compiler/plugins`` - respectively. Nimsuggest used to live in the ``compiler`` directory also, - but was moved to https://github.com/nim-lang/nimsuggest. +* ``compiler/`` - the compiler source code. Also includes nimfix, and plugins within + ``compiler/nimfix`` and ``compiler/plugins`` respectively. Nimsuggest was moved to + the [``nim-lang/nimsuggest``][nimsuggest-repo] repository, though it previously also + lived within the ``compiler/`` directory. * ``config/`` - the configuration for the compiler and documentation generator. * ``doc/`` - the documentation files in reStructuredText format. -* ``lib/`` - where the standard library lives. +* ``lib/`` - the standard library, including: * ``pure/`` - modules in the standard library written in pure Nim. - * ``impure/`` - modules in the standard library written in pure Nim which - depend on libraries written in other languages. - * ``wrappers/`` - modules which wrap libraries written in other languages. -* ``tests/`` - contains tests for the compiler and standard library, organised by - category. -* ``tools/`` - the tools including ``niminst`` and ``nimweb``, most of these are invoked - via ``koch``. -* ``web/`` - the Nim website (http://nim-lang.org). -* ``koch.nim`` - tool used to bootstrap Nim, generate C sources, build the website, documentation - and more. - -Most importantly, the ``koch`` tool can be used to run the test suite. To do so compile it first -by executing ``nim c koch``, then execute ``./koch tests``. The test suite takes a while to run, -but you can run specific tests by specifying a category to run, for example ``./koch tests cat async``. - -Make sure that the tests all pass before -[submitting your pull request](https://help.github.com/articles/using-pull-requests/). -If you're short on time, you can -just run the tests specific to your change. Just run the category which corresponds to the change -you've made. When you create your pull request, Travis CI will verify that all the tests pass -anyway. - -If you're looking for things to do, take a look at our -[issue tracker](https://github.com/nim-lang/Nim/issues). There is always plenty of issues -labelled [``Easy``](https://github.com/nim-lang/Nim/labels/Easy), these should be a good -starting point if this is your first contribution to Nim. - -You can also help with the development of Nim by making donations. You can do so -in many ways: - -* [Gratipay](https://gratipay.com/nim/) -* [Bountysource](https://www.bountysource.com/teams/nim) -* Bitcoin - 1BXfuKM2uvoD6mbx4g5xM3eQhLzkCK77tJ - -Finally, if you have any questions feel free to submit a question on the issue tracker, -on the [Nim forum](http://forum.nim-lang.org), or on IRC. + * ``impure/`` - modules in the standard library written in pure Nim with + dependencies written in other languages. + * ``wrappers/`` - modules which wrap dependencies written in other languages. +* ``tests/`` - contains categorized tests for the compiler and standard library. +* ``tools/`` - the tools including ``niminst`` and ``nimweb`` (mostly invoked via + ``koch``). +* ``web/`` - [the Nim website][nim-site]. +* ``koch.nim`` - tool used to bootstrap Nim, generate C sources, build the website, + and generate the documentation. + +If you are not familiar with making a pull request using GitHub and/or git, please +read [this guide][pull-request-instructions]. + +Ideally you should make sure that all tests pass before submitting a pull request. +However, if you are short on time, you can just run the tests specific to your +changes by only running the corresponding categories of tests. Travis CI verifies +that all tests pass before allowing the pull request to be accepted, so only +running specific tests should be harmless. + +If you're looking for ways to contribute, please look at our [issue tracker][nim-issues]. +There are always plenty of issues labelled [``Easy``][nim-issues-easy]; these should +be a good starting point for an initial contribution to Nim. + +You can also help with the development of Nim by making donations. Donations can be +made using: + +* [Gratipay][nim-gratipay] +* [Bountysource][nim-bountysource] +* [Bitcoin][nim-bitcoin] + +If you have any questions feel free to submit a question on the +[Nim forum][nim-forum], or via IRC on [the \#nim channel][nim-irc]. ## License -The compiler and the standard library are licensed under the MIT license, -except for some modules where the documentation suggests otherwise. This means -that you can use any license for your own programs developed with Nim, -allowing you to create commercial applications. - -Read copying.txt for more details. - -Copyright (c) 2006-2017 Andreas Rumpf. -All rights reserved. +The compiler and the standard library are licensed under the MIT license, except +for some modules which explicitly state otherwise. As a result you may use any +compatible license (essentially any license) for your own programs developed with +Nim. You are explicitly permitted to develop commercial applications using Nim. + +Please read the [copying.txt](copying.txt) file for more details. + +Copyright © 2006-2017 Andreas Rumpf, all rights reserved. + +[nim-site]: https://nim-lang.org +[nim-forum]: https://forum.nim-lang.org +[nim-issues]: https://github.com/nim-lang/Nim/issues +[nim-issues-easy]: https://github.com/nim-lang/Nim/labels/Easy +[nim-irc]: https://webchat.freenode.net/?channels=nim +[nim-travisci]: https://travis-ci.org/nim-lang/Nim +[nim-twitter]: https://twitter.com/nim_lang +[nim-stackoverflow]: https://stackoverflow.com/questions/tagged/nim +[nim-stackoverflow-newest]: https://stackoverflow.com/questions/tagged/nim?sort=newest&pageSize=15 +[nim-gitter]: https://gitter.im/nim-lang/Nim +[nim-gratipay]: https://gratipay.com/nim/ +[nim-bountysource]: https://www.bountysource.com/teams/nim +[nim-bitcoin]: https://blockchain.info/address/1BXfuKM2uvoD6mbx4g5xM3eQhLzkCK77tJ +[nimble-repo]: https://github.com/nim-lang/nimble +[nimsuggest-repo]: https://github.com/nim-lang/nimsuggest +[csources-repo]: https://github.com/nim-lang/csources +[badge-nim-travisci]: https://img.shields.io/travis/nim-lang/Nim/devel.svg?style=flat-square +[badge-nim-irc]: https://img.shields.io/badge/chat-on_irc-blue.svg?style=flat-square +[badge-nim-gitter]: https://img.shields.io/badge/chat-on_gitter-blue.svg?style=flat-square +[badge-nim-forum-gethelp]: https://img.shields.io/badge/Forum-get%20help-4eb899.svg?style=flat-square +[badge-nim-twitter]: https://img.shields.io/twitter/follow/nim_lang.svg?style=social +[badge-nim-stackoverflow]: https://img.shields.io/badge/stackoverflow-nim_tag-yellow.svg?style=flat-square +[badge-nim-gratipay]: https://img.shields.io/gratipay/team/nim.svg?style=flat-square +[badge-nim-bountysource]: https://img.shields.io/bountysource/team/nim/activity.svg?style=flat-square +[badge-nim-bitcoin]: https://img.shields.io/badge/bitcoin-1BXfuKM2uvoD6mbx4g5xM3eQhLzkCK77tJ-D69134.svg?style=flat-square +[image-nim-logo]: https://images1-focus-opensocial.googleusercontent.com/gadgets/proxy?url=https://raw.githubusercontent.com/nim-lang/assets/master/Art/logo-crown.png&container=focus&resize_w=36&refresh=21600 +[pull-request-instructions]: https://help.github.com/articles/using-pull-requests/ diff --git a/tests/ccgbugs/tgeneric_smallobj_asgn_opt.nim b/tests/ccgbugs/tgeneric_smallobj_asgn_opt.nim new file mode 100644 index 000000000..919dc3fc1 --- /dev/null +++ b/tests/ccgbugs/tgeneric_smallobj_asgn_opt.nim @@ -0,0 +1,26 @@ +discard """ + output: '''false''' +""" + +# bug #5402 + +import lists + +type + Container[T] = ref object + obj: T + + ListOfContainers[T] = ref object + list: DoublyLinkedList[Container[T]] + +proc contains[T](this: ListOfContainers[T], obj: T): bool = + for item in this.list.items(): + if item.obj == obj: return true + return false + +proc newListOfContainers[T](): ListOfContainers[T] = + new(result) + result.list = initDoublyLinkedList[Container[T]]() + +let q = newListOfContainers[int64]() +echo q.contains(123) diff --git a/tests/errmsgs/tproper_stacktrace3.nim b/tests/errmsgs/tproper_stacktrace3.nim new file mode 100644 index 000000000..c292fa092 --- /dev/null +++ b/tests/errmsgs/tproper_stacktrace3.nim @@ -0,0 +1,23 @@ +discard """ + outputsub: '''tproper_stacktrace3.nim(21) main''' + exitcode: 1 +""" + +# bug #5400 + +type Container = object + val: int + +proc actualResolver(x: ptr Container): ptr Container = x + +template resolve(): untyped = actualResolver(db) + +proc myfail(): int = + doAssert false + +proc main() = + var db: ptr Container = nil + # actualResolver(db).val = myfail() # actualResolver is not included in stack trace. + resolve().val = myfail() # resolve template is included in stack trace. + +main() diff --git a/tests/gc/foreign_thr.nim b/tests/gc/foreign_thr.nim new file mode 100644 index 000000000..88ab95113 --- /dev/null +++ b/tests/gc/foreign_thr.nim @@ -0,0 +1,88 @@ +discard """ + output: ''' +Hello from thread +Hello from thread +Hello from thread +Hello from thread +''' + cmd: "nim $target --hints:on --threads:on --tlsEmulation:off $options $file" +""" +# Copied from stdlib +import strutils + +const + StackGuardSize = 4096 + ThreadStackMask = 1024*256*sizeof(int)-1 + ThreadStackSize = ThreadStackMask+1 - StackGuardSize + +type ThreadFunc = proc() {.thread.} + +when defined(posix): + import posix + + proc runInForeignThread(f: ThreadFunc) = + proc wrapper(p: pointer): pointer {.noconv.} = + let thr = cast[ThreadFunc](p) + setupForeignThreadGc() + thr() + tearDownForeignThreadGc() + setupForeignThreadGc() + thr() + tearDownForeignThreadGc() + result = nil + + var attrs {.noinit.}: PthreadAttr + doAssert pthread_attr_init(addr attrs) == 0 + doAssert pthread_attr_setstacksize(addr attrs, ThreadStackSize) == 0 + var tid: Pthread + doAssert pthread_create(addr tid, addr attrs, wrapper, f) == 0 + doAssert pthread_join(tid, nil) == 0 + +elif defined(windows): + import winlean + type + WinThreadProc = proc (x: pointer): int32 {.stdcall.} + + proc createThread(lpThreadAttributes: pointer, dwStackSize: DWORD, + lpStartAddress: WinThreadProc, + lpParameter: pointer, + dwCreationFlags: DWORD, + lpThreadId: var DWORD): Handle {. + stdcall, dynlib: "kernel32", importc: "CreateThread".} + + proc wrapper(p: pointer): int32 {.stdcall.} = + let thr = cast[ThreadFunc](p) + setupForeignThreadGc() + thr() + tearDownForeignThreadGc() + setupForeignThreadGc() + thr() + tearDownForeignThreadGc() + result = 0'i32 + + proc runInForeignThread(f: ThreadFunc) = + var dummyThreadId: DWORD + var h = createThread(nil, ThreadStackSize.int32, wrapper.WinThreadProc, cast[pointer](f), 0, dummyThreadId) + doAssert h != 0.Handle + doAssert waitForSingleObject(h, -1'i32) == 0.DWORD + +else: + {.fatal: "Unknown system".} + +proc runInNativeThread(f: ThreadFunc) = + proc wrapper(f: ThreadFunc) {.thread.} = + # These operations must be NOP + setupForeignThreadGc() + tearDownForeignThreadGc() + f() + f() + var thr: Thread[ThreadFunc] + createThread(thr, wrapper, f) + joinThread(thr) + +proc f {.thread.} = + var msg = "Hello " & "from thread" + echo msg + +runInForeignThread(f) +runInNativeThread(f) diff --git a/tests/metatype/tfieldaccessor.nim b/tests/metatype/tfieldaccessor.nim new file mode 100644 index 000000000..7054dd22b --- /dev/null +++ b/tests/metatype/tfieldaccessor.nim @@ -0,0 +1,17 @@ +type + Test = object + x: int + case p: bool + of true: + a: int + else: + case q: bool + of true: + b: int + else: + discard + +proc f[T](t: typedesc[T]): int = + 1 + +assert Test.f == 1 diff --git a/tests/overload/tprefer_tygenericinst.nim b/tests/overload/tprefer_tygenericinst.nim index 9787af06b..56541c7e8 100644 --- a/tests/overload/tprefer_tygenericinst.nim +++ b/tests/overload/tprefer_tygenericinst.nim @@ -19,7 +19,7 @@ when true: q(B()) # This call reported as ambiguous. # bug #2219 -template testPred(a: expr) = +template testPred(a: untyped) = block: type A = object of RootObj type B = object of A diff --git a/tests/stdlib/tbug5382.nim b/tests/stdlib/tbug5382.nim new file mode 100644 index 000000000..c86656d32 --- /dev/null +++ b/tests/stdlib/tbug5382.nim @@ -0,0 +1,11 @@ +discard """ + output: ''' +02 +''' +""" +import re + +let regexp = re"^\/([0-9]{2})\.html$" +var matches: array[1, string] +discard "/02.html".find(regexp, matches) +echo matches[0] diff --git a/tests/template/tgensymregression.nim b/tests/template/tgensymregression.nim index e73ff258d..e758e0d9a 100644 --- a/tests/template/tgensymregression.nim +++ b/tests/template/tgensymregression.nim @@ -1,3 +1,10 @@ +discard """ + output: '''[0.0, 0.0, 0.0] + +[0.0, 0.0, 0.0, 0.0] + +5050''' +""" template mathPerComponent(op: untyped): untyped = proc op*[N,T](v,u: array[N,T]): array[N,T] {.inline.} = @@ -19,3 +26,24 @@ proc foo(): void = echo repr(v1 *** v2) foo() + +# bug #5383 +import sequtils + +proc zipWithIndex[A](ts: seq[A]): seq[(int, A)] = + toSeq(pairs(ts)) + +proc main = + discard zipWithIndex(@["foo", "bar"]) + discard zipWithIndex(@[1, 2]) + discard zipWithIndex(@[true, false]) + +main() + +# bug #5405 + +proc main2() = + let s = toSeq(1..100).foldL(a + b) + echo s + +main2() diff --git a/tests/template/tparams_gensymed.nim b/tests/template/tparams_gensymed.nim index 6c4413866..568725fd4 100644 --- a/tests/template/tparams_gensymed.nim +++ b/tests/template/tparams_gensymed.nim @@ -5,8 +5,8 @@ import macros # Test that parameters are properly gensym'ed finally: -template genNodeKind(kind, name: expr): stmt = - proc name*(children: varargs[PNimrodNode]): PNimrodNode {.compiletime.}= +template genNodeKind(kind, name: untyped) = + proc name*(children: varargs[NimNode]): NimNode {.compiletime.}= result = newNimNode(kind) for c in children: result.add(c) @@ -22,7 +22,7 @@ type Something = object proc testA(x: Something) = discard -template def(name: expr) {.immediate.} = +template def(name: untyped) = proc testB[T](reallyUniqueName: T) = `test name`(reallyUniqueName) def A @@ -35,8 +35,7 @@ testB(x) # Test that templates in generics still work (regression to fix the # regression...) -template forStatic(index: expr, slice: Slice[int], predicate: stmt): - stmt {.immediate.} = +template forStatic(index, slice, predicate: untyped) = const a = slice.a const b = slice.b when a <= b: diff --git a/tests/testament/categories.nim b/tests/testament/categories.nim index 2dc8e3318..0685dd73a 100644 --- a/tests/testament/categories.nim +++ b/tests/testament/categories.nim @@ -147,6 +147,7 @@ proc gcTests(r: var TResults, cat: Category, options: string) = testSpec r, makeTest("tests/gc" / filename, options & " -d:release --gc:boehm", cat, actionRun) + testWithoutBoehm "foreign_thr" test "gcemscripten" test "growobjcrash" test "gcbench" diff --git a/tests/untestable/tpostgres.nim b/tests/untestable/tpostgres.nim index dcbdaad39..486d0d703 100644 --- a/tests/untestable/tpostgres.nim +++ b/tests/untestable/tpostgres.nim @@ -76,7 +76,236 @@ for i in 1..10: echo(name) discard db.getRow( SqlQuery("INSERT INTO tags(name) VALUES(\'$1\') RETURNING id" % [name])) + +# get column details +db.exec(SqlQuery("DROP TABLE IF EXISTS dbtypes;")) +db.exec(SqlQuery("DROP TYPE IF EXISTS custom_enum;")) +db.exec(SqlQuery("CREATE TYPE custom_enum AS ENUM ('1', '2', '3');")) +db.exec(SqlQuery("DROP TYPE IF EXISTS custom_composite;")) +db.exec(SqlQuery("CREATE TYPE custom_composite AS (r double precision, i double precision);")) +db.exec(SqlQuery("""CREATE TABLE dbtypes( + id serial UNIQUE, + bytea_col bytea, + smallint_col smallint, + integer_col integer, + bigint_col bigint, + decimal_col decimal, + numeric_col numeric, + real_col real, + double_precision_col double precision, + smallserial_col smallserial, + serial_col serial, + bigserial_col bigserial, + money_col money, + varchar_col varchar(10), + character_col character(1), + text_col text, + timestamp_col timestamp, + date_col date, + time_col time, + interval_col interval, + bool_col boolean, + custom_enum_col custom_enum, + point_col point, + line_col line, + lseg_col lseg, + box_col box, + path_col path, + polygon_col polygon, + circle_col circle, + cidr_col cidr, + inet_col inet, + macaddr_col macaddr, + bit_col bit, + varbit_col bit(3), + tsvector_col tsvector, + tsquery_col tsquery, + uuid_col uuid, + xml_col xml, + json_col json, + array_col integer[], + custom_composite_col custom_composite, + range_col int4range + );""")) +db.exec(SqlQuery("INSERT INTO dbtypes (id) VALUES(0);")) +var dbCols : DbColumns = @[] +for row in db.instantRows(dbCols, sql"SELECT * FROM dbtypes"): + doAssert len(dbCols) == 42 + +doAssert dbCols[0].name == "id" +doAssert dbCols[0].typ.kind == DbTypeKind.dbInt +doAssert dbCols[0].typ.name == "int4" +doAssert dbCols[0].typ.size == 4 + +doAssert dbCols[1].name == "bytea_col" +doAssert dbCols[1].typ.kind == DbTypeKind.dbBlob +doAssert dbCols[1].typ.name == "bytea" + +doAssert dbCols[2].name == "smallint_col" +doAssert dbCols[2].typ.kind == DbTypeKind.dbInt +doAssert dbCols[2].typ.name == "int2" +doAssert dbCols[2].typ.size == 2 + +doAssert dbCols[3].name == "integer_col" +doAssert dbCols[3].typ.kind == DbTypeKind.dbInt +doAssert dbCols[3].typ.name == "int4" +doAssert dbCols[3].typ.size == 4 + +doAssert dbCols[4].name == "bigint_col" +doAssert dbCols[4].typ.kind == DbTypeKind.dbInt +doAssert dbCols[4].typ.name == "int8" +doAssert dbCols[4].typ.size == 8 + +doAssert dbCols[5].name == "decimal_col" +doAssert dbCols[5].typ.kind == DbTypeKind.dbDecimal +doAssert dbCols[5].typ.name == "numeric" + +doAssert dbCols[6].name == "numeric_col" +doAssert dbCols[6].typ.kind == DbTypeKind.dbDecimal +doAssert dbCols[6].typ.name == "numeric" + +doAssert dbCols[7].name == "real_col" +doAssert dbCols[7].typ.kind == DbTypeKind.dbFloat +doAssert dbCols[7].typ.name == "float4" + +doAssert dbCols[8].name == "double_precision_col" +doAssert dbCols[8].typ.kind == DbTypeKind.dbFloat +doAssert dbCols[8].typ.name == "float8" + +doAssert dbCols[9].name == "smallserial_col" +doAssert dbCols[9].typ.kind == DbTypeKind.dbInt +doAssert dbCols[9].typ.name == "int2" + +doAssert dbCols[10].name == "serial_col" +doAssert dbCols[10].typ.kind == DbTypeKind.dbInt +doAssert dbCols[10].typ.name == "int4" + +doAssert dbCols[11].name == "bigserial_col" +doAssert dbCols[11].typ.kind == DbTypeKind.dbInt +doAssert dbCols[11].typ.name == "int8" + +doAssert dbCols[12].name == "money_col" +doAssert dbCols[12].typ.kind == DbTypeKind.dbDecimal +doAssert dbCols[12].typ.name == "money" + +doAssert dbCols[13].name == "varchar_col" +doAssert dbCols[13].typ.kind == DbTypeKind.dbVarchar +doAssert dbCols[13].typ.name == "varchar" + +doAssert dbCols[14].name == "character_col" +doAssert dbCols[14].typ.kind == DbTypeKind.dbFixedChar +doAssert dbCols[14].typ.name == "bpchar" + +doAssert dbCols[15].name == "text_col" +doAssert dbCols[15].typ.kind == DbTypeKind.dbVarchar +doAssert dbCols[15].typ.name == "text" + +doAssert dbCols[16].name == "timestamp_col" +doAssert dbCols[16].typ.kind == DbTypeKind.dbTimestamp +doAssert dbCols[16].typ.name == "timestamp" + +doAssert dbCols[17].name == "date_col" +doAssert dbCols[17].typ.kind == DbTypeKind.dbDate +doAssert dbCols[17].typ.name == "date" + +doAssert dbCols[18].name == "time_col" +doAssert dbCols[18].typ.kind == DbTypeKind.dbTime +doAssert dbCols[18].typ.name == "time" + +doAssert dbCols[19].name == "interval_col" +doAssert dbCols[19].typ.kind == DbTypeKind.dbTimeInterval +doAssert dbCols[19].typ.name == "interval" + +doAssert dbCols[20].name == "bool_col" +doAssert dbCols[20].typ.kind == DbTypeKind.dbBool +doAssert dbCols[20].typ.name == "bool" + +doAssert dbCols[21].name == "custom_enum_col" +doAssert dbCols[21].typ.kind == DbTypeKind.dbUnknown +doAssert parseInt(dbCols[21].typ.name) > 0 + +doAssert dbCols[22].name == "point_col" +doAssert dbCols[22].typ.kind == DbTypeKind.dbPoint +doAssert dbCols[22].typ.name == "point" + +doAssert dbCols[23].name == "line_col" +doAssert dbCols[23].typ.kind == DbTypeKind.dbLine +doAssert dbCols[23].typ.name == "line" + +doAssert dbCols[24].name == "lseg_col" +doAssert dbCols[24].typ.kind == DbTypeKind.dbLseg +doAssert dbCols[24].typ.name == "lseg" + +doAssert dbCols[25].name == "box_col" +doAssert dbCols[25].typ.kind == DbTypeKind.dbBox +doAssert dbCols[25].typ.name == "box" + +doAssert dbCols[26].name == "path_col" +doAssert dbCols[26].typ.kind == DbTypeKind.dbPath +doAssert dbCols[26].typ.name == "path" + +doAssert dbCols[27].name == "polygon_col" +doAssert dbCols[27].typ.kind == DbTypeKind.dbPolygon +doAssert dbCols[27].typ.name == "polygon" + +doAssert dbCols[28].name == "circle_col" +doAssert dbCols[28].typ.kind == DbTypeKind.dbCircle +doAssert dbCols[28].typ.name == "circle" + +doAssert dbCols[29].name == "cidr_col" +doAssert dbCols[29].typ.kind == DbTypeKind.dbInet +doAssert dbCols[29].typ.name == "cidr" + +doAssert dbCols[30].name == "inet_col" +doAssert dbCols[30].typ.kind == DbTypeKind.dbInet +doAssert dbCols[30].typ.name == "inet" + +doAssert dbCols[31].name == "macaddr_col" +doAssert dbCols[31].typ.kind == DbTypeKind.dbMacAddress +doAssert dbCols[31].typ.name == "macaddr" + +doAssert dbCols[32].name == "bit_col" +doAssert dbCols[32].typ.kind == DbTypeKind.dbBit +doAssert dbCols[32].typ.name == "bit" + +doAssert dbCols[33].name == "varbit_col" +doAssert dbCols[33].typ.kind == DbTypeKind.dbBit +doAssert dbCols[33].typ.name == "bit" + +doAssert dbCols[34].name == "tsvector_col" +doAssert dbCols[34].typ.kind == DbTypeKind.dbVarchar +doAssert dbCols[34].typ.name == "tsvector" + +doAssert dbCols[35].name == "tsquery_col" +doAssert dbCols[35].typ.kind == DbTypeKind.dbVarchar +doAssert dbCols[35].typ.name == "tsquery" + +doAssert dbCols[36].name == "uuid_col" +doAssert dbCols[36].typ.kind == DbTypeKind.dbVarchar +doAssert dbCols[36].typ.name == "uuid" + +doAssert dbCols[37].name == "xml_col" +doAssert dbCols[37].typ.kind == DbTypeKind.dbXml +doAssert dbCols[37].typ.name == "xml" + +doAssert dbCols[38].name == "json_col" +doAssert dbCols[38].typ.kind == DbTypeKind.dbJson +doAssert dbCols[38].typ.name == "json" + +doAssert dbCols[39].name == "array_col" +doAssert dbCols[39].typ.kind == DbTypeKind.dbArray +doAssert dbCols[39].typ.name == "int4[]" + +doAssert dbCols[40].name == "custom_composite_col" +doAssert dbCols[40].typ.kind == DbTypeKind.dbUnknown +doAssert parseInt(dbCols[40].typ.name) > 0 + +doAssert dbCols[41].name == "range_col" +doAssert dbCols[41].typ.kind == DbTypeKind.dbComposite +doAssert dbCols[41].typ.name == "int4range" + echo("All tests succeeded!") db.close() + diff --git a/tools/finish.nim b/tools/finish.nim index a9fb444a0..e39062b02 100644 --- a/tools/finish.nim +++ b/tools/finish.nim @@ -209,6 +209,9 @@ proc main() = of Manual: echo "After download, move it to: ", dest if askBool("Download successful? (y/n) "): + while not fileExists("dist" / mingw): + echo "could not find: ", "dist" / mingw + if not askBool("Try again? (y/n) "): break if unzip(): retry = true of Failure: discard of Success: diff --git a/web/bountysource.nim.cfg b/web/bountysource.nim.cfg new file mode 100644 index 000000000..521e21de4 --- /dev/null +++ b/web/bountysource.nim.cfg @@ -0,0 +1 @@ +-d:ssl diff --git a/web/inactive_sponsors.csv b/web/inactive_sponsors.csv index d466f3f31..6352bc194 100644 --- a/web/inactive_sponsors.csv +++ b/web/inactive_sponsors.csv @@ -1,26 +1,40 @@ logo, name, url, this_month, all_time, since, level ,bogen,,0,1010,"Jul 23, 2016",1 ,mikra,,0,400,"Apr 28, 2016",1 -,linkmonitor,,0,180,"Jan 28, 2016",1 +,shkolnick-kun,,0,375,"Jul 6, 2016",1 +,"Chris Heller",,0,350,"May 19, 2016",1 +,linkmonitor,,0,280,"Jan 28, 2016",1 ,avsej,,0,110,"Jun 10, 2016",1 ,WilRubin,,0,100,"Aug 11, 2015",1 ,"Benny Luypaert",,0,100,"Apr 10, 2016",1 -,"Chris Heller",,0,100,"May 19, 2016",1 ,PhilipWitte,,0,100,"Aug 5, 2016",1 +,skunkiferous,,0,100,"Oct 2, 2016",1 +,"Jonathan Arnett",,0,90,"May 20, 2016",1 ,Boxifier,,0,75,"Apr 12, 2016",1 ,iolloyd,,0,75,"Apr 29, 2016",1 +,btbytes,,0,70,"Apr 6, 2016",1 ,rb01,,0,50,"May 4, 2016",1 +,barcharcraz,,0,50,"Jun 2, 2016",1 +,zachaysan,,0,50,"Jun 7, 2016",1 +,kunev,,0,50,"Dec 26, 2016",1 +,iboB,,0,50,"Jan 28, 2017",1 ,TedSinger,,0,45,"Apr 9, 2016",1 +,johnnovak,,0,45,"Apr 30, 2016",1 +,"Matthew Baulch",,0,40,"Jun 7, 2016",1 +,"Matthew Newton",,0,35,"Apr 20, 2016",1 ,martinbbjerregaard,,0,35,"Jun 9, 2016",1 ,RationalG,,0,30,"Jun 17, 2016",1 ,benbve,,0,30,"Jul 12, 2016",1 -,barcharcraz,,0,25,"Jun 2, 2016",1 +,multikatt,,0,30,"Nov 2, 2016",1 ,"Landon Bass",,0,25,"Jun 7, 2016",1 ,jimrichards,,0,25,"Jun 8, 2016",1 ,jjzazuet,,0,25,"Jul 10, 2016",1 -,moigagoo,,0,20,"May 13, 2016",1 ,kteza1,,0,20,"Jun 10, 2016",1 ,tomkeus,,0,20,"Sep 4, 2016",1 +,csoriano89,,0,20,"Sep 7, 2016",1 +,juanjux,,0,20,"Oct 29, 2016",1 +,zagfai,,0,20,"Nov 3, 2016",1 +,hellcoderz,,0,20,"Jan 24, 2017",1 ,mirek,,0,15,"Apr 9, 2016",1 ,DateinAsia,,0,15,"Jul 30, 2016",1 ,rickc,,0,15,"Jul 31, 2016",1 @@ -38,13 +52,6 @@ logo, name, url, this_month, all_time, since, level ,Blumenversand,,0,10,"Jul 21, 2016",1 ,cinnabardk,,0,10,"Aug 6, 2016",1 ,reddec,,0,10,"Aug 31, 2016",1 +,cupen,,0,10,"Nov 21, 2016",1 +,yay,,0,10,"Jan 25, 2017",1 ,niv,,0,5,"Apr 6, 2016",1 -,goniz,,0,5,"Apr 7, 2016",1 -,genunix,,0,5,"Apr 12, 2016",1 -,CynepHy6,,0,5,"Apr 14, 2016",1 -,ivanflorentin,,0,5,"May 3, 2016",1 -,stevenyhw,,0,5,"May 20, 2016",1 -,"Sanjay Singh",,0,5,"Jun 6, 2016",1 -,yuttie,,0,5,"Jun 7, 2016",1 -,hron,,0,5,"Jun 11, 2016",1 -,laszlowaty,,0,5,"Jun 17, 2016",1 diff --git a/web/news/e031_version_0_16_2.rst b/web/news/e031_version_0_16_2.rst index 5ee40b772..171e4cef1 100644 --- a/web/news/e031_version_0_16_2.rst +++ b/web/news/e031_version_0_16_2.rst @@ -18,7 +18,11 @@ Changes affecting backwards compatibility - The IO routines now raise ``EOFError`` for the "end of file" condition. ``EOFError`` is a subtype of ``IOError`` and so it's easier to distinguish between "error during read" and "error due to EOF". - +- A hash procedure has been added for ``cstring`` type in ``hashes`` module. + Previously, hash of a ``cstring`` would be calculated as a hash of the + pointer. Now the hash is calculated from the contents of the string, assuming + ``cstring`` is a null-terminated string. Equal ``string`` and ``cstring`` + values produce an equal hash value. Library Additions ----------------- diff --git a/web/sponsors.csv b/web/sponsors.csv index 0701575d5..7136808c6 100644 --- a/web/sponsors.csv +++ b/web/sponsors.csv @@ -1,34 +1,40 @@ logo, name, url, this_month, all_time, since, level -assets/bountysource/secondspectrum.png,Second Spectrum,http://www.secondspectrum.com/,250,1250,"May 5, 2016",250 -assets/bountysource/xored.svg,"Xored Software, Inc.",http://xored.com/,250,1000,"Jun 20, 2016",250 -,shkolnick-kun,https://github.com/shkolnick-kun,75,225,"Jul 6, 2016",75 -,flyx,http://flyx.org,35,210,"Apr 7, 2016",75 -,"Yuriy Glukhov",,25,150,"Apr 6, 2016",25 -,endragor,https://github.com/endragor,25,150,"Apr 7, 2016",25 -,FedericoCeratto,http://firelet.net,25,150,"Apr 7, 2016",25 -,"Adrian Veith",,25,150,"Apr 20, 2016",25 -,skunkiferous,https://github.com/skunkiferous,100,100,"Oct 2, 2016",150 -,euantorano,http://euantorano.co.uk,25,100,"Jun 7, 2016",25 -,xxlabaza,https://github.com/xxlabaza,25,95,"Jun 17, 2016",25 -,btbytes,https://www.btbytes.com/,10,60,"Apr 6, 2016",10 -,niebaopeng,https://github.com/niebaopeng,10,50,"Apr 15, 2016",10 -,"Jonathan Arnett",,10,50,"May 20, 2016",10 -,swalf,https://github.com/swalf,5,45,"May 9, 2016",5 -,zolern,https://github.com/zolern,10,40,"Apr 15, 2016",10 -,"pyloor ",https://schwarz-weiss.cc/,10,40,"May 16, 2016",10 -,zachaysan,http://venn.lc,10,40,"Jun 7, 2016",10 -,"Matthew Baulch",,10,40,"Jun 7, 2016",10 -,"Oskari Timperi",,10,40,"Jun 8, 2016",10 -,"Handojo Goenadi",,5,35,"Apr 19, 2016",5 -,"Matthew Newton",,5,30,"Apr 20, 2016",5 -,johnnovak,http://www.johnnovak.net/,5,30,"Apr 29, 2016",5 -,RyanMarcus,http://rmarcus.info,5,15,"Jul 19, 2016",5 -,lenzenmi,https://github.com/lenzenmi,5,15,"Jul 28, 2016",5 -,cpunion,https://github.com/cpunion,10,10,"Sep 9, 2016",10 -,pandada8,https://github.com/pandada8,5,10,"Aug 12, 2016",5 -,abeaumont,http://alfredobeaumont.org/blog,5,10,"Aug 12, 2016",5 -,"Svend Knudsen",,1,6,"Apr 11, 2016",1 -,"Michael D. Sklaroff",,1,6,"Apr 27, 2016",1 -,csoriano89,https://github.com/csoriano89,5,5,"Sep 7, 2016",5 -,nicck,,1,2,"Aug 9, 2016",1 -,campbellr,,1,1,"Sep 4, 2016",1 +assets/bountysource/secondspectrum.png,Second Spectrum,http://www.secondspectrum.com/,250,2250,"May 5, 2016",250 +assets/bountysource/xored.svg,"Xored Software, Inc.",http://xored.com/,250,1000,250,2000,"Jun 20, 2016",250 +,Varriount,https://github.com/Varriount,250,750,"Nov 18, 2016",250 +,flyx,http://flyx.org,35,350,"Apr 7, 2016",75 +,"Yuriy Glukhov",,25,250,"Apr 6, 2016",25 +,endragor,https://github.com/endragor,25,250,"Apr 7, 2016",25 +,FedericoCeratto,http://firelet.net,25,250,"Apr 7, 2016",25 +,"Adrian Veith",,25,250,"Apr 20, 2016",25 +,euantorano,http://euantorano.co.uk,25,200,"Jun 7, 2016",25 +,xxlabaza,https://github.com/xxlabaza,25,170,"Jun 17, 2016",25 +,devted,https://github.com/devted,25,100,"Oct 19, 2016",25 +,"pyloor ",https://schwarz-weiss.cc/,10,95,"May 16, 2016",10 +,niebaopeng,https://github.com/niebaopeng,10,90,"Apr 15, 2016",10 +,zolern,https://github.com/zolern,10,80,"Apr 15, 2016",10 +,"Oskari Timperi",,10,80,"Jun 8, 2016",10 +,jcosborn,https://github.com/jcosborn,25,75,"Nov 21, 2016",25 +,swalf,https://github.com/swalf,5,65,"May 9, 2016",5 +,"Handojo Goenadi",,5,55,"Apr 19, 2016",5 +,cpunion,https://github.com/cpunion,10,50,"Sep 9, 2016",10 +,D-L,https://github.com/D-L,5,50,"Apr 7, 2016",5 +,moigagoo,http://sloth-ci.com,10,40,"May 13, 2016",10 +,enthus1ast,http://code0.xyz/,10,40,"Oct 28, 2016",10 +,RyanMarcus,http://rmarcus.info,5,35,"Jul 19, 2016",5 +,lenzenmi,https://github.com/lenzenmi,5,35,"Jul 28, 2016",5 +,"Christian Bagley",,10,30,"Oct 11, 2016",10 +,pandada8,https://github.com/pandada8,5,30,"Aug 12, 2016",5 +,abeaumont,http://alfredobeaumont.org/blog,5,30,"Aug 12, 2016",5 +,opendragon,http://www.opendragon.com,25,25,"Jan 18, 2017",25 +,"Eric Raible",,10,20,"Dec 23, 2016",10 +,zefciu,http://pythonista.net,10,20,"Dec 29, 2016",10 +,"Andrey ",https://github.com/Andrey,5,20,"Oct 10, 2016",5 +,syrol,https://github.com/syrol,5,10,"Dec 12, 2016",5 +,"Svend Knudsen",,1,10,"Apr 11, 2016",1 +,"Michael D. Sklaroff",,1,10,"Apr 27, 2016",1 +,nicck,,1,6,"Aug 9, 2016",1 +,cnygaard,,5,5,"Jan 17, 2017",5 +,Aldrog,,5,5,"Feb 11, 2017",5 +,mpachecofaulk55,,5,5,"Feb 11, 2017",5 +,campbellr,,1,5,"Sep 4, 2016",1 |