diff options
115 files changed, 1291 insertions, 1554 deletions
diff --git a/.gitignore b/.gitignore index 5476e173f..fc090130b 100644 --- a/.gitignore +++ b/.gitignore @@ -57,3 +57,7 @@ dist/ # Private directories and files (IDEs) .*/ ~* + +# testament cruft +testresults/ +test.txt diff --git a/changelog.md b/changelog.md index 77e5a29b5..14191e85f 100644 --- a/changelog.md +++ b/changelog.md @@ -2,9 +2,7 @@ ### Changes affecting backwards compatibility -- Removed basic2d/basic3d out of the stdlib and into Nimble packages. - These packages deprecated however, use the ``glm``, ``arraymancer``, ``neo`` - or another package. + - Arrays of char cannot be converted to ``cstring`` anymore, pointers to arrays of char can! This means ``$`` for arrays can finally exist in ``system.nim`` and do the right thing. @@ -16,7 +14,6 @@ module. - The overloading rules changed slightly so that constrained generics are preferred over unconstrained generics. (Bug #6526) -- Removed libuv out of the stdlib and into Nimble packages. - It is now possible to forward declare object types so that mutually recursive types can be created across module boundaries. See [package level objects](https://nim-lang.org/docs/manual.html#package-level-objects) @@ -40,8 +37,6 @@ - Added ``typetraits.$`` as an alias for ``typetraits.name``. - ``os.getEnv`` now takes an optional ``default`` parameter that tells ``getEnv`` what to return if the environment variable does not exist. -- Removed PDCurses wrapper from the stdlib and published it as a separate - Nimble package. - Bodies of ``for`` loops now get their own scope: .. code-block:: nim @@ -74,3 +69,26 @@ This now needs to be written as: var a = -5 for i in a..b: echo i + +- ``formatFloat``/``formatBiggestFloat`` now support formatting floats with zero + precision digits. The previous ``precision = 0`` behavior (default formatting) + is now available via ``precision = -1``. +- The ``nim doc`` command is now an alias for ``nim doc2``, the second version of + the documentation generator. The old version 1 can still be accessed + via the new ``nim doc0`` command. +- Added ``system.getStackTraceEntries`` that allows you to access the stack + trace in a structured manner without string parsing. +- Added ``sequtils.mapLiterals`` for easier construction of array and tuple + literals. +- Added ``parseutils.parseSaturatedNatural``. +- Moved from stdlib into Nimble packages: + - [``basic2d``](https://github.com/nim-lang/basic2d) + _deprecated: use ``glm``, ``arraymancer``, ``neo``, or another package instead_ + - [``basic3d``](https://github.com/nim-lang/basic3d) + _deprecated: use ``glm``, ``arraymancer``, ``neo``, or another package instead_ + - [``gentabs``](https://github.com/lcrees/gentabs) + - [``libuv``](https://github.com/lcrees/libuv) + - [``numeric``](https://github.com/lcrees/polynumeric) + - [``poly``](https://github.com/lcrees/polynumeric) + - [``pdcurses``](https://github.com/lcrees/pdcurses) + - [``romans``](https://github.com/lcrees/romans) diff --git a/compiler/ccgexprs.nim b/compiler/ccgexprs.nim index 5ea719839..d7b8ec667 100644 --- a/compiler/ccgexprs.nim +++ b/compiler/ccgexprs.nim @@ -798,8 +798,7 @@ proc genRecordField(p: BProc, e: PNode, d: var TLoc) = proc genInExprAux(p: BProc, e: PNode, a, b, d: var TLoc) -proc genFieldCheck(p: BProc, e: PNode, obj: Rope, field: PSym; - origTy: PType) = +proc genFieldCheck(p: BProc, e: PNode, obj: Rope, field: PSym) = var test, u, v: TLoc for i in countup(1, sonsLen(e) - 1): var it = e.sons[i] @@ -811,12 +810,10 @@ proc genFieldCheck(p: BProc, e: PNode, obj: Rope, field: PSym; assert(disc.kind == nkSym) initLoc(test, locNone, it, OnStack) initLocExpr(p, it.sons[1], u) - var o = obj - let d = lookupFieldAgain(p, origTy, disc.sym, o) initLoc(v, locExpr, disc, OnUnknown) - v.r = o + v.r = obj v.r.add(".") - v.r.add(d.loc.r) + v.r.add(disc.sym.loc.r) genInExprAux(p, it, u, v, test) let id = nodeTableTestOrSet(p.module.dataCache, newStrNode(nkStrLit, field.name.s), p.module.labels) @@ -842,7 +839,7 @@ proc genCheckedRecordField(p: BProc, e: PNode, d: var TLoc) = if field.loc.r == nil: fillObjectFields(p.module, ty) if field.loc.r == nil: internalError(e.info, "genCheckedRecordField") # generate the checks: - genFieldCheck(p, e, r, field, ty) + genFieldCheck(p, e, r, field) add(r, rfmt(nil, ".$1", field.loc.r)) putIntoDest(p, d, e.sons[0], r, a.storage) else: @@ -1226,7 +1223,7 @@ proc genObjConstr(p: BProc, e: PNode, d: var TLoc) = if field.loc.r == nil: fillObjectFields(p.module, ty) if field.loc.r == nil: internalError(e.info, "genObjConstr") if it.len == 3 and optFieldCheck in p.options: - genFieldCheck(p, it.sons[2], r, field, ty) + genFieldCheck(p, it.sons[2], r, field) add(tmp2.r, ".") add(tmp2.r, field.loc.r) tmp2.k = locTemp @@ -1645,8 +1642,14 @@ proc genSomeCast(p: BProc, e: PNode, d: var TLoc) = putIntoDest(p, d, e, "(($1) ($2))" % [getClosureType(p.module, etyp, clHalfWithEnv), rdCharLoc(a)], a.storage) else: - putIntoDest(p, d, e, "(($1) ($2))" % - [getTypeDesc(p.module, e.typ), rdCharLoc(a)], a.storage) + let srcTyp = skipTypes(e.sons[1].typ, abstractRange) + # C++ does not like direct casts from pointer to shorter integral types + if srcTyp.kind in {tyPtr, tyPointer} and etyp.kind in IntegralTypes: + putIntoDest(p, d, e, "(($1) (ptrdiff_t) ($2))" % + [getTypeDesc(p.module, e.typ), rdCharLoc(a)], a.storage) + else: + putIntoDest(p, d, e, "(($1) ($2))" % + [getTypeDesc(p.module, e.typ), rdCharLoc(a)], a.storage) proc genCast(p: BProc, e: PNode, d: var TLoc) = const ValueTypes = {tyFloat..tyFloat128, tyTuple, tyObject, tyArray} @@ -2275,7 +2278,13 @@ proc getDefaultValue(p: BProc; typ: PType; info: TLineInfo): Rope = result = "{{$1}}" % [genTypeInfo(p.module, t, info)] else: result = rope"{}" - of tyArray, tyTuple: result = rope"{}" + of tyTuple: + result = rope"{" + for i in 0 ..< typ.len: + if i > 0: result.add ", " + result.add getDefaultValue(p, typ.sons[i], info) + result.add "}" + of tyArray: result = rope"{}" of tySet: if mapType(t) == ctArray: result = rope"{}" else: result = rope"0" diff --git a/compiler/cgen.nim b/compiler/cgen.nim index 2a979e8c5..07c2824d0 100644 --- a/compiler/cgen.nim +++ b/compiler/cgen.nim @@ -896,14 +896,15 @@ proc addIntTypes(result: var Rope) {.inline.} = platform.CPU[targetCPU].intSize.rope]) proc getCopyright(cfile: Cfile): Rope = + const copyrightYear = "2017" if optCompileOnly in gGlobalOptions: result = ("/* Generated by Nim Compiler v$1 */$N" & - "/* (c) " & CompileDate.substr(0, 3) & " Andreas Rumpf */$N" & + "/* (c) " & copyrightYear & " Andreas Rumpf */$N" & "/* The generated code is subject to the original license. */$N") % [rope(VersionAsString)] else: result = ("/* Generated by Nim Compiler v$1 */$N" & - "/* (c) " & CompileDate.substr(0, 3) & " Andreas Rumpf */$N" & + "/* (c) " & copyrightYear & " Andreas Rumpf */$N" & "/* The generated code is subject to the original license. */$N" & "/* Compiled for: $2, $3, $4 */$N" & "/* Command for C compiler:$n $5 */$N") % @@ -1292,7 +1293,7 @@ proc myOpen(graph: ModuleGraph; module: PSym; cache: IdentCache): PPassContext = proc writeHeader(m: BModule) = var result = ("/* Generated by Nim Compiler v$1 */$N" & - "/* (c) " & CompileDate.substr(0, 3) & " Andreas Rumpf */$N" & + "/* (c) 2017 Andreas Rumpf */$N" & "/* The generated code is subject to the original license. */$N") % [rope(VersionAsString)] diff --git a/compiler/commands.nim b/compiler/commands.nim index 71de28e09..11a66cf55 100644 --- a/compiler/commands.nim +++ b/compiler/commands.nim @@ -53,8 +53,8 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo; # implementation const - HelpMessage = "Nim Compiler Version $1 (" & CompileDate & ") [$2: $3]\n" & - "Copyright (c) 2006-" & CompileDate.substr(0, 3) & " by Andreas Rumpf\n" + HelpMessage = "Nim Compiler Version $1 [$2: $3]\n" & + "Copyright (c) 2006-2017 by Andreas Rumpf\n" const Usage = slurp"../doc/basicopt.txt".replace("//", "") diff --git a/compiler/docgen.nim b/compiler/docgen.nim index b31e9ce75..8978052e2 100644 --- a/compiler/docgen.nim +++ b/compiler/docgen.nim @@ -608,6 +608,52 @@ proc generateJson*(d: PDoc, n: PNode) = generateJson(d, lastSon(n.sons[0])) else: discard +proc genTagsItem(d: PDoc, n, nameNode: PNode, k: TSymKind): string = + var + name = getName(d, nameNode) + + result = name & "\n" + +proc generateTags*(d: PDoc, n: PNode, r: var Rope) = + case n.kind + of nkCommentStmt: + if n.comment != nil and startsWith(n.comment, "##"): + let stripped = n.comment.substr(2).strip + r.add stripped + of nkProcDef: + when useEffectSystem: documentRaises(n) + r.add genTagsItem(d, n, n.sons[namePos], skProc) + of nkFuncDef: + when useEffectSystem: documentRaises(n) + r.add genTagsItem(d, n, n.sons[namePos], skFunc) + of nkMethodDef: + when useEffectSystem: documentRaises(n) + r.add genTagsItem(d, n, n.sons[namePos], skMethod) + of nkIteratorDef: + when useEffectSystem: documentRaises(n) + r.add genTagsItem(d, n, n.sons[namePos], skIterator) + of nkMacroDef: + r.add genTagsItem(d, n, n.sons[namePos], skMacro) + of nkTemplateDef: + r.add genTagsItem(d, n, n.sons[namePos], skTemplate) + of nkConverterDef: + when useEffectSystem: documentRaises(n) + r.add genTagsItem(d, n, n.sons[namePos], skConverter) + of nkTypeSection, nkVarSection, nkLetSection, nkConstSection: + for i in countup(0, sonsLen(n) - 1): + if n.sons[i].kind != nkCommentStmt: + # order is always 'type var let const': + r.add genTagsItem(d, n.sons[i], n.sons[i].sons[0], + succ(skType, ord(n.kind)-ord(nkTypeSection))) + of nkStmtList: + for i in countup(0, sonsLen(n) - 1): + generateTags(d, n.sons[i], r) + of nkWhenStmt: + # generate documentation for the first branch only: + if not checkForFalse(n.sons[0].sons[0]): + generateTags(d, lastSon(n.sons[0]), r) + else: discard + proc genSection(d: PDoc, kind: TSymKind) = const sectionNames: array[skModule..skTemplate, string] = [ "Imports", "Types", "Vars", "Lets", "Consts", "Vars", "Procs", "Funcs", @@ -745,6 +791,21 @@ proc commandJson*() = #echo getOutFile(gProjectFull, JsonExt) writeRope(content, getOutFile(gProjectFull, JsonExt), useWarning = false) +proc commandTags*() = + var ast = parseFile(gProjectMainIdx, newIdentCache()) + if ast == nil: return + var d = newDocumentor(gProjectFull, options.gConfigVars) + d.hasToc = true + var + content: Rope + generateTags(d, ast, content) + + if optStdout in gGlobalOptions: + writeRope(stdout, content) + else: + #echo getOutFile(gProjectFull, TagsExt) + writeRope(content, getOutFile(gProjectFull, TagsExt), useWarning = false) + proc commandBuildIndex*() = var content = mergeIndexes(gProjectFull).rope diff --git a/compiler/guards.nim b/compiler/guards.nim index 3805320ae..a5e6058c9 100644 --- a/compiler/guards.nim +++ b/compiler/guards.nim @@ -52,7 +52,7 @@ proc isLet(n: PNode): bool = proc isVar(n: PNode): bool = n.kind == nkSym and n.sym.kind in {skResult, skVar} and - {sfGlobal, sfAddrTaken} * n.sym.flags == {} + {sfAddrTaken} * n.sym.flags == {} proc isLetLocation(m: PNode, isApprox: bool): bool = # consider: 'n[].kind' --> we really need to support 1 deref op even if this @@ -768,8 +768,10 @@ macro `=~`(x: PNode, pat: untyped): bool = var conds = newTree(nnkBracket) m(x, pat, conds) - result = nestList(!"and", conds) - + when declared(macros.toNimIdent): + result = nestList(toNimIdent"and", conds) + else: + result = nestList(!"and", conds) proc isMinusOne(n: PNode): bool = n.kind in {nkCharLit..nkUInt64Lit} and n.intVal == -1 diff --git a/compiler/jsgen.nim b/compiler/jsgen.nim index 8be53f11d..4e0d4e28b 100644 --- a/compiler/jsgen.nim +++ b/compiler/jsgen.nim @@ -376,8 +376,8 @@ const # magic checked op; magic unchecked op; checked op; unchecked op ["addInt", "", "addInt($1, $2)", "($1 + $2)"], # AddI ["subInt", "", "subInt($1, $2)", "($1 - $2)"], # SubI ["mulInt", "", "mulInt($1, $2)", "($1 * $2)"], # MulI - ["divInt", "", "divInt($1, $2)", "Math.floor($1 / $2)"], # DivI - ["modInt", "", "modInt($1, $2)", "Math.floor($1 % $2)"], # ModI + ["divInt", "", "divInt($1, $2)", "Math.trunc($1 / $2)"], # DivI + ["modInt", "", "modInt($1, $2)", "Math.trunc($1 % $2)"], # ModI ["addInt", "", "addInt($1, $2)", "($1 + $2)"], # Succ ["subInt", "", "subInt($1, $2)", "($1 - $2)"], # Pred ["", "", "($1 + $2)", "($1 + $2)"], # AddF64 @@ -444,8 +444,8 @@ const # magic checked op; magic unchecked op; checked op; unchecked op ["toU32", "toU32", "toU32($1)", "toU32($1)"], # toU32 ["", "", "$1", "$1"], # ToFloat ["", "", "$1", "$1"], # ToBiggestFloat - ["", "", "Math.floor($1)", "Math.floor($1)"], # ToInt - ["", "", "Math.floor($1)", "Math.floor($1)"], # ToBiggestInt + ["", "", "Math.trunc($1)", "Math.trunc($1)"], # ToInt + ["", "", "Math.trunc($1)", "Math.trunc($1)"], # ToBiggestInt ["nimCharToStr", "nimCharToStr", "nimCharToStr($1)", "nimCharToStr($1)"], ["nimBoolToStr", "nimBoolToStr", "nimBoolToStr($1)", "nimBoolToStr($1)"], ["cstrToNimstr", "cstrToNimstr", "cstrToNimstr(($1)+\"\")", "cstrToNimstr(($1)+\"\")"], diff --git a/compiler/main.nim b/compiler/main.nim index 450542c4c..1e94a6ca0 100644 --- a/compiler/main.nim +++ b/compiler/main.nim @@ -186,12 +186,12 @@ proc mainCommand*(graph: ModuleGraph; cache: IdentCache) = of "php": gCmd = cmdCompileToPHP commandCompileToJS(graph, cache) - of "doc": + of "doc0": wantMainModule() gCmd = cmdDoc loadConfigs(DocConfig, cache) commandDoc() - of "doc2": + of "doc2", "doc": gCmd = cmdDoc loadConfigs(DocConfig, cache) defineSymbol("nimdoc") @@ -217,6 +217,13 @@ proc mainCommand*(graph: ModuleGraph; cache: IdentCache) = wantMainModule() defineSymbol("nimdoc") commandDoc2(graph, cache, true) + of "ctags": + wantMainModule() + gCmd = cmdDoc + loadConfigs(DocConfig, cache) + wantMainModule() + defineSymbol("nimdoc") + commandTags() of "buildindex": gCmd = cmdDoc loadConfigs(DocConfig, cache) diff --git a/compiler/options.nim b/compiler/options.nim index 312e4539a..eec9ce448 100644 --- a/compiler/options.nim +++ b/compiler/options.nim @@ -162,6 +162,7 @@ const RodExt* = "rod" HtmlExt* = "html" JsonExt* = "json" + TagsExt* = "tags" TexExt* = "tex" IniExt* = "ini" DefaultConfig* = "nim.cfg" diff --git a/compiler/pluginsupport.nim b/compiler/pluginsupport.nim index 19a0bc84d..f67942c97 100644 --- a/compiler/pluginsupport.nim +++ b/compiler/pluginsupport.nim @@ -7,8 +7,9 @@ # distribution, for details about the copyright. # -## Plugin support for the Nim compiler. Right now they -## need to be build with the compiler, no DLL support. +## Plugin support for the Nim compiler. Right now plugins +## need to be built with the compiler only: plugins using +## DLLs or the FFI will not work. import ast, semdata, idents diff --git a/compiler/semfold.nim b/compiler/semfold.nim index 089e66abd..1e7c0aa9e 100644 --- a/compiler/semfold.nim +++ b/compiler/semfold.nim @@ -606,13 +606,13 @@ proc getConstExpr(m: PSym, n: PNode): PNode = if a == nil: return nil result.sons[i] = a incl(result.flags, nfAllConst) - of nkObjConstr: - result = copyTree(n) - for i in countup(1, sonsLen(n) - 1): - var a = getConstExpr(m, n.sons[i].sons[1]) - if a == nil: return nil - result.sons[i].sons[1] = a - incl(result.flags, nfAllConst) + #of nkObjConstr: + # result = copyTree(n) + # for i in countup(1, sonsLen(n) - 1): + # var a = getConstExpr(m, n.sons[i].sons[1]) + # if a == nil: return nil + # result.sons[i].sons[1] = a + # incl(result.flags, nfAllConst) of nkPar: # tuple constructor result = copyTree(n) diff --git a/compiler/semmagic.nim b/compiler/semmagic.nim index 0803b99ec..0d0f2ee82 100644 --- a/compiler/semmagic.nim +++ b/compiler/semmagic.nim @@ -127,7 +127,7 @@ proc evalTypeTrait(traitCall: PNode, operand: PType, context: PSym): PNode = of "not": return typeWithSonsResult(tyNot, @[operand]) of "name": - result = newStrNode(nkStrLit, operand.typeToString(preferName)) + result = newStrNode(nkStrLit, operand.typeToString(preferTypeName)) result.typ = newType(tyString, context) result.info = traitCall.info of "arity": diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim index 92a36857a..b032557b7 100644 --- a/compiler/semtypes.nim +++ b/compiler/semtypes.nim @@ -1420,9 +1420,13 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType = else: result = semGeneric(c, n, s, prev) of nkDotExpr: let typeExpr = semExpr(c, n) - if typeExpr.typ.kind == tyFromExpr: - return typeExpr.typ - if typeExpr.typ.kind != tyTypeDesc: + if typeExpr.typ.isNil: + localError(n.info, "object constructor needs an object type;" & + " for named arguments use '=' instead of ':'") + result = errorType(c) + elif typeExpr.typ.kind == tyFromExpr: + result = typeExpr.typ + elif typeExpr.typ.kind != tyTypeDesc: localError(n.info, errTypeExpected) result = errorType(c) else: diff --git a/compiler/sigmatch.nim b/compiler/sigmatch.nim index 97b18306b..3d0b0ed3d 100644 --- a/compiler/sigmatch.nim +++ b/compiler/sigmatch.nim @@ -1385,7 +1385,7 @@ proc typeRelImpl(c: var TCandidate, f, aOrig: PType, # XXX: This is very hacky. It should be moved back into liftTypeParam if x.kind in {tyGenericInst, tyArray} and c.calleeSym != nil and - c.calleeSym.kind in {skProc, skFunc}: + c.calleeSym.kind in {skProc, skFunc} and c.call != nil: let inst = prepareMetatypeForSigmatch(c.c, c.bindings, c.call.info, f) return typeRel(c, inst, a) @@ -1613,7 +1613,7 @@ proc typeRelImpl(c: var TCandidate, f, aOrig: PType, if not exprStructuralEquivalent(f.n, aOrig.n): result = isNone if result != isNone: put(c, f, aOrig) - elif aOrig.n != nil: + elif aOrig.n != nil and aOrig.n.typ != nil: result = typeRel(c, f.lastSon, aOrig.n.typ) if result != isNone: var boundType = newTypeWithSons(c.c, tyStatic, @[aOrig.n.typ]) diff --git a/compiler/transf.nim b/compiler/transf.nim index baf801cbf..69c526951 100644 --- a/compiler/transf.nim +++ b/compiler/transf.nim @@ -365,7 +365,7 @@ proc transformAddrDeref(c: PTransf, n: PNode, a, b: TNodeKind): PTransNode = # addr ( nkConv ( deref ( x ) ) ) --> nkConv(x) n.sons[0].sons[0] = m.sons[0] result = PTransNode(n.sons[0]) - if n.typ.kind != tyOpenArray: + if n.typ.skipTypes(abstractVar).kind != tyOpenArray: PNode(result).typ = n.typ of nkHiddenStdConv, nkHiddenSubConv, nkConv: var m = n.sons[0].sons[1] @@ -373,13 +373,13 @@ proc transformAddrDeref(c: PTransf, n: PNode, a, b: TNodeKind): PTransNode = # addr ( nkConv ( deref ( x ) ) ) --> nkConv(x) n.sons[0].sons[1] = m.sons[0] result = PTransNode(n.sons[0]) - if n.typ.kind != tyOpenArray: + if n.typ.skipTypes(abstractVar).kind != tyOpenArray: PNode(result).typ = n.typ else: if n.sons[0].kind == a or n.sons[0].kind == b: # addr ( deref ( x )) --> x result = PTransNode(n.sons[0].sons[0]) - if n.typ.kind != tyOpenArray: + if n.typ.skipTypes(abstractVar).kind != tyOpenArray: PNode(result).typ = n.typ proc generateThunk(prc: PNode, dest: PType): PNode = diff --git a/compiler/types.nim b/compiler/types.nim index b49640b1f..369d918fd 100644 --- a/compiler/types.nim +++ b/compiler/types.nim @@ -14,7 +14,8 @@ import type TPreferedDesc* = enum - preferName, preferDesc, preferExported, preferModuleInfo, preferGenericArg + preferName, preferDesc, preferExported, preferModuleInfo, preferGenericArg, + preferTypeName proc typeToString*(typ: PType; prefer: TPreferedDesc = preferName): string template `$`*(typ: PType): string = typeToString(typ) @@ -394,7 +395,7 @@ const "and", "or", "not", "any", "static", "TypeFromExpr", "FieldAccessor", "void"] -const preferToResolveSymbols = {preferName, preferModuleInfo, preferGenericArg} +const preferToResolveSymbols = {preferName, preferTypeName, preferModuleInfo, preferGenericArg} template bindConcreteTypeToUserTypeClass*(tc, concrete: PType) = tc.sons.safeAdd concrete @@ -420,7 +421,7 @@ proc typeToString(typ: PType, prefer: TPreferedDesc = preferName): string = sfAnon notin t.sym.flags: if t.kind == tyInt and isIntLit(t): result = t.sym.name.s & " literal(" & $t.n.intVal & ")" - elif prefer == preferName or t.sym.owner.isNil: + elif prefer in {preferName, preferTypeName} or t.sym.owner.isNil: result = t.sym.name.s if t.kind == tyGenericParam and t.sons != nil and t.sonsLen > 0: result.add ": " @@ -518,7 +519,7 @@ proc typeToString(typ: PType, prefer: TPreferedDesc = preferName): string = result = "openarray[" & typeToString(t.sons[0]) & ']' of tyDistinct: result = "distinct " & typeToString(t.sons[0], - if prefer == preferModuleInfo: preferModuleInfo else: preferName) + if prefer == preferModuleInfo: preferModuleInfo else: preferTypeName) of tyTuple: # we iterate over t.sons here, because t.n may be nil if t.n != nil: @@ -532,7 +533,8 @@ proc typeToString(typ: PType, prefer: TPreferedDesc = preferName): string = elif sonsLen(t) == 0: result = "tuple[]" else: - result = "(" + if prefer == preferTypeName: result = "(" + else: result = "tuple of (" for i in countup(0, sonsLen(t) - 1): add(result, typeToString(t.sons[i])) if i < sonsLen(t) - 1: add(result, ", ") diff --git a/compiler/vmgen.nim b/compiler/vmgen.nim index b869be113..8f0c72e45 100644 --- a/compiler/vmgen.nim +++ b/compiler/vmgen.nim @@ -1773,8 +1773,8 @@ proc gen(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags = {}) = of nkAddr, nkHiddenAddr: genAddrDeref(c, n, dest, opcAddrNode, flags) of nkIfStmt, nkIfExpr: genIf(c, n, dest) of nkWhenStmt: - # This is "when nimvm" node. Chose the first branch. - gen(c, n.sons[0].sons[1], dest) + # This is "when nimvm" node. Chose the first branch. + gen(c, n.sons[0].sons[1], dest) of nkCaseStmt: genCase(c, n, dest) of nkWhileStmt: unused(n, dest) @@ -1810,7 +1810,7 @@ proc gen(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags = {}) = of nkVarSection, nkLetSection: unused(n, dest) genVarSection(c, n) - of declarativeDefs: + of declarativeDefs, nkMacroDef: unused(n, dest) of nkLambdaKinds: #let s = n.sons[namePos].sym diff --git a/doc/advopt.txt b/doc/advopt.txt index fb6fd719b..60fd081b8 100644 --- a/doc/advopt.txt +++ b/doc/advopt.txt @@ -7,7 +7,8 @@ Advanced commands: //rst2html convert a reStructuredText file to HTML //rst2tex convert a reStructuredText file to TeX //jsondoc extract the documentation to a json file - //jsondoc2 extract documentation to a json file (uses doc2) + //jsondoc2 extract the documentation to a json file (uses doc2) + //ctags create a tags file //buildIndex build an index for the whole documentation //run run the project (with Tiny C backend; buggy!) //genDepend generate a DOT file containing the diff --git a/doc/basicopt.txt b/doc/basicopt.txt index 3b0271077..4db2d5af7 100644 --- a/doc/basicopt.txt +++ b/doc/basicopt.txt @@ -5,7 +5,6 @@ Command: //compile, c compile project with default code generator (C) //doc generate the documentation for inputfile - //doc2 generate the documentation for inputfile Arguments: arguments are passed to the program being run (if --run option is selected) diff --git a/doc/lib.rst b/doc/lib.rst index 069fcc87c..6dd3f5bea 100644 --- a/doc/lib.rst +++ b/doc/lib.rst @@ -64,7 +64,6 @@ Core * `cpuinfo <cpuinfo.html>`_ This module implements procs to determine the number of CPUs / cores. - Collections and algorithms -------------------------- @@ -182,6 +181,12 @@ Generic Operating System Services This module implements asynchronous file reading and writing using ``asyncdispatch``. +* `distros <distros.html>`_ + This module implements the basics for OS distribution ("distro") detection and the OS's native package manager. + Its primary purpose is to produce output for Nimble packages, but it also contains the widely used **Distribution** enum + that is useful for writing platform specific code. + + Math libraries -------------- diff --git a/doc/manual/generics.txt b/doc/manual/generics.txt index cceea33c0..1ba62bb5c 100644 --- a/doc/manual/generics.txt +++ b/doc/manual/generics.txt @@ -9,26 +9,26 @@ The following example shows a generic binary tree can be modelled: .. code-block:: nim type - BinaryTreeObj[T] = object # BinaryTreeObj is a generic type with - # with generic param ``T`` - le, ri: BinaryTree[T] # left and right subtrees; may be nil - data: T # the data stored in a node - BinaryTree[T] = ref BinaryTreeObj[T] # a shorthand for notational convenience + BinaryTree*[T] = ref object # BinaryTree is a generic type with + # generic param ``T`` + le, ri: BinaryTree[T] # left and right subtrees; may be nil + data: T # the data stored in a node - proc newNode[T](data: T): BinaryTree[T] = # constructor for a node + proc newNode*[T](data: T): BinaryTree[T] = + # constructor for a node new(result) result.data = data - proc add[T](root: var BinaryTree[T], n: BinaryTree[T]) = + proc add*[T](root: var BinaryTree[T], n: BinaryTree[T]) = + # insert a node into the tree if root == nil: root = n else: var it = root while it != nil: - var c = cmp(it.data, n.data) # compare the data items; uses - # the generic ``cmp`` proc that works for - # any type that has a ``==`` and ``<`` - # operator + # compare the data items; uses the generic ``cmp`` proc + # that works for any type that has a ``==`` and ``<`` operator + var c = cmp(it.data, n.data) if c < 0: if it.le == nil: it.le = n @@ -40,20 +40,28 @@ The following example shows a generic binary tree can be modelled: return it = it.ri - iterator inorder[T](root: BinaryTree[T]): T = - # inorder traversal of a binary tree - # recursive iterators are not yet implemented, so this does not work in - # the current compiler! - if root.le != nil: yield inorder(root.le) - yield root.data - if root.ri != nil: yield inorder(root.ri) + proc add*[T](root: var BinaryTree[T], data: T) = + # convenience proc: + add(root, newNode(data)) + + iterator preorder*[T](root: BinaryTree[T]): T = + # Preorder traversal of a binary tree. + # Since recursive iterators are not yet implemented, + # this uses an explicit stack (which is more efficient anyway): + var stack: seq[BinaryTree[T]] = @[root] + while stack.len > 0: + var n = stack.pop() + while n != nil: + yield n.data + add(stack, n.ri) # push right subtree onto the stack + n = n.le # and follow the left pointer var - root: BinaryTree[string] # instantiate a BinaryTree with the type string - add(root, newNode("hallo")) # instantiates generic procs ``newNode`` and - add(root, newNode("world")) # ``add`` - for str in inorder(root): - writeLine(stdout, str) + root: BinaryTree[string] # instantiate a BinaryTree with ``string`` + add(root, newNode("hello")) # instantiates ``newNode`` and ``add`` + add(root, "world") # instantiates the second ``add`` proc + for str in preorder(root): + stdout.writeLine(str) Is operator diff --git a/doc/manual/pragmas.txt b/doc/manual/pragmas.txt index db7ce7e63..835b6909d 100644 --- a/doc/manual/pragmas.txt +++ b/doc/manual/pragmas.txt @@ -102,6 +102,14 @@ collector to not consider objects of this type as part of a cycle: left, right: Node data: string +Or if we directly use a ref object: + +.. code-block:: nim + type + Node = ref object {.acyclic, final.} + left, right: Node + data: string + In the example a tree structure is declared with the ``Node`` type. Note that the type definition is recursive and the GC has to assume that objects of this type may form a cyclic graph. The ``acyclic`` pragma passes the diff --git a/doc/manual/type_sections.txt b/doc/manual/type_sections.txt index af761c77e..12b2a11ac 100644 --- a/doc/manual/type_sections.txt +++ b/doc/manual/type_sections.txt @@ -5,8 +5,7 @@ Example: .. code-block:: nim type # example demonstrating mutually recursive types - Node = ref NodeObj # a traced pointer to a NodeObj - NodeObj = object + Node = ref object # an object managed by the garbage collector (ref) le, ri: Node # left and right subtrees sym: ref Sym # leaves contain a reference to a Sym diff --git a/doc/tut1.rst b/doc/tut1.rst index be5cd8c11..6731efde9 100644 --- a/doc/tut1.rst +++ b/doc/tut1.rst @@ -1511,8 +1511,7 @@ operators perform implicit dereferencing operations for reference types: .. code-block:: nim type - Node = ref NodeObj - NodeObj = object + Node = ref object le, ri: Node data: int var diff --git a/doc/tut2.rst b/doc/tut2.rst index 0bb4c94e1..0636c4ed6 100644 --- a/doc/tut2.rst +++ b/doc/tut2.rst @@ -104,15 +104,14 @@ Example: .. code-block:: nim type - Node = ref NodeObj # a traced reference to a NodeObj - NodeObj = object + Node = ref object # a reference to an object with the following field: le, ri: Node # left and right subtrees sym: ref Sym # leaves contain a reference to a Sym Sym = object # a symbol name: string # the symbol's name line: int # the line the symbol was declared in - code: Node # the symbol's abstract syntax tree + code: Node # the symbol's abstract syntax tree Type conversions @@ -155,8 +154,7 @@ An example: nkAdd, # an addition nkSub, # a subtraction nkIf # an if statement - Node = ref NodeObj - NodeObj = object + Node = ref object case kind: NodeKind # the ``kind`` field is the discriminator of nkInt: intVal: int of nkFloat: floatVal: float @@ -482,11 +480,10 @@ containers: .. code-block:: nim type - BinaryTreeObj[T] = object # BinaryTree is a generic type with - # with generic param ``T`` - le, ri: BinaryTree[T] # left and right subtrees; may be nil - data: T # the data stored in a node - BinaryTree*[T] = ref BinaryTreeObj[T] # type that is exported + BinaryTree*[T] = ref object # BinaryTree is a generic type with + # generic param ``T`` + le, ri: BinaryTree[T] # left and right subtrees; may be nil + data: T # the data stored in a node proc newNode*[T](data: T): BinaryTree[T] = # constructor for a node diff --git a/examples/cross_calculator/ios/resources/ui/NRViewController.xib b/examples/cross_calculator/ios/resources/ui/NRViewController.xib index b260db2af..2118b5044 100644 --- a/examples/cross_calculator/ios/resources/ui/NRViewController.xib +++ b/examples/cross_calculator/ios/resources/ui/NRViewController.xib @@ -85,7 +85,7 @@ <int key="IBUITag">2</int> <bool key="IBUIUserInteractionEnabled">NO</bool> <string key="targetRuntimeIdentifier">IBCocoaTouchFramework</string> - <string key="IBUIText">Nimrod Crossplatform Calculator</string> + <string key="IBUIText">Nim Crossplatform Calculator</string> <object class="NSColor" key="IBUITextColor" id="128895179"> <int key="NSColorSpace">1</int> <bytes key="NSRGB">MCAwIDAAA</bytes> diff --git a/examples/cross_calculator/lazarus/unit1.pas b/examples/cross_calculator/lazarus/unit1.pas index aa0ef6cf7..6091a61d3 100644 --- a/examples/cross_calculator/lazarus/unit1.pas +++ b/examples/cross_calculator/lazarus/unit1.pas @@ -42,7 +42,7 @@ function myAdd(x, y: longint): longint; cdecl; external; procedure TForm1.FormCreate(Sender: TObject); begin - // we initialize the Nimrod data structures here: + // we initialize the Nim data structures here: NimMain(); end; diff --git a/examples/cross_calculator/nim_commandline/nim.cfg b/examples/cross_calculator/nim_commandline/nim.cfg index 41c034430..6f0cb4a01 100644 --- a/examples/cross_calculator/nim_commandline/nim.cfg +++ b/examples/cross_calculator/nim_commandline/nim.cfg @@ -1,4 +1,4 @@ -# Nimrod configuration file. +# Nim configuration file. # The file is used only to add the path of the backend to the compiler options. path="../nim_backend" diff --git a/examples/cross_calculator/readme.txt b/examples/cross_calculator/readme.txt index 5011792b9..72e4130eb 100644 --- a/examples/cross_calculator/readme.txt +++ b/examples/cross_calculator/readme.txt @@ -2,7 +2,7 @@ The cross platform calculator illustrates how to use Nim to create a backend called by different native user interfaces. Since the purpose of the example is to show how the cross platform code -interacts with Nimrod the actual backend code is just a simple addition proc. +interacts with Nim the actual backend code is just a simple addition proc. By keeping your program logic in Nim you can easily reuse it in different platforms. diff --git a/examples/cross_todo/nim_backend/backend.nim b/examples/cross_todo/nim_backend/backend.nim index 6869665f8..513fe304f 100644 --- a/examples/cross_todo/nim_backend/backend.nim +++ b/examples/cross_todo/nim_backend/backend.nim @@ -4,9 +4,8 @@ import db_sqlite, parseutils, strutils, times - type - TTodo* = object + Todo* = object ## A todo object holding the information serialized to the database. id: int64 ## Unique identifier of the object in the ## database, use the getId() accessor to read it. @@ -17,7 +16,7 @@ type ## outside of this module, use the ## getModificationDate accessor. - TPagedParams* = object + PagedParams* = object ## Contains parameters for a query, initialize default values with ## initDefaults(). pageSize*: int64 ## Lines per returned query page, -1 for @@ -27,11 +26,10 @@ type showUnchecked*: bool ## Get unchecked objects. showChecked*: bool ## Get checked objects. - # - General procs -# -proc initDefaults*(params: var TPagedParams) = - ## Sets sane defaults for a TPagedParams object. + +proc initDefaults*(params: var PagedParams) = + ## Sets sane defaults for a PagedParams object. ## ## Note that you should always provide a non zero pageSize, either a specific ## positive value or negative for unbounded query results. @@ -41,7 +39,6 @@ proc initDefaults*(params: var TPagedParams) = params.showUnchecked = true params.showChecked = false - proc openDatabase*(path: string): DbConn = ## Creates or opens the sqlite3 database. ## @@ -56,16 +53,14 @@ proc openDatabase*(path: string): DbConn = desc TEXT NOT NULL, modification_date INTEGER NOT NULL, CONSTRAINT Todos UNIQUE (id))""" - db_sqlite.exec(conn, query) result = conn +# - Procs related to Todo objects -# - Procs related to TTodo objects -# proc initFromDB(id: int64; text: string; priority: int, isDone: bool; - modificationDate: Time): TTodo = - ## Returns an initialized TTodo object created from database parameters. + modificationDate: Time): Todo = + ## Returns an initialized Todo object created from database parameters. ## ## The proc assumes all values are right. Note this proc is NOT exported. assert(id >= 0, "Identity identifiers should not be negative") @@ -75,29 +70,25 @@ proc initFromDB(id: int64; text: string; priority: int, isDone: bool; result.isDone = isDone result.modificationDate = modificationDate - -proc getId*(todo: TTodo): int64 = +proc getId*(todo: Todo): int64 = ## Accessor returning the value of the private id property. return todo.id - -proc getModificationDate*(todo: TTodo): Time = - ## Returns the last modification date of a TTodo entry. +proc getModificationDate*(todo: Todo): Time = + ## Returns the last modification date of a Todo entry. return todo.modificationDate - -proc update*(todo: var TTodo; conn: DbConn): bool = +proc update*(todo: var Todo; conn: DbConn): bool = ## Checks the database for the object and refreshes its variables. ## ## Use this method if you (or another entity) have modified the database and ## want to update the object you have with whatever the database has stored. ## Returns true if the update succeeded, or false if the object was not found ## in the database any more, in which case you should probably get rid of the - ## TTodo object. + ## Todo object. assert(todo.id >= 0, "The identifier of the todo entry can't be negative") let query = sql"""SELECT desc, priority, is_done, modification_date FROM Todos WHERE id = ?""" - try: let rows = conn.getAllRows(query, $todo.id) if len(rows) < 1: @@ -111,8 +102,7 @@ proc update*(todo: var TTodo; conn: DbConn): bool = except: echo("Something went wrong selecting for id " & $todo.id) - -proc save*(todo: var TTodo; conn: DbConn): bool = +proc save*(todo: var Todo; conn: DbConn): bool = ## Saves the current state of text, priority and isDone to the database. ## ## Returns true if the database object was updated (in which case the @@ -127,15 +117,13 @@ proc save*(todo: var TTodo; conn: DbConn): bool = WHERE id = ?""" rowsUpdated = conn.execAffectedRows(query, $todo.text, $todo.priority, $todo.isDone, $int(currentDate), $todo.id) - if 1 == rowsUpdated: todo.modificationDate = currentDate result = true - # - Procs dealing directly with the database -# -proc addTodo*(conn: DbConn; priority: int; text: string): TTodo = + +proc addTodo*(conn: DbConn; priority: int; text: string): Todo = ## Inserts a new todo into the database. ## ## Returns the generated todo object. If there is an error EDb will be raised. @@ -145,10 +133,8 @@ proc addTodo*(conn: DbConn; priority: int; text: string): TTodo = (priority, is_done, desc, modification_date) VALUES (?, 'false', ?, ?)""" todoId = conn.insertId(query, priority, text, $int(currentDate)) - result = initFromDB(todoId, text, priority, false, currentDate) - proc deleteTodo*(conn: DbConn; todoId: int64): int64 {.discardable.} = ## Deletes the specified todo identifier. ## @@ -156,7 +142,6 @@ proc deleteTodo*(conn: DbConn; todoId: int64): int64 {.discardable.} = let query = sql"""DELETE FROM Todos WHERE id = ?""" result = conn.execAffectedRows(query, $todoId) - proc getNumEntries*(conn: DbConn): int = ## Returns the number of entries in the Todos table. ## @@ -170,38 +155,30 @@ proc getNumEntries*(conn: DbConn): int = echo("Something went wrong retrieving number of Todos entries") result = -1 - -proc getPagedTodos*(conn: DbConn; params: TPagedParams; - page = 0'i64): seq[TTodo] = +proc getPagedTodos*(conn: DbConn; params: PagedParams; page = 0'i64): seq[Todo] = ## Returns the todo entries for a specific page. ## ## Pages are calculated based on the params.pageSize parameter, which can be ## set to a negative value to specify no limit at all. The query will be - ## affected by the TPagedParams, which should have sane values (call + ## affected by the PagedParams, which should have sane values (call ## initDefaults). assert(page >= 0, "You should request a page zero or bigger than zero") result = @[] - # Well, if you don't want to see anything, there's no point in asking the db. if not params.showUnchecked and not params.showChecked: return - let order_by = [ if params.priorityAscending: "ASC" else: "DESC", if params.dateAscending: "ASC" else: "DESC"] - query = sql("""SELECT id, desc, priority, is_done, modification_date FROM Todos WHERE is_done = ? OR is_done = ? ORDER BY priority $1, modification_date $2, id DESC LIMIT ? * ?,?""" % order_by) - args = @[$params.showChecked, $(not params.showUnchecked), $params.pageSize, $page, $params.pageSize] - #echo("Query " & string(query)) #echo("args: " & args.join(", ")) - var newId: BiggestInt for row in conn.fastRows(query, args): let numChars = row[0].parseBiggestInt(newId) @@ -209,10 +186,9 @@ proc getPagedTodos*(conn: DbConn; params: TPagedParams; result.add(initFromDB(int64(newId), row[1], row[2].parseInt, row[3].parseBool, Time(row[4].parseInt))) - -proc getTodo*(conn: DbConn; todoId: int64): ref TTodo = - ## Returns a reference to a TTodo or nil if the todo could not be found. - var tempTodo: TTodo +proc getTodo*(conn: DbConn; todoId: int64): ref Todo = + ## Returns a reference to a Todo or nil if the todo could not be found. + var tempTodo: Todo tempTodo.id = todoId if tempTodo.update(conn): new(result) diff --git a/examples/cross_todo/nim_backend/testbackend.nim b/examples/cross_todo/nim_backend/testbackend.nim index 6754f013a..4a71d5f2c 100644 --- a/examples/cross_todo/nim_backend/testbackend.nim +++ b/examples/cross_todo/nim_backend/testbackend.nim @@ -2,8 +2,7 @@ import backend, db_sqlite, strutils, times - -proc showPagedResults(conn: DbConn; params: TPagedParams) = +proc showPagedResults(conn: DbConn; params: PagedParams) = ## Shows the contents of the database in pages of specified size. ## ## Hmm... I guess this is more of a debug proc which should be moved outside, @@ -11,7 +10,6 @@ proc showPagedResults(conn: DbConn; params: TPagedParams) = var page = 0'i64 rows = conn.getPagedTodos(params) - while rows.len > 0: echo("page " & $page) for row in rows: @@ -25,7 +23,6 @@ proc showPagedResults(conn: DbConn; params: TPagedParams) = else: break - proc dumTest() = let conn = openDatabase("todo.sqlite3") try: @@ -35,10 +32,8 @@ proc dumTest() = # Fill some dummy rows if there are not many entries yet. discard conn.addTodo(3, "Filler1") discard conn.addTodo(4, "Filler2") - var todo = conn.addTodo(2, "Testing") echo("New todo added with id " & $todo.getId) - # Try changing it and updating the database. var clonedTodo = conn.getTodo(todo.getId)[] assert(clonedTodo.text == todo.text, "Should be equal") @@ -49,13 +44,11 @@ proc dumTest() = echo("Updated priority $1, done $2" % [$todo.priority, $todo.isDone]) else: assert(false, "Uh oh, I wasn't expecting that!") - # Verify our cloned copy is different but can be updated. assert(clonedTodo.text != todo.text, "Should be different") discard clonedTodo.update(conn) assert(clonedTodo.text == todo.text, "Should be equal") - - var params: TPagedParams + var params: PagedParams params.initDefaults conn.showPagedResults(params) conn.deleteTodo(todo.getId) @@ -66,7 +59,6 @@ proc dumTest() = echo("Later priority $1, done $2" % [$todo.priority, $todo.isDone]) else: echo("Can't update object $1 from db!" % $todo.getId) - # Try to list content in a different way. params.pageSize = 5 params.priorityAscending = true @@ -77,7 +69,6 @@ proc dumTest() = conn.close echo("Database closed") - # Code that will be run only on the commandline. when isMainModule: dumTest() diff --git a/examples/cross_todo/nim_commandline/nim.cfg b/examples/cross_todo/nim_commandline/nim.cfg index 41c034430..6f0cb4a01 100644 --- a/examples/cross_todo/nim_commandline/nim.cfg +++ b/examples/cross_todo/nim_commandline/nim.cfg @@ -1,4 +1,4 @@ -# Nimrod configuration file. +# Nim configuration file. # The file is used only to add the path of the backend to the compiler options. path="../nim_backend" diff --git a/examples/cross_todo/nim_commandline/nimtodo.nim b/examples/cross_todo/nim_commandline/nimtodo.nim index c8993b2c8..be5b3407b 100644 --- a/examples/cross_todo/nim_commandline/nimtodo.nim +++ b/examples/cross_todo/nim_commandline/nimtodo.nim @@ -18,8 +18,8 @@ Commands: -h, --help shows this help List options (optional): - -p=+|- Sorts list by ascending|desdencing priority. Default:desdencing. - -m=+|- Sorts list by ascending|desdencing date. Default:desdencing. + -p=+|- Sorts list by ascending|descending priority. Default:descending. + -m=+|- Sorts list by ascending|descending date. Default:descending. -t Show checked entries. By default they are not shown. -z Hide unchecked entries. By default they are shown. @@ -33,7 +33,7 @@ Examples: """ type - TCommand = enum # The possible types of commands + Command = enum # The possible types of commands cmdAdd # The user wants to add a new todo entry. cmdCheck # User wants to check a todo entry. cmdUncheck # User wants to uncheck a todo entry. @@ -42,30 +42,27 @@ type cmdGenerate # Add random rows to the database, for testing. cmdList # User wants to list contents. - TParamConfig = object + ParamConfig = object # Structure containing the parsed options from the commandline. - command: TCommand # Store the type of operation + command: Command # Store the type of operation addPriority: int # Only valid with cmdAdd, stores priority. addText: seq[string] # Only valid with cmdAdd, stores todo text. todoId: int64 # The todo id for operations like check or delete. - listParams: TPagedParams # Uses the backend structure directly for params. + listParams: PagedParams # Uses the backend structure directly for params. - -proc initDefaults(params: var TParamConfig) = +proc initDefaults(params: var ParamConfig) = ## Initialises defaults value in the structure. ## ## Most importantly we want to have an empty list for addText. params.listParams.initDefaults params.addText = @[] - proc abort(message: string, value: int) = # Simple wrapper to abort also displaying the help to the user. stdout.write(USAGE) quit(message, value) - -template parseTodoIdAndSetCommand(newCommand: TCommand): stmt = +template parseTodoIdAndSetCommand(newCommand: Command): untyped = ## Helper to parse a big todo identifier into todoId and set command. try: let numChars = val.parseBiggestInt(newId) @@ -75,8 +72,7 @@ template parseTodoIdAndSetCommand(newCommand: TCommand): stmt = except OverflowError: raise newException(ValueError, "Value $1 too big" % val) - -template verifySingleCommand(actions: stmt): stmt = +template verifySingleCommand(actions: typed): typed = ## Helper to make sure only one command has been specified so far. if specifiedCommand: abort("Only one command can be specified at a time! (extra:$1)" % [key], 2) @@ -84,7 +80,6 @@ template verifySingleCommand(actions: stmt): stmt = actions specifiedCommand = true - proc parsePlusMinus(val: string, debugText: string): bool = ## Helper to process a plus or minus character from the commandline. ## @@ -100,11 +95,10 @@ proc parsePlusMinus(val: string, debugText: string): bool = else: abort("$1 parameter should be + or - but was '$2'." % [debugText, val], 4) - -proc parseCmdLine(): TParamConfig = +proc parseCmdLine(): ParamConfig = ## Parses the commandline. ## - ## Returns a TParamConfig structure filled with the proper values or directly + ## Returns a ParamConfig structure filled with the proper values or directly ## calls quit() with the appropriate error message. var specifiedCommand = false @@ -112,15 +106,12 @@ proc parseCmdLine(): TParamConfig = p = initOptParser() key, val: TaintedString newId: BiggestInt - result.initDefaults - try: while true: next(p) key = p.key val = p.val - case p.kind of cmdArgument: if specifiedCommand and cmdAdd == result.command: @@ -180,17 +171,13 @@ proc parseCmdLine(): TParamConfig = break except ValueError: abort("Invalid integer value '$1' for parameter '$2'." % [val, key], 7) - if not specifiedCommand: abort("Didn't specify any command.", 8) - if cmdAdd == result.command and result.addText.len < 1: abort("Used the add command, but provided no text/description.", 9) - if usesListParams and cmdList != result.command: abort("Used list options, but didn't specify the list command.", 10) - proc generateDatabaseRows(conn: DbConn) = ## Adds some rows to the database ignoring errors. discard conn.addTodo(1, "Watch another random youtube video") @@ -208,19 +195,16 @@ proc generateDatabaseRows(conn: DbConn) = discard conn.addTodo(6, "Learn a functional programming language") echo("Generated some entries, they were added to your database.") - -proc listDatabaseContents(conn: DbConn; listParams: TPagedParams) = +proc listDatabaseContents(conn: DbConn; listParams: PagedParams) = ## Dumps the database contents formatted to the standard output. ## ## Pass the list/filter parameters parsed from the commandline. var params = listParams params.pageSize = -1 - let todos = conn.getPagedTodos(params) if todos.len < 1: echo("Database empty") return - echo("Todo id, is done, priority, last modification date, text:") # First detect how long should be our columns for formatting. var cols: array[0..2, int] @@ -228,7 +212,6 @@ proc listDatabaseContents(conn: DbConn; listParams: TPagedParams) = cols[0] = max(cols[0], ($todo.getId).len) cols[1] = max(cols[1], ($todo.priority).len) cols[2] = max(cols[2], ($todo.getModificationDate).len) - # Now dump all the rows using the calculated alignment sizes. for todo in todos: echo("$1 $2 $3, $4, $5" % [ @@ -238,7 +221,6 @@ proc listDatabaseContents(conn: DbConn; listParams: TPagedParams) = ($todo.getModificationDate).align(cols[2]), todo.text]) - proc deleteOneTodo(conn: DbConn; todoId: int64) = ## Deletes a single todo entry from the database. let numDeleted = conn.deleteTodo(todoId) @@ -247,7 +229,6 @@ proc deleteOneTodo(conn: DbConn; todoId: int64) = else: quit("Couldn't delete todo id " & $todoId, 11) - proc deleteAllTodos(conn: DbConn) = ## Deletes all the contents from the database. ## @@ -256,43 +237,35 @@ proc deleteAllTodos(conn: DbConn) = ## ourselfves to the API exported by backend. var counter: int64 - params: TPagedParams - + params: PagedParams params.initDefaults params.pageSize = -1 params.showUnchecked = true params.showChecked = true - let todos = conn.getPagedTodos(params) for todo in todos: if conn.deleteTodo(todo.getId) > 0: counter += 1 else: quit("Couldn't delete todo id " & $todo.getId, 12) - echo("Deleted $1 todo entries from database." % $counter) - proc setTodoCheck(conn: DbConn; todoId: int64; value: bool) = ## Changes the check state of a todo entry to the specified value. let newState = if value: "checked" else: "unchecked" todo = conn.getTodo(todoId) - if todo == nil: quit("Can't modify todo id $1, its not in the database." % $todoId, 13) - if todo[].isDone == value: echo("Todo id $1 was already set to $2." % [$todoId, newState]) return - todo[].isDone = value if todo[].save(conn): echo("Todo id $1 set to $2." % [$todoId, newState]) else: quit("Error updating todo id $1 to $2." % [$todoId, newState]) - proc addTodo(conn: DbConn; priority: int; tokens: seq[string]) = ## Adds to the database a todo with the specified priority. ## @@ -302,17 +275,14 @@ proc addTodo(conn: DbConn; priority: int; tokens: seq[string]) = echo("Created todo entry with id:$1 for priority $2 and text '$3'." % [ $todo.getId, $todo.priority, todo.text]) - when isMainModule: ## Main entry point. let opt = parseCmdLine() dbPath = getConfigDir() / "nimtodo.sqlite3" - if not dbPath.existsFile: createDir(getConfigDir()) echo("No database found at $1, it will be created for you." % dbPath) - let conn = openDatabase(dbPath) try: case opt.command diff --git a/examples/cross_todo/nim_commandline/readme.txt b/examples/cross_todo/nim_commandline/readme.txt index ca4b67521..7d68bbc8b 100644 --- a/examples/cross_todo/nim_commandline/readme.txt +++ b/examples/cross_todo/nim_commandline/readme.txt @@ -14,6 +14,6 @@ generation switch can be used to fill the database with some basic todo entries you can play with. Compilation is fairly easy despite having the source split in different -directories. Thanks to the Nim.cfg file, which adds the ../Nim_backend +directories. Thanks to the nim.cfg file, which adds the ../Nim_backend directory as a search path, you can compile and run the example just fine from the command line with 'nim c -r nimtodo.nim'. diff --git a/examples/cross_todo/readme.txt b/examples/cross_todo/readme.txt index 5be01e197..44e8c47aa 100644 --- a/examples/cross_todo/readme.txt +++ b/examples/cross_todo/readme.txt @@ -1,4 +1,4 @@ -The cross platform todo illustrates how to use Nim to create a backend +This cross platform todo illustrates how to use Nim to create a backend called by different native user interfaces. This example builds on the knowledge learned from the cross_calculator example. diff --git a/examples/keyval.nim b/examples/keyval.nim index ae8cb8f08..a594c0fa8 100644 --- a/examples/keyval.nim +++ b/examples/keyval.nim @@ -3,7 +3,6 @@ import re for x in lines("myfile.txt"): if x =~ re"(\w+)=(.*)": - echo "Key: ", matches[0], - " Value: ", matches[1] + echo "Key: ", matches[0], " Value: ", matches[1] diff --git a/examples/readme.txt b/examples/readme.txt index dc3d3fb9b..176bc8239 100644 --- a/examples/readme.txt +++ b/examples/readme.txt @@ -1,4 +1,4 @@ -In this directory you will find several examples for how to use the Nimrod +In this directory you will find several examples for how to use the Nim library. Copyright (c) 2004-2012 Andreas Rumpf. diff --git a/examples/talk/dsl.nim b/examples/talk/dsl.nim index 1034c99d4..2dde51790 100644 --- a/examples/talk/dsl.nim +++ b/examples/talk/dsl.nim @@ -1,20 +1,20 @@ import strutils -template html(name: expr, matter: stmt) {.immediate.} = +template html(name, matter: untyped) = proc name(): string = result = "<html>" matter result.add("</html>") -template nestedTag(tag: expr) {.immediate.} = - template tag(matter: stmt) {.immediate.} = +template nestedTag(tag: untyped) = + template tag(matter: typed) = result.add("<" & astToStr(tag) & ">") matter result.add("</" & astToStr(tag) & ">") -template simpleTag(tag: expr) {.immediate.} = - template tag(matter: expr) {.immediate.} = +template simpleTag(tag: untyped) = + template tag(matter: untyped) = result.add("<$1>$2</$1>" % [astToStr(tag), matter]) nestedTag body diff --git a/examples/talk/formatoptimizer.nim b/examples/talk/formatoptimizer.nim index db11d112d..104214e19 100644 --- a/examples/talk/formatoptimizer.nim +++ b/examples/talk/formatoptimizer.nim @@ -5,7 +5,7 @@ import macros proc invalidFormatString() = echo "invalidFormatString" -template formatImpl(handleChar: expr) = +template formatImpl(handleChar: untyped) = var i = 0 while i < f.len: if f[i] == '$': @@ -24,11 +24,11 @@ template formatImpl(handleChar: expr) = i += 1 proc `%`*(f: string, a: openArray[string]): string = - template identity(x: expr): expr = x + template identity(x: untyped): untyped = x result = "" formatImpl(identity) -macro optFormat{`%`(f, a)}(f: string{lit}, a: openArray[string]): expr = +macro optFormat{`%`(f, a)}(f: string{lit}, a: openArray[string]): untyped = result = newNimNode(nnkBracket) #newCall("&") let f = f.strVal diff --git a/examples/talk/quasiquote.nim b/examples/talk/quasiquote.nim index df4003e6e..b3c7bb971 100644 --- a/examples/talk/quasiquote.nim +++ b/examples/talk/quasiquote.nim @@ -1,7 +1,7 @@ import macros -macro check(ex: expr): stmt = +macro check(ex: untyped): typed = var info = ex.lineinfo var expString = ex.toStrLit result = quote do: diff --git a/examples/talk/tags.nim b/examples/talk/tags.nim index 12b9a08c3..8bf3450c9 100644 --- a/examples/talk/tags.nim +++ b/examples/talk/tags.nim @@ -1,8 +1,9 @@ -template htmlTag(tag: expr) {.immediate.} = +template htmlTag(tag: untyped) = proc tag(): string = "<" & astToStr(tag) & ">" htmlTag(br) htmlTag(html) echo br() +echo html() \ No newline at end of file diff --git a/koch.nim b/koch.nim index dd4213437..cde74966b 100644 --- a/koch.nim +++ b/koch.nim @@ -473,7 +473,7 @@ proc temp(args: string) = # commit. let (bootArgs, programArgs) = splitArgs(args) let nimexec = findNim() - exec(nimexec & " c " & bootArgs & " compiler" / "nim", 125) + exec(nimexec & " c -d:debug " & bootArgs & " compiler" / "nim", 125) copyExe(output, finalDest) if programArgs.len > 0: exec(finalDest & " " & programArgs) @@ -482,7 +482,7 @@ proc xtemp(cmd: string) = copyExe(d / "bin" / "nim".exe, d / "bin" / "nim_backup".exe) try: withDir(d): - temp"-d:debug" + temp"" copyExe(d / "bin" / "nim_temp".exe, d / "bin" / "nim".exe) exec(cmd) finally: diff --git a/lib/core/macros.nim b/lib/core/macros.nim index dc56bb671..ebc9f7714 100644 --- a/lib/core/macros.nim +++ b/lib/core/macros.nim @@ -113,7 +113,9 @@ type type NimIdent* = object of RootObj - ## represents a Nim identifier in the AST + ## represents a Nim identifier in the AST. **Note**: This is only + ## rarely useful, for identifier construction from a string + ## use ``ident"abc"``. NimSymObj = object # hidden NimSym* = ref NimSymObj @@ -129,7 +131,11 @@ const nnkCallKinds* = {nnkCall, nnkInfix, nnkPrefix, nnkPostfix, nnkCommand, nnkCallStrLit} -proc `!`*(s: string): NimIdent {.magic: "StrToIdent", noSideEffect.} +proc `!`*(s: string): NimIdent {.magic: "StrToIdent", noSideEffect, deprecated.} + ## constructs an identifier from the string `s` + ## **Deprecated since version 0.18.0**: Use ``toNimIdent`` instead. + +proc toNimIdent*(s: string): NimIdent {.magic: "StrToIdent", noSideEffect.} ## constructs an identifier from the string `s` proc `$`*(i: NimIdent): string {.magic: "IdentToStr", noSideEffect.} @@ -237,7 +243,7 @@ proc `ident=`*(n: NimNode, val: NimIdent) {.magic: "NSetIdent", noSideEffect.} proc `strVal=`*(n: NimNode, val: string) {.magic: "NSetStrVal", noSideEffect.} proc newNimNode*(kind: NimNodeKind, - lineInfoFrom: NimNode=nil): NimNode + lineInfoFrom: NimNode = nil): NimNode {.magic: "NNewNimNode", noSideEffect.} ## Creates a new AST node of the specified kind. ## @@ -290,7 +296,7 @@ proc newIdentNode*(i: NimIdent): NimNode {.compileTime.} = proc newIdentNode*(i: string): NimNode {.compileTime.} = ## creates an identifier node from `i` result = newNimNode(nnkIdent) - result.ident = !i + result.ident = toNimIdent i type @@ -400,7 +406,7 @@ proc quote*(bl: typed, op = "``"): NimNode {.magic: "QuoteAst", noSideEffect.} ## ## .. code-block:: nim ## - ## macro check(ex: expr): stmt = + ## macro check(ex: untyped): typed = ## # this is a simplified version of the check macro from the ## # unittest module. ## @@ -477,7 +483,6 @@ proc newLit*(c: char): NimNode {.compileTime.} = result = newNimNode(nnkCharLit) result.intVal = ord(c) - proc newLit*(i: int): NimNode {.compileTime.} = ## produces a new integer literal node. result = newNimNode(nnkIntLit) @@ -581,7 +586,7 @@ proc newLit*[T](arg: seq[T]): NimNode {.compileTime.} = proc newLit*(arg: tuple): NimNode {.compileTime.} = result = nnkPar.newTree for a,b in arg.fieldPairs: - result.add nnkExprColonExpr.newTree( newIdentNode(a), newLit(b) ) + result.add nnkExprColonExpr.newTree(newIdentNode(a), newLit(b)) proc newLit*(s: string): NimNode {.compileTime.} = ## produces a new string literal node. @@ -615,7 +620,7 @@ proc treeRepr*(n: NimNode): string {.compileTime, benign.} = of nnkCharLit..nnkInt64Lit: res.add(" " & $n.intVal) of nnkFloatLit..nnkFloat64Lit: res.add(" " & $n.floatVal) of nnkStrLit..nnkTripleStrLit: res.add(" " & $n.strVal) - of nnkIdent: res.add(" !\"" & $n.ident & '"') + of nnkIdent: res.add(" ident\"" & $n.ident & '"') of nnkSym: res.add(" \"" & $n.symbol & '"') of nnkNone: assert false else: @@ -640,7 +645,7 @@ proc lispRepr*(n: NimNode): string {.compileTime, benign.} = of nnkCharLit..nnkInt64Lit: add(result, $n.intVal) of nnkFloatLit..nnkFloat64Lit: add(result, $n.floatVal) of nnkStrLit..nnkTripleStrLit: add(result, $n.strVal) - of nnkIdent: add(result, "!\"" & $n.ident & '"') + of nnkIdent: add(result, "ident\"" & $n.ident & '"') of nnkSym: add(result, $n.symbol) of nnkNone: assert false else: @@ -664,7 +669,7 @@ proc astGenRepr*(n: NimNode): string {.compileTime, benign.} = ## .. code-block:: nim ## nnkStmtList.newTree( ## nnkCommand.newTree( - ## newIdentNode(!"echo"), + ## newIdentNode("echo"), ## newLit("Hello world") ## ) ## ) @@ -718,7 +723,7 @@ proc astGenRepr*(n: NimNode): string {.compileTime, benign.} = of nnkIntLit..nnkInt64Lit: res.add($n.intVal) of nnkFloatLit..nnkFloat64Lit: res.add($n.floatVal) of nnkStrLit..nnkTripleStrLit: res.add($n.strVal.escape()) - of nnkIdent: res.add("!" & ($n.ident).escape()) + of nnkIdent: res.add(($n.ident).escape()) of nnkSym: res.add(($n.symbol).escape()) of nnkNone: assert false else: @@ -898,6 +903,48 @@ proc newIfStmt*(branches: varargs[tuple[cond, body: NimNode]]): for i in branches: result.add(newNimNode(nnkElifBranch).add(i.cond, i.body)) +proc newEnum*(name: NimNode, fields: openArray[NimNode], + public, pure: bool): NimNode {.compileTime.} = + + ## Creates a new enum. `name` must be an ident. Fields are allowed to be + ## either idents or EnumFieldDef + ## + ## .. code-block:: nim + ## + ## newEnum( + ## name = ident("Colors"), + ## fields = [ident("Blue"), ident("Red")], + ## public = true, pure = false) + ## + ## # type Colors* = Blue Red + ## + + expectKind name, nnkIdent + doAssert len(fields) > 0, "Enum must contain at least one field" + for field in fields: + expectKind field, {nnkIdent, nnkEnumFieldDef} + + let enumBody = newNimNode(nnkEnumTy).add(newEmptyNode()).add(fields) + var typeDefArgs = [name, newEmptyNode(), enumBody] + + if public: + let postNode = newNimNode(nnkPostfix).add( + newIdentNode("*"), typeDefArgs[0]) + + typeDefArgs[0] = postNode + + if pure: + let pragmaNode = newNimNode(nnkPragmaExpr).add( + typeDefArgs[0], + add(newNimNode(nnkPragma), newIdentNode("pure"))) + + typeDefArgs[0] = pragmaNode + + let + typeDef = add(newNimNode(nnkTypeDef), typeDefArgs) + typeSect = add(newNimNode(nnkTypeSection), typeDef) + + return typeSect proc copyChildrenTo*(src, dest: NimNode) {.compileTime.}= ## Copy all children from `src` to `dest` @@ -1015,7 +1062,7 @@ template findChild*(n: NimNode; cond: untyped): NimNode {.dirty.} = ## ## .. code-block:: nim ## var res = findChild(n, it.kind == nnkPostfix and - ## it.basename.ident == !"foo") + ## it.basename.ident == toNimIdent"foo") block: var res: NimNode for it in n.children: @@ -1049,7 +1096,7 @@ proc basename*(a: NimNode): NimNode = proc `basename=`*(a: NimNode; val: string) {.compileTime.}= case a.kind - of nnkIdent: macros.`ident=`(a, !val) + of nnkIdent: macros.`ident=`(a, toNimIdent val) of nnkPostfix, nnkPrefix: a[1] = ident(val) else: quit "Do not know how to get basename of (" & treeRepr(a) & ")\n" & repr(a) @@ -1110,7 +1157,7 @@ proc eqIdent*(node: NimNode; s: string): bool {.compileTime.} = ## other ways like ``node.ident`` are much more error-prone, unfortunately. case node.kind of nnkIdent: - result = node.ident == !s + result = node.ident == toNimIdent s of nnkSym: result = eqIdent($node.symbol, s) of nnkOpenSymChoice, nnkClosedSymChoice: diff --git a/lib/deprecated/pure/rawsockets.nim b/lib/deprecated/pure/rawsockets.nim index ee77b232e..876334f9e 100644 --- a/lib/deprecated/pure/rawsockets.nim +++ b/lib/deprecated/pure/rawsockets.nim @@ -3,12 +3,12 @@ export nativesockets {.warning: "rawsockets module is deprecated, use nativesockets instead".} -template newRawSocket*(domain, sockType, protocol: cint): expr = +template newRawSocket*(domain, sockType, protocol: cint): untyped = {.warning: "newRawSocket is deprecated, use newNativeSocket instead".} newNativeSocket(domain, sockType, protocol) template newRawSocket*(domain: Domain = AF_INET, sockType: SockType = SOCK_STREAM, - protocol: Protocol = IPPROTO_TCP): expr = + protocol: Protocol = IPPROTO_TCP): untyped = {.warning: "newRawSocket is deprecated, use newNativeSocket instead".} newNativeSocket(domain, sockType, protocol) diff --git a/lib/pure/asynchttpserver.nim b/lib/pure/asynchttpserver.nim index 6d4b85145..433931c9d 100644 --- a/lib/pure/asynchttpserver.nim +++ b/lib/pure/asynchttpserver.nim @@ -58,15 +58,18 @@ type socket: AsyncSocket reuseAddr: bool reusePort: bool + maxBody: int ## The maximum content-length that will be read for the body. {.deprecated: [TRequest: Request, PAsyncHttpServer: AsyncHttpServer, THttpCode: HttpCode, THttpVersion: HttpVersion].} -proc newAsyncHttpServer*(reuseAddr = true, reusePort = false): AsyncHttpServer = +proc newAsyncHttpServer*(reuseAddr = true, reusePort = false, + maxBody = 8388608): AsyncHttpServer = ## Creates a new ``AsyncHttpServer`` instance. new result result.reuseAddr = reuseAddr result.reusePort = reusePort + result.maxBody = maxBody proc addHeaders(msg: var string, headers: HttpHeaders) = for k, v in headers: @@ -122,144 +125,160 @@ proc parseProtocol(protocol: string): tuple[orig: string, major, minor: int] = raise newException(ValueError, "Invalid request protocol. Got: " & protocol) result.orig = protocol - i.inc protocol.parseInt(result.major, i) + i.inc protocol.parseSaturatedNatural(result.major, i) i.inc # Skip . - i.inc protocol.parseInt(result.minor, i) + i.inc protocol.parseSaturatedNatural(result.minor, i) proc sendStatus(client: AsyncSocket, status: string): Future[void] = client.send("HTTP/1.1 " & status & "\c\L\c\L") -proc processClient(client: AsyncSocket, address: string, - callback: proc (request: Request): +proc processRequest(server: AsyncHttpServer, req: FutureVar[Request], + client: AsyncSocket, + address: string, lineFut: FutureVar[string], + callback: proc (request: Request): Future[void] {.closure, gcsafe.}) {.async.} = - var request: Request - request.url = initUri() - request.headers = newHttpHeaders() - var lineFut = newFutureVar[string]("asynchttpserver.processClient") - lineFut.mget() = newStringOfCap(80) - var key, value = "" - while not client.isClosed: - # GET /path HTTP/1.1 - # Header: val - # \n - request.headers.clear() - request.body = "" - request.hostname.shallowCopy(address) - assert client != nil - request.client = client - - # We should skip at least one empty line before the request - # https://tools.ietf.org/html/rfc7230#section-3.5 - for i in 0..1: - lineFut.mget().setLen(0) - lineFut.clean() - await client.recvLineInto(lineFut, maxLength=maxLine) # TODO: Timeouts. - - if lineFut.mget == "": - client.close() - return + # Alias `request` to `req.mget()` so we don't have to write `mget` everywhere. + template request(): Request = + req.mget() + + # GET /path HTTP/1.1 + # Header: val + # \n + request.headers.clear() + request.body = "" + request.hostname.shallowCopy(address) + assert client != nil + request.client = client + + # We should skip at least one empty line before the request + # https://tools.ietf.org/html/rfc7230#section-3.5 + for i in 0..1: + lineFut.mget().setLen(0) + lineFut.clean() + await client.recvLineInto(lineFut, maxLength=maxLine) # TODO: Timeouts. + + if lineFut.mget == "": + client.close() + return - if lineFut.mget.len > maxLine: - await request.respondError(Http413) - client.close() + if lineFut.mget.len > maxLine: + await request.respondError(Http413) + client.close() + return + if lineFut.mget != "\c\L": + break + + # First line - GET /path HTTP/1.1 + var i = 0 + for linePart in lineFut.mget.split(' '): + case i + of 0: + try: + # TODO: this is likely slow. + request.reqMethod = parseEnum[HttpMethod]("http" & linePart) + except ValueError: + asyncCheck request.respondError(Http400) return - if lineFut.mget != "\c\L": - break - - # First line - GET /path HTTP/1.1 - var i = 0 - for linePart in lineFut.mget.split(' '): - case i - of 0: - try: - # TODO: this is likely slow. - request.reqMethod = parseEnum[HttpMethod]("http" & linePart) - except ValueError: - asyncCheck request.respondError(Http400) - continue - of 1: - try: - parseUri(linePart, request.url) - except ValueError: - asyncCheck request.respondError(Http400) - continue - of 2: - try: - request.protocol = parseProtocol(linePart) - except ValueError: - asyncCheck request.respondError(Http400) - continue - else: - await request.respondError(Http400) - continue - inc i - - # Headers - while true: - i = 0 - lineFut.mget.setLen(0) - lineFut.clean() - await client.recvLineInto(lineFut, maxLength=maxLine) - - if lineFut.mget == "": - client.close(); return - if lineFut.mget.len > maxLine: - await request.respondError(Http413) - client.close(); return - if lineFut.mget == "\c\L": break - let (key, value) = parseHeader(lineFut.mget) - request.headers[key] = value - # Ensure the client isn't trying to DoS us. - if request.headers.len > headerLimit: - await client.sendStatus("400 Bad Request") - request.client.close() + of 1: + try: + parseUri(linePart, request.url) + except ValueError: + asyncCheck request.respondError(Http400) return + of 2: + try: + request.protocol = parseProtocol(linePart) + except ValueError: + asyncCheck request.respondError(Http400) + return + else: + await request.respondError(Http400) + return + inc i - if request.reqMethod == HttpPost: - # Check for Expect header - if request.headers.hasKey("Expect"): - if "100-continue" in request.headers["Expect"]: - await client.sendStatus("100 Continue") - else: - await client.sendStatus("417 Expectation Failed") - - # Read the body - # - Check for Content-length header - if request.headers.hasKey("Content-Length"): - var contentLength = 0 - if parseInt(request.headers["Content-Length"], - contentLength) == 0: - await request.respond(Http400, "Bad Request. Invalid Content-Length.") - continue - else: - request.body = await client.recv(contentLength) - if request.body.len != contentLength: - await request.respond(Http400, "Bad Request. Content-Length does not match actual.") - continue - elif request.reqMethod == HttpPost: - await request.respond(Http411, "Content-Length required.") - continue - - # Call the user's callback. - await callback(request) - - if "upgrade" in request.headers.getOrDefault("connection"): + # Headers + while true: + i = 0 + lineFut.mget.setLen(0) + lineFut.clean() + await client.recvLineInto(lineFut, maxLength=maxLine) + + if lineFut.mget == "": + client.close(); return + if lineFut.mget.len > maxLine: + await request.respondError(Http413) + client.close(); return + if lineFut.mget == "\c\L": break + let (key, value) = parseHeader(lineFut.mget) + request.headers[key] = value + # Ensure the client isn't trying to DoS us. + if request.headers.len > headerLimit: + await client.sendStatus("400 Bad Request") + request.client.close() return - # Persistent connections - if (request.protocol == HttpVer11 and - request.headers.getOrDefault("connection").normalize != "close") or - (request.protocol == HttpVer10 and - request.headers.getOrDefault("connection").normalize == "keep-alive"): - # In HTTP 1.1 we assume that connection is persistent. Unless connection - # header states otherwise. - # In HTTP 1.0 we assume that the connection should not be persistent. - # Unless the connection header states otherwise. - discard + if request.reqMethod == HttpPost: + # Check for Expect header + if request.headers.hasKey("Expect"): + if "100-continue" in request.headers["Expect"]: + await client.sendStatus("100 Continue") + else: + await client.sendStatus("417 Expectation Failed") + + # Read the body + # - Check for Content-length header + if request.headers.hasKey("Content-Length"): + var contentLength = 0 + if parseSaturatedNatural(request.headers["Content-Length"], contentLength) == 0: + await request.respond(Http400, "Bad Request. Invalid Content-Length.") + return else: - request.client.close() - break + if contentLength > server.maxBody: + await request.respondError(Http413) + return + request.body = await client.recv(contentLength) + if request.body.len != contentLength: + await request.respond(Http400, "Bad Request. Content-Length does not match actual.") + return + elif request.reqMethod == HttpPost: + await request.respond(Http411, "Content-Length required.") + return + + # Call the user's callback. + await callback(request) + + if "upgrade" in request.headers.getOrDefault("connection"): + return + + # Persistent connections + if (request.protocol == HttpVer11 and + cmpIgnoreCase(request.headers.getOrDefault("connection"), "close") != 0) or + (request.protocol == HttpVer10 and + cmpIgnoreCase(request.headers.getOrDefault("connection"), "keep-alive") == 0): + # In HTTP 1.1 we assume that connection is persistent. Unless connection + # header states otherwise. + # In HTTP 1.0 we assume that the connection should not be persistent. + # Unless the connection header states otherwise. + discard + else: + request.client.close() + return + +proc processClient(server: AsyncHttpServer, client: AsyncSocket, address: string, + callback: proc (request: Request): + Future[void] {.closure, gcsafe.}) {.async.} = + var request = newFutureVar[Request]("asynchttpserver.processClient") + request.mget().url = initUri() + request.mget().headers = newHttpHeaders() + var lineFut = newFutureVar[string]("asynchttpserver.processClient") + lineFut.mget() = newStringOfCap(80) + + while not client.isClosed: + try: + await processRequest(server, request, client, address, lineFut, callback) + except: + asyncCheck request.mget().respondError(Http500) proc serve*(server: AsyncHttpServer, port: Port, callback: proc (request: Request): Future[void] {.closure,gcsafe.}, @@ -280,7 +299,7 @@ proc serve*(server: AsyncHttpServer, port: Port, # TODO: Causes compiler crash. #var (address, client) = await server.socket.acceptAddr() var fut = await server.socket.acceptAddr() - asyncCheck processClient(fut.client, fut.address, callback) + asyncCheck processClient(server, fut.client, fut.address, callback) #echo(f.isNil) #echo(f.repr) diff --git a/lib/pure/asyncmacro.nim b/lib/pure/asyncmacro.nim index 981190211..a8e378d5c 100644 --- a/lib/pure/asyncmacro.nim +++ b/lib/pure/asyncmacro.nim @@ -472,27 +472,13 @@ proc stripAwait(node: NimNode): NimNode = proc splitParamType(paramType: NimNode, async: bool): NimNode = result = paramType if paramType.kind == nnkInfix and $paramType[0].ident in ["|", "or"]: - let firstType = paramType[1] - let firstTypeName = $firstType.ident - let secondType = paramType[2] - let secondTypeName = $secondType.ident - - # Make sure that at least one has the name `async`, otherwise we shouldn't - # touch it. - if not ("async" in firstTypeName.normalize or - "async" in secondTypeName.normalize): - return - - if async: - if firstTypeName.normalize.startsWith("async"): - result = paramType[1] - elif secondTypeName.normalize.startsWith("async"): - result = paramType[2] - else: - if not firstTypeName.normalize.startsWith("async"): - result = paramType[1] - elif not secondTypeName.normalize.startsWith("async"): - result = paramType[2] + let firstAsync = "async" in ($paramType[1].ident).normalize + let secondAsync = "async" in ($paramType[2].ident).normalize + + if firstAsync: + result = paramType[if async: 1 else: 2] + elif secondAsync: + result = paramType[if async: 2 else: 1] proc stripReturnType(returnType: NimNode): NimNode = # Strip out the 'Future' from 'Future[T]'. @@ -535,4 +521,4 @@ macro multisync*(prc: untyped): untyped = let (sync, asyncPrc) = splitProc(prc) result = newStmtList() result.add(asyncSingleProc(asyncPrc)) - result.add(sync) \ No newline at end of file + result.add(sync) diff --git a/lib/pure/browsers.nim b/lib/pure/browsers.nim index c6a603318..6912b893c 100644 --- a/lib/pure/browsers.nim +++ b/lib/pure/browsers.nim @@ -21,24 +21,18 @@ proc openDefaultBrowser*(url: string) = ## opens `url` with the user's default browser. This does not block. ## ## Under Windows, ``ShellExecute`` is used. Under Mac OS X the ``open`` - ## command is used. Under Unix, it is checked if ``gnome-open`` exists and - ## used if it does. Next attempt is ``kde-open``, then ``xdg-open``. - ## Otherwise the environment variable ``BROWSER`` is used to determine the - ## default browser to use. + ## command is used. Under Unix, it is checked if ``xdg-open`` exists and + ## used if it does. Otherwise the environment variable ``BROWSER`` is + ## used to determine the default browser to use. when defined(windows): - when useWinUnicode: - var o = newWideCString("open") - var u = newWideCString(url) - discard shellExecuteW(0'i32, o, u, nil, nil, SW_SHOWNORMAL) - else: - discard shellExecuteA(0'i32, "open", url, nil, nil, SW_SHOWNORMAL) + var o = newWideCString("open") + var u = newWideCString(url) + discard shellExecuteW(0'i32, o, u, nil, nil, SW_SHOWNORMAL) elif defined(macosx): discard execShellCmd("open " & quoteShell(url)) else: - const attempts = ["gnome-open ", "kde-open ", "xdg-open "] var u = quoteShell(url) - for a in items(attempts): - if execShellCmd(a & u) == 0: return + if execShellCmd("xdg-open " & u) == 0: return for b in getEnv("BROWSER").string.split(PathSep): try: # we use ``startProcess`` here because we don't want to block! diff --git a/lib/pure/collections/sequtils.nim b/lib/pure/collections/sequtils.nim index 0eb8e6704..3f8a9574d 100644 --- a/lib/pure/collections/sequtils.nim +++ b/lib/pure/collections/sequtils.nim @@ -20,6 +20,8 @@ include "system/inclrtl" +import macros + when not defined(nimhygiene): {.pragma: dirty.} @@ -704,6 +706,52 @@ template newSeqWith*(len: int, init: untyped): untyped = result[i] = init result +proc mapLitsImpl(constructor: NimNode; op: NimNode; nested: bool; + filter = nnkLiterals): NimNode = + if constructor.kind in filter: + result = newNimNode(nnkCall, lineInfoFrom=constructor) + result.add op + result.add constructor + else: + result = newNimNode(constructor.kind, lineInfoFrom=constructor) + for v in constructor: + if nested or v.kind in filter: + result.add mapLitsImpl(v, op, nested, filter) + else: + result.add v + +macro mapLiterals*(constructor, op: untyped; + nested = true): untyped = + ## applies ``op`` to each of the **atomic** literals like ``3`` + ## or ``"abc"`` in the specified ``constructor`` AST. This can + ## be used to map every array element to some target type: + ## + ## Example: + ## + ## .. code-block:: + ## let x = mapLiterals([0.1, 1.2, 2.3, 3.4], int) + ## doAssert x is array[4, int] + ## + ## Short notation for: + ## + ## .. code-block:: + ## let x = [int(0.1), int(1.2), int(2.3), int(3.4)] + ## + ## If ``nested`` is true, the literals are replaced everywhere + ## in the ``constructor`` AST, otherwise only the first level + ## is considered: + ## + ## .. code-block:: + ## mapLiterals((1, ("abc"), 2), float, nested=false) + ## + ## Produces:: + ## + ## (float(1), ("abc"), float(2)) + ## + ## There are no constraints for the ``constructor`` AST, it + ## works for nested tuples of arrays of sets etc. + result = mapLitsImpl(constructor, op, nested.boolVal) + when isMainModule: import strutils block: # concat test @@ -992,5 +1040,11 @@ when isMainModule: seq2D[0][1] = true doAssert seq2D == @[@[true, true], @[true, false], @[false, false], @[false, false]] + block: # mapLiterals tests + let x = mapLiterals([0.1, 1.2, 2.3, 3.4], int) + doAssert x is array[4, int] + doAssert mapLiterals((1, ("abc"), 2), float, nested=false) == (float(1), "abc", float(2)) + doAssert mapLiterals(([1], ("abc"), 2), `$`, nested=true) == (["1"], "abc", "2") + when not defined(testing): echo "Finished doc tests" diff --git a/lib/pure/collections/sharedstrings.nim b/lib/pure/collections/sharedstrings.nim index 83edf8d94..7e9de4b73 100644 --- a/lib/pure/collections/sharedstrings.nim +++ b/lib/pure/collections/sharedstrings.nim @@ -87,10 +87,10 @@ proc newSharedString*(s: string): SharedString = result.len = len when declared(atomicLoadN): - template load(x): expr = atomicLoadN(addr x, ATOMIC_SEQ_CST) + template load(x): untyped = atomicLoadN(addr x, ATOMIC_SEQ_CST) else: # XXX Fixme - template load(x): expr = x + template load(x): untyped = x proc add*(s: var SharedString; t: cstring; len: Natural) = if len == 0: return diff --git a/lib/pure/cookies.nim b/lib/pure/cookies.nim index 7d850798c..07b37c7d4 100644 --- a/lib/pure/cookies.nim +++ b/lib/pure/cookies.nim @@ -13,6 +13,15 @@ import strtabs, times proc parseCookies*(s: string): StringTableRef = ## parses cookies into a string table. + ## + ## The proc is meant to parse the Cookie header set by a client, not the + ## "Set-Cookie" header set by servers. + ## + ## Example: + ## + ## .. code-block::Nim + ## doAssert parseCookies("a=1; foo=bar") == {"a": 1, "foo": "bar"}.newStringTable + result = newStringTable(modeCaseInsensitive) var i = 0 while true: diff --git a/lib/pure/future.nim b/lib/pure/future.nim index f6592df71..1a3ab688d 100644 --- a/lib/pure/future.nim +++ b/lib/pure/future.nim @@ -197,4 +197,4 @@ macro dump*(x: typed): untyped = let s = x.toStrLit let r = quote do: debugEcho `s`, " = ", `x` - return r \ No newline at end of file + return r diff --git a/lib/pure/gentabs.nim b/lib/pure/gentabs.nim deleted file mode 100644 index 928ff8fe0..000000000 --- a/lib/pure/gentabs.nim +++ /dev/null @@ -1,211 +0,0 @@ -# -# -# Nim's Runtime Library -# (c) Copyright 2012 Andreas Rumpf -# -# See the file "copying.txt", included in this -# distribution, for details about the copyright. -# - -## The ``gentabs`` module implements an efficient hash table that is a -## key-value mapping. The keys are required to be strings, but the values -## may be any Nim or user defined type. This module supports matching -## of keys in case-sensitive, case-insensitive and style-insensitive modes. -## -## **Warning:** This module is deprecated, new code shouldn't use it! - -{.deprecated.} - -import - os, hashes, strutils - -type - GenTableMode* = enum ## describes the table's key matching mode - modeCaseSensitive, ## case sensitive matching of keys - modeCaseInsensitive, ## case insensitive matching of keys - modeStyleInsensitive ## style sensitive matching of keys - - GenKeyValuePair[T] = tuple[key: string, val: T] - GenKeyValuePairSeq[T] = seq[GenKeyValuePair[T]] - GenTable*[T] = object of RootObj - counter: int - data: GenKeyValuePairSeq[T] - mode: GenTableMode - - PGenTable*[T] = ref GenTable[T] ## use this type to declare hash tables - -{.deprecated: [TGenTableMode: GenTableMode, TGenKeyValuePair: GenKeyValuePair, - TGenKeyValuePairSeq: GenKeyValuePairSeq, TGenTable: GenTable].} - -const - growthFactor = 2 - startSize = 64 - - -proc len*[T](tbl: PGenTable[T]): int {.inline.} = - ## returns the number of keys in `tbl`. - result = tbl.counter - -iterator pairs*[T](tbl: PGenTable[T]): tuple[key: string, value: T] = - ## iterates over any (key, value) pair in the table `tbl`. - for h in 0..high(tbl.data): - if not isNil(tbl.data[h].key): - yield (tbl.data[h].key, tbl.data[h].val) - -proc myhash[T](tbl: PGenTable[T], key: string): Hash = - case tbl.mode - of modeCaseSensitive: result = hashes.hash(key) - of modeCaseInsensitive: result = hashes.hashIgnoreCase(key) - of modeStyleInsensitive: result = hashes.hashIgnoreStyle(key) - -proc myCmp[T](tbl: PGenTable[T], a, b: string): bool = - case tbl.mode - of modeCaseSensitive: result = cmp(a, b) == 0 - of modeCaseInsensitive: result = cmpIgnoreCase(a, b) == 0 - of modeStyleInsensitive: result = cmpIgnoreStyle(a, b) == 0 - -proc mustRehash(length, counter: int): bool = - assert(length > counter) - result = (length * 2 < counter * 3) or (length - counter < 4) - -proc newGenTable*[T](mode: GenTableMode): PGenTable[T] = - ## creates a new generic hash table that is empty. - new(result) - result.mode = mode - result.counter = 0 - newSeq(result.data, startSize) - -proc nextTry(h, maxHash: Hash): Hash {.inline.} = - result = ((5 * h) + 1) and maxHash - -proc rawGet[T](tbl: PGenTable[T], key: string): int = - var h: Hash - h = myhash(tbl, key) and high(tbl.data) # start with real hash value - while not isNil(tbl.data[h].key): - if myCmp(tbl, tbl.data[h].key, key): - return h - h = nextTry(h, high(tbl.data)) - result = - 1 - -proc rawInsert[T](tbl: PGenTable[T], data: var GenKeyValuePairSeq[T], - key: string, val: T) = - var h: Hash - h = myhash(tbl, key) and high(data) - while not isNil(data[h].key): - h = nextTry(h, high(data)) - data[h].key = key - data[h].val = val - -proc enlarge[T](tbl: PGenTable[T]) = - var n: GenKeyValuePairSeq[T] - newSeq(n, len(tbl.data) * growthFactor) - for i in countup(0, high(tbl.data)): - if not isNil(tbl.data[i].key): - rawInsert[T](tbl, n, tbl.data[i].key, tbl.data[i].val) - swap(tbl.data, n) - -proc hasKey*[T](tbl: PGenTable[T], key: string): bool = - ## returns true iff `key` is in the table `tbl`. - result = rawGet(tbl, key) >= 0 - -proc `[]`*[T](tbl: PGenTable[T], key: string): T = - ## retrieves the value at ``tbl[key]``. If `key` is not in `tbl`, - ## default(T) is returned and no exception is raised. One can check - ## with ``hasKey`` whether the key exists. - var index = rawGet(tbl, key) - if index >= 0: result = tbl.data[index].val - -proc `[]=`*[T](tbl: PGenTable[T], key: string, val: T) = - ## puts a (key, value)-pair into `tbl`. - var index = rawGet(tbl, key) - if index >= 0: - tbl.data[index].val = val - else: - if mustRehash(len(tbl.data), tbl.counter): enlarge(tbl) - rawInsert(tbl, tbl.data, key, val) - inc(tbl.counter) - - -when isMainModule: - # - # Verify tables of integer values (string keys) - # - var x = newGenTable[int](modeCaseInsensitive) - x["one"] = 1 - x["two"] = 2 - x["three"] = 3 - x["four"] = 4 - x["five"] = 5 - assert(len(x) == 5) # length procedure works - assert(x["one"] == 1) # case-sensitive lookup works - assert(x["ONE"] == 1) # case-insensitive should work for this table - assert(x["one"]+x["two"] == 3) # make sure we're getting back ints - assert(x.hasKey("one")) # hasKey should return 'true' for a key - # of "one"... - assert(not x.hasKey("NOPE")) # ...but key "NOPE" is not in the table. - for k,v in pairs(x): # make sure the 'pairs' iterator works - assert(x[k]==v) - - # - # Verify a table of user-defined types - # - type - MyType = tuple[first, second: string] # a pair of strings - {.deprecated: [TMyType: MyType].} - - var y = newGenTable[MyType](modeCaseInsensitive) # hash table where each - # value is MyType tuple - - #var junk: MyType = ("OK", "Here") - - #echo junk.first, " ", junk.second - - y["Hello"] = ("Hello", "World") - y["Goodbye"] = ("Goodbye", "Everyone") - #y["Hello"] = MyType( ("Hello", "World") ) - #y["Goodbye"] = MyType( ("Goodbye", "Everyone") ) - - assert( not isNil(y["Hello"].first) ) - assert( y["Hello"].first == "Hello" ) - assert( y["Hello"].second == "World" ) - - # - # Verify table of tables - # - var z: PGenTable[ PGenTable[int] ] # hash table where each value is - # a hash table of ints - - z = newGenTable[PGenTable[int]](modeCaseInsensitive) - z["first"] = newGenTable[int](modeCaseInsensitive) - z["first"]["one"] = 1 - z["first"]["two"] = 2 - z["first"]["three"] = 3 - - z["second"] = newGenTable[int](modeCaseInsensitive) - z["second"]["red"] = 10 - z["second"]["blue"] = 20 - - assert(len(z) == 2) # length of outer table - assert(len(z["first"]) == 3) # length of "first" table - assert(len(z["second"]) == 2) # length of "second" table - assert( z["first"]["one"] == 1) # retrieve from first inner table - assert( z["second"]["red"] == 10) # retrieve from second inner table - - when false: - # disabled: depends on hash order: - var output = "" - for k, v in pairs(z): - output.add( "$# ($#) ->\L" % [k,$len(v)] ) - for k2,v2 in pairs(v): - output.add( " $# <-> $#\L" % [k2,$v2] ) - - let expected = unindent """ - first (3) -> - two <-> 2 - three <-> 3 - one <-> 1 - second (2) -> - red <-> 10 - blue <-> 20 - """ - assert output == expected diff --git a/lib/pure/ioselectors.nim b/lib/pure/ioselectors.nim index ef8072221..1722da1c6 100644 --- a/lib/pure/ioselectors.nim +++ b/lib/pure/ioselectors.nim @@ -43,6 +43,7 @@ const bsdPlatform = defined(macosx) or defined(freebsd) or defined(dragonfly) when defined(nimdoc): + type SocketHandle = int type Selector*[T] = ref object ## An object which holds descriptors to be checked for read/write status diff --git a/lib/pure/json.nim b/lib/pure/json.nim index 3d86cc9d7..cea485c43 100644 --- a/lib/pure/json.nim +++ b/lib/pure/json.nim @@ -1849,8 +1849,8 @@ when isMainModule: discard parseJson"""{ invalid""" except: discard - # memory diff should less than 2M - doAssert(abs(getOccupiedMem() - startMemory) < 2 * 1024 * 1024) + # memory diff should less than 4M + doAssert(abs(getOccupiedMem() - startMemory) < 4 * 1024 * 1024) # test `$` diff --git a/lib/pure/numeric.nim b/lib/pure/numeric.nim deleted file mode 100644 index ccda3a146..000000000 --- a/lib/pure/numeric.nim +++ /dev/null @@ -1,87 +0,0 @@ -# -# -# Nim's Runtime Library -# (c) Copyright 2013 Robert Persson -# -# See the file "copying.txt", included in this -# distribution, for details about the copyright. -# - -## **Warning:** This module will be moved out of the stdlib and into a -## Nimble package, don't use it. - -type OneVarFunction* = proc (x: float): float - -{.deprecated: [TOneVarFunction: OneVarFunction].} - -proc brent*(xmin,xmax:float, function:OneVarFunction, tol:float,maxiter=1000): - tuple[rootx, rooty: float, success: bool]= - ## Searches `function` for a root between `xmin` and `xmax` - ## using brents method. If the function value at `xmin`and `xmax` has the - ## same sign, `rootx`/`rooty` is set too the extrema value closest to x-axis - ## and succes is set to false. - ## Otherwise there exists at least one root and success is set to true. - ## This root is searched for at most `maxiter` iterations. - ## If `tol` tolerance is reached within `maxiter` iterations - ## the root refinement stops and success=true. - - # see http://en.wikipedia.org/wiki/Brent%27s_method - var - a=xmin - b=xmax - c=a - d=1.0e308 - fa=function(a) - fb=function(b) - fc=fa - s=0.0 - fs=0.0 - mflag:bool - i=0 - tmp2:float - - if fa*fb>=0: - if abs(fa)<abs(fb): - return (a,fa,false) - else: - return (b,fb,false) - - if abs(fa)<abs(fb): - swap(fa,fb) - swap(a,b) - - while fb!=0.0 and abs(a-b)>tol: - if fa!=fc and fb!=fc: # inverse quadratic interpolation - s = a * fb * fc / (fa - fb) / (fa - fc) + b * fa * fc / (fb - fa) / (fb - fc) + c * fa * fb / (fc - fa) / (fc - fb) - else: #secant rule - s = b - fb * (b - a) / (fb - fa) - tmp2 = (3.0 * a + b) / 4.0 - if not((s > tmp2 and s < b) or (s < tmp2 and s > b)) or - (mflag and abs(s - b) >= (abs(b - c) / 2.0)) or - (not mflag and abs(s - b) >= abs(c - d) / 2.0): - s=(a+b)/2.0 - mflag=true - else: - if (mflag and (abs(b - c) < tol)) or (not mflag and (abs(c - d) < tol)): - s=(a+b)/2.0 - mflag=true - else: - mflag=false - fs = function(s) - d = c - c = b - fc = fb - if fa * fs<0.0: - b=s - fb=fs - else: - a=s - fa=fs - if abs(fa)<abs(fb): - swap(a,b) - swap(fa,fb) - inc i - if i>maxiter: - break - - return (b,fb,true) diff --git a/lib/pure/os.nim b/lib/pure/os.nim index a1ae4e250..8b26552de 100644 --- a/lib/pure/os.nim +++ b/lib/pure/os.nim @@ -1195,14 +1195,15 @@ when defined(nimdoc): ## Returns the number of `command line arguments`:idx: given to the ## application. ## - ## If your binary was called without parameters this will return zero. You - ## can later query each individual paramater with `paramStr() <#paramStr>`_ + ## Unlike `argc`:idx: in C, if your binary was called without parameters this + ## will return zero. + ## You can query each individual paramater with `paramStr() <#paramStr>`_ ## or retrieve all of them in one go with `commandLineParams() ## <#commandLineParams>`_. ## - ## **Availability**: On Posix there is no portable way to get the command - ## line from a DLL and thus the proc isn't defined in this environment. You - ## can test for its availability with `declared() <system.html#declared>`_. + ## **Availability**: When generating a dynamic library (see --app:lib) on + ## Posix this proc is not defined. + ## Test for availability using `declared() <system.html#declared>`_. ## Example: ## ## .. code-block:: nim @@ -1219,13 +1220,14 @@ when defined(nimdoc): ## `paramCount() <#paramCount>`_ with this proc you can call the ## convenience `commandLineParams() <#commandLineParams>`_. ## - ## It is possible to call ``paramStr(0)`` but this will return OS specific + ## Similarly to `argv`:idx: in C, + ## it is possible to call ``paramStr(0)`` but this will return OS specific ## contents (usually the name of the invoked executable). You should avoid ## this and call `getAppFilename() <#getAppFilename>`_ instead. ## - ## **Availability**: On Posix there is no portable way to get the command - ## line from a DLL and thus the proc isn't defined in this environment. You - ## can test for its availability with `declared() <system.html#declared>`_. + ## **Availability**: When generating a dynamic library (see --app:lib) on + ## Posix this proc is not defined. + ## Test for availability using `declared() <system.html#declared>`_. ## Example: ## ## .. code-block:: nim diff --git a/lib/pure/osproc.nim b/lib/pure/osproc.nim index d768a7de9..dc25ea4c3 100644 --- a/lib/pure/osproc.nim +++ b/lib/pure/osproc.nim @@ -841,7 +841,7 @@ elif not defined(useNimRtl): var attr: Tposix_spawnattr var fops: Tposix_spawn_file_actions - template chck(e: expr) = + template chck(e: untyped) = if e != 0'i32: raiseOSError(osLastError()) chck posix_spawn_file_actions_init(fops) diff --git a/lib/pure/parseutils.nim b/lib/pure/parseutils.nim index 3c790512f..a602b0e1b 100644 --- a/lib/pure/parseutils.nim +++ b/lib/pure/parseutils.nim @@ -252,6 +252,31 @@ proc parseInt*(s: string, number: var int, start = 0): int {. elif result != 0: number = int(res) +proc parseSaturatedNatural*(s: string, b: var int, start = 0): int = + ## parses a natural number into ``b``. This cannot raise an overflow + ## error. Instead of an ``Overflow`` exception ``high(int)`` is returned. + ## The number of processed character is returned. + ## This is usually what you really want to use instead of `parseInt`:idx:. + ## Example: + ## + ## .. code-block:: nim + ## var res = 0 + ## discard parseSaturatedNatural("848", res) + ## doAssert res == 848 + var i = start + if s[i] == '+': inc(i) + if s[i] in {'0'..'9'}: + b = 0 + while s[i] in {'0'..'9'}: + let c = ord(s[i]) - ord('0') + if b <= (high(int) - c) div 10: + b = b * 10 + c + else: + b = high(int) + inc(i) + while s[i] == '_': inc(i) # underscores are allowed and ignored + result = i - start + # overflowChecks doesn't work with BiggestUInt proc rawParseUInt(s: string, b: var BiggestUInt, start = 0): int = var @@ -393,16 +418,43 @@ when isMainModule: let input = "$test{} $this is ${an{ example}} " let expected = @[(ikVar, "test"), (ikStr, "{} "), (ikVar, "this"), (ikStr, " is "), (ikExpr, "an{ example}"), (ikStr, " ")] - assert toSeq(interpolatedFragments(input)) == expected + doAssert toSeq(interpolatedFragments(input)) == expected var value = 0 discard parseHex("0x38", value) - assert value == 56 + doAssert value == 56 discard parseHex("0x34", value) - assert value == 56 * 256 + 52 + doAssert value == 56 * 256 + 52 value = -1 discard parseHex("0x38", value) - assert value == -200 + doAssert value == -200 + value = -1 + doAssert(parseSaturatedNatural("848", value) == 3) + doAssert value == 848 + + value = -1 + discard parseSaturatedNatural("84899999999999999999324234243143142342135435342532453", value) + doAssert value == high(int) + + value = -1 + discard parseSaturatedNatural("9223372036854775808", value) + doAssert value == high(int) + + value = -1 + discard parseSaturatedNatural("9223372036854775807", value) + doAssert value == high(int) + + value = -1 + discard parseSaturatedNatural("18446744073709551616", value) + doAssert value == high(int) + + value = -1 + discard parseSaturatedNatural("18446744073709551615", value) + doAssert value == high(int) + + value = -1 + doAssert(parseSaturatedNatural("1_000_000", value) == 9) + doAssert value == 1_000_000 {.pop.} diff --git a/lib/pure/poly.nim b/lib/pure/poly.nim deleted file mode 100644 index e286c5d17..000000000 --- a/lib/pure/poly.nim +++ /dev/null @@ -1,371 +0,0 @@ -# -# -# Nim's Runtime Library -# (c) Copyright 2013 Robert Persson -# -# See the file "copying.txt", included in this -# distribution, for details about the copyright. -# - -## **Warning:** This module will be moved out of the stdlib and into a -## Nimble package, don't use it. - -import math -import strutils -import numeric - -type - Poly* = object - cofs:seq[float] - -{.deprecated: [TPoly: Poly].} - -proc degree*(p:Poly):int= - ## Returns the degree of the polynomial, - ## that is the number of coefficients-1 - return p.cofs.len-1 - - -proc eval*(p:Poly,x:float):float= - ## Evaluates a polynomial function value for `x` - ## quickly using Horners method - var n=p.degree - result=p.cofs[n] - dec n - while n>=0: - result = result*x+p.cofs[n] - dec n - -proc `[]` *(p:Poly;idx:int):float= - ## Gets a coefficient of the polynomial. - ## p[2] will returns the quadric term, p[3] the cubic etc. - ## Out of bounds index will return 0.0. - if idx<0 or idx>p.degree: - return 0.0 - return p.cofs[idx] - -proc `[]=` *(p:var Poly;idx:int,v:float)= - ## Sets an coefficient of the polynomial by index. - ## p[2] set the quadric term, p[3] the cubic etc. - ## If index is out of range for the coefficients, - ## the polynomial grows to the smallest needed degree. - assert(idx>=0) - - if idx>p.degree: #polynomial must grow - var oldlen=p.cofs.len - p.cofs.setLen(idx+1) - for q in oldlen.. <high(p.cofs): - p.cofs[q]=0.0 #new-grown coefficients set to zero - - p.cofs[idx]=v - - -iterator items*(p:Poly):float= - ## Iterates through the coefficients of the polynomial. - var i=p.degree - while i>=0: - yield p[i] - dec i - -proc clean*(p:var Poly;zerotol=0.0)= - ## Removes leading zero coefficients of the polynomial. - ## An optional tolerance can be given for what's considered zero. - var n=p.degree - var relen=false - - while n>0 and abs(p[n])<=zerotol: # >0 => keep at least one coefficient - dec n - relen=true - - if relen: p.cofs.setLen(n+1) - - -proc `$` *(p:Poly):string = - ## Gets a somewhat reasonable string representation of the polynomial - ## The format should be compatible with most online function plotters, - ## for example directly in google search - result="" - var first=true #might skip + sign if first coefficient - - for idx in countdown(p.degree,0): - let a=p[idx] - - if a==0.0: - continue - - if a>= 0.0 and not first: - result.add('+') - first=false - - if a!=1.0 or idx==0: - result.add(formatFloat(a,ffDefault,0)) - if idx>=2: - result.add("x^" & $idx) - elif idx==1: - result.add("x") - - if result=="": - result="0" - - -proc derivative*(p: Poly): Poly= - ## Returns a new polynomial, which is the derivative of `p` - newSeq[float](result.cofs,p.degree) - for idx in 0..high(result.cofs): - result.cofs[idx]=p.cofs[idx+1]*float(idx+1) - -proc diff*(p:Poly,x:float):float= - ## Evaluates the differentiation of a polynomial with - ## respect to `x` quickly using a modifed Horners method - var n=p.degree - result=p[n]*float(n) - dec n - while n>=1: - result = result*x+p[n]*float(n) - dec n - -proc integral*(p:Poly):Poly= - ## Returns a new polynomial which is the indefinite - ## integral of `p`. The constant term is set to 0.0 - newSeq(result.cofs,p.cofs.len+1) - result.cofs[0]=0.0 #constant arbitrary term, use 0.0 - for i in 1..high(result.cofs): - result.cofs[i]=p.cofs[i-1]/float(i) - - -proc integrate*(p:Poly;xmin,xmax:float):float= - ## Computes the definite integral of `p` between `xmin` and `xmax` - ## quickly using a modified version of Horners method - var - n=p.degree - s1=p[n]/float(n+1) - s2=s1 - fac:float - - dec n - while n>=0: - fac=p[n]/float(n+1) - s1 = s1*xmin+fac - s2 = s2*xmax+fac - dec n - - result=s2*xmax-s1*xmin - -proc initPoly*(cofs:varargs[float]):Poly= - ## Initializes a polynomial with given coefficients. - ## The most significant coefficient is first, so to create x^2-2x+3: - ## intiPoly(1.0,-2.0,3.0) - if len(cofs)<=0: - result.cofs= @[0.0] #need at least one coefficient - else: - # reverse order of coefficients so indexing matches degree of - # coefficient... - result.cofs= @[] - for idx in countdown(cofs.len-1,0): - result.cofs.add(cofs[idx]) - - result.clean #remove leading zero terms - - -proc divMod*(p,d:Poly;q,r:var Poly)= - ## Divides `p` with `d`, and stores the quotinent in `q` and - ## the remainder in `d` - var - pdeg=p.degree - ddeg=d.degree - power=p.degree-d.degree - ratio:float - - r.cofs = p.cofs #initial remainder=numerator - if power<0: #denominator is larger than numerator - q.cofs= @ [0.0] #quotinent is 0.0 - return # keep remainder as numerator - - q.cofs=newSeq[float](power+1) - - for i in countdown(pdeg,ddeg): - ratio=r.cofs[i]/d.cofs[ddeg] - - q.cofs[i-ddeg]=ratio - r.cofs[i]=0.0 - - for j in countup(0,<ddeg): - var idx=i-ddeg+j - r.cofs[idx] = r.cofs[idx] - d.cofs[j]*ratio - - r.clean # drop zero coefficients in remainder - -proc `+` *(p1:Poly,p2:Poly):Poly= - ## Adds two polynomials - var n=max(p1.cofs.len,p2.cofs.len) - newSeq(result.cofs,n) - - for idx in countup(0,n-1): - result[idx]=p1[idx]+p2[idx] - - result.clean # drop zero coefficients in remainder - -proc `*` *(p1:Poly,p2:Poly):Poly= - ## Multiplies the polynomial `p1` with `p2` - var - d1=p1.degree - d2=p2.degree - n=d1+d2 - idx:int - - newSeq(result.cofs,n) - - for i1 in countup(0,d1): - for i2 in countup(0,d2): - idx=i1+i2 - result[idx]=result[idx]+p1[i1]*p2[i2] - - result.clean - -proc `*` *(p:Poly,f:float):Poly= - ## Multiplies the polynomial `p` with a real number - newSeq(result.cofs,p.cofs.len) - for i in 0..high(p.cofs): - result[i]=p.cofs[i]*f - result.clean - -proc `*` *(f:float,p:Poly):Poly= - ## Multiplies a real number with a polynomial - return p*f - -proc `-`*(p:Poly):Poly= - ## Negates a polynomial - result=p - for i in countup(0,<result.cofs.len): - result.cofs[i]= -result.cofs[i] - -proc `-` *(p1:Poly,p2:Poly):Poly= - ## Subtract `p1` with `p2` - var n=max(p1.cofs.len,p2.cofs.len) - newSeq(result.cofs,n) - - for idx in countup(0,n-1): - result[idx]=p1[idx]-p2[idx] - - result.clean # drop zero coefficients in remainder - -proc `/`*(p:Poly,f:float):Poly= - ## Divides polynomial `p` with a real number `f` - newSeq(result.cofs,p.cofs.len) - for i in 0..high(p.cofs): - result[i]=p.cofs[i]/f - result.clean - -proc `/` *(p,q:Poly):Poly= - ## Divides polynomial `p` with polynomial `q` - var dummy:Poly - p.divMod(q,result,dummy) - -proc `mod` *(p,q:Poly):Poly= - ## Computes the polynomial modulo operation, - ## that is the remainder of `p`/`q` - var dummy:Poly - p.divMod(q,dummy,result) - - -proc normalize*(p:var Poly)= - ## Multiplies the polynomial inplace by a term so that - ## the leading term is 1.0. - ## This might lead to an unstable polynomial - ## if the leading term is zero. - p=p/p[p.degree] - - -proc solveQuadric*(a,b,c:float;zerotol=0.0):seq[float]= - ## Solves the quadric equation `ax^2+bx+c`, with a possible - ## tolerance `zerotol` to find roots of curves just 'touching' - ## the x axis. Returns sequence with 0,1 or 2 solutions. - - var p,q,d:float - - p=b/(2.0*a) - - if p==Inf or p==NegInf: #linear equation.. - var linrt= -c/b - if linrt==Inf or linrt==NegInf: #constant only - return @[] - return @[linrt] - - q=c/a - d=p*p-q - - if d<0.0: - #check for inside zerotol range for neg. roots - var err=a*p*p-b*p+c #evaluate error at parabola center axis - if(err<=zerotol): return @[-p] - return @[] - else: - var sr=sqrt(d) - result= @[-sr-p,sr-p] - -proc getRangeForRoots(p:Poly):tuple[xmin,xmax:float]= - ## helper function for `roots` function - ## quickly computes a range, guaranteed to contain - ## all the real roots of the polynomial - # see http://www.mathsisfun.com/algebra/polynomials-bounds-zeros.html - - var deg=p.degree - var d=p[deg] - var bound1,bound2:float - - for i in countup(0,deg): - var c=abs(p.cofs[i]/d) - bound1=max(bound1,c+1.0) - bound2=bound2+c - - bound2=max(1.0,bound2) - result.xmax=min(bound1,bound2) - result.xmin= -result.xmax - - -proc addRoot(p:Poly,res:var seq[float],xp0,xp1,tol,zerotol,mergetol:float,maxiter:int)= - ## helper function for `roots` function - ## try to do a numeric search for a single root in range xp0-xp1, - ## adding it to `res` (allocating `res` if nil) - var br=brent(xp0,xp1, proc(x:float):float=p.eval(x),tol) - if br.success: - if res.len==0 or br.rootx>=res[high(res)]+mergetol: #dont add equal roots. - res.add(br.rootx) - else: - #this might be a 'touching' case, check function value against - #zero tolerance - if abs(br.rooty)<=zerotol: - if res.len==0 or br.rootx>=res[high(res)]+mergetol: #dont add equal roots. - res.add(br.rootx) - - -proc roots*(p:Poly,tol=1.0e-9,zerotol=1.0e-6,mergetol=1.0e-12,maxiter=1000):seq[float]= - ## Computes the real roots of the polynomial `p` - ## `tol` is the tolerance used to break searching for each root when reached. - ## `zerotol` is the tolerance, which is 'close enough' to zero to be considered a root - ## and is used to find roots for curves that only 'touch' the x-axis. - ## `mergetol` is the tolerance, of which two x-values are considered being the same root. - ## `maxiter` can be used to limit the number of iterations for each root. - ## Returns a (possibly empty) sorted sequence with the solutions. - var deg=p.degree - if deg<=0: #constant only => no roots - return @[] - elif p.degree==1: #linear - var linrt= -p.cofs[0]/p.cofs[1] - if linrt==Inf or linrt==NegInf: - return @[] #constant only => no roots - return @[linrt] - elif p.degree==2: - return solveQuadric(p.cofs[2],p.cofs[1],p.cofs[0],zerotol) - else: - # degree >=3 , find min/max points of polynomial with recursive - # derivative and do a numerical search for root between each min/max - var range=p.getRangeForRoots() - var minmax=p.derivative.roots(tol,zerotol,mergetol) - result= @[] - if minmax!=nil: #ie. we have minimas/maximas in this function - for x in minmax.items: - addRoot(p,result,range.xmin,x,tol,zerotol,mergetol,maxiter) - range.xmin=x - addRoot(p,result,range.xmin,range.xmax,tol,zerotol,mergetol,maxiter) - diff --git a/lib/pure/romans.nim b/lib/pure/romans.nim deleted file mode 100644 index aa047d1cc..000000000 --- a/lib/pure/romans.nim +++ /dev/null @@ -1,59 +0,0 @@ -# -# -# Nim's Runtime Library -# (c) Copyright 2011 Philippe Lhoste -# -# See the file "copying.txt", included in this -# distribution, for details about the copyright. -# - -## Module for converting an integer to a Roman numeral. -## See http://en.wikipedia.org/wiki/Roman_numerals for reference. -## -## **Warning:** This module will be moved out of the stdlib and into a -## Nimble package, don't use it. - -const - RomanNumeralDigits* = {'I', 'i', 'V', 'v', 'X', 'x', 'L', 'l', 'C', 'c', - 'D', 'd', 'M', 'm'} ## set of all characters a Roman numeral may consist of - -proc romanToDecimal*(romanVal: string): int = - ## Converts a Roman numeral to its int representation. - result = 0 - var prevVal = 0 - for i in countdown(romanVal.len - 1, 0): - var val = 0 - case romanVal[i] - of 'I', 'i': val = 1 - of 'V', 'v': val = 5 - of 'X', 'x': val = 10 - of 'L', 'l': val = 50 - of 'C', 'c': val = 100 - of 'D', 'd': val = 500 - of 'M', 'm': val = 1000 - else: - raise newException(EInvalidValue, "invalid roman numeral: " & $romanVal) - if val >= prevVal: - inc(result, val) - else: - dec(result, val) - prevVal = val - -proc decimalToRoman*(number: range[1..3_999]): string = - ## Converts a number to a Roman numeral. - const romanComposites = [ - ("M", 1000), ("CM", 900), - ("D", 500), ("CD", 400), ("C", 100), - ("XC", 90), ("L", 50), ("XL", 40), ("X", 10), ("IX", 9), - ("V", 5), ("IV", 4), ("I", 1)] - result = "" - var decVal: int = number - for key, val in items(romanComposites): - while decVal >= val: - dec(decVal, val) - result.add(key) - -when isMainModule: - for i in 1 .. 3_999: - assert i == i.decimalToRoman.romanToDecimal - diff --git a/lib/pure/strutils.nim b/lib/pure/strutils.nim index 5c07757cb..d773cc7d8 100644 --- a/lib/pure/strutils.nim +++ b/lib/pure/strutils.nim @@ -1927,7 +1927,7 @@ type {.deprecated: [TFloatFormat: FloatFormatMode].} proc formatBiggestFloat*(f: BiggestFloat, format: FloatFormatMode = ffDefault, - precision: range[0..32] = 16; + precision: range[-1..32] = 16; decimalSep = '.'): string {. noSideEffect, rtl, extern: "nsu$1".} = ## Converts a floating point value `f` to a string. @@ -1939,7 +1939,7 @@ proc formatBiggestFloat*(f: BiggestFloat, format: FloatFormatMode = ffDefault, ## `precision`'s default value is the maximum number of meaningful digits ## after the decimal point for Nim's ``biggestFloat`` type. ## - ## If ``precision == 0``, it tries to format it nicely. + ## If ``precision == -1``, it tries to format it nicely. when defined(js): var res: cstring case format @@ -1961,7 +1961,7 @@ proc formatBiggestFloat*(f: BiggestFloat, format: FloatFormatMode = ffDefault, buf {.noinit.}: array[0..2500, char] L: cint frmtstr[0] = '%' - if precision > 0: + if precision >= 0: frmtstr[1] = '#' frmtstr[2] = '.' frmtstr[3] = '*' @@ -1984,7 +1984,7 @@ proc formatBiggestFloat*(f: BiggestFloat, format: FloatFormatMode = ffDefault, # but nothing else is possible: if buf[i] in {'.', ','}: result[i] = decimalsep else: result[i] = buf[i] - when defined(vcc): + when defined(windows): # VS pre 2015 violates the C standard: "The exponent always contains at # least two digits, and only as many more digits as necessary to # represent the exponent." [C11 §7.21.6.1] @@ -1995,7 +1995,7 @@ proc formatBiggestFloat*(f: BiggestFloat, format: FloatFormatMode = ffDefault, result.setLen(result.len - 1) proc formatFloat*(f: float, format: FloatFormatMode = ffDefault, - precision: range[0..32] = 16; decimalSep = '.'): string {. + precision: range[-1..32] = 16; decimalSep = '.'): string {. noSideEffect, rtl, extern: "nsu$1".} = ## Converts a floating point value `f` to a string. ## @@ -2006,16 +2006,16 @@ proc formatFloat*(f: float, format: FloatFormatMode = ffDefault, ## `precision`'s default value is the maximum number of meaningful digits ## after the decimal point for Nim's ``float`` type. ## - ## If ``precision == 0``, it tries to format it nicely. + ## If ``precision == -1``, it tries to format it nicely. ## ## Examples: ## ## .. code-block:: nim ## ## let x = 123.456 - ## echo x.formatFloat() # 123.4560000000000 - ## echo x.formatFloat(ffDecimal, 4) # 123.4560 - ## echo x.formatFloat(ffScientific, 2) # 1.23e+02 + ## doAssert x.formatFloat() == "123.4560000000000" + ## doAssert x.formatFloat(ffDecimal, 4) == "123.4560" + ## doAssert x.formatFloat(ffScientific, 2) == "1.23e+02" ## result = formatBiggestFloat(f, format, precision, decimalSep) @@ -2471,12 +2471,14 @@ when isMainModule: outp = " this is a\nlong text\n--\nmuchlongerthan10chars\nand here\nit goes" doAssert wordWrap(inp, 10, false) == outp + doAssert formatBiggestFloat(1234.567, ffDecimal, -1) == "1234.567000" + doAssert formatBiggestFloat(1234.567, ffDecimal, 0) == "1235." + doAssert formatBiggestFloat(1234.567, ffDecimal, 1) == "1234.6" doAssert formatBiggestFloat(0.00000000001, ffDecimal, 11) == "0.00000000001" doAssert formatBiggestFloat(0.00000000001, ffScientific, 1, ',') in ["1,0e-11", "1,0e-011"] # bug #6589 - doAssert formatFloat(123.456, ffScientific, precision=0) in - ["1.234560e+02", "1.234560e+002"] + doAssert formatFloat(123.456, ffScientific, precision = -1) == "1.234560e+02" doAssert "$# $3 $# $#" % ["a", "b", "c"] == "a c b c" doAssert "${1}12 ${-1}$2" % ["a", "b"] == "a12 bb" diff --git a/lib/pure/typetraits.nim b/lib/pure/typetraits.nim index 3b6f7de1a..2047abda4 100644 --- a/lib/pure/typetraits.nim +++ b/lib/pure/typetraits.nim @@ -21,7 +21,7 @@ proc name*(t: typedesc): string {.magic: "TypeTrait".} ## ## proc `$`*(T: typedesc): string = name(T) ## - ## template test(x): stmt = + ## template test(x): typed = ## echo "type: ", type(x), ", value: ", x ## ## test 42 diff --git a/lib/pure/uri.nim b/lib/pure/uri.nim index c702b054c..164a57ecf 100644 --- a/lib/pure/uri.nim +++ b/lib/pure/uri.nim @@ -308,11 +308,16 @@ proc `$`*(u: Uri): string = result.add(":") result.add(u.password) result.add("@") - result.add(u.hostname) + if u.hostname.endswith('/'): + result.add(u.hostname[0..^2]) + else: + result.add(u.hostname) if u.port.len > 0: result.add(":") result.add(u.port) if u.path.len > 0: + if u.hostname.len > 0 and u.path[0] != '/': + result.add('/') result.add(u.path) if u.query.len > 0: result.add("?") @@ -483,6 +488,34 @@ when isMainModule: let foo = parseUri("http://localhost:9515") / "status" doAssert $foo == "http://localhost:9515/status" + # bug #6649 #6652 + block: + var foo = parseUri("http://example.com") + foo.hostname = "example.com" + foo.path = "baz" + doAssert $foo == "http://example.com/baz" + + foo.hostname = "example.com/" + foo.path = "baz" + doAssert $foo == "http://example.com/baz" + + foo.hostname = "example.com" + foo.path = "/baz" + doAssert $foo == "http://example.com/baz" + + foo.hostname = "example.com/" + foo.path = "/baz" + doAssert $foo == "http://example.com/baz" + + foo.hostname = "example.com/" + foo.port = "8000" + foo.path = "baz" + doAssert $foo == "http://example.com:8000/baz" + + foo = parseUri("file:/dir/file") + foo.path = "relative" + doAssert $foo == "file:relative" + # isAbsolute tests block: doAssert "www.google.com".parseUri().isAbsolute() == false @@ -524,4 +557,4 @@ when isMainModule: doAssert "https://example.com/about/staff.html?".parseUri().isAbsolute == true doAssert "https://example.com/about/staff.html?parameters".parseUri().isAbsolute == true - echo("All good!") \ No newline at end of file + echo("All good!") diff --git a/lib/system.nim b/lib/system.nim index 687a68119..c1bf1a919 100644 --- a/lib/system.nim +++ b/lib/system.nim @@ -456,6 +456,13 @@ type WriteIOEffect* = object of IOEffect ## Effect describing a write IO operation. ExecIOEffect* = object of IOEffect ## Effect describing an executing IO operation. + StackTraceEntry* = object ## In debug mode exceptions store the stack trace that led + ## to them. A StackTraceEntry is a single entry of the + ## stack trace. + procname*: cstring ## name of the proc that is currently executing + line*: int ## line number of the proc that is currently executing + filename*: cstring ## filename of the proc that is currently executing + Exception* {.compilerproc.} = object of RootObj ## \ ## Base exception class. ## @@ -468,7 +475,10 @@ type msg* {.exportc: "message".}: string ## the exception's message. Not ## providing an exception message ## is bad style. - trace: string + when defined(js): + trace: string + else: + trace: seq[StackTraceEntry] up: ref Exception # used for stacking exceptions. Not exported! SystemError* = object of Exception ## \ @@ -895,7 +905,7 @@ proc `div` *(x, y: int8): int8 {.magic: "DivI", noSideEffect.} proc `div` *(x, y: int16): int16 {.magic: "DivI", noSideEffect.} proc `div` *(x, y: int32): int32 {.magic: "DivI", noSideEffect.} ## computes the integer division. This is roughly the same as - ## ``floor(x/y)``. + ## ``trunc(x/y)``. ## ## .. code-block:: Nim ## 1 div 2 == 0 @@ -1097,7 +1107,7 @@ proc `*`*[T: SomeUnsignedInt](x, y: T): T {.magic: "MulU", noSideEffect.} proc `div`*[T: SomeUnsignedInt](x, y: T): T {.magic: "DivU", noSideEffect.} ## computes the integer division. This is roughly the same as - ## ``floor(x/y)``. + ## ``trunc(x/y)``. ## ## .. code-block:: Nim ## (7 div 5) == 1 @@ -2015,19 +2025,19 @@ when defined(nimNewRoof): yield res inc(res) - iterator `..`*(a, b: int64): int64 {.inline.} = - ## A special version of ``..`` for ``int64`` only. - var res = a - while res <= b: - yield res - inc(res) + template dotdotImpl(t) {.dirty.} = + iterator `..`*(a, b: t): t {.inline.} = + ## A type specialized version of ``..`` for convenience so that + ## mixing integer types work better. + var res = a + while res <= b: + yield res + inc(res) - iterator `..`*(a, b: int32): int32 {.inline.} = - ## A special version of ``..`` for ``int32`` only. - var res = a - while res <= b: - yield res - inc(res) + dotdotImpl(int64) + dotdotImpl(int32) + dotdotImpl(uint64) + dotdotImpl(uint32) else: iterator countup*[S, T](a: S, b: T, step = 1): T {.inline.} = @@ -2763,9 +2773,9 @@ when defined(nimvarargstyped): ## pretends to be free of side effects, so that it can be used for debugging ## routines marked as `noSideEffect <manual.html#pragmas-nosideeffect-pragma>`_. else: - proc echo*(x: varargs[expr, `$`]) {.magic: "Echo", tags: [WriteIOEffect], + proc echo*(x: varargs[untyped, `$`]) {.magic: "Echo", tags: [WriteIOEffect], benign, sideEffect.} - proc debugEcho*(x: varargs[expr, `$`]) {.magic: "Echo", noSideEffect, + proc debugEcho*(x: varargs[untyped, `$`]) {.magic: "Echo", noSideEffect, tags: [], raises: [].} template newException*(exceptn: typedesc, message: string; @@ -3706,7 +3716,7 @@ proc instantiationInfo*(index = -1, fullPaths = false): tuple[ ## .. code-block:: nim ## import strutils ## - ## template testException(exception, code: expr): stmt = + ## template testException(exception, code: untyped): typed = ## try: ## let pos = instantiationInfo() ## discard(code) @@ -3844,11 +3854,11 @@ type {.deprecated: [PNimrodNode: NimNode].} when false: - template eval*(blk: stmt): stmt = + template eval*(blk: typed): typed = ## executes a block of code at compile time just as if it was a macro ## optionally, the block can return an AST tree that will replace the ## eval expression - macro payload: stmt {.gensym.} = blk + macro payload: typed {.gensym.} = blk payload() when hasAlloc: diff --git a/lib/system/atomics.nim b/lib/system/atomics.nim index 8c3801687..afc435638 100644 --- a/lib/system/atomics.nim +++ b/lib/system/atomics.nim @@ -213,12 +213,20 @@ proc atomicDec*(memLoc: var int, x: int = 1): int = result = memLoc when defined(vcc): - proc interlockedCompareExchange64(p: pointer; exchange, comparand: int64): int64 - {.importc: "_InterlockedCompareExchange64", header: "<intrin.h>".} - proc interlockedCompareExchange32(p: pointer; exchange, comparand: int32): int32 - {.importc: "_InterlockedCompareExchange", header: "<intrin.h>".} - proc interlockedCompareExchange8(p: pointer; exchange, comparand: byte): byte - {.importc: "_InterlockedCompareExchange64", header: "<intrin.h>".} + when defined(cpp): + proc interlockedCompareExchange64(p: pointer; exchange, comparand: int64): int64 + {.importcpp: "_InterlockedCompareExchange64(static_cast<NI64 volatile *>(#), #, #)", header: "<intrin.h>".} + proc interlockedCompareExchange32(p: pointer; exchange, comparand: int32): int32 + {.importcpp: "_InterlockedCompareExchange(static_cast<NI volatile *>(#), #, #)", header: "<intrin.h>".} + proc interlockedCompareExchange8(p: pointer; exchange, comparand: byte): byte + {.importcpp: "_InterlockedCompareExchange8(static_cast<char volatile *>(#), #, #)", header: "<intrin.h>".} + else: + proc interlockedCompareExchange64(p: pointer; exchange, comparand: int64): int64 + {.importc: "_InterlockedCompareExchange64", header: "<intrin.h>".} + proc interlockedCompareExchange32(p: pointer; exchange, comparand: int32): int32 + {.importc: "_InterlockedCompareExchange", header: "<intrin.h>".} + proc interlockedCompareExchange8(p: pointer; exchange, comparand: byte): byte + {.importc: "_InterlockedCompareExchange8", header: "<intrin.h>".} proc cas*[T: bool|int|ptr](p: ptr T; oldValue, newValue: T): bool = when sizeof(T) == 8: diff --git a/lib/system/endb.nim b/lib/system/endb.nim index 35d8f25c4..d51ae29df 100644 --- a/lib/system/endb.nim +++ b/lib/system/endb.nim @@ -370,7 +370,7 @@ proc commandPrompt() = if dbgUser.len == 0: dbgUser.len = oldLen # now look what we have to do: var i = scanWord(addr dbgUser.data, dbgTemp, 0) - template `?`(x: expr): expr = dbgTemp == cstring(x) + template `?`(x: untyped): untyped = dbgTemp == cstring(x) if ?"s" or ?"step": dbgState = dbStepInto again = false diff --git a/lib/system/excpt.nim b/lib/system/excpt.nim index 9e02d6824..70c18ae21 100644 --- a/lib/system/excpt.nim +++ b/lib/system/excpt.nim @@ -155,6 +155,52 @@ when not hasThreadSupport: var tempFrames: array[0..127, PFrame] # should not be alloc'd on stack +const + reraisedFromBegin = -10 + reraisedFromEnd = -100 + +template reraisedFrom(z): untyped = + StackTraceEntry(procname: nil, line: z, filename: nil) + +proc auxWriteStackTrace(f: PFrame; s: var seq[StackTraceEntry]) = + var + it = f + i = 0 + while it != nil: + inc(i) + it = it.prev + var last = i-1 + if s.isNil: + s = newSeq[StackTraceEntry](i) + else: + last = s.len + i - 1 + s.setLen(last+1) + it = f + while it != nil: + s[last] = StackTraceEntry(procname: it.procname, + line: it.line, + filename: it.filename) + it = it.prev + dec last + +template addFrameEntry(s, f: untyped) = + var oldLen = s.len + add(s, f.filename) + if f.line > 0: + add(s, '(') + add(s, $f.line) + add(s, ')') + for k in 1..max(1, 25-(s.len-oldLen)): add(s, ' ') + add(s, f.procname) + add(s, "\n") + +proc `$`(s: seq[StackTraceEntry]): string = + result = newStringOfCap(2000) + for i in 0 .. s.len-1: + if s[i].line == reraisedFromBegin: result.add "[[reraised from:\n" + elif s[i].line == reraisedFromEnd: result.add "]]\n" + else: addFrameEntry(result, s[i]) + proc auxWriteStackTrace(f: PFrame, s: var string) = when hasThreadSupport: var @@ -194,17 +240,9 @@ proc auxWriteStackTrace(f: PFrame, s: var string) = if tempFrames[j] == nil: add(s, "(") add(s, $skipped) - add(s, " calls omitted) ...") + add(s, " calls omitted) ...\n") else: - var oldLen = s.len - add(s, tempFrames[j].filename) - if tempFrames[j].line > 0: - add(s, '(') - add(s, $tempFrames[j].line) - add(s, ')') - for k in 1..max(1, 25-(s.len-oldLen)): add(s, ' ') - add(s, tempFrames[j].procname) - add(s, "\n") + addFrameEntry(s, tempFrames[j]) proc stackTraceAvailable*(): bool @@ -221,6 +259,13 @@ when hasSomeStackTrace: auxWriteStackTraceWithBacktrace(s) else: add(s, "No stack traceback available\n") + + proc rawWriteStackTrace(s: var seq[StackTraceEntry]) = + when NimStackTrace: + auxWriteStackTrace(framePtr, s) + else: + s = nil + proc stackTraceAvailable(): bool = when NimStackTrace: if framePtr == nil: @@ -240,12 +285,6 @@ proc quitOrDebug() {.inline.} = else: endbStep() # call the debugger -when false: - proc rawRaise*(e: ref Exception) = - ## undocumented. Do not use. - pushCurrentException(e) - c_longjmp(excHandler.context, 1) - var onUnhandledException*: (proc (errorMsg: string) {. nimcall.}) ## set this error \ ## handler to override the existing behaviour on an unhandled exception. @@ -282,7 +321,7 @@ proc raiseExceptionAux(e: ref Exception) = when hasSomeStackTrace: var buf = newStringOfCap(2000) if isNil(e.trace): rawWriteStackTrace(buf) - else: add(buf, e.trace) + else: add(buf, $e.trace) add(buf, "Error: unhandled exception: ") if not isNil(e.msg): add(buf, e.msg) add(buf, " [") @@ -318,12 +357,11 @@ proc raiseException(e: ref Exception, ename: cstring) {.compilerRtl.} = if e.name.isNil: e.name = ename when hasSomeStackTrace: if e.trace.isNil: - e.trace = "" rawWriteStackTrace(e.trace) elif framePtr != nil: - e.trace.add "[[reraised from:\n" + e.trace.add reraisedFrom(reraisedFromBegin) auxWriteStackTrace(framePtr, e.trace) - e.trace.add "]]\n" + e.trace.add reraisedFrom(reraisedFromEnd) raiseExceptionAux(e) proc reraiseException() {.compilerRtl.} = @@ -349,10 +387,15 @@ proc getStackTrace(): string = proc getStackTrace(e: ref Exception): string = if not isNil(e) and not isNil(e.trace): - result = e.trace + result = $e.trace else: result = "" +proc getStackTraceEntries*(e: ref Exception): seq[StackTraceEntry] = + ## Returns the attached stack trace to the exception ``e`` as + ## a ``seq``. This is not yet available for the JS backend. + shallowCopy(result, e.trace) + when defined(nimRequiresNimFrame): proc stackOverflow() {.noinline.} = writeStackTrace() diff --git a/lib/system/gc2.nim b/lib/system/gc2.nim index 70bed8740..4ecf3b226 100644 --- a/lib/system/gc2.nim +++ b/lib/system/gc2.nim @@ -173,7 +173,7 @@ proc internRefcount(p: pointer): int {.exportc: "getRefcount".} = when BitsPerPage mod (sizeof(int)*8) != 0: {.error: "(BitsPerPage mod BitsPerUnit) should be zero!".} -template color(c): expr = c.refCount and colorMask +template color(c): untyped = c.refCount and colorMask template setColor(c, col) = c.refcount = c.refcount and not colorMask or col diff --git a/lib/system/sysspawn.nim b/lib/system/sysspawn.nim index a0d10c446..dc2d13578 100644 --- a/lib/system/sysspawn.nim +++ b/lib/system/sysspawn.nim @@ -153,12 +153,12 @@ proc preferSpawn*(): bool = ## it is not necessary to call this directly; use 'spawnX' instead. result = gSomeReady.event -proc spawn*(call: stmt) {.magic: "Spawn".} +proc spawn*(call: typed) {.magic: "Spawn".} ## always spawns a new task, so that the 'call' is never executed on ## the calling thread. 'call' has to be proc call 'p(...)' where 'p' ## is gcsafe and has 'void' as the return type. -template spawnX*(call: stmt) = +template spawnX*(call: typed) = ## spawns a new task if a CPU core is ready, otherwise executes the ## call in the calling thread. Usually it is advised to ## use 'spawn' in order to not block the producer for an unknown diff --git a/lib/system/sysstr.nim b/lib/system/sysstr.nim index d9586b00d..3f8e0eff0 100644 --- a/lib/system/sysstr.nim +++ b/lib/system/sysstr.nim @@ -322,37 +322,40 @@ proc nimIntToStr(x: int): string {.compilerRtl.} = result.add x proc add*(result: var string; x: float) = - var buf: array[0..64, char] - when defined(nimNoArrayToCstringConversion): - var n: int = c_sprintf(addr buf, "%.16g", x) + when nimvm: + result.add $x else: - var n: int = c_sprintf(buf, "%.16g", x) - var hasDot = false - for i in 0..n-1: - if buf[i] == ',': - buf[i] = '.' - hasDot = true - elif buf[i] in {'a'..'z', 'A'..'Z', '.'}: - hasDot = true - if not hasDot: - buf[n] = '.' - buf[n+1] = '0' - buf[n+2] = '\0' - # On Windows nice numbers like '1.#INF', '-1.#INF' or '1.#NAN' - # of '-1.#IND' are produced. - # We want to get rid of these here: - if buf[n-1] in {'n', 'N', 'D', 'd'}: - result.add "nan" - elif buf[n-1] == 'F': - if buf[0] == '-': - result.add "-inf" + var buf: array[0..64, char] + when defined(nimNoArrayToCstringConversion): + var n: int = c_sprintf(addr buf, "%.16g", x) else: - result.add "inf" - else: - var i = 0 - while buf[i] != '\0': - result.add buf[i] - inc i + var n: int = c_sprintf(buf, "%.16g", x) + var hasDot = false + for i in 0..n-1: + if buf[i] == ',': + buf[i] = '.' + hasDot = true + elif buf[i] in {'a'..'z', 'A'..'Z', '.'}: + hasDot = true + if not hasDot: + buf[n] = '.' + buf[n+1] = '0' + buf[n+2] = '\0' + # On Windows nice numbers like '1.#INF', '-1.#INF' or '1.#NAN' + # of '-1.#IND' are produced. + # We want to get rid of these here: + if buf[n-1] in {'n', 'N', 'D', 'd'}: + result.add "nan" + elif buf[n-1] == 'F': + if buf[0] == '-': + result.add "-inf" + else: + result.add "inf" + else: + var i = 0 + while buf[i] != '\0': + result.add buf[i] + inc i proc nimFloatToStr(f: float): string {.compilerproc.} = result = newStringOfCap(8) diff --git a/tests/async/tasyncall.nim b/tests/async/tasyncall.nim index 7daecd9ef..a3926eabd 100644 --- a/tests/async/tasyncall.nim +++ b/tests/async/tasyncall.nim @@ -40,8 +40,7 @@ proc testVarargs(x, y, z: int): seq[int] = result = waitFor all(a, b, c) -suite "tasyncall": - test "testFuturesWithValue": +block: let startTime = cpuTime() results = testFuturesWithValue(42) @@ -51,14 +50,14 @@ suite "tasyncall": doAssert execTime * 1000 < taskCount * sleepDuration doAssert results == expected - test "testFuturesWithoutValues": +block: let startTime = cpuTime() testFuturesWithoutValues() let execTime = cpuTime() - startTime doAssert execTime * 1000 < taskCount * sleepDuration - test "testVarargs": +block: let startTime = cpuTime() results = testVarargs(1, 2, 3) @@ -68,7 +67,7 @@ suite "tasyncall": doAssert execTime * 100 < taskCount * sleepDuration doAssert results == expected - test "all on seq[Future]": +block: let noIntFuturesFut = all(newSeq[Future[int]]()) noVoidFuturesFut = all(newSeq[Future[void]]()) diff --git a/tests/async/tasyncsend4757.nim b/tests/async/tasyncsend4757.nim index 1066f38e5..752bb3e75 100644 --- a/tests/async/tasyncsend4757.nim +++ b/tests/async/tasyncsend4757.nim @@ -3,11 +3,22 @@ discard """ output: "Finished" """ -import asyncdispatch +import asyncdispatch, asyncnet + +proc createServer(port: Port) {.async.} = + var server = newAsyncSocket() + server.setSockOpt(OptReuseAddr, true) + bindAddr(server, port) + server.listen() + while true: + let client = await server.accept() + discard await client.recvLine() + +asyncCheck createServer(10335.Port) proc f(): Future[void] {.async.} = let s = newAsyncNativeSocket() - await s.connect("example.com", 80.Port) + await s.connect("localhost", 10335.Port) await s.send("123") echo "Finished" diff --git a/tests/async/tnewasyncudp.nim b/tests/async/tnewasyncudp.nim index b56cdc71b..e61f630e4 100644 --- a/tests/async/tnewasyncudp.nim +++ b/tests/async/tnewasyncudp.nim @@ -86,7 +86,7 @@ proc readMessages(server: AsyncFD) {.async.} = size = 0 var grammString = $cstring(addr buffer) if grammString.startswith("Message ") and - saddr.sin_addr.s_addr == 0x100007F: + saddr.sin_addr.s_addr == nativesockets.ntohl(INADDR_LOOPBACK.uint32): await sendTo(server, addr grammString[0], len(grammString), cast[ptr SockAddr](addr saddr), slen) inc(msgCount) diff --git a/tests/ccgbugs/trefseqsort.nim b/tests/ccgbugs/trefseqsort.nim new file mode 100644 index 000000000..2410770cf --- /dev/null +++ b/tests/ccgbugs/trefseqsort.nim @@ -0,0 +1,33 @@ +discard """ + output: '''@[0, 4, 9, 1, 3, 2] +@[0, 1, 2, 3, 9]''' +""" +# bug #6724 +import algorithm + +type + Bar = object + bar: ref seq[int] + Foo = ref Bar + +proc test(x: ref Foo) = + x.bar[].del(1) + x.bar[].sort(cmp) + +proc main() = + var foo: ref Foo + new(foo) + + var s = @[0, 4, 9, 1, 3, 2] + + var sr: ref seq[int] + new(sr) + sr[] = s + + foo[] = Foo(bar: sr) + echo($foo.bar[]) + + test(foo) + echo($foo.bar[]) + +main() diff --git a/tests/cpp/tasync_cpp.nim b/tests/cpp/tasync_cpp.nim index ec78ae26c..a5e3374b6 100644 --- a/tests/cpp/tasync_cpp.nim +++ b/tests/cpp/tasync_cpp.nim @@ -1,5 +1,5 @@ discard """ - cmd: "nim cpp $file" + targets: "cpp" output: "hello" """ diff --git a/tests/cpp/tcasts.nim b/tests/cpp/tcasts.nim new file mode 100644 index 000000000..35f06234d --- /dev/null +++ b/tests/cpp/tcasts.nim @@ -0,0 +1,10 @@ +discard """ + cmd: "nim cpp $file" + output: "" +""" + +block: #5979 + var a = 'a' + var p: pointer = cast[pointer](a) + var c = cast[char](p) + doAssert(c == 'a') diff --git a/tests/cpp/tcovariancerules.nim b/tests/cpp/tcovariancerules.nim index dfe4cb941..9365a3a18 100644 --- a/tests/cpp/tcovariancerules.nim +++ b/tests/cpp/tcovariancerules.nim @@ -1,5 +1,5 @@ discard """ -cmd: "nim cpp $file" +targets: "cpp" output: ''' cat cat diff --git a/tests/cpp/tcppraise.nim b/tests/cpp/tcppraise.nim index a9ea8e6ce..7db9c0cfa 100644 --- a/tests/cpp/tcppraise.nim +++ b/tests/cpp/tcppraise.nim @@ -1,5 +1,5 @@ discard """ - cmd: "nim cpp $file" + targets: "cpp" output: '''foo bar Need odd and >= 3 digits## diff --git a/tests/cpp/tdont_init_instantiation.nim b/tests/cpp/tdont_init_instantiation.nim index 652cb1414..fe487fba0 100644 --- a/tests/cpp/tdont_init_instantiation.nim +++ b/tests/cpp/tdont_init_instantiation.nim @@ -1,5 +1,5 @@ discard """ - cmd: "nim cpp $file" + targets: "cpp" output: '''''' disabled: true """ diff --git a/tests/cpp/tembarrassing_generic_failure.nim b/tests/cpp/tembarrassing_generic_failure.nim index 3c31dcdb8..4b5050948 100644 --- a/tests/cpp/tembarrassing_generic_failure.nim +++ b/tests/cpp/tembarrassing_generic_failure.nim @@ -1,4 +1,5 @@ discard """ + targets: "cpp" cmd: "nim cpp --threads:on $file" """ diff --git a/tests/cpp/temitlist.nim b/tests/cpp/temitlist.nim index cef0fc52d..a7a8ebde4 100644 --- a/tests/cpp/temitlist.nim +++ b/tests/cpp/temitlist.nim @@ -1,5 +1,5 @@ discard """ - cmd: "nim cpp $file" + targets: "cpp" output: '''6.0''' """ diff --git a/tests/cpp/tempty_generic_obj.nim b/tests/cpp/tempty_generic_obj.nim index e2957a5cd..b4c746a30 100644 --- a/tests/cpp/tempty_generic_obj.nim +++ b/tests/cpp/tempty_generic_obj.nim @@ -1,5 +1,5 @@ discard """ - cmd: "nim cpp $file" + targets: "cpp" output: '''int float''' """ diff --git a/tests/cpp/tgen_prototype_for_importc.nim b/tests/cpp/tgen_prototype_for_importc.nim index 91f34755b..4e5a197a8 100644 --- a/tests/cpp/tgen_prototype_for_importc.nim +++ b/tests/cpp/tgen_prototype_for_importc.nim @@ -1,5 +1,5 @@ discard """ - cmd: "nim cpp $file" + targets: "cpp" output: '''Hello world''' """ diff --git a/tests/cpp/tget_subsystem.nim b/tests/cpp/tget_subsystem.nim index 81009dd39..e9a3fabdd 100644 --- a/tests/cpp/tget_subsystem.nim +++ b/tests/cpp/tget_subsystem.nim @@ -1,5 +1,5 @@ discard """ - cmd: "nim cpp $file" + targets: "cpp" """ {.emit: """ diff --git a/tests/cpp/tnativesockets.nim b/tests/cpp/tnativesockets.nim index 6108380a8..c62008050 100644 --- a/tests/cpp/tnativesockets.nim +++ b/tests/cpp/tnativesockets.nim @@ -1,5 +1,5 @@ discard """ - cmd: "nim cpp $file" + targets: "cpp" """ import nativesockets diff --git a/tests/cpp/treturn_array.nim b/tests/cpp/treturn_array.nim index ba4fbd6cc..432b9ce3b 100644 --- a/tests/cpp/treturn_array.nim +++ b/tests/cpp/treturn_array.nim @@ -1,3 +1,6 @@ +discard """ + targets: "cpp" +""" # bug #2259 type Mat4f* = array[0..15, float] diff --git a/tests/cpp/tsigbreak.nim b/tests/cpp/tsigbreak.nim index c8044f2bf..9a381d84f 100644 --- a/tests/cpp/tsigbreak.nim +++ b/tests/cpp/tsigbreak.nim @@ -1,5 +1,5 @@ discard """ - cmd: "nim cpp $file" + targets: "cpp" """ import tables, lists diff --git a/tests/cpp/tstaticvar_via_typedesc.nim b/tests/cpp/tstaticvar_via_typedesc.nim index 7a9fa2afc..0d8f424d0 100644 --- a/tests/cpp/tstaticvar_via_typedesc.nim +++ b/tests/cpp/tstaticvar_via_typedesc.nim @@ -1,5 +1,5 @@ discard """ - cmd: "nim cpp $file" + targets: "cpp" output: "42" """ diff --git a/tests/cpp/ttemplatetype.nim b/tests/cpp/ttemplatetype.nim index 7f56a225d..ef24e4cdc 100644 --- a/tests/cpp/ttemplatetype.nim +++ b/tests/cpp/ttemplatetype.nim @@ -1,3 +1,7 @@ +discard """ + targets: "cpp" +""" + type Map {.importcpp: "std::map", header: "<map>".} [T,U] = object diff --git a/tests/cpp/tthread_createthread.nim b/tests/cpp/tthread_createthread.nim index 363136e9d..b46b876b7 100644 --- a/tests/cpp/tthread_createthread.nim +++ b/tests/cpp/tthread_createthread.nim @@ -1,4 +1,5 @@ discard """ + targets: "cpp" cmd: "nim cpp --hints:on --threads:on $options $file" """ diff --git a/tests/cpp/ttypeinfo.nim b/tests/cpp/ttypeinfo.nim index 282c682b2..97825f452 100644 --- a/tests/cpp/ttypeinfo.nim +++ b/tests/cpp/ttypeinfo.nim @@ -1,6 +1,6 @@ discard """ + targets: "cpp" output: '''100''' - cmd: "nim cpp $file" """ import typeinfo diff --git a/tests/cpp/ttypeinfo2.nim b/tests/cpp/ttypeinfo2.nim index 64bd43e96..e3661c848 100644 --- a/tests/cpp/ttypeinfo2.nim +++ b/tests/cpp/ttypeinfo2.nim @@ -1,5 +1,5 @@ discard """ - cmd: "nim cpp $file" + targets: "cpp" """ # bug #2841 import typeinfo diff --git a/tests/cpp/tvector_iterator.nim b/tests/cpp/tvector_iterator.nim index cb5ab33af..9df3754ba 100644 --- a/tests/cpp/tvector_iterator.nim +++ b/tests/cpp/tvector_iterator.nim @@ -1,5 +1,5 @@ discard """ - cmd: "nim cpp $file" + targets: "cpp" """ {.emit: """ diff --git a/tests/cpp/tvectorseq.nim b/tests/cpp/tvectorseq.nim index 6eb5dc9e4..4d9ffc3d6 100644 --- a/tests/cpp/tvectorseq.nim +++ b/tests/cpp/tvectorseq.nim @@ -1,7 +1,7 @@ discard """ + targets: "cpp" output: '''(x: 1.0) (x: 0.0)''' - cmd: "nim cpp $file" disabled: "true" """ diff --git a/tests/errmsgs/tmake_tuple_visible.nim b/tests/errmsgs/tmake_tuple_visible.nim new file mode 100644 index 000000000..43337c2a9 --- /dev/null +++ b/tests/errmsgs/tmake_tuple_visible.nim @@ -0,0 +1,23 @@ +discard """ + errormsg: '''got (tuple of (type NimEdAppWindow, int))''' + line: 22 + nimout: '''got (tuple of (type NimEdAppWindow, int)) +but expected one of: +template xxx(tn: typedesc; i: int)''' +""" + +type + NimEdAppWindow = ptr NimEdAppWindowObj + NimEdAppWindowObj = object + i: int + +template gDefineTypeExtended*(tn: typeDesc) = + discard + +gDefineTypeExtended (NimEdAppWindow) + +template xxx*(tn: typeDesc, i: int) = + discard + +xxx (NimEdAppWindow, 0) +# bug #6776 diff --git a/tests/exprs/tstmtexprs.nim b/tests/exprs/tstmtexprs.nim index b2d5db408..01f429b07 100644 --- a/tests/exprs/tstmtexprs.nim +++ b/tests/exprs/tstmtexprs.nim @@ -5,7 +5,8 @@ discard """ 6 abcdefghijklmnopqrstuvwxyz 145 23 -3''' +3 +2''' """ import strutils @@ -122,3 +123,21 @@ var testTry = PFooBase(field: 5) echo(testTry.field) + +# bug #6166 + +proc quo(op: proc (x: int): bool): int = + result = + if op(3): + 2 + else: + 0 + +echo( + if true: + quo do (a: int) -> bool: + a mod 2 != 0 + else: + quo do (a: int) -> bool: + a mod 3 != 0 +) \ No newline at end of file diff --git a/tests/generics/toverloading_typedesc.nim b/tests/generics/toverloading_typedesc.nim index 5882640f2..94f4d860d 100644 --- a/tests/generics/toverloading_typedesc.nim +++ b/tests/generics/toverloading_typedesc.nim @@ -1,5 +1,6 @@ discard """ exitcode: 0 + disabled: '''true''' """ import moverloading_typedesc import tables diff --git a/tests/misc/tromans.nim b/tests/misc/tromans.nim deleted file mode 100644 index 132c73ddd..000000000 --- a/tests/misc/tromans.nim +++ /dev/null @@ -1,71 +0,0 @@ -discard """ - file: "tromans.nim" - output: "success" -""" -import - strutils - -## Convert an integer to a Roman numeral -# See http://en.wikipedia.org/wiki/Roman_numerals for reference - -proc raiseInvalidValue(msg: string) {.noreturn.} = - # Yes, we really need a shorthand for this code... - var e: ref EInvalidValue - new(e) - e.msg = msg - raise e - -# I should use a class, perhaps. -# --> No. Why introduce additional state into such a simple and nice -# interface? State is evil. :D - -proc RomanToDecimal(romanVal: string): int = - result = 0 - var prevVal = 0 - for i in countdown(romanVal.len - 1, 0): - var val = 0 - case romanVal[i] - of 'I', 'i': val = 1 - of 'V', 'v': val = 5 - of 'X', 'x': val = 10 - of 'L', 'l': val = 50 - of 'C', 'c': val = 100 - of 'D', 'd': val = 500 - of 'M', 'm': val = 1000 - else: raiseInvalidValue("Incorrect character in roman numeral! (" & - $romanVal[i] & ")") - if val >= prevVal: - inc(result, val) - else: - dec(result, val) - prevVal = val - -proc DecimalToRoman(decValParam: int): string = - # Apparently numbers cannot be above 4000 - # Well, they can be (using overbar or parenthesis notation) - # but I see little interest (beside coding challenge) in coding them as - # we rarely use huge Roman numeral. - const romanComposites = [ - ("M", 1000), ("CM", 900), - ("D", 500), ("CD", 400), ("C", 100), - ("XC", 90), ("L", 50), ("XL", 40), ("X", 10), ("IX", 9), - ("V", 5), ("IV", 4), ("I", 1)] - if decValParam < 1 or decValParam > 3999: - raiseInvalidValue("number not representable") - result = "" - var decVal = decValParam - for key, val in items(romanComposites): - while decVal >= val: - dec(decVal, val) - result.add(key) - -for i in 1..100: - if RomanToDecimal(DecimalToRoman(i)) != i: quit "BUG" - -for i in items([1238, 1777, 3830, 2401, 379, 33, 940, 3973]): - if RomanToDecimal(DecimalToRoman(i)) != i: quit "BUG" - -echo "success" #OUT success - - - diff --git a/tests/overload/tstatic_with_converter.nim b/tests/overload/tstatic_with_converter.nim new file mode 100644 index 000000000..2871744eb --- /dev/null +++ b/tests/overload/tstatic_with_converter.nim @@ -0,0 +1,47 @@ +discard """ +output: ''' +9.0''' +""" + +### bug #6773 + +{.emit: """ /*INCLUDESECTION*/ +typedef double cimported; + +cimported set1_imported(double x) { + return x; +} + +"""} + +type vfloat{.importc: "cimported".} = object + +proc set1(a: float): vfloat {.importc: "set1_imported".} + +converter scalar_to_vector(x: float): vfloat = + set1(x) + +proc sqrt(x: vfloat): vfloat = + x + +proc pow(x, y: vfloat): vfloat = + y + +proc `^`(x: vfloat, exp: static[int]): vfloat = + when exp == 0: + 1.0 + else: + x + +proc `^`(x: vfloat, exp: static[float]): vfloat = + when exp == 0.5: + sqrt(x) + else: + pow(x, exp) + +proc `$`(x: vfloat): string = + let y = cast[ptr float](unsafeAddr x) + echo y[] + +let x = set1(9.0) +echo x^0.5 diff --git a/tests/parser/tpostexprblocks.nim b/tests/parser/tpostexprblocks.nim index 341ca737a..3b9c956c2 100644 --- a/tests/parser/tpostexprblocks.nim +++ b/tests/parser/tpostexprblocks.nim @@ -1,82 +1,82 @@ discard """ nimout: ''' StmtList - Ident !"foo010" + Ident ident"foo010" Call - Ident !"foo020" + Ident ident"foo020" Call - Ident !"foo030" - Ident !"x" + Ident ident"foo030" + Ident ident"x" Command - Ident !"foo040" - Ident !"x" + Ident ident"foo040" + Ident ident"x" Call - Ident !"foo050" + Ident ident"foo050" StmtList DiscardStmt Empty Call - Ident !"foo060" + Ident ident"foo060" StmtList DiscardStmt Empty Call - Ident !"foo070" + Ident ident"foo070" StrLit test StmtList DiscardStmt Empty Call - Ident !"foo080" + Ident ident"foo080" StrLit test StmtList DiscardStmt Empty Command - Ident !"foo090" + Ident ident"foo090" StrLit test StmtList DiscardStmt Empty Command - Ident !"foo100" + Ident ident"foo100" Call StrLit test StmtList DiscardStmt Empty Command - Ident !"foo101" + Ident ident"foo101" Call IntLit 10 StmtList DiscardStmt Empty Command - Ident !"foo110" + Ident ident"foo110" IntLit 1 Par Infix - Ident !"+" + Ident ident"+" IntLit 2 IntLit 3 StmtList DiscardStmt Empty Command - Ident !"foo120" + Ident ident"foo120" IntLit 1 Call Par Infix - Ident !"+" + Ident ident"+" IntLit 2 IntLit 3 StmtList DiscardStmt Empty Call - Ident !"foo130" + Ident ident"foo130" Do Empty Empty @@ -84,7 +84,7 @@ StmtList FormalParams Empty IdentDefs - Ident !"x" + Ident ident"x" Empty Empty Empty @@ -93,7 +93,7 @@ StmtList DiscardStmt Empty Call - Ident !"foo140" + Ident ident"foo140" Do Empty Empty @@ -101,8 +101,8 @@ StmtList FormalParams Empty IdentDefs - Ident !"x" - Ident !"int" + Ident ident"x" + Ident ident"int" Empty Empty Empty @@ -110,16 +110,16 @@ StmtList DiscardStmt Empty Call - Ident !"foo150" + Ident ident"foo150" Do Empty Empty Empty FormalParams - Ident !"int" + Ident ident"int" IdentDefs - Ident !"x" - Ident !"int" + Ident ident"x" + Ident ident"int" Empty Empty Empty @@ -127,9 +127,9 @@ StmtList DiscardStmt Empty Command - Ident !"foo160" + Ident ident"foo160" Call - Ident !"x" + Ident ident"x" Do Empty Empty @@ -137,7 +137,7 @@ StmtList FormalParams Empty IdentDefs - Ident !"y" + Ident ident"y" Empty Empty Empty @@ -146,7 +146,7 @@ StmtList DiscardStmt Empty Call - Ident !"foo170" + Ident ident"foo170" StmtList DiscardStmt Empty @@ -155,7 +155,7 @@ StmtList DiscardStmt Empty Call - Ident !"foo180" + Ident ident"foo180" StmtList DiscardStmt Empty @@ -167,9 +167,9 @@ StmtList DiscardStmt Empty Command - Ident !"foo190" + Ident ident"foo190" Call - Ident !"x" + Ident ident"x" Do Empty Empty @@ -177,7 +177,7 @@ StmtList FormalParams Empty IdentDefs - Ident !"y" + Ident ident"y" Empty Empty Empty @@ -190,9 +190,9 @@ StmtList Empty Empty FormalParams - Ident !"int" + Ident ident"int" IdentDefs - Ident !"z" + Ident ident"z" Empty Empty Empty @@ -205,10 +205,10 @@ StmtList Empty Empty FormalParams - Ident !"int" + Ident ident"int" IdentDefs - Ident !"w" - Ident !"int" + Ident ident"w" + Ident ident"int" Empty Empty Empty @@ -223,10 +223,10 @@ StmtList DiscardStmt Empty Call - Ident !"foo200" - Ident !"x" + Ident ident"foo200" + Ident ident"x" Call - Ident !"bar" + Ident ident"bar" StmtList DiscardStmt Empty @@ -236,53 +236,53 @@ StmtList Empty VarSection IdentDefs - Ident !"a" + Ident ident"a" Empty - Ident !"foo210" + Ident ident"foo210" VarSection IdentDefs - Ident !"a" + Ident ident"a" Empty Call - Ident !"foo220" + Ident ident"foo220" VarSection IdentDefs - Ident !"a" + Ident ident"a" Empty Call - Ident !"foo230" - Ident !"x" + Ident ident"foo230" + Ident ident"x" VarSection IdentDefs - Ident !"a" + Ident ident"a" Empty Command - Ident !"foo240" - Ident !"x" + Ident ident"foo240" + Ident ident"x" VarSection IdentDefs - Ident !"a" + Ident ident"a" Empty Call - Ident !"foo250" + Ident ident"foo250" StmtList DiscardStmt Empty VarSection IdentDefs - Ident !"a" + Ident ident"a" Empty Call - Ident !"foo260" + Ident ident"foo260" StmtList DiscardStmt Empty VarSection IdentDefs - Ident !"a" + Ident ident"a" Empty Call - Ident !"foo270" + Ident ident"foo270" StmtList DiscardStmt Empty @@ -292,12 +292,12 @@ StmtList Empty VarSection IdentDefs - Ident !"a" + Ident ident"a" Empty Command - Ident !"foo280" + Ident ident"foo280" Call - Ident !"x" + Ident ident"x" Do Empty Empty @@ -305,7 +305,7 @@ StmtList FormalParams Empty IdentDefs - Ident !"y" + Ident ident"y" Empty Empty Empty @@ -318,40 +318,40 @@ StmtList DiscardStmt Empty Asgn - Ident !"a" - Ident !"foo290" + Ident ident"a" + Ident ident"foo290" Asgn - Ident !"a" + Ident ident"a" Call - Ident !"foo300" + Ident ident"foo300" Asgn - Ident !"a" + Ident ident"a" Call - Ident !"foo310" - Ident !"x" + Ident ident"foo310" + Ident ident"x" Asgn - Ident !"a" + Ident ident"a" Command - Ident !"foo320" - Ident !"x" + Ident ident"foo320" + Ident ident"x" Asgn - Ident !"a" + Ident ident"a" Call - Ident !"foo330" + Ident ident"foo330" StmtList DiscardStmt Empty Asgn - Ident !"a" + Ident ident"a" Call - Ident !"foo340" + Ident ident"foo340" StmtList DiscardStmt Empty Asgn - Ident !"a" + Ident ident"a" Call - Ident !"foo350" + Ident ident"foo350" StmtList DiscardStmt Empty @@ -360,13 +360,13 @@ StmtList DiscardStmt Empty Asgn - Ident !"a" + Ident ident"a" Command - Ident !"foo360" + Ident ident"foo360" Call DotExpr - Ident !"x" - Ident !"bar" + Ident ident"x" + Ident ident"bar" Do Empty Empty @@ -374,7 +374,7 @@ StmtList FormalParams Empty IdentDefs - Ident !"y" + Ident ident"y" Empty Empty Empty @@ -388,20 +388,20 @@ StmtList Empty Command DotExpr - Ident !"foo370" - Ident !"add" + Ident ident"foo370" + Ident ident"add" Call - Ident !"quote" + Ident ident"quote" StmtList DiscardStmt Empty Call DotExpr - Ident !"foo380" - Ident !"add" + Ident ident"foo380" + Ident ident"add" BracketExpr Call - Ident !"quote" + Ident ident"quote" StmtList DiscardStmt Empty diff --git a/tests/rational/trat_init.nim b/tests/rational/trat_init.nim index df29ff6e3..360a48537 100644 --- a/tests/rational/trat_init.nim +++ b/tests/rational/trat_init.nim @@ -1,10 +1,14 @@ discard """ - file: "trat_init.nim" - exitcode: "1" + output: '''true''' """ import rationals var z = Rational[int](num: 0, den: 1) o = initRational(num=1, den=1) a = initRational(1, 2) - r = initRational(1, 0) # this fails - no zero denominator + +try: + var + r = initRational(1, 0) # this fails - no zero denominator +except AssertionError: + echo "true" diff --git a/tests/stdlib/thttpclient.nim b/tests/stdlib/thttpclient.nim index 54588d3f0..fff02722a 100644 --- a/tests/stdlib/thttpclient.nim +++ b/tests/stdlib/thttpclient.nim @@ -3,6 +3,7 @@ discard """ exitcode: 0 output: "OK" disabled: "travis" + disabled: "appveyor" """ import strutils diff --git a/tests/test_nimscript.nims b/tests/test_nimscript.nims index 2500bac73..d3eb9808e 100644 --- a/tests/test_nimscript.nims +++ b/tests/test_nimscript.nims @@ -21,5 +21,6 @@ import subexes import tables import unicode import uri +import macros echo "Nimscript imports are successful." diff --git a/tests/testament/categories.nim b/tests/testament/categories.nim index 68e988975..ca621969f 100644 --- a/tests/testament/categories.nim +++ b/tests/testament/categories.nim @@ -219,9 +219,9 @@ proc debuggerTests(r: var TResults, cat: Category, options: string) = proc jsTests(r: var TResults, cat: Category, options: string) = template test(filename: untyped) = testSpec r, makeTest(filename, options & " -d:nodejs", cat, - actionRun, targetJS) + actionRun), targetJS testSpec r, makeTest(filename, options & " -d:nodejs -d:release", cat, - actionRun, targetJS) + actionRun), targetJS for t in os.walkFiles("tests/js/t*.nim"): test(t) @@ -245,10 +245,10 @@ proc testNimInAction(r: var TResults, cat: Category, options: string) = testSpec r, makeTest(filename, options, cat, action) template testJS(filename: untyped) = - testSpec r, makeTest(filename, options, cat, actionCompile, targetJS) + testSpec r, makeTest(filename, options, cat, actionCompile), targetJS template testCPP(filename: untyped) = - testSpec r, makeTest(filename, options, cat, actionCompile, targetCPP) + testSpec r, makeTest(filename, options, cat, actionCompile), targetCPP let tests = [ "niminaction/Chapter3/ChatApp/src/server", @@ -387,7 +387,7 @@ proc testNimblePackages(r: var TResults, cat: Category, filter: PackageFilter) = installStatus = waitForExitEx(installProcess) installProcess.close if installStatus != QuitSuccess: - r.addResult(test, "", "", reInstallFailed) + r.addResult(test, targetC, "", "", reInstallFailed) continue let @@ -396,12 +396,12 @@ proc testNimblePackages(r: var TResults, cat: Category, filter: PackageFilter) = buildStatus = waitForExitEx(buildProcess) buildProcess.close if buildStatus != QuitSuccess: - r.addResult(test, "", "", reBuildFailed) - r.addResult(test, "", "", reSuccess) - r.addResult(packageFileTest, "", "", reSuccess) + r.addResult(test, targetC, "", "", reBuildFailed) + r.addResult(test, targetC, "", "", reSuccess) + r.addResult(packageFileTest, targetC, "", "", reSuccess) except JsonParsingError: echo("[Warning] - Cannot run nimble tests: Invalid package file.") - r.addResult(packageFileTest, "", "", reBuildFailed) + r.addResult(packageFileTest, targetC, "", "", reBuildFailed) # ---------------------------------------------------------------------------- @@ -420,7 +420,7 @@ proc processSingleTest(r: var TResults, cat: Category, options, test: string) = let test = "tests" & DirSep &.? cat.string / test let target = if cat.string.normalize == "js": targetJS else: targetC - if existsFile(test): testSpec r, makeTest(test, options, cat, target = target) + if existsFile(test): testSpec r, makeTest(test, options, cat), target else: echo "[Warning] - ", test, " test does not exist" proc processCategory(r: var TResults, cat: Category, options: string) = diff --git a/tests/testament/specs.nim b/tests/testament/specs.nim index e5506e796..e8513ab24 100644 --- a/tests/testament/specs.nim +++ b/tests/testament/specs.nim @@ -13,6 +13,7 @@ import parseutils, strutils, os, osproc, streams, parsecfg var compilerPrefix* = "compiler" / "nim " let isTravis = existsEnv("TRAVIS") +let isAppVeyor = existsEnv("APPVEYOR") proc cmdTemplate*(): string = compilerPrefix & "$target --lib:lib --hints:on -d:testing $options $file" @@ -151,6 +152,7 @@ proc parseSpec*(filename: string): TSpec = result.sortoutput = parseCfgBool(e.value) of "exitcode": discard parseInt(e.value, result.exitCode) + result.action = actionRun of "msg": result.msg = e.value if result.action != actionRun: @@ -178,6 +180,8 @@ proc parseSpec*(filename: string): TSpec = when defined(posix): result.err = reIgnored of "travis": if isTravis: result.err = reIgnored + of "appveyor": + if isAppVeyor: result.err = reIgnored else: raise newException(ValueError, "cannot interpret as a bool: " & e.value) of "cmd": diff --git a/tests/testament/tester.nim b/tests/testament/tester.nim index d75c9d770..2f0485135 100644 --- a/tests/testament/tester.nim +++ b/tests/testament/tester.nim @@ -44,7 +44,6 @@ type name: string cat: Category options: string - target: TTarget action: TTestAction startTime: float @@ -155,13 +154,13 @@ proc `$`(x: TResults): string = "Tests skipped: $2 / $3 <br />\n") % [$x.passed, $x.skipped, $x.total] -proc addResult(r: var TResults, test: TTest, +proc addResult(r: var TResults, test: TTest, target: TTarget, expected, given: string, success: TResultEnum) = - let name = test.name.extractFilename & test.options + let name = test.name.extractFilename & " " & $target & test.options let duration = epochTime() - test.startTime backend.writeTestResult(name = name, category = test.cat.string, - target = $test.target, + target = $target, action = $test.action, result = $success, expected = expected, @@ -197,29 +196,29 @@ proc addResult(r: var TResults, test: TTest, discard waitForExit(p) close(p) -proc cmpMsgs(r: var TResults, expected, given: TSpec, test: TTest) = +proc cmpMsgs(r: var TResults, expected, given: TSpec, test: TTest, target: TTarget) = if strip(expected.msg) notin strip(given.msg): - r.addResult(test, expected.msg, given.msg, reMsgsDiffer) + r.addResult(test, target, expected.msg, given.msg, reMsgsDiffer) elif expected.nimout.len > 0 and expected.nimout.normalizeMsg notin given.nimout.normalizeMsg: - r.addResult(test, expected.nimout, given.nimout, reMsgsDiffer) + r.addResult(test, target, expected.nimout, given.nimout, reMsgsDiffer) elif expected.tfile == "" and extractFilename(expected.file) != extractFilename(given.file) and "internal error:" notin expected.msg: - r.addResult(test, expected.file, given.file, reFilesDiffer) + r.addResult(test, target, expected.file, given.file, reFilesDiffer) elif expected.line != given.line and expected.line != 0 or expected.column != given.column and expected.column != 0: - r.addResult(test, $expected.line & ':' & $expected.column, + r.addResult(test, target, $expected.line & ':' & $expected.column, $given.line & ':' & $given.column, reLinesDiffer) elif expected.tfile != "" and extractFilename(expected.tfile) != extractFilename(given.tfile) and "internal error:" notin expected.msg: - r.addResult(test, expected.tfile, given.tfile, reFilesDiffer) + r.addResult(test, target, expected.tfile, given.tfile, reFilesDiffer) elif expected.tline != given.tline and expected.tline != 0 or expected.tcolumn != given.tcolumn and expected.tcolumn != 0: - r.addResult(test, $expected.tline & ':' & $expected.tcolumn, + r.addResult(test, target, $expected.tline & ':' & $expected.tcolumn, $given.tline & ':' & $given.tcolumn, reLinesDiffer) else: - r.addResult(test, expected.msg, given.msg, reSuccess) + r.addResult(test, target, expected.msg, given.msg, reSuccess) inc(r.passed) proc generatedFile(path, name: string, target: TTarget): string = @@ -231,11 +230,11 @@ proc generatedFile(path, name: string, target: TTarget): string = proc needsCodegenCheck(spec: TSpec): bool = result = spec.maxCodeSize > 0 or spec.ccodeCheck.len > 0 -proc codegenCheck(test: TTest, spec: TSpec, expectedMsg: var string, +proc codegenCheck(test: TTest, target: TTarget, spec: TSpec, expectedMsg: var string, given: var TSpec) = try: let (path, name, _) = test.name.splitFile - let genFile = generatedFile(path, name, test.target) + let genFile = generatedFile(path, name, target) let contents = readFile(genFile).string let check = spec.ccodeCheck if check.len > 0: @@ -267,13 +266,13 @@ proc makeDeterministic(s: string): string = sort(x, system.cmp) result = join(x, "\n") -proc compilerOutputTests(test: TTest, given: var TSpec, expected: TSpec; - r: var TResults) = +proc compilerOutputTests(test: TTest, target: TTarget, given: var TSpec, + expected: TSpec; r: var TResults) = var expectedmsg: string = "" var givenmsg: string = "" if given.err == reSuccess: if expected.needsCodegenCheck: - codegenCheck(test, expected, expectedmsg, given) + codegenCheck(test, target, expected, expectedmsg, given) givenmsg = given.msg if expected.nimout.len > 0: expectedmsg = expected.nimout @@ -282,7 +281,7 @@ proc compilerOutputTests(test: TTest, given: var TSpec, expected: TSpec; else: givenmsg = given.nimout.strip if given.err == reSuccess: inc(r.passed) - r.addResult(test, expectedmsg, givenmsg, given.err) + r.addResult(test, target, expectedmsg, givenmsg, given.err) proc analyzeAndConsolidateOutput(s: string): string = result = "" @@ -298,13 +297,7 @@ proc analyzeAndConsolidateOutput(s: string): string = result = substr(rows[i], pos) return -proc testSpec(r: var TResults, test: TTest) = - # major entry point for a single test - if test.target notin targets: - r.addResult(test, "", "", reIgnored) - inc(r.skipped) - return - +proc testSpec(r: var TResults, test: TTest, target = targetC) = let tname = test.name.addFileExt(".nim") #echo "TESTING ", tname inc(r.total) @@ -316,83 +309,92 @@ proc testSpec(r: var TResults, test: TTest) = expected.action = actionRunNoSpec if expected.err == reIgnored: - r.addResult(test, "", "", reIgnored) + r.addResult(test, target, "", "", reIgnored) inc(r.skipped) return - case expected.action - of actionCompile: - var given = callCompiler(expected.cmd, test.name, - test.options & " --stdout --hint[Path]:off --hint[Processing]:off", - test.target) - compilerOutputTests(test, given, expected, r) - of actionRun, actionRunNoSpec: - # In this branch of code "early return" pattern is clearer than deep - # nested conditionals - the empty rows in between to clarify the "danger" - var given = callCompiler(expected.cmd, test.name, test.options, - test.target) - - if given.err != reSuccess: - r.addResult(test, "", given.msg, given.err) - return - - let isJsTarget = test.target == targetJS - var exeFile: string - if isJsTarget: - let (dir, file, _) = splitFile(tname) - exeFile = dir / "nimcache" / file & ".js" # *TODO* hardcoded "nimcache" - else: - exeFile = changeFileExt(tname, ExeExt) - - if not existsFile(exeFile): - r.addResult(test, expected.outp, "executable not found", reExeNotFound) - return - - let nodejs = if isJsTarget: findNodeJs() else: "" - if isJsTarget and nodejs == "": - r.addResult(test, expected.outp, "nodejs binary not in PATH", - reExeNotFound) - return - - let exeCmd = (if isJsTarget: nodejs & " " else: "") & exeFile - var (buf, exitCode) = execCmdEx(exeCmd, options = {poStdErrToStdOut}) - - # Treat all failure codes from nodejs as 1. Older versions of nodejs used - # to return other codes, but for us it is sufficient to know that it's not 0. - if exitCode != 0: exitCode = 1 - - let bufB = if expected.sortoutput: makeDeterministic(strip(buf.string)) - else: strip(buf.string) - let expectedOut = strip(expected.outp) - - if exitCode != expected.exitCode: - r.addResult(test, "exitcode: " & $expected.exitCode, - "exitcode: " & $exitCode & "\n\nOutput:\n" & - analyzeAndConsolidateOutput(bufB), - reExitCodesDiffer) - return - - if bufB != expectedOut and expected.action != actionRunNoSpec: - if not (expected.substr and expectedOut in bufB): - given.err = reOutputsDiffer - r.addResult(test, expected.outp, bufB, reOutputsDiffer) - return - - compilerOutputTests(test, given, expected, r) - return - - of actionReject: - var given = callCompiler(expected.cmd, test.name, test.options, - test.target) - cmpMsgs(r, expected, given, test) - return - -proc testNoSpec(r: var TResults, test: TTest) = + if expected.targets == {}: + expected.targets.incl(target) + + for target in expected.targets: + if target notin targets: + r.addResult(test, target, "", "", reIgnored) + inc(r.skipped) + continue + + case expected.action + of actionCompile: + var given = callCompiler(expected.cmd, test.name, + test.options & " --stdout --hint[Path]:off --hint[Processing]:off", + target) + compilerOutputTests(test, target, given, expected, r) + of actionRun, actionRunNoSpec: + # In this branch of code "early return" pattern is clearer than deep + # nested conditionals - the empty rows in between to clarify the "danger" + var given = callCompiler(expected.cmd, test.name, test.options, + target) + + if given.err != reSuccess: + r.addResult(test, target, "", given.msg, given.err) + continue + + let isJsTarget = target == targetJS + var exeFile: string + if isJsTarget: + let (dir, file, _) = splitFile(tname) + exeFile = dir / "nimcache" / file & ".js" # *TODO* hardcoded "nimcache" + else: + exeFile = changeFileExt(tname, ExeExt) + + if not existsFile(exeFile): + r.addResult(test, target, expected.outp, "executable not found", reExeNotFound) + continue + + let nodejs = if isJsTarget: findNodeJs() else: "" + if isJsTarget and nodejs == "": + r.addResult(test, target, expected.outp, "nodejs binary not in PATH", + reExeNotFound) + continue + + let exeCmd = (if isJsTarget: nodejs & " " else: "") & exeFile + var (buf, exitCode) = execCmdEx(exeCmd, options = {poStdErrToStdOut}) + + # Treat all failure codes from nodejs as 1. Older versions of nodejs used + # to return other codes, but for us it is sufficient to know that it's not 0. + if exitCode != 0: exitCode = 1 + + let bufB = if expected.sortoutput: makeDeterministic(strip(buf.string)) + else: strip(buf.string) + let expectedOut = strip(expected.outp) + + if exitCode != expected.exitCode: + r.addResult(test, target, "exitcode: " & $expected.exitCode, + "exitcode: " & $exitCode & "\n\nOutput:\n" & + analyzeAndConsolidateOutput(bufB), + reExitCodesDiffer) + continue + + if bufB != expectedOut and expected.action != actionRunNoSpec: + if not (expected.substr and expectedOut in bufB): + given.err = reOutputsDiffer + r.addResult(test, target, expected.outp, bufB, reOutputsDiffer) + continue + + compilerOutputTests(test, target, given, expected, r) + continue + + of actionReject: + var given = callCompiler(expected.cmd, test.name, test.options, + target) + cmpMsgs(r, expected, given, test, target) + continue + +proc testNoSpec(r: var TResults, test: TTest, target = targetC) = # does not extract the spec because the file is not supposed to have any #let tname = test.name.addFileExt(".nim") inc(r.total) - let given = callCompiler(cmdTemplate(), test.name, test.options, test.target) - r.addResult(test, "", given.msg, given.err) + let given = callCompiler(cmdTemplate(), test.name, test.options, target) + r.addResult(test, target, "", given.msg, given.err) if given.err == reSuccess: inc(r.passed) proc testC(r: var TResults, test: TTest) = @@ -400,9 +402,9 @@ proc testC(r: var TResults, test: TTest) = let tname = test.name.addFileExt(".c") inc(r.total) styledEcho "Processing ", fgCyan, extractFilename(tname) - var given = callCCompiler(cmdTemplate(), test.name & ".c", test.options, test.target) + var given = callCCompiler(cmdTemplate(), test.name & ".c", test.options, targetC) if given.err != reSuccess: - r.addResult(test, "", given.msg, given.err) + r.addResult(test, targetC, "", given.msg, given.err) elif test.action == actionRun: let exeFile = changeFileExt(test.name, ExeExt) var (_, exitCode) = execCmdEx(exeFile, options = {poStdErrToStdOut, poUsePath}) @@ -410,10 +412,10 @@ proc testC(r: var TResults, test: TTest) = if given.err == reSuccess: inc(r.passed) proc makeTest(test, options: string, cat: Category, action = actionCompile, - target = targetC, env: string = ""): TTest = + env: string = ""): TTest = # start with 'actionCompile', will be overwritten in the spec: result = TTest(cat: cat, name: test, options: options, - target: target, action: action, startTime: epochTime()) + action: action, startTime: epochTime()) when defined(windows): const @@ -463,7 +465,10 @@ proc main() = let testsDir = "tests" & DirSep var myself = quoteShell(findExe("tests" / "testament" / "tester")) if targetsStr.len > 0: - myself &= " '--targets:" & targetsStr & "'" + myself &= " " & quoteShell("--targets:" & targetsStr) + + myself &= " " & quoteShell("--nim:" & compilerPrefix) + var cmds: seq[string] = @[] let rest = if p.cmdLineRest.string.len > 0: " " & p.cmdLineRest.string else: "" for kind, dir in walkDir(testsDir): diff --git a/tests/types/tinheritref.nim b/tests/types/tinheritref.nim index ecd62a06f..00af0538d 100644 --- a/tests/types/tinheritref.nim +++ b/tests/types/tinheritref.nim @@ -1,5 +1,7 @@ discard """ - output: "23" + output: '''23 +1.5 +''' """ # bug #554, #179 @@ -25,3 +27,24 @@ type var it: TKeysIterator[int, string] = nil +#bug #5521 +type + Texture = enum + Smooth + Coarse + + FruitBase = object of RootObj + color: int + case kind: Texture + of Smooth: + skin: float64 + of Coarse: + grain: int + + Apple = object of FruitBase + width: int + taste: float64 + +var x = Apple(kind: Smooth, skin: 1.5) +var u = x.skin +echo u diff --git a/tests/types/tyet_another_generic_regression.nim b/tests/types/tyet_another_generic_regression.nim new file mode 100644 index 000000000..914166e06 --- /dev/null +++ b/tests/types/tyet_another_generic_regression.nim @@ -0,0 +1,13 @@ +import system + +type Bar[T] = ref object + value: T + +type types = int32|int64 # if I change this to just int32 or int64 it works (compiles) + +# if I replace Bar everywhere with seq it also compiles fine +proc Foo[T: Bar[types]](): T = + when T is Bar: nil + +discard Foo[Bar[int32]]() +#bug #6073 diff --git a/tests/vm/tnimnode.nim b/tests/vm/tnimnode.nim index 60e3189b0..0614b9807 100644 --- a/tests/vm/tnimnode.nim +++ b/tests/vm/tnimnode.nim @@ -26,12 +26,12 @@ proc checkNode(arg: NimNode; name: string): void {. compileTime .} = seqAppend.add(arg) # bit this creates a copy arg.add newCall(ident"echo", newLit("Hello World")) - assertEq arg.lispRepr , """StmtList(DiscardStmt(Empty()), Call(Ident(!"echo"), StrLit(Hello World)))""" - assertEq node.lispRepr , """StmtList(DiscardStmt(Empty()), Call(Ident(!"echo"), StrLit(Hello World)))""" - assertEq nodeArray[0].lispRepr , """StmtList(DiscardStmt(Empty()), Call(Ident(!"echo"), StrLit(Hello World)))""" - assertEq nodeSeq[0].lispRepr , """StmtList(DiscardStmt(Empty()), Call(Ident(!"echo"), StrLit(Hello World)))""" - assertEq seqAppend[0].lispRepr , """StmtList(DiscardStmt(Empty()), Call(Ident(!"echo"), StrLit(Hello World)))""" - assertEq seqAppend[1].lispRepr , """StmtList(DiscardStmt(Empty()), Call(Ident(!"echo"), StrLit(Hello World)))""" + assertEq arg.lispRepr , """StmtList(DiscardStmt(Empty()), Call(Ident(ident"echo"), StrLit(Hello World)))""" + assertEq node.lispRepr , """StmtList(DiscardStmt(Empty()), Call(Ident(ident"echo"), StrLit(Hello World)))""" + assertEq nodeArray[0].lispRepr , """StmtList(DiscardStmt(Empty()), Call(Ident(ident"echo"), StrLit(Hello World)))""" + assertEq nodeSeq[0].lispRepr , """StmtList(DiscardStmt(Empty()), Call(Ident(ident"echo"), StrLit(Hello World)))""" + assertEq seqAppend[0].lispRepr , """StmtList(DiscardStmt(Empty()), Call(Ident(ident"echo"), StrLit(Hello World)))""" + assertEq seqAppend[1].lispRepr , """StmtList(DiscardStmt(Empty()), Call(Ident(ident"echo"), StrLit(Hello World)))""" echo "OK" diff --git a/todo.txt b/todo.txt index ba137039e..a30c23ce3 100644 --- a/todo.txt +++ b/todo.txt @@ -1,9 +1,6 @@ version 1.0 battle plan ======================= -- make 'doc2' an alias for 'doc' and introduce 'doc0' -- make 'break' not leave named blocks -- make FlowVar compatible to Futures - fix "high priority" bugs - try to fix as many compiler crashes as reasonable @@ -11,6 +8,8 @@ version 1.0 battle plan Not critical for 1.0 ==================== +- make 'break' not leave named blocks +- make FlowVar compatible to Futures - make 'not nil' the default (produce warnings instead of errors for a smooth migration path) - case objects needs to be safe and need to support pattern matching diff --git a/tools/niminst/buildsh.tmpl b/tools/niminst/buildsh.tmpl index ec20566ac..3e7d8ae6e 100644 --- a/tools/niminst/buildsh.tmpl +++ b/tools/niminst/buildsh.tmpl @@ -144,6 +144,8 @@ case $ucpu in mycpu="sparc64" fi ;; + *ppc64le* ) + mycpu="powerpc64el" ;; *ppc64* ) if [ "$myos" = "linux" ] ; then COMP_FLAGS="$COMP_FLAGS -m64" diff --git a/tools/niminst/makefile.tmpl b/tools/niminst/makefile.tmpl index 4a20680e0..fa96117b8 100644 --- a/tools/niminst/makefile.tmpl +++ b/tools/niminst/makefile.tmpl @@ -17,7 +17,6 @@ endif ucpu := $(shell sh -c 'uname -m | tr "[:upper:]" "[:lower:]"') uos := $(shell sh -c 'uname | tr "[:upper:]" "[:lower:]"') -uosname := $(shell sh -c 'uname -o | tr "[:upper:]" "[:lower:]"') ifeq ($(uos),linux) myos = linux @@ -65,15 +64,10 @@ endif ifeq ($(uos),haiku) myos = haiku endif -ifndef uos +ifndef myos $(error unknown operating system: $(uos)) endif -ifeq ($(uosname),android) - myos = android - LINK_FLAGS += -landroid-glob -endif - ifeq ($(ucpu),i386) mycpu = i386 endif @@ -107,6 +101,9 @@ endif ifeq ($(ucpu),sun) mycpu = sparc endif +ifeq ($(ucpu),ppc64le) + mycpu = powerpc64el +endif ifeq ($(ucpu),ppc64) mycpu = powerpc64 ifeq ($(myos),linux) @@ -138,7 +135,16 @@ endif ifeq ($(ucpu),armv6l) mycpu = arm endif -ifndef ucpu +ifeq ($(ucpu),armv7l) + mycpu = arm +endif +ifeq ($(ucpu),armv7hl) + mycpu = arm +endif +ifeq ($(ucpu),aarch64) + mycpu = arm64 +endif +ifndef mycpu $(error unknown processor: $(ucpu)) endif diff --git a/tools/nimweb.nim b/tools/nimweb.nim index a082520e0..ffb1ac4e3 100644 --- a/tools/nimweb.nim +++ b/tools/nimweb.nim @@ -307,7 +307,7 @@ proc buildDoc(c: var TConfigData, destPath: string) = destPath / changeFileExt(splitFile(d).name, "html"), d] i.inc for d in items(c.srcdoc): - commands[i] = findNim() & " doc $# --git.url:$# -o:$# --index:on $#" % + commands[i] = findNim() & " doc0 $# --git.url:$# -o:$# --index:on $#" % [c.nimArgs, gitRepo, destPath / changeFileExt(splitFile(d).name, "html"), d] i.inc diff --git a/web/website.ini b/web/website.ini index 6142bcddb..be9ec85f0 100644 --- a/web/website.ini +++ b/web/website.ini @@ -60,7 +60,7 @@ srcdoc2: "packages/docutils/rst;packages/docutils/rstast" srcdoc2: "packages/docutils/rstgen;pure/logging;pure/options;pure/asyncdispatch;pure/asyncnet" srcdoc2: "pure/nativesockets;pure/asynchttpserver;pure/net;pure/selectors;pure/future" srcdoc2: "deprecated/pure/ftpclient;pure/collections/chains" -srcdoc2: "pure/asyncfile;pure/asyncftpclient" +srcdoc2: "pure/asyncfile;pure/asyncftpclient;pure/ioselectors" srcdoc2: "pure/md5;pure/rationals" srcdoc2: "posix/posix;pure/distros;pure/oswalkdir" srcdoc2: "pure/collections/heapqueue" |