diff options
185 files changed, 3559 insertions, 1437 deletions
diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 000000000..c027386f5 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,29 @@ +sudo: false +language: c +os: + - linux +addons: + apt: + packages: + - libcurl4-openssl-dev + - libsdl1.2-dev +before_script: + - set -e + - wget http://flatassembler.net/fasm-1.71.39.tgz + - tar xvf fasm-1.71.39.tgz + - git clone --depth 1 https://github.com/nim-lang/csources.git + - cd csources + - sh build.sh + - cd .. + - sed -i -e 's,cc = gcc,cc = clang,' config/nim.cfg + - export PATH=$(pwd)/bin:$(pwd)/fasm:$PATH +script: + - nim c koch + - ./koch boot + - ./koch boot -d:release + - nim e install_nimble.nims + - nimble update + - nimble install zip + - nimble install opengl + - nimble install sdl1 + - ./koch test all --pedantic diff --git a/bootstrap.sh b/bootstrap.sh index ade74a9aa..7f19c2440 100755 --- a/bootstrap.sh +++ b/bootstrap.sh @@ -12,8 +12,11 @@ cd ".." ./bin/nim c koch ./koch boot -d:release +./koch geninstall -cp -f install.sh.template install.sh -chmod +x install.sh +set +x + +echo +echo 'Install Nim using "./install.sh <dir>" or "sudo ./install.sh <dir>".' exit 0 diff --git a/compiler/aliases.nim b/compiler/aliases.nim index 3d3fc9a79..4b592ee60 100644 --- a/compiler/aliases.nim +++ b/compiler/aliases.nim @@ -146,7 +146,7 @@ proc isPartOf*(a, b: PNode): TAnalysisResult = # go down recursively; this is quite demanding: const Ix0Kinds = {nkDotExpr, nkBracketExpr, nkObjUpConv, nkObjDownConv, - nkCheckedFieldExpr} + nkCheckedFieldExpr, nkHiddenAddr} Ix1Kinds = {nkHiddenStdConv, nkHiddenSubConv, nkConv} DerefKinds = {nkHiddenDeref, nkDerefExpr} case b.kind diff --git a/compiler/ast.nim b/compiler/ast.nim index 177b5c5c7..25958f580 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -477,6 +477,8 @@ type # wildcard type. tfHasAsgn # type has overloaded assignment operator tfBorrowDot # distinct type borrows '.' + tfTriggersCompileTime # uses the NimNode type which make the proc + # implicitly '.compiletime' TTypeFlags* = set[TTypeFlag] @@ -537,7 +539,7 @@ const type TMagic* = enum # symbols that require compiler magic: mNone, - mDefined, mDefinedInScope, mCompiles, + mDefined, mDefinedInScope, mCompiles, mArrGet, mArrPut, mAsgn, mLow, mHigh, mSizeOf, mTypeTrait, mIs, mOf, mAddr, mTypeOf, mRoof, mPlugin, mEcho, mShallowCopy, mSlurp, mStaticExec, mParseExprToAst, mParseStmtToAst, mExpandToAst, mQuoteAst, @@ -614,6 +616,7 @@ const ctfeWhitelist* = {mNone, mUnaryLt, mSucc, mPred, mInc, mDec, mOrd, mLengthOpenArray, mLengthStr, mLengthArray, mLengthSeq, mXLenStr, mXLenSeq, + mArrGet, mArrPut, mAsgn, mIncl, mExcl, mCard, mChr, mAddI, mSubI, mMulI, mDivI, mModI, mAddF64, mSubF64, mMulF64, mDivF64, @@ -709,6 +712,7 @@ type lfSingleUse # no location yet and will only be used once TStorageLoc* = enum OnUnknown, # location is unknown (stack, heap or static) + OnStatic, # in a static section OnStack, # location is on hardware stack OnHeap # location is on heap or global # (reference counting needed) @@ -734,6 +738,8 @@ type name*: Rope path*: PNode # can be a string literal! + CompilesId* = int ## id that is used for the caching logic within + ## ``system.compiles``. See the seminst module. TInstantiation* = object sym*: PSym concreteTypes*: seq[PType] @@ -741,6 +747,7 @@ type # needed in caas mode for purging the cache # XXX: it's possible to switch to a # simple ref count here + compilesId*: CompilesId PInstantiation* = ref TInstantiation @@ -777,6 +784,7 @@ type tab*: TStrTable # interface table for modules of skLet, skVar, skField, skForVar: guard*: PSym + bitsize*: int else: nil magic*: TMagic typ*: PType @@ -1378,6 +1386,9 @@ proc propagateToOwner*(owner, elem: PType) = o2.flags.incl tfHasAsgn owner.flags.incl tfHasAsgn + if tfTriggersCompileTime in elem.flags: + owner.flags.incl tfTriggersCompileTime + if owner.kind notin {tyProc, tyGenericInst, tyGenericBody, tyGenericInvocation, tyPtr}: let elemB = elem.skipTypes({tyGenericInst}) @@ -1578,6 +1589,14 @@ proc makeStmtList*(n: PNode): PNode = result = newNodeI(nkStmtList, n.info) result.add n +proc skipStmtList*(n: PNode): PNode = + if n.kind in {nkStmtList, nkStmtListExpr}: + for i in 0 .. n.len-2: + if n[i].kind notin {nkEmpty, nkCommentStmt}: return n + result = n.lastSon + else: + result = n + proc createMagic*(name: string, m: TMagic): PSym = result = newSym(skProc, getIdent(name), nil, unknownLineInfo()) result.magic = m @@ -1585,3 +1604,10 @@ proc createMagic*(name: string, m: TMagic): PSym = let opNot* = createMagic("not", mNot) opContains* = createMagic("contains", mInSet) + +when false: + proc containsNil*(n: PNode): bool = + # only for debugging + if n.isNil: return true + for i in 0 ..< n.safeLen: + if n[i].containsNil: return true diff --git a/compiler/ccgexprs.nim b/compiler/ccgexprs.nim index ba8ced52a..6d69dafa5 100644 --- a/compiler/ccgexprs.nim +++ b/compiler/ccgexprs.nim @@ -221,7 +221,7 @@ proc optAsgnLoc(a: TLoc, t: PType, field: Rope): TLoc = proc genOptAsgnTuple(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) = let newflags = - if src.k == locData: + if src.s == OnStatic: flags + {needToCopy} elif tfShallow in dest.t.flags: flags - {needToCopy} @@ -238,7 +238,7 @@ proc genOptAsgnObject(p: BProc, dest, src: TLoc, flags: TAssignmentFlags, t: PNode) = if t == nil: return let newflags = - if src.k == locData: + if src.s == OnStatic: flags + {needToCopy} elif tfShallow in dest.t.flags: flags - {needToCopy} @@ -287,13 +287,13 @@ proc genAssignment(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) = of tyRef: genRefAssign(p, dest, src, flags) of tySequence: - if needToCopy notin flags and src.k != locData: + if needToCopy notin flags and src.s != OnStatic: genRefAssign(p, dest, src, flags) else: linefmt(p, cpsStmts, "#genericSeqAssign($1, $2, $3);$n", addrLoc(dest), rdLoc(src), genTypeInfo(p.module, dest.t)) of tyString: - if needToCopy notin flags and src.k != locData: + if needToCopy notin flags and src.s != OnStatic: genRefAssign(p, dest, src, flags) else: if dest.s == OnStack or not usesNativeGC(): @@ -413,7 +413,7 @@ proc putDataIntoDest(p: BProc, d: var TLoc, t: PType, r: Rope) = var a: TLoc if d.k != locNone: # need to generate an assignment here - initLoc(a, locData, t, OnUnknown) + initLoc(a, locData, t, OnStatic) a.r = r if lfNoDeepCopy in d.flags: genAssignment(p, d, a, {}) else: genAssignment(p, d, a, {needToCopy}) @@ -424,11 +424,11 @@ proc putDataIntoDest(p: BProc, d: var TLoc, t: PType, r: Rope) = d.t = t d.r = r -proc putIntoDest(p: BProc, d: var TLoc, t: PType, r: Rope) = +proc putIntoDest(p: BProc, d: var TLoc, t: PType, r: Rope; s=OnUnknown) = var a: TLoc if d.k != locNone: # need to generate an assignment here - initLoc(a, locExpr, t, OnUnknown) + initLoc(a, locExpr, t, s) a.r = r if lfNoDeepCopy in d.flags: genAssignment(p, d, a, {}) else: genAssignment(p, d, a, {needToCopy}) @@ -685,7 +685,7 @@ proc genDeref(p: BProc, e: PNode, d: var TLoc; enforceDeref=false) = d.s = OnUnknown if tfVarIsPtr notin typ.flags and p.module.compileToCpp and e.kind == nkHiddenDeref: - putIntoDest(p, d, e.typ, rdLoc(a)) + putIntoDest(p, d, e.typ, rdLoc(a), a.s) return of tyPtr: d.s = OnUnknown # BUGFIX! @@ -694,7 +694,7 @@ proc genDeref(p: BProc, e: PNode, d: var TLoc; enforceDeref=false) = let typ = skipTypes(a.t, abstractInst) if typ.kind == tyVar and tfVarIsPtr notin typ.flags and e.kind == nkHiddenDeref: - putIntoDest(p, d, e.typ, rdLoc(a)) + putIntoDest(p, d, e.typ, rdLoc(a), a.s) return if enforceDeref and mt == ctPtrToArray: # we lie about the type for better C interop: 'ptr array[3,T]' is @@ -702,23 +702,23 @@ proc genDeref(p: BProc, e: PNode, d: var TLoc; enforceDeref=false) = # See tmissingderef. So we get rid of the deref instead. The codegen # ends up using 'memcpy' for the array assignment, # so the '&' and '*' cancel out: - putIntoDest(p, d, a.t.sons[0], rdLoc(a)) + putIntoDest(p, d, a.t.sons[0], rdLoc(a), a.s) else: - putIntoDest(p, d, e.typ, "(*$1)" % [rdLoc(a)]) + putIntoDest(p, d, e.typ, "(*$1)" % [rdLoc(a)], a.s) proc genAddr(p: BProc, e: PNode, d: var TLoc) = # careful 'addr(myptrToArray)' needs to get the ampersand: if e.sons[0].typ.skipTypes(abstractInst).kind in {tyRef, tyPtr}: var a: TLoc initLocExpr(p, e.sons[0], a) - putIntoDest(p, d, e.typ, "&" & a.r) + putIntoDest(p, d, e.typ, "&" & a.r, a.s) #Message(e.info, warnUser, "HERE NEW &") elif mapType(e.sons[0].typ) == ctArray or isCppRef(p, e.sons[0].typ): expr(p, e.sons[0], d) else: var a: TLoc initLocExpr(p, e.sons[0], a) - putIntoDest(p, d, e.typ, addrLoc(a)) + putIntoDest(p, d, e.typ, addrLoc(a), a.s) template inheritLocation(d: var TLoc, a: TLoc) = if d.k == locNone: d.s = a.s @@ -745,7 +745,18 @@ proc genTupleElem(p: BProc, e: PNode, d: var TLoc) = of nkIntLit..nkUInt64Lit: i = int(e.sons[1].intVal) else: internalError(e.info, "genTupleElem") addf(r, ".Field$1", [rope(i)]) - putIntoDest(p, d, ty.sons[i], r) + putIntoDest(p, d, ty.sons[i], r, a.s) + +proc lookupFieldAgain(p: BProc, ty: PType; field: PSym; r: var Rope): PSym = + var ty = ty + assert r != nil + while ty != nil: + assert(ty.kind in {tyTuple, tyObject}) + result = lookupInRecord(ty.n, field.name) + if result != nil: break + if not p.module.compileToCpp: add(r, ".Sup") + ty = getUniqueType(ty.sons[0]) + if result == nil: internalError(field.info, "genCheckedRecordField") proc genRecordField(p: BProc, e: PNode, d: var TLoc) = var a: TLoc @@ -756,25 +767,18 @@ proc genRecordField(p: BProc, e: PNode, d: var TLoc) = # we found a unique tuple type which lacks field information # so we use Field$i addf(r, ".Field$1", [rope(f.position)]) - putIntoDest(p, d, f.typ, r) + putIntoDest(p, d, f.typ, r, a.s) else: - var field: PSym = nil - while ty != nil: - if ty.kind notin {tyTuple, tyObject}: - internalError(e.info, "genRecordField") - field = lookupInRecord(ty.n, f.name) - if field != nil: break - if not p.module.compileToCpp: add(r, ".Sup") - ty = getUniqueType(ty.sons[0]) - if field == nil: internalError(e.info, "genRecordField 2 ") + let field = lookupFieldAgain(p, ty, f, r) if field.loc.r == nil: internalError(e.info, "genRecordField 3") addf(r, ".$1", [field.loc.r]) - putIntoDest(p, d, field.typ, r) + putIntoDest(p, d, field.typ, r, a.s) #d.s = a.s proc genInExprAux(p: BProc, e: PNode, a, b, d: var TLoc) -proc genFieldCheck(p: BProc, e: PNode, obj: Rope, field: PSym) = +proc genFieldCheck(p: BProc, e: PNode, obj: Rope, field: PSym; + origTy: PType) = var test, u, v: TLoc for i in countup(1, sonsLen(e) - 1): var it = e.sons[i] @@ -786,8 +790,12 @@ proc genFieldCheck(p: BProc, e: PNode, obj: Rope, field: PSym) = assert(disc.kind == nkSym) initLoc(test, locNone, it.typ, OnStack) initLocExpr(p, it.sons[1], u) - initLoc(v, locExpr, disc.typ, OnUnknown) - v.r = "$1.$2" % [obj, disc.sym.loc.r] + var o = obj + let d = lookupFieldAgain(p, origTy, disc.sym, o) + initLoc(v, locExpr, d.typ, OnUnknown) + v.r = o + v.r.add(".") + v.r.add(d.loc.r) genInExprAux(p, it, u, v, test) let id = nodeTableTestOrSet(p.module.dataCache, newStrNode(nkStrLit, field.name.s), gBackendId) @@ -804,27 +812,16 @@ proc genFieldCheck(p: BProc, e: PNode, obj: Rope, field: PSym) = proc genCheckedRecordField(p: BProc, e: PNode, d: var TLoc) = if optFieldCheck in p.options: - var - a: TLoc - f, field: PSym - ty: PType - r: Rope - ty = genRecordFieldAux(p, e.sons[0], d, a) - r = rdLoc(a) - f = e.sons[0].sons[1].sym - field = nil - while ty != nil: - assert(ty.kind in {tyTuple, tyObject}) - field = lookupInRecord(ty.n, f.name) - if field != nil: break - if not p.module.compileToCpp: add(r, ".Sup") - ty = getUniqueType(ty.sons[0]) - if field == nil: internalError(e.info, "genCheckedRecordField") + var a: TLoc + let ty = genRecordFieldAux(p, e.sons[0], d, a) + var r = rdLoc(a) + let f = e.sons[0].sons[1].sym + let field = lookupFieldAgain(p, ty, f, r) if field.loc.r == nil: internalError(e.info, "genCheckedRecordField") # generate the checks: - genFieldCheck(p, e, r, field) + genFieldCheck(p, e, r, field, ty) add(r, rfmt(nil, ".$1", field.loc.r)) - putIntoDest(p, d, field.typ, r) + putIntoDest(p, d, field.typ, r, a.s) else: genRecordField(p, e.sons[0], d) @@ -851,7 +848,7 @@ proc genArrayElem(p: BProc, x, y: PNode, d: var TLoc) = localError(x.info, errIndexOutOfBounds) d.inheritLocation(a) putIntoDest(p, d, elemType(skipTypes(ty, abstractVar)), - rfmt(nil, "$1[($2)- $3]", rdLoc(a), rdCharLoc(b), first)) + rfmt(nil, "$1[($2)- $3]", rdLoc(a), rdCharLoc(b), first), a.s) proc genCStringElem(p: BProc, x, y: PNode, d: var TLoc) = var a, b: TLoc @@ -860,7 +857,7 @@ proc genCStringElem(p: BProc, x, y: PNode, d: var TLoc) = var ty = skipTypes(a.t, abstractVarRange) if d.k == locNone: d.s = a.s putIntoDest(p, d, elemType(skipTypes(ty, abstractVar)), - rfmt(nil, "$1[$2]", rdLoc(a), rdCharLoc(b))) + rfmt(nil, "$1[$2]", rdLoc(a), rdCharLoc(b)), a.s) proc genOpenArrayElem(p: BProc, x, y: PNode, d: var TLoc) = var a, b: TLoc @@ -871,7 +868,7 @@ proc genOpenArrayElem(p: BProc, x, y: PNode, d: var TLoc) = rdLoc(b), rdLoc(a)) # BUGFIX: ``>=`` and not ``>``! if d.k == locNone: d.s = a.s putIntoDest(p, d, elemType(skipTypes(a.t, abstractVar)), - rfmt(nil, "$1[$2]", rdLoc(a), rdCharLoc(b))) + rfmt(nil, "$1[$2]", rdLoc(a), rdCharLoc(b)), a.s) proc genSeqElem(p: BProc, x, y: PNode, d: var TLoc) = var a, b: TLoc @@ -894,7 +891,7 @@ proc genSeqElem(p: BProc, x, y: PNode, d: var TLoc) = if skipTypes(a.t, abstractVar).kind in {tyRef, tyPtr}: a.r = rfmt(nil, "(*$1)", a.r) putIntoDest(p, d, elemType(skipTypes(a.t, abstractVar)), - rfmt(nil, "$1->data[$2]", rdLoc(a), rdCharLoc(b))) + rfmt(nil, "$1->data[$2]", rdLoc(a), rdCharLoc(b)), a.s) proc genBracketExpr(p: BProc; n: PNode; d: var TLoc) = var ty = skipTypes(n.sons[0].typ, abstractVarRange) @@ -1150,20 +1147,15 @@ proc genObjConstr(p: BProc, e: PNode, d: var TLoc) = else: constructLoc(p, tmp) discard getTypeDesc(p.module, t) + let ty = getUniqueType(t) for i in 1 .. <e.len: let it = e.sons[i] var tmp2: TLoc tmp2.r = r - var field: PSym = nil - var ty = getUniqueType(t) - while ty != nil: - field = lookupInRecord(ty.n, it.sons[0].sym.name) - if field != nil: break - if not p.module.compileToCpp: add(tmp2.r, ".Sup") - ty = getUniqueType(ty.sons[0]) - if field == nil or field.loc.r == nil: internalError(e.info, "genObjConstr") + let field = lookupFieldAgain(p, ty, it.sons[0].sym, tmp2.r) + if field.loc.r == nil: internalError(e.info, "genObjConstr") if it.len == 3 and optFieldCheck in p.options: - genFieldCheck(p, it.sons[2], tmp2.r, field) + genFieldCheck(p, it.sons[2], tmp2.r, field, ty) add(tmp2.r, ".") add(tmp2.r, field.loc.r) tmp2.k = locTemp @@ -1272,7 +1264,7 @@ proc genOf(p: BProc, x: PNode, typ: PType, d: var TLoc) = r = rfmt(p.module, "(($1) && ($2))", nilCheck, genOfHelper(p, dest, r)) else: r = rfmt(p.module, "($1)", genOfHelper(p, dest, r)) - putIntoDest(p, d, getSysType(tyBool), r) + putIntoDest(p, d, getSysType(tyBool), r, a.s) proc genOf(p: BProc, n: PNode, d: var TLoc) = genOf(p, n.sons[1], n.sons[2].typ, d) @@ -1284,47 +1276,47 @@ proc genRepr(p: BProc, e: PNode, d: var TLoc) = case t.kind of tyInt..tyInt64, tyUInt..tyUInt64: putIntoDest(p, d, e.typ, - ropecg(p.module, "#reprInt((NI64)$1)", [rdLoc(a)])) + ropecg(p.module, "#reprInt((NI64)$1)", [rdLoc(a)]), a.s) of tyFloat..tyFloat128: - putIntoDest(p, d, e.typ, ropecg(p.module, "#reprFloat($1)", [rdLoc(a)])) + putIntoDest(p, d, e.typ, ropecg(p.module, "#reprFloat($1)", [rdLoc(a)]), a.s) of tyBool: - putIntoDest(p, d, e.typ, ropecg(p.module, "#reprBool($1)", [rdLoc(a)])) + putIntoDest(p, d, e.typ, ropecg(p.module, "#reprBool($1)", [rdLoc(a)]), a.s) of tyChar: - putIntoDest(p, d, e.typ, ropecg(p.module, "#reprChar($1)", [rdLoc(a)])) + putIntoDest(p, d, e.typ, ropecg(p.module, "#reprChar($1)", [rdLoc(a)]), a.s) of tyEnum, tyOrdinal: putIntoDest(p, d, e.typ, ropecg(p.module, "#reprEnum($1, $2)", [ - rdLoc(a), genTypeInfo(p.module, t)])) + rdLoc(a), genTypeInfo(p.module, t)]), a.s) of tyString: - putIntoDest(p, d, e.typ, ropecg(p.module, "#reprStr($1)", [rdLoc(a)])) + putIntoDest(p, d, e.typ, ropecg(p.module, "#reprStr($1)", [rdLoc(a)]), a.s) of tySet: putIntoDest(p, d, e.typ, ropecg(p.module, "#reprSet($1, $2)", [ - addrLoc(a), genTypeInfo(p.module, t)])) + addrLoc(a), genTypeInfo(p.module, t)]), a.s) of tyOpenArray, tyVarargs: var b: TLoc case a.t.kind of tyOpenArray, tyVarargs: - putIntoDest(p, b, e.typ, "$1, $1Len0" % [rdLoc(a)]) + putIntoDest(p, b, e.typ, "$1, $1Len0" % [rdLoc(a)], a.s) of tyString, tySequence: putIntoDest(p, b, e.typ, - "$1->data, $1->$2" % [rdLoc(a), lenField(p)]) + "$1->data, $1->$2" % [rdLoc(a), lenField(p)], a.s) of tyArray, tyArrayConstr: putIntoDest(p, b, e.typ, - "$1, $2" % [rdLoc(a), rope(lengthOrd(a.t))]) + "$1, $2" % [rdLoc(a), rope(lengthOrd(a.t))], a.s) else: internalError(e.sons[0].info, "genRepr()") putIntoDest(p, d, e.typ, ropecg(p.module, "#reprOpenArray($1, $2)", [rdLoc(b), - genTypeInfo(p.module, elemType(t))])) + genTypeInfo(p.module, elemType(t))]), a.s) of tyCString, tyArray, tyArrayConstr, tyRef, tyPtr, tyPointer, tyNil, tySequence: putIntoDest(p, d, e.typ, ropecg(p.module, "#reprAny($1, $2)", [ - rdLoc(a), genTypeInfo(p.module, t)])) + rdLoc(a), genTypeInfo(p.module, t)]), a.s) of tyEmpty: localError(e.info, "'repr' doesn't support 'void' type") else: putIntoDest(p, d, e.typ, ropecg(p.module, "#reprAny($1, $2)", - [addrLoc(a), genTypeInfo(p.module, t)])) + [addrLoc(a), genTypeInfo(p.module, t)]), a.s) gcUsage(e) proc genGetTypeInfo(p: BProc, e: PNode, d: var TLoc) = @@ -1549,13 +1541,13 @@ proc genSomeCast(p: BProc, e: PNode, d: var TLoc) = let etyp = skipTypes(e.typ, abstractRange) if etyp.kind in ValueTypes and lfIndirect notin a.flags: putIntoDest(p, d, e.typ, "(*($1*) ($2))" % - [getTypeDesc(p.module, e.typ), addrLoc(a)]) + [getTypeDesc(p.module, e.typ), addrLoc(a)], a.s) elif etyp.kind == tyProc and etyp.callConv == ccClosure: putIntoDest(p, d, e.typ, "(($1) ($2))" % - [getClosureType(p.module, etyp, clHalfWithEnv), rdCharLoc(a)]) + [getClosureType(p.module, etyp, clHalfWithEnv), rdCharLoc(a)], a.s) else: putIntoDest(p, d, e.typ, "(($1) ($2))" % - [getTypeDesc(p.module, e.typ), rdCharLoc(a)]) + [getTypeDesc(p.module, e.typ), rdCharLoc(a)], a.s) proc genCast(p: BProc, e: PNode, d: var TLoc) = const floatTypes = {tyFloat..tyFloat128} @@ -1575,7 +1567,7 @@ proc genCast(p: BProc, e: PNode, d: var TLoc) = tmp.s = OnStack tmp.flags = {} expr(p, e.sons[1], tmp) - putIntoDest(p, d, e.typ, "LOC$#.dest" % [lbl]) + putIntoDest(p, d, e.typ, "LOC$#.dest" % [lbl], tmp.s) else: # I prefer the shorter cast version for pointer types -> generate less # C code; plus it's the right thing to do for closures: @@ -1589,13 +1581,13 @@ proc genRangeChck(p: BProc, n: PNode, d: var TLoc, magic: string) = {tyUInt..tyUInt64}: initLocExpr(p, n.sons[0], a) putIntoDest(p, d, n.typ, "(($1) ($2))" % - [getTypeDesc(p.module, dest), rdCharLoc(a)]) + [getTypeDesc(p.module, dest), rdCharLoc(a)], a.s) else: initLocExpr(p, n.sons[0], a) putIntoDest(p, d, dest, ropecg(p.module, "(($1)#$5($2, $3, $4))", [ getTypeDesc(p.module, dest), rdCharLoc(a), genLiteral(p, n.sons[1], dest), genLiteral(p, n.sons[2], dest), - rope(magic)])) + rope(magic)]), a.s) proc genConv(p: BProc, e: PNode, d: var TLoc) = let destType = e.typ.skipTypes({tyVar, tyGenericInst}) @@ -1607,13 +1599,13 @@ proc genConv(p: BProc, e: PNode, d: var TLoc) = proc convStrToCStr(p: BProc, n: PNode, d: var TLoc) = var a: TLoc initLocExpr(p, n.sons[0], a) - putIntoDest(p, d, skipTypes(n.typ, abstractVar), "$1->data" % [rdLoc(a)]) + putIntoDest(p, d, skipTypes(n.typ, abstractVar), "$1->data" % [rdLoc(a)], a.s) proc convCStrToStr(p: BProc, n: PNode, d: var TLoc) = var a: TLoc initLocExpr(p, n.sons[0], a) putIntoDest(p, d, skipTypes(n.typ, abstractVar), - ropecg(p.module, "#cstrToNimstr($1)", [rdLoc(a)])) + ropecg(p.module, "#cstrToNimstr($1)", [rdLoc(a)]), a.s) gcUsage(n) proc genStrEquals(p: BProc, e: PNode, d: var TLoc) = @@ -1741,6 +1733,8 @@ proc genMagicExpr(p: BProc, e: PNode, d: var TLoc, op: TMagic) = of mEcho: genEcho(p, e[1].skipConv) of mArrToSeq: genArrToSeq(p, e, d) of mNLen..mNError, mSlurp..mQuoteAst: + echo "from here ", p.prc.name.s, " ", p.prc.info + writestacktrace() localError(e.info, errXMustBeCompileTime, e.sons[0].sym.name.s) of mSpawn: let n = lowerings.wrapProcForSpawn(p.module.module, e, e.typ, nil, nil) @@ -1763,7 +1757,7 @@ proc handleConstExpr(p: BProc, n: PNode, d: var TLoc): bool = var t = getUniqueType(n.typ) discard getTypeDesc(p.module, t) # so that any fields are initialized var id = nodeTableTestOrSet(p.module.dataCache, n, gBackendId) - fillLoc(d, locData, t, "TMP" & rope(id), OnHeap) + fillLoc(d, locData, t, "TMP" & rope(id), OnStatic) if id == gBackendId: # expression not found in the cache: inc(gBackendId) @@ -1849,7 +1843,7 @@ proc genClosure(p: BProc, n: PNode, d: var TLoc) = var tmp = "LOC" & rope(p.labels) addf(p.module.s[cfsData], "NIM_CONST $1 $2 = $3;$n", [getTypeDesc(p.module, n.typ), tmp, genConstExpr(p, n)]) - putIntoDest(p, d, n.typ, tmp) + putIntoDest(p, d, n.typ, tmp, OnStatic) else: var tmp, a, b: TLoc initLocExpr(p, n.sons[0], a) @@ -1903,10 +1897,10 @@ proc upConv(p: BProc, n: PNode, d: var TLoc) = r, genTypeInfo(p.module, dest)) if n.sons[0].typ.kind != tyObject: putIntoDest(p, d, n.typ, - "(($1) ($2))" % [getTypeDesc(p.module, n.typ), rdLoc(a)]) + "(($1) ($2))" % [getTypeDesc(p.module, n.typ), rdLoc(a)], a.s) else: putIntoDest(p, d, n.typ, "(*($1*) ($2))" % - [getTypeDesc(p.module, dest), addrLoc(a)]) + [getTypeDesc(p.module, dest), addrLoc(a)], a.s) proc downConv(p: BProc, n: PNode, d: var TLoc) = if p.module.compileToCpp: @@ -1938,9 +1932,9 @@ proc downConv(p: BProc, n: PNode, d: var TLoc) = linefmt(p, cpsStmts, "$1 = &$2;$n", rdLoc(d), r) else: r = "&" & r - putIntoDest(p, d, n.typ, r) + putIntoDest(p, d, n.typ, r, a.s) else: - putIntoDest(p, d, n.typ, r) + putIntoDest(p, d, n.typ, r, a.s) proc exprComplexConst(p: BProc, n: PNode, d: var TLoc) = var t = getUniqueType(n.typ) @@ -1955,7 +1949,7 @@ proc exprComplexConst(p: BProc, n: PNode, d: var TLoc) = [getTypeDesc(p.module, t), tmp, genConstExpr(p, n)]) if d.k == locNone: - fillLoc(d, locData, t, tmp, OnHeap) + fillLoc(d, locData, t, tmp, OnStatic) else: putDataIntoDest(p, d, t, tmp) @@ -1973,6 +1967,9 @@ proc expr(p: BProc, n: PNode, d: var TLoc) = genProc(p.module, sym) putLocIntoDest(p, d, sym.loc) of skProc, skConverter, skIterators: + if sfCompileTime in sym.flags: + localError(n.info, "request to generate code for .compileTime proc: " & + sym.name.s) genProc(p.module, sym) if sym.loc.r == nil or sym.loc.t == nil: internalError(n.info, "expr: proc not init " & sym.name.s) @@ -1982,7 +1979,7 @@ proc expr(p: BProc, n: PNode, d: var TLoc) = if sfGlobal in sym.flags: genVarPrototype(p.module, sym) putLocIntoDest(p, d, sym.loc) elif isSimpleConst(sym.typ): - putIntoDest(p, d, n.typ, genLiteral(p, sym.ast, sym.typ)) + putIntoDest(p, d, n.typ, genLiteral(p, sym.ast, sym.typ), OnStatic) else: genComplexConst(p, sym, d) of skEnumField: @@ -2126,7 +2123,7 @@ proc expr(p: BProc, n: PNode, d: var TLoc) = # due to a bug/limitation in the lambda lifting, unused inner procs # are not transformed correctly. We work around this issue (#411) here # by ensuring it's no inner proc (owner is a module): - if prc.skipGenericOwner.kind == skModule: + if prc.skipGenericOwner.kind == skModule and sfCompileTime notin prc.flags: if (optDeadCodeElim notin gGlobalOptions and sfDeadCodeElim notin getModule(prc).flags) or ({sfExportc, sfCompilerProc} * prc.flags == {sfExportc}) or diff --git a/compiler/ccgtypes.nim b/compiler/ccgtypes.nim index 84d02d1da..1ed9ce113 100644 --- a/compiler/ccgtypes.nim +++ b/compiler/ccgtypes.nim @@ -441,6 +441,8 @@ proc genRecordFieldsAux(m: BModule, n: PNode, elif fieldType.kind == tySequence: # we need to use a weak dependency here for trecursive_table. addf(result, "$1 $2;$n", [getTypeDescWeak(m, field.loc.t, check), sname]) + elif field.bitsize != 0: + addf(result, "$1 $2:$3;$n", [getTypeDescAux(m, field.loc.t, check), sname, rope($field.bitsize)]) else: # don't use fieldType here because we need the # tyGenericInst for C++ template support diff --git a/compiler/ccgutils.nim b/compiler/ccgutils.nim index 4ba6643ec..6dfd7b52c 100644 --- a/compiler/ccgutils.nim +++ b/compiler/ccgutils.nim @@ -99,7 +99,10 @@ proc getUniqueType*(key: PType): PType = gCanonicalTypes[k] = key result = key of tyTypeDesc, tyTypeClasses, tyGenericParam, tyFromExpr, tyFieldAccessor: - internalError("getUniqueType") + if key.sym != nil: + internalError(key.sym.info, "metatype not eliminated") + else: + internalError("metatype not eliminated") of tyDistinct: if key.deepCopy != nil: result = key else: result = getUniqueType(lastSon(key)) diff --git a/compiler/cgen.nim b/compiler/cgen.nim index 13514818e..f63134b66 100644 --- a/compiler/cgen.nim +++ b/compiler/cgen.nim @@ -760,7 +760,7 @@ proc requestConstImpl(p: BProc, sym: PSym) = var m = p.module useHeader(m, sym) if sym.loc.k == locNone: - fillLoc(sym.loc, locData, sym.typ, mangleName(sym), OnUnknown) + fillLoc(sym.loc, locData, sym.typ, mangleName(sym), OnStatic) if lfNoDecl in sym.loc.flags: return # declare implementation: var q = findPendingModule(m, sym) @@ -1110,7 +1110,7 @@ proc rawNewModule(module: PSym, filename: string): BModule = proc nullify[T](arr: var T) = for i in low(arr)..high(arr): - arr[i] = nil + arr[i] = Rope(nil) proc resetModule*(m: BModule) = # between two compilations in CAAS mode, we can throw diff --git a/compiler/condsyms.nim b/compiler/condsyms.nim index 297b865b2..60e8f2826 100644 --- a/compiler/condsyms.nim +++ b/compiler/condsyms.nim @@ -91,3 +91,5 @@ proc initDefines*() = defineSymbol("nimnomagic64") defineSymbol("nimvarargstyped") defineSymbol("nimtypedescfixed") + defineSymbol("nimKnowsNimvm") + defineSymbol("nimArrIdx") diff --git a/compiler/evaltempl.nim b/compiler/evaltempl.nim index 58594a8b7..c33e5be86 100644 --- a/compiler/evaltempl.nim +++ b/compiler/evaltempl.nim @@ -62,7 +62,7 @@ proc evalTemplateArgs(n: PNode, s: PSym): PNode = # if the template has zero arguments, it can be called without ``()`` # `n` is then a nkSym or something similar var totalParams = case n.kind - of nkCall, nkInfix, nkPrefix, nkPostfix, nkCommand, nkCallStrLit: <n.len + of nkCall, nkInfix, nkPrefix, nkPostfix, nkCommand, nkCallStrLit: n.len-1 else: 0 var @@ -90,8 +90,7 @@ proc evalTemplateArgs(n: PNode, s: PSym): PNode = # not supplied by the user for i in givenRegularParams+1 .. expectedRegularParams: let default = s.typ.n.sons[i].sym.ast - internalAssert default != nil - if default.kind == nkEmpty: + if default.isNil or default.kind == nkEmpty: localError(n.info, errWrongNumberOfArguments) addSon(result, ast.emptyNode) else: diff --git a/compiler/extccomp.nim b/compiler/extccomp.nim index 02198d06e..3882bdd03 100644 --- a/compiler/extccomp.nim +++ b/compiler/extccomp.nim @@ -14,7 +14,7 @@ import lists, ropes, os, strutils, osproc, platform, condsyms, options, msgs, - securehash + securehash, streams type TSystemCC* = enum @@ -474,7 +474,7 @@ proc execWithEcho(cmd: string, msg = hintExecuting): int = proc execExternalProgram*(cmd: string, msg = hintExecuting) = if execWithEcho(cmd, msg) != 0: - rawMessage(errExecutionOfProgramFailed, "") + rawMessage(errExecutionOfProgramFailed, cmd) proc generateScript(projectFile: string, script: Rope) = let (dir, name, ext) = splitFile(projectFile) @@ -672,6 +672,12 @@ proc callCCompiler*(projectfile: string) = var prettyCmds: TStringSeq = @[] let prettyCb = proc (idx: int) = echo prettyCmds[idx] + let runCb = proc (idx: int, p: Process) = + let exitCode = p.peekExitCode + if exitCode != 0: + rawMessage(errGenerated, "execution of an external compiler program '" & + cmds[idx] & "' failed with exit code: " & $exitCode & "\n\n" & + p.outputStream.readAll.strip) compileCFile(toCompile, script, cmds, prettyCmds, false) compileCFile(externalToCompile, script, cmds, prettyCmds, true) if optCompileOnly notin gGlobalOptions: @@ -680,22 +686,19 @@ proc callCCompiler*(projectfile: string) = if gNumberOfProcessors <= 1: for i in countup(0, high(cmds)): res = execWithEcho(cmds[i]) - if res != 0: rawMessage(errExecutionOfProgramFailed, []) + if res != 0: rawMessage(errExecutionOfProgramFailed, cmds[i]) elif optListCmd in gGlobalOptions or gVerbosity > 1: - res = execProcesses(cmds, {poEchoCmd, poUsePath, poParentStreams}, - gNumberOfProcessors) + res = execProcesses(cmds, {poEchoCmd, poStdErrToStdOut, poUsePath, poParentStreams}, + gNumberOfProcessors, afterRunEvent=runCb) elif gVerbosity == 1: - res = execProcesses(cmds, {poUsePath, poParentStreams}, - gNumberOfProcessors, prettyCb) + res = execProcesses(cmds, {poStdErrToStdOut, poUsePath, poParentStreams}, + gNumberOfProcessors, prettyCb, afterRunEvent=runCb) else: - res = execProcesses(cmds, {poUsePath, poParentStreams}, - gNumberOfProcessors) + res = execProcesses(cmds, {poStdErrToStdOut, poUsePath, poParentStreams}, + gNumberOfProcessors, afterRunEvent=runCb) if res != 0: if gNumberOfProcessors <= 1: - rawMessage(errExecutionOfProgramFailed, []) - else: - rawMessage(errGenerated, " execution of an external program failed; " & - "rerun with --parallelBuild:1 to see the error message") + rawMessage(errExecutionOfProgramFailed, cmds.join()) if optNoLinking notin gGlobalOptions: # call the linker: var it = PStrEntry(toLink.head) diff --git a/compiler/filter_tmpl.nim b/compiler/filter_tmpl.nim index d3ab1728c..21810adb9 100644 --- a/compiler/filter_tmpl.nim +++ b/compiler/filter_tmpl.nim @@ -67,9 +67,9 @@ proc parseLine(p: var TTmplParser) = keyw: string j = 0 while p.x[j] == ' ': inc(j) - if (p.x[0] == p.nimDirective) and (p.x[0 + 1] == '!'): + if p.x[0] == p.nimDirective and p.x[1] in {'?', '!'}: newLine(p) - elif (p.x[j] == p.nimDirective): + elif p.x[j] == p.nimDirective: newLine(p) inc(j) while p.x[j] == ' ': inc(j) diff --git a/compiler/guards.nim b/compiler/guards.nim index bc802ae33..5ad932e48 100644 --- a/compiler/guards.nim +++ b/compiler/guards.nim @@ -37,6 +37,7 @@ const someMod = {mModI} someMax = {mMaxI, mMaxF64} someMin = {mMinI, mMinF64} + someBinaryOp = someAdd+someSub+someMul+someMax+someMin proc isValue(n: PNode): bool = n.kind in {nkCharLit..nkNilLit} proc isLocation(n: PNode): bool = not n.isValue @@ -165,11 +166,21 @@ proc `|+|`(a, b: PNode): PNode = if a.kind in {nkCharLit..nkUInt64Lit}: result.intVal = a.intVal |+| b.intVal else: result.floatVal = a.floatVal + b.floatVal +proc `|-|`(a, b: PNode): PNode = + result = copyNode(a) + if a.kind in {nkCharLit..nkUInt64Lit}: result.intVal = a.intVal |-| b.intVal + else: result.floatVal = a.floatVal - b.floatVal + proc `|*|`(a, b: PNode): PNode = result = copyNode(a) if a.kind in {nkCharLit..nkUInt64Lit}: result.intVal = a.intVal |*| b.intVal else: result.floatVal = a.floatVal * b.floatVal +proc `|div|`(a, b: PNode): PNode = + result = copyNode(a) + if a.kind in {nkCharLit..nkUInt64Lit}: result.intVal = a.intVal div b.intVal + else: result.floatVal = a.floatVal / b.floatVal + proc negate(a, b, res: PNode): PNode = if b.kind in {nkCharLit..nkUInt64Lit} and b.intVal != low(BiggestInt): var b = copyNode(b) @@ -213,10 +224,16 @@ proc reassociation(n: PNode): PNode = if result[2].isValue and result[1].getMagic in someAdd and result[1][2].isValue: result = opAdd.buildCall(result[1][1], result[1][2] |+| result[2]) + if result[2].intVal == 0: + result = result[1] of someMul: if result[2].isValue and result[1].getMagic in someMul and result[1][2].isValue: - result = opAdd.buildCall(result[1][1], result[1][2] |*| result[2]) + result = opMul.buildCall(result[1][1], result[1][2] |*| result[2]) + if result[2].intVal == 1: + result = result[1] + elif result[2].intVal == 0: + result = zero() else: discard proc pred(n: PNode): PNode = @@ -234,7 +251,7 @@ proc canon*(n: PNode): PNode = result.sons[i] = canon(n.sons[i]) elif n.kind == nkSym and n.sym.kind == skLet and n.sym.ast.getMagic in (someEq + someAdd + someMul + someMin + - someMax + someHigh + {mUnaryLt} + someSub + someLen): + someMax + someHigh + {mUnaryLt} + someSub + someLen + someDiv): result = n.sym.ast.copyTree else: result = n @@ -248,7 +265,7 @@ proc canon*(n: PNode): PNode = # high == len+(-1) result = opAdd.buildCall(opLen.buildCall(result[1]), minusOne()) of mUnaryLt: - result = buildCall(opAdd, result[1], newIntNode(nkIntLit, -1)) + result = buildCall(opAdd, result[1], minusOne()) of someSub: # x - 4 --> x + (-4) result = negate(result[1], result[2], result) @@ -294,6 +311,16 @@ proc canon*(n: PNode): PNode = if plus != nil and not isLetLocation(x, true): result = buildCall(result[0].sym, plus, y[1]) else: discard + elif x.isValue and y.getMagic in someAdd and y[2].isValue: + # 0 <= a.len + 3 + # -3 <= a.len + result.sons[1] = x |-| y[2] + result.sons[2] = y[1] + elif x.isValue and y.getMagic in someSub and y[2].isValue: + # 0 <= a.len - 3 + # 3 <= a.len + result.sons[1] = x |+| y[2] + result.sons[2] = y[1] else: discard proc `+@`*(a: PNode; b: BiggestInt): PNode = @@ -313,6 +340,9 @@ proc usefulFact(n: PNode): PNode = if isLetLocation(n.sons[1], true) or isLetLocation(n.sons[2], true): # XXX algebraic simplifications! 'i-1 < a.len' --> 'i < a.len+1' result = n + elif n[1].getMagic in someLen or n[2].getMagic in someLen: + # XXX Rethink this whole idea of 'usefulFact' for semparallel + result = n of mIsNil: if isLetLocation(n.sons[1], false) or isVar(n.sons[1]): result = n @@ -366,8 +396,8 @@ proc usefulFact(n: PNode): PNode = type TModel* = seq[PNode] # the "knowledge base" -proc addFact*(m: var TModel, n: PNode) = - let n = usefulFact(n) +proc addFact*(m: var TModel, nn: PNode) = + let n = usefulFact(nn) if n != nil: m.add n proc addFactNeg*(m: var TModel, n: PNode) = @@ -697,10 +727,57 @@ proc simpleSlice*(a, b: PNode): BiggestInt = else: result = -1 + +template isMul(x): expr = x.getMagic in someMul +template isDiv(x): expr = x.getMagic in someDiv +template isAdd(x): expr = x.getMagic in someAdd +template isSub(x): expr = x.getMagic in someSub +template isVal(x): expr = x.kind in {nkCharLit..nkUInt64Lit} +template isIntVal(x, y): expr = x.intVal == y + +import macros + +macro `=~`(x: PNode, pat: untyped): bool = + proc m(x, pat, conds: NimNode) = + case pat.kind + of nnkInfix: + case $pat[0] + of "*": conds.add getAst(isMul(x)) + of "/": conds.add getAst(isDiv(x)) + of "+": conds.add getAst(isAdd(x)) + of "-": conds.add getAst(isSub(x)) + else: + error("invalid pattern") + m(newTree(nnkBracketExpr, x, newLit(1)), pat[1], conds) + m(newTree(nnkBracketExpr, x, newLit(2)), pat[2], conds) + of nnkPar: + if pat.len == 1: + m(x, pat[0], conds) + else: + error("invalid pattern") + of nnkIdent: + let c = newTree(nnkStmtListExpr, newLetStmt(pat, x)) + conds.add c + if ($pat)[^1] == 'c': c.add(getAst(isVal(pat))) + else: c.add bindSym"true" + of nnkIntLit: + conds.add(getAst(isIntVal(pat.intVal))) + else: + error("invalid pattern") + + var conds = newTree(nnkBracket) + m(x, pat, conds) + result = nestList(!"and", conds) + + +proc isMinusOne(n: PNode): bool = + n.kind in {nkCharLit..nkUInt64Lit} and n.intVal == -1 + proc pleViaModel(model: TModel; aa, bb: PNode): TImplication proc ple(m: TModel; a, b: PNode): TImplication = template `<=?`(a,b): expr = ple(m,a,b) == impYes + template `>=?`(a,b): expr = ple(m, nkIntLit.newIntNode(b), a) == impYes # 0 <= 3 if a.isValue and b.isValue: @@ -721,6 +798,7 @@ proc ple(m: TModel; a, b: PNode): TImplication = if a.intVal <= 0: return impYes # x <= y+c if 0 <= c and x <= y + # x <= y+(-c) if c <= 0 and y >= x if b.getMagic in someAdd and zero() <=? b[2] and a <=? b[1]: return impYes # x+c <= y if c <= 0 and x <= y @@ -730,10 +808,44 @@ proc ple(m: TModel; a, b: PNode): TImplication = if b.getMagic in someMul: if a <=? b[1] and one() <=? b[2] and zero() <=? b[1]: return impYes + + if a.getMagic in someMul and a[2].isValue and a[1].getMagic in someDiv and + a[1][2].isValue: + # simplify (x div 4) * 2 <= y to x div (c div d) <= y + if ple(m, buildCall(opDiv, a[1][1], `|div|`(a[1][2], a[2])), b) == impYes: + return impYes + + # x*3 + x == x*4. It follows that: + # x*3 + y <= x*4 if y <= x and 3 <= 4 + if a =~ x*dc + y and b =~ x2*ec: + if sameTree(x, x2): + let ec1 = opAdd.buildCall(ec, minusOne()) + if x >=? 1 and ec >=? 1 and dc >=? 1 and dc <=? ec1 and y <=? x: + return impYes + elif a =~ x*dc and b =~ x2*ec + y: + #echo "BUG cam ehrer e ", a, " <=? ", b + if sameTree(x, x2): + let ec1 = opAdd.buildCall(ec, minusOne()) + if x >=? 1 and ec >=? 1 and dc >=? 1 and dc <=? ec1 and y <=? zero(): + return impYes + + # x+c <= x+d if c <= d. Same for *, - etc. + if a.getMagic in someBinaryOp and a.getMagic == b.getMagic: + if sameTree(a[1], b[1]) and a[2] <=? b[2]: return impYes + elif sameTree(a[2], b[2]) and a[1] <=? b[1]: return impYes + # x div c <= y if 1 <= c and 0 <= y and x <= y: if a.getMagic in someDiv: if one() <=? a[2] and zero() <=? b and a[1] <=? b: return impYes + # x div c <= x div d if d <= c + if b.getMagic in someDiv: + if sameTree(a[1], b[1]) and b[2] <=? a[2]: return impYes + + # x div z <= x - 1 if z <= x + if a[2].isValue and b.getMagic in someAdd and b[2].isMinusOne: + if a[2] <=? a[1] and sameTree(a[1], b[1]): return impYes + # slightly subtle: # x <= max(y, z) iff x <= y or x <= z # note that 'x <= max(x, z)' is a special case of the above rule @@ -769,11 +881,19 @@ proc pleViaModelRec(m: var TModel; a, b: PNode): TImplication = for i in 0..m.high: let fact = m[i] if fact != nil and fact.getMagic in someLe: - # x <= y implies a <= b if a <= x and y <= b - let x = fact[1] - let y = fact[2] # mark as used: m[i] = nil + # i <= len-100 + # i <=? len-1 + # --> true if (len-100) <= (len-1) + let x = fact[1] + let y = fact[2] + if sameTree(x, a) and y.getMagic in someAdd and b.getMagic in someAdd and + sameTree(y[1], b[1]): + if ple(m, b[2], y[2]) == impYes: + return impYes + + # x <= y implies a <= b if a <= x and y <= b if ple(m, a, x) == impYes: if ple(m, y, b) == impYes: return impYes diff --git a/compiler/installer.ini b/compiler/installer.ini index c8af38886..729c13503 100644 --- a/compiler/installer.ini +++ b/compiler/installer.ini @@ -115,6 +115,9 @@ Files: "lib/posix/*.nim" Files: "lib/js/*.nim" Files: "lib/packages/docutils/*.nim" +Files: "lib/deprecated/core/*.nim" +Files: "lib/deprecated/pure/*.nim" +Files: "lib/deprecated/pure/*.cfg" [Other] Files: "examples/*.nim" diff --git a/compiler/jstypes.nim b/compiler/jstypes.nim index 851938327..832d9996c 100644 --- a/compiler/jstypes.nim +++ b/compiler/jstypes.nim @@ -116,8 +116,7 @@ proc genEnumInfo(p: PProc, typ: PType, name: Rope) = [name, genTypeInfo(p, typ.sons[0])]) proc genTypeInfo(p: PProc, typ: PType): Rope = - var t = typ - if t.kind == tyGenericInst: t = lastSon(t) + let t = typ.skipTypes({tyGenericInst}) result = "NTI$1" % [rope(t.id)] if containsOrIncl(p.g.typeInfoGenerated, t.id): return case t.kind @@ -141,7 +140,7 @@ proc genTypeInfo(p: PProc, typ: PType): Rope = [result, rope(ord(t.kind))] prepend(p.g.typeInfo, s) addf(p.g.typeInfo, "$1.base = $2;$n", - [result, genTypeInfo(p, typ.sons[1])]) + [result, genTypeInfo(p, t.sons[1])]) of tyEnum: genEnumInfo(p, t, result) of tyObject: genObjectInfo(p, t, result) of tyTuple: genTupleInfo(p, t, result) diff --git a/compiler/lambdalifting.nim b/compiler/lambdalifting.nim index c669fc745..cccc94756 100644 --- a/compiler/lambdalifting.nim +++ b/compiler/lambdalifting.nim @@ -859,11 +859,17 @@ proc transformOuterProc(o: POuterContext, n: PNode; it: TIter): PNode = return indirectAccess(newSymNode(it.closureParam), local, n.info) if local.kind == skClosureIterator: + # bug #3354; allow for + #iterator iter(): int {.closure.}= + # s.add(iter) + # yield 1 + + #if local == o.fn or local == it.fn: + # message(n.info, errRecursiveDependencyX, local.name.s) + # consider: [i1, i2, i1] Since we merged the iterator's closure # with the captured owning variables, we need to generate the # closure generation code again: - if local == o.fn or local == it.fn: - message(n.info, errRecursiveDependencyX, local.name.s) # XXX why doesn't this work? var closure = PEnv(idTableGet(o.lambdasToEnv, local)) if closure.isNil: diff --git a/compiler/lookups.nim b/compiler/lookups.nim index 88e32404a..e88589c3e 100644 --- a/compiler/lookups.nim +++ b/compiler/lookups.nim @@ -116,7 +116,7 @@ proc errorSym*(c: PContext, n: PNode): PSym = result.typ = errorType(c) incl(result.flags, sfDiscardable) # pretend it's imported from some unknown module to prevent cascading errors: - if gCmd != cmdInteractive and c.inCompilesContext == 0: + if gCmd != cmdInteractive and c.compilesContextId == 0: c.importTable.addSym(result) type diff --git a/compiler/magicsys.nim b/compiler/magicsys.nim index 98a4b08bf..40e0728ae 100644 --- a/compiler/magicsys.nim +++ b/compiler/magicsys.nim @@ -14,22 +14,14 @@ import var systemModule*: PSym -proc registerSysType*(t: PType) - # magic symbols in the system module: -proc getSysType*(kind: TTypeKind): PType -proc getCompilerProc*(name: string): PSym -proc registerCompilerProc*(s: PSym) -proc finishSystem*(tab: TStrTable) -proc getSysSym*(name: string): PSym -# implementation - var gSysTypes: array[TTypeKind, PType] compilerprocs: TStrTable + exposed: TStrTable proc nilOrSysInt*: PType = gSysTypes[tyInt] -proc registerSysType(t: PType) = +proc registerSysType*(t: PType) = if gSysTypes[t.kind] == nil: gSysTypes[t.kind] = t proc newSysType(kind: TTypeKind, size: int): PType = @@ -37,7 +29,7 @@ proc newSysType(kind: TTypeKind, size: int): PType = result.size = size result.align = size.int16 -proc getSysSym(name: string): PSym = +proc getSysSym*(name: string): PSym = result = strTableGet(systemModule.tab, getIdent(name)) if result == nil: rawMessage(errSystemNeeds, name) @@ -61,7 +53,7 @@ proc getSysMagic*(name: string, m: TMagic): PSym = proc sysTypeFromName*(name: string): PType = result = getSysSym(name).typ -proc getSysType(kind: TTypeKind): PType = +proc getSysType*(kind: TTypeKind): PType = result = gSysTypes[kind] if result == nil: case kind @@ -97,6 +89,7 @@ var proc resetSysTypes* = systemModule = nil initStrTable(compilerprocs) + initStrTable(exposed) for i in low(gSysTypes)..high(gSysTypes): gSysTypes[i] = nil @@ -163,8 +156,8 @@ proc setIntLitType*(result: PNode) = result.typ = getSysType(tyInt64) else: internalError(result.info, "invalid int size") -proc getCompilerProc(name: string): PSym = - var ident = getIdent(name, hashIgnoreStyle(name)) +proc getCompilerProc*(name: string): PSym = + let ident = getIdent(name) result = strTableGet(compilerprocs, ident) if result == nil: result = strTableGet(rodCompilerprocs, ident) @@ -173,9 +166,22 @@ proc getCompilerProc(name: string): PSym = if result.kind == skStub: loadStub(result) if result.kind == skAlias: result = result.owner -proc registerCompilerProc(s: PSym) = +proc registerCompilerProc*(s: PSym) = strTableAdd(compilerprocs, s) -proc finishSystem(tab: TStrTable) = discard +proc registerNimScriptSymbol*(s: PSym) = + # Nimscript symbols must be al unique: + let conflict = strTableGet(exposed, s.name) + if conflict == nil: + strTableAdd(exposed, s) + else: + localError(s.info, "symbol conflicts with other .exportNims symbol at: " & + $conflict.info) + +proc getNimScriptSymbol*(name: string): PSym = + strTableGet(exposed, getIdent(name)) + +proc resetNimScriptSymbols*() = initStrTable(exposed) initStrTable(compilerprocs) +initStrTable(exposed) diff --git a/compiler/modules.nim b/compiler/modules.nim index 85c99c4ec..3893d377e 100644 --- a/compiler/modules.nim +++ b/compiler/modules.nim @@ -78,6 +78,13 @@ proc resetModule*(fileIdx: int32) = if fileIdx <% cgendata.gModules.len: cgendata.gModules[fileIdx] = nil +proc resetModule*(module: PSym) = + let conflict = getModule(module.position.int32) + if conflict == nil: return + doAssert conflict == module + resetModule(module.position.int32) + initStrTable(module.tab) + proc resetAllModules* = for i in 0..gCompiledModules.high: if gCompiledModules[i] != nil: diff --git a/compiler/msgs.nim b/compiler/msgs.nim index 8b3b11f4a..4dd134177 100644 --- a/compiler/msgs.nim +++ b/compiler/msgs.nim @@ -203,7 +203,7 @@ const errUseQualifier: "ambiguous identifier: \'$1\' -- use a qualifier", errTypeExpected: "type expected", errSystemNeeds: "system module needs \'$1\'", - errExecutionOfProgramFailed: "execution of an external program failed", + errExecutionOfProgramFailed: "execution of an external program failed: '$1'", errNotOverloadable: "overloaded \'$1\' leads to ambiguous calls", errInvalidArgForX: "invalid argument for \'$1\'", errStmtHasNoEffect: "statement has no effect", @@ -469,10 +469,10 @@ type fullPath: string # This is a canonical full filesystem path projPath*: string # This is relative to the project's root shortName*: string # short name of the module - quotedName*: Rope # cached quoted short name for codegen + quotedName*: Rope # cached quoted short name for codegen # purposes - lines*: seq[Rope] # the source code of the module + lines*: seq[Rope] # the source code of the module # used for better error messages and # embedding the original source in the # generated code @@ -514,6 +514,7 @@ const {low(TNoteKind)..high(TNoteKind)} - {warnShadowIdent, warnUninit, warnProveField, warnProveIndex, warnGcUnsafe, + hintPath, hintConf, hintDependency, hintExecuting, hintCodeBegin, hintCodeEnd, @@ -630,8 +631,11 @@ var proc suggestWriteln*(s: string) = if eStdOut in errorOutputs: - if isNil(writelnHook): writeLine(stdout, s) - else: writelnHook(s) + if isNil(writelnHook): + writeLine(stdout, s) + flushFile(stdout) + else: + writelnHook(s) proc msgQuit*(x: int8) = quit x proc msgQuit*(x: string) = quit x @@ -725,7 +729,9 @@ var gTrackPos*: TLineInfo proc outWriteln*(s: string) = ## Writes to stdout. Always. - if eStdOut in errorOutputs: writeLine(stdout, s) + if eStdOut in errorOutputs: + writeLine(stdout, s) + flushFile(stdout) proc msgWriteln*(s: string) = ## Writes to stdout. If --stdout option is given, writes to stderr instead. @@ -735,9 +741,13 @@ proc msgWriteln*(s: string) = if not isNil(writelnHook): writelnHook(s) elif optStdout in gGlobalOptions: - if eStdErr in errorOutputs: writeLine(stderr, s) + if eStdErr in errorOutputs: + writeLine(stderr, s) + flushFile(stderr) else: - if eStdOut in errorOutputs: writeLine(stdout, s) + if eStdOut in errorOutputs: + writeLine(stdout, s) + flushFile(stdout) macro callIgnoringStyle(theProc: typed, first: typed, args: varargs[expr]): stmt = @@ -772,13 +782,16 @@ template styledMsgWriteln*(args: varargs[expr]) = if not isNil(writelnHook): callIgnoringStyle(callWritelnHook, nil, args) elif optStdout in gGlobalOptions: - if eStdErr in errorOutputs: callIgnoringStyle(writeLine, stderr, args) + if eStdErr in errorOutputs: + callIgnoringStyle(writeLine, stderr, args) + flushFile(stderr) else: if eStdOut in errorOutputs: if optUseColors in gGlobalOptions: callStyledEcho(args) else: callIgnoringStyle(writeLine, stdout, args) + flushFile stdout proc coordToStr(coord: int): string = if coord == -1: result = "???" diff --git a/compiler/nim.nim b/compiler/nim.nim index 51f4cae92..1293ec922 100644 --- a/compiler/nim.nim +++ b/compiler/nim.nim @@ -48,7 +48,7 @@ proc handleCmdLine() = gProjectFull = canonicalizePath(gProjectName) except OSError: gProjectFull = gProjectName - var p = splitFile(gProjectFull) + let p = splitFile(gProjectFull) gProjectPath = p.dir gProjectName = p.name else: @@ -59,6 +59,9 @@ proc handleCmdLine() = runNimScript(scriptFile) # 'nim foo.nims' means to just run the NimScript file and do nothing more: if scriptFile == gProjectFull: return + elif fileExists(gProjectPath / "config.nims"): + # directory wide NimScript file + runNimScript(gProjectPath / "config.nims") # now process command line arguments again, because some options in the # command line can overwite the config file's settings extccomp.initVars() diff --git a/compiler/options.nim b/compiler/options.nim index adb340fea..23c76acc5 100644 --- a/compiler/options.nim +++ b/compiler/options.nim @@ -86,7 +86,7 @@ type # please make sure we have under 32 options gcNone, gcBoehm, gcGo, gcMarkAndSweep, gcRefc, gcV2, gcGenerational IdeCmd* = enum - ideNone, ideSug, ideCon, ideDef, ideUse + ideNone, ideSug, ideCon, ideDef, ideUse, ideDus var gIdeCmd*: IdeCmd @@ -421,6 +421,7 @@ proc parseIdeCmd*(s: string): IdeCmd = of "con": ideCon of "def": ideDef of "use": ideUse + of "dus": ideDus else: ideNone proc `$`*(c: IdeCmd): string = @@ -429,4 +430,5 @@ proc `$`*(c: IdeCmd): string = of ideCon: "con" of ideDef: "def" of ideUse: "use" + of ideDus: "dus" of ideNone: "none" diff --git a/compiler/parampatterns.nim b/compiler/parampatterns.nim index ae391945a..978583c14 100644 --- a/compiler/parampatterns.nim +++ b/compiler/parampatterns.nim @@ -225,8 +225,11 @@ proc isAssignable*(owner: PSym, n: PNode; isUnsafeAddr=false): TAssignableResult result = isAssignable(owner, n.sons[0], isUnsafeAddr) of nkCallKinds: # builtin slice keeps lvalue-ness: - if getMagic(n) == mSlice: + if getMagic(n) in {mArrGet, mSlice}: result = isAssignable(owner, n.sons[1], isUnsafeAddr) + of nkStmtList, nkStmtListExpr: + if n.typ != nil: + result = isAssignable(owner, n.lastSon, isUnsafeAddr) else: discard diff --git a/compiler/pragmas.nim b/compiler/pragmas.nim index 1c51251fe..79d7884fa 100644 --- a/compiler/pragmas.nim +++ b/compiler/pragmas.nim @@ -25,18 +25,19 @@ const wBorrow, wExtern, wImportCompilerProc, wThread, wImportCpp, wImportObjC, wAsmNoStackFrame, wError, wDiscardable, wNoInit, wDestructor, wCodegenDecl, wGensym, wInject, wRaises, wTags, wLocks, wDelegator, wGcSafe, - wOverride, wConstructor} + wOverride, wConstructor, wExportNims} converterPragmas* = procPragmas methodPragmas* = procPragmas+{wBase} templatePragmas* = {wImmediate, wDeprecated, wError, wGensym, wInject, wDirty, - wDelegator} + wDelegator, wExportNims} macroPragmas* = {FirstCallConv..LastCallConv, wImmediate, wImportc, wExportc, wNodecl, wMagic, wNosideeffect, wCompilerproc, wDeprecated, wExtern, - wImportCpp, wImportObjC, wError, wDiscardable, wGensym, wInject, wDelegator} + wImportCpp, wImportObjC, wError, wDiscardable, wGensym, wInject, wDelegator, + wExportNims} iteratorPragmas* = {FirstCallConv..LastCallConv, wNosideeffect, wSideeffect, wImportc, wExportc, wNodecl, wMagic, wDeprecated, wBorrow, wExtern, wImportCpp, wImportObjC, wError, wDiscardable, wGensym, wInject, wRaises, - wTags, wLocks, wGcSafe} + wTags, wLocks, wGcSafe, wExportNims} exprPragmas* = {wLine, wLocks, wNoRewrite} stmtPragmas* = {wChecks, wObjChecks, wFieldChecks, wRangechecks, wBoundchecks, wOverflowchecks, wNilchecks, wAssertions, wWarnings, wHints, @@ -54,15 +55,15 @@ const wPure, wHeader, wCompilerproc, wFinal, wSize, wExtern, wShallow, wImportCpp, wImportObjC, wError, wIncompleteStruct, wByCopy, wByRef, wInheritable, wGensym, wInject, wRequiresInit, wUnchecked, wUnion, wPacked, - wBorrow, wGcSafe} + wBorrow, wGcSafe, wExportNims} fieldPragmas* = {wImportc, wExportc, wDeprecated, wExtern, - wImportCpp, wImportObjC, wError, wGuard} + wImportCpp, wImportObjC, wError, wGuard, wBitsize} varPragmas* = {wImportc, wExportc, wVolatile, wRegister, wThreadVar, wNodecl, wMagic, wHeader, wDeprecated, wCompilerproc, wDynlib, wExtern, wImportCpp, wImportObjC, wError, wNoInit, wCompileTime, wGlobal, - wGensym, wInject, wCodegenDecl, wGuard, wGoto} + wGensym, wInject, wCodegenDecl, wGuard, wGoto, wExportNims} constPragmas* = {wImportc, wExportc, wHeader, wDeprecated, wMagic, wNodecl, - wExtern, wImportCpp, wImportObjC, wError, wGensym, wInject} + wExtern, wImportCpp, wImportObjC, wError, wGensym, wInject, wExportNims} letPragmas* = varPragmas procTypePragmas* = {FirstCallConv..LastCallConv, wVarargs, wNosideeffect, wThread, wRaises, wLocks, wTags, wGcSafe} @@ -844,6 +845,11 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: int, if sym == nil: pragmaLockStmt(c, it) elif sym.typ == nil: invalidPragma(it) else: sym.typ.lockLevel = pragmaLocks(c, it) + of wBitsize: + if sym == nil or sym.kind != skField or it.kind != nkExprColonExpr: + invalidPragma(it) + else: + sym.bitsize = expectIntLit(c, it) of wGuard: if sym == nil or sym.kind notin {skVar, skLet, skField}: invalidPragma(it) @@ -854,6 +860,9 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: int, invalidPragma(it) else: sym.flags.incl sfGoto + of wExportNims: + if sym == nil: invalidPragma(it) + else: magicsys.registerNimScriptSymbol(sym) of wInjectStmt: if it.kind != nkExprColonExpr: localError(it.info, errExprExpected) diff --git a/compiler/scriptconfig.nim b/compiler/scriptconfig.nim index 8499ebd98..22cd282fd 100644 --- a/compiler/scriptconfig.nim +++ b/compiler/scriptconfig.nim @@ -105,6 +105,13 @@ proc setupVM*(module: PSym; scriptName: string): PEvalContext = setResult(a, strutils.cmpIgnoreCase(a.getString 0, a.getString 1)) cbconf setCommand: options.command = a.getString 0 + let arg = a.getString 1 + if arg.len > 0: + gProjectName = arg + try: + gProjectFull = canonicalizePath(gProjectPath / gProjectName) + except OSError: + gProjectFull = gProjectName cbconf getCommand: setResult(a, options.command) cbconf switch: @@ -133,4 +140,7 @@ proc runNimScript*(scriptName: string) = # ensure we load 'system.nim' again for the real non-config stuff! resetAllModulesHard() vm.globalCtx = nil - initDefines() + # do not remove the defined symbols + #initDefines() + undefSymbol("nimscript") + undefSymbol("nimconfig") diff --git a/compiler/semcall.nim b/compiler/semcall.nim index 2f181b5f3..381093531 100644 --- a/compiler/semcall.nim +++ b/compiler/semcall.nim @@ -95,7 +95,7 @@ proc notFoundError*(c: PContext, n: PNode, errors: CandidateErrors) = # Gives a detailed error message; this is separated from semOverloadedCall, # as semOverlodedCall is already pretty slow (and we need this information # only in case of an error). - if c.inCompilesContext > 0: + if c.compilesContextId > 0: # fail fast: globalError(n.info, errTypeMismatch, "") if errors.isNil or errors.len == 0: @@ -235,7 +235,7 @@ proc resolveOverloads(c: PContext, n, orig: PNode, internalAssert result.state == csMatch #writeMatches(result) #writeMatches(alt) - if c.inCompilesContext > 0: + if c.compilesContextId > 0: # quick error message for performance of 'compiles' built-in: globalError(n.info, errGenerated, "ambiguous call") elif gErrorCounter == 0: @@ -308,7 +308,10 @@ proc semResolvedCall(c: PContext, n: PNode, x: TCandidate): PNode = let gp = finalCallee.ast.sons[genericParamsPos] if gp.kind != nkEmpty: if x.calleeSym.kind notin {skMacro, skTemplate}: - finalCallee = generateInstance(c, x.calleeSym, x.bindings, n.info) + if x.calleeSym.magic in {mArrGet, mArrPut}: + finalCallee = x.calleeSym + else: + finalCallee = generateInstance(c, x.calleeSym, x.bindings, n.info) else: # For macros and templates, the resolved generic params # are added as normal params. diff --git a/compiler/semdata.nim b/compiler/semdata.nim index 345a8c0d1..9b2f2e2ce 100644 --- a/compiler/semdata.nim +++ b/compiler/semdata.nim @@ -45,7 +45,8 @@ type TExprFlag* = enum efLValue, efWantIterator, efInTypeof, efWantStmt, efAllowStmt, efDetermineType, - efAllowDestructor, efWantValue, efOperand, efNoSemCheck + efAllowDestructor, efWantValue, efOperand, efNoSemCheck, + efNoProcvarCheck TExprFlags* = set[TExprFlag] TTypeAttachedOp* = enum @@ -70,7 +71,8 @@ type inTypeClass*: int # > 0 if we are in a user-defined type class inGenericContext*: int # > 0 if we are in a generic type inUnrolledContext*: int # > 0 if we are unrolling a loop - inCompilesContext*: int # > 0 if we are in a ``compiles`` magic + compilesContextId*: int # > 0 if we are in a ``compiles`` magic + compilesContextIdGenerator*: int inGenericInst*: int # > 0 if we are instantiating a generic converters*: TSymSeq # sequence of converters patterns*: TSymSeq # sequence of pattern matchers diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index 3ff04a4fc..4792702dc 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -52,7 +52,7 @@ proc semExprWithType(c: PContext, n: PNode, flags: TExprFlags = {}): PNode = result.typ = errorType(c) else: # XXX tyGenericInst here? - semProcvarCheck(c, result) + if efNoProcvarCheck notin flags: semProcvarCheck(c, result) if result.typ.kind == tyVar: result = newDeref(result) semDestructorCheck(c, result, flags) @@ -452,18 +452,18 @@ proc changeType(n: PNode, newType: PType, check: bool) = let tup = newType.skipTypes({tyGenericInst}) if tup.kind != tyTuple: if tup.kind == tyObject: return - internalError(n.info, "changeType: no tuple type for constructor") + globalError(n.info, "no tuple type for constructor") elif sonsLen(n) > 0 and n.sons[0].kind == nkExprColonExpr: # named tuple? for i in countup(0, sonsLen(n) - 1): var m = n.sons[i].sons[0] if m.kind != nkSym: - internalError(m.info, "changeType(): invalid tuple constr") + globalError(m.info, "invalid tuple constructor") return if tup.n != nil: var f = getSymFromList(tup.n, m.sym.name) if f == nil: - internalError(m.info, "changeType(): invalid identifier") + globalError(m.info, "unknown identifier: " & m.sym.name.s) return changeType(n.sons[i].sons[1], f.typ, check) else: @@ -818,7 +818,7 @@ proc semIndirectOp(c: PContext, n: PNode, flags: TExprFlags): PNode = # This is a proc variable, apply normal overload resolution let m = resolveIndirectCall(c, n, nOrig, t) if m.state != csMatch: - if c.inCompilesContext > 0: + if c.compilesContextId > 0: # speed up error generation: globalError(n.info, errTypeMismatch, "") return emptyNode @@ -1156,7 +1156,7 @@ proc semSubscript(c: PContext, n: PNode, flags: TExprFlags): PNode = result.add(x[0]) return checkMinSonsLen(n, 2) - n.sons[0] = semExprWithType(c, n.sons[0]) + n.sons[0] = semExprWithType(c, n.sons[0], {efNoProcvarCheck}) let arr = skipTypes(n.sons[0].typ, {tyGenericInst, tyVar, tyPtr, tyRef}) case arr.kind of tyArray, tyOpenArray, tyVarargs, tyArrayConstr, tySequence, tyString, @@ -1196,7 +1196,17 @@ proc semSubscript(c: PContext, n: PNode, flags: TExprFlags): PNode = localError(n.info, errIndexTypesDoNotMatch) result = n else: - c.p.bracketExpr = n.sons[0] + let s = if n.sons[0].kind == nkSym: n.sons[0].sym + elif n[0].kind in nkSymChoices: n.sons[0][0].sym + else: nil + if s != nil and s.kind in {skProc, skMethod, skConverter}+skIterators: + # type parameters: partial generic specialization + n.sons[0] = semSymGenericInstantiation(c, n.sons[0], s) + result = explicitGenericInstantiation(c, n, s) + elif s != nil and s.kind == skType: + result = symNodeFromType(c, semTypeNode(c, n, nil), n.info) + else: + c.p.bracketExpr = n.sons[0] proc semArrayAccess(c: PContext, n: PNode, flags: TExprFlags): PNode = let oldBracketExpr = c.p.bracketExpr @@ -1250,7 +1260,7 @@ proc asgnToResultVar(c: PContext, n, le, ri: PNode) {.inline.} = template resultTypeIsInferrable(typ: PType): expr = typ.isMetaType and typ.kind != tyTypeDesc -proc semAsgn(c: PContext, n: PNode): PNode = +proc semAsgn(c: PContext, n: PNode; mode=asgnNormal): PNode = checkSonsLen(n, 2) var a = n.sons[0] case a.kind @@ -1273,12 +1283,15 @@ proc semAsgn(c: PContext, n: PNode): PNode = # --> `[]=`(a, i, x) let oldBracketExpr = c.p.bracketExpr a = semSubscript(c, a, {efLValue}) - if a == nil: + if a == nil and mode != noOverloadedSubscript: result = buildOverloadedSubscripts(n.sons[0], getIdent"[]=") add(result, n[1]) result = semExprNoType(c, result) c.p.bracketExpr = oldBracketExpr return result + elif a == nil: + localError(n.info, "could not resolve: " & $n[0]) + return n c.p.bracketExpr = oldBracketExpr of nkCurlyExpr: # a{i} = x --> `{}=`(a, i, x) @@ -1323,7 +1336,8 @@ proc semAsgn(c: PContext, n: PNode): PNode = typeMismatch(n, lhs.typ, rhs.typ) n.sons[1] = fitNode(c, le, rhs) - if tfHasAsgn in lhs.typ.flags and not lhsIsResult: + if tfHasAsgn in lhs.typ.flags and not lhsIsResult and + mode != noOverloadedAsgn: return overloadedAsgn(c, lhs, n.sons[1]) fixAbstractType(c, n) @@ -1622,7 +1636,9 @@ proc tryExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode = # watch out, hacks ahead: let oldErrorCount = msgs.gErrorCounter let oldErrorMax = msgs.gErrorMax - inc c.inCompilesContext + let oldCompilesId = c.compilesContextId + inc c.compilesContextIdGenerator + c.compilesContextId = c.compilesContextIdGenerator # do not halt after first error: msgs.gErrorMax = high(int) @@ -1646,6 +1662,7 @@ proc tryExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode = except ERecoverableError: discard # undo symbol table changes (as far as it's possible): + c.compilesContextId = oldCompilesId c.generics = oldGenerics c.inGenericContext = oldInGenericContext c.inUnrolledContext = oldInUnrolledContext @@ -1654,7 +1671,6 @@ proc tryExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode = msgs.setInfoContextLen(oldContextLen) setLen(gOwners, oldOwnerLen) c.currentScope = oldScope - dec c.inCompilesContext errorOutputs = oldErrorOutputs msgs.gErrorCounter = oldErrorCount msgs.gErrorMax = oldErrorMax @@ -1715,6 +1731,9 @@ proc semMagic(c: PContext, n: PNode, s: PSym, flags: TExprFlags): PNode = of mTypeOf: checkSonsLen(n, 2) result = semTypeOf(c, n.sons[1]) + #of mArrGet: result = semArrGet(c, n, flags) + #of mArrPut: result = semArrPut(c, n, flags) + #of mAsgn: result = semAsgnOpr(c, n) of mDefined: result = semDefined(c, setMs(n, s), false) of mDefinedInScope: result = semDefined(c, setMs(n, s), true) of mCompiles: result = semCompiles(c, setMs(n, s), flags) @@ -2066,6 +2085,19 @@ proc semExport(c: PContext, n: PNode): PNode = c.module.ast.add x result = n +proc shouldBeBracketExpr(n: PNode): bool = + assert n.kind in nkCallKinds + let a = n.sons[0] + if a.kind in nkCallKinds: + let b = a[0] + if b.kind in nkSymChoices: + for i in 0..<b.len: + if b[i].sym.magic == mArrGet: + let be = newNodeI(nkBracketExpr, n.info) + for i in 1..<a.len: be.add(a[i]) + n.sons[0] = be + return true + proc setGenericParams(c: PContext, n: PNode) = for i in 1 .. <n.len: n[i].typ = semTypeNode(c, n[i], nil) @@ -2173,7 +2205,8 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode = else: #liMessage(n.info, warnUser, renderTree(n)); result = semIndirectOp(c, n, flags) - elif n[0].kind == nkBracketExpr and isSymChoice(n[0][0]): + elif (n[0].kind == nkBracketExpr or shouldBeBracketExpr(n)) and + isSymChoice(n[0][0]): # indirectOp can deal with explicit instantiations; the fixes # the 'newSeq[T](x)' bug setGenericParams(c, n.sons[0]) @@ -2194,16 +2227,7 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode = result = semExpr(c, result, flags) of nkBracketExpr: checkMinSonsLen(n, 1) - var s = qualifiedLookUp(c, n.sons[0], {checkUndeclared}) - if (s != nil and s.kind in {skProc, skMethod, skConverter}+skIterators) or - n[0].kind in nkSymChoices: - # type parameters: partial generic specialization - n.sons[0] = semSymGenericInstantiation(c, n.sons[0], s) - result = explicitGenericInstantiation(c, n, s) - elif s != nil and s.kind in {skType}: - result = symNodeFromType(c, semTypeNode(c, n, nil), n.info) - else: - result = semArrayAccess(c, n, flags) + result = semArrayAccess(c, n, flags) of nkCurlyExpr: result = semExpr(c, buildOverloadedSubscripts(n, getIdent"{}"), flags) of nkPragmaExpr: diff --git a/compiler/semfold.nim b/compiler/semfold.nim index 2ab43a9c9..5fe4e3299 100644 --- a/compiler/semfold.nim +++ b/compiler/semfold.nim @@ -430,17 +430,10 @@ proc evalOp(m: TMagic, n, a, b, c: PNode): PNode = of mCompileOptionArg: result = newIntNodeT(ord( testCompileOptionArg(getStr(a), getStr(b), n.info)), n) - of mNewString, mNewStringOfCap, - mExit, mInc, ast.mDec, mEcho, mSwap, mAppendStrCh, - mAppendStrStr, mAppendSeqElem, mSetLengthStr, mSetLengthSeq, - mParseExprToAst, mParseStmtToAst, mExpandToAst, mTypeTrait, mDotDot, - mNLen..mNError, mEqRef, mSlurp, mStaticExec, mNGenSym, mSpawn, - mParallel, mPlugin, mGetTypeInfo: - discard of mEqProc: result = newIntNodeT(ord( exprStructuralEquivalent(a, b, strictSymEquality=true)), n) - else: internalError(a.info, "evalOp(" & $m & ')') + else: discard proc getConstIfExpr(c: PSym, n: PNode): PNode = result = nil diff --git a/compiler/semgnrc.nim b/compiler/semgnrc.nim index e3b598919..620453277 100644 --- a/compiler/semgnrc.nim +++ b/compiler/semgnrc.nim @@ -30,6 +30,7 @@ type GenericCtx = object toMixin: IntSet cursorInBody: bool # only for nimsuggest + bracketExpr: PNode type TSemGenericFlag = enum @@ -227,6 +228,10 @@ proc semGenericStmt(c: PContext, n: PNode, discard of skProc, skMethod, skIterators, skConverter, skModule: result.sons[0] = symChoice(c, fn, s, scOption) + # do not check of 's.magic==mRoof' here because it might be some + # other '^' but after overload resolution the proper one: + if ctx.bracketExpr != nil and n.len == 2 and s.name.s == "^": + result.add ctx.bracketExpr first = 1 of skGenericParam: result.sons[0] = newSymNodeTypeDesc(s, fn.info) @@ -251,6 +256,40 @@ proc semGenericStmt(c: PContext, n: PNode, let flags = if mixinContext: flags+{withinMixin} else: flags for i in countup(first, sonsLen(result) - 1): result.sons[i] = semGenericStmt(c, result.sons[i], flags, ctx) + of nkCurlyExpr: + result = newNodeI(nkCall, n.info) + result.add newIdentNode(getIdent("{}"), n.info) + for i in 0 ..< n.len: result.add(n[i]) + result = semGenericStmt(c, result, flags, ctx) + of nkBracketExpr: + result = newNodeI(nkCall, n.info) + result.add newIdentNode(getIdent("[]"), n.info) + for i in 0 ..< n.len: result.add(n[i]) + withBracketExpr ctx, n.sons[0]: + result = semGenericStmt(c, result, flags, ctx) + of nkAsgn, nkFastAsgn: + checkSonsLen(n, 2) + let a = n.sons[0] + let b = n.sons[1] + + let k = a.kind + case k + of nkCurlyExpr: + result = newNodeI(nkCall, n.info) + result.add newIdentNode(getIdent("{}="), n.info) + for i in 0 ..< a.len: result.add(a[i]) + result.add(b) + result = semGenericStmt(c, result, flags, ctx) + of nkBracketExpr: + result = newNodeI(nkCall, n.info) + result.add newIdentNode(getIdent("[]="), n.info) + for i in 0 ..< a.len: result.add(a[i]) + result.add(b) + withBracketExpr ctx, a.sons[0]: + result = semGenericStmt(c, result, flags, ctx) + else: + for i in countup(0, sonsLen(n) - 1): + result.sons[i] = semGenericStmt(c, n.sons[i], flags, ctx) of nkIfStmt: for i in countup(0, sonsLen(n)-1): n.sons[i] = semGenericStmtScope(c, n.sons[i], flags, ctx) @@ -357,7 +396,7 @@ proc semGenericStmt(c: PContext, n: PNode, of nkEnumFieldDef: a = n.sons[i].sons[0] of nkIdent: a = n.sons[i] else: illFormedAst(n) - addDecl(c, newSymS(skUnknown, getIdentNode(a.sons[i]), c)) + addDecl(c, newSymS(skUnknown, getIdentNode(a), c)) of nkObjectTy, nkTupleTy, nkTupleClassTy: discard of nkFormalParams: diff --git a/compiler/seminst.nim b/compiler/seminst.nim index 370990326..abc5600c3 100644 --- a/compiler/seminst.nim +++ b/compiler/seminst.nim @@ -47,10 +47,11 @@ proc sameInstantiation(a, b: TInstantiation): bool = flags = {ExactTypeDescValues}): return result = true -proc genericCacheGet(genericSym: PSym, entry: TInstantiation): PSym = +proc genericCacheGet(genericSym: PSym, entry: TInstantiation; + id: CompilesId): PSym = if genericSym.procInstCache != nil: for inst in genericSym.procInstCache: - if sameInstantiation(entry, inst[]): + if inst.compilesId == id and sameInstantiation(entry, inst[]): return inst.sym proc removeDefaultParamValues(n: PNode) = @@ -164,7 +165,7 @@ proc instantiateProcType(c: PContext, pt: TIdTable, addDecl(c, prc) pushInfoContext(info) - var cl = initTypeVars(c, pt, info) + var cl = initTypeVars(c, pt, info, nil) var result = instCopyType(cl, prc.typ) let originalParams = result.n result.n = originalParams.shallowCopy @@ -221,6 +222,8 @@ proc generateInstance(c: PContext, fn: PSym, pt: TIdTable, # NOTE: for access of private fields within generics from a different module # we set the friend module: c.friendModules.add(getModule(fn)) + let oldInTypeClass = c.inTypeClass + c.inTypeClass = 0 let oldScope = c.currentScope while not isTopLevel(c): c.currentScope = c.currentScope.parent result = copySym(fn, false) @@ -244,14 +247,18 @@ proc generateInstance(c: PContext, fn: PSym, pt: TIdTable, inc i pushProcCon(c, result) instantiateProcType(c, pt, result, info) + if tfTriggersCompileTime in result.typ.flags: + incl(result.flags, sfCompileTime) n.sons[genericParamsPos] = ast.emptyNode - var oldPrc = genericCacheGet(fn, entry[]) + var oldPrc = genericCacheGet(fn, entry[], c.compilesContextId) if oldPrc == nil: # we MUST not add potentially wrong instantiations to the caching mechanism. # This means recursive instantiations behave differently when in # a ``compiles`` context but this is the lesser evil. See # bug #1055 (tevilcompiles). - if c.inCompilesContext == 0: fn.procInstCache.safeAdd(entry) + #if c.compilesContextId == 0: + entry.compilesId = c.compilesContextId + fn.procInstCache.safeAdd(entry) c.generics.add(makeInstPair(fn, entry)) if n.sons[pragmasPos].kind != nkEmpty: pragma(c, result, n.sons[pragmasPos], allRoutinePragmas) @@ -269,4 +276,5 @@ proc generateInstance(c: PContext, fn: PSym, pt: TIdTable, c.currentScope = oldScope discard c.friendModules.pop() dec(c.instCounter) + c.inTypeClass = oldInTypeClass if result.kind == skMethod: finishMethod(c, result) diff --git a/compiler/semmagic.nim b/compiler/semmagic.nim index 5d16470b0..deef38ae3 100644 --- a/compiler/semmagic.nim +++ b/compiler/semmagic.nim @@ -26,6 +26,41 @@ proc semTypeOf(c: PContext; n: PNode): PNode = result.add typExpr result.typ = makeTypeDesc(c, typExpr.typ.skipTypes({tyTypeDesc, tyIter})) +type + SemAsgnMode = enum asgnNormal, noOverloadedSubscript, noOverloadedAsgn + +proc semAsgn(c: PContext, n: PNode; mode=asgnNormal): PNode +proc semSubscript(c: PContext, n: PNode, flags: TExprFlags): PNode + +proc skipAddr(n: PNode): PNode {.inline.} = + (if n.kind == nkHiddenAddr: n.sons[0] else: n) + +proc semArrGet(c: PContext; n: PNode; flags: TExprFlags): PNode = + result = newNodeI(nkBracketExpr, n.info) + for i in 1..<n.len: result.add(n[i]) + let oldBracketExpr = c.p.bracketExpr + result = semSubscript(c, result, flags) + c.p.bracketExpr = oldBracketExpr + if result.isNil: + localError(n.info, "could not resolve: " & $n) + result = n + +proc semArrPut(c: PContext; n: PNode; flags: TExprFlags): PNode = + # rewrite `[]=`(a, i, x) back to ``a[i] = x``. + let b = newNodeI(nkBracketExpr, n.info) + b.add(n[1].skipAddr) + for i in 2..n.len-2: b.add(n[i]) + result = newNodeI(nkAsgn, n.info, 2) + result.sons[0] = b + result.sons[1] = n.lastSon + result = semAsgn(c, result, noOverloadedSubscript) + +proc semAsgnOpr(c: PContext; n: PNode): PNode = + result = newNodeI(nkAsgn, n.info, 2) + result.sons[0] = n[1] + result.sons[1] = n[2] + result = semAsgn(c, result, noOverloadedAsgn) + proc semIsPartOf(c: PContext, n: PNode, flags: TExprFlags): PNode = var r = isPartOf(n[1], n[2]) result = newIntNodeT(ord(r), n) @@ -125,6 +160,9 @@ proc magicsAfterOverloadResolution(c: PContext, n: PNode, of mTypeOf: checkSonsLen(n, 2) result = semTypeOf(c, n.sons[1]) + of mArrGet: result = semArrGet(c, n, flags) + of mArrPut: result = semArrPut(c, n, flags) + of mAsgn: result = semAsgnOpr(c, n) of mIsPartOf: result = semIsPartOf(c, n, flags) of mTypeTrait: result = semTypeTraits(c, n) of mAstToStr: @@ -145,25 +183,28 @@ proc magicsAfterOverloadResolution(c: PContext, n: PNode, if isNegative(n.sons[1]) or (n.len > 2 and isNegative(n.sons[2])): localError(n.info, "use '^' instead of '-'; negative indexing is obsolete") of mRoof: - # error correction: - result = n.sons[1] - if c.p.bracketExpr.isNil: + let bracketExpr = if n.len == 3: n.sons[2] else: c.p.bracketExpr + if bracketExpr.isNil: localError(n.info, "no surrounding array access context for '^'") - elif c.p.bracketExpr.checkForSideEffects != seNoSideEffect: + result = n.sons[1] + elif bracketExpr.checkForSideEffects != seNoSideEffect: localError(n.info, "invalid context for '^' as '$#' has side effects" % - renderTree(c.p.bracketExpr)) - elif c.p.bracketExpr.typ.isStrangeArray: + renderTree(bracketExpr)) + result = n.sons[1] + elif bracketExpr.typ.isStrangeArray: localError(n.info, "invalid context for '^' as len!=high+1 for '$#'" % - renderTree(c.p.bracketExpr)) + renderTree(bracketExpr)) + result = n.sons[1] else: # ^x is rewritten to: len(a)-x let lenExpr = newNodeI(nkCall, n.info) lenExpr.add newIdentNode(getIdent"len", n.info) - lenExpr.add c.p.bracketExpr + lenExpr.add bracketExpr let lenExprB = semExprWithType(c, lenExpr) if lenExprB.typ.isNil or not isOrdinalType(lenExprB.typ): localError(n.info, "'$#' has to be of an ordinal type for '^'" % renderTree(lenExpr)) + result = n.sons[1] else: result = newNodeIT(nkCall, n.info, getSysType(tyInt)) result.add newSymNode(createMagic("-", mSubI), n.info) diff --git a/compiler/semparallel.nim b/compiler/semparallel.nim index 36c63d038..b04ba4657 100644 --- a/compiler/semparallel.nim +++ b/compiler/semparallel.nim @@ -128,10 +128,10 @@ template `?`(x): expr = x.renderTree proc checkLe(c: AnalysisCtx; a, b: PNode) = case proveLe(c.guards, a, b) of impUnknown: - localError(a.info, "cannot prove: " & ?a & " <= " & ?b) + localError(a.info, "cannot prove: " & ?a & " <= " & ?b & " (bounds check)") of impYes: discard of impNo: - localError(a.info, "can prove: " & ?a & " > " & ?b) + localError(a.info, "can prove: " & ?a & " > " & ?b & " (bounds check)") proc checkBounds(c: AnalysisCtx; arr, idx: PNode) = checkLe(c, arr.lowBound, idx) @@ -156,19 +156,23 @@ proc addSlice(c: var AnalysisCtx; n: PNode; x, le, ri: PNode) = proc overlap(m: TModel; x,y,c,d: PNode) = # X..Y and C..D overlap iff (X <= D and C <= Y) - case proveLe(m, x, d) + case proveLe(m, c, y) of impUnknown: - localError(x.info, - "cannot prove: $# > $#; required for ($#)..($#) disjoint from ($#)..($#)" % - [?x, ?d, ?x, ?y, ?c, ?d]) + case proveLe(m, x, d) + of impNo: discard + of impUnknown, impYes: + localError(x.info, + "cannot prove: $# > $#; required for ($#)..($#) disjoint from ($#)..($#)" % + [?c, ?y, ?x, ?y, ?c, ?d]) of impYes: - case proveLe(m, c, y) + case proveLe(m, x, d) of impUnknown: localError(x.info, "cannot prove: $# > $#; required for ($#)..($#) disjoint from ($#)..($#)" % - [?c, ?y, ?x, ?y, ?c, ?d]) + [?x, ?d, ?x, ?y, ?c, ?d]) of impYes: - localError(x.info, "($#)..($#) not disjoint from ($#)..($#)" % [?x, ?y, ?c, ?d]) + localError(x.info, "($#)..($#) not disjoint from ($#)..($#)" % + [?c, ?y, ?x, ?y, ?c, ?d]) of impNo: discard of impNo: discard @@ -278,10 +282,12 @@ proc analyseCall(c: var AnalysisCtx; n: PNode; op: PSym) = slot.stride = min(slot.stride, incr) analyseSons(c, n) elif op.name.s == "[]" and op.fromSystem: - c.addSlice(n, n[1], n[2][1], n[2][2]) + let slice = n[2].skipStmtList + c.addSlice(n, n[1], slice[1], slice[2]) analyseSons(c, n) elif op.name.s == "[]=" and op.fromSystem: - c.addSlice(n, n[1], n[2][1], n[2][2]) + let slice = n[2].skipStmtList + c.addSlice(n, n[1], slice[1], slice[2]) analyseSons(c, n) else: analyseSons(c, n) @@ -395,8 +401,9 @@ proc transformSlices(n: PNode): PNode = result = copyNode(n) result.add opSlice.newSymNode result.add n[1] - result.add n[2][1] - result.add n[2][2] + let slice = n[2].skipStmtList + result.add slice[1] + result.add slice[2] return result if n.safeLen > 0: result = shallowCopy(n) diff --git a/compiler/sempass2.nim b/compiler/sempass2.nim index 272412b04..ef014963c 100644 --- a/compiler/sempass2.nim +++ b/compiler/sempass2.nim @@ -855,6 +855,8 @@ proc initEffects(effects: PNode; s: PSym; t: var TEffects) = newSeq(effects.sons, effectListLen) effects.sons[exceptionEffects] = newNodeI(nkArgList, s.info) effects.sons[tagEffects] = newNodeI(nkArgList, s.info) + effects.sons[usesEffects] = ast.emptyNode + effects.sons[writeEffects] = ast.emptyNode t.exc = effects.sons[exceptionEffects] t.tags = effects.sons[tagEffects] diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim index ffda6a1bb..adb1c81c1 100644 --- a/compiler/semstmts.nim +++ b/compiler/semstmts.nim @@ -957,27 +957,35 @@ proc semDo(c: PContext, n: PNode, flags: TExprFlags): PNode = proc semInferredLambda(c: PContext, pt: TIdTable, n: PNode): PNode = var n = n - n = replaceTypesInBody(c, pt, n) - result = n + let original = n.sons[namePos].sym + let s = copySym(original, false) + incl(s.flags, sfFromGeneric) + n = replaceTypesInBody(c, pt, n, original) + result = n + s.ast = result + n.sons[namePos].sym = s n.sons[genericParamsPos] = emptyNode - n.sons[paramsPos] = n.typ.n - + let params = n.typ.n + n.sons[paramsPos] = params + s.typ = n.typ + for i in 1..<params.len: + if params[i].typ.kind in {tyTypeDesc, tyGenericParam, + tyFromExpr, tyFieldAccessor}+tyTypeClasses: + localError(params[i].info, "cannot infer type of parameter: " & + params[i].sym.name.s) openScope(c) - var s = n.sons[namePos].sym pushOwner(s) - addParams(c, n.typ.n, skProc) + addParams(c, params, skProc) pushProcCon(c, s) addResult(c, n.typ.sons[0], n.info, skProc) addResultNode(c, n) let semBody = hloBody(c, semProcBody(c, n.sons[bodyPos])) - n.sons[bodyPos] = transformBody(c.module, semBody, n.sons[namePos].sym) + n.sons[bodyPos] = transformBody(c.module, semBody, s) popProcCon(c) popOwner() closeScope(c) - s.ast = result - # alternative variant (not quite working): # var prc = arg[0].sym # let inferred = c.semGenerateInstance(c, prc, m.bindings, arg.info) @@ -1033,6 +1041,7 @@ proc semOverride(c: PContext, s: PSym, n: PNode) = "signature for 'deepCopy' must be proc[T: ptr|ref](x: T): T") incl(s.flags, sfUsed) of "=": + if s.magic == mAsgn: return incl(s.flags, sfUsed) let t = s.typ if t.len == 3 and t.sons[0] == nil and t.sons[1].kind == tyVar: @@ -1131,6 +1140,7 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind, # semParamList(c, n.sons[ParamsPos], nil, s) else: s.typ = newProcType(c, n.info) + if tfTriggersCompileTime in s.typ.flags: incl(s.flags, sfCompileTime) if n.sons[patternPos].kind != nkEmpty: n.sons[patternPos] = semPattern(c, n.sons[patternPos]) if s.kind in skIterators: @@ -1425,7 +1435,7 @@ proc semStmtList(c: PContext, n: PNode, flags: TExprFlags): PNode = localError(result.info, "type class predicate failed") of tyUnknown: continue else: discard - if n.sons[i].typ == enforceVoidContext or usesResult(n.sons[i]): + if n.sons[i].typ == enforceVoidContext: #or usesResult(n.sons[i]): voidContext = true n.typ = enforceVoidContext if i == last and (length == 1 or efWantValue in flags): diff --git a/compiler/semtempl.nim b/compiler/semtempl.nim index 4d1eae48f..2dda8276d 100644 --- a/compiler/semtempl.nim +++ b/compiler/semtempl.nim @@ -110,6 +110,13 @@ type toBind, toMixin, toInject: IntSet owner: PSym cursorInBody: bool # only for nimsuggest + bracketExpr: PNode + +template withBracketExpr(ctx, x, body: untyped) = + let old = ctx.bracketExpr + ctx.bracketExpr = x + body + ctx.bracketExpr = old proc getIdentNode(c: var TemplCtx, n: PNode): PNode = case n.kind @@ -281,6 +288,47 @@ proc semTemplBodySons(c: var TemplCtx, n: PNode): PNode = for i in 0.. < n.len: result.sons[i] = semTemplBody(c, n.sons[i]) +proc wrapInBind(c: var TemplCtx; n: PNode; opr: string): PNode = + let ident = getIdent(opr) + if ident.id in c.toInject: return n + + let s = searchInScopes(c.c, ident) + if s != nil: + var callee: PNode + if contains(c.toBind, s.id): + callee = symChoice(c.c, n, s, scClosed) + elif contains(c.toMixin, s.name.id): + callee = symChoice(c.c, n, s, scForceOpen) + elif s.owner == c.owner and sfGenSym in s.flags: + # template tmp[T](x: var seq[T]) = + # var yz: T + incl(s.flags, sfUsed) + callee = newSymNode(s, n.info) + styleCheckUse(n.info, s) + else: + callee = semTemplSymbol(c.c, n, s) + + let call = newNodeI(nkCall, n.info) + call.add(callee) + for i in 0 .. n.len-1: call.add(n[i]) + result = newNodeI(nkBind, n.info, 2) + result.sons[0] = n + result.sons[1] = call + else: + result = n + +proc oprIsRoof(n: PNode): bool = + const roof = "^" + case n.kind + of nkIdent: result = n.ident.s == roof + of nkSym: result = n.sym.name.s == roof + of nkAccQuoted: + if n.len == 1: + result = oprIsRoof(n.sons[0]) + of nkOpenSymChoice, nkClosedSymChoice: + result = oprIsRoof(n.sons[0]) + else: discard + proc semTemplBody(c: var TemplCtx, n: PNode): PNode = result = n semIdeForTemplateOrGenericCheck(n, c.cursorInBody) @@ -423,24 +471,64 @@ proc semTemplBody(c: var TemplCtx, n: PNode): PNode = result.sons[1] = semTemplBody(c, n.sons[1]) of nkPragma: result = onlyReplaceParams(c, n) - else: + of nkBracketExpr: + result = newNodeI(nkCall, n.info) + result.add newIdentNode(getIdent("[]"), n.info) + for i in 0 ..< n.len: result.add(n[i]) + let n0 = semTemplBody(c, n.sons[0]) + withBracketExpr c, n0: + result = semTemplBodySons(c, result) + of nkCurlyExpr: + result = newNodeI(nkCall, n.info) + result.add newIdentNode(getIdent("{}"), n.info) + for i in 0 ..< n.len: result.add(n[i]) + result = semTemplBodySons(c, result) + of nkAsgn, nkFastAsgn: + checkSonsLen(n, 2) + let a = n.sons[0] + let b = n.sons[1] + + let k = a.kind + case k + of nkBracketExpr: + result = newNodeI(nkCall, n.info) + result.add newIdentNode(getIdent("[]="), n.info) + for i in 0 ..< a.len: result.add(a[i]) + result.add(b) + let a0 = semTemplBody(c, a.sons[0]) + withBracketExpr c, a0: + result = semTemplBodySons(c, result) + of nkCurlyExpr: + result = newNodeI(nkCall, n.info) + result.add newIdentNode(getIdent("{}="), n.info) + for i in 0 ..< a.len: result.add(a[i]) + result.add(b) + result = semTemplBodySons(c, result) + else: + result = semTemplBodySons(c, n) + of nkCallKinds-{nkPostfix}: + result = semTemplBodySons(c, n) + if c.bracketExpr != nil and n.len == 2 and oprIsRoof(n.sons[0]): + result.add c.bracketExpr + of nkDotExpr, nkAccQuoted: # dotExpr is ambiguous: note that we explicitly allow 'x.TemplateParam', # so we use the generic code for nkDotExpr too - if n.kind == nkDotExpr or n.kind == nkAccQuoted: - let s = qualifiedLookUp(c.c, n, {}) - if s != nil: - # do not symchoice a quoted template parameter (bug #2390): - if s.owner == c.owner and s.kind == skParam and - n.kind == nkAccQuoted and n.len == 1: - incl(s.flags, sfUsed) - styleCheckUse(n.info, s) - return newSymNode(s, n.info) - elif contains(c.toBind, s.id): - return symChoice(c.c, n, s, scClosed) - elif contains(c.toMixin, s.name.id): - return symChoice(c.c, n, s, scForceOpen) - else: - return symChoice(c.c, n, s, scOpen) + let s = qualifiedLookUp(c.c, n, {}) + if s != nil: + # do not symchoice a quoted template parameter (bug #2390): + if s.owner == c.owner and s.kind == skParam and + n.kind == nkAccQuoted and n.len == 1: + incl(s.flags, sfUsed) + styleCheckUse(n.info, s) + return newSymNode(s, n.info) + elif contains(c.toBind, s.id): + return symChoice(c.c, n, s, scClosed) + elif contains(c.toMixin, s.name.id): + return symChoice(c.c, n, s, scForceOpen) + else: + return symChoice(c.c, n, s, scOpen) + result = semTemplBodySons(c, n) + else: result = semTemplBodySons(c, n) proc semTemplBodyDirty(c: var TemplCtx, n: PNode): PNode = @@ -471,27 +559,6 @@ proc semTemplBodyDirty(c: var TemplCtx, n: PNode): PNode = for i in countup(0, sonsLen(n) - 1): result.sons[i] = semTemplBodyDirty(c, n.sons[i]) -proc transformToExpr(n: PNode): PNode = - var realStmt: int - result = n - case n.kind - of nkStmtList: - realStmt = - 1 - for i in countup(0, sonsLen(n) - 1): - case n.sons[i].kind - of nkCommentStmt, nkEmpty, nkNilLit: - discard - else: - if realStmt == - 1: realStmt = i - else: realStmt = - 2 - if realStmt >= 0: result = transformToExpr(n.sons[realStmt]) - else: n.kind = nkStmtListExpr - of nkBlockStmt: - n.kind = nkBlockExpr - #nkIfStmt: n.kind = nkIfExpr // this is not correct! - else: - discard - proc semTemplateDef(c: PContext, n: PNode): PNode = var s: PSym if isTopLevel(c): @@ -549,9 +616,7 @@ proc semTemplateDef(c: PContext, n: PNode): PNode = n.sons[bodyPos] = semTemplBodyDirty(ctx, n.sons[bodyPos]) else: n.sons[bodyPos] = semTemplBody(ctx, n.sons[bodyPos]) - if s.typ.sons[0].kind notin {tyStmt, tyTypeDesc}: - n.sons[bodyPos] = transformToExpr(n.sons[bodyPos]) - # only parameters are resolved, no type checking is performed + # only parameters are resolved, no type checking is performed semIdeForTemplateOrGeneric(c, n.sons[bodyPos], ctx.cursorInBody) closeScope(c) popOwner() @@ -604,6 +669,11 @@ proc semPatternBody(c: var TemplCtx, n: PNode): PNode = localError(n.info, errInvalidExpression) result = n + proc stupidStmtListExpr(n: PNode): bool = + for i in 0 .. n.len-2: + if n[i].kind notin {nkEmpty, nkCommentStmt}: return false + result = true + result = n case n.kind of nkIdent: @@ -629,6 +699,12 @@ proc semPatternBody(c: var TemplCtx, n: PNode): PNode = localError(n.info, errInvalidExpression) else: localError(n.info, errInvalidExpression) + of nkStmtList, nkStmtListExpr: + if stupidStmtListExpr(n): + result = semPatternBody(c, n.lastSon) + else: + for i in countup(0, sonsLen(n) - 1): + result.sons[i] = semPatternBody(c, n.sons[i]) of nkCallKinds: let s = qualifiedLookUp(c.c, n.sons[0], {}) if s != nil: diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim index 5ae3d16c0..65cb9421b 100644 --- a/compiler/semtypes.nim +++ b/compiler/semtypes.nim @@ -718,12 +718,12 @@ proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode, if paramType == nil: return # (e.g. proc return type) proc addImplicitGenericImpl(typeClass: PType, typId: PIdent): PType = - let finalTypId = if typId != nil: typId - else: getIdent(paramName & ":type") if genericParams == nil: # This happens with anonymous proc types appearing in signatures # XXX: we need to lift these earlier return + let finalTypId = if typId != nil: typId + else: getIdent(paramName & ":type") # is this a bindOnce type class already present in the param list? for i in countup(0, genericParams.len - 1): if genericParams.sons[i].sym.name.id == finalTypId.id: @@ -757,7 +757,7 @@ proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode, case paramType.kind: of tyAnything: - result = addImplicitGeneric(newTypeS(tyGenericParam, c)) + result = addImplicitGenericImpl(newTypeS(tyGenericParam, c), nil) of tyStatic: # proc(a: expr{string}, b: expr{nkLambda}) @@ -868,6 +868,7 @@ proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode, of tyExpr: if procKind notin {skMacro, skTemplate}: result = addImplicitGeneric(newTypeS(tyAnything, c)) + #result = addImplicitGenericImpl(newTypeS(tyGenericParam, c), nil) of tyGenericParam: markUsed(info, paramType.sym) @@ -970,18 +971,26 @@ proc semProcTypeNode(c: PContext, n, genericParams: PNode, elif kind == skIterator: # XXX This is special magic we should likely get rid of r = newTypeS(tyExpr, c) + message(n.info, warnDeprecated, "implicit return type for 'iterator'") if r != nil: # turn explicit 'void' return type into 'nil' because the rest of the # compiler only checks for 'nil': if skipTypes(r, {tyGenericInst}).kind != tyEmpty: # 'auto' as a return type does not imply a generic: - if r.kind != tyExpr: + if r.kind == tyAnything: + # 'p(): auto' and 'p(): expr' are equivalent, but the rest of the + # compiler is hardly aware of 'auto': + r = newTypeS(tyExpr, c) + elif r.kind != tyExpr: if r.sym == nil or sfAnon notin r.sym.flags: let lifted = liftParamType(c, kind, genericParams, r, "result", n.sons[0].info) - if lifted != nil: r = lifted - r.flags.incl tfRetType + if lifted != nil: + r = lifted + #if r.kind != tyGenericParam: + #echo "came here for ", typeToString(r) + r.flags.incl tfRetType r = skipIntLit(r) if kind == skIterator: # see tchainediterators @@ -1148,7 +1157,17 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType = else: result = semAnonTuple(c, n, prev) of nkCallKinds: - if isRange(n): + let x = n[0] + let ident = case x.kind + of nkIdent: x.ident + of nkSym: x.sym.name + of nkClosedSymChoice, nkOpenSymChoice: x[0].sym.name + else: nil + if ident != nil and ident.s == "[]": + let b = newNodeI(nkBracketExpr, n.info) + for i in 1..<n.len: b.add(n[i]) + result = semTypeNode(c, b, prev) + elif ident != nil and ident.id == ord(wDotDot): result = semRangeAux(c, n, prev) elif n[0].kind notin nkIdentKinds: result = semTypeExpr(c, n) @@ -1335,8 +1354,11 @@ proc processMagicType(c: PContext, m: PSym) = of mIntSetBaseType: setMagicType(m, tyRange, intSize) of mNil: setMagicType(m, tyNil, ptrSize) of mExpr: - setMagicType(m, tyExpr, 0) - if m.name.s == "expr": m.typ.flags.incl tfOldSchoolExprStmt + if m.name.s == "auto": + setMagicType(m, tyAnything, 0) + else: + setMagicType(m, tyExpr, 0) + if m.name.s == "expr": m.typ.flags.incl tfOldSchoolExprStmt of mStmt: setMagicType(m, tyStmt, 0) if m.name.s == "stmt": m.typ.flags.incl tfOldSchoolExprStmt @@ -1364,7 +1386,8 @@ proc processMagicType(c: PContext, m: PSym) = of mOrdinal: setMagicType(m, tyOrdinal, 0) rawAddSon(m.typ, newTypeS(tyNone, c)) - of mPNimrodNode: discard + of mPNimrodNode: + incl m.typ.flags, tfTriggersCompileTime of mShared: setMagicType(m, tyObject, 0) m.typ.n = newNodeI(nkRecList, m.info) diff --git a/compiler/semtypinst.nim b/compiler/semtypinst.nim index 3ac145eb8..f643fb903 100644 --- a/compiler/semtypinst.nim +++ b/compiler/semtypinst.nim @@ -90,6 +90,7 @@ type allowMetaTypes*: bool # allow types such as seq[Number] # i.e. the result contains unresolved generics skipTypedesc*: bool # wether we should skip typeDescs + owner*: PSym # where this instantiation comes from recursionLimit: int proc replaceTypeVarsTAux(cl: var TReplTypeVars, t: PType): PType @@ -208,6 +209,9 @@ proc replaceTypeVarsN(cl: var TReplTypeVars, n: PNode): PNode = proc replaceTypeVarsS(cl: var TReplTypeVars, s: PSym): PSym = if s == nil: return nil + # symbol is not our business: + if cl.owner != nil and s.owner != cl.owner: + return s result = PSym(idTableGet(cl.symMap, s)) if result == nil: result = copySym(s, false) @@ -477,22 +481,33 @@ proc replaceTypeVarsTAux(cl: var TReplTypeVars, t: PType): PType = else: discard -proc initTypeVars*(p: PContext, pt: TIdTable, info: TLineInfo): TReplTypeVars = +proc initTypeVars*(p: PContext, pt: TIdTable, info: TLineInfo; + owner: PSym): TReplTypeVars = initIdTable(result.symMap) copyIdTable(result.typeMap, pt) initIdTable(result.localCache) result.info = info result.c = p + result.owner = owner -proc replaceTypesInBody*(p: PContext, pt: TIdTable, n: PNode): PNode = - var cl = initTypeVars(p, pt, n.info) +proc replaceTypesInBody*(p: PContext, pt: TIdTable, n: PNode; + owner: PSym): PNode = + var cl = initTypeVars(p, pt, n.info, owner) + pushInfoContext(n.info) + result = replaceTypeVarsN(cl, n) + popInfoContext() + +proc replaceTypesForLambda*(p: PContext, pt: TIdTable, n: PNode; + original, new: PSym): PNode = + var cl = initTypeVars(p, pt, n.info, original) + idTablePut(cl.symMap, original, new) pushInfoContext(n.info) result = replaceTypeVarsN(cl, n) popInfoContext() proc generateTypeInstance*(p: PContext, pt: TIdTable, info: TLineInfo, t: PType): PType = - var cl = initTypeVars(p, pt, info) + var cl = initTypeVars(p, pt, info, nil) pushInfoContext(info) result = replaceTypeVarsT(cl, t) popInfoContext() diff --git a/compiler/sigmatch.nim b/compiler/sigmatch.nim index 61f1a7444..642f50330 100644 --- a/compiler/sigmatch.nim +++ b/compiler/sigmatch.nim @@ -511,6 +511,9 @@ proc typeRangeRel(f, a: PType): TTypeRelation {.noinline.} = proc matchUserTypeClass*(c: PContext, m: var TCandidate, ff, a: PType): TTypeRelation = var body = ff.skipTypes({tyUserTypeClassInst}) + if c.inTypeClass > 20: + localError(body.n[3].info, $body.n[3] & " too nested for type matching") + return isNone openScope(c) inc c.inTypeClass @@ -590,7 +593,7 @@ proc tryResolvingStaticExpr(c: var TCandidate, n: PNode): PNode = # Here, N-1 will be initially nkStaticExpr that can be evaluated only after # N is bound to a concrete value during the matching of the first param. # This proc is used to evaluate such static expressions. - let instantiated = replaceTypesInBody(c.c, c.bindings, n) + let instantiated = replaceTypesInBody(c.c, c.bindings, n, nil) result = c.c.semExpr(c.c, instantiated) proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation = @@ -1691,6 +1694,10 @@ proc partialMatch*(c: PContext, n, nOrig: PNode, m: var TCandidate) = matchesAux(c, n, nOrig, m, marker) proc matches*(c: PContext, n, nOrig: PNode, m: var TCandidate) = + if m.calleeSym != nil and m.calleeSym.magic in {mArrGet, mArrPut}: + m.state = csMatch + m.call = n + return var marker = initIntSet() matchesAux(c, n, nOrig, m, marker) if m.state == csNoMatch: return diff --git a/compiler/suggest.nim b/compiler/suggest.nim index c15a56c54..18d723315 100644 --- a/compiler/suggest.nim +++ b/compiler/suggest.nim @@ -309,6 +309,10 @@ proc suggestSym*(info: TLineInfo; s: PSym) {.inline.} = findUsages(info, s) elif gIdeCmd == ideDef: findDefinition(info, s) + elif gIdeCmd == ideDus and s != nil: + if isTracked(info, s.name.s.len): + suggestResult(symToSuggest(s, isLocal=false, $ideDef)) + findUsages(info, s) proc markUsed(info: TLineInfo; s: PSym) = incl(s.flags, sfUsed) @@ -335,8 +339,8 @@ proc suggestExpr*(c: PContext, node: PNode) = if cp == cpNone: return var outputs = 0 # This keeps semExpr() from coming here recursively: - if c.inCompilesContext > 0: return - inc(c.inCompilesContext) + if c.compilesContextId > 0: return + inc(c.compilesContextId) if gIdeCmd == ideSug: var n = if nfIsCursor in node.flags: node else: findClosestDot(node) @@ -365,8 +369,8 @@ proc suggestExpr*(c: PContext, node: PNode) = addSon(a, x) suggestCall(c, a, n, outputs) - dec(c.inCompilesContext) - if outputs > 0 and gIdeCmd != ideUse: suggestQuit() + dec(c.compilesContextId) + if outputs > 0 and gIdeCmd notin {ideUse, ideDus}: suggestQuit() proc suggestStmt*(c: PContext, n: PNode) = suggestExpr(c, n) diff --git a/compiler/syntaxes.nim b/compiler/syntaxes.nim index 91cfdbefd..021910544 100644 --- a/compiler/syntaxes.nim +++ b/compiler/syntaxes.nim @@ -92,10 +92,15 @@ proc parsePipe(filename: string, inputStream: PLLStream): PNode = var line = newStringOfCap(80) discard llStreamReadLine(s, line) var i = utf8Bom(line) + var linenumber = 1 if containsShebang(line, i): discard llStreamReadLine(s, line) i = 0 - if line[i] == '#' and line[i+1] == '!': + inc linenumber + if line[i] == '#' and line[i+1] in {'?', '!'}: + if line[i+1] == '!': + message(newLineInfo(filename, linenumber, 1), + warnDeprecated, "use '#?' instead; '#!'") inc(i, 2) while line[i] in Whitespace: inc(i) var q: TParser diff --git a/compiler/transf.nim b/compiler/transf.nim index 4ca40ab74..0ea9f7d80 100644 --- a/compiler/transf.nim +++ b/compiler/transf.nim @@ -139,23 +139,26 @@ proc transformVarSection(c: PTransf, v: PNode): PTransNode = if it.kind == nkCommentStmt: result[i] = PTransNode(it) elif it.kind == nkIdentDefs: - if it.sons[0].kind != nkSym: internalError(it.info, "transformVarSection") - internalAssert(it.len == 3) - var newVar = copySym(it.sons[0].sym) - incl(newVar.flags, sfFromGeneric) - # fixes a strange bug for rodgen: - #include(it.sons[0].sym.flags, sfFromGeneric); - newVar.owner = getCurrOwner(c) - idNodeTablePut(c.transCon.mapping, it.sons[0].sym, newSymNode(newVar)) - var defs = newTransNode(nkIdentDefs, it.info, 3) - if importantComments(): - # keep documentation information: - PNode(defs).comment = it.comment - defs[0] = newSymNode(newVar).PTransNode - defs[1] = it.sons[1].PTransNode - defs[2] = transform(c, it.sons[2]) - newVar.ast = defs[2].PNode - result[i] = defs + if it.sons[0].kind == nkSym: + internalAssert(it.len == 3) + var newVar = copySym(it.sons[0].sym) + incl(newVar.flags, sfFromGeneric) + # fixes a strange bug for rodgen: + #include(it.sons[0].sym.flags, sfFromGeneric); + newVar.owner = getCurrOwner(c) + idNodeTablePut(c.transCon.mapping, it.sons[0].sym, newSymNode(newVar)) + var defs = newTransNode(nkIdentDefs, it.info, 3) + if importantComments(): + # keep documentation information: + PNode(defs).comment = it.comment + defs[0] = newSymNode(newVar).PTransNode + defs[1] = it.sons[1].PTransNode + defs[2] = transform(c, it.sons[2]) + newVar.ast = defs[2].PNode + result[i] = defs + else: + # has been transformed into 'param.x' for closure iterators, so keep it: + result[i] = PTransNode(it) else: if it.kind != nkVarTuple: internalError(it.info, "transformVarSection: not nkVarTuple") diff --git a/compiler/types.nim b/compiler/types.nim index 5f3a74aca..66fb657fc 100644 --- a/compiler/types.nim +++ b/compiler/types.nim @@ -1265,13 +1265,16 @@ proc computeSizeAux(typ: PType, a: var BiggestInt): BiggestInt = else: result = 8 a = result of tySet: - length = lengthOrd(typ.sons[0]) - if length <= 8: result = 1 - elif length <= 16: result = 2 - elif length <= 32: result = 4 - elif length <= 64: result = 8 - elif align(length, 8) mod 8 == 0: result = align(length, 8) div 8 - else: result = align(length, 8) div 8 + 1 + if typ.sons[0].kind == tyGenericParam: + result = szUnknownSize + else: + length = lengthOrd(typ.sons[0]) + if length <= 8: result = 1 + elif length <= 16: result = 2 + elif length <= 32: result = 4 + elif length <= 64: result = 8 + elif align(length, 8) mod 8 == 0: result = align(length, 8) div 8 + else: result = align(length, 8) div 8 + 1 a = result of tyRange: result = computeSizeAux(typ.sons[0], a) diff --git a/compiler/vm.nim b/compiler/vm.nim index 05d00c19f..ded66d3d0 100644 --- a/compiler/vm.nim +++ b/compiler/vm.nim @@ -311,7 +311,7 @@ proc opConv*(dest: var TFullReg, src: TFullReg, desttyp, srctyp: PType): bool = if f.position == x: dest.node.strVal = if f.ast.isNil: f.name.s else: f.ast.strVal return - internalError("opConv for enum") + dest.node.strVal = styp.sym.name.s & " " & $x of tyInt..tyInt64: dest.node.strVal = $src.intVal of tyUInt..tyUInt64: @@ -320,8 +320,19 @@ proc opConv*(dest: var TFullReg, src: TFullReg, desttyp, srctyp: PType): bool = dest.node.strVal = if src.intVal == 0: "false" else: "true" of tyFloat..tyFloat128: dest.node.strVal = $src.floatVal - of tyString, tyCString: + of tyString: dest.node.strVal = src.node.strVal + of tyCString: + if src.node.kind == nkBracket: + # Array of chars + var strVal = "" + for son in src.node.sons: + let c = char(son.intVal) + if c == '\0': break + strVal.add(c) + dest.node.strVal = strVal + else: + dest.node.strVal = src.node.strVal of tyChar: dest.node.strVal = $chr(src.intVal) else: @@ -1400,6 +1411,31 @@ proc execute(c: PCtx, start: int): PNode = newSeq(tos.slots, c.prc.maxSlots) result = rawExecute(c, start, tos).regToNode +proc execProc*(c: PCtx; sym: PSym; args: openArray[PNode]): PNode = + if sym.kind in routineKinds: + if sym.typ.len-1 != args.len: + localError(sym.info, + "NimScript: expected $# arguments, but got $#" % [ + $(sym.typ.len-1), $args.len]) + else: + let start = genProc(c, sym) + + var tos = PStackFrame(prc: sym, comesFrom: 0, next: nil) + let maxSlots = sym.offset + newSeq(tos.slots, maxSlots) + + # setup parameters: + if not isEmptyType(sym.typ.sons[0]) or sym.kind == skMacro: + putIntoReg(tos.slots[0], getNullValue(sym.typ.sons[0], sym.info)) + # XXX We could perform some type checking here. + for i in 1.. <sym.typ.len: + putIntoReg(tos.slots[i], args[i-1]) + + result = rawExecute(c, start, tos).regToNode + else: + localError(sym.info, + "NimScript: attempt to call non-routine: " & sym.name.s) + proc evalStmt*(c: PCtx, n: PNode) = let n = transformExpr(c.module, n) let start = genStmt(c, n) @@ -1461,6 +1497,8 @@ proc evalConstExprAux(module, prc: PSym, n: PNode, mode: TEvalMode): PNode = let n = transformExpr(module, n) setupGlobalCtx(module) var c = globalCtx + let oldMode = c.mode + defer: c.mode = oldMode c.mode = mode let start = genExpr(c, n, requiresValue = mode!=emStaticStmt) if c.code[start].opcode == opcEof: return emptyNode diff --git a/compiler/vmgen.nim b/compiler/vmgen.nim index 32982602b..92db0d513 100644 --- a/compiler/vmgen.nim +++ b/compiler/vmgen.nim @@ -37,7 +37,7 @@ when hasFFI: import evalffi type - TGenFlag = enum gfNone, gfAddrOf + TGenFlag = enum gfAddrOf, gfFieldAccess TGenFlags = set[TGenFlag] proc debugInfo(info: TLineInfo): string = @@ -535,7 +535,7 @@ proc genIndex(c: PCtx; n: PNode; arr: PType): TRegister = proc genAsgnPatch(c: PCtx; le: PNode, value: TRegister) = case le.kind of nkBracketExpr: - let dest = c.genx(le.sons[0], {gfAddrOf}) + let dest = c.genx(le.sons[0], {gfAddrOf, gfFieldAccess}) let idx = c.genIndex(le.sons[1], le.sons[0].typ) c.gABC(le, opcWrArr, dest, idx, value) c.freeTemp(dest) @@ -543,7 +543,7 @@ proc genAsgnPatch(c: PCtx; le: PNode, value: TRegister) = of nkDotExpr, nkCheckedFieldExpr: # XXX field checks here let left = if le.kind == nkDotExpr: le else: le.sons[0] - let dest = c.genx(left.sons[0], {gfAddrOf}) + let dest = c.genx(left.sons[0], {gfAddrOf, gfFieldAccess}) let idx = genField(left.sons[1]) c.gABC(left, opcWrObj, dest, idx, value) c.freeTemp(dest) @@ -1216,7 +1216,7 @@ proc preventFalseAlias(c: PCtx; n: PNode; opc: TOpcode; proc genAsgn(c: PCtx; le, ri: PNode; requiresCopy: bool) = case le.kind of nkBracketExpr: - let dest = c.genx(le.sons[0], {gfAddrOf}) + let dest = c.genx(le.sons[0], {gfAddrOf, gfFieldAccess}) let idx = c.genIndex(le.sons[1], le.sons[0].typ) let tmp = c.genx(ri) if le.sons[0].typ.skipTypes(abstractVarRange-{tyTypeDesc}).kind in { @@ -1228,7 +1228,7 @@ proc genAsgn(c: PCtx; le, ri: PNode; requiresCopy: bool) = of nkDotExpr, nkCheckedFieldExpr: # XXX field checks here let left = if le.kind == nkDotExpr: le else: le.sons[0] - let dest = c.genx(left.sons[0], {gfAddrOf}) + let dest = c.genx(left.sons[0], {gfAddrOf, gfFieldAccess}) let idx = genField(left.sons[1]) let tmp = c.genx(ri) c.preventFalseAlias(left, opcWrObj, dest, idx, tmp) @@ -1321,7 +1321,7 @@ proc genRdVar(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags) = c.gABx(n, opcLdGlobal, cc, s.position) c.gABC(n, opcNodeToReg, dest, cc) c.freeTemp(cc) - elif gfAddrOf in flags: + elif {gfAddrOf, gfFieldAccess} * flags == {gfAddrOf}: c.gABx(n, opcLdGlobalAddr, dest, s.position) else: c.gABx(n, opcLdGlobal, dest, s.position) @@ -1377,9 +1377,11 @@ proc genCheckedObjAccess(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags) = genObjAccess(c, n.sons[0], dest, flags) proc genArrAccess(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags) = - if n.sons[0].typ.skipTypes(abstractVarRange-{tyTypeDesc}).kind in { - tyString, tyCString}: + let arrayType = n.sons[0].typ.skipTypes(abstractVarRange-{tyTypeDesc}).kind + if arrayType in {tyString, tyCString}: genArrAccess2(c, n, dest, opcLdStrIdx, {}) + elif arrayType == tyTypeDesc: + c.genTypeLit(n.typ, dest) else: genArrAccess2(c, n, dest, opcLdArr, flags) @@ -1771,6 +1773,9 @@ proc genExpr*(c: PCtx; n: PNode, requiresValue = true): int = d = 0 c.gABC(n, opcEof, d) + #echo renderTree(n) + #c.echoCode(result) + proc genParams(c: PCtx; params: PNode) = # res.sym.position is already 0 c.prc.slots[0] = (inUse: true, kind: slotFixedVar) diff --git a/compiler/vmhooks.nim b/compiler/vmhooks.nim index 5dd27feda..576b0565f 100644 --- a/compiler/vmhooks.nim +++ b/compiler/vmhooks.nim @@ -30,6 +30,14 @@ proc setResult*(a: VmArgs; v: string) = s[a.ra].node = newNode(nkStrLit) s[a.ra].node.strVal = v +proc setResult*(a: VmArgs; n: PNode) = + var s: seq[TFullReg] + move(s, cast[seq[TFullReg]](a.slots)) + if s[a.ra].kind != rkNode: + myreset(s[a.ra]) + s[a.ra].kind = rkNode + s[a.ra].node = n + proc setResult*(a: VmArgs; v: seq[string]) = var s: seq[TFullReg] move(s, cast[seq[TFullReg]](a.slots)) diff --git a/compiler/wordrecg.nim b/compiler/wordrecg.nim index 23f012ea5..0a0534118 100644 --- a/compiler/wordrecg.nim +++ b/compiler/wordrecg.nim @@ -42,7 +42,7 @@ type wImmediate, wConstructor, wDestructor, wDelegator, wOverride, wImportCpp, wImportObjC, wImportCompilerProc, - wImportc, wExportc, wIncompleteStruct, wRequiresInit, + wImportc, wExportc, wExportNims, wIncompleteStruct, wRequiresInit, wAlign, wNodecl, wPure, wSideeffect, wHeader, wNosideeffect, wGcSafe, wNoreturn, wMerge, wLib, wDynlib, wCompilerproc, wProcVar, wBase, @@ -82,6 +82,7 @@ type wStdIn, wStdOut, wStdErr, wInOut, wByCopy, wByRef, wOneWay, + wBitsize, TSpecialWords* = set[TSpecialWord] @@ -125,7 +126,8 @@ const "immediate", "constructor", "destructor", "delegator", "override", "importcpp", "importobjc", - "importcompilerproc", "importc", "exportc", "incompletestruct", + "importcompilerproc", "importc", "exportc", "exportnims", + "incompletestruct", "requiresinit", "align", "nodecl", "pure", "sideeffect", "header", "nosideeffect", "gcsafe", "noreturn", "merge", "lib", "dynlib", "compilerproc", "procvar", "base", @@ -168,6 +170,7 @@ const "stdin", "stdout", "stderr", "inout", "bycopy", "byref", "oneway", + "bitsize", ] proc findStr*(a: openArray[string], s: string): int = diff --git a/compiler/writetracking.nim b/compiler/writetracking.nim index 38258b07a..141b496c1 100644 --- a/compiler/writetracking.nim +++ b/compiler/writetracking.nim @@ -200,7 +200,7 @@ proc deps(w: var W; n: PNode) = proc possibleAliases(w: var W; result: var seq[ptr TSym]) = # this is an expensive fixpoint iteration. We could speed up this analysis - # by a smarter data-structure but we wait until prolifing shows us it's + # by a smarter data-structure but we wait until profiling shows us it's # expensive. Usually 'w.assignments' is small enough. var alreadySeen = initIntSet() template addNoDup(x) = diff --git a/config/nim.cfg b/config/nim.cfg index 56bfbc64d..b8608b3ce 100644 --- a/config/nim.cfg +++ b/config/nim.cfg @@ -27,28 +27,20 @@ mips.linux.gcc.linkerexe = "mips-openwrt-linux-gcc" cs:partial @end -path="$lib/core" - -path="$lib/pure" +path="$lib/deprecated/core" +path="$lib/deprecated/pure" path="$lib/pure/collections" path="$lib/pure/concurrency" path="$lib/impure" path="$lib/wrappers" -# path="$lib/wrappers/cairo" -# path="$lib/wrappers/gtk" -# path="$lib/wrappers/lua" -# path="$lib/wrappers/opengl" -path="$lib/wrappers/pcre" path="$lib/wrappers/linenoise" -path="$lib/wrappers/sdl" -# path="$lib/wrappers/x11" -path="$lib/wrappers/zip" -path="$lib/wrappers/libffi" path="$lib/windows" path="$lib/posix" path="$lib/js" path="$lib/pure/unidecode" path="$lib/arch" +path="$lib/core" +path="$lib/pure" @if nimbabel: nimblepath="$home/.nimble/pkgs/" diff --git a/config/nimdoc.cfg b/config/nimdoc.cfg index 4773258cf..23151b275 100644 --- a/config/nimdoc.cfg +++ b/config/nimdoc.cfg @@ -145,7 +145,7 @@ body { font-weight: 400; font-size: 14px; line-height: 20px; - color: #2d2d2d; + color: #666; background-color: rgba(252, 248, 244, 0.75); } /* Skeleton grid */ diff --git a/doc/filelist.txt b/doc/filelist.txt index beff8f6c8..71379b40d 100644 --- a/doc/filelist.txt +++ b/doc/filelist.txt @@ -4,12 +4,12 @@ Short description of Nim's modules ============== ========================================================== Module Description ============== ========================================================== -nim main module: parses the command line and calls +nim main module: parses the command line and calls ``main.MainCommand`` main implements the top-level command dispatching nimconf implements the config file reader syntaxes dispatcher for the different parsers and filters -filter_tmpl standard template filter (``#! stdtempl``) +filter_tmpl standard template filter (``#? stdtempl``) lexbase buffer handling of the lexical analyser lexer lexical analyser parser Nim's parser diff --git a/doc/filters.txt b/doc/filters.txt index afbd61e3c..46bc6c3e5 100644 --- a/doc/filters.txt +++ b/doc/filters.txt @@ -8,9 +8,9 @@ A `Source Code Filter` transforms the input character stream to an in-memory output stream before parsing. A filter can be used to provide templating systems or preprocessors. -To use a filter for a source file the *shebang* notation is used:: +To use a filter for a source file the ``#?`` notation is used:: - #! stdtmpl(subsChar = '$', metaChar = '#') + #? stdtmpl(subsChar = '$', metaChar = '#') #proc generateXML(name, age: string): string = # result = "" <xml> @@ -20,7 +20,8 @@ To use a filter for a source file the *shebang* notation is used:: As the example shows, passing arguments to a filter can be done just like an ordinary procedure call with named or positional arguments. The -available parameters depend on the invoked filter. +available parameters depend on the invoked filter. Before version 0.12.0 of +the language ``#!`` was used instead of ``#?``. Pipe operator @@ -28,7 +29,7 @@ Pipe operator Filters can be combined with the ``|`` pipe operator:: - #! strip(startswith="<") | stdtmpl + #? strip(startswith="<") | stdtmpl #proc generateXML(name, age: string): string = # result = "" <xml> @@ -104,7 +105,7 @@ Parameters and their defaults: Example:: - #! stdtmpl | standard + #? stdtmpl | standard #proc generateHTMLPage(title, currentTab, content: string, # tabs: openArray[string]): string = # result = "" @@ -172,7 +173,7 @@ produces ``$``. The template engine is quite flexible. It is easy to produce a procedure that writes the template code directly to a file:: - #! stdtmpl(emit="f.write") | standard + #? stdtmpl(emit="f.write") | standard #proc writeHTMLPage(f: File, title, currentTab, content: string, # tabs: openArray[string]) = <head><title>$title</title></head> diff --git a/doc/lib.txt b/doc/lib.txt index 6a8f32e07..3dc58eebf 100644 --- a/doc/lib.txt +++ b/doc/lib.txt @@ -122,7 +122,7 @@ String handling This module contains various string matchers for email addresses, etc. * `subexes <subexes.html>`_ - This module implements advanted string substitution operations. + This module implements advanced string substitution operations. Generic Operating System Services @@ -201,12 +201,6 @@ Internet Protocols and Support * `scgi <scgi.html>`_ This module implements helpers for SCGI applications. -* `sockets <sockets.html>`_ - This module implements a simple portable type-safe sockets layer. - -* `asyncio <asyncio.html>`_ - This module implements an asynchronous event loop for sockets. - * `browsers <browsers.html>`_ This module implements procs for opening URLs with the user's default browser. @@ -220,9 +214,6 @@ Internet Protocols and Support * `smtp <smtp.html>`_ This module implement a simple SMTP client. -* `ftpclient <ftpclient.html>`_ - This module implements an FTP client. - * `cookies <cookies.html>`_ This module contains helper procs for parsing and generating cookies. @@ -253,7 +244,7 @@ Internet Protocols and Support This module implements a high-level sockets API. It will replace the ``sockets`` module in the future. -* `rawsockets <rawsockets.html>`_ +* `nativesockets <nativesockets.html>`_ This module implements a low-level sockets API. * `selectors <selectors.html>`_ @@ -265,8 +256,6 @@ Parsers * `parseopt <parseopt.html>`_ The ``parseopt`` module implements a command line option parser. - **Deprecated since version 0.9.3:** Use the `parseopt2 - <parseopt2.html>`_ module instead. * `parseopt2 <parseopt2.html>`_ The ``parseopt2`` module implements a command line option parser. This @@ -397,6 +386,31 @@ Modules for JS backend Declaration of the Document Object Model for the JS backend. +Deprecated modules +------------------ + +* `asyncio <asyncio.html>`_ + This module implements an asynchronous event loop for sockets. + **Deprecated since version 0.11.2:** + Use the `asyncnet <asyncnet.html>`_ together with the + `asyncdispatch <asyncdispatch.html>`_ module instead. + +* `ftpclient <ftpclient.html>`_ + This module implements an FTP client. + **Deprecated since version 0.11.3:** + Use the `asyncftpclient <asyncftpclient.html>`_ module instead. + +* `sockets <sockets.html>`_ + This module implements a simple portable type-safe sockets layer. + **Deprecated since version 0.11.2:** + Use the `net <net.html>`_ or the `rawsockets <rawsockets.html>`_ module + instead. + +* `rawsockets <rawsockets.html>`_ + **Deprecated since version 0.11.4:** + This module has been renamed to `nativesockets <nativesockets.html>`_. + + Impure libraries ================ diff --git a/doc/manual/generics.txt b/doc/manual/generics.txt index 8f7dcd580..c6206d030 100644 --- a/doc/manual/generics.txt +++ b/doc/manual/generics.txt @@ -116,7 +116,7 @@ type class matches ``array`` any array type ``set`` any set type ``seq`` any seq type -``auto`` any type +``any`` any type ================== =================================================== Furthermore, every generic type automatically creates a type class of the same @@ -163,15 +163,6 @@ module to illustrate this: Alternatively, the ``distinct`` type modifier can be applied to the type class to allow each param matching the type class to bind to a different type. -If a proc param doesn't have a type specified, Nim will use the -``distinct auto`` type class (also known as ``any``). Note this behavior is -deprecated for procs; templates, however, support them: - -.. code-block:: nim - # allow any combination of param types - proc concat(a, b): string = $a & $b # deprecated - proc concat(a, b: any): string = $a & $b # preferred - Procs written with the implicitly generic style will often need to refer to the type parameters of the matched generic type. They can be easily accessed using the dot syntax: diff --git a/doc/manual/pragmas.txt b/doc/manual/pragmas.txt index 8166994a9..f89194c9a 100644 --- a/doc/manual/pragmas.txt +++ b/doc/manual/pragmas.txt @@ -72,7 +72,20 @@ compileTime pragma ------------------ The ``compileTime`` pragma is used to mark a proc or variable to be used at compile time only. No code will be generated for it. Compile time procs are -useful as helpers for macros. +useful as helpers for macros. Since version 0.12.0 of the language, a proc +that uses ``system.NimNode`` within its parameter types is implictly declared +``compileTime``: + +.. code-block:: nim + proc astHelper(n: NimNode): NimNode = + result = n + +Is the same as: + +.. code-block:: nim + proc astHelper(n: NimNode): NimNode {.compileTime.} = + result = n + noReturn pragma --------------- @@ -518,6 +531,24 @@ Implementation Specific Pragmas This section describes additional pragmas that the current Nim implementation supports but which should not be seen as part of the language specification. +Bitsize pragma +-------------- + +The ``bitsize`` pragma is for object field members. It declares the field as +a bitfield in C/C++. + +.. code-block:: Nim + type + mybitfield = object + flag {.bitsize:1.}: cuint + +generates: + +.. code-block:: C + struct mybitfield { + unsigned int flag:1; + }; + Volatile pragma --------------- diff --git a/doc/manual/procs.txt b/doc/manual/procs.txt index 9d00b7e3c..ee74b2ea6 100644 --- a/doc/manual/procs.txt +++ b/doc/manual/procs.txt @@ -137,6 +137,14 @@ to supply any type of first argument for procedures: Another way to look at the method call syntax is that it provides the missing postfix notation. +The method call syntax conflicts with explicit generic instantiations: +``p[T](x)`` cannot be written as ``x.p[T]`` because ``x.p[T]`` is always +parsed as ``(x.p)[T]``. + +**Future directions**: ``p[.T.]`` might be introduced as an alternative syntax +to pass explict types to a generic and then ``x.p[.T.]`` can be parsed as +``x.(p[.T.])``. + See also: `Limitations of the method call syntax`_. diff --git a/doc/manual/stmts.txt b/doc/manual/stmts.txt index 062c08d7c..a833d7b7d 100644 --- a/doc/manual/stmts.txt +++ b/doc/manual/stmts.txt @@ -60,6 +60,24 @@ An empty ``discard`` statement is often used as a null statement: else: discard +Void context +------------ + +In a list of statements every expression except the last one needs to have the +type ``void``. In addition to this rule an assignment to the builtin ``result`` +symbol also triggers a ``void`` context: + +.. code-block:: nim + proc invalid*(): string = + result = "foo" + "invalid" # Error: value of type 'string' has to be discarded + +.. code-block:: nim + proc valid*(): string = + let x = 317 + "valid" + + Var statement ------------- diff --git a/doc/manual/syntax.txt b/doc/manual/syntax.txt index 99af15948..c444a3995 100644 --- a/doc/manual/syntax.txt +++ b/doc/manual/syntax.txt @@ -68,13 +68,13 @@ Strong spaces ------------- The number of spaces preceding a non-keyword operator affects precedence -if the experimental parser directive ``#!strongSpaces`` is used. Indentation +if the experimental parser directive ``#?strongSpaces`` is used. Indentation is not used to determine the number of spaces. If 2 or more operators have the same number of preceding spaces the precedence table applies, so ``1 + 3 * 4`` is still parsed as ``1 + (3 * 4)``, but ``1+3 * 4`` is parsed as ``(1+3) * 4``: .. code-block:: nim - #! strongSpaces + #? strongSpaces if foo+4 * 4 == 8 and b&c | 9 ++ bar: echo "" @@ -86,7 +86,7 @@ Furthermore whether an operator is used a prefix operator is affected by the number of spaces: .. code-block:: nim - #! strongSpaces + #? strongSpaces echo $foo # is parsed as echo($foo) @@ -95,7 +95,7 @@ This also affects whether ``[]``, ``{}``, ``()`` are parsed as constructors or as accessors: .. code-block:: nim - #! strongSpaces + #? strongSpaces echo (1,2) # is parsed as echo((1,2)) diff --git a/doc/manual/types.txt b/doc/manual/types.txt index 44a20d093..babf3286f 100644 --- a/doc/manual/types.txt +++ b/doc/manual/types.txt @@ -490,6 +490,11 @@ to an open array parameter. The openarray type cannot be nested: multidimensional openarrays are not supported because this is seldom needed and cannot be done efficiently. +.. code-block:: nim + proc testOpenArray(x: openArray[int]) = echo repr(x) + + testOpenArray([1,2,3]) # array[] + testOpenArray(@[1,2,3]) # seq[] Varargs ------- @@ -1228,3 +1233,27 @@ However, a ``void`` type cannot be inferred in generic code: The ``void`` type is only valid for parameters and return types; other symbols cannot have the type ``void``. + + +Auto type +--------- + +The ``auto`` type can only be used for return types and parameters. For return +types it causes the compiler to infer the type from the routine body: + +.. code-block:: nim + proc returnsInt(): auto = 1984 + +For parameters it currently creates implicitly generic routines: + +.. code-block:: nim + proc foo(a, b: auto) = discard + +Is the same as: + +.. code-block:: nim + proc foo[T1, T2](a: T1, b: T2) = discard + +However later versions of the language might change this to mean "infer the +parameters' types from the body". Then the above ``foo`` would be rejected as +the parameters' types can not be infered from an empty ``discard`` statement. diff --git a/doc/nims.txt b/doc/nims.txt index 2b9df4a87..7c76efe42 100644 --- a/doc/nims.txt +++ b/doc/nims.txt @@ -9,7 +9,8 @@ system. So instead of a ``myproject.nim.cfg`` configuration file, you can use a ``myproject.nims`` file that simply contains Nim code controlling the -compilation process. +compilation process. For a directory wide configuration, use ``config.nims`` +instead of ``nim.cfg``. The VM cannot deal with ``importc``, the FFI is not available, so there are not many stdlib modules that you can use with Nim's VM. However, at least the diff --git a/doc/subexes.txt b/doc/subexes.txt index f43983e09..54304f033 100644 --- a/doc/subexes.txt +++ b/doc/subexes.txt @@ -1,7 +1,7 @@ Substitution Expressions (subex) ================================ -A *subex* (*Substitution Expression*) represents an advanted string +A *subex* (*Substitution Expression*) represents an advanced string substitution. In contrast to a `regex`:idx: which deals with string analysis, a *subex* deals with string synthesis. diff --git a/examples/curlex.nim b/examples/curlex.nim deleted file mode 100644 index 21786a6ee..000000000 --- a/examples/curlex.nim +++ /dev/null @@ -1,10 +0,0 @@ -import - libcurl - -var hCurl = easy_init() -if hCurl != nil: - discard easy_setopt(hCurl, OPT_VERBOSE, true) - discard easy_setopt(hCurl, OPT_URL, "http://nim-lang.org/") - discard easy_perform(hCurl) - easy_cleanup(hCurl) - diff --git a/examples/filterex.nim b/examples/filterex.nim index d9bfb6782..083945254 100644 --- a/examples/filterex.nim +++ b/examples/filterex.nim @@ -1,4 +1,4 @@ -#! stdtmpl | standard +#? stdtmpl | standard #proc generateHTMLPage(title, currentTab, content: string, # tabs: openArray[string]): string = # result = "" diff --git a/examples/iupex1.nim b/examples/iupex1.nim deleted file mode 100644 index f768fb23f..000000000 --- a/examples/iupex1.nim +++ /dev/null @@ -1,37 +0,0 @@ -# Example IUP program - -# iupTabs: Creates a iupTabs control. - -import iup - -discard iup.open(nil, nil) - -var vbox1 = iup.vbox(iup.label("Inside Tab A"), iup.button("Button A", ""), nil) -var vbox2 = iup.vbox(iup.label("Inside Tab B"), iup.button("Button B", ""), nil) - -iup.setAttribute(vbox1, "TABTITLE", "Tab A") -iup.setAttribute(vbox2, "TABTITLE", "Tab B") - -var tabs1 = iup.tabs(vbox1, vbox2, nil) - -vbox1 = iup.vbox(iup.label("Inside Tab C"), iup.button("Button C", ""), nil) -vbox2 = iup.vbox(iup.label("Inside Tab D"), iup.button("Button D", ""), nil) - -iup.setAttribute(vbox1, "TABTITLE", "Tab C") -iup.setAttribute(vbox2, "TABTITLE", "Tab D") - -var tabs2 = iup.tabs(vbox1, vbox2, nil) -iup.setAttribute(tabs2, "TABTYPE", "LEFT") - -var box = iup.hbox(tabs1, tabs2, nil) -iup.setAttribute(box, "MARGIN", "10x10") -iup.setAttribute(box, "GAP", "10") - -var dlg = iup.dialog(box) -iup.setAttribute(dlg, "TITLE", "iupTabs") -iup.setAttribute(dlg, "SIZE", "200x100") - -discard showXY(dlg, IUP_CENTER, IUP_CENTER) -discard mainLoop() -close() - diff --git a/install.sh.template b/install.sh.template deleted file mode 100644 index 1292abdab..000000000 --- a/install.sh.template +++ /dev/null @@ -1,9 +0,0 @@ -#!/bin/sh -set -e -set -x - -if [ "$1" != "" ]; then - exec ./koch install "$1" -else - exec ./koch install -fi diff --git a/install.txt b/install.txt index ef367639c..55e8d8ce5 100644 --- a/install.txt +++ b/install.txt @@ -62,7 +62,7 @@ Currently, the following C compilers are supported under Windows: However, most testing is done with GCC. -Bootstrapping from Github +Bootstrapping from GitHub ------------------------- Take a look at the readme file on github `here <https://github.com/nim-lang/Nim#readme>`_ diff --git a/koch.nim b/koch.nim index 8992271bf..3d1a22b9c 100644 --- a/koch.nim +++ b/koch.nim @@ -41,6 +41,7 @@ Options: Possible Commands: boot [options] bootstraps with given command line options install [bindir] installs to given directory; Unix only! + geninstall generate ./install.sh; Unix only! clean cleans Nim project; removes generated files web [options] generates the website and the full documentation website [options] generates only the website @@ -127,9 +128,12 @@ proc nsis(args: string) = exec(("tools" / "niminst" / "niminst --var:version=$# --var:mingw=mingw$#" & " nsis compiler/installer.ini") % [VersionAsString, $(sizeof(pointer)*8)]) +proc geninstall(args="") = + exec("$# cc -r $# --var:version=$# --var:mingw=none --main:compiler/nim.nim scripts compiler/installer.ini $#" % + [findNim(), compileNimInst, VersionAsString, args]) + proc install(args: string) = - exec("$# cc -r $# --var:version=$# --var:mingw=none --main:compiler/nim.nim scripts compiler/installer.ini" % - [findNim(), compileNimInst, VersionAsString]) + geninstall() exec("sh ./install.sh $#" % args) proc web(args: string) = @@ -373,6 +377,7 @@ of cmdArgument: of "zip": zip(op.cmdLineRest) of "xz": xz(op.cmdLineRest) of "nsis": nsis(op.cmdLineRest) + of "geninstall": geninstall(op.cmdLineRest) of "install": install(op.cmdLineRest) of "test", "tests": tests(op.cmdLineRest) of "update": diff --git a/lib/core/unsigned.nim b/lib/deprecated/core/unsigned.nim index 93a29e1c9..93a29e1c9 100644 --- a/lib/core/unsigned.nim +++ b/lib/deprecated/core/unsigned.nim diff --git a/lib/pure/actors.nim b/lib/deprecated/pure/actors.nim index f0791f954..f0791f954 100644 --- a/lib/pure/actors.nim +++ b/lib/deprecated/pure/actors.nim diff --git a/lib/pure/actors.nim.cfg b/lib/deprecated/pure/actors.nim.cfg index c6bb9c545..c6bb9c545 100644 --- a/lib/pure/actors.nim.cfg +++ b/lib/deprecated/pure/actors.nim.cfg diff --git a/lib/pure/asyncio.nim b/lib/deprecated/pure/asyncio.nim index 5fd45b215..5fd45b215 100644 --- a/lib/pure/asyncio.nim +++ b/lib/deprecated/pure/asyncio.nim diff --git a/lib/pure/ftpclient.nim b/lib/deprecated/pure/ftpclient.nim index 778ba6857..1188c0795 100644 --- a/lib/pure/ftpclient.nim +++ b/lib/deprecated/pure/ftpclient.nim @@ -11,9 +11,14 @@ include "system/inclrtl" import sockets, strutils, parseutils, times, os, asyncio from asyncnet import nil -from rawsockets import nil +from nativesockets import nil from asyncdispatch import PFuture - +## **Note**: This module is deprecated since version 0.11.3. +## You should use the async version of this module +## `asyncftpclient <asyncftpclient.html>`_. +## +## ---- +## ## This module **partially** implements an FTP client as specified ## by `RFC 959 <http://tools.ietf.org/html/rfc959>`_. ## @@ -36,6 +41,8 @@ from asyncdispatch import PFuture ## **Warning:** The API of this module is unstable, and therefore is subject ## to change. +{.deprecated.} + type FtpBase*[SockType] = ref FtpBaseObj[SockType] FtpBaseObj*[SockType] = object @@ -48,7 +55,7 @@ type user*, pass*: string address*: string when SockType is asyncnet.AsyncSocket: - port*: rawsockets.Port + port*: nativesockets.Port else: port*: Port diff --git a/lib/pure/parseopt.nim b/lib/deprecated/pure/parseopt.nim index 218f5ab81..218f5ab81 100644 --- a/lib/pure/parseopt.nim +++ b/lib/deprecated/pure/parseopt.nim diff --git a/lib/pure/parseurl.nim b/lib/deprecated/pure/parseurl.nim index 6d58e8a73..6d58e8a73 100644 --- a/lib/pure/parseurl.nim +++ b/lib/deprecated/pure/parseurl.nim diff --git a/lib/deprecated/pure/rawsockets.nim b/lib/deprecated/pure/rawsockets.nim new file mode 100644 index 000000000..ee77b232e --- /dev/null +++ b/lib/deprecated/pure/rawsockets.nim @@ -0,0 +1,14 @@ +import nativesockets +export nativesockets + +{.warning: "rawsockets module is deprecated, use nativesockets instead".} + +template newRawSocket*(domain, sockType, protocol: cint): expr = + {.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 = + {.warning: "newRawSocket is deprecated, use newNativeSocket instead".} + newNativeSocket(domain, sockType, protocol) diff --git a/lib/pure/sockets.nim b/lib/deprecated/pure/sockets.nim index 29f0bf87d..5d6fa0078 100644 --- a/lib/pure/sockets.nim +++ b/lib/deprecated/pure/sockets.nim @@ -9,7 +9,7 @@ ## **Warning:** Since version 0.10.2 this module is deprecated. ## Use the `net <net.html>`_ or the -## `rawsockets <rawsockets.html>`_ module instead. +## `nativesockets <nativesockets.html>`_ module instead. ## ## This module implements portable sockets, it supports a mix of different types ## of sockets. Sockets are buffered by default meaning that data will be @@ -314,10 +314,7 @@ when defined(ssl): of protSSLv23: newCTX = SSL_CTX_new(SSLv23_method()) # SSlv2,3 and TLS1 support. of protSSLv2: - when not defined(linux) and not defined(OpenBSD): - newCTX = SSL_CTX_new(SSLv2_method()) - else: - raiseSslError() + raiseSslError("SSLv2 is no longer secure and has been deprecated, use protSSLv3") of protSSLv3: newCTX = SSL_CTX_new(SSLv3_method()) of protTLSv1: diff --git a/lib/impure/graphics.nim b/lib/impure/graphics.nim deleted file mode 100644 index 8bd769fd8..000000000 --- a/lib/impure/graphics.nim +++ /dev/null @@ -1,577 +0,0 @@ -# -# -# Nim's Runtime Library -# (c) Copyright 2012 Andreas Rumpf, Dominik Picheta -# -# See the file "copying.txt", included in this -# distribution, for details about the copyright. -# - -## This module implements graphical output for Nim; the current -## implementation uses SDL but the interface is meant to support multiple -## backends some day. There is no need to init SDL as this module does that -## implicitly. - -import colors, math -from sdl import PSurface # Bug -from sdl_ttf import openFont, closeFont - -type - Rect* = tuple[x, y, width, height: int] - Point* = tuple[x, y: int] - - PSurface* = ref Surface ## a surface to draw onto - Surface* {.pure, final.} = object - w*, h*: Natural - s*: sdl.PSurface - - EGraphics* = object of IOError - - Font {.pure, final.} = object - f: sdl_ttf.PFont - color: sdl.Color - PFont* = ref Font ## represents a font -{.deprecated: [TSurface: Surface, TFont: Font, TRect: Rect, TPoint: Point].} - -proc toSdlColor*(c: Color): sdl.Color = - ## Convert colors.Color to sdl.Color - var x = c.extractRGB - result.r = x.r and 0xff - result.g = x.g and 0xff - result.b = x.b and 0xff - -proc createSdlColor*(sur: PSurface, c: Color, alpha: int = 0): int32 = - ## Creates a color using ``sdl.MapRGBA``. - var x = c.extractRGB - return sdl.mapRGBA(sur.s.format, x.r and 0xff, x.g and 0xff, - x.b and 0xff, alpha and 0xff) - -proc toSdlRect*(r: Rect): sdl.Rect = - ## Convert ``graphics.Rect`` to ``sdl.Rect``. - result.x = int16(r.x) - result.y = int16(r.y) - result.w = uint16(r.width) - result.h = uint16(r.height) - -proc raiseEGraphics = - raise newException(EGraphics, $sdl.getError()) - -proc surfaceFinalizer(s: PSurface) = sdl.freeSurface(s.s) - -proc newSurface*(width, height: int): PSurface = - ## creates a new surface. - new(result, surfaceFinalizer) - result.w = width - result.h = height - result.s = sdl.createRGBSurface(sdl.SWSURFACE, width, height, - 32, 0x00FF0000, 0x0000FF00, 0x000000FF, 0) - if result.s == nil: - raiseEGraphics() - - assert(not sdl.mustLock(result.s)) - -proc fontFinalizer(f: PFont) = closeFont(f.f) - -proc newFont*(name = "VeraMono.ttf", size = 9, color = colBlack): PFont = - ## Creates a new font object. Raises ``EIO`` if the font cannot be loaded. - new(result, fontFinalizer) - result.f = openFont(name, size.cint) - if result.f == nil: - raise newException(IOError, "Could not open font file: " & name) - result.color = toSdlColor(color) - -var - defaultFont*: PFont ## default font that is used; this needs to initialized - ## by the client! - -proc initDefaultFont*(name = "VeraMono.ttf", size = 9, color = colBlack) = - ## initializes the `defaultFont` var. - defaultFont = newFont(name, size, color) - -proc newScreenSurface*(width, height: int): PSurface = - ## Creates a new screen surface - new(result, surfaceFinalizer) - result.w = width - result.h = height - result.s = sdl.setVideoMode(width, height, 0, 0) - if result.s == nil: - raiseEGraphics() - -proc writeToBMP*(sur: PSurface, filename: string) = - ## Saves the contents of the surface `sur` to the file `filename` as a - ## BMP file. - if sdl.saveBMP(sur.s, filename) != 0: - raise newException(IOError, "cannot write: " & filename) - -type - Pixels = array[0..1000_000-1, int32] - PPixels = ptr Pixels -{.deprecated: [TPixels: Pixels].} - -template setPix(video, pitch, x, y, col: expr): stmt = - video[y * pitch + x] = int32(col) - -template getPix(video, pitch, x, y: expr): expr = - colors.Color(video[y * pitch + x]) - -const - ColSize = 4 - -proc getPixel(sur: PSurface, x, y: Natural): colors.Color {.inline.} = - assert x <% sur.w - assert y <% sur.h - result = getPix(cast[PPixels](sur.s.pixels), sur.s.pitch.int div ColSize, - x, y) - -proc setPixel(sur: PSurface, x, y: Natural, col: colors.Color) {.inline.} = - assert x <% sur.w - assert y <% sur.h - var pixs = cast[PPixels](sur.s.pixels) - #pixs[y * (sur.s.pitch div colSize) + x] = int(col) - setPix(pixs, sur.s.pitch.int div ColSize, x, y, col) - -proc `[]`*(sur: PSurface, p: Point): Color = - ## get pixel at position `p`. No range checking is done! - result = getPixel(sur, p.x, p.y) - -proc `[]`*(sur: PSurface, x, y: int): Color = - ## get pixel at position ``(x, y)``. No range checking is done! - result = getPixel(sur, x, y) - -proc `[]=`*(sur: PSurface, p: Point, col: Color) = - ## set the pixel at position `p`. No range checking is done! - setPixel(sur, p.x, p.y, col) - -proc `[]=`*(sur: PSurface, x, y: int, col: Color) = - ## set the pixel at position ``(x, y)``. No range checking is done! - setPixel(sur, x, y, col) - -proc blit*(destSurf: PSurface, destRect: Rect, srcSurf: PSurface, - srcRect: Rect) = - ## Copies ``srcSurf`` into ``destSurf`` - var destTRect, srcTRect: sdl.Rect - - destTRect.x = int16(destRect.x) - destTRect.y = int16(destRect.y) - destTRect.w = uint16(destRect.width) - destTRect.h = uint16(destRect.height) - - srcTRect.x = int16(srcRect.x) - srcTRect.y = int16(srcRect.y) - srcTRect.w = uint16(srcRect.width) - srcTRect.h = uint16(srcRect.height) - - if sdl.blitSurface(srcSurf.s, addr(srcTRect), destSurf.s, addr(destTRect)) != 0: - raiseEGraphics() - -proc textBounds*(text: string, font = defaultFont): tuple[width, height: int] = - var w, h: cint - if sdl_ttf.sizeUTF8(font.f, text, w, h) < 0: raiseEGraphics() - result.width = int(w) - result.height = int(h) - -proc drawText*(sur: PSurface, p: Point, text: string, font = defaultFont) = - ## Draws text with a transparent background, at location ``p`` with the given - ## font. - var textSur: PSurface # This surface will have the text drawn on it - new(textSur, surfaceFinalizer) - - # Render the text - textSur.s = sdl_ttf.renderTextBlended(font.f, text, font.color) - # Merge the text surface with sur - sur.blit((p.x, p.y, sur.w, sur.h), textSur, (0, 0, sur.w, sur.h)) - -proc drawText*(sur: PSurface, p: Point, text: string, - bg: Color, font = defaultFont) = - ## Draws text, at location ``p`` with font ``font``. ``bg`` - ## is the background color. - var textSur: PSurface # This surface will have the text drawn on it - new(textSur, surfaceFinalizer) - textSur.s = sdl_ttf.renderTextShaded(font.f, text, font.color, toSdlColor(bg)) - # Merge the text surface with sur - sur.blit((p.x, p.y, sur.w, sur.h), textSur, (0, 0, sur.w, sur.h)) - -proc drawCircle*(sur: PSurface, p: Point, r: Natural, color: Color) = - ## draws a circle with center `p` and radius `r` with the given color - ## onto the surface `sur`. - var video = cast[PPixels](sur.s.pixels) - var pitch = sur.s.pitch.int div ColSize - var a = 1 - r - var py = r - var px = 0 - var x = p.x - var y = p.y - while px <= py + 1: - if x+px <% sur.w: - if y+py <% sur.h: setPix(video, pitch, x+px, y+py, color) - if y-py <% sur.h: setPix(video, pitch, x+px, y-py, color) - - if x-px <% sur.w: - if y+py <% sur.h: setPix(video, pitch, x-px, y+py, color) - if y-py <% sur.h: setPix(video, pitch, x-px, y-py, color) - - if x+py <% sur.w: - if y+px <% sur.h: setPix(video, pitch, x+py, y+px, color) - if y-px <% sur.h: setPix(video, pitch, x+py, y-px, color) - - if x-py <% sur.w: - if y+px <% sur.h: setPix(video, pitch, x-py, y+px, color) - if y-px <% sur.h: setPix(video, pitch, x-py, y-px, color) - - if a < 0: - a = a + (2 * px + 3) - else: - a = a + (2 * (px - py) + 5) - py = py - 1 - px = px + 1 - -proc `>-<`(val: int, s: PSurface): int {.inline.} = - return if val < 0: 0 elif val >= s.w: s.w-1 else: val - -proc `>|<`(val: int, s: PSurface): int {.inline.} = - return if val < 0: 0 elif val >= s.h: s.h-1 else: val - -proc drawLine*(sur: PSurface, p1, p2: Point, color: Color) = - ## draws a line between the two points `p1` and `p2` with the given color - ## onto the surface `sur`. - var stepx, stepy: int = 0 - var x0 = p1.x >-< sur - var x1 = p2.x >-< sur - var y0 = p1.y >|< sur - var y1 = p2.y >|< sur - var dy = y1 - y0 - var dx = x1 - x0 - if dy < 0: - dy = -dy - stepy = -1 - else: - stepy = 1 - if dx < 0: - dx = -dx - stepx = -1 - else: - stepx = 1 - dy = dy * 2 - dx = dx * 2 - var video = cast[PPixels](sur.s.pixels) - var pitch = sur.s.pitch.int div ColSize - setPix(video, pitch, x0, y0, color) - if dx > dy: - var fraction = dy - (dx div 2) - while x0 != x1: - if fraction >= 0: - y0 = y0 + stepy - fraction = fraction - dx - x0 = x0 + stepx - fraction = fraction + dy - setPix(video, pitch, x0, y0, color) - else: - var fraction = dx - (dy div 2) - while y0 != y1: - if fraction >= 0: - x0 = x0 + stepx - fraction = fraction - dy - y0 = y0 + stepy - fraction = fraction + dx - setPix(video, pitch, x0, y0, color) - -proc drawHorLine*(sur: PSurface, x, y, w: Natural, color: Color) = - ## draws a horizontal line from (x,y) to (x+w-1, y). - var video = cast[PPixels](sur.s.pixels) - var pitch = sur.s.pitch.int div ColSize - - if y >= 0 and y <= sur.s.h: - for i in 0 .. min(sur.s.w-x, w)-1: - setPix(video, pitch, x + i, y, color) - -proc drawVerLine*(sur: PSurface, x, y, h: Natural, color: Color) = - ## draws a vertical line from (x,y) to (x, y+h-1). - var video = cast[PPixels](sur.s.pixels) - var pitch = sur.s.pitch.int div ColSize - - if x >= 0 and x <= sur.s.w: - for i in 0 .. min(sur.s.h-y, h)-1: - setPix(video, pitch, x, y + i, color) - -proc fillCircle*(s: PSurface, p: Point, r: Natural, color: Color) = - ## draws a circle with center `p` and radius `r` with the given color - ## onto the surface `sur` and fills it. - var a = 1 - r - var py: int = r - var px = 0 - var x = p.x - var y = p.y - while px <= py: - # Fill up the middle half of the circle - drawVerLine(s, x + px, y, py + 1, color) - drawVerLine(s, x + px, y - py, py, color) - if px != 0: - drawVerLine(s, x - px, y, py + 1, color) - drawVerLine(s, x - px, y - py, py, color) - if a < 0: - a = a + (2 * px + 3) - else: - a = a + (2 * (px - py) + 5) - py = py - 1 - # Fill up the left/right half of the circle - if py >= px: - drawVerLine(s, x + py + 1, y, px + 1, color) - drawVerLine(s, x + py + 1, y - px, px, color) - drawVerLine(s, x - py - 1, y, px + 1, color) - drawVerLine(s, x - py - 1, y - px, px, color) - px = px + 1 - -proc drawRect*(sur: PSurface, r: Rect, color: Color) = - ## draws a rectangle. - var video = cast[PPixels](sur.s.pixels) - var pitch = sur.s.pitch.int div ColSize - if (r.x >= 0 and r.x <= sur.s.w) and (r.y >= 0 and r.y <= sur.s.h): - var minW = min(sur.s.w - r.x, r.width) - var minH = min(sur.s.h - r.y, r.height) - - # Draw Top - for i in 0 .. minW - 1: - setPix(video, pitch, r.x + i, r.y, color) - setPix(video, pitch, r.x + i, r.y + minH - 1, color) # Draw bottom - - # Draw left side - for i in 0 .. minH - 1: - setPix(video, pitch, r.x, r.y + i, color) - setPix(video, pitch, r.x + minW - 1, r.y + i, color) # Draw right side - -proc fillRect*(sur: PSurface, r: Rect, col: Color) = - ## Fills a rectangle using sdl's ``FillRect`` function. - var rect = toSdlRect(r) - if sdl.fillRect(sur.s, addr(rect), sur.createSdlColor(col)) == -1: - raiseEGraphics() - -proc plot4EllipsePoints(sur: PSurface, cx, cy, x, y: Natural, col: Color) = - var video = cast[PPixels](sur.s.pixels) - var pitch = sur.s.pitch.int div ColSize - if cx+x <= sur.s.w-1: - if cy+y <= sur.s.h-1: setPix(video, pitch, cx+x, cy+y, col) - if cy-y <= sur.s.h-1: setPix(video, pitch, cx+x, cy-y, col) - if cx-x <= sur.s.w-1: - if cy+y <= sur.s.h-1: setPix(video, pitch, cx-x, cy+y, col) - if cy-y <= sur.s.h-1: setPix(video, pitch, cx-x, cy-y, col) - -proc drawEllipse*(sur: PSurface, cx, cy, xRadius, yRadius: Natural, - col: Color) = - ## Draws an ellipse, ``CX`` and ``CY`` specify the center X and Y of the - ## ellipse, ``XRadius`` and ``YRadius`` specify half the width and height - ## of the ellipse. - var - x, y: Natural - xChange, yChange: int - ellipseError: Natural - twoASquare, twoBSquare: Natural - stoppingX, stoppingY: Natural - - twoASquare = 2 * xRadius * xRadius - twoBSquare = 2 * yRadius * yRadius - x = xRadius - y = 0 - xChange = yRadius * yRadius * (1 - 2 * xRadius) - yChange = xRadius * xRadius - ellipseError = 0 - stoppingX = twoBSquare * xRadius - stoppingY = 0 - - while stoppingX >= stoppingY: # 1st set of points, y` > - 1 - sur.plot4EllipsePoints(cx, cy, x, y, col) - inc(y) - inc(stoppingY, twoASquare) - inc(ellipseError, yChange) - inc(yChange, twoASquare) - if (2 * ellipseError + xChange) > 0 : - dec(x) - dec(stoppingX, twoBSquare) - inc(ellipseError, xChange) - inc(xChange, twoBSquare) - - # 1st point set is done; start the 2nd set of points - x = 0 - y = yRadius - xChange = yRadius * yRadius - yChange = xRadius * xRadius * (1 - 2 * yRadius) - ellipseError = 0 - stoppingX = 0 - stoppingY = twoASquare * yRadius - while stoppingX <= stoppingY: - sur.plot4EllipsePoints(cx, cy, x, y, col) - inc(x) - inc(stoppingX, twoBSquare) - inc(ellipseError, xChange) - inc(xChange,twoBSquare) - if (2 * ellipseError + yChange) > 0: - dec(y) - dec(stoppingY, twoASquare) - inc(ellipseError, yChange) - inc(yChange,twoASquare) - - -proc plotAA(sur: PSurface, x, y: int, c: float, color: Color) = - if (x > 0 and x < sur.s.w) and (y > 0 and y < sur.s.h): - var video = cast[PPixels](sur.s.pixels) - var pitch = sur.s.pitch.int div ColSize - - var pixColor = getPix(video, pitch, x, y) - - setPix(video, pitch, x, y, - pixColor.intensity(1.0 - c) + color.intensity(c)) - - -template ipart(x: expr): expr = floor(x) -template cround(x: expr): expr = ipart(x + 0.5) -template fpart(x: expr): expr = x - ipart(x) -template rfpart(x: expr): expr = 1.0 - fpart(x) - -proc drawLineAA*(sur: PSurface, p1, p2: Point, color: Color) = - ## Draws a anti-aliased line from ``p1`` to ``p2``, using Xiaolin Wu's - ## line algorithm - var (x1, x2, y1, y2) = (p1.x.toFloat(), p2.x.toFloat(), - p1.y.toFloat(), p2.y.toFloat()) - var dx = x2 - x1 - var dy = y2 - y1 - - var ax = dx - if ax < 0'f64: - ax = 0'f64 - ax - var ay = dy - if ay < 0'f64: - ay = 0'f64 - ay - - if ax < ay: - swap(x1, y1) - swap(x2, y2) - swap(dx, dy) - - template doPlot(x, y: int, c: float, color: Color): stmt = - if ax < ay: - sur.plotAA(y, x, c, color) - else: - sur.plotAA(x, y, c, color) - - if x2 < x1: - swap(x1, x2) - swap(y1, y2) - - var gradient = dy / dx - # handle first endpoint - var xend = cround(x1) - var yend = y1 + gradient * (xend - x1) - var xgap = rfpart(x1 + 0.5) - var xpxl1 = int(xend) # this will be used in the main loop - var ypxl1 = int(ipart(yend)) - doPlot(xpxl1, ypxl1, rfpart(yend)*xgap, color) - doPlot(xpxl1, ypxl1 + 1, fpart(yend)*xgap, color) - var intery = yend + gradient # first y-intersection for the main loop - - # handle second endpoint - xend = cround(x2) - yend = y2 + gradient * (xend - x2) - xgap = fpart(x2 + 0.5) - var xpxl2 = int(xend) # this will be used in the main loop - var ypxl2 = int(ipart(yend)) - doPlot(xpxl2, ypxl2, rfpart(yend) * xgap, color) - doPlot(xpxl2, ypxl2 + 1, fpart(yend) * xgap, color) - - # main loop - var x = xpxl1 + 1 - while x <= xpxl2-1: - doPlot(x, int(ipart(intery)), rfpart(intery), color) - doPlot(x, int(ipart(intery)) + 1, fpart(intery), color) - intery = intery + gradient - inc(x) - -proc fillSurface*(sur: PSurface, color: Color) = - ## Fills the entire surface with ``color``. - if sdl.fillRect(sur.s, nil, sur.createSdlColor(color)) == -1: - raiseEGraphics() - -template withEvents*(surf: PSurface, event: expr, actions: stmt): stmt {. - immediate.} = - ## Simple template which creates an event loop. ``Event`` is the name of the - ## variable containing the Event object. - while true: - var event: sdl.Event - if sdl.waitEvent(addr(event)) == 1: - actions - -if sdl.init(sdl.INIT_VIDEO) < 0: raiseEGraphics() -if sdl_ttf.init() < 0: raiseEGraphics() - -when not defined(testing) and isMainModule: - var surf = newScreenSurface(800, 600) - - surf.fillSurface(colWhite) - - # Draw the shapes - surf.drawLineAA((150, 170), (400, 471), colTan) - surf.drawLine((100, 170), (400, 471), colRed) - - surf.drawEllipse(200, 300, 200, 30, colSeaGreen) - surf.drawHorLine(1, 300, 400, colViolet) - # Check if the ellipse is the size it's suppose to be. - surf.drawVerLine(200, 300 - 30 + 1, 60, colViolet) # ^^ | i suppose it is - - surf.drawEllipse(400, 300, 300, 300, colOrange) - surf.drawEllipse(5, 5, 5, 5, colGreen) - - surf.drawHorLine(5, 5, 900, colRed) - surf.drawVerLine(5, 60, 800, colRed) - surf.drawCircle((600, 500), 60, colRed) - - surf.fillRect((50, 50, 100, 100), colFuchsia) - surf.fillRect((150, 50, 100, 100), colGreen) - surf.drawRect((50, 150, 100, 100), colGreen) - surf.drawRect((150, 150, 100, 100), colAqua) - surf.drawRect((250, 150, 100, 100), colBlue) - surf.drawHorLine(250, 150, 100, colRed) - - surf.drawLineAA((592, 160), (592, 280), colPurple) - - #surf.drawText((300, 300), "TEST", colMidnightBlue) - #var textSize = textBounds("TEST") - #surf.drawText((300, 300 + textSize.height), $textSize.width & ", " & - # $textSize.height, colDarkGreen) - - var mouseStartX = -1 - var mouseStartY = -1 - withEvents(surf, event): - var eventp = addr(event) - case event.kind: - of sdl.QUITEV: - break - of sdl.KEYDOWN: - var evk = sdl.evKeyboard(eventp) - if evk.keysym.sym == sdl.K_LEFT: - surf.drawHorLine(395, 300, 50, colBlack) - echo("Drawing") - elif evk.keysym.sym == sdl.K_ESCAPE: - break - else: - echo(evk.keysym.sym) - of sdl.MOUSEBUTTONDOWN: - var mbd = sdl.evMouseButton(eventp) - if mouseStartX == -1 or mouseStartY == -1: - mouseStartX = int(mbd.x) - mouseStartY = int(mbd.y) - else: - surf.drawLineAA((mouseStartX, mouseStartY), (int(mbd.x), int(mbd.y)), colPurple) - mouseStartX = -1 - mouseStartY = -1 - - of sdl.MOUSEMOTION: - var mm = sdl.evMouseMotion(eventp) - if mouseStartX != -1 and mouseStartY != -1: - surf.drawLineAA((mouseStartX, mouseStartY), (int(mm.x), int(mm.y)), colPurple) - #echo(mm.x, " ", mm.y, " ", mm.yrel) - - else: - discard "echo(event.kind)" - - sdl.updateRect(surf.s, 0, 0, 800, 600) - - surf.writeToBMP("test.bmp") - sdl.quit() diff --git a/lib/posix/kqueue.nim b/lib/posix/kqueue.nim new file mode 100644 index 000000000..511ada9ac --- /dev/null +++ b/lib/posix/kqueue.nim @@ -0,0 +1,71 @@ +# +# +# Nim's Runtime Library +# (c) Copyright 2015 Adam Strzelecki +# +# See the file "copying.txt", included in this +# distribution, for details about the copyright. +# + +{.deadCodeElim:on.} + +from posix import Timespec + +# Filters: +const + EVFILT_READ* = -1 + EVFILT_WRITE* = -2 + EVFILT_AIO* = -3 + EVFILT_VNODE* = -4 + EVFILT_PROC* = -5 + EVFILT_SIGNAL* = -6 + EVFILT_TIMER* = -7 + EVFILT_MACHPORT* = -8 + EVFILT_FS* = -9 + EVFILT_USER* = -10 + # -11 is unused + EVFILT_VM* = -12 + +# Actions: +const + EV_ADD* = 0x0001 ## Add event to queue (implies enable). + ## Re-adding an existing element modifies it. + EV_DELETE* = 0x0002 ## Delete event from queue. + EV_ENABLE* = 0x0004 ## Enable event. + EV_DISABLE* = 0x0008 ## Disable event (not reported). + +# Flags: +const + EV_ONESHOT* = 0x0010 ## Only report one occurrence. + EV_CLEAR* = 0x0020 ## Clear event state after reporting. + EV_RECEIPT* = 0x0040 ## Force EV_ERROR on success, data == 0 + EV_DISPATCH* = 0x0080 ## Disable event after reporting. + +# Return values: +const + EV_EOF* = 0x8000 ## EOF detected + EV_ERROR* = 0x4000 ## Error, data contains errno + +type + KEvent* {.importc: "struct kevent", + header: "<sys/event.h>", pure, final.} = object + ident*: cuint ## identifier for this event (uintptr_t) + filter*: cshort ## filter for event + flags*: cushort ## general flags + fflags*: cuint ## filter-specific flags + data*: cuint ## filter-specific data (intptr_t) + #udata*: ptr void ## opaque user data identifier + +proc kqueue*(): cint {.importc: "kqueue", header: "<sys/event.h>".} + ## Creates new queue and returns its descriptor. + +proc kevent*(kqFD: cint, + changelist: ptr KEvent, nchanges: cint, + eventlist: ptr KEvent, nevents: cint, timeout: ptr Timespec): cint + {.importc: "kevent", header: "<sys/event.h>".} + ## Manipulates queue for given ``kqFD`` descriptor. + +proc EV_SET*(event: ptr KEvent, ident: cuint, filter: cshort, flags: cushort, + fflags: cuint, data: cuint, udata: ptr void) + {.importc: "EV_SET", header: "<sys/event.h>".} + ## Fills event with given data. diff --git a/lib/posix/posix.nim b/lib/posix/posix.nim index d264dc02a..5f1dfcfcd 100644 --- a/lib/posix/posix.nim +++ b/lib/posix/posix.nim @@ -1810,7 +1810,7 @@ proc ntohs*(a1: int16): int16 {.importc, header: "<arpa/inet.h>".} proc inet_addr*(a1: cstring): InAddrT {.importc, header: "<arpa/inet.h>".} proc inet_ntoa*(a1: InAddr): cstring {.importc, header: "<arpa/inet.h>".} proc inet_ntop*(a1: cint, a2: pointer, a3: cstring, a4: int32): cstring {. - importc, header: "<arpa/inet.h>".} + importc:"(char *)$1", header: "<arpa/inet.h>".} proc inet_pton*(a1: cint, a2: cstring, a3: pointer): cint {. importc, header: "<arpa/inet.h>".} @@ -2316,7 +2316,7 @@ proc timer_settime*(a1: Timer, a2: cint, a3: var Itimerspec, proc tzset*() {.importc, header: "<time.h>".} -proc wait*(a1: var cint): Pid {.importc, header: "<sys/wait.h>".} +proc wait*(a1: ptr cint): Pid {.importc, discardable, header: "<sys/wait.h>".} proc waitid*(a1: cint, a2: Id, a3: var SigInfo, a4: cint): cint {. importc, header: "<sys/wait.h>".} proc waitpid*(a1: Pid, a2: var cint, a3: cint): Pid {. @@ -2381,7 +2381,7 @@ proc sched_setscheduler*(a1: Pid, a2: cint, a3: var Sched_param): cint {. proc sched_yield*(): cint {.importc, header: "<sched.h>".} proc strerror*(errnum: cint): cstring {.importc, header: "<string.h>".} -proc hstrerror*(herrnum: cint): cstring {.importc, header: "<netdb.h>".} +proc hstrerror*(herrnum: cint): cstring {.importc:"(char *)$1", header: "<netdb.h>".} proc FD_CLR*(a1: cint, a2: var TFdSet) {.importc, header: "<sys/select.h>".} proc FD_ISSET*(a1: cint | SocketHandle, a2: var TFdSet): cint {. @@ -2565,7 +2565,7 @@ proc endprotoent*() {.importc, header: "<netdb.h>".} proc endservent*() {.importc, header: "<netdb.h>".} proc freeaddrinfo*(a1: ptr AddrInfo) {.importc, header: "<netdb.h>".} -proc gai_strerror*(a1: cint): cstring {.importc, header: "<netdb.h>".} +proc gai_strerror*(a1: cint): cstring {.importc:"(char *)$1", header: "<netdb.h>".} proc getaddrinfo*(a1, a2: cstring, a3: ptr AddrInfo, a4: var ptr AddrInfo): cint {.importc, header: "<netdb.h>".} diff --git a/lib/pure/asyncdispatch.nim b/lib/pure/asyncdispatch.nim index f49388b17..cc337452f 100644 --- a/lib/pure/asyncdispatch.nim +++ b/lib/pure/asyncdispatch.nim @@ -11,7 +11,7 @@ include "system/inclrtl" import os, oids, tables, strutils, macros, times -import rawsockets, net +import nativesockets, net export Port, SocketFlag @@ -126,6 +126,7 @@ export Port, SocketFlag ## * Can't await in a ``except`` body ## * Forward declarations for async procs are broken, ## link includes workaround: https://github.com/nim-lang/Nim/issues/3182. +## * FutureVar[T] needs to be completed manually. # TODO: Check if yielded future is nil and throw a more meaningful exception @@ -145,10 +146,15 @@ type Future*[T] = ref object of FutureBase ## Typed future. value: T ## Stored value -{.deprecated: [PFutureBase: FutureBase, PFuture: Future].} + FutureVar*[T] = distinct Future[T] + + FutureError* = object of Exception + cause*: FutureBase +{.deprecated: [PFutureBase: FutureBase, PFuture: Future].} -var currentID = 0 +when not defined(release): + var currentID = 0 proc newFuture*[T](fromProc: string = "unspecified"): Future[T] = ## Creates a new future. ## @@ -162,18 +168,39 @@ proc newFuture*[T](fromProc: string = "unspecified"): Future[T] = result.fromProc = fromProc currentID.inc() +proc newFutureVar*[T](fromProc = "unspecified"): FutureVar[T] = + ## Create a new ``FutureVar``. This Future type is ideally suited for + ## situations where you want to avoid unnecessary allocations of Futures. + ## + ## Specifying ``fromProc``, which is a string specifying the name of the proc + ## that this future belongs to, is a good habit as it helps with debugging. + result = FutureVar[T](newFuture[T](fromProc)) + +proc clean*[T](future: FutureVar[T]) = + ## Resets the ``finished`` status of ``future``. + Future[T](future).finished = false + Future[T](future).error = nil + proc checkFinished[T](future: Future[T]) = + ## Checks whether `future` is finished. If it is then raises a + ## ``FutureError``. when not defined(release): if future.finished: - echo("<-----> ", future.id, " ", future.fromProc) - echo(future.stackTrace) - echo("-----") + var msg = "" + msg.add("An attempt was made to complete a Future more than once. ") + msg.add("Details:") + msg.add("\n Future ID: " & $future.id) + msg.add("\n Created in proc: " & future.fromProc) + msg.add("\n Stack trace to moment of creation:") + msg.add("\n" & indent(future.stackTrace.strip(), 4)) when T is string: - echo("Contents: ", future.value.repr) - echo("<----->") - echo("Future already finished, cannot finish twice.") - echo getStackTrace() - assert false + msg.add("\n Contents (string): ") + msg.add("\n" & indent(future.value.repr, 4)) + msg.add("\n Stack trace to moment of secondary completion:") + msg.add("\n" & indent(getStackTrace().strip(), 4)) + var err = newException(FutureError, msg) + err.cause = future + raise err proc complete*[T](future: Future[T], val: T) = ## Completes ``future`` with value ``val``. @@ -194,6 +221,15 @@ proc complete*(future: Future[void]) = if future.cb != nil: future.cb() +proc complete*[T](future: FutureVar[T]) = + ## Completes a ``FutureVar``. + template fut: expr = Future[T](future) + checkFinished(fut) + assert(fut.error == nil) + fut.finished = true + if fut.cb != nil: + fut.cb() + proc fail*[T](future: Future[T], error: ref Exception) = ## Completes ``future`` with ``error``. #assert(not future.finished, "Future already finished, cannot finish twice.") @@ -230,15 +266,17 @@ proc `callback=`*[T](future: Future[T], ## If future has already completed then ``cb`` will be called immediately. future.callback = proc () = cb(future) -proc echoOriginalStackTrace[T](future: Future[T]) = +proc injectStacktrace[T](future: Future[T]) = # TODO: Come up with something better. when not defined(release): - echo("Original stack trace in ", future.fromProc, ":") + var msg = "" + msg.add("\n " & future.fromProc & "'s lead up to read of failed Future:") + if not future.errorStackTrace.isNil and future.errorStackTrace != "": - echo(future.errorStackTrace) + msg.add("\n" & indent(future.errorStackTrace.strip(), 4)) else: - echo("Empty or nil stack trace.") - echo("Continuing...") + msg.add("\n Empty or nil stack trace.") + future.error.msg.add(msg) proc read*[T](future: Future[T]): T = ## Retrieves the value of ``future``. Future must be finished otherwise @@ -247,7 +285,7 @@ proc read*[T](future: Future[T]): T = ## If the result of the future is an error then that error will be raised. if future.finished: if future.error != nil: - echoOriginalStackTrace(future) + injectStacktrace(future) raise future.error when T isnot void: return future.value @@ -264,6 +302,13 @@ proc readError*[T](future: Future[T]): ref Exception = else: raise newException(ValueError, "No error in future.") +proc mget*[T](future: FutureVar[T]): var T = + ## Returns a mutable value stored in ``future``. + ## + ## Unlike ``read``, this function will not raise an exception if the + ## Future has not been finished. + result = Future[T](future).value + proc finished*[T](future: Future[T]): bool = ## Determines whether ``future`` has completed. ## @@ -282,7 +327,7 @@ proc asyncCheck*[T](future: Future[T]) = future.callback = proc () = if future.failed: - echoOriginalStackTrace(future) + injectStacktrace(future) raise future.error proc `and`*[T, Y](fut1: Future[T], fut2: Future[Y]): Future[void] = @@ -430,7 +475,7 @@ when defined(windows) or defined(nimdoc): addr bytesRet, nil, nil) == 0 proc initAll() = - let dummySock = newRawSocket() + let dummySock = newNativeSocket() if not initPointer(dummySock, connectExPtr, WSAID_CONNECTEX): raiseOSError(osLastError()) if not initPointer(dummySock, acceptExPtr, WSAID_ACCEPTEX): @@ -483,7 +528,7 @@ when defined(windows) or defined(nimdoc): RemoteSockaddr, RemoteSockaddrLength) proc connect*(socket: AsyncFD, address: string, port: Port, - domain = rawsockets.AF_INET): Future[void] = + domain = nativesockets.AF_INET): Future[void] = ## Connects ``socket`` to server at ``address:port``. ## ## Returns a ``Future`` which will complete when the connection succeeds @@ -566,7 +611,7 @@ when defined(windows) or defined(nimdoc): var retFuture = newFuture[string]("recv") var dataBuf: TWSABuf dataBuf.buf = cast[cstring](alloc0(size)) - dataBuf.len = size + dataBuf.len = size.ULONG var bytesReceived: Dword var flagsio = flags.toOSFlags().Dword @@ -661,7 +706,7 @@ when defined(windows) or defined(nimdoc): #buf[] = '\0' var dataBuf: TWSABuf dataBuf.buf = buf - dataBuf.len = size + dataBuf.len = size.ULONG var bytesReceived: Dword var flagsio = flags.toOSFlags().Dword @@ -732,7 +777,7 @@ when defined(windows) or defined(nimdoc): var dataBuf: TWSABuf dataBuf.buf = data # since this is not used in a callback, this is fine - dataBuf.len = data.len + dataBuf.len = data.len.ULONG var bytesReceived, lowFlags: Dword var ol = PCustomOverlapped() @@ -782,7 +827,7 @@ when defined(windows) or defined(nimdoc): verifyPresence(socket) var retFuture = newFuture[tuple[address: string, client: AsyncFD]]("acceptAddr") - var clientSock = newRawSocket() + var clientSock = newNativeSocket() if clientSock == osInvalidSocket: raiseOSError(osLastError()) const lpOutputLen = 1024 @@ -855,17 +900,17 @@ when defined(windows) or defined(nimdoc): return retFuture - proc newAsyncRawSocket*(domain, sockType, protocol: cint): AsyncFD = + proc newAsyncNativeSocket*(domain, sockType, protocol: cint): AsyncFD = ## Creates a new socket and registers it with the dispatcher implicitly. - result = newRawSocket(domain, sockType, protocol).AsyncFD + result = newNativeSocket(domain, sockType, protocol).AsyncFD result.SocketHandle.setBlocking(false) register(result) - proc newAsyncRawSocket*(domain: Domain = rawsockets.AF_INET, - sockType: SockType = SOCK_STREAM, - protocol: Protocol = IPPROTO_TCP): AsyncFD = + proc newAsyncNativeSocket*(domain: Domain = nativesockets.AF_INET, + sockType: SockType = SOCK_STREAM, + protocol: Protocol = IPPROTO_TCP): AsyncFD = ## Creates a new socket and registers it with the dispatcher implicitly. - result = newRawSocket(domain, sockType, protocol).AsyncFD + result = newNativeSocket(domain, sockType, protocol).AsyncFD result.SocketHandle.setBlocking(false) register(result) @@ -928,18 +973,18 @@ else: var data = PData(fd: fd, readCBs: @[], writeCBs: @[]) p.selector.register(fd.SocketHandle, {}, data.RootRef) - proc newAsyncRawSocket*(domain: cint, sockType: cint, - protocol: cint): AsyncFD = - result = newRawSocket(domain, sockType, protocol).AsyncFD + proc newAsyncNativeSocket*(domain: cint, sockType: cint, + protocol: cint): AsyncFD = + result = newNativeSocket(domain, sockType, protocol).AsyncFD result.SocketHandle.setBlocking(false) when defined(macosx): result.SocketHandle.setSockOptInt(SOL_SOCKET, SO_NOSIGPIPE, 1) register(result) - proc newAsyncRawSocket*(domain: Domain = AF_INET, - sockType: SockType = SOCK_STREAM, - protocol: Protocol = IPPROTO_TCP): AsyncFD = - result = newRawSocket(domain, sockType, protocol).AsyncFD + proc newAsyncNativeSocket*(domain: Domain = AF_INET, + sockType: SockType = SOCK_STREAM, + protocol: Protocol = IPPROTO_TCP): AsyncFD = + result = newNativeSocket(domain, sockType, protocol).AsyncFD result.SocketHandle.setBlocking(false) when defined(macosx): result.SocketHandle.setSockOptInt(SOL_SOCKET, SO_NOSIGPIPE, 1) @@ -947,8 +992,8 @@ else: proc closeSocket*(sock: AsyncFD) = let disp = getGlobalDispatcher() - sock.SocketHandle.close() disp.selector.unregister(sock.SocketHandle) + sock.SocketHandle.close() proc unregister*(fd: AsyncFD) = getGlobalDispatcher().selector.unregister(fd.SocketHandle) @@ -1423,16 +1468,25 @@ proc asyncSingleProc(prc: NimNode): NimNode {.compileTime.} = hint("Processing " & prc[0].getName & " as an async proc.") let returnType = prc[3][0] + var baseType: NimNode # Verify that the return type is a Future[T] - if returnType.kind == nnkIdent: - error("Expected return type of 'Future' got '" & $returnType & "'") - elif returnType.kind == nnkBracketExpr: - if $returnType[0] != "Future": - error("Expected return type of 'Future' got '" & $returnType[0] & "'") + if returnType.kind == nnkBracketExpr: + let fut = repr(returnType[0]) + if fut != "Future": + error("Expected return type of 'Future' got '" & fut & "'") + baseType = returnType[1] + elif returnType.kind in nnkCallKinds and $returnType[0] == "[]": + let fut = repr(returnType[1]) + if fut != "Future": + error("Expected return type of 'Future' got '" & fut & "'") + baseType = returnType[2] + elif returnType.kind == nnkEmpty: + baseType = returnType + else: + error("Expected return type of 'Future' got '" & repr(returnType) & "'") let subtypeIsVoid = returnType.kind == nnkEmpty or - (returnType.kind == nnkBracketExpr and - returnType[1].kind == nnkIdent and returnType[1].ident == !"void") + (baseType.kind == nnkIdent and returnType[1].ident == !"void") var outerProcBody = newNimNode(nnkStmtList, prc[6]) @@ -1440,7 +1494,7 @@ proc asyncSingleProc(prc: NimNode): NimNode {.compileTime.} = var retFutureSym = genSym(nskVar, "retFuture") var subRetType = if returnType.kind == nnkEmpty: newIdentNode("void") - else: returnType[1] + else: baseType outerProcBody.add( newVarStmt(retFutureSym, newCall( @@ -1464,7 +1518,7 @@ proc asyncSingleProc(prc: NimNode): NimNode {.compileTime.} = newIdentNode("off")))) # -> {.push warning[resultshadowed]: off.} procBody.insert(1, newNimNode(nnkVarSection, prc[6]).add( - newIdentDefs(newIdentNode("result"), returnType[1]))) # -> var result: T + newIdentDefs(newIdentNode("result"), baseType))) # -> var result: T procBody.insert(2, newNimNode(nnkPragma).add( newIdentNode("pop"))) # -> {.pop.}) @@ -1505,8 +1559,8 @@ proc asyncSingleProc(prc: NimNode): NimNode {.compileTime.} = result[6] = outerProcBody #echo(treeRepr(result)) - if prc[0].getName == "hubConnectionLoop": - echo(toStrLit(result)) + #if prc[0].getName == "hubConnectionLoop": + # echo(toStrLit(result)) macro async*(prc: stmt): stmt {.immediate.} = ## Macro which processes async procedures into the appropriate diff --git a/lib/pure/asyncftpclient.nim b/lib/pure/asyncftpclient.nim index fe4577ed9..b806f4235 100644 --- a/lib/pure/asyncftpclient.nim +++ b/lib/pure/asyncftpclient.nim @@ -6,23 +6,74 @@ # distribution, for details about the copyright. # -## This module implement an asynchronous FTP client. +## This module implements an asynchronous FTP client. It allows you to connect +## to an FTP server and perform operations on it such as for example: ## -## Examples -## -------- +## * The upload of new files. +## * The removal of existing files. +## * Download of files. +## * Changing of files' permissions. +## * Navigation through the FTP server's directories. ## -## .. code-block::nim +## Connecting to an FTP server +## ------------------------ ## -## var ftp = newAsyncFtpClient("example.com", user = "test", pass = "test") -## proc main(ftp: AsyncFtpClient) {.async.} = +## In order to begin any sort of transfer of files you must first +## connect to an FTP server. You can do so with the ``connect`` procedure. +## +## .. code-block::nim +## import asyncdispatch, asyncftpclient +## proc main() {.async.} = +## var ftp = newAsyncFtpClient("example.com", user = "test", pass = "test") +## await ftp.connect() +## echo("Connected") +## waitFor(main()) +## +## A new ``main`` async procedure must be declared to allow the use of the +## ``await`` keyword. The connection will complete asynchronously and the +## client will be connected after the ``await ftp.connect()`` call. +## +## Uploading a new file +## -------------------- +## +## After a connection is made you can use the ``store`` procedure to upload +## a new file to the FTP server. Make sure to check you are in the correct +## working directory before you do so with the ``pwd`` procedure, you can also +## instead specify an absolute path. +## +## .. code-block::nim +## import asyncdispatch, asyncftpclient +## proc main() {.async.} = +## var ftp = newAsyncFtpClient("example.com", user = "test", pass = "test") ## await ftp.connect() -## echo await ftp.pwd() -## echo await ftp.listDirs() -## await ftp.store("payload.jpg", "payload.jpg") -## await ftp.retrFile("payload.jpg", "payload2.jpg") -## echo("Finished") +## let currentDir = await ftp.pwd() +## assert currentDir == "/home/user/" +## await ftp.store("file.txt", "file.txt") +## echo("File finished uploading") +## waitFor(main()) ## -## waitFor main(ftp) +## Checking the progress of a file transfer +## ---------------------------------------- +## +## The progress of either a file upload or a file download can be checked +## by specifying a ``onProgressChanged`` procedure to the ``store`` or +## ``retrFile`` procedures. +## +## .. code-block::nim +## import asyncdispatch, asyncftpclient +## +## proc onProgressChanged(total, progress: BiggestInt, +## speed: float): Future[void] = +## echo("Uploaded ", progress, " of ", total, " bytes") +## echo("Current speed: ", speed, " kb/s") +## +## proc main() {.async.} = +## var ftp = newAsyncFtpClient("example.com", user = "test", pass = "test") +## await ftp.connect() +## await ftp.store("file.txt", "/home/user/file.txt", onProgressChanged) +## echo("File finished uploading") +## waitFor(main()) + import asyncdispatch, asyncnet, strutils, parseutils, os, times diff --git a/lib/pure/asynchttpserver.nim b/lib/pure/asynchttpserver.nim index f9085e4bf..5d74896bf 100644 --- a/lib/pure/asynchttpserver.nim +++ b/lib/pure/asynchttpserver.nim @@ -67,6 +67,12 @@ type Http409 = "409 Conflict", Http410 = "410 Gone", Http411 = "411 Length Required", + Http412 = "412 Precondition Failed", + Http413 = "413 Request Entity Too Large", + Http414 = "414 Request-URI Too Long", + Http415 = "415 Unsupported Media Type", + Http416 = "416 Requested Range Not Satisfiable", + Http417 = "417 Expectation Failed", Http418 = "418 I'm a teapot", Http500 = "500 Internal Server Error", Http501 = "501 Not Implemented", @@ -151,7 +157,8 @@ proc processClient(client: AsyncSocket, address: string, var request: Request request.url = initUri() request.headers = newStringTable(modeCaseInsensitive) - var line = newStringOfCap(80) + var lineFut = newFutureVar[string]("asynchttpserver.processClient") + lineFut.mget() = newStringOfCap(80) var key, value = "" while not client.isClosed: @@ -165,14 +172,15 @@ proc processClient(client: AsyncSocket, address: string, request.client = client # First line - GET /path HTTP/1.1 - line.setLen(0) - await client.recvLineInto(addr line) # TODO: Timeouts. - if line == "": + lineFut.mget().setLen(0) + lineFut.clean() + await client.recvLineInto(lineFut) # TODO: Timeouts. + if lineFut.mget == "": client.close() return var i = 0 - for linePart in line.split(' '): + for linePart in lineFut.mget.split(' '): case i of 0: request.reqMethod.shallowCopy(linePart.normalize) of 1: parseUri(linePart, request.url) @@ -184,20 +192,21 @@ proc processClient(client: AsyncSocket, address: string, "Invalid request protocol. Got: " & linePart) continue else: - await request.respond(Http400, "Invalid request. Got: " & line) + await request.respond(Http400, "Invalid request. Got: " & lineFut.mget) continue inc i # Headers while true: i = 0 - line.setLen(0) - await client.recvLineInto(addr line) + lineFut.mget.setLen(0) + lineFut.clean() + await client.recvLineInto(lineFut) - if line == "": + if lineFut.mget == "": client.close(); return - if line == "\c\L": break - let (key, value) = parseHeader(line) + if lineFut.mget == "\c\L": break + let (key, value) = parseHeader(lineFut.mget) request.headers[key] = value if request.reqMethod == "post": diff --git a/lib/pure/asyncnet.nim b/lib/pure/asyncnet.nim index 9139200f3..6b19a48be 100644 --- a/lib/pure/asyncnet.nim +++ b/lib/pure/asyncnet.nim @@ -56,7 +56,7 @@ ## import asyncdispatch -import rawsockets +import nativesockets import net import os @@ -112,8 +112,8 @@ proc newAsyncSocket*(domain: Domain = AF_INET, sockType: SockType = SOCK_STREAM, ## ## This procedure will also create a brand new file descriptor for ## this socket. - result = newAsyncSocket(newAsyncRawSocket(domain, sockType, protocol), domain, - sockType, protocol, buffered) + result = newAsyncSocket(newAsyncNativeSocket(domain, sockType, protocol), + domain, sockType, protocol, buffered) proc newAsyncSocket*(domain, sockType, protocol: cint, buffered = true): AsyncSocket = @@ -121,8 +121,9 @@ proc newAsyncSocket*(domain, sockType, protocol: cint, ## ## This procedure will also create a brand new file descriptor for ## this socket. - result = newAsyncSocket(newAsyncRawSocket(domain, sockType, protocol), - Domain(domain), SockType(sockType), Protocol(protocol), buffered) + result = newAsyncSocket(newAsyncNativeSocket(domain, sockType, protocol), + Domain(domain), SockType(sockType), + Protocol(protocol), buffered) when defined(ssl): proc getSslError(handle: SslPtr, err: cint): cint = @@ -316,7 +317,7 @@ proc accept*(socket: AsyncSocket, retFut.complete(future.read.client) return retFut -proc recvLineInto*(socket: AsyncSocket, resString: ptr string, +proc recvLineInto*(socket: AsyncSocket, resString: FutureVar[string], flags = {SocketFlag.SafeDisconn}) {.async.} = ## Reads a line of data from ``socket`` into ``resString``. ## @@ -338,16 +339,23 @@ proc recvLineInto*(socket: AsyncSocket, resString: ptr string, ## **Warning**: ``recvLineInto`` currently uses a raw pointer to a string for ## performance reasons. This will likely change soon to use FutureVars. assert SocketFlag.Peek notin flags ## TODO: + assert(not resString.mget.isNil(), + "String inside resString future needs to be initialised") result = newFuture[void]("asyncnet.recvLineInto") + # TODO: Make the async transformation check for FutureVar params and complete + # them when the result future is completed. + # Can we replace the result future with the FutureVar? + template addNLIfEmpty(): stmt = - if resString[].len == 0: - resString[].add("\c\L") + if resString.mget.len == 0: + resString.mget.add("\c\L") if socket.isBuffered: if socket.bufLen == 0: let res = socket.readIntoBuf(flags) if res == 0: + resString.complete() return var lastR = false @@ -355,7 +363,8 @@ proc recvLineInto*(socket: AsyncSocket, resString: ptr string, if socket.currPos >= socket.bufLen: let res = socket.readIntoBuf(flags) if res == 0: - resString[].setLen(0) + resString.mget.setLen(0) + resString.complete() return case socket.buffer[socket.currPos] @@ -365,13 +374,15 @@ proc recvLineInto*(socket: AsyncSocket, resString: ptr string, of '\L': addNLIfEmpty() socket.currPos.inc() + resString.complete() return else: if lastR: socket.currPos.inc() + resString.complete() return else: - resString[].add socket.buffer[socket.currPos] + resString.mget.add socket.buffer[socket.currPos] socket.currPos.inc() else: var c = "" @@ -379,18 +390,22 @@ proc recvLineInto*(socket: AsyncSocket, resString: ptr string, let recvFut = recv(socket, 1, flags) c = recvFut.read() if c.len == 0: - resString[].setLen(0) + resString.mget.setLen(0) + resString.complete() return if c == "\r": let recvFut = recv(socket, 1, flags) # Skip \L c = recvFut.read() assert c == "\L" addNLIfEmpty() + resString.complete() return elif c == "\L": addNLIfEmpty() + resString.complete() return - resString[].add c + resString.mget.add c + resString.complete() proc recvLine*(socket: AsyncSocket, flags = {SocketFlag.SafeDisconn}): Future[string] {.async.} = @@ -416,8 +431,11 @@ proc recvLine*(socket: AsyncSocket, result.add("\c\L") assert SocketFlag.Peek notin flags ## TODO: - result = "" - await socket.recvLineInto(addr result, flags) + # TODO: Optimise this + var resString = newFutureVar[string]("asyncnet.recvLine") + resString.mget() = "" + await socket.recvLineInto(resString, flags) + result = resString.mget() proc listen*(socket: AsyncSocket, backlog = SOMAXCONN) {.tags: [ReadIOEffect].} = ## Marks ``socket`` as accepting connections. diff --git a/lib/pure/base64.nim b/lib/pure/base64.nim index deab39c7c..32d37ce02 100644 --- a/lib/pure/base64.nim +++ b/lib/pure/base64.nim @@ -8,6 +8,38 @@ # ## This module implements a base64 encoder and decoder. +## +## Encoding data +## ------------- +## +## In order to encode some text simply call the ``encode`` procedure: +## +## .. code-block::nim +## import base64 +## let encoded = encode("Hello World") +## echo(encoded) # SGVsbG8gV29ybGQ= +## +## Apart from strings you can also encode lists of integers or characters: +## +## .. code-block::nim +## import base64 +## let encodedInts = encode([1,2,3]) +## echo(encodedInts) # AQID +## let encodedChars = encode(['h','e','y']) +## echo(encodedChars) # aGV5 +## +## The ``encode`` procedure takes an ``openarray`` so both arrays and sequences +## can be passed as parameters. +## +## Decoding data +## ------------- +## +## To decode a base64 encoded data string simply call the ``decode`` +## procedure: +## +## .. code-block::nim +## import base64 +## echo(decode("SGVsbG8gV29ybGQ=")) # Hello World const cb64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/" @@ -64,11 +96,16 @@ template encodeInternal(s: expr, lineLen: int, newLine: string): stmt {.immediat proc encode*[T:SomeInteger|char](s: openarray[T], lineLen = 75, newLine="\13\10"): string = ## encodes `s` into base64 representation. After `lineLen` characters, a ## `newline` is added. + ## + ## This procedure encodes an openarray (array or sequence) of either integers + ## or characters. encodeInternal(s, lineLen, newLine) proc encode*(s: string, lineLen = 75, newLine="\13\10"): string = ## encodes `s` into base64 representation. After `lineLen` characters, a ## `newline` is added. + ## + ## This procedure encodes a string. encodeInternal(s, lineLen, newLine) proc decodeByte(b: char): int {.inline.} = diff --git a/lib/pure/basic2d.nim b/lib/pure/basic2d.nim index ad8f8653d..7d74424fa 100644 --- a/lib/pure/basic2d.nim +++ b/lib/pure/basic2d.nim @@ -18,6 +18,8 @@ import strutils ## ## Quick start example: ## +## .. code-block:: nim +## ## # Create a matrix which first rotates, then scales and at last translates ## ## var m:Matrix2d=rotate(DEG90) & scale(2.0) & move(100.0,200.0) @@ -93,11 +95,11 @@ let IDMATRIX*:Matrix2d=matrix2d(1.0,0.0,0.0,1.0,0.0,0.0) ## Quick access to an identity matrix ORIGO*:Point2d=point2d(0.0,0.0) - ## Quick acces to point (0,0) + ## Quick access to point (0,0) XAXIS*:Vector2d=vector2d(1.0,0.0) - ## Quick acces to an 2d x-axis unit vector + ## Quick access to an 2d x-axis unit vector YAXIS*:Vector2d=vector2d(0.0,1.0) - ## Quick acces to an 2d y-axis unit vector + ## Quick access to an 2d y-axis unit vector # *************************************** diff --git a/lib/pure/basic3d.nim b/lib/pure/basic3d.nim index 7fea54d58..424c191f8 100644 --- a/lib/pure/basic3d.nim +++ b/lib/pure/basic3d.nim @@ -23,6 +23,8 @@ import times ## ## Quick start example: ## +## .. code-block:: nim +## ## # Create a matrix which first rotates, then scales and at last translates ## ## var m:Matrix3d=rotate(PI,vector3d(1,1,2.5)) & scale(2.0) & move(100.0,200.0,300.0) diff --git a/lib/pure/collections/critbits.nim b/lib/pure/collections/critbits.nim index 424bcdcca..09b20fd45 100644 --- a/lib/pure/collections/critbits.nim +++ b/lib/pure/collections/critbits.nim @@ -123,6 +123,14 @@ proc containsOrIncl*(c: var CritBitTree[void], key: string): bool = var n = rawInsert(c, key) result = c.count == oldCount +proc inc*(c: var CritBitTree[int]; key: string) = + ## counts the 'key'. + let oldCount = c.count + var n = rawInsert(c, key) + if c.count == oldCount: + # not a new key: + inc n.val + proc incl*(c: var CritBitTree[void], key: string) = ## includes `key` in `c`. discard rawInsert(c, key) diff --git a/lib/pure/coro.nim b/lib/pure/coro.nim index 8fa529474..c5724f26f 100644 --- a/lib/pure/coro.nim +++ b/lib/pure/coro.nim @@ -119,7 +119,7 @@ proc wait*(c: proc(), interval=0.01) = while alive(c): suspend interval -when isMainModule: +when defined(nimCoroutines) and isMainModule: var stackCheckValue = 1100220033 proc c2() diff --git a/lib/pure/coro.nimcfg b/lib/pure/coro.nimcfg new file mode 100644 index 000000000..b011bc585 --- /dev/null +++ b/lib/pure/coro.nimcfg @@ -0,0 +1 @@ +-d:nimCoroutines diff --git a/lib/pure/future.nim b/lib/pure/future.nim index 661afd7b3..4767266e5 100644 --- a/lib/pure/future.nim +++ b/lib/pure/future.nim @@ -75,7 +75,7 @@ macro `=>`*(p, b: expr): expr {.immediate.} = identDefs.add(newEmptyNode()) of nnkIdent: identDefs.add(c) - identDefs.add(newEmptyNode()) + identDefs.add(newIdentNode("auto")) identDefs.add(newEmptyNode()) of nnkInfix: if c[0].kind == nnkIdent and c[0].ident == !"->": @@ -93,7 +93,7 @@ macro `=>`*(p, b: expr): expr {.immediate.} = of nnkIdent: var identDefs = newNimNode(nnkIdentDefs) identDefs.add(p) - identDefs.add(newEmptyNode()) + identDefs.add(newIdentNode("auto")) identDefs.add(newEmptyNode()) params.add(identDefs) of nnkInfix: diff --git a/lib/pure/hashes.nim b/lib/pure/hashes.nim index 61c16129b..11af81149 100644 --- a/lib/pure/hashes.nim +++ b/lib/pure/hashes.nim @@ -8,9 +8,10 @@ # ## This module implements efficient computations of hash values for diverse -## Nim types. All the procs are based on these two building blocks: the `!& -## proc <#!&>`_ used to start or mix a hash value, and the `!$ proc <#!$>`_ -## used to *finish* the hash value. If you want to implement hash procs for +## Nim types. All the procs are based on these two building blocks: +## - `!& proc <#!&>`_ used to start or mix a hash value, and +## - `!$ proc <#!$>`_ used to *finish* the hash value. +## If you want to implement hash procs for ## your custom types you will end up writing the following kind of skeleton of ## code: ## @@ -108,7 +109,7 @@ proc hash*(x: int): Hash {.inline.} = result = x proc hash*(x: int64): Hash {.inline.} = - ## efficient hashing of integers + ## efficient hashing of int64 integers result = toU32(x) proc hash*(x: char): Hash {.inline.} = @@ -126,6 +127,16 @@ proc hash*(x: string): Hash = h = h !& ord(x[i]) result = !$h +proc hash*(sBuf: string, sPos, ePos: int): Hash = + ## efficient hashing of a string buffer, from starting + ## position `sPos` to ending position `ePos` + ## + ## ``hash(myStr, 0, myStr.high)`` is equivalent to ``hash(myStr)`` + var h: Hash = 0 + for i in sPos..ePos: + h = h !& ord(sBuf[i]) + result = !$h + proc hashIgnoreStyle*(x: string): Hash = ## efficient hashing of strings; style is ignored var h: Hash = 0 @@ -145,6 +156,27 @@ proc hashIgnoreStyle*(x: string): Hash = result = !$h +proc hashIgnoreStyle*(sBuf: string, sPos, ePos: int): Hash = + ## efficient hashing of a string buffer, from starting + ## position `sPos` to ending position `ePos`; style is ignored + ## + ## ``hashIgnoreStyle(myBuf, 0, myBuf.high)`` is equivalent + ## to ``hashIgnoreStyle(myBuf)`` + var h: Hash = 0 + var i = sPos + while i <= ePos: + var c = sBuf[i] + if c == '_': + inc(i) + elif isMagicIdentSeparatorRune(cstring(sBuf), i): + inc(i, magicIdentSeparatorRuneByteWidth) + else: + if c in {'A'..'Z'}: + c = chr(ord(c) + (ord('a') - ord('A'))) # toLower() + h = h !& ord(c) + inc(i) + result = !$h + proc hashIgnoreCase*(x: string): Hash = ## efficient hashing of strings; case is ignored var h: Hash = 0 @@ -155,7 +187,22 @@ proc hashIgnoreCase*(x: string): Hash = h = h !& ord(c) result = !$h +proc hashIgnoreCase*(sBuf: string, sPos, ePos: int): Hash = + ## efficient hashing of a string buffer, from starting + ## position `sPos` to ending position `ePos`; case is ignored + ## + ## ``hashIgnoreCase(myBuf, 0, myBuf.high)`` is equivalent + ## to ``hashIgnoreCase(myBuf)`` + var h: Hash = 0 + for i in sPos..ePos: + var c = sBuf[i] + if c in {'A'..'Z'}: + c = chr(ord(c) + (ord('a') - ord('A'))) # toLower() + h = h !& ord(c) + result = !$h + proc hash*(x: float): Hash {.inline.} = + ## efficient hashing of floats. var y = x + 1.0 result = cast[ptr Hash](addr(y))[] @@ -173,10 +220,29 @@ proc hash*[T: tuple](x: T): Hash = result = !$result proc hash*[A](x: openArray[A]): Hash = + ## efficient hashing of arrays and sequences. for it in items(x): result = result !& hash(it) result = !$result +proc hash*[A](aBuf: openArray[A], sPos, ePos: int): Hash = + ## efficient hashing of portions of arrays and sequences. + ## + ## ``hash(myBuf, 0, myBuf.high)`` is equivalent to ``hash(myBuf)`` + for i in sPos..ePos: + result = result !& hash(aBuf[i]) + result = !$result + proc hash*[A](x: set[A]): Hash = + ## efficient hashing of sets. for it in items(x): result = result !& hash(it) result = !$result +when isMainModule: + doAssert( hash("aa bb aaaa1234") == hash("aa bb aaaa1234", 0, 13) ) + doAssert( hashIgnoreCase("aa bb aaaa1234") == hash("aa bb aaaa1234") ) + doAssert( hashIgnoreStyle("aa bb aaaa1234") == hashIgnoreCase("aa bb aaaa1234") ) + let xx = @['H','e','l','l','o'] + let ss = "Hello" + doAssert( hash(xx) == hash(ss) ) + doAssert( hash(xx) == hash(xx, 0, xx.high) ) + doAssert( hash(ss) == hash(ss, 0, ss.high) ) diff --git a/lib/pure/httpclient.nim b/lib/pure/httpclient.nim index 30b838b7e..a5d4ec1a1 100644 --- a/lib/pure/httpclient.nim +++ b/lib/pure/httpclient.nim @@ -81,7 +81,7 @@ import net, strutils, uri, parseutils, strtabs, base64, os, mimetypes, math import asyncnet, asyncdispatch -import rawsockets +import nativesockets type Response* = tuple[ @@ -764,10 +764,10 @@ proc newConnection(client: AsyncHttpClient, url: Uri) {.async.} = let port = if url.port == "": if url.scheme.toLower() == "https": - rawsockets.Port(443) + nativesockets.Port(443) else: - rawsockets.Port(80) - else: rawsockets.Port(url.port.parseInt) + nativesockets.Port(80) + else: nativesockets.Port(url.port.parseInt) if url.scheme.toLower() == "https": when defined(ssl): diff --git a/lib/pure/json.nim b/lib/pure/json.nim index 540a1a8eb..06d5a13e2 100644 --- a/lib/pure/json.nim +++ b/lib/pure/json.nim @@ -622,9 +622,12 @@ proc getNum*(n: JsonNode, default: BiggestInt = 0): BiggestInt = proc getFNum*(n: JsonNode, default: float = 0.0): float = ## Retrieves the float value of a `JFloat JsonNode`. ## - ## Returns ``default`` if ``n`` is not a ``JFloat``, or if ``n`` is nil. - if n.isNil or n.kind != JFloat: return default - else: return n.fnum + ## Returns ``default`` if ``n`` is not a ``JFloat`` or ``JInt``, or if ``n`` is nil. + if n.isNil: return default + case n.kind + of JFloat: return n.fnum + of JInt: return float(n.num) + else: return default proc getBVal*(n: JsonNode, default: bool = false): bool = ## Retrieves the bool value of a `JBool JsonNode`. @@ -1074,9 +1077,9 @@ when not defined(js): ## for nice error messages. var p: JsonParser p.open(s, filename) + defer: p.close() discard getTok(p) # read first token result = p.parseJson() - p.close() proc parseJson*(buffer: string): JsonNode = ## Parses JSON from `buffer`. @@ -1203,6 +1206,17 @@ when isMainModule: testJson{["c", "d"]} = %true assert(testJson["c"]["d"].bval) + # make sure no memory leek when parsing invalid string + let startMemory = getOccupiedMem() + for i in 0 .. 10000: + try: + discard parseJson"""{ invalid""" + except: + discard + # memory diff should less than 2M + assert(abs(getOccupiedMem() - startMemory) < 2 * 1024 * 1024) + + # test `$` let stringified = $testJson let parsedAgain = parseJson(stringified) diff --git a/lib/pure/math.nim b/lib/pure/math.nim index c1d5c9439..391a880ae 100644 --- a/lib/pure/math.nim +++ b/lib/pure/math.nim @@ -21,6 +21,20 @@ include "system/inclrtl" {.push debugger:off .} # the user does not want to trace a part # of the standard library! +proc binom*(n, k: int): int {.noSideEffect.} = + ## Computes the binomial coefficient + if k <= 0: return 1 + if 2*k > n: return binom(n, n-k) + result = n + for i in countup(2, k): + result = (result * (n + 1 - i)) div i + +proc fac*(n: int): int {.noSideEffect.} = + ## Computes the faculty/factorial function. + result = 1 + for i in countup(2, n): + result = result * i + {.push checks:off, line_dir:off, stack_trace:off.} when defined(Posix) and not defined(haiku): @@ -72,21 +86,6 @@ proc classify*(x: float): FloatClass = return fcNormal # XXX: fcSubnormal is not detected! - -proc binom*(n, k: int): int {.noSideEffect.} = - ## Computes the binomial coefficient - if k <= 0: return 1 - if 2*k > n: return binom(n, n-k) - result = n - for i in countup(2, k): - result = (result * (n + 1 - i)) div i - -proc fac*(n: int): int {.noSideEffect.} = - ## Computes the faculty/factorial function. - result = 1 - for i in countup(2, n): - result = result * i - proc isPowerOfTwo*(x: int): bool {.noSideEffect.} = ## Returns true, if `x` is a power of two, false otherwise. ## Zero and negative numbers are not a power of two. @@ -476,7 +475,7 @@ when isMainModule and not defined(JS): return sqrt(num) # check gamma function - assert(tgamma(5.0) == 24.0) # 4! + assert($tgamma(5.0) == $24.0) # 4! assert(lgamma(1.0) == 0.0) # ln(1.0) == 0.0 assert(erf(6.0) > erf(5.0)) assert(erfc(6.0) < erfc(5.0)) diff --git a/lib/pure/memfiles.nim b/lib/pure/memfiles.nim index 27b989597..b9c574944 100644 --- a/lib/pure/memfiles.nim +++ b/lib/pure/memfiles.nim @@ -32,8 +32,9 @@ type size*: int ## size of the memory mapped file when defined(windows): - fHandle: int - mapHandle: int + fHandle: Handle + mapHandle: Handle + wasOpened: bool ## only close if wasOpened else: handle: cint @@ -115,7 +116,8 @@ proc open*(filename: string, mode: FileMode = fmRead, template callCreateFile(winApiProc, filename: expr): expr = winApiProc( filename, - if readonly: GENERIC_READ else: GENERIC_ALL, + # GENERIC_ALL != (GENERIC_READ or GENERIC_WRITE) + if readonly: GENERIC_READ else: GENERIC_READ or GENERIC_WRITE, FILE_SHARE_READ, nil, if newFileSize != -1: CREATE_ALWAYS else: OPEN_EXISTING, @@ -172,6 +174,8 @@ proc open*(filename: string, mode: FileMode = fmRead, if mappedSize != -1: result.size = min(fileSize, mappedSize).int else: result.size = fileSize.int + result.wasOpened = true + else: template fail(errCode: OSErrorCode, msg: expr) = rollback() @@ -226,7 +230,7 @@ proc close*(f: var MemFile) = var lastErr: OSErrorCode when defined(windows): - if f.fHandle != INVALID_HANDLE_VALUE: + if f.fHandle != INVALID_HANDLE_VALUE and f.wasOpened: error = unmapViewOfFile(f.mem) == 0 lastErr = osLastError() error = (closeHandle(f.mapHandle) == 0) or error @@ -243,6 +247,7 @@ proc close*(f: var MemFile) = when defined(windows): f.fHandle = 0 f.mapHandle = 0 + f.wasOpened = false else: f.handle = 0 diff --git a/lib/pure/rawsockets.nim b/lib/pure/nativesockets.nim index 7873e7226..c9e067a3e 100644 --- a/lib/pure/rawsockets.nim +++ b/lib/pure/nativesockets.nim @@ -93,8 +93,8 @@ when useWinVersion: IOC_IN* = int(-2147483648) FIONBIO* = IOC_IN.int32 or ((sizeof(int32) and IOCPARM_MASK) shl 16) or (102 shl 8) or 126 - rawAfInet = winlean.AF_INET - rawAfInet6 = winlean.AF_INET6 + nativeAfInet = winlean.AF_INET + nativeAfInet6 = winlean.AF_INET6 proc ioctlsocket*(s: SocketHandle, cmd: clong, argptr: ptr clong): cint {. @@ -102,8 +102,8 @@ when useWinVersion: else: let osInvalidSocket* = posix.INVALID_SOCKET - rawAfInet = posix.AF_INET - rawAfInet6 = posix.AF_INET6 + nativeAfInet = posix.AF_INET + nativeAfInet6 = posix.AF_INET6 proc `==`*(a, b: Port): bool {.borrow.} ## ``==`` for ports. @@ -157,12 +157,14 @@ else: result = cint(ord(p)) -proc newRawSocket*(domain: Domain = AF_INET, sockType: SockType = SOCK_STREAM, - protocol: Protocol = IPPROTO_TCP): SocketHandle = +proc newNativeSocket*(domain: Domain = AF_INET, + sockType: SockType = SOCK_STREAM, + protocol: Protocol = IPPROTO_TCP): SocketHandle = ## Creates a new socket; returns `InvalidSocket` if an error occurs. socket(toInt(domain), toInt(sockType), toInt(protocol)) -proc newRawSocket*(domain: cint, sockType: cint, protocol: cint): SocketHandle = +proc newNativeSocket*(domain: cint, sockType: cint, + protocol: cint): SocketHandle = ## Creates a new socket; returns `InvalidSocket` if an error occurs. ## ## Use this overload if one of the enums specified above does @@ -201,7 +203,9 @@ proc getAddrInfo*(address: string, port: Port, domain: Domain = AF_INET, hints.ai_family = toInt(domain) hints.ai_socktype = toInt(sockType) hints.ai_protocol = toInt(protocol) - hints.ai_flags = AI_V4MAPPED + # https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=198092 + when not defined(freebsd): + hints.ai_flags = AI_V4MAPPED var gaiResult = getaddrinfo(address, $port, addr(hints), result) if gaiResult != 0'i32: when useWinVersion: @@ -229,17 +233,17 @@ proc ntohs*(x: int16): int16 = when cpuEndian == bigEndian: result = x else: result = (x shr 8'i16) or (x shl 8'i16) -proc htonl*(x: int32): int32 = +template htonl*(x: int32): expr = ## Converts 32-bit integers from host to network byte order. On machines ## where the host byte order is the same as network byte order, this is ## a no-op; otherwise, it performs a 4-byte swap operation. - result = rawsockets.ntohl(x) + nativesockets.ntohl(x) -proc htons*(x: int16): int16 = +template htons*(x: int16): expr = ## Converts 16-bit positive integers from host to network byte order. ## On machines where the host byte order is the same as network byte ## order, this is a no-op; otherwise, it performs a 2-byte swap operation. - result = rawsockets.ntohs(x) + nativesockets.ntohs(x) proc getServByName*(name, proto: string): Servent {.tags: [ReadIOEffect].} = ## Searches the database from the beginning and finds the first entry for @@ -280,7 +284,7 @@ proc getHostByAddr*(ip: string): Hostent {.tags: [ReadIOEffect].} = when useWinVersion: var s = winlean.gethostbyaddr(addr(myaddr), sizeof(myaddr).cuint, - cint(rawsockets.AF_INET)) + cint(AF_INET)) if s == nil: raiseOSError(osLastError()) else: var s = posix.gethostbyaddr(addr(myaddr), sizeof(myaddr).Socklen, @@ -330,9 +334,9 @@ proc getSockDomain*(socket: SocketHandle): Domain = if getsockname(socket, cast[ptr SockAddr](addr(name)), addr(namelen)) == -1'i32: raiseOSError(osLastError()) - if name.sa_family == rawAfInet: + if name.sa_family == nativeAfInet: result = AF_INET - elif name.sa_family == rawAfInet6: + elif name.sa_family == nativeAfInet6: result = AF_INET6 else: raiseOSError(osLastError(), "unknown socket family in getSockFamily") @@ -340,12 +344,11 @@ proc getSockDomain*(socket: SocketHandle): Domain = proc getAddrString*(sockAddr: ptr SockAddr): string = ## return the string representation of address within sockAddr - if sockAddr.sa_family == rawAfInet: + if sockAddr.sa_family == nativeAfInet: result = $inet_ntoa(cast[ptr Sockaddr_in](sockAddr).sin_addr) - elif sockAddr.sa_family == rawAfInet6: + elif sockAddr.sa_family == nativeAfInet6: when not useWinVersion: # TODO: Windows - var v6addr = cast[ptr Sockaddr_in6](sockAddr).sin6_addr result = newString(posix.INET6_ADDRSTRLEN) let addr6 = addr cast[ptr Sockaddr_in6](sockAddr).sin6_addr discard posix.inet_ntop(posix.AF_INET6, addr6, result.cstring, @@ -369,7 +372,79 @@ proc getSockName*(socket: SocketHandle): Port = if getsockname(socket, cast[ptr SockAddr](addr(name)), addr(namelen)) == -1'i32: raiseOSError(osLastError()) - result = Port(rawsockets.ntohs(name.sin_port)) + result = Port(nativesockets.ntohs(name.sin_port)) + +proc getLocalAddr*(socket: SocketHandle, domain: Domain): (string, Port) = + ## returns the socket's local address and port number. + ## + ## Similar to POSIX's `getsockname`:idx:. + case domain + of AF_INET: + var name: Sockaddr_in + when useWinVersion: + name.sin_family = int16(ord(AF_INET)) + else: + name.sin_family = posix.AF_INET + var namelen = sizeof(name).SockLen + if getsockname(socket, cast[ptr SockAddr](addr(name)), + addr(namelen)) == -1'i32: + raiseOSError(osLastError()) + result = ($inet_ntoa(name.sin_addr), + Port(nativesockets.ntohs(name.sin_port))) + of AF_INET6: + var name: Sockaddr_in6 + when useWinVersion: + name.sin6_family = int16(ord(AF_INET6)) + else: + name.sin6_family = posix.AF_INET6 + var namelen = sizeof(name).SockLen + if getsockname(socket, cast[ptr SockAddr](addr(name)), + addr(namelen)) == -1'i32: + raiseOSError(osLastError()) + # Cannot use INET6_ADDRSTRLEN here, because it's a C define. + var buf: array[64, char] + if inet_ntop(name.sin6_family.cint, + addr name, buf.cstring, sizeof(buf).int32).isNil: + raiseOSError(osLastError()) + result = ($buf, Port(nativesockets.ntohs(name.sin6_port))) + else: + raiseOSError(OSErrorCode(-1), "invalid socket family in getLocalAddr") + +proc getPeerAddr*(socket: SocketHandle, domain: Domain): (string, Port) = + ## returns the socket's peer address and port number. + ## + ## Similar to POSIX's `getpeername`:idx: + case domain + of AF_INET: + var name: Sockaddr_in + when useWinVersion: + name.sin_family = int16(ord(AF_INET)) + else: + name.sin_family = posix.AF_INET + var namelen = sizeof(name).SockLen + if getpeername(socket, cast[ptr SockAddr](addr(name)), + addr(namelen)) == -1'i32: + raiseOSError(osLastError()) + result = ($inet_ntoa(name.sin_addr), + Port(nativesockets.ntohs(name.sin_port))) + of AF_INET6: + var name: Sockaddr_in6 + when useWinVersion: + name.sin6_family = int16(ord(AF_INET6)) + else: + name.sin6_family = posix.AF_INET6 + var namelen = sizeof(name).SockLen + if getpeername(socket, cast[ptr SockAddr](addr(name)), + addr(namelen)) == -1'i32: + raiseOSError(osLastError()) + # Cannot use INET6_ADDRSTRLEN here, because it's a C define. + var buf: array[64, char] + if inet_ntop(name.sin6_family.cint, + addr name, buf.cstring, sizeof(buf).int32).isNil: + raiseOSError(osLastError()) + result = ($buf, Port(nativesockets.ntohs(name.sin6_port))) + else: + raiseOSError(OSErrorCode(-1), "invalid socket family in getLocalAddr") proc getSockOptInt*(socket: SocketHandle, level, optname: int): int {. tags: [ReadIOEffect].} = diff --git a/lib/pure/net.nim b/lib/pure/net.nim index 141543c70..d1016011e 100644 --- a/lib/pure/net.nim +++ b/lib/pure/net.nim @@ -10,7 +10,7 @@ ## This module implements a high-level cross-platform sockets interface. {.deadCodeElim: on.} -import rawsockets, os, strutils, unsigned, parseutils, times +import nativesockets, os, strutils, unsigned, parseutils, times export Port, `$`, `==` const useWinVersion = defined(Windows) or defined(nimdoc) @@ -145,7 +145,7 @@ proc newSocket*(domain, sockType, protocol: cint, buffered = true): Socket = ## Creates a new socket. ## ## If an error occurs EOS will be raised. - let fd = newRawSocket(domain, sockType, protocol) + let fd = newNativeSocket(domain, sockType, protocol) if fd == osInvalidSocket: raiseOSError(osLastError()) result = newSocket(fd, domain.Domain, sockType.SockType, protocol.Protocol, @@ -156,7 +156,7 @@ proc newSocket*(domain: Domain = AF_INET, sockType: SockType = SOCK_STREAM, ## Creates a new socket. ## ## If an error occurs EOS will be raised. - let fd = newRawSocket(domain, sockType, protocol) + let fd = newNativeSocket(domain, sockType, protocol) if fd == osInvalidSocket: raiseOSError(osLastError()) result = newSocket(fd, domain, sockType, protocol, buffered) @@ -223,10 +223,7 @@ when defined(ssl): of protSSLv23: newCTX = SSL_CTX_new(SSLv23_method()) # SSlv2,3 and TLS1 support. of protSSLv2: - when not defined(linux): - newCTX = SSL_CTX_new(SSLv2_method()) - else: - raiseSslError() + raiseSslError("SSLv2 is no longer secure and has been deprecated, use protSSLv3") of protSSLv3: newCTX = SSL_CTX_new(SSLv3_method()) of protTLSv1: @@ -357,7 +354,7 @@ proc listen*(socket: Socket, backlog = SOMAXCONN) {.tags: [ReadIOEffect].} = ## queue of pending connections. ## ## Raises an EOS error upon failure. - if rawsockets.listen(socket.fd, backlog) < 0'i32: + if nativesockets.listen(socket.fd, backlog) < 0'i32: raiseOSError(osLastError()) proc bindAddr*(socket: Socket, port = Port(0), address = "") {. @@ -533,6 +530,18 @@ proc getSockOpt*(socket: Socket, opt: SOBool, level = SOL_SOCKET): bool {. var res = getSockOptInt(socket.fd, cint(level), toCInt(opt)) result = res != 0 +proc getLocalAddr*(socket: Socket): (string, Port) = + ## Get the socket's local address and port number. + ## + ## This is high-level interface for `getsockname`:idx:. + getLocalAddr(socket.fd, socket.domain) + +proc getPeerAddr*(socket: Socket): (string, Port) = + ## Get the socket's peer address and port number. + ## + ## This is high-level interface for `getpeername`:idx:. + getPeerAddr(socket.fd, socket.domain) + proc setSockOpt*(socket: Socket, opt: SOBool, value: bool, level = SOL_SOCKET) {. tags: [WriteIOEffect].} = ## Sets option ``opt`` to a boolean value specified by ``value``. diff --git a/lib/pure/options.nim b/lib/pure/options.nim index ef01e1260..3122d58b1 100644 --- a/lib/pure/options.nim +++ b/lib/pure/options.nim @@ -108,6 +108,36 @@ proc get*[T](self: Option[T]): T = raise UnpackError(msg : "Can't obtain a value from a `none`") self.val +proc get*[T](self: Option[T], otherwise: T): T = + ## Returns the contents of this option or `otherwise` if the option is none. + if self.isSome: + self.val + else: + otherwise + + +proc map*[T](self: Option[T], callback: proc (input: T)) = + ## Applies a callback to the value in this Option + if self.has: + callback(self.val) + +proc map*[T, R](self: Option[T], callback: proc (input: T): R): Option[R] = + ## Applies a callback to the value in this Option and returns an option + ## containing the new value. If this option is None, None will be returned + if self.has: + some[R]( callback(self.val) ) + else: + none(R) + +proc filter*[T](self: Option[T], callback: proc (input: T): bool): Option[T] = + ## Applies a callback to the value in this Option. If the callback returns + ## `true`, the option is returned as a Some. If it returns false, it is + ## returned as a None. + if self.has and not callback(self.val): + none(T) + else: + self + proc `==`*(a, b: Option): bool = ## Returns ``true`` if both ``Option``s are ``none``, @@ -115,8 +145,16 @@ proc `==`*(a, b: Option): bool = (a.has and b.has and a.val == b.val) or (not a.has and not b.has) +proc `$`*[T]( self: Option[T] ): string = + ## Returns the contents of this option or `otherwise` if the option is none. + if self.has: + "Some(" & $self.val & ")" + else: + "None[" & T.name & "]" + + when isMainModule: - import unittest + import unittest, sequtils suite "optionals": # work around a bug in unittest @@ -158,3 +196,27 @@ when isMainModule: check false when compiles(none(string) == none(int)): check false + + test "get with a default value": + check( some("Correct").get("Wrong") == "Correct" ) + check( stringNone.get("Correct") == "Correct" ) + + test "$": + check( $(some("Correct")) == "Some(Correct)" ) + check( $(stringNone) == "None[string]" ) + + test "map with a void result": + var procRan = 0 + some(123).map(proc (v: int) = procRan = v) + check procRan == 123 + intNone.map(proc (v: int) = check false) + + test "map": + check( some(123).map(proc (v: int): int = v * 2) == some(246) ) + check( intNone.map(proc (v: int): int = v * 2).isNone ) + + test "filter": + check( some(123).filter(proc (v: int): bool = v == 123) == some(123) ) + check( some(456).filter(proc (v: int): bool = v == 123).isNone ) + check( intNone.filter(proc (v: int): bool = check false).isNone ) + diff --git a/lib/pure/os.nim b/lib/pure/os.nim index f413371cb..c01228563 100644 --- a/lib/pure/os.nim +++ b/lib/pure/os.nim @@ -810,11 +810,12 @@ type {.deprecated: [TPathComponent: PathComponent].} -iterator walkDir*(dir: string): tuple[kind: PathComponent, path: string] {. +iterator walkDir*(dir: string; relative=false): tuple[kind: PathComponent, path: string] {. tags: [ReadDirEffect].} = ## walks over the directory `dir` and yields for each directory or file in ## `dir`. The component type and full path for each item is returned. - ## Walking is not recursive. + ## Walking is not recursive. If ``relative`` is true the resulting path is + ## shortened to be relative to ``dir``. ## Example: This directory structure:: ## dirA / dirB / fileB1.txt ## / dirC @@ -843,7 +844,9 @@ iterator walkDir*(dir: string): tuple[kind: PathComponent, path: string] {. k = pcDir if (f.dwFileAttributes and FILE_ATTRIBUTE_REPARSE_POINT) != 0'i32: k = succ(k) - yield (k, dir / extractFilename(getFilename(f))) + let xx = if relative: extractFilename(getFilename(f)) + else: dir / extractFilename(getFilename(f)) + yield (k, xx) if findNextFile(h, f) == 0'i32: break findClose(h) else: @@ -855,7 +858,8 @@ iterator walkDir*(dir: string): tuple[kind: PathComponent, path: string] {. var y = $x.d_name if y != "." and y != "..": var s: Stat - y = dir / y + if not relative: + y = dir / y var k = pcFile when defined(linux) or defined(macosx) or defined(bsd): diff --git a/lib/pure/ospaths.nim b/lib/pure/ospaths.nim index 99f6bcd4d..e9f5bee0a 100644 --- a/lib/pure/ospaths.nim +++ b/lib/pure/ospaths.nim @@ -10,6 +10,10 @@ # Included by the ``os`` module but a module in its own right for NimScript # support. +when isMainModule: + {.pragma: rtl.} + import strutils + when defined(nimscript) or (defined(nimdoc) and not declared(os)): {.pragma: rtl.} {.push hint[ConvFromXtoItselfNotNeeded]:off.} @@ -22,8 +26,10 @@ when not declared(getEnv) or defined(nimscript): ## to an environment variable ReadDirEffect* = object of ReadIOEffect ## effect that denotes a write - ## operation to the directory structure - WriteDirEffect* = object of WriteIOEffect ## effect that denotes a write operation to + ## operation to the directory + ## structure + WriteDirEffect* = object of WriteIOEffect ## effect that denotes a write + ## operation to ## the directory structure OSErrorCode* = distinct int32 ## Specifies an OS Error Code. @@ -59,13 +65,13 @@ when not declared(getEnv) or defined(nimscript): AltSep* = '/' ## An alternative character used by the operating system to separate ## pathname components, or the same as `DirSep` if only one separator - ## character exists. This is set to '/' on Windows systems where `DirSep` - ## is a backslash. + ## character exists. This is set to '/' on Windows systems + ## where `DirSep` is a backslash. PathSep* = ':' ## The character conventionally used by the operating system to separate - ## search patch components (as in PATH), such as ':' for POSIX or ';' for - ## Windows. + ## search patch components (as in PATH), such as ':' for POSIX + ## or ';' for Windows. FileSystemCaseSensitive* = true ## true if the file system is case sensitive, false otherwise. Used by @@ -100,7 +106,8 @@ when not declared(getEnv) or defined(nimscript): # MacOS directory separator is a colon ":" which is the only character not # allowed in filenames. # - # A path containing no colon or which begins with a colon is a partial path. + # A path containing no colon or which begins with a colon is a partial + # path. # E.g. ":kalle:petter" ":kalle" "kalle" # # All other paths are full (absolute) paths. E.g. "HD:kalle:" "HD:" @@ -202,9 +209,9 @@ when not declared(getEnv) or defined(nimscript): proc joinPath*(parts: varargs[string]): string {.noSideEffect, rtl, extern: "nos$1OpenArray".} = - ## The same as `joinPath(head, tail)`, but works with any number of directory - ## parts. You need to pass at least one element or the proc will assert in - ## debug builds and crash on release builds. + ## The same as `joinPath(head, tail)`, but works with any number of + ## directory parts. You need to pass at least one element or the proc + ## will assert in debug builds and crash on release builds. result = parts[0] for i in 1..high(parts): result = joinPath(result, parts[i]) @@ -312,8 +319,8 @@ when not declared(getEnv) or defined(nimscript): if inclusive: yield path proc `/../` * (head, tail: string): string {.noSideEffect.} = - ## The same as ``parentDir(head) / tail`` unless there is no parent directory. - ## Then ``head / tail`` is performed instead. + ## The same as ``parentDir(head) / tail`` unless there is no parent + ## directory. Then ``head / tail`` is performed instead. let sepPos = parentDirPos(head) if sepPos >= 0: result = substr(head, 0, sepPos-1) / tail @@ -496,7 +503,8 @@ when defined(nimdoc) and not declared(os): proc existsFile(x: string): bool = discard when declared(getEnv) or defined(nimscript): - proc getHomeDir*(): string {.rtl, extern: "nos$1", tags: [ReadEnvEffect].} = + proc getHomeDir*(): string {.rtl, extern: "nos$1", + tags: [ReadEnvEffect, ReadIOEffect].} = ## Returns the home directory of the current user. ## ## This proc is wrapped by the expandTilde proc for the convenience of @@ -504,18 +512,21 @@ when declared(getEnv) or defined(nimscript): when defined(windows): return string(getEnv("USERPROFILE")) & "\\" else: return string(getEnv("HOME")) & "/" - proc getConfigDir*(): string {.rtl, extern: "nos$1", tags: [ReadEnvEffect].} = + proc getConfigDir*(): string {.rtl, extern: "nos$1", + tags: [ReadEnvEffect, ReadIOEffect].} = ## Returns the config directory of the current user for applications. when defined(windows): return string(getEnv("APPDATA")) & "\\" else: return string(getEnv("HOME")) & "/.config/" - proc getTempDir*(): string {.rtl, extern: "nos$1", tags: [ReadEnvEffect].} = + proc getTempDir*(): string {.rtl, extern: "nos$1", + tags: [ReadEnvEffect, ReadIOEffect].} = ## Returns the temporary directory of the current user for applications to ## save temporary files in. when defined(windows): return string(getEnv("TEMP")) & "\\" else: return "/tmp/" - proc expandTilde*(path: string): string {.tags: [ReadEnvEffect].} = + proc expandTilde*(path: string): string {. + tags: [ReadEnvEffect, ReadIOEffect].} = ## Expands a path starting with ``~/`` to a full path. ## ## If `path` starts with the tilde character and is followed by `/` or `\\` @@ -523,8 +534,8 @@ when declared(getEnv) or defined(nimscript): ## the getHomeDir() proc, otherwise the input path will be returned without ## modification. ## - ## The behaviour of this proc is the same on the Windows platform despite not - ## having this convention. Example: + ## The behaviour of this proc is the same on the Windows platform despite + ## not having this convention. Example: ## ## .. code-block:: nim ## let configFile = expandTilde("~" / "appname.cfg") @@ -545,7 +556,8 @@ when declared(getEnv) or defined(nimscript): yield substr(s, first, last-1) inc(last) - proc findExe*(exe: string): string {.tags: [ReadDirEffect, ReadEnvEffect].} = + proc findExe*(exe: string): string {. + tags: [ReadDirEffect, ReadEnvEffect, ReadIOEffect].} = ## Searches for `exe` in the current working directory and then ## in directories listed in the ``PATH`` environment variable. ## Returns "" if the `exe` cannot be found. On DOS-like platforms, `exe` diff --git a/lib/pure/osproc.nim b/lib/pure/osproc.nim index 7431be702..fa20afff0 100644 --- a/lib/pure/osproc.nim +++ b/lib/pure/osproc.nim @@ -24,6 +24,20 @@ when defined(linux): import linux type + ProcessOption* = enum ## options that can be passed `startProcess` + poEchoCmd, ## echo the command before execution + poUsePath, ## Asks system to search for executable using PATH environment + ## variable. + ## On Windows, this is the default. + poEvalCommand, ## Pass `command` directly to the shell, without quoting. + ## Use it only if `command` comes from trused source. + poStdErrToStdOut, ## merge stdout and stderr to the stdout stream + poParentStreams, ## use the parent's streams + poInteractive ## optimize the buffer handling for responsiveness for + ## UI applications. Currently this only affects + ## Windows: Named pipes are used so that you can peek + ## at the process' output streams. + ProcessObj = object of RootObj when defined(windows): fProcessHandle: Handle @@ -34,18 +48,10 @@ type inStream, outStream, errStream: Stream id: Pid exitCode: cint + options: set[ProcessOption] Process* = ref ProcessObj ## represents an operating system process - ProcessOption* = enum ## options that can be passed `startProcess` - poEchoCmd, ## echo the command before execution - poUsePath, ## Asks system to search for executable using PATH environment - ## variable. - ## On Windows, this is the default. - poEvalCommand, ## Pass `command` directly to the shell, without quoting. - ## Use it only if `command` comes from trused source. - poStdErrToStdOut, ## merge stdout and stderr to the stdout stream - poParentStreams ## use the parent's streams {.deprecated: [TProcess: ProcessObj, PProcess: Process, TProcessOption: ProcessOption].} @@ -242,7 +248,8 @@ proc countProcessors*(): int {.rtl, extern: "nosp$1".} = proc execProcesses*(cmds: openArray[string], options = {poStdErrToStdOut, poParentStreams}, n = countProcessors(), - beforeRunEvent: proc(idx: int) = nil): int + beforeRunEvent: proc(idx: int) = nil, + afterRunEvent: proc(idx: int, p: Process) = nil): int {.rtl, extern: "nosp$1", tags: [ExecIOEffect, TimeEffect, ReadEnvEffect, RootEffect]} = ## executes the commands `cmds` in parallel. Creates `n` processes @@ -272,6 +279,7 @@ proc execProcesses*(cmds: openArray[string], err.add("\n") echo(err) result = max(waitForExit(q[r]), result) + if afterRunEvent != nil: afterRunEvent(r, q[r]) if q[r] != nil: close(q[r]) if beforeRunEvent != nil: beforeRunEvent(i) @@ -285,6 +293,7 @@ proc execProcesses*(cmds: openArray[string], if not running(q[r]): #echo(outputStream(q[r]).readLine()) result = max(waitForExit(q[r]), result) + if afterRunEvent != nil: afterRunEvent(r, q[r]) if q[r] != nil: close(q[r]) if beforeRunEvent != nil: beforeRunEvent(i) @@ -293,6 +302,7 @@ proc execProcesses*(cmds: openArray[string], if i > high(cmds): break for j in 0..m-1: result = max(waitForExit(q[j]), result) + if afterRunEvent != nil: afterRunEvent(j, q[j]) if q[j] != nil: close(q[j]) else: for i in 0..high(cmds): @@ -300,9 +310,10 @@ proc execProcesses*(cmds: openArray[string], beforeRunEvent(i) var p = startProcess(cmds[i], options=options + {poEvalCommand}) result = max(waitForExit(p), result) + if afterRunEvent != nil: afterRunEvent(i, p) close(p) -proc select*(readfds: var seq[Process], timeout = 500): int +proc select*(readfds: var seq[Process], timeout = 500): int {.benign.} ## `select` with a sensible Nim interface. `timeout` is in milliseconds. ## Specify -1 for no timeout. Returns the number of processes that are ## ready to read from. The processes that are ready to be read from are @@ -352,7 +363,7 @@ when defined(Windows) and not defined(useNimRtl): # TRUE and n (>0) bytes returned (good data). # FALSE and bytes returned undefined (system error). if a == 0 and br != 0: raiseOSError(osLastError()) - s.atTheEnd = br < bufLen + s.atTheEnd = br == 0 #< bufLen result = br proc hsWriteData(s: Stream, buffer: pointer, bufLen: int) = @@ -394,13 +405,68 @@ when defined(Windows) and not defined(useNimRtl): #var # O_WRONLY {.importc: "_O_WRONLY", header: "<fcntl.h>".}: int # O_RDONLY {.importc: "_O_RDONLY", header: "<fcntl.h>".}: int + proc myDup(h: Handle; inherit: WinBool=1): Handle = + let thisProc = getCurrentProcess() + if duplicateHandle(thisProc, h, + thisProc, addr result,0,inherit, + DUPLICATE_SAME_ACCESS) == 0: + raiseOSError(osLastError()) + + proc createAllPipeHandles(si: var STARTUPINFO; + stdin, stdout, stderr: var Handle) = + var sa: SECURITY_ATTRIBUTES + sa.nLength = sizeof(SECURITY_ATTRIBUTES).cint + sa.lpSecurityDescriptor = nil + sa.bInheritHandle = 1 + let pipeOutName = newWideCString(r"\\.\pipe\stdout") + let pipeInName = newWideCString(r"\\.\pipe\stdin") + let pipeOut = createNamedPipe(pipeOutName, + dwOpenMode=PIPE_ACCESS_INBOUND or FILE_FLAG_WRITE_THROUGH, + dwPipeMode=PIPE_NOWAIT, + nMaxInstances=1, + nOutBufferSize=1024, nInBufferSize=1024, + nDefaultTimeOut=0,addr sa) + if pipeOut == INVALID_HANDLE_VALUE: + raiseOSError(osLastError()) + let pipeIn = createNamedPipe(pipeInName, + dwOpenMode=PIPE_ACCESS_OUTBOUND or FILE_FLAG_WRITE_THROUGH, + dwPipeMode=PIPE_NOWAIT, + nMaxInstances=1, + nOutBufferSize=1024, nInBufferSize=1024, + nDefaultTimeOut=0,addr sa) + if pipeIn == INVALID_HANDLE_VALUE: + raiseOSError(osLastError()) + + si.hStdOutput = createFileW(pipeOutName, + FILE_WRITE_DATA or SYNCHRONIZE, 0, addr sa, + OPEN_EXISTING, # very important flag! + FILE_ATTRIBUTE_NORMAL, + 0 # no template file for OPEN_EXISTING + ) + if si.hStdOutput == INVALID_HANDLE_VALUE: + raiseOSError(osLastError()) + si.hStdError = myDup(si.hStdOutput) + si.hStdInput = createFileW(pipeInName, + FILE_READ_DATA or SYNCHRONIZE, 0, addr sa, + OPEN_EXISTING, # very important flag! + FILE_ATTRIBUTE_NORMAL, + 0 # no template file for OPEN_EXISTING + ) + if si.hStdOutput == INVALID_HANDLE_VALUE: + raiseOSError(osLastError()) + + stdin = myDup(pipeIn, 0) + stdout = myDup(pipeOut, 0) + discard closeHandle(pipeIn) + discard closeHandle(pipeOut) + stderr = stdout proc createPipeHandles(rdHandle, wrHandle: var Handle) = - var piInheritablePipe: SECURITY_ATTRIBUTES - piInheritablePipe.nLength = sizeof(SECURITY_ATTRIBUTES).cint - piInheritablePipe.lpSecurityDescriptor = nil - piInheritablePipe.bInheritHandle = 1 - if createPipe(rdHandle, wrHandle, piInheritablePipe, 1024) == 0'i32: + var sa: SECURITY_ATTRIBUTES + sa.nLength = sizeof(SECURITY_ATTRIBUTES).cint + sa.lpSecurityDescriptor = nil + sa.bInheritHandle = 1 + if createPipe(rdHandle, wrHandle, sa, 1024) == 0'i32: raiseOSError(osLastError()) proc fileClose(h: Handle) {.inline.} = @@ -417,16 +483,20 @@ when defined(Windows) and not defined(useNimRtl): success: int hi, ho, he: Handle new(result) + result.options = options si.cb = sizeof(si).cint if poParentStreams notin options: si.dwFlags = STARTF_USESTDHANDLES # STARTF_USESHOWWINDOW or - createPipeHandles(si.hStdInput, hi) - createPipeHandles(ho, si.hStdOutput) - if poStdErrToStdOut in options: - si.hStdError = si.hStdOutput - he = ho + if poInteractive notin options: + createPipeHandles(si.hStdInput, hi) + createPipeHandles(ho, si.hStdOutput) + if poStdErrToStdOut in options: + si.hStdError = si.hStdOutput + he = ho + else: + createPipeHandles(he, si.hStdError) else: - createPipeHandles(he, si.hStdError) + createAllPipeHandles(si, hi, ho, he) result.inHandle = FileHandle(hi) result.outHandle = FileHandle(ho) result.errHandle = FileHandle(he) @@ -469,6 +539,7 @@ when defined(Windows) and not defined(useNimRtl): if e != nil: dealloc(e) if success == 0: + if poInteractive in result.options: close(result) const errInvalidParameter = 87.int const errFileNotFound = 2.int if lastError.int in {errInvalidParameter, errFileNotFound}: @@ -482,12 +553,12 @@ when defined(Windows) and not defined(useNimRtl): result.id = procInfo.dwProcessId proc close(p: Process) = - when false: - # somehow this does not work on Windows: + if poInteractive in p.options: + # somehow this is not always required on Windows: discard closeHandle(p.inHandle) discard closeHandle(p.outHandle) discard closeHandle(p.errHandle) - discard closeHandle(p.FProcessHandle) + #discard closeHandle(p.FProcessHandle) proc suspend(p: Process) = discard suspendThread(p.fProcessHandle) @@ -564,7 +635,7 @@ when defined(Windows) and not defined(useNimRtl): assert readfds.len <= MAXIMUM_WAIT_OBJECTS var rfds: WOHandleArray for i in 0..readfds.len()-1: - rfds[i] = readfds[i].fProcessHandle + rfds[i] = readfds[i].outHandle #fProcessHandle var ret = waitForMultipleObjects(readfds.len.int32, addr(rfds), 0'i32, timeout.int32) @@ -578,6 +649,11 @@ when defined(Windows) and not defined(useNimRtl): readfds.del(i) return 1 + proc hasData*(p: Process): bool = + var x: int32 + if peekNamedPipe(p.outHandle, lpTotalBytesAvail=addr x): + result = x > 0 + elif not defined(useNimRtl): const readIdx = 0 @@ -635,6 +711,7 @@ elif not defined(useNimRtl): var pStdin, pStdout, pStderr: array [0..1, cint] new(result) + result.options = options result.exitCode = -3 # for ``waitForExit`` if poParentStreams notin options: if pipe(pStdin) != 0'i32 or pipe(pStdout) != 0'i32 or @@ -960,6 +1037,15 @@ elif not defined(useNimRtl): pruneProcessSet(readfds, (rd)) + proc hasData*(p: Process): bool = + var rd: TFdSet + + FD_ZERO(rd) + let m = max(0, int(p.outHandle)) + FD_SET(cint(p.outHandle), rd) + + result = int(select(cint(m+1), addr(rd), nil, nil, nil)) == 1 + proc execCmdEx*(command: string, options: set[ProcessOption] = { poStdErrToStdOut, poUsePath}): tuple[ diff --git a/lib/pure/rationals.nim b/lib/pure/rationals.nim index 60d09c71a..72c64befc 100644 --- a/lib/pure/rationals.nim +++ b/lib/pure/rationals.nim @@ -18,8 +18,9 @@ type Rational*[T] = object ## a rational number, consisting of a numerator and denominator num*, den*: T -proc initRational*[T](num, den: T): Rational[T] = +proc initRational*[T:SomeInteger](num, den: T): Rational[T] = ## Create a new rational number. + assert(den != 0, "a denominator of zero value is invalid") result.num = num result.den = den @@ -33,11 +34,68 @@ proc `$`*[T](x: Rational[T]): string = ## Turn a rational number into a string. result = $x.num & "/" & $x.den -proc toRational*[T](x: T): Rational[T] = +proc toRational*[T:SomeInteger](x: T): Rational[T] = ## Convert some integer `x` to a rational number. result.num = x result.den = 1 +proc toRationalSub(x: float, n: int): Rational[int] = + var + a = 0 + b, c, d = 1 + result = 0 // 1 # rational 0 + while b <= n and d <= n: + let ac = (a+c) + let bd = (b+d) + # scale by 1000 so not overflow for high precision + let mediant = (ac/1000) / (bd/1000) + if x == mediant: + if bd <= n: + result.num = ac + result.den = bd + return result + elif d > b: + result.num = c + result.den = d + return result + else: + result.num = a + result.den = b + return result + elif x > mediant: + a = ac + b = bd + else: + c = ac + d = bd + if (b > n): + return initRational(c, d) + return initRational(a, b) + +proc toRational*(x: float, n: int = high(int)): Rational[int] = + ## Calculate the best rational numerator and denominator + ## that approximates to `x`, where the denominator is + ## smaller than `n` (default is the largest possible + ## int to give maximum resolution) + ## + ## The algorithm is based on the Farey sequence named + ## after John Farey + ## + ## .. code-block:: Nim + ## import math, rationals + ## for i in 1..10: + ## let t = (10 ^ (i+3)).int + ## let x = toRational(PI, t) + ## let newPI = x.num / x.den + ## echo x, " ", newPI, " error: ", PI - newPI, " ", t + if x > 1: + result = toRationalSub(1.0/x, n) + swap(result.num, result.den) + elif x == 1.0: + result = 1 // 1 + else: + result = toRationalSub(x, n) + proc toFloat*[T](x: Rational[T]): float = ## Convert a rational number `x` to a float. x.num / x.den @@ -47,7 +105,7 @@ proc toInt*[T](x: Rational[T]): int = ## `x` does not contain an integer value. x.num div x.den -proc reduce*[T](x: var Rational[T]) = +proc reduce*[T:SomeInteger](x: var Rational[T]) = ## Reduce rational `x`. let common = gcd(x.num, x.den) if x.den > 0: @@ -287,3 +345,8 @@ when isMainModule: assert toRational(5) == 5//1 assert abs(toFloat(y) - 0.4814814814814815) < 1.0e-7 assert toInt(z) == 0 + + assert toRational(0.98765432) == 12345679 // 12500000 + assert toRational(0.1, 1000000) == 1 // 10 + assert toRational(0.9, 1000000) == 9 // 10 + assert toRational(PI) == 80143857 // 25510582 diff --git a/lib/pure/selectors.nim b/lib/pure/selectors.nim index bfc393a96..ca969c761 100644 --- a/lib/pure/selectors.nim +++ b/lib/pure/selectors.nim @@ -13,6 +13,8 @@ import os, unsigned, hashes when defined(linux): import posix, epoll +elif defined(macosx) or defined(freebsd) or defined(openbsd) or defined(netbsd): + import posix, kqueue, times elif defined(windows): import winlean else: @@ -79,7 +81,6 @@ when defined(nimdoc): proc `[]`*(s: Selector, fd: SocketHandle): SelectorKey = ## Retrieves the selector key for ``fd``. - elif defined(linux): type Selector* = object @@ -99,15 +100,13 @@ elif defined(linux): result.data.fd = fd.cint proc register*(s: var Selector, fd: SocketHandle, events: set[Event], - data: SelectorData) = + data: SelectorData) = var event = createEventStruct(events, fd) if events != {}: if epoll_ctl(s.epollFD, EPOLL_CTL_ADD, fd, addr(event)) != 0: raiseOSError(osLastError()) - var key = SelectorKey(fd: fd, events: events, data: data) - - s.fds[fd] = key + s.fds[fd] = SelectorKey(fd: fd, events: events, data: data) proc update*(s: var Selector, fd: SocketHandle, events: set[Event]) = if s.fds[fd].events != events: @@ -154,11 +153,6 @@ elif defined(linux): raiseOSError(err) proc select*(s: var Selector, timeout: int): seq[ReadyInfo] = - ## - ## The ``events`` field of the returned ``key`` contains the original events - ## for which the ``fd`` was bound. This is contrary to the ``events`` field - ## of the ``TReadyInfo`` tuple which determines which events are ready - ## on the ``fd``. result = @[] let evNum = epoll_wait(s.epollFD, addr s.events[0], 64.cint, timeout.cint) if evNum < 0: @@ -204,6 +198,86 @@ elif defined(linux): ## Retrieves the selector key for ``fd``. return s.fds[fd] +elif defined(macosx) or defined(freebsd) or defined(openbsd) or defined(netbsd): + type + Selector* = object + kqFD: cint + events: array[64, KEvent] + when MultiThreaded: + fds: SharedTable[SocketHandle, SelectorKey] + else: + fds: Table[SocketHandle, SelectorKey] + + template modifyKQueue(kqFD: cint, fd: SocketHandle, event: Event, + op: cushort) = + var kev = KEvent(ident: fd.cuint, + filter: if event == EvRead: EVFILT_READ else: EVFILT_WRITE, + flags: op) + if kevent(kqFD, addr kev, 1, nil, 0, nil) == -1: + raiseOSError(osLastError()) + + proc register*(s: var Selector, fd: SocketHandle, events: set[Event], + data: SelectorData) = + for event in events: + modifyKQueue(s.kqFD, fd, event, EV_ADD) + s.fds[fd] = SelectorKey(fd: fd, events: events, data: data) + + proc update*(s: var Selector, fd: SocketHandle, events: set[Event]) = + let previousEvents = s.fds[fd].events + if previousEvents != events: + for event in events-previousEvents: + modifyKQueue(s.kqFD, fd, event, EV_ADD) + for event in previousEvents-events: + modifyKQueue(s.kqFD, fd, event, EV_DELETE) + s.fds.mget(fd).events = events + + proc unregister*(s: var Selector, fd: SocketHandle) = + for event in s.fds[fd].events: + modifyKQueue(s.kqFD, fd, event, EV_DELETE) + s.fds.del(fd) + + proc close*(s: var Selector) = + when MultiThreaded: deinitSharedTable(s.fds) + if s.kqFD.close() != 0: raiseOSError(osLastError()) + + proc select*(s: var Selector, timeout: int): seq[ReadyInfo] = + result = @[] + var tv = Timespec(tv_sec: timeout.Time, tv_nsec: 0) + let evNum = kevent(s.kqFD, nil, 0, addr s.events[0], 64.cint, addr tv) + if evNum < 0: + let err = osLastError() + if err.cint == EINTR: + return @[] + raiseOSError(err) + if evNum == 0: return @[] + for i in 0 .. <evNum: + let fd = s.events[i].ident.SocketHandle + + var evSet: set[Event] = {} + if (s.events[i].flags and EV_EOF) != 0: evSet = evSet + {EvError} + if s.events[i].filter == EVFILT_READ: evSet = evSet + {EvRead} + elif s.events[i].filter == EVFILT_WRITE: evSet = evSet + {EvWrite} + let selectorKey = s.fds[fd] + assert selectorKey.fd != 0.SocketHandle + result.add((selectorKey, evSet)) + + proc newSelector*(): Selector = + result.kqFD = kqueue() + if result.kqFD < 0: + raiseOSError(osLastError()) + when MultiThreaded: + result.fds = initSharedTable[SocketHandle, SelectorKey]() + else: + result.fds = initTable[SocketHandle, SelectorKey]() + + proc contains*(s: Selector, fd: SocketHandle): bool = + ## Determines whether selector contains a file descriptor. + s.fds.hasKey(fd) # and s.fds[fd].events != {} + + proc `[]`*(s: Selector, fd: SocketHandle): SelectorKey = + ## Retrieves the selector key for ``fd``. + return s.fds[fd] + elif not defined(nimdoc): # TODO: kqueue for bsd/mac os x. type diff --git a/lib/pure/streams.nim b/lib/pure/streams.nim index 8aa8d35d8..38e91fee4 100644 --- a/lib/pure/streams.nim +++ b/lib/pure/streams.nim @@ -11,6 +11,26 @@ ## the `FileStream` and the `StringStream` which implement the stream ## interface for Nim file objects (`File`) and strings. Other modules ## may provide other implementations for this standard stream interface. +## +## Examples: +## +## .. code-block:: Nim +## +## import streams +## var +## ss = newStringStream("""The first line +## the second line +## the third line""") +## line = "" +## while ss.readLine(line): +## echo line +## ss.close() +## +## var fs = newFileStream("somefile.txt", fmRead) +## if not isNil(fs): +## while fs.readLine(line): +## echo line +## fs.close() include "system/inclrtl" @@ -81,6 +101,19 @@ proc readData*(s: Stream, buffer: pointer, bufLen: int): int = ## low level proc that reads data into an untyped `buffer` of `bufLen` size. result = s.readDataImpl(s, buffer, bufLen) +proc readAll*(s: Stream): string = + ## Reads all available data. + const bufferSize = 1000 + result = newString(bufferSize) + var r = 0 + while true: + let readBytes = readData(s, addr(result[r]), bufferSize) + if readBytes < bufferSize: + setLen(result, r+readBytes) + break + inc r, bufferSize + setLen(result, r+bufferSize) + proc readData*(s, unused: Stream, buffer: pointer, bufLen: int): int {.deprecated.} = ## low level proc that reads data into an untyped `buffer` of `bufLen` size. @@ -371,7 +404,7 @@ when not defined(js): result.writeDataImpl = fsWriteData result.flushImpl = fsFlush - proc newFileStream*(filename: string, mode: FileMode): FileStream = + proc newFileStream*(filename: string, mode: FileMode = fmRead): FileStream = ## creates a new stream from the file named `filename` with the mode `mode`. ## If the file cannot be opened, nil is returned. See the `system ## <system.html>`_ module for a list of available FileMode enums. diff --git a/lib/pure/strtabs.nim b/lib/pure/strtabs.nim index 86f81aa43..1ce9067a7 100644 --- a/lib/pure/strtabs.nim +++ b/lib/pure/strtabs.nim @@ -173,6 +173,9 @@ proc clear*(s: StringTableRef, mode: StringTableMode) = s.mode = mode s.counter = 0 s.data.setLen(startSize) + for i in 0..<s.data.len: + if not isNil(s.data[i].key): + s.data[i].key = nil proc newStringTable*(keyValuePairs: varargs[string], mode: StringTableMode): StringTableRef {. @@ -248,3 +251,6 @@ when isMainModule: x.mget("11") = "23" assert x["11"] == "23" + x.clear(modeCaseInsensitive) + x["11"] = "22" + assert x["11"] == "22" diff --git a/lib/pure/strutils.nim b/lib/pure/strutils.nim index ae3bd7f63..a78fed4b9 100644 --- a/lib/pure/strutils.nim +++ b/lib/pure/strutils.nim @@ -60,6 +60,132 @@ const ## doAssert "01234".find(invalid) == -1 ## doAssert "01A34".find(invalid) == 2 +proc isAlpha*(c: char): bool {.noSideEffect, procvar, + rtl, extern: "nsuIsAlphaChar".}= + ## Checks whether or not `c` is alphabetical. + ## + ## This checks a-z, A-Z ASCII characters only. + return c in Letters + +proc isAlphaNumeric*(c: char): bool {.noSideEffect, procvar, + rtl, extern: "nsuIsAlphaNumericChar".}= + ## Checks whether or not `c` is alphanumeric. + ## + ## This checks a-z, A-Z, 0-9 ASCII characters only. + return c in Letters or c in Digits + +proc isDigit*(c: char): bool {.noSideEffect, procvar, + rtl, extern: "nsuIsDigitChar".}= + ## Checks whether or not `c` is a number. + ## + ## This checks 0-9 ASCII characters only. + return c in Digits + +proc isSpace*(c: char): bool {.noSideEffect, procvar, + rtl, extern: "nsuIsSpaceChar".}= + ## Checks whether or not `c` is a whitespace character. + return c in Whitespace + +proc isLower*(c: char): bool {.noSideEffect, procvar, + rtl, extern: "nsuIsLowerChar".}= + ## Checks whether or not `c` is a lower case character. + ## + ## This checks ASCII characters only. + return c in {'a'..'z'} + +proc isUpper*(c: char): bool {.noSideEffect, procvar, + rtl, extern: "nsuIsUpperChar".}= + ## Checks whether or not `c` is an upper case character. + ## + ## This checks ASCII characters only. + return c in {'A'..'Z'} + +proc isAlpha*(s: string): bool {.noSideEffect, procvar, + rtl, extern: "nsuIsAlphaStr".}= + ## Checks whether or not `s` is alphabetical. + ## + ## This checks a-z, A-Z ASCII characters only. + ## Returns true if all characters in `s` are + ## alphabetic and there is at least one character + ## in `s`. + if s.len() == 0: + return false + + result = true + for c in s: + result = c.isAlpha() and result + +proc isAlphaNumeric*(s: string): bool {.noSideEffect, procvar, + rtl, extern: "nsuIsAlphaNumericStr".}= + ## Checks whether or not `s` is alphanumeric. + ## + ## This checks a-z, A-Z, 0-9 ASCII characters only. + ## Returns true if all characters in `s` are + ## alpanumeric and there is at least one character + ## in `s`. + if s.len() == 0: + return false + + result = true + for c in s: + result = c.isAlphaNumeric() and result + +proc isDigit*(s: string): bool {.noSideEffect, procvar, + rtl, extern: "nsuIsDigitStr".}= + ## Checks whether or not `s` is a numeric value. + ## + ## This checks 0-9 ASCII characters only. + ## Returns true if all characters in `s` are + ## numeric and there is at least one character + ## in `s`. + if s.len() == 0: + return false + + result = true + for c in s: + result = c.isDigit() and result + +proc isSpace*(s: string): bool {.noSideEffect, procvar, + rtl, extern: "nsuIsSpaceStr".}= + ## Checks whether or not `s` is completely whitespace. + ## + ## Returns true if all characters in `s` are whitespace + ## characters and there is at least one character in `s`. + if s.len() == 0: + return false + + result = true + for c in s: + result = c.isSpace() and result + +proc isLower*(s: string): bool {.noSideEffect, procvar, + rtl, extern: "nsuIsLowerStr".}= + ## Checks whether or not `s` contains all lower case characters. + ## + ## This checks ASCII characters only. + ## Returns true if all characters in `s` are lower case + ## and there is at least one character in `s`. + if s.len() == 0: + return false + + result = true + for c in s: + result = c.isLower() and result + +proc isUpper*(s: string): bool {.noSideEffect, procvar, + rtl, extern: "nsuIsUpperStr".}= + ## Checks whether or not `s` contains all upper case characters. + ## + ## This checks ASCII characters only. + ## Returns true if all characters in `s` are upper case + ## and there is at least one character in `s`. + if s.len() == 0: + return false + + result = true + for c in s: + result = c.isUpper() and result + proc toLower*(c: char): char {.noSideEffect, procvar, rtl, extern: "nsuToLowerChar".} = ## Converts `c` into lower case. @@ -169,7 +295,8 @@ proc cmpIgnoreStyle*(a, b: string): int {.noSideEffect, inc(j) -proc strip*(s: string, leading = true, trailing = true, chars: set[char] = Whitespace): string +proc strip*(s: string, leading = true, trailing = true, + chars: set[char] = Whitespace): string {.noSideEffect, rtl, extern: "nsuStrip".} = ## Strips `chars` from `s` and returns the resulting string. ## @@ -504,7 +631,8 @@ proc repeat*(c: char, count: Natural): string {.noSideEffect, ## ## .. code-block:: nim ## proc tabexpand(indent: int, text: string, tabsize: int = 4) = - ## echo '\t'.repeat(indent div tabsize), ' '.repeat(indent mod tabsize), text + ## echo '\t'.repeat(indent div tabsize), ' '.repeat(indent mod tabsize), + ## text ## ## tabexpand(4, "At four") ## tabexpand(5, "At five") @@ -533,11 +661,13 @@ template spaces*(n: Natural): string = repeat(' ',n) ## echo text1 & spaces(max(0, width - text1.len)) & "|" ## echo text2 & spaces(max(0, width - text2.len)) & "|" -proc repeatChar*(count: Natural, c: char = ' '): string {.deprecated.} = repeat(c, count) +proc repeatChar*(count: Natural, c: char = ' '): string {.deprecated.} = ## deprecated: use repeat() or spaces() + repeat(c, count) -proc repeatStr*(count: Natural, s: string): string {.deprecated.} = repeat(s, count) +proc repeatStr*(count: Natural, s: string): string {.deprecated.} = ## deprecated: use repeat(string, count) or string.repeat(count) + repeat(s, count) proc align*(s: string, count: Natural, padding = ' '): string {. noSideEffect, rtl, extern: "nsuAlignString".} = @@ -630,6 +760,22 @@ proc wordWrap*(s: string, maxLineWidth = 80, result.add(lastSep & word) lastSep.setLen(0) +proc indent*(s: string, count: Natural, padding: string = " "): string + {.noSideEffect, rtl, extern: "nsuIndent".} = + ## Indents each line in ``s`` by ``count`` amount of ``padding``. + ## + ## **Note:** This currently does not preserve the specific new line characters + ## used. + result = "" + var i = 0 + for line in s.splitLines(): + if i != 0: + result.add("\n") + for j in 1..count: + result.add(padding) + result.add(line) + i.inc + proc unindent*(s: string, eatAllIndent = false): string {. noSideEffect, rtl, extern: "nsuUnindent".} = ## Unindents `s`. @@ -834,8 +980,8 @@ proc rfind*(s: string, sub: char, start: int = -1): int {.noSideEffect, if sub == s[i]: return i return -1 -proc count*(s: string, sub: string, overlapping: bool = false): int {.noSideEffect, - rtl, extern: "nsuCountString".} = +proc count*(s: string, sub: string, overlapping: bool = false): int {. + noSideEffect, rtl, extern: "nsuCountString".} = ## Count the occurrences of a substring `sub` in the string `s`. ## Overlapping occurrences of `sub` only count when `overlapping` ## is set to true. @@ -1433,7 +1579,8 @@ proc removeSuffix*(s: var string, chars: set[char] = Newlines) {. s.setLen(last + 1) -proc removeSuffix*(s: var string, c: char) {.rtl, extern: "nsuRemoveSuffixChar".} = +proc removeSuffix*(s: var string, c: char) {. + rtl, extern: "nsuRemoveSuffixChar".} = ## Removes a single character (in-place) from a string. ## .. code-block:: nim ## var @@ -1499,6 +1646,61 @@ when isMainModule: doAssert strip("sfoofoofoos", chars = {'s'}) == "foofoofoo" doAssert strip("barfoofoofoobar", chars = {'b', 'a', 'r'}) == "foofoofoo" doAssert strip("stripme but don't strip this stripme", - chars = {'s', 't', 'r', 'i', 'p', 'm', 'e'}) == " but don't strip this " + chars = {'s', 't', 'r', 'i', 'p', 'm', 'e'}) == + " but don't strip this " doAssert strip("sfoofoofoos", leading = false, chars = {'s'}) == "sfoofoofoo" doAssert strip("sfoofoofoos", trailing = false, chars = {'s'}) == "foofoofoos" + + doAssert " foo\n bar".indent(4, "Q") == "QQQQ foo\nQQQQ bar" + + doAssert isAlpha('r') + doAssert isAlpha('A') + doAssert(not isAlpha('$')) + + doAssert isAlpha("Rasp") + doAssert isAlpha("Args") + doAssert(not isAlpha("$Tomato")) + + doAssert isAlphaNumeric('3') + doAssert isAlphaNumeric('R') + doAssert(not isAlphaNumeric('!')) + + doAssert isAlphaNumeric("34ABc") + doAssert isAlphaNumeric("Rad") + doAssert isAlphaNumeric("1234") + doAssert(not isAlphaNumeric("@nose")) + + doAssert isDigit('3') + doAssert(not isDigit('a')) + doAssert(not isDigit('%')) + + doAssert isDigit("12533") + doAssert(not isDigit("12.33")) + doAssert(not isDigit("A45b")) + + doAssert isSpace('\t') + doAssert isSpace('\l') + doAssert(not isSpace('A')) + + doAssert isSpace("\t\l \v\r\f") + doAssert isSpace(" ") + doAssert(not isSpace("ABc \td")) + + doAssert isLower('a') + doAssert isLower('z') + doAssert(not isLower('A')) + doAssert(not isLower('5')) + doAssert(not isLower('&')) + + doAssert isLower("abcd") + doAssert(not isLower("abCD")) + doAssert(not isLower("33aa")) + + doAssert isUpper('A') + doAssert(not isUpper('b')) + doAssert(not isUpper('5')) + doAssert(not isUpper('%')) + + doAssert isUpper("ABC") + doAssert(not isUpper("AAcc")) + doAssert(not isUpper("A#$")) diff --git a/lib/pure/subexes.nim b/lib/pure/subexes.nim index 2d1adc0eb..5824ace81 100644 --- a/lib/pure/subexes.nim +++ b/lib/pure/subexes.nim @@ -351,6 +351,7 @@ proc format*(formatstr: Subex, a: varargs[string, `$`]): string {.noSideEffect, {.pop.} when isMainModule: + from strutils import replace proc `%`(formatstr: string, a: openarray[string]): string = result = newStringOfCap(formatstr.len + a.len shl 4) @@ -382,18 +383,18 @@ when isMainModule: doAssert "${$1}" % "1" == "1" doAssert "${$$-1} $$1" % "1" == "1 $1" - doAssert "$#($', '10c'\n '{#..})" % ["doAssert", "longishA", "longish"] == + doAssert(("$#($', '10c'\n '{#..})" % ["doAssert", "longishA", "longish"]).replace(" \n", "\n") == """doAssert( longishA, - longish)""" + longish)""") - assert "type MyEnum* = enum\n $', '2i'\n '{..}" % ["fieldA", - "fieldB", "FiledClkad", "fieldD", "fieldE", "longishFieldName"] == + doAssert(("type MyEnum* = enum\n $', '2i'\n '{..}" % ["fieldA", + "fieldB", "FiledClkad", "fieldD", "fieldE", "longishFieldName"]).replace(" \n", "\n") == strutils.unindent """ type MyEnum* = enum fieldA, fieldB, FiledClkad, fieldD, - fieldE, longishFieldName""" + fieldE, longishFieldName""") doAssert subex"$1($', '{2..})" % ["f", "a", "b", "c"] == "f(a, b, c)" @@ -401,12 +402,10 @@ when isMainModule: doAssert subex"$['''|'|''''|']']#" % "0" == "'|" - assert subex("type\n Enum = enum\n $', '40c'\n '{..}") % [ - "fieldNameA", "fieldNameB", "fieldNameC", "fieldNameD"] == + doAssert((subex("type\n Enum = enum\n $', '40c'\n '{..}") % [ + "fieldNameA", "fieldNameB", "fieldNameC", "fieldNameD"]).replace(" \n", "\n") == strutils.unindent """ type Enum = enum fieldNameA, fieldNameB, fieldNameC, - fieldNameD""" - - + fieldNameD""") diff --git a/lib/pure/times.nim b/lib/pure/times.nim index aa4ae5ace..3142952e7 100644 --- a/lib/pure/times.nim +++ b/lib/pure/times.nim @@ -11,6 +11,26 @@ ## This module contains routines and types for dealing with time. ## This module is available for the `JavaScript target ## <backends.html#the-javascript-target>`_. +## +## Examples: +## +## .. code-block:: nim +## +## import times, os +## var +## t = cpuTime() +## +## sleep(100) # replace this with something to be timed +## echo "Time taken: ",cpuTime() - t +## +## echo "My formatted time: ", format(getLocalTime(getTime()), "d MMMM yyyy HH:mm") +## echo "Using predefined formats: ", getClockStr(), " ", getDateStr() +## +## echo "epochTime() float value: ", epochTime() +## echo "getTime() float value: ", toSeconds(getTime()) +## echo "cpuTime() float value: ", cpuTime() +## echo "An hour from now : ", getLocalTime(getTime()) + initInterval(0,0,0,1) +## echo "An hour from (UTC) now: ", getGmTime(getTime()) + initInterval(0,0,0,1) {.push debugger:off.} # the user does not want to trace a part # of the standard library! @@ -288,10 +308,10 @@ proc `+`*(a: TimeInfo, interval: TimeInterval): TimeInfo = ## very accurate. let t = toSeconds(timeInfoToTime(a)) let secs = toSeconds(a, interval) - if a.tzname == "UTC": - result = getGMTime(fromSeconds(t + secs)) - else: - result = getLocalTime(fromSeconds(t + secs)) + #if a.tzname == "UTC": + # result = getGMTime(fromSeconds(t + secs)) + #else: + result = getLocalTime(fromSeconds(t + secs)) proc `-`*(a: TimeInfo, interval: TimeInterval): TimeInfo = ## subtracts ``interval`` time. @@ -300,10 +320,10 @@ proc `-`*(a: TimeInfo, interval: TimeInterval): TimeInfo = ## when you subtract so much that you reach the Julian calendar. let t = toSeconds(timeInfoToTime(a)) let secs = toSeconds(a, interval) - if a.tzname == "UTC": - result = getGMTime(fromSeconds(t - secs)) - else: - result = getLocalTime(fromSeconds(t - secs)) + #if a.tzname == "UTC": + # result = getGMTime(fromSeconds(t - secs)) + #else: + result = getLocalTime(fromSeconds(t - secs)) when not defined(JS): proc epochTime*(): float {.rtl, extern: "nt$1", tags: [TimeEffect].} @@ -1269,3 +1289,28 @@ when isMainModule: assert getDayOfWeekJulian(21, 9, 1970) == dMon assert getDayOfWeekJulian(1, 1, 2000) == dSat assert getDayOfWeekJulian(1, 1, 2021) == dFri + + # toSeconds tests with GM and Local timezones + #var t4 = getGMTime(fromSeconds(876124714)) # Mon 6 Oct 08:58:34 BST 1997 + var t4L = getLocalTime(fromSeconds(876124714)) + assert toSeconds(timeInfoToTime(t4L)) == 876124714 # fromSeconds is effectively "localTime" + assert toSeconds(timeInfoToTime(t4L)) + t4L.timezone.float == toSeconds(timeInfoToTime(t4)) + + assert toSeconds(t4, initInterval(seconds=0)) == 0.0 + assert toSeconds(t4L, initInterval(milliseconds=1)) == toSeconds(t4, initInterval(milliseconds=1)) + assert toSeconds(t4L, initInterval(seconds=1)) == toSeconds(t4, initInterval(seconds=1)) + assert toSeconds(t4L, initInterval(minutes=1)) == toSeconds(t4, initInterval(minutes=1)) + assert toSeconds(t4L, initInterval(hours=1)) == toSeconds(t4, initInterval(hours=1)) + assert toSeconds(t4L, initInterval(days=1)) == toSeconds(t4, initInterval(days=1)) + assert toSeconds(t4L, initInterval(months=1)) == toSeconds(t4, initInterval(months=1)) + assert toSeconds(t4L, initInterval(years=1)) == toSeconds(t4, initInterval(years=1)) + + # adding intervals + var + a1L = toSeconds(timeInfoToTime(t4L + initInterval(hours = 1))) + t4L.timezone.float + a1G = toSeconds(timeInfoToTime(t4)) + 60.0 * 60.0 + assert a1L == a1G + # subtracting intervals + a1L = toSeconds(timeInfoToTime(t4L - initInterval(hours = 1))) + t4L.timezone.float + a1G = toSeconds(timeInfoToTime(t4)) - (60.0 * 60.0) + assert a1L == a1G diff --git a/lib/pure/unicode.nim b/lib/pure/unicode.nim index 396957f6c..b059a7315 100644 --- a/lib/pure/unicode.nim +++ b/lib/pure/unicode.nim @@ -1319,15 +1319,43 @@ proc reversed*(s: string): string = reverseUntil(len(s)) +proc graphemeLen*(s: string; i: Natural): Natural = + ## The number of bytes belonging to 's[i]' including following combining + ## characters. + var j = i.int + var r, r2: Rune + if j < s.len: + fastRuneAt(s, j, r, true) + result = j-i + while j < s.len: + fastRuneAt(s, j, r2, true) + if not isCombining(r2): break + result = j-i + +proc lastRune*(s: string; last: int): (Rune, int) = + ## length of the last rune in 's[0..last]'. Returns the rune and its length + ## in bytes. + if s[last] <= chr(127): + result = (Rune(s[last]), 1) + else: + var L = 0 + while last-L >= 0 and ord(s[last-L]) shr 6 == 0b10: inc(L) + var r: Rune + fastRuneAt(s, last-L, r, false) + result = (r, L+1) + when isMainModule: let someString = "öÑ" someRunes = @[runeAt(someString, 0), runeAt(someString, 2)] compared = (someString == $someRunes) - assert compared == true + doAssert compared == true - assert reversed("Reverse this!") == "!siht esreveR" - assert reversed("先秦兩漢") == "漢兩秦先" - assert reversed("as⃝df̅") == "f̅ds⃝a" - assert reversed("a⃞b⃞c⃞") == "c⃞b⃞a⃞" - assert len(toRunes("as⃝df̅")) == runeLen("as⃝df̅") + doAssert reversed("Reverse this!") == "!siht esreveR" + doAssert reversed("先秦兩漢") == "漢兩秦先" + doAssert reversed("as⃝df̅") == "f̅ds⃝a" + doAssert reversed("a⃞b⃞c⃞") == "c⃞b⃞a⃞" + doAssert len(toRunes("as⃝df̅")) == runeLen("as⃝df̅") + const test = "as⃝" + doAssert lastRune(test, test.len-1)[1] == 3 + doAssert graphemeLen("è", 0) == 2 diff --git a/lib/pure/uri.nim b/lib/pure/uri.nim index 492de3b46..abb1a462d 100644 --- a/lib/pure/uri.nim +++ b/lib/pure/uri.nim @@ -142,6 +142,7 @@ proc parseUri*(uri: string): Uri = parseUri(uri, result) proc removeDotSegments(path: string): string = + if path.len == 0: return "" var collection: seq[string] = @[] let endsWithSlash = path[path.len-1] == '/' var i = 0 @@ -432,3 +433,12 @@ when isMainModule: block: let test = parseUri("http://example.com/foo/") / "/bar/asd" doAssert test.path == "/foo/bar/asd" + + # removeDotSegments tests + block: + # empty test + doAssert removeDotSegments("") == "" + + # bug #3207 + block: + doAssert parseUri("http://qq/1").combine(parseUri("https://qqq")).`$` == "https://qqq" diff --git a/lib/pure/xmltree.nim b/lib/pure/xmltree.nim index 1c8573986..7c97a0a56 100644 --- a/lib/pure/xmltree.nim +++ b/lib/pure/xmltree.nim @@ -104,10 +104,23 @@ proc tag*(n: XmlNode): string {.inline.} = assert n.k == xnElement result = n.fTag +proc `tag=`*(n: XmlNode, tag: string) {.inline.} = + ## sets the tag name of `n`. `n` has to be an ``xnElement`` node. + assert n.k == xnElement + n.fTag = tag + proc add*(father, son: XmlNode) {.inline.} = ## adds the child `son` to `father`. add(father.s, son) +proc insert*(father, son: XmlNode, index: int) {.inline.} = + ## insert the child `son` to a given position in `father`. + assert father.k == xnElement and son.k == xnElement + if len(father.s) > index: + insert(father.s, son, index) + else: + insert(father.s, son, len(father.s)) + proc len*(n: XmlNode): int {.inline.} = ## returns the number `n`'s children. if n.k == xnElement: result = len(n.s) diff --git a/lib/system.nim b/lib/system.nim index e0bfbe8ea..f7178adcc 100644 --- a/lib/system.nim +++ b/lib/system.nim @@ -78,7 +78,7 @@ type stmt* {.magic: Stmt.} ## meta type to denote a statement (for templates) typedesc* {.magic: TypeDesc.} ## meta type to denote a type description void* {.magic: "VoidType".} ## meta type to denote the absence of any type - auto* = expr ## meta type for automatic type determination + auto* {.magic: Expr.} ## meta type for automatic type determination any* = distinct auto ## meta type for any supported type untyped* {.magic: Expr.} ## meta type to denote an expression that ## is not resolved (for templates) @@ -104,7 +104,7 @@ type SomeNumber* = SomeInteger|SomeReal ## type class matching all number types -proc defined*(x: expr): bool {.magic: "Defined", noSideEffect.} +proc defined*(x: expr): bool {.magic: "Defined", noSideEffect, compileTime.} ## Special compile-time procedure that checks whether `x` is ## defined. ## `x` is an external symbol introduced through the compiler's @@ -125,7 +125,7 @@ when defined(nimalias): TNumber: SomeNumber, TOrdinal: SomeOrdinal].} -proc declared*(x: expr): bool {.magic: "Defined", noSideEffect.} +proc declared*(x: expr): bool {.magic: "Defined", noSideEffect, compileTime.} ## Special compile-time procedure that checks whether `x` is ## declared. `x` has to be an identifier or a qualified identifier. ## This can be used to check whether a library provides a certain @@ -140,11 +140,11 @@ when defined(useNimRtl): {.deadCodeElim: on.} proc definedInScope*(x: expr): bool {. - magic: "DefinedInScope", noSideEffect, deprecated.} + magic: "DefinedInScope", noSideEffect, deprecated, compileTime.} ## **Deprecated since version 0.9.6**: Use ``declaredInScope`` instead. proc declaredInScope*(x: expr): bool {. - magic: "DefinedInScope", noSideEffect.} + magic: "DefinedInScope", noSideEffect, compileTime.} ## Special compile-time procedure that checks whether `x` is ## declared in the current scope. `x` has to be an identifier. @@ -160,7 +160,7 @@ proc unsafeAddr*[T](x: var T): ptr T {.magic: "Addr", noSideEffect.} = ## Cannot be overloaded. discard -proc `type`*(x: expr): typeDesc {.magic: "TypeOf", noSideEffect.} = +proc `type`*(x: expr): typeDesc {.magic: "TypeOf", noSideEffect, compileTime.} = ## Builtin 'type' operator for accessing the type of an expression. ## Cannot be overloaded. discard @@ -221,11 +221,21 @@ proc high*[T](x: T): T {.magic: "High", noSideEffect.} ## the highest possible value of an ordinal value `x`. As a special ## semantic rule, `x` may also be a type identifier. ## ``high(int)`` is Nim's way of writing `INT_MAX`:idx: or `MAX_INT`:idx:. + ## + ## .. code-block:: nim + ## var arr = [1,2,3,4,5,6,7] + ## high(arr) #=> 6 + ## high(2) #=> 9223372036854775807 proc low*[T](x: T): T {.magic: "Low", noSideEffect.} ## returns the lowest possible index of an array, a sequence, a string or ## the lowest possible value of an ordinal value `x`. As a special ## semantic rule, `x` may also be a type identifier. + ## + ## .. code-block:: nim + ## var arr = [1,2,3,4,5,6,7] + ## high(arr) #=> 0 + ## high(2) #=> -9223372036854775808 type range*{.magic: "Range".}[T] ## Generic type to construct range types. @@ -239,6 +249,14 @@ type seq*{.magic: "Seq".}[T] ## Generic type to construct sequences. set*{.magic: "Set".}[T] ## Generic type to construct bit sets. +when defined(nimArrIdx): + # :array|openarray|string|seq|cstring|tuple + proc `[]`*[I: Ordinal;T](a: T; i: I): T {. + noSideEffect, magic: "ArrGet".} + proc `[]=`*[I: Ordinal;T,S](a: T; i: I; + x: S) {.noSideEffect, magic: "ArrPut".} + proc `=`*[T](dest: var T; src: T) {.noSideEffect, magic: "Asgn".} + type Slice*[T] = object ## builtin slice type a*, b*: T ## the bounds @@ -576,6 +594,10 @@ proc sizeof*[T](x: T): int {.magic: "SizeOf", noSideEffect.} ## its usage is discouraged - using ``new`` for the most cases suffices ## that one never needs to know ``x``'s size. As a special semantic rule, ## ``x`` may also be a type identifier (``sizeof(int)`` is valid). + ## + ## .. code-block:: nim + ## sizeof('A') #=> 1 + ## sizeof(2) #=> 8 when defined(nimtypedescfixed): proc sizeof*(x: typedesc): int {.magic: "SizeOf", noSideEffect.} @@ -602,11 +624,21 @@ proc inc*[T: Ordinal|uint|uint64](x: var T, y = 1) {.magic: "Inc", noSideEffect. ## increments the ordinal ``x`` by ``y``. If such a value does not ## exist, ``EOutOfRange`` is raised or a compile time error occurs. This is a ## short notation for: ``x = succ(x, y)``. + ## + ## .. code-block:: nim + ## var i = 2 + ## inc(i) #=> 3 + ## inc(i, 3) #=> 6 proc dec*[T: Ordinal|uint|uint64](x: var T, y = 1) {.magic: "Dec", noSideEffect.} ## decrements the ordinal ``x`` by ``y``. If such a value does not ## exist, ``EOutOfRange`` is raised or a compile time error occurs. This is a ## short notation for: ``x = pred(x, y)``. + ## + ## .. code-block:: nim + ## var i = 2 + ## dec(i) #=> 1 + ## dec(i, 3) #=> -2 proc newSeq*[T](s: var seq[T], len: Natural) {.magic: "NewSeq", noSideEffect.} ## creates a new sequence of type ``seq[T]`` with length ``len``. @@ -651,11 +683,22 @@ proc len*[T](x: seq[T]): int {.magic: "LengthSeq", noSideEffect.} ## returns the length of an array, an openarray, a sequence or a string. ## This is roughly the same as ``high(T)-low(T)+1``, but its resulting type is ## always an int. + ## + ## .. code-block:: nim + ## var arr = [1,1,1,1,1] + ## len(arr) #=> 5 + ## for i in 0..<arr.len: + ## echo arr[i] #=> 1,1,1,1,1 # set routines: proc incl*[T](x: var set[T], y: T) {.magic: "Incl", noSideEffect.} ## includes element ``y`` to the set ``x``. This is the same as ## ``x = x + {y}``, but it might be more efficient. + ## + ## .. code-block:: nim + ## var a = initSet[int](4) + ## a.incl(2) #=> {2} + ## a.incl(3) #=> {2, 3} template incl*[T](s: var set[T], flags: set[T]) = ## includes the set of flags to the set ``x``. @@ -664,6 +707,10 @@ template incl*[T](s: var set[T], flags: set[T]) = proc excl*[T](x: var set[T], y: T) {.magic: "Excl", noSideEffect.} ## excludes element ``y`` to the set ``x``. This is the same as ## ``x = x - {y}``, but it might be more efficient. + ## + ## .. code-block:: nim + ## var b = {2,3,5,6,12,545} + ## b.excl(5) #=> {2,3,6,12,545} template excl*[T](s: var set[T], flags: set[T]) = ## excludes the set of flags to ``x``. @@ -672,12 +719,22 @@ template excl*[T](s: var set[T], flags: set[T]) = proc card*[T](x: set[T]): int {.magic: "Card", noSideEffect.} ## returns the cardinality of the set ``x``, i.e. the number of elements ## in the set. + ## + ## .. code-block:: nim + ## var i = {1,2,3,4} + ## card(i) #=> 4 proc ord*[T](x: T): int {.magic: "Ord", noSideEffect.} ## returns the internal int value of an ordinal value ``x``. + ## + ## .. code-block:: nim + ## ord('A') #=> 65 proc chr*(u: range[0..255]): char {.magic: "Chr", noSideEffect.} ## converts an int in the range 0..255 to a character. + ## + ## .. code-block:: nim + ## chr(65) #=> A # -------------------------------------------------------------------------- # built-in operators @@ -1175,9 +1232,12 @@ const seqShallowFlag = low(int) -let nimvm* {.magic: "Nimvm".}: bool = false +when defined(nimKnowsNimvm): + let nimvm* {.magic: "Nimvm".}: bool = false ## may be used only in "when" expression. ## It is true in Nim VM context and false otherwise +else: + const nimvm*: bool = false proc compileOption*(option: string): bool {. magic: "CompileOption", noSideEffect.} @@ -1196,7 +1256,7 @@ proc compileOption*(option, arg: string): bool {. ## echo "compiled with optimization for size and uses Boehm's GC" const - hasThreadSupport = compileOption("threads") + hasThreadSupport = compileOption("threads") and not defined(nimscript) hasSharedHeap = defined(boehmgc) or defined(gogc) # don't share heaps; every thread has its own taintMode = compileOption("taintmode") @@ -1288,6 +1348,10 @@ proc add *[T](x: var seq[T], y: openArray[T]) {.noSideEffect.} = ## containers should also call their adding proc `add` for consistency. ## Generic code becomes much easier to write if the Nim naming scheme is ## respected. + ## + ## .. code-block:: nim + ## var s: seq[string] = @["test2","test2"] + ## s.add("test") #=> @[test2, test2, test] let xl = x.len setLen(x, xl + y.len) for i in 0..high(y): x[xl+i] = y[i] @@ -1302,31 +1366,66 @@ proc shallowCopy*[T](x: var T, y: T) {.noSideEffect, magic: "ShallowCopy".} proc del*[T](x: var seq[T], i: Natural) {.noSideEffect.} = ## deletes the item at index `i` by putting ``x[high(x)]`` into position `i`. ## This is an O(1) operation. - let xl = x.len - shallowCopy(x[i], x[xl-1]) - setLen(x, xl-1) + ## + ## .. code-block:: nim + ## var i = @[1,2,3,4,5] + ## i.del(2) #=> @[1, 2, 5, 4] + let xl = x.len - 1 + shallowCopy(x[i], x[xl]) + setLen(x, xl) proc delete*[T](x: var seq[T], i: Natural) {.noSideEffect.} = ## deletes the item at index `i` by moving ``x[i+1..]`` by one position. ## This is an O(n) operation. - let xl = x.len - for j in i..xl-2: shallowCopy(x[j], x[j+1]) - setLen(x, xl-1) + ## + ## .. code-block:: nim + ## var i = @[1,2,3,4,5] + ## i.delete(2) #=> @[1, 2, 4, 5] + template defaultImpl = + let xl = x.len + for j in i..xl-2: shallowCopy(x[j], x[j+1]) + setLen(x, xl-1) + + when nimvm: + defaultImpl() + else: + when defined(js): + {.emit: "`x`[`x`_Idx].splice(`i`, 1);".} + else: + defaultImpl() proc insert*[T](x: var seq[T], item: T, i = 0.Natural) {.noSideEffect.} = ## inserts `item` into `x` at position `i`. - let xl = x.len - setLen(x, xl+1) - var j = xl-1 - while j >= i: - shallowCopy(x[j+1], x[j]) - dec(j) + ## + ## .. code-block:: nim + ## var i = @[1,2,3,4,5] + ## i.insert(2,4) #=> @[1, 2, 3, 4, 2, 5] + template defaultImpl = + let xl = x.len + setLen(x, xl+1) + var j = xl-1 + while j >= i: + shallowCopy(x[j+1], x[j]) + dec(j) + when nimvm: + defaultImpl() + else: + when defined(js): + {.emit: "`x`[`x`_Idx].splice(`i`, 0, null);".} + else: + defaultImpl() x[i] = item proc repr*[T](x: T): string {.magic: "Repr", noSideEffect.} ## takes any Nim variable and returns its string representation. It ## works even for complex data graphs with cycles. This is a great ## debugging tool. + ## + ## .. code-block:: nim + ## var s: seq[string] = @["test2","test2"] + ## var i = @[1,2,3,4,5] + ## repr(s) #=> 0x1055eb050[0x1055ec050"test2", 0x1055ec078"test2"] + ## repr(i) #=> 0x1055ed050[1, 2, 3, 4, 5] type ByteAddress* = int @@ -2216,7 +2315,9 @@ proc `$`*[T: tuple|object](x: T): string = firstElement = false result.add(")") -proc collectionToString[T](x: T, b, e: string): string = +proc collectionToString[T: set | seq](x: T, b, e: string): string = + when x is seq: + if x.isNil: return "nil" result = b var firstElement = true for value in items(x): @@ -3373,7 +3474,7 @@ when hasAlloc: x[j+i] = item[j] inc(j) -proc compiles*(x: expr): bool {.magic: "Compiles", noSideEffect.} = +proc compiles*(x: expr): bool {.magic: "Compiles", noSideEffect, compileTime.} = ## Special compile-time procedure that checks whether `x` can be compiled ## without any semantic error. ## This can be used to check whether a type supports some operation: @@ -3437,7 +3538,7 @@ when hasAlloc and not defined(nimscript) and not defined(JS): include "system/deepcopy" -proc procCall*(x: expr) {.magic: "ProcCall".} = +proc procCall*(x: expr) {.magic: "ProcCall", compileTime.} = ## special magic to prohibit dynamic binding for `method`:idx: calls. ## This is similar to `super`:idx: in ordinary OO languages. ## @@ -3446,6 +3547,7 @@ proc procCall*(x: expr) {.magic: "ProcCall".} = ## procCall someMethod(a, b) discard +proc `^`*[T](x: int; y: openArray[T]): int {.noSideEffect, magic: "Roof".} proc `^`*(x: int): int {.noSideEffect, magic: "Roof".} = ## builtin `roof`:idx: operator that can be used for convenient array access. ## ``a[^x]`` is rewritten to ``a[a.len-x]``. However currently the ``a`` diff --git a/lib/system/alloc.nim b/lib/system/alloc.nim index 13a10e46f..3ebbc8c1e 100644 --- a/lib/system/alloc.nim +++ b/lib/system/alloc.nim @@ -27,10 +27,60 @@ sysAssert(roundup(65, 8) == 72, "roundup broken 2") # some platforms have really weird unmap behaviour: unmap(blockStart, PageSize) # really frees the whole block. Happens for Linux/PowerPC for example. Amd64 # and x86 are safe though; Windows is special because MEM_RELEASE can only be -# used with a size of 0: -const weirdUnmap = not (defined(amd64) or defined(i386)) or defined(windows) +# used with a size of 0. We also allow unmapping to be turned off with +# -d:nimAllocNoUnmap: +const doNotUnmap = not (defined(amd64) or defined(i386)) or + defined(windows) or defined(nimAllocNoUnmap) -when defined(posix): + +when defined(emscripten): + const + PROT_READ = 1 # page can be read + PROT_WRITE = 2 # page can be written + MAP_PRIVATE = 2'i32 # Changes are private + + var MAP_ANONYMOUS {.importc: "MAP_ANONYMOUS", header: "<sys/mman.h>".}: cint + type + PEmscriptenMMapBlock = ptr EmscriptenMMapBlock + EmscriptenMMapBlock {.pure, inheritable.} = object + realSize: int # size of previous chunk; for coalescing + realPointer: pointer # if < PageSize it is a small chunk + + proc mmap(adr: pointer, len: int, prot, flags, fildes: cint, + off: int): pointer {.header: "<sys/mman.h>".} + + proc munmap(adr: pointer, len: int) {.header: "<sys/mman.h>".} + + proc osAllocPages(block_size: int): pointer {.inline.} = + let realSize = block_size + sizeof(EmscriptenMMapBlock) + PageSize + 1 + result = mmap(nil, realSize, PROT_READ or PROT_WRITE, + MAP_PRIVATE or MAP_ANONYMOUS, -1, 0) + if result == nil or result == cast[pointer](-1): + raiseOutOfMem() + + let realPointer = result + let pos = cast[int](result) + + # Convert pointer to PageSize correct one. + var new_pos = cast[ByteAddress](pos) +% (PageSize - (pos %% PageSize)) + if (new_pos-pos)< sizeof(EmscriptenMMapBlock): + new_pos = new_pos +% PageSize + result = cast[pointer](new_pos) + + var mmapDescrPos = cast[ByteAddress](result) -% sizeof(EmscriptenMMapBlock) + + var mmapDescr = cast[EmscriptenMMapBlock](mmapDescrPos) + mmapDescr.realSize = realSize + mmapDescr.realPointer = realPointer + + c_fprintf(c_stdout, "[Alloc] size %d %d realSize:%d realPos:%d\n", block_size, cast[int](result), realSize, cast[int](realPointer)) + + proc osDeallocPages(p: pointer, size: int) {.inline} = + var mmapDescrPos = cast[ByteAddress](p) -% sizeof(EmscriptenMMapBlock) + var mmapDescr = cast[EmscriptenMMapBlock](mmapDescrPos) + munmap(mmapDescr.realPointer, mmapDescr.realSize) + +elif defined(posix): const PROT_READ = 1 # page can be read PROT_WRITE = 2 # page can be written @@ -478,7 +528,7 @@ proc freeBigChunk(a: var MemRegion, c: PBigChunk) = excl(a.chunkStarts, pageIndex(c)) c = cast[PBigChunk](le) - if c.size < ChunkOsReturn or weirdUnmap: + if c.size < ChunkOsReturn or doNotUnmap: incl(a, a.chunkStarts, pageIndex(c)) updatePrevSize(a, c, c.size) listAdd(a.freeChunksList, c) @@ -762,7 +812,7 @@ proc deallocOsPages(a: var MemRegion) = # we free every 'ordinarily' allocated page by iterating over the page bits: for p in elements(a.chunkStarts): var page = cast[PChunk](p shl PageShift) - when not weirdUnmap: + when not doNotUnmap: var size = if page.size < PageSize: PageSize else: page.size osDeallocPages(page, size) else: diff --git a/lib/system/gc_common.nim b/lib/system/gc_common.nim index ceb362378..47e8b4b1f 100644 --- a/lib/system/gc_common.nim +++ b/lib/system/gc_common.nim @@ -95,7 +95,9 @@ proc setupForeignThreadGc*() = # ----------------- stack management -------------------------------------- # inspired from Smart Eiffel -when defined(sparc): +when defined(emscripten): + const stackIncreases = true +elif defined(sparc): const stackIncreases = false elif defined(hppa) or defined(hp9000) or defined(hp9000s300) or defined(hp9000s700) or defined(hp9000s800) or defined(hp9000s820): @@ -162,9 +164,9 @@ elif stackIncreases: proc isOnStack(p: pointer): bool = var stackTop {.volatile.}: pointer stackTop = addr(stackTop) - var a = cast[TAddress](gch.stackBottom) - var b = cast[TAddress](stackTop) - var x = cast[TAddress](p) + var a = cast[ByteAddress](gch.stackBottom) + var b = cast[ByteAddress](stackTop) + var x = cast[ByteAddress](p) result = a <=% x and x <=% b var @@ -173,14 +175,14 @@ elif stackIncreases: # in a platform independent way template forEachStackSlot(gch, gcMark: expr) {.immediate, dirty.} = - var registers: C_JmpBuf + var registers {.noinit.}: C_JmpBuf if c_setjmp(registers) == 0'i32: # To fill the C stack with registers. - var max = cast[TAddress](gch.stackBottom) - var sp = cast[TAddress](addr(registers)) +% jmpbufSize -% sizeof(pointer) + var max = cast[ByteAddress](gch.stackBottom) + var sp = cast[ByteAddress](addr(registers)) +% jmpbufSize -% sizeof(pointer) # sp will traverse the JMP_BUF as well (jmp_buf size is added, # otherwise sp would be below the registers structure). while sp >=% max: - gcMark(gch, cast[ppointer](sp)[]) + gcMark(gch, cast[PPointer](sp)[]) sp = sp -% sizeof(pointer) else: diff --git a/lib/system/nimscript.nim b/lib/system/nimscript.nim index 4841749a9..22430348c 100644 --- a/lib/system/nimscript.nim +++ b/lib/system/nimscript.nim @@ -31,7 +31,8 @@ proc moveFile(src, dest: string) {. tags: [ReadIOEffect, WriteIOEffect], raises: [OSError].} = builtin proc copyFile(src, dest: string) {. tags: [ReadIOEffect, WriteIOEffect], raises: [OSError].} = builtin -proc createDir(dir: string) {.tags: [WriteIOEffect], raises: [OSError].} = builtin +proc createDir(dir: string) {.tags: [WriteIOEffect], raises: [OSError].} = + builtin proc getOsError: string = builtin proc setCurrentDir(dir: string) = builtin proc getCurrentDir(): string = builtin @@ -56,7 +57,7 @@ proc getCommand*(): string = ## "c", "js", "build", "help". builtin -proc setCommand*(cmd: string) = +proc setCommand*(cmd: string; project="") = ## Sets the Nim command that should be continued with after this Nimscript ## has finished. builtin diff --git a/lib/system/repr.nim b/lib/system/repr.nim index b4188527f..1f81a0813 100644 --- a/lib/system/repr.nim +++ b/lib/system/repr.nim @@ -21,9 +21,22 @@ proc reprPointer(x: pointer): string {.compilerproc.} = return $buf proc `$`(x: uint64): string = - var buf: array [0..59, char] - discard c_sprintf(buf, "%llu", x) - return $buf + if x == 0: + result = "0" + else: + var buf: array [60, char] + var i = 0 + var n = x + while n != 0: + let nn = n div 10'u64 + buf[i] = char(n - 10'u64 * nn + ord('0')) + inc i + n = nn + + let half = i div 2 + # Reverse + for t in 0 .. < half: swap(buf[t], buf[i-t-1]) + result = $buf proc reprStrAux(result: var string, s: string) = if cast[pointer](s) == nil: @@ -294,4 +307,3 @@ when not defined(useNimRtl): reprAux(result, addr(p), typ, cl) add result, "\n" deinitReprClosure(cl) - diff --git a/lib/windows/winlean.nim b/lib/windows/winlean.nim index 24015dd3a..89d86c62a 100644 --- a/lib/windows/winlean.nim +++ b/lib/windows/winlean.nim @@ -18,7 +18,7 @@ const type Handle* = int LONG* = int32 - ULONG* = int + ULONG* = int32 PULONG* = ptr int WINBOOL* = int32 DWORD* = int32 @@ -108,6 +108,13 @@ const CREATE_UNICODE_ENVIRONMENT* = 1024'i32 + PIPE_ACCESS_DUPLEX* = 0x00000003'i32 + PIPE_ACCESS_INBOUND* = 1'i32 + PIPE_ACCESS_OUTBOUND* = 2'i32 + PIPE_NOWAIT* = 0x00000001'i32 + SYNCHRONIZE* = 0x00100000'i32 + FILE_FLAG_WRITE_THROUGH* = 0x80000000'i32 + proc closeHandle*(hObject: Handle): WINBOOL {.stdcall, dynlib: "kernel32", importc: "CloseHandle".} @@ -125,6 +132,19 @@ proc createPipe*(hReadPipe, hWritePipe: var Handle, nSize: int32): WINBOOL{. stdcall, dynlib: "kernel32", importc: "CreatePipe".} +proc createNamedPipe*(lpName: WideCString, + dwOpenMode, dwPipeMode, nMaxInstances, nOutBufferSize, + nInBufferSize, nDefaultTimeOut: int32, + lpSecurityAttributes: ptr SECURITY_ATTRIBUTES): Handle {. + stdcall, dynlib: "kernel32", importc: "CreateNamedPipeW".} + +proc peekNamedPipe*(hNamedPipe: Handle, lpBuffer: pointer=nil, + nBufferSize: int32 = 0, + lpBytesRead: ptr int32 = nil, + lpTotalBytesAvail: ptr int32 = nil, + lpBytesLeftThisMessage: ptr int32 = nil): bool {. + stdcall, dynlib: "kernel32", importc: "PeekNamedPipe".} + when useWinUnicode: proc createProcessW*(lpApplicationName, lpCommandLine: WideCString, lpProcessAttributes: ptr SECURITY_ATTRIBUTES, @@ -409,7 +429,7 @@ type bytes*: array[0..15, char] Sockaddr_in6* {.importc: "SOCKADDR_IN6", - header: "winsock2.h".} = object + header: "ws2tcpip.h".} = object sin6_family*: int16 sin6_port*: int16 # unsigned sin6_flowinfo*: int32 # unsigned @@ -511,6 +531,9 @@ proc connect*(s: SocketHandle, name: ptr SockAddr, namelen: SockLen): cint {. proc getsockname*(s: SocketHandle, name: ptr SockAddr, namelen: ptr SockLen): cint {. stdcall, importc: "getsockname", dynlib: ws2dll.} +proc getpeername*(s: SocketHandle, name: ptr SockAddr, + namelen: ptr SockLen): cint {. + stdcall, importc, dynlib: ws2dll.} proc getsockopt*(s: SocketHandle, level, optname: cint, optval: pointer, optlen: ptr SockLen): cint {. stdcall, importc: "getsockopt", dynlib: ws2dll.} @@ -572,6 +595,9 @@ proc freeaddrinfo*(ai: ptr AddrInfo) {. proc inet_ntoa*(i: InAddr): cstring {. stdcall, importc, dynlib: ws2dll.} +proc inet_ntop*(family: cint, paddr: pointer, pStringBuffer: cstring, + stringBufSize: int32): cstring {.stdcall, importc, dynlib: ws2dll.} + const MAXIMUM_WAIT_OBJECTS* = 0x00000040 @@ -609,12 +635,24 @@ const FILE_FLAG_BACKUP_SEMANTICS* = 33554432'i32 FILE_FLAG_OPEN_REPARSE_POINT* = 0x00200000'i32 + DUPLICATE_SAME_ACCESS* = 2 + FILE_READ_DATA* = 0x00000001 # file & pipe + FILE_WRITE_DATA* = 0x00000002 # file & pipe # Error Constants const ERROR_ACCESS_DENIED* = 5 ERROR_HANDLE_EOF* = 38 +proc duplicateHandle*(hSourceProcessHandle: HANDLE, hSourceHandle: HANDLE, + hTargetProcessHandle: HANDLE, + lpTargetHandle: ptr HANDLE, + dwDesiredAccess: DWORD, bInheritHandle: WINBOOL, + dwOptions: DWORD): WINBOOL{.stdcall, dynlib: "kernel32", + importc: "DuplicateHandle".} +proc getCurrentProcess*(): HANDLE{.stdcall, dynlib: "kernel32", + importc: "GetCurrentProcess".} + when useWinUnicode: proc createFileW*(lpFileName: WideCString, dwDesiredAccess, dwShareMode: DWORD, lpSecurityAttributes: pointer, diff --git a/readme.md b/readme.md index 6aa562f2a..57fc223d3 100644 --- a/readme.md +++ b/readme.md @@ -1,4 +1,8 @@ # Nim Compiler + +[](https://gitter.im/nim-lang/Nim?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) + + This repo contains the Nim compiler, Nim's stdlib, tools and documentation. @@ -75,11 +79,11 @@ All rights reserved. # Build Status [**Build Waterfall**][waterfall] -| | Linux | Windows | Mac | -| ------ | ----- | ------- | --- | -| x86 | ![linux-x86][linux-x86-img] | ![windows-x86][windows-x86-img] | ![mac-x86][mac-x86-img] | -| x86_64 | ![linux-x86_64][linux-x86_64-img] | ![windows-x86_64][windows-x86_64-img] | ![mac-x86_64][mac-x86_64-img] | -| arm | ![linux-armv5][linux-arm5-img]<br/> ![linux-armv6][linux-arm6-img]<br/> ![linux-armv7][linux-arm7-img] | | | +| | Linux | Windows | Mac | +| ------ | ----- | ------- | --- | +| x86 | ![linux-x86][linux-x86-img] | ![windows-x86][windows-x86-img] | +| x86_64 | ![linux-x86_64][linux-x86_64-img] | ![windows-x86_64][windows-x86_64-img] | ![mac-x86_64][mac-x86_64-img] | +| arm | ![linux-armv5][linux-arm5-img]<br/> ![linux-armv6][linux-arm6-img]<br/> ![linux-armv7][linux-arm7-img] [linux-x86-img]: http://buildbot.nim-lang.org/buildstatusimage?builder=linux-x32-builder [linux-x86_64-img]: http://buildbot.nim-lang.org/buildstatusimage?builder=linux-x64-builder @@ -90,7 +94,6 @@ All rights reserved. [windows-x86-img]: http://buildbot.nim-lang.org/buildstatusimage?builder=windows-x32-builder [windows-x86_64-img]: http://buildbot.nim-lang.org/buildstatusimage?builder=windows-x64-builder -[mac-x86-img]: http://buildbot.nim-lang.org/buildstatusimage?builder=mac-x32-builder [mac-x86_64-img]: http://buildbot.nim-lang.org/buildstatusimage?builder=mac-x64-builder [waterfall]: http://buildbot.nim-lang.org/waterfall diff --git a/tests/alias/talias.nim b/tests/alias/talias.nim index 6addc4704..810ea2095 100644 --- a/tests/alias/talias.nim +++ b/tests/alias/talias.nim @@ -30,7 +30,7 @@ type c: char se: seq[TA] -proc p(param1, param2: TC): TC = +proc p(param1, param2: TC, param3: var TC): TC = var local: TC plocal: ptr TC @@ -43,6 +43,7 @@ proc p(param1, param2: TC): TC = plocal2[] ?<| local param1 ?<| param2 + local ?<| param3 local.arr[0] !<| param1 local.arr !<| param1 @@ -62,5 +63,5 @@ var a <| a a !<| b -discard p(x, x) +discard p(x, x, x) diff --git a/tests/async/tasyncawait.nim b/tests/async/tasyncawait.nim index e5895abe1..443f769cd 100644 --- a/tests/async/tasyncawait.nim +++ b/tests/async/tasyncawait.nim @@ -2,7 +2,7 @@ discard """ file: "tasyncawait.nim" output: "5000" """ -import asyncdispatch, rawsockets, net, strutils, os +import asyncdispatch, nativesockets, net, strutils, os var msgCount = 0 @@ -18,7 +18,7 @@ proc sendMessages(client: TAsyncFD) {.async.} = proc launchSwarm(port: TPort) {.async.} = for i in 0 .. <swarmSize: - var sock = newAsyncRawSocket() + var sock = newAsyncNativeSocket() await connect(sock, "localhost", port) await sendMessages(sock) @@ -38,7 +38,7 @@ proc readMessages(client: TAsyncFD) {.async.} = doAssert false proc createServer(port: TPort) {.async.} = - var server = newAsyncRawSocket() + var server = newAsyncNativeSocket() block: var name: Sockaddr_in when defined(windows): diff --git a/tests/async/tasyncconnect.nim b/tests/async/tasyncconnect.nim index bc63b8e82..3dac379b2 100644 --- a/tests/async/tasyncconnect.nim +++ b/tests/async/tasyncconnect.nim @@ -1,7 +1,7 @@ discard """ file: "tasyncconnect.nim" exitcode: 1 - outputsub: "Error: unhandled exception: Connection refused [Exception]" + outputsub: "Error: unhandled exception: Connection refused" """ import @@ -15,10 +15,11 @@ const when defined(windows) or defined(nimdoc): - discard + # TODO: just make it work on Windows for now. + quit("Error: unhandled exception: Connection refused") else: proc testAsyncConnect() {.async.} = - var s = newAsyncRawSocket() + var s = newAsyncNativeSocket() await s.connect(testHost, testPort) diff --git a/tests/async/tasynceverror.nim b/tests/async/tasynceverror.nim index 5575cfe82..22b4fe9a7 100644 --- a/tests/async/tasynceverror.nim +++ b/tests/async/tasynceverror.nim @@ -1,13 +1,13 @@ discard """ file: "tasynceverror.nim" exitcode: 1 - outputsub: "Error: unhandled exception: Connection reset by peer [Exception]" + outputsub: "Error: unhandled exception: Connection reset by peer" """ import asyncdispatch, asyncnet, - rawsockets, + nativesockets, os @@ -17,10 +17,11 @@ const when defined(windows) or defined(nimdoc): - discard + # TODO: just make it work on Windows for now. + quit("Error: unhandled exception: Connection reset by peer") else: proc createListenSocket(host: string, port: Port): TAsyncFD = - result = newAsyncRawSocket() + result = newAsyncNativeSocket() SocketHandle(result).setSockOptInt(SOL_SOCKET, SO_REUSEADDR, 1) diff --git a/tests/async/tasyncexceptions.nim b/tests/async/tasyncexceptions.nim index c4379f7d8..aab08e30f 100644 --- a/tests/async/tasyncexceptions.nim +++ b/tests/async/tasyncexceptions.nim @@ -1,7 +1,7 @@ discard """ file: "tasyncexceptions.nim" exitcode: 1 - outputsub: "Error: unhandled exception: foobar [Exception]" + outputsub: "Error: unhandled exception: foobar" """ import asyncdispatch diff --git a/tests/bind/tbind2.nim b/tests/bind/tbind2.nim index d2219765d..0e0cbd788 100644 --- a/tests/bind/tbind2.nim +++ b/tests/bind/tbind2.nim @@ -1,6 +1,6 @@ discard """ file: "tbind2.nim" - line: 14 + line: 12 errormsg: "ambiguous call" """ # Test the new ``bind`` keyword for templates diff --git a/tests/ccgbugs/tnocodegen_for_compiletime.nim b/tests/ccgbugs/tnocodegen_for_compiletime.nim new file mode 100644 index 000000000..a88ba4b32 --- /dev/null +++ b/tests/ccgbugs/tnocodegen_for_compiletime.nim @@ -0,0 +1,9 @@ +# bug #1679 +import macros, tables, hashes +proc hash(v: NimNode): Hash = 4 # performance is for suckers +macro test(body: stmt): stmt {.immediate.} = + var a = initCountTable[NimNode]() + a.inc(body) + +test: + 1 + 1 diff --git a/tests/ccgbugs/twrong_discriminant_check.nim b/tests/ccgbugs/twrong_discriminant_check.nim new file mode 100644 index 000000000..a802f45ef --- /dev/null +++ b/tests/ccgbugs/twrong_discriminant_check.nim @@ -0,0 +1,30 @@ +discard """ + output: "(kind: None)" +""" + +when true: + # bug #2637 + + type + OptionKind = enum + None, + Some + + Option*[T] = object + case kind: OptionKind + of None: + discard + of Some: + value*: T + + proc none*[T](): Option[T] = + Option[T](kind: None) + + proc none*(T: typedesc): Option[T] = none[T]() + + + proc test(): Option[int] = + int.none + + echo test() + diff --git a/tests/ccgbugs/twrong_string_asgn.nim b/tests/ccgbugs/twrong_string_asgn.nim new file mode 100644 index 000000000..b62e70e7c --- /dev/null +++ b/tests/ccgbugs/twrong_string_asgn.nim @@ -0,0 +1,19 @@ +discard """ + output: "adf" +""" + +import asyncdispatch +const + test = ["adf"] + +proc foo() {.async.} = + for i in test: + echo(i) + +var finished = false +let x = foo() +x.callback = + proc () = + finished = true + +while not finished: discard diff --git a/tests/compiles/trecursive_generic_in_compiles.nim b/tests/compiles/trecursive_generic_in_compiles.nim new file mode 100644 index 000000000..77bf0bb02 --- /dev/null +++ b/tests/compiles/trecursive_generic_in_compiles.nim @@ -0,0 +1,98 @@ +# bug #3313 +import unittest, future + +type + ListNodeKind = enum + lnkNil, lnkCons + List*[T] = ref object + ## List ADT + case kind: ListNodeKind + of lnkNil: + discard + of lnkCons: + value: T + next: List[T] not nil + +proc Cons*[T](head: T, tail: List[T]): List[T] = + ## Constructs non empty list + List[T](kind: lnkCons, value: head, next: tail) + +proc Nil*[T](): List[T] = + ## Constructs empty list + List[T](kind: lnkNil) + +proc head*[T](xs: List[T]): T = + ## Returns list's head + xs.value + +# TODO +# proc headOption*[T](xs: List[T]): Option[T] = ??? + +proc tail*[T](xs: List[T]): List[T] = + ## Returns list's tail + case xs.kind + of lnkCons: xs.next + else: xs + +proc isEmpty*(xs: List): bool = + ## Checks if list is empty + xs.kind == lnkNil + +proc `==`*[T](xs, ys: List[T]): bool = + ## Compares two lists + if (xs.isEmpty, ys.isEmpty) == (true, true): true + elif (xs.isEmpty, ys.isEmpty) == (false, false): xs.head == ys.head and xs.tail == ys.tail + else: false + +proc asList*[T](xs: varargs[T]): List[T] = + ## Creates list from varargs + proc initListImpl(i: int, xs: openarray[T]): List[T] = + if i > high(xs): + Nil[T]() + else: + Cons(xs[i], initListImpl(i+1, xs)) + initListImpl(0, xs) + +proc foldRight*[T,U](xs: List[T], z: U, f: (T, U) -> U): U = + case xs.isEmpty + of true: z + else: f(xs.head, xs.tail.foldRight(z, f)) + +proc dup*[T](xs: List[T]): List[T] = + ## Duplicates the list + xs.foldRight(Nil[T](), (x: T, xs: List[T]) => Cons(x, xs)) + +type + ListFormat = enum + lfADT, lfSTD + +proc asString[T](xs: List[T], f = lfSTD): string = + proc asAdt(xs: List[T]): string = + case xs.isEmpty + of true: "Nil" + else: "Cons(" & $xs.head & ", " & xs.tail.asAdt & ")" + + proc asStd(xs: List[T]): string = + "List(" & xs.foldLeft("", (s: string, v: T) => + (if s == "": $v else: s & ", " & $v)) & ")" + + case f + of lfADT: xs.asAdt + else: xs.asStd + +proc `$`*[T](xs: List[T]): string = + ## Converts list to string + result = xs.asString + +proc foldLeft*[T,U](xs: List[T], z: U, f: (U, T) -> U): U = + case xs.isEmpty + of true: z + else: foldLeft(xs.tail, f(z, xs.head), f) + +suite "unittest compilation error": + + test "issue 3313": + let lst = lc[$x | (x <- 'a'..'z'), string].asList + + let lstCopy = lst.dup + check: lstCopy == lst diff --git a/tests/cpp/trawsockets.nim b/tests/cpp/tnativesockets.nim index bc129de57..6108380a8 100644 --- a/tests/cpp/trawsockets.nim +++ b/tests/cpp/tnativesockets.nim @@ -2,4 +2,4 @@ discard """ cmd: "nim cpp $file" """ -import rawsockets +import nativesockets diff --git a/tests/discard/tvoidcontext.nim b/tests/discard/tvoidcontext.nim new file mode 100644 index 000000000..c3ea68bae --- /dev/null +++ b/tests/discard/tvoidcontext.nim @@ -0,0 +1,12 @@ +discard """ + errormsg: "value of type 'string' has to be discarded" + line: 12 +""" + +proc valid*(): string = + let x = 317 + "valid" + +proc invalid*(): string = + result = "foo" + "invalid" diff --git a/tests/enum/tenummix.nim b/tests/enum/tenummix.nim index 4352cdd81..c7db4e056 100644 --- a/tests/enum/tenummix.nim +++ b/tests/enum/tenummix.nim @@ -1,6 +1,6 @@ discard """ - file: "tenummix.nim" - line: 11 + tfile: "tenummix.nim" + tline: 11 errormsg: "type mismatch" """ diff --git a/tests/gc/gcemscripten.nim b/tests/gc/gcemscripten.nim new file mode 100644 index 000000000..bbef13d98 --- /dev/null +++ b/tests/gc/gcemscripten.nim @@ -0,0 +1,59 @@ +discard """ + outputsub: "77\n77" +""" + +## Check how GC/Alloc works in Emscripten +import strutils + +type + X = ref XObj + XObj = object + name: string + value: int +when defined(allow_print): + const print = true +else: + const print = false + +proc myResult3*(i:int):X {.exportc.} = + if print: echo "3" + new(result) + if print: echo "3-2" + result.value = i + +proc myResult5*(i:int, x:X):X {.exportc.} = + if print: echo "5" + system.GC_fullCollect() + new(result) + if print: echo "5-2" + result.value = i + x.value = i+1 + if result.value == x.value: + echo "This should not happen. Just allocated variable points to parameter" + +proc myResult2*(val: string, i: int): X {.exportc.} = + if print: echo "2-1" + result = myResult3(i) + if print: echo "2-2" + system.GC_fullCollect() + if print: echo "2-3" + var t = new(X) + if print: echo "2-4" + result.name = val + if t.name == "qwe": + echo "This should not happen. Variable is GC collected and new one on same place are allocated." + if print: echo "2-5" + +proc myResult4*(val: string, i: int): X {.exportc.} = + if print: echo "4-1" + result = myResult5(i, X()) + if print: echo "4-2" + +var x = myResult2("qwe", 77) +echo intToStr(x.value) + +var x2 = myResult4("qwe", 77) +echo intToStr(x2.value) + + + diff --git a/tests/generics/mbind_bracket.nim b/tests/generics/mbind_bracket.nim new file mode 100644 index 000000000..4bf18b471 --- /dev/null +++ b/tests/generics/mbind_bracket.nim @@ -0,0 +1,17 @@ + +import tables + +type + UUIDObject* = ref object + uuid: string + + Registry*[T] = ref object + objects: Table[string, T] + +proc newRegistry*[T](): Registry[T] = + result = Registry[T]() + result.objects = initTable[string, T](128) + +proc register*[T](self: Registry[T], obj: T) = + self.objects[obj.uuid] = obj + diff --git a/tests/generics/tbind_bracket.nim b/tests/generics/tbind_bracket.nim new file mode 100644 index 000000000..d0c5e2c6b --- /dev/null +++ b/tests/generics/tbind_bracket.nim @@ -0,0 +1,20 @@ +discard """ + output: "317" +""" + +# bug #2599 + +import mbind_bracket + +# also test that `[]` can be passed now as a first class construct: + +template takeBracket(x, a, i: untyped) = + echo x(a, i) + +var a: array[10, int] +a[8] = 317 + +takeBracket(`[]`, a, 8) + +let reg = newRegistry[UUIDObject]() +reg.register(UUIDObject()) diff --git a/tests/generics/tmap_auto.nim b/tests/generics/tmap_auto.nim new file mode 100644 index 000000000..dea9b571f --- /dev/null +++ b/tests/generics/tmap_auto.nim @@ -0,0 +1,13 @@ +import future + +let x = map(@[1, 2, 3], x => x+10) +assert x == @[11, 12, 13] + +let y = map(@[(1,"a"), (2,"b"), (3,"c")], x => $x[0] & x[1]) +assert y == @["1a", "2b", "3c"] + +proc eatsTwoArgProc[T,S,U](a: T, b: S, f: proc(t: T, s: S): U): U = + f(a,b) + +let z = eatsTwoArgProc(1, "a", (t,s) => $t & s) +assert z == "1a" diff --git a/tests/generics/tthread_generic.nim b/tests/generics/tthread_generic.nim index d34b24628..e8946caf6 100644 --- a/tests/generics/tthread_generic.nim +++ b/tests/generics/tthread_generic.nim @@ -3,7 +3,7 @@ discard """ """ type - TThreadFuncArgs[T] = object of TObject + TThreadFuncArgs[T] = object of RootObj a: proc(): T {.thread.} b: proc(val: T) {.thread.} diff --git a/tests/macros/tgensym.nim b/tests/macros/tgensym.nim index b3aef0a2c..a4d1a3606 100644 --- a/tests/macros/tgensym.nim +++ b/tests/macros/tgensym.nim @@ -1,6 +1,6 @@ -import rawsockets, asyncdispatch, macros +import nativesockets, asyncdispatch, macros var p = newDispatcher() -var sock = newAsyncRawSocket() +var sock = newAsyncNativeSocket() proc convertReturns(node, retFutureSym: NimNode): NimNode {.compileTime.} = case node.kind diff --git a/tests/manyloc/keineschweine/lib/zlib_helpers.nim b/tests/manyloc/keineschweine/lib/zlib_helpers.nim index 5241a77c0..076475964 100644 --- a/tests/manyloc/keineschweine/lib/zlib_helpers.nim +++ b/tests/manyloc/keineschweine/lib/zlib_helpers.nim @@ -1,4 +1,4 @@ -import zlib +import zip/zlib proc compress*(source: string): string = var diff --git a/tests/manyloc/nake/nakefile.nim b/tests/manyloc/nake/nakefile.nim index 6dc453e8d..2fe07ec17 100644 --- a/tests/manyloc/nake/nakefile.nim +++ b/tests/manyloc/nake/nakefile.nim @@ -1,5 +1,5 @@ import nake -import httpclient, zipfiles, times, math +import httpclient, zip/zipfiles, times, math nakeImports randomize() diff --git a/tests/metatype/tprocbothmeta.nim b/tests/metatype/tprocbothmeta.nim index ad12c5d26..ba061dda2 100644 --- a/tests/metatype/tprocbothmeta.nim +++ b/tests/metatype/tprocbothmeta.nim @@ -1,5 +1,5 @@ -proc myFun[A,B](x: A): B = +proc myFun[A](x: A): auto = result = float(x+10) proc myMap[T,S](sIn: seq[T], f: proc (q: T): S): seq[S] = diff --git a/tests/metatype/ttypedesc1.nim b/tests/metatype/ttypedesc1.nim index 19072d966..d78c62a94 100644 --- a/tests/metatype/ttypedesc1.nim +++ b/tests/metatype/ttypedesc1.nim @@ -7,7 +7,7 @@ type proc getTypeName(t: typedesc): string = t.name -proc foo(T: typedesc[float], a: expr): string = +proc foo(T: typedesc[float], a: auto): string = result = "float " & $(a.len > 5) proc foo(T: typedesc[TFoo], a: int): string = diff --git a/tests/metatype/tunresolved_return_type.nim b/tests/metatype/tunresolved_return_type.nim new file mode 100644 index 000000000..f67e065ea --- /dev/null +++ b/tests/metatype/tunresolved_return_type.nim @@ -0,0 +1,20 @@ +discard """ + errormsg: "cannot instantiate: 'T'" + line: 12 +""" + +# bug #2594 + + +type + ResultValue* = int64 + +proc toNumber[T: int|uint|int64|uint64](v: ResultValue): T = + if v < low(T) or v > high(T): + raise newException(RangeError, "protocol error") + return T(v) + +#proc toNumber[T](v: int32): T = +# return (v) + +echo toNumber(23) diff --git a/tests/method/tmapper.nim b/tests/method/tmapper.nim index 0008d9033..75b36e69a 100644 --- a/tests/method/tmapper.nim +++ b/tests/method/tmapper.nim @@ -1,5 +1,5 @@ discard """ - errormsg: "invalid declaration order; cannot attach 'step' to method defined here: tmapper.nim(22,7)" + errormsg: "invalid declaration order; cannot attach 'step' to method defined here: tests/method/tmapper.nim(22,7)" line: 25 """ diff --git a/tests/misc/tnot.nim b/tests/misc/tnot.nim index 60d23c035..8c75c6bc0 100644 --- a/tests/misc/tnot.nim +++ b/tests/misc/tnot.nim @@ -1,6 +1,6 @@ discard """ - file: "tnot.nim" - line: 14 + tfile: "tnot.nim" + tline: 14 errormsg: "type mismatch" """ # BUG: following compiles, but should not: @@ -17,6 +17,3 @@ proc main = echo "No" main() - - - diff --git a/tests/misc/tvarious1.nim b/tests/misc/tvarious1.nim index 9d7cf6584..1d5ad876a 100644 --- a/tests/misc/tvarious1.nim +++ b/tests/misc/tvarious1.nim @@ -22,7 +22,7 @@ import queues type TWidget = object - names: TQueue[string] + names: Queue[string] var w = TWidget(names: initQueue[string]()) diff --git a/tests/modules/texport.nim b/tests/modules/texport.nim index 0890fb369..a8c217ab8 100644 --- a/tests/modules/texport.nim +++ b/tests/modules/texport.nim @@ -5,7 +5,7 @@ discard """ import mexporta # bug #1029: -from rawsockets import accept +from nativesockets import accept # B.TMyObject has been imported implicitly here: var x: TMyObject diff --git a/tests/newconfig/tfoo.nim b/tests/newconfig/tfoo.nim index 0ace7c88a..d593d4a75 100644 --- a/tests/newconfig/tfoo.nim +++ b/tests/newconfig/tfoo.nim @@ -4,4 +4,7 @@ discard """ msg: '''[NimScript] exec: gcc -v''' """ +when not defined(definedefine): + {.fatal: "wrong nim script configuration".} + echo "hello world!" diff --git a/tests/newconfig/tfoo.nims b/tests/newconfig/tfoo.nims index 90205cddb..519a868d5 100644 --- a/tests/newconfig/tfoo.nims +++ b/tests/newconfig/tfoo.nims @@ -3,6 +3,9 @@ mode = ScriptMode.Whatif exec "gcc -v" +# test that ospaths actually compiles: +import ospaths + --forceBuild task listDirs, "lists every subdirectory": @@ -10,5 +13,6 @@ task listDirs, "lists every subdirectory": echo "DIR ", x task default, "default target": + --define: definedefine setCommand "c" diff --git a/tests/objects/tillegal_recursion.nim b/tests/objects/tillegal_recursion.nim index 171a04f87..222139101 100644 --- a/tests/objects/tillegal_recursion.nim +++ b/tests/objects/tillegal_recursion.nim @@ -1,5 +1,5 @@ discard """ - errormsg: "illegal recursion in type 'object'" + errormsg: "inheritance only works with non-final objects" line: 7 """ # bug #1691 diff --git a/tests/parallel/tparfind.nim b/tests/parallel/tparfind.nim new file mode 100644 index 000000000..9de5012f5 --- /dev/null +++ b/tests/parallel/tparfind.nim @@ -0,0 +1,28 @@ +discard """ + output: "500" +""" + +import threadpool, sequtils + +{.experimental.} + +proc linearFind(a: openArray[int]; x, offset: int): int = + for i, y in a: + if y == x: return i+offset + result = -1 + +proc parFind(a: seq[int]; x: int): int = + var results: array[4, int] + parallel: + if a.len >= 4: + let chunk = a.len div 4 + results[0] = spawn linearFind(a[0 ..< chunk], x, 0) + results[1] = spawn linearFind(a[chunk ..< chunk*2], x, chunk) + results[2] = spawn linearFind(a[chunk*2 ..< chunk*3], x, chunk*2) + results[3] = spawn linearFind(a[chunk*3 ..< a.len], x, chunk*3) + result = max(results) + + +let data = toSeq(0..1000) +echo parFind(data, 500) + diff --git a/tests/parser/tstrongspaces.nim b/tests/parser/tstrongspaces.nim index e70b91988..cb0219976 100644 --- a/tests/parser/tstrongspaces.nim +++ b/tests/parser/tstrongspaces.nim @@ -1,4 +1,4 @@ -#! strongSpaces +#? strongSpaces discard """ output: '''35 diff --git a/tests/pragmas/tbitsize.nim b/tests/pragmas/tbitsize.nim new file mode 100644 index 000000000..7a44944d2 --- /dev/null +++ b/tests/pragmas/tbitsize.nim @@ -0,0 +1,22 @@ +discard """ +ccodeCheck: "\\i @'unsigned int flag:1;' .*" +""" + +type + bits* = object + flag* {.bitsize: 1.}: cuint + opts* {.bitsize: 4.}: cint + +var + b: bits + +assert b.flag == 0 +b.flag = 1 +assert b.flag == 1 +b.flag = 2 +assert b.flag == 0 + +b.opts = 7 +assert b.opts == 7 +b.opts = 9 +assert b.opts == -7 diff --git a/tests/rational/trat_float.nim b/tests/rational/trat_float.nim new file mode 100644 index 000000000..24797c4a0 --- /dev/null +++ b/tests/rational/trat_float.nim @@ -0,0 +1,9 @@ +discard """ + file: "trat_float.nim" + line: "9,19" + errormsg: '''type mismatch: got''' +""" +import rationals +var + # this fails - no floats as num or den + r = initRational(1.0'f, 1.0'f) diff --git a/tests/rational/trat_init.nim b/tests/rational/trat_init.nim new file mode 100644 index 000000000..df29ff6e3 --- /dev/null +++ b/tests/rational/trat_init.nim @@ -0,0 +1,10 @@ +discard """ + file: "trat_init.nim" + exitcode: "1" +""" +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 diff --git a/tests/stdlib/tmemfiles1.nim b/tests/stdlib/tmemfiles1.nim new file mode 100644 index 000000000..8b66dfcc1 --- /dev/null +++ b/tests/stdlib/tmemfiles1.nim @@ -0,0 +1,12 @@ +discard """ + file: "tmemfiles1.nim" +""" +import memfiles, os +var + mm: MemFile + fn = "test.mmap" +# Create a new file +mm = memfiles.open(fn, mode = fmReadWrite, newFileSize = 20) +mm.close() +mm.close() +if fileExists(fn): removeFile(fn) diff --git a/tests/stdlib/tmemfiles2.nim b/tests/stdlib/tmemfiles2.nim new file mode 100644 index 000000000..026443e93 --- /dev/null +++ b/tests/stdlib/tmemfiles2.nim @@ -0,0 +1,39 @@ +discard """ + file: "tmemfiles2.nim" + disabled: true + output: '''Full read size: 20 +Half read size: 10 Data: Hello''' +""" +# doesn't work on windows. fmReadWrite doesn't create a file. +import memfiles, os +var + mm, mm_full, mm_half: MemFile + fn = "test.mmap" + p: pointer + +if fileExists(fn): removeFile(fn) + +# Create a new file, data all zeros +mm = memfiles.open(fn, mode = fmReadWrite, newFileSize = 20) +mm.close() + +# read, change +mm_full = memfiles.open(fn, mode = fmWrite, mappedSize = -1) +echo "Full read size: ",mm_full.size +p = mm_full.mapMem(fmReadWrite, 20, 0) +var p2 = cast[cstring](p) +p2[0] = 'H' +p2[1] = 'e' +p2[2] = 'l' +p2[3] = 'l' +p2[4] = 'o' +p2[5] = '\0' +mm_full.unmapMem(p, 20) +mm_full.close() + +# read half, and verify data change +mm_half = memfiles.open(fn, mode = fmRead, mappedSize = 10) +echo "Half read size: ",mm_half.size, " Data: ", cast[cstring](mm_half.mem) +mm_half.close() + +if fileExists(fn): removeFile(fn) diff --git a/tests/stdlib/tstreams2.nim b/tests/stdlib/tstreams2.nim new file mode 100644 index 000000000..90102d8e3 --- /dev/null +++ b/tests/stdlib/tstreams2.nim @@ -0,0 +1,13 @@ +discard """ + file: "tstreams2.nim" + output: '''fs is: nil''' +""" +import streams +var + fs = newFileStream("amissingfile.txt") + line = "" +echo "fs is: ",repr(fs) +if not isNil(fs): + while fs.readLine(line): + echo line + fs.close() diff --git a/tests/system/tfloatToString.nim b/tests/system/tfloatToString.nim deleted file mode 100644 index bb45a91d7..000000000 --- a/tests/system/tfloatToString.nim +++ /dev/null @@ -1,22 +0,0 @@ -discard """ - output:'''2.3242 -2.982 -123912.1 -123912.1823 -5.0 -1e+100 -inf --inf -nan -''' -""" - -echo($(2.3242)) -echo($(2.982)) -echo($(123912.1)) -echo($(123912.1823)) -echo($(5.0)) -echo($(1e100)) -echo($(1e1000000)) -echo($(-1e1000000)) -echo($(0.0/0.0)) diff --git a/tests/system/toString.nim b/tests/system/toString.nim index 52e7a4b92..a2337f5dd 100644 --- a/tests/system/toString.nim +++ b/tests/system/toString.nim @@ -1,9 +1,42 @@ discard """ output:'''@[23, 45] -@[, foo, bar]''' +@[, foo, bar] +{a, b, c} +2.3242 +2.982 +123912.1 +123912.1823 +5.0 +1e+100 +inf +-inf +nan +nil +nil''' """ echo($(@[23, 45])) echo($(@["", "foo", "bar"])) #echo($(["", "foo", "bar"])) #echo($([23, 45])) + +# bug #2395 + +let alphaSet: set[char] = {'a'..'c'} +echo alphaSet + +echo($(2.3242)) +echo($(2.982)) +echo($(123912.1)) +echo($(123912.1823)) +echo($(5.0)) +echo($(1e100)) +echo($(1e1000000)) +echo($(-1e1000000)) +echo($(0.0/0.0)) + +# nil tests +var x: seq[string] +var y: string +echo(x) +echo(y) diff --git a/tests/system/tsettostring.nim b/tests/system/tsettostring.nim deleted file mode 100644 index c6846ee99..000000000 --- a/tests/system/tsettostring.nim +++ /dev/null @@ -1,8 +0,0 @@ -discard """ - output: "{a, b, c}" -""" - -# bug #2395 - -let alphaSet: set[char] = {'a'..'c'} -echo alphaSet diff --git a/tests/template/sunset.tmpl b/tests/template/sunset.tmpl index 6475bac4e..465b12a5e 100644 --- a/tests/template/sunset.tmpl +++ b/tests/template/sunset.tmpl @@ -1,4 +1,4 @@ -#! stdtmpl +#? stdtmpl #proc sunsetTemplate*(current, ticker, content: string, # tabs: openarray[array[0..1, string]]): string = # result = "" diff --git a/tests/template/twrongmapit.nim b/tests/template/twrongmapit.nim index bca1292b8..0a6d694f6 100644 --- a/tests/template/twrongmapit.nim +++ b/tests/template/twrongmapit.nim @@ -1,7 +1,5 @@ discard """ - errormsg: "'" - file: "sequtils.nim" - line: 435 + output: "####" """ # unfortunately our tester doesn't support multiple lines of compiler # error messages yet... @@ -29,4 +27,6 @@ when ATTEMPT == 0: # bug #1543 import sequtils -(var i= @[""];i).mapIt(it) +(var i = @[""];i).mapIt(it) +# now works: +echo "##", i[0], "##" diff --git a/tests/testament/categories.nim b/tests/testament/categories.nim index 10e39b20a..33ce086d3 100644 --- a/tests/testament/categories.nim +++ b/tests/testament/categories.nim @@ -89,8 +89,11 @@ proc runBasicDLLTest(c, r: var TResults, cat: Category, options: string) = var libpath = getEnv"LD_LIBRARY_PATH".string # Temporarily add the lib directory to LD_LIBRARY_PATH: putEnv("LD_LIBRARY_PATH", "lib:" & libpath) + defer: putEnv("LD_LIBRARY_PATH", libpath) var serverDll = DynlibFormat % "server" safeCopyFile("tests/dll" / serverDll, "lib" / serverDll) + var nimrtlDll = DynlibFormat % "nimrtl" + safeCopyFile("tests/dll" / nimrtlDll, "lib" / nimrtlDll) testSpec r, makeTest("tests/dll/client.nim", options & " -d:useNimRtl", cat, actionRun) @@ -127,6 +130,7 @@ proc gcTests(r: var TResults, cat: Category, options: string) = testSpec r, makeTest("tests/gc" / filename, options & " -d:release --gc:boehm", cat, actionRun) + test "gcemscripten" test "growobjcrash" test "gcbench" test "gcleak" @@ -345,7 +349,7 @@ proc `&?.`(a, b: string): string = # candidate for the stdlib? result = if a.endswith(b): a else: a & b -proc processCategory(r: var TResults, cat: Category, options: string) = +proc processCategory(r: var TResults, cat: Category, options: string, fileGlob: string = "t*.nim") = case cat.string.normalize of "rodfiles": discard # Disabled for now @@ -382,5 +386,5 @@ proc processCategory(r: var TResults, cat: Category, options: string) = of "nimble-all": testNimblePackages(r, cat, pfAll) else: - for name in os.walkFiles("tests" & DirSep &.? cat.string / "t*.nim"): + for name in os.walkFiles("tests" & DirSep &.? cat.string / fileGlob): testSpec r, makeTest(name, options, cat) diff --git a/tests/testament/specs.nim b/tests/testament/specs.nim index 99640f22c..bab17d2cd 100644 --- a/tests/testament/specs.nim +++ b/tests/testament/specs.nim @@ -44,6 +44,8 @@ type file*, cmd*: string outp*: string line*, column*: int + tfile*: string + tline*, tcolumn*: int exitCode*: int msg*: string ccodeCheck*: string @@ -101,6 +103,9 @@ proc specDefaults*(result: var TSpec) = result.cmd = cmdTemplate result.line = 0 result.column = 0 + result.tfile = "" + result.tline = 0 + result.tcolumn = 0 proc parseSpec*(filename: string): TSpec = specDefaults(result) @@ -116,6 +121,9 @@ proc parseSpec*(filename: string): TSpec = of "file": result.file = e.value of "line": discard parseInt(e.value, result.line) of "column": discard parseInt(e.value, result.column) + of "tfile": result.tfile = e.value + of "tline": discard parseInt(e.value, result.tline) + of "tcolumn": discard parseInt(e.value, result.tcolumn) of "output": result.action = actionRun result.outp = e.value diff --git a/tests/testament/tester.nim b/tests/testament/tester.nim index b138c9909..636093a7f 100644 --- a/tests/testament/tester.nim +++ b/tests/testament/tester.nim @@ -23,6 +23,7 @@ const Command: all run all tests c|category <category> run all the tests of a certain category + r|run <test> run single test file html [commit] generate $1 from the database; uses the latest commit or a specific one (use -1 for the commit before latest etc) @@ -52,6 +53,8 @@ type let pegLineError = peg"{[^(]*} '(' {\d+} ', ' {\d+} ') ' ('Error') ':' \s* {.*}" + pegLineTemplate = + peg"{[^(]*} '(' {\d+} ', ' {\d+} ') ' 'template/generic instantiation from here'.*" pegOtherError = peg"'Error:' \s* {.*}" pegSuccess = peg"'Hint: operation successful'.*" pegOfInterest = pegLineError / pegOtherError @@ -65,6 +68,7 @@ proc callCompiler(cmdTemplate, filename, options: string, let outp = p.outputStream var suc = "" var err = "" + var tmpl = "" var x = newStringOfCap(120) result.nimout = "" while outp.readLine(x.TaintedString) or running(p): @@ -72,6 +76,9 @@ proc callCompiler(cmdTemplate, filename, options: string, if x =~ pegOfInterest: # `err` should contain the last error/warning message err = x + elif x =~ pegLineTemplate and err == "": + # `tmpl` contains the last template expansion before the error + tmpl = x elif x =~ pegSuccess: suc = x close(p) @@ -80,6 +87,13 @@ proc callCompiler(cmdTemplate, filename, options: string, result.outp = "" result.line = 0 result.column = 0 + result.tfile = "" + result.tline = 0 + result.tcolumn = 0 + if tmpl =~ pegLineTemplate: + result.tfile = extractFilename(matches[0]) + result.tline = parseInt(matches[1]) + result.tcolumn = parseInt(matches[2]) if err =~ pegLineError: result.file = extractFilename(matches[0]) result.line = parseInt(matches[1]) @@ -153,7 +167,7 @@ proc addResult(r: var TResults, test: TTest, proc cmpMsgs(r: var TResults, expected, given: TSpec, test: TTest) = if strip(expected.msg) notin strip(given.msg): r.addResult(test, expected.msg, given.msg, reMsgsDiffer) - elif extractFilename(expected.file) != extractFilename(given.file) and + elif expected.tfile == "" and extractFilename(expected.file) != extractFilename(given.file) and "internal error:" notin expected.msg: r.addResult(test, expected.file, given.file, reFilesDiffer) elif expected.line != given.line and expected.line != 0 or @@ -161,6 +175,14 @@ proc cmpMsgs(r: var TResults, expected, given: TSpec, test: TTest) = r.addResult(test, $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) + 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, + $given.tline & ':' & $given.tcolumn, + reLinesDiffer) else: r.addResult(test, expected.msg, given.msg, reSuccess) inc(r.passed) @@ -212,6 +234,8 @@ proc compilerOutputTests(test: TTest, given: var TSpec, expected: TSpec; expectedmsg = expected.nimout givenmsg = given.nimout.strip nimoutCheck(test, expectedmsg, given) + else: + givenmsg = given.nimout.strip if given.err == reSuccess: inc(r.passed) r.addResult(test, expectedmsg, givenmsg, given.err) @@ -279,7 +303,7 @@ proc testSpec(r: var TResults, test: TTest) = return let exeCmd = (if isJsTarget: nodejs & " " else: "") & exeFile - let (buf, exitCode) = execCmdEx(exeCmd) + var (buf, exitCode) = execCmdEx(exeCmd, options = {poStdErrToStdOut}) let bufB = if expected.sortoutput: makeDeterministic(strip(buf.string)) else: strip(buf.string) let expectedOut = strip(expected.outp) @@ -376,6 +400,11 @@ proc main() = var cat = Category(p.key) p.next processCategory(r, cat, p.cmdLineRest.string) + of "r", "run": + let (dir, file) = splitPath(p.key.string) + let (_, subdir) = splitPath(dir) + var cat = Category(subdir) + processCategory(r, cat, p.cmdLineRest.string, file) of "html": var commit = 0 discard parseInt(p.cmdLineRest.string, commit) diff --git a/tests/typerel/t2plus.nim b/tests/typerel/t2plus.nim new file mode 100644 index 000000000..08378b804 --- /dev/null +++ b/tests/typerel/t2plus.nim @@ -0,0 +1,22 @@ +discard """ + output: "2.0" +""" + +{.warning[TypelessParam]: off.} + +import future + +# bug #3329 + +proc foldRight[T,U](lst: seq[T], v: U, f: (T, U) -> U): U = + result = v + for x in lst: + result = f(x, result) + +proc mean[T: SomeNumber](xs: seq[T]): T = + xs.foldRight(0.T, (xBAZ: auto, yBAZ: auto) => xBAZ + yBAZ) / T(xs.len) + +when isMainModule: + let x = mean(@[1.float, 2, 3]) + echo x + diff --git a/tests/typerel/typeof_in_template.nim b/tests/typerel/typeof_in_template.nim new file mode 100644 index 000000000..9ec06f2e3 --- /dev/null +++ b/tests/typerel/typeof_in_template.nim @@ -0,0 +1,16 @@ +discard """ + output: '''@[a, c]''' +""" + +# bug #3230 + +import sequtils + +const + test_strings = ["a", "b", "c"] + +proc is_doc(x: string): bool = x == "b" + +let + tests = @test_strings.filter_it(not it.is_doc) +echo tests diff --git a/tests/types/tauto_excessive.nim b/tests/types/tauto_excessive.nim new file mode 100644 index 000000000..2626b0cf4 --- /dev/null +++ b/tests/types/tauto_excessive.nim @@ -0,0 +1,20 @@ +discard """ + output: '''10 +10.0 +1.0hiho''' +""" + +# bug #3224 +proc f(x: auto): auto = + result = $(x+10) + +proc f(x, y: auto): auto = + result = $(x+y) + + +echo f(0) # prints 10 +echo f(0.0) # prints 10.0 + +proc `+`(a, b: string): string = a & b + +echo f(0.7, 0.3), f("hi", "ho") diff --git a/tests/vm/tconstobj.nim b/tests/vm/tconstobj.nim index 414708945..51f30fb78 100644 --- a/tests/vm/tconstobj.nim +++ b/tests/vm/tconstobj.nim @@ -1,8 +1,9 @@ discard """ - output: '''(name: hello)''' + output: '''(name: hello) +(-1, 0)''' """ -# bug #2774 +# bug #2774, bug #3195 type Foo = object name: string @@ -12,3 +13,24 @@ const fooArray = [ ] echo fooArray[0] + + +type + Position = object + x, y: int + +proc `$`(pos: Position): string = + result = "(" & $pos.x & ", " & $pos.y & ")" + +proc newPos(x, y: int): Position = + result = Position(x: x, y: y) + +const + offset: array[1..4, Position] = [ + newPos(-1, 0), + newPos(1, 0), + newPos(0, -1), + newPos(0, 1) + ] + +echo offset[1] diff --git a/tests/vm/tsimpleglobals.nim b/tests/vm/tsimpleglobals.nim new file mode 100644 index 000000000..27bfdce50 --- /dev/null +++ b/tests/vm/tsimpleglobals.nim @@ -0,0 +1,16 @@ +discard """ + msg: "abc xyz bb" +""" + +# bug #2473 +type + Test = tuple[a,b: string] + +static: + var s:seq[Test] = @[(a:"a", b:"b")] + s[0] = (a:"aa", b:"bb") + + var x: Test + x.a = "abc" + x.b = "xyz" + echo x.a, " ", x.b, " ", s[0].b diff --git a/tests/vm/twrongconst.nim b/tests/vm/twrongconst.nim index 68ab2757c..424ed080e 100644 --- a/tests/vm/twrongconst.nim +++ b/tests/vm/twrongconst.nim @@ -1,6 +1,6 @@ discard """ errormsg: "cannot evaluate at compile time: x" - line: 9 + line: 7 """ var x: array[100, char] diff --git a/tests/vm/tyaytypedesc.nim b/tests/vm/tyaytypedesc.nim new file mode 100644 index 000000000..a3ad9b707 --- /dev/null +++ b/tests/vm/tyaytypedesc.nim @@ -0,0 +1,21 @@ +discard """ + output: "ntWhitespace" +""" + +# bug #3357 + +type NodeType* = enum + ntWhitespace + +type TokenType* = enum + ttWhitespace + +proc enumTable*[A, B, C](a: openarray[tuple[key: A, val: B]], ret: typedesc[C]): C = + for item in a: + result[item.key] = item.val + +const tokenTypeToNodeType = { + ttWhitespace: ntWhitespace, +}.enumTable(array[ttWhitespace..ttWhitespace, NodeType]) + +echo tokenTypeToNodeType[ttWhitespace] diff --git a/todo.txt b/todo.txt index c645f45e9..306b7008e 100644 --- a/todo.txt +++ b/todo.txt @@ -23,9 +23,7 @@ version 1.0 - nimsuggest: auto-completion needs to work in 'class' macros - The bitwise 'not' operator will be renamed to 'bnot' to prevent 'not 4 == 5' from compiling. -> requires 'mixin' annotation for procs! -- iterators always require a return type - split docgen into separate tool -- special rule for ``[]=``, items, pairs - BUG: echo with template `$`*(info: TLineInfo): expr = toFileLineCol(info) - make 'nil' work for 'add': - resizeString diff --git a/tools/niminst/buildbat.tmpl b/tools/niminst/buildbat.tmpl index 2c6a2b5a8..2a8da144d 100644 --- a/tools/niminst/buildbat.tmpl +++ b/tools/niminst/buildbat.tmpl @@ -1,4 +1,4 @@ -#! stdtmpl(subsChar='?') | standard +#? stdtmpl(subsChar='?') | standard #proc generateBuildBatchScript(c: ConfigData, winIndex, cpuIndex: int): string = # result = "@echo off\nREM Generated by niminst\n" SET CC=gcc diff --git a/tools/niminst/buildsh.tmpl b/tools/niminst/buildsh.tmpl index 52da351be..463a1ad52 100644 --- a/tools/niminst/buildsh.tmpl +++ b/tools/niminst/buildsh.tmpl @@ -1,4 +1,4 @@ -#! stdtmpl(subsChar='?') | standard +#? stdtmpl(subsChar='?') | standard #proc generateBuildShellScript(c: ConfigData): string = # result = "#! /bin/sh\n# Generated from niminst\n" & # "# Template is in tools/niminst/buildsh.tmpl\n" & diff --git a/tools/niminst/deinstall.tmpl b/tools/niminst/deinstall.tmpl index c4717a257..7349abcb4 100644 --- a/tools/niminst/deinstall.tmpl +++ b/tools/niminst/deinstall.tmpl @@ -1,4 +1,4 @@ -#! stdtmpl(subsChar='?') | standard +#? stdtmpl(subsChar='?') | standard #proc generateDeinstallScript(c: ConfigData): string = # result = "#! /bin/sh\n# Generated by niminst\n" # var proj = c.name.toLower diff --git a/tools/niminst/inno.tmpl b/tools/niminst/inno.tmpl index 4acf0557c..ef2da8a75 100644 --- a/tools/niminst/inno.tmpl +++ b/tools/niminst/inno.tmpl @@ -1,4 +1,4 @@ -#! stdtmpl | standard +#? stdtmpl | standard #proc generateInnoSetup(c: ConfigData): string = # result = "" ; Default Template for NimInst diff --git a/tools/niminst/install.tmpl b/tools/niminst/install.tmpl index 3ec42c287..14d88e07d 100644 --- a/tools/niminst/install.tmpl +++ b/tools/niminst/install.tmpl @@ -1,4 +1,4 @@ -#! stdtmpl(subsChar = '?') | standard +#? stdtmpl(subsChar = '?') | standard #proc generateInstallScript(c: ConfigData): string = # result = "#! /bin/sh\n# Generated by niminst\n" # var proj = c.name.toLower diff --git a/tools/niminst/makefile.tmpl b/tools/niminst/makefile.tmpl index 8ab3b89d1..6615ddc02 100644 --- a/tools/niminst/makefile.tmpl +++ b/tools/niminst/makefile.tmpl @@ -1,4 +1,4 @@ -#! stdtmpl(subsChar='?') | standard +#? stdtmpl(subsChar='?') | standard #proc generateMakefile(c: ConfigData): string = # result = "# Generated from niminst\n" & # "# Template is in tools/niminst/makefile.tmpl\n" & diff --git a/tools/niminst/nsis.tmpl b/tools/niminst/nsis.tmpl index 843a8cf44..abf462388 100644 --- a/tools/niminst/nsis.tmpl +++ b/tools/niminst/nsis.tmpl @@ -1,4 +1,4 @@ -#! stdtmpl(subsChar='?') | standard +#? stdtmpl(subsChar='?') | standard #proc generateNsisSetup(c: ConfigData): string = # result = "; NSIS script generated by niminst\n" & # "; To regenerate run ``niminst nsis`` or ``koch nsis``\n" diff --git a/tools/website.tmpl b/tools/website.tmpl index bc3ed8e2c..3209aac51 100644 --- a/tools/website.tmpl +++ b/tools/website.tmpl @@ -1,5 +1,5 @@ -#! stdtmpl | standard -#proc generateHTMLPage(c: var TConfigData, currentTab, content, rss: string): string = +#? stdtmpl | standard +#proc generateHTMLPage(c: var TConfigData, currentTab, content, rss: string): string = # result = "" <!DOCTYPE html> <html> @@ -23,7 +23,7 @@ # if t != "index" and t != "community" and t != "news": # let name = c.tabs[i].key # if currentTab == t: - <a class="active" + <a class="active" # else: <a # end if @@ -104,7 +104,7 @@ p.greet() <span class="cmt"># or greet(p)</span> <span class="cmt"># declare a C procedure..</span> <span class="kwd">proc</span> <span class="def">unsafeScanf</span>(f: <span class="typ">File</span>, s: <span class="typ">cstring</span>) <span class="tab"> </span>{.varargs, -<span class="tab"> </span>importc: <span class="val">"fscanf"</span>, +<span class="tab"> </span>importc: <span class="val">"fscanf"</span>, <span class="tab end"> </span>header: <span class="val">"<stdio.h>"</span>.} <span class="cmt"># ..and use it...</span> @@ -191,7 +191,7 @@ runForever() </div> </div> </footer> - + # if currentTab == "index": <script src="assets/index.js"></script> # end if diff --git a/web/community.txt b/web/community.txt index 4b8c481b9..b8a6c7372 100644 --- a/web/community.txt +++ b/web/community.txt @@ -41,14 +41,14 @@ Nim's Community .. container:: standout - Github + GitHub ------ - Nim's `source code <http://github.com/nim-lang/Nim>`_ is hosted on Github. + Nim's `source code <http://github.com/nim-lang/Nim>`_ is hosted on GitHub. Together with the `wiki <http://github.com/nim-lang/Nim/wiki>`_ and `issue tracker <http://github.com/nim-lang/Nim/issues>`_. - Github also hosts other projects relating to Nim. These projects are a part + GitHub also hosts other projects relating to Nim. These projects are a part of the `nim-lang organisation <http://github.com/nim-lang>`_. This includes the `Nimble package manager <https://github.com/nim-lang/nimble>`_ and its `package repository <http://github.com/nim-lang/packages>`_. diff --git a/web/news.txt b/web/news.txt index ac6f8ae09..2b6079620 100644 --- a/web/news.txt +++ b/web/news.txt @@ -3,12 +3,15 @@ News ==== .. - 2015-05-05 Version 0.11.4 released + 2015-xx-xx Version 0.11.4 released ================================== Changes affecting backwards compatibility ----------------------------------------- + - The ``rawsockets`` module has been renamed to ``nativesockets`` to avoid + confusion with TCP/IP raw sockets, so ``newNativeSocket`` should be used + instead of ``newRawSocket``. - The ``miliseconds`` property of ``times.TimeInterval`` is now ``milliseconds``. Code accessing that property is deprecated and code using ``miliseconds`` during object initialization or as a named parameter of ``initInterval()`` @@ -58,7 +61,7 @@ News of all the DLLs the standard library needs. This means that the following DLLs are now split into 32 and 64 versions: - * ``prce.dll``: Split into ``prce32.dll`` and ``prce64.dll``. + * ``pcre.dll``: Split into ``pcre32.dll`` and ``pcre64.dll``. * ``pdcurses.dll``: Split into ``pdcurses32.dll`` and ``pdcurses64.dll``. * ``sqlite3.dll``: Split into ``sqlite3_32.dll`` and ``sqlite3_64.dll``. * ``ssleay32.dll``: Split into ``ssleay32.dll`` and ``ssleay64.dll``. @@ -71,6 +74,19 @@ News are likely to break as well. - Base methods now need to be annotated with the ``base`` pragma. This makes multi methods less error-prone to use with the effect system. + - Nim's parser directive ``#!`` is now ``#?`` in order to produce no conflicts + with Unix's ``#!``. + - An implicit return type for an iterator is now deprecated. Use ``auto`` if + you want more type inference. + - The type ``auto`` is now a "multi-bind" metatype, so the following compiles: + + .. code-block:: nim + proc f(x, y: auto): auto = + result = $x & y + + echo f(0, "abc") + - The ``ftpclient`` module is now deprecated in favour of the + ``asyncdispatch`` module. Library Additions @@ -84,6 +100,7 @@ News to benchmark it. - ``strutils.formatFloat`` and ``formatBiggestFloat`` do not depend on the C locale anymore and now take an optional ``decimalSep = '.'`` parameter. + - Added ``unicode.lastRune``, ``unicode.graphemeLen``. Compiler Additions @@ -91,6 +108,11 @@ News - The compiler now supports a new configuration system based on `NimScript <docs/nims.html>`_. + - The compiler finally considers symbol binding rules in templates and + generics for overloaded ``[]``, ``[]=``, ``{}``, ``{}=`` operators + (issue `#2599 <https://github.com/nim-lang/Nim/issues/2599>`_). + - The compiler now supports a `bitsize pragma <docs/manual.html#pragmas-bitsize-pragma>`_ + for constructing bitfields. Language Additions @@ -103,15 +125,214 @@ News - Added ``macros.getImpl`` that can be used to access the implementation of a routine or a constant. This allows for example for user-defined inlining of function calls. - - Tuple unpacking finally works in a non-var/let context: ``(x, y) == f()`` + - Tuple unpacking finally works in a non-var/let context: ``(x, y) = f()`` is allowed. Note that this doesn't declare ``x`` and ``y`` variables, for - this ``let (x, y) == f()`` still needs to be used. + this ``let (x, y) = f()`` still needs to be used. - ``when nimvm`` can now be used for compiletime versions of some code sections. Click `here <docs/manual.html#when-nimvm-statement>`_ for details. + - Usage of the type ``NimNode`` in a proc now implicitly annotates the proc + with ``.compileTime``. This means generics work much better for ``NimNode``. Bugfixes -------- + - Fixed "Compiler internal error on iterator it(T: typedesc[Base]) called with it(Child), where Child = object of Base" + (`#2662 <https://github.com/Araq/Nim/issues/2662>`_) + - Fixed "repr() misses base object field in 2nd level derived object" + (`#2749 <https://github.com/Araq/Nim/issues/2749>`_) + - Fixed "nimsuggest doesn't work more than once on the non-main file" + (`#2694 <https://github.com/Araq/Nim/issues/2694>`_) + - Fixed "JS Codegen. Passing arguments by var in certain cases leads to invalid JS." + (`#2798 <https://github.com/Araq/Nim/issues/2798>`_) + - Fixed ""check" proc in unittest.nim prevents the propagation of changes to var parameters." + (`#964 <https://github.com/Araq/Nim/issues/964>`_) + - Fixed "Excessive letters in integer literals are not an error" + (`#2523 <https://github.com/Araq/Nim/issues/2523>`_) + - Fixed "Unicode dashes as "lisp'ish" alternative to hump and snake notation" + (`#2811 <https://github.com/Araq/Nim/issues/2811>`_) + - Fixed "Bad error message when trying to construct an object incorrectly" + (`#2584 <https://github.com/Araq/Nim/issues/2584>`_) + - Fixed "Determination of GC safety of globals is broken " + (`#2854 <https://github.com/Araq/Nim/issues/2854>`_) + - Fixed "v2 gc crashes compiler" + (`#2687 <https://github.com/Araq/Nim/issues/2687>`_) + - Fixed "Compile error using object in const array" + (`#2774 <https://github.com/Araq/Nim/issues/2774>`_) + - Fixed "httpclient async requests with method httpPOST isn't sending Content-Length header" + (`#2884 <https://github.com/Araq/Nim/issues/2884>`_) + - Fixed "Streams module not working with JS backend" + (`#2148 <https://github.com/Araq/Nim/issues/2148>`_) + - Fixed "Sign of certain short constants is wrong" + (`#1179 <https://github.com/Araq/Nim/issues/1179>`_) + - Fixed "Symlinks to directories reported as symlinks to files" + (`#1985 <https://github.com/Araq/Nim/issues/1985>`_) + - Fixed "64-bit literals broken on x86" + (`#2909 <https://github.com/Araq/Nim/issues/2909>`_) + - Fixed "import broken for certain names" + (`#2904 <https://github.com/Araq/Nim/issues/2904>`_) + - Fixed "Invalid UTF-8 strings in JavaScript" + (`#2917 <https://github.com/Araq/Nim/issues/2917>`_) + - Fixed "[JS][Codegen] Initialising object doesn't create unmentioned fields." + + (`#2617 <https://github.com/Araq/Nim/issues/2617>`_) + - Fixed "Table returned from proc computed at compile time is missing keys:" + (`#2297 <https://github.com/Araq/Nim/issues/2297>`_) + - Fixed "Clarify copyright status for some files" + (`#2949 <https://github.com/Araq/Nim/issues/2949>`_) + - Fixed "math.nim: trigonometry: radians to degrees conversion" + (`#2881 <https://github.com/Araq/Nim/issues/2881>`_) + - Fixed "xoring unsigned integers yields RangeError in certain conditions" + (`#2979 <https://github.com/Araq/Nim/issues/2979>`_) + - Fixed "Directly checking equality between procs" + (`#2985 <https://github.com/Araq/Nim/issues/2985>`_) + - Fixed "Compiler crashed, but there have to be meaningful error message" + (`#2974 <https://github.com/Araq/Nim/issues/2974>`_) + - Fixed "repr is broken" + (`#2992 <https://github.com/Araq/Nim/issues/2992>`_) + - Fixed "Ipv6 devel - add IPv6 support for asyncsockets, make AF_INET6 a default" + (`#2976 <https://github.com/Araq/Nim/issues/2976>`_) + - Fixed "Compilation broken on windows" + (`#2996 <https://github.com/Araq/Nim/issues/2996>`_) + - Fixed "'u64 literal conversion compiler error" + (`#2731 <https://github.com/Araq/Nim/issues/2731>`_) + - Fixed "Importing 'impure' libraries while using threads causes segfaults" + (`#2672 <https://github.com/Araq/Nim/issues/2672>`_) + - Fixed "Uncatched exception in async procedure on raise statement" + (`#3014 <https://github.com/Araq/Nim/issues/3014>`_) + - Fixed "nim doc2 fails in Mac OS X due to system.nim (possibly related to #1898)" + (`#3005 <https://github.com/Araq/Nim/issues/3005>`_) + - Fixed "IndexError when rebuilding Nim on iteration 2" + (`#3018 <https://github.com/Araq/Nim/issues/3018>`_) + - Fixed "Assigning large const set to variable looses some information" + (`#2880 <https://github.com/Araq/Nim/issues/2880>`_) + - Fixed "Inconsistent generics behavior" + (`#3022 <https://github.com/Araq/Nim/issues/3022>`_) + - Fixed "Compiler breaks on float64 division" + (`#3028 <https://github.com/Araq/Nim/issues/3028>`_) + - Fixed "Confusing error message comparing string to nil " + (`#2935 <https://github.com/Araq/Nim/issues/2935>`_) + - Fixed "convert 64bit number to float on 32bit" + (`#1463 <https://github.com/Araq/Nim/issues/1463>`_) + - Fixed "Type redefinition and construction will break nim check" + (`#3032 <https://github.com/Araq/Nim/issues/3032>`_) + - Fixed "XmlParser fails on very large XML files without new lines" + (`#2429 <https://github.com/Araq/Nim/issues/2429>`_) + - Fixed "Error parsing arguments with whitespaces" + (`#2874 <https://github.com/Araq/Nim/issues/2874>`_) + - Fixed "Crash when missing one arg and used a named arg" + (`#2993 <https://github.com/Araq/Nim/issues/2993>`_) + - Fixed "Wrong number of arguments in assert will break nim check" + (`#3044 <https://github.com/Araq/Nim/issues/3044>`_) + - Fixed "Wrong const definition will break nim check" + (`#3041 <https://github.com/Araq/Nim/issues/3041>`_) + - Fixed "Wrong set declaration will break nim check" + (`#3040 <https://github.com/Araq/Nim/issues/3040>`_) + - Fixed "Compiler segfault (type section)" + (`#2540 <https://github.com/Araq/Nim/issues/2540>`_) + - Fixed "Segmentation fault when compiling this code" + (`#3038 <https://github.com/Araq/Nim/issues/3038>`_) + - Fixed "Kill nim i" + (`#2633 <https://github.com/Araq/Nim/issues/2633>`_) + - Fixed "Nim check will break on wrong array declaration" + (`#3048 <https://github.com/Araq/Nim/issues/3048>`_) + - Fixed "boolVal seems to be broken" + (`#3046 <https://github.com/Araq/Nim/issues/3046>`_) + - Fixed "Nim check crashes on wrong set/array declaration inside ref object" + (`#3062 <https://github.com/Araq/Nim/issues/3062>`_) + - Fixed "Nim check crashes on incorrect generic arg definition" + (`#3051 <https://github.com/Araq/Nim/issues/3051>`_) + - Fixed "Nim check crashes on iterating nonexistent var" + (`#3053 <https://github.com/Araq/Nim/issues/3053>`_) + - Fixed "Nim check crashes on wrong param set declaration + iteration" + (`#3054 <https://github.com/Araq/Nim/issues/3054>`_) + - Fixed "Wrong sharing of static_t instantations" + (`#3112 <https://github.com/Araq/Nim/issues/3112>`_) + - Fixed "Automatically generated proc conflicts with user-defined proc when .exportc.'ed" + (`#3134 <https://github.com/Araq/Nim/issues/3134>`_) + - Fixed "getTypeInfo call crashes nim" + (`#3099 <https://github.com/Araq/Nim/issues/3099>`_) + - Fixed "Array ptr dereference" + (`#2963 <https://github.com/Araq/Nim/issues/2963>`_) + - Fixed "Internal error when `repr`-ing a type directly" + (`#3079 <https://github.com/Araq/Nim/issues/3079>`_) + - Fixed "unknown type name 'TNimType' after importing typeinfo module" + (`#2841 <https://github.com/Araq/Nim/issues/2841>`_) + - Fixed "Can export a template twice and from inside a block" + (`#1738 <https://github.com/Araq/Nim/issues/1738>`_) + - Fixed "C Codegen: C Types are defined after their usage in certain cases" + (`#2823 <https://github.com/Araq/Nim/issues/2823>`_) + - Fixed "s.high refers to the current seq instead of the old one" + (`#1832 <https://github.com/Araq/Nim/issues/1832>`_) + - Fixed "Error while unmarshaling null values" + (`#3149 <https://github.com/Araq/Nim/issues/3149>`_) + - Fixed "Inference of `static[T]` in sequences" + (`#3144 <https://github.com/Araq/Nim/issues/3144>`_) + - Fixed "Argument named "closure" to proc inside template interfere with closure pragma" + (`#3171 <https://github.com/Araq/Nim/issues/3171>`_) + - Fixed "Internal error with aliasing inside template" + (`#3158 <https://github.com/Araq/Nim/issues/3158>`_) + - Fixed "Cardinality of sets prints unexpected value" + (`#3135 <https://github.com/Araq/Nim/issues/3135>`_) + - Fixed "Nim crashes on const assignment from function returning var ref object" + (`#3103 <https://github.com/Araq/Nim/issues/3103>`_) + - Fixed "`repr` cstring" + (`#3080 <https://github.com/Araq/Nim/issues/3080>`_) + - Fixed "Nim check crashes on wrong enum declaration" + (`#3052 <https://github.com/Araq/Nim/issues/3052>`_) + - Fixed "Compiler assertion when evaluating template with static[T]" + (`#1858 <https://github.com/Araq/Nim/issues/1858>`_) + - Fixed "Erroneous overflow in iterators when compiler built with overflowChecks enabled" + (`#3140 <https://github.com/Araq/Nim/issues/3140>`_) + - Fixed "Unicode dashes as "lisp'ish" alternative to hump and snake notation" + (`#2811 <https://github.com/Araq/Nim/issues/2811>`_) + - Fixed "Calling discardable proc from a defer is an error." + (`#3185 <https://github.com/Araq/Nim/issues/3185>`_) + - Fixed "Defer statement at the end of a block produces ICE" + (`#3186 <https://github.com/Araq/Nim/issues/3186>`_) + - Fixed "Call to `createU` fails to compile" + (`#3193 <https://github.com/Araq/Nim/issues/3193>`_) + - Fixed "VM crash when accessing array's element" + (`#3192 <https://github.com/Araq/Nim/issues/3192>`_) + - Fixed "Unexpected proc invoked when different modules add procs to a type from a 3rd module" + (`#2664 <https://github.com/Araq/Nim/issues/2664>`_) + - Fixed "Nim crashes on conditional declaration inside a template" + (`#2670 <https://github.com/Araq/Nim/issues/2670>`_) + - Fixed "Iterator names conflict within different scopes" + (`#2752 <https://github.com/Araq/Nim/issues/2752>`_) + - Fixed "VM: Cannot assign int value to ref variable" + (`#1329 <https://github.com/Araq/Nim/issues/1329>`_) + - Fixed "Incorrect code generated for tagged unions with enums not starting at zero" + (`#3096 <https://github.com/Araq/Nim/issues/3096>`_) + - Fixed "Compile time procs using forward declarations are silently ignored" + (`#3066 <https://github.com/Araq/Nim/issues/3066>`_) + - Fixed "re binding error in generic" + (`#1965 <https://github.com/Araq/Nim/issues/1965>`_) + - Fixed "os.getCreationTime is incorrect/impossible on Posix systems" + (`#1058 <https://github.com/Araq/Nim/issues/1058>`_) + - Fixed "Improve error message for osproc.startProcess when command does not exist" + (`#2183 <https://github.com/Araq/Nim/issues/2183>`_) + - Fixed "gctest segfaults with --gc:markandsweep on x86_64" + (`#2305 <https://github.com/Araq/Nim/issues/2305>`_) + - Fixed "Coroutine changes break compilation on unsupported architectures" + (`#3245 <https://github.com/Araq/Nim/issues/3245>`_) + - Fixed "Bugfix: Windows 32bit TinyCC support issue fixed" + (`#3237 <https://github.com/Araq/Nim/issues/3237>`_) + - Fixed "db_mysql getValue() followed by exec() causing error" + (`#3220 <https://github.com/Araq/Nim/issues/3220>`_) + - Fixed "xmltree.newEntity creates xnCData instead of xnEntity" + (`#3282 <https://github.com/Araq/Nim/issues/3282>`_) + - Fixed "Methods and modules don't work together" + (`#2590 <https://github.com/Araq/Nim/issues/2590>`_) + - Fixed "String slicing not working in the vm" + (`#3300 <https://github.com/Araq/Nim/issues/3300>`_) + - Fixed "internal error: evalOp(mTypeOf)" + (`#3230 <https://github.com/Araq/Nim/issues/3230>`_) + - Fixed "#! source code prefix collides with Unix Shebang" + (`#2559 <https://github.com/Araq/Nim/issues/2559>`_) + - Fixed "wrong codegen for constant object" + (`#3195 <https://github.com/Araq/Nim/issues/3195>`_) + - Fixed "Doc comments inside procs with implicit returns don't work" + (`#1528 <https://github.com/Araq/Nim/issues/1528>`_) 2015-05-04 Version 0.11.2 released @@ -576,7 +797,7 @@ is the installation of packages containing libraries and/or applications written in Nim. Even though Nimble is still very young it already is very functional. It can install packages by name, it does so by accessing a -packages repository which is hosted on a Github repo. Packages can also be +packages repository which is hosted on a GitHub repo. Packages can also be installed via a Git repo URL or Mercurial repo URL. The package repository is searchable through Nimble. Anyone is free to add their own packages to the package repository by forking the diff --git a/web/question.txt b/web/question.txt index 46ea7161c..da38760f5 100644 --- a/web/question.txt +++ b/web/question.txt @@ -94,9 +94,9 @@ General FAQ What about JVM/CLR backends? ---------------------------- - A JVM backend is almost impossible. The JVM is not expressive enough. It has - never been designed as a general purpose VM anyway. A CLR backend is possible - but would require much work. + JVM/CLR support is not in the nearest plans. However, since these VMs support FFI to C + it should be possible to create native Nim bridges, that transparenlty generate all the + glue code thanks to powerful metaprogramming capabilities of Nim. .. container:: standout diff --git a/web/website.ini b/web/website.ini index c160d624d..dcfea8bf4 100644 --- a/web/website.ini +++ b/web/website.ini @@ -9,7 +9,7 @@ Authors: "Andreas Rumpf and contributors" # Everything after ; is the ID Community: "community.html;link_forum" Aporia_IDE: "https://github.com/nim-lang/Aporia;link_aporia" -Github_Repo: "http://github.com/Araq/Nim;link_github" +GitHub_Repo: "http://github.com/Araq/Nim;link_github" [Tabs] |