diff options
35 files changed, 1236 insertions, 219 deletions
diff --git a/compiler/ast.nim b/compiler/ast.nim index 668f20bcd..167f58d0b 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -539,22 +539,21 @@ type mIncl, mExcl, mCard, mChr, mGCref, mGCunref, - mAddI, mSubI, mMulI, mDivI, mModI, mAddI64, mSubI64, mMulI64, - mDivI64, mModI64, mSucc, mPred, + mAddI, mSubI, mMulI, mDivI, mModI, + mSucc, mPred, mAddF64, mSubF64, mMulF64, mDivF64, mShrI, mShlI, mBitandI, mBitorI, mBitxorI, mMinI, mMaxI, - mShrI64, mShlI64, mBitandI64, mBitorI64, mBitxorI64, mMinF64, mMaxF64, mAddU, mSubU, mMulU, mDivU, mModU, mEqI, mLeI, mLtI, - mEqI64, mLeI64, mLtI64, mEqF64, mLeF64, mLtF64, + mEqF64, mLeF64, mLtF64, mLeU, mLtU, mLeU64, mLtU64, mEqEnum, mLeEnum, mLtEnum, mEqCh, mLeCh, mLtCh, mEqB, mLeB, mLtB, mEqRef, mEqUntracedRef, mLePtr, mLtPtr, mEqCString, mXor, mEqProc, mUnaryMinusI, - mUnaryMinusI64, mAbsI, mAbsI64, mNot, + mUnaryMinusI64, mAbsI, mNot, mUnaryPlusI, mBitnotI, - mBitnotI64, mUnaryPlusF64, mUnaryMinusF64, mAbsF64, mZe8ToI, mZe8ToI64, + mUnaryPlusF64, mUnaryMinusF64, mAbsF64, mZe8ToI, mZe8ToI64, mZe16ToI, mZe16ToI64, mZe32ToI64, mZeIToI64, mToU8, mToU16, mToU32, mToFloat, mToBiggestFloat, mToInt, mToBiggestInt, mCharToStr, mBoolToStr, mIntToStr, mInt64ToStr, mFloatToStr, mCStrToStr, mStrToStr, mEnumToStr, @@ -594,20 +593,19 @@ const mPred, mInc, mDec, mOrd, mLengthOpenArray, mLengthStr, mLengthArray, mLengthSeq, mXLenStr, mXLenSeq, mIncl, mExcl, mCard, mChr, - mAddI, mSubI, mMulI, mDivI, mModI, mAddI64, mSubI64, mMulI64, - mDivI64, mModI64, mAddF64, mSubF64, mMulF64, mDivF64, + mAddI, mSubI, mMulI, mDivI, mModI, + mAddF64, mSubF64, mMulF64, mDivF64, mShrI, mShlI, mBitandI, mBitorI, mBitxorI, mMinI, mMaxI, - mShrI64, mShlI64, mBitandI64, mBitorI64, mBitxorI64, mMinF64, mMaxF64, mAddU, mSubU, mMulU, mDivU, mModU, mEqI, mLeI, mLtI, - mEqI64, mLeI64, mLtI64, mEqF64, mLeF64, mLtF64, + mEqF64, mLeF64, mLtF64, mLeU, mLtU, mLeU64, mLtU64, mEqEnum, mLeEnum, mLtEnum, mEqCh, mLeCh, mLtCh, mEqB, mLeB, mLtB, mEqRef, mEqProc, mEqUntracedRef, mLePtr, mLtPtr, mEqCString, mXor, mUnaryMinusI, - mUnaryMinusI64, mAbsI, mAbsI64, mNot, + mUnaryMinusI64, mAbsI, mNot, mUnaryPlusI, mBitnotI, - mBitnotI64, mUnaryPlusF64, mUnaryMinusF64, mAbsF64, mZe8ToI, mZe8ToI64, + mUnaryPlusF64, mUnaryMinusF64, mAbsF64, mZe8ToI, mZe8ToI64, mZe16ToI, mZe16ToI64, mZe32ToI64, mZeIToI64, mToU8, mToU16, mToU32, mToFloat, mToBiggestFloat, mToInt, mToBiggestInt, mCharToStr, mBoolToStr, mIntToStr, mInt64ToStr, mFloatToStr, mCStrToStr, mStrToStr, mEnumToStr, diff --git a/compiler/ccgexprs.nim b/compiler/ccgexprs.nim index 64902c3fc..862776740 100644 --- a/compiler/ccgexprs.nim +++ b/compiler/ccgexprs.nim @@ -503,15 +503,10 @@ proc binaryArithOverflow(p: BProc, e: PNode, d: var TLoc, m: TMagic) = "$# = #addInt($#, $#);$n", "$# = #subInt($#, $#);$n", "$# = #mulInt($#, $#);$n", "$# = #divInt($#, $#);$n", "$# = #modInt($#, $#);$n", - "$# = #addInt64($#, $#);$n", "$# = #subInt64($#, $#);$n", - "$# = #mulInt64($#, $#);$n", "$# = #divInt64($#, $#);$n", - "$# = #modInt64($#, $#);$n", "$# = #addInt($#, $#);$n", "$# = #subInt($#, $#);$n"] opr: array[mAddI..mPred, string] = [ "($#)($# + $#)", "($#)($# - $#)", "($#)($# * $#)", "($#)($# / $#)", "($#)($# % $#)", - "($#)($# + $#)", "($#)($# - $#)", "($#)($# * $#)", - "($#)($# / $#)", "($#)($# % $#)", "($#)($# + $#)", "($#)($# - $#)"] var a, b: TLoc assert(e.sons[1].typ != nil) @@ -530,11 +525,10 @@ proc binaryArithOverflow(p: BProc, e: PNode, d: var TLoc, m: TMagic) = proc unaryArithOverflow(p: BProc, e: PNode, d: var TLoc, m: TMagic) = const - opr: array[mUnaryMinusI..mAbsI64, string] = [ + opr: array[mUnaryMinusI..mAbsI, string] = [ mUnaryMinusI: "((NI$2)-($1))", mUnaryMinusI64: "-($1)", - mAbsI: "($1 > 0? ($1) : -($1))", - mAbsI64: "($1 > 0? ($1) : -($1))"] + mAbsI: "($1 > 0? ($1) : -($1))"] var a: TLoc t: PType @@ -561,11 +555,6 @@ proc binaryArith(p: BProc, e: PNode, d: var TLoc, op: TMagic) = "($4)($1 ^ $2)", # BitxorI "(($1 <= $2) ? $1 : $2)", # MinI "(($1 >= $2) ? $1 : $2)", # MaxI - "($4)((NU64)($1) >> (NU64)($2))", # ShrI64 - "($4)((NU64)($1) << (NU64)($2))", # ShlI64 - "($4)($1 & $2)", # BitandI64 - "($4)($1 | $2)", # BitorI64 - "($4)($1 ^ $2)", # BitxorI64 "(($1 <= $2) ? $1 : $2)", # MinF64 "(($1 >= $2) ? $1 : $2)", # MaxF64 "($4)((NU$3)($1) + (NU$3)($2))", # AddU @@ -576,9 +565,6 @@ proc binaryArith(p: BProc, e: PNode, d: var TLoc, op: TMagic) = "($1 == $2)", # EqI "($1 <= $2)", # LeI "($1 < $2)", # LtI - "($1 == $2)", # EqI64 - "($1 <= $2)", # LeI64 - "($1 < $2)", # LtI64 "($1 == $2)", # EqF64 "($1 <= $2)", # LeF64 "($1 < $2)", # LtF64 @@ -638,7 +624,6 @@ proc unaryArith(p: BProc, e: PNode, d: var TLoc, op: TMagic) = unArithTab: array[mNot..mToBiggestInt, string] = ["!($1)", # Not "$1", # UnaryPlusI "($3)((NU$2) ~($1))", # BitnotI - "($3)((NU$2) ~($1))", # BitnotI64 "$1", # UnaryPlusF64 "-($1)", # UnaryMinusF64 "($1 > 0? ($1) : -($1))", # AbsF64; BUGFIX: fabs() makes problems @@ -1661,7 +1646,7 @@ proc genMagicExpr(p: BProc, e: PNode, d: var TLoc, op: TMagic) = case op of mOr, mAnd: genAndOr(p, e, d, op) of mNot..mToBiggestInt: unaryArith(p, e, d, op) - of mUnaryMinusI..mAbsI64: unaryArithOverflow(p, e, d, op) + of mUnaryMinusI..mAbsI: unaryArithOverflow(p, e, d, op) of mAddF64..mDivF64: binaryFloatArith(p, e, d, op) of mShrI..mXor: binaryArith(p, e, d, op) of mEqProc: genEqProc(p, e, d) diff --git a/compiler/forloops.nim b/compiler/forloops.nim index efe000968..949b7d8c6 100644 --- a/compiler/forloops.nim +++ b/compiler/forloops.nim @@ -12,9 +12,9 @@ import ast, astalgo const - someCmp = {mEqI, mEqI64, mEqF64, mEqEnum, mEqCh, mEqB, mEqRef, mEqProc, - mEqUntracedRef, mLeI, mLeI64, mLeF64, mLeU, mLeU64, mLeEnum, - mLeCh, mLeB, mLePtr, mLtI, mLtI64, mLtF64, mLtU, mLtU64, mLtEnum, + someCmp = {mEqI, mEqF64, mEqEnum, mEqCh, mEqB, mEqRef, mEqProc, + mEqUntracedRef, mLeI, mLeF64, mLeU, mLeU64, mLeEnum, + mLeCh, mLeB, mLePtr, mLtI, mLtF64, mLtU, mLtU64, mLtEnum, mLtCh, mLtB, mLtPtr} proc isCounter(s: PSym): bool {.inline.} = diff --git a/compiler/guards.nim b/compiler/guards.nim index df2c1dd75..bc802ae33 100644 --- a/compiler/guards.nim +++ b/compiler/guards.nim @@ -13,13 +13,13 @@ import ast, astalgo, msgs, magicsys, nimsets, trees, types, renderer, idents, saturate const - someEq = {mEqI, mEqI64, mEqF64, mEqEnum, mEqCh, mEqB, mEqRef, mEqProc, + someEq = {mEqI, mEqF64, mEqEnum, mEqCh, mEqB, mEqRef, mEqProc, mEqUntracedRef, mEqStr, mEqSet, mEqCString} # set excluded here as the semantics are vastly different: - someLe = {mLeI, mLeI64, mLeF64, mLeU, mLeU64, mLeEnum, + someLe = {mLeI, mLeF64, mLeU, mLeU64, mLeEnum, mLeCh, mLeB, mLePtr, mLeStr} - someLt = {mLtI, mLtI64, mLtF64, mLtU, mLtU64, mLtEnum, + someLt = {mLtI, mLtF64, mLtU, mLtU64, mLtEnum, mLtCh, mLtB, mLtPtr, mLtStr} someLen = {mLengthOpenArray, mLengthStr, mLengthArray, mLengthSeq, @@ -30,11 +30,11 @@ const someHigh = {mHigh} # we don't list unsigned here because wrap around semantics suck for # proving anything: - someAdd = {mAddI, mAddI64, mAddF64, mSucc} - someSub = {mSubI, mSubI64, mSubF64, mPred} - someMul = {mMulI, mMulI64, mMulF64} - someDiv = {mDivI, mDivI64, mDivF64} - someMod = {mModI, mModI64} + someAdd = {mAddI, mAddF64, mSucc} + someSub = {mSubI, mSubF64, mPred} + someMul = {mMulI, mMulF64} + someDiv = {mDivI, mDivF64} + someMod = {mModI} someMax = {mMaxI, mMaxF64} someMin = {mMinI, mMinF64} diff --git a/compiler/jsgen.nim b/compiler/jsgen.nim index 1f82306d2..1fd9aa937 100644 --- a/compiler/jsgen.nim +++ b/compiler/jsgen.nim @@ -258,11 +258,6 @@ const # magic checked op; magic unchecked op; checked op; unchecked op ["mulInt", "", "mulInt($1, $2)", "($1 * $2)"], # MulI ["divInt", "", "divInt($1, $2)", "Math.floor($1 / $2)"], # DivI ["modInt", "", "modInt($1, $2)", "Math.floor($1 % $2)"], # ModI - ["addInt64", "", "addInt64($1, $2)", "($1 + $2)"], # AddI64 - ["subInt64", "", "subInt64($1, $2)", "($1 - $2)"], # SubI64 - ["mulInt64", "", "mulInt64($1, $2)", "($1 * $2)"], # MulI64 - ["divInt64", "", "divInt64($1, $2)", "Math.floor($1 / $2)"], # DivI64 - ["modInt64", "", "modInt64($1, $2)", "Math.floor($1 % $2)"], # ModI64 ["addInt", "", "addInt($1, $2)", "($1 + $2)"], # Succ ["subInt", "", "subInt($1, $2)", "($1 - $2)"], # Pred ["", "", "($1 + $2)", "($1 + $2)"], # AddF64 @@ -276,11 +271,6 @@ const # magic checked op; magic unchecked op; checked op; unchecked op ["", "", "($1 ^ $2)", "($1 ^ $2)"], # BitxorI ["nimMin", "nimMin", "nimMin($1, $2)", "nimMin($1, $2)"], # MinI ["nimMax", "nimMax", "nimMax($1, $2)", "nimMax($1, $2)"], # MaxI - ["", "", "($1 >>> $2)", "($1 >>> $2)"], # ShrI64 - ["", "", "($1 << $2)", "($1 << $2)"], # ShlI64 - ["", "", "($1 & $2)", "($1 & $2)"], # BitandI64 - ["", "", "($1 | $2)", "($1 | $2)"], # BitorI64 - ["", "", "($1 ^ $2)", "($1 ^ $2)"], # BitxorI64 ["nimMin", "nimMin", "nimMin($1, $2)", "nimMin($1, $2)"], # MinF64 ["nimMax", "nimMax", "nimMax($1, $2)", "nimMax($1, $2)"], # MaxF64 ["addU", "addU", "addU($1, $2)", "addU($1, $2)"], # addU @@ -291,9 +281,6 @@ const # magic checked op; magic unchecked op; checked op; unchecked op ["", "", "($1 == $2)", "($1 == $2)"], # EqI ["", "", "($1 <= $2)", "($1 <= $2)"], # LeI ["", "", "($1 < $2)", "($1 < $2)"], # LtI - ["", "", "($1 == $2)", "($1 == $2)"], # EqI64 - ["", "", "($1 <= $2)", "($1 <= $2)"], # LeI64 - ["", "", "($1 < $2)", "($1 < $2)"], # LtI64 ["", "", "($1 == $2)", "($1 == $2)"], # EqF64 ["", "", "($1 <= $2)", "($1 <= $2)"], # LeF64 ["", "", "($1 < $2)", "($1 < $2)"], # LtF64 @@ -320,11 +307,9 @@ const # magic checked op; magic unchecked op; checked op; unchecked op ["negInt", "", "negInt($1)", "-($1)"], # UnaryMinusI ["negInt64", "", "negInt64($1)", "-($1)"], # UnaryMinusI64 ["absInt", "", "absInt($1)", "Math.abs($1)"], # AbsI - ["absInt64", "", "absInt64($1)", "Math.abs($1)"], # AbsI64 ["", "", "!($1)", "!($1)"], # Not ["", "", "+($1)", "+($1)"], # UnaryPlusI ["", "", "~($1)", "~($1)"], # BitnotI - ["", "", "~($1)", "~($1)"], # BitnotI64 ["", "", "+($1)", "+($1)"], # UnaryPlusF64 ["", "", "-($1)", "-($1)"], # UnaryMinusF64 ["", "", "Math.abs($1)", "Math.abs($1)"], # AbsF64 @@ -357,11 +342,6 @@ const # magic checked op; magic unchecked op; checked op; unchecked op ["mulInt", "", "mulInt($1, $2)", "($1 * $2)"], # MulI ["divInt", "", "divInt($1, $2)", "Math.floor($1 / $2)"], # DivI ["modInt", "", "modInt($1, $2)", "Math.floor($1 % $2)"], # ModI - ["addInt64", "", "addInt64($1, $2)", "($1 + $2)"], # AddI64 - ["subInt64", "", "subInt64($1, $2)", "($1 - $2)"], # SubI64 - ["mulInt64", "", "mulInt64($1, $2)", "($1 * $2)"], # MulI64 - ["divInt64", "", "divInt64($1, $2)", "Math.floor($1 / $2)"], # DivI64 - ["modInt64", "", "modInt64($1, $2)", "Math.floor($1 % $2)"], # ModI64 ["addInt", "", "addInt($1, $2)", "($1 + $2)"], # Succ ["subInt", "", "subInt($1, $2)", "($1 - $2)"], # Pred ["", "", "($1 + $2)", "($1 + $2)"], # AddF64 @@ -375,11 +355,6 @@ const # magic checked op; magic unchecked op; checked op; unchecked op ["", "", "($1 ^ $2)", "($1 ^ $2)"], # BitxorI ["nimMin", "nimMin", "nimMin($1, $2)", "nimMin($1, $2)"], # MinI ["nimMax", "nimMax", "nimMax($1, $2)", "nimMax($1, $2)"], # MaxI - ["", "", "($1 >>> $2)", "($1 >>> $2)"], # ShrI64 - ["", "", "($1 << $2)", "($1 << $2)"], # ShlI64 - ["", "", "($1 & $2)", "($1 & $2)"], # BitandI64 - ["", "", "($1 | $2)", "($1 | $2)"], # BitorI64 - ["", "", "($1 ^ $2)", "($1 ^ $2)"], # BitxorI64 ["nimMin", "nimMin", "nimMin($1, $2)", "nimMin($1, $2)"], # MinF64 ["nimMax", "nimMax", "nimMax($1, $2)", "nimMax($1, $2)"], # MaxF64 ["addU", "addU", "addU($1, $2)", "addU($1, $2)"], # addU @@ -390,9 +365,6 @@ const # magic checked op; magic unchecked op; checked op; unchecked op ["", "", "($1 == $2)", "($1 == $2)"], # EqI ["", "", "($1 <= $2)", "($1 <= $2)"], # LeI ["", "", "($1 < $2)", "($1 < $2)"], # LtI - ["", "", "($1 == $2)", "($1 == $2)"], # EqI64 - ["", "", "($1 <= $2)", "($1 <= $2)"], # LeI64 - ["", "", "($1 < $2)", "($1 < $2)"], # LtI64 ["", "", "($1 == $2)", "($1 == $2)"], # EqF64 ["", "", "($1 <= $2)", "($1 <= $2)"], # LeF64 ["", "", "($1 < $2)", "($1 < $2)"], # LtF64 @@ -419,11 +391,9 @@ const # magic checked op; magic unchecked op; checked op; unchecked op ["negInt", "", "negInt($1)", "-($1)"], # UnaryMinusI ["negInt64", "", "negInt64($1)", "-($1)"], # UnaryMinusI64 ["absInt", "", "absInt($1)", "Math.abs($1)"], # AbsI - ["absInt64", "", "absInt64($1)", "Math.abs($1)"], # AbsI64 ["", "", "not ($1)", "not ($1)"], # Not ["", "", "+($1)", "+($1)"], # UnaryPlusI ["", "", "~($1)", "~($1)"], # BitnotI - ["", "", "~($1)", "~($1)"], # BitnotI64 ["", "", "+($1)", "+($1)"], # UnaryPlusF64 ["", "", "-($1)", "-($1)"], # UnaryMinusF64 ["", "", "Math.abs($1)", "Math.abs($1)"], # AbsF64 @@ -817,7 +787,7 @@ proc genAsgnAux(p: PProc, x, y: PNode, noCopyNeeded: bool) = addf(p.body, "$1 = $2;$n", [a.rdLoc, b.rdLoc]) else: useMagic(p, "nimCopy") - addf(p.body, "$1 = nimCopy($2, $3);$n", + addf(p.body, "$1 = nimCopy($1, $2, $3);$n", [a.res, b.res, genTypeInfo(p, y.typ)]) of etyBaseIndex: if a.typ != etyBaseIndex or b.typ != etyBaseIndex: @@ -1206,7 +1176,7 @@ proc genVarInit(p: PProc, v: PSym, n: PNode) = s = a.res else: useMagic(p, "nimCopy") - s = "nimCopy($1, $2)" % [a.res, genTypeInfo(p, n.typ)] + s = "nimCopy(null, $1, $2)" % [a.res, genTypeInfo(p, n.typ)] of etyBaseIndex: if (a.typ != etyBaseIndex): internalError(n.info, "genVarInit") if {sfAddrTaken, sfGlobal} * v.flags != {}: diff --git a/compiler/nodejs.nim b/compiler/nodejs.nim index e2b79df19..7f9f28aaf 100644 --- a/compiler/nodejs.nim +++ b/compiler/nodejs.nim @@ -4,3 +4,5 @@ proc findNodeJs*(): string = result = findExe("nodejs") if result == "": result = findExe("node") + if result == "": + result = findExe("iojs") diff --git a/compiler/semfold.nim b/compiler/semfold.nim index da24005c2..b83641706 100644 --- a/compiler/semfold.nim +++ b/compiler/semfold.nim @@ -179,7 +179,7 @@ proc getIntervalType*(m: TMagic, n: PNode): PType = else: result = makeRangeF(a, abs(getFloat(a.n.sons[1])), abs(getFloat(a.n.sons[0]))) - of mAbsI, mAbsI64: + of mAbsI: let a = n.sons[1].typ if isIntRange(a): if a.n[0].intVal <= 0: @@ -200,13 +200,13 @@ proc getIntervalType*(m: TMagic, n: PNode): PType = if isIntRange(a) and isIntLit(b): result = makeRange(a, pickMinInt(n.sons[1]) |-| pickMinInt(n.sons[2]), pickMaxInt(n.sons[1]) |-| pickMaxInt(n.sons[2])) - of mAddI, mAddI64, mAddU: + of mAddI, mAddU: commutativeOp(`|+|`) - of mMulI, mMulI64, mMulU: + of mMulI, mMulU: commutativeOp(`|*|`) - of mSubI, mSubI64, mSubU: + of mSubI, mSubU: binaryOp(`|-|`) - of mBitandI, mBitandI64: + of mBitandI: # since uint64 is still not even valid for 'range' (since it's no ordinal # yet), we exclude it from the list (see bug #1638) for now: var a = n.sons[1] @@ -225,7 +225,7 @@ proc getIntervalType*(m: TMagic, n: PNode): PType = result = makeRange(a.typ, 0, b.intVal-1) else: result = makeRange(a.typ, b.intVal+1, 0) - of mModI, mModI64: + of mModI: # so ... if you ever wondered about modulo's signedness; this defines it: let a = n.sons[1] let b = n.sons[2] @@ -234,7 +234,7 @@ proc getIntervalType*(m: TMagic, n: PNode): PType = result = makeRange(a.typ, -(b.intVal-1), b.intVal-1) else: result = makeRange(a.typ, b.intVal+1, -(b.intVal+1)) - of mDivI, mDivI64, mDivU: + of mDivI, mDivU: binaryOp(`|div|`) of mMinI: commutativeOp(min) @@ -243,8 +243,8 @@ proc getIntervalType*(m: TMagic, n: PNode): PType = else: discard discard """ - mShlI, mShlI64, - mShrI, mShrI64, mAddF64, mSubF64, mMulF64, mDivF64, mMaxF64, mMinF64 + mShlI, + mShrI, mAddF64, mSubF64, mMulF64, mDivF64, mMaxF64, mMinF64 """ proc evalIs(n, a: PNode): PNode = @@ -285,7 +285,7 @@ proc evalOp(m: TMagic, n, a, b, c: PNode): PNode = of mUnaryMinusF64: result = newFloatNodeT(- getFloat(a), n) of mNot: result = newIntNodeT(1 - getInt(a), n) of mCard: result = newIntNodeT(nimsets.cardSet(a), n) - of mBitnotI, mBitnotI64: result = newIntNodeT(not getInt(a), n) + of mBitnotI: result = newIntNodeT(not getInt(a), n) of mLengthStr, mXLenStr: if a.kind == nkNilLit: result = newIntNodeT(0, n) else: result = newIntNodeT(len(getStr(a)), n) @@ -298,7 +298,7 @@ proc evalOp(m: TMagic, n, a, b, c: PNode): PNode = result = newFloatNodeT(toFloat(int(getInt(a))), n) of mToInt, mToBiggestInt: result = newIntNodeT(system.toInt(getFloat(a)), n) of mAbsF64: result = newFloatNodeT(abs(getFloat(a)), n) - of mAbsI, mAbsI64: + of mAbsI: if getInt(a) >= 0: result = a else: result = newIntNodeT(- getInt(a), n) of mZe8ToI, mZe8ToI64, mZe16ToI, mZe16ToI64, mZe32ToI64, mZeIToI64: @@ -310,16 +310,16 @@ proc evalOp(m: TMagic, n, a, b, c: PNode): PNode = of mUnaryLt: result = newIntNodeT(getOrdValue(a) - 1, n) of mSucc: result = newIntNodeT(getOrdValue(a) + getInt(b), n) of mPred: result = newIntNodeT(getOrdValue(a) - getInt(b), n) - of mAddI, mAddI64: result = newIntNodeT(getInt(a) + getInt(b), n) - of mSubI, mSubI64: result = newIntNodeT(getInt(a) - getInt(b), n) - of mMulI, mMulI64: result = newIntNodeT(getInt(a) * getInt(b), n) + of mAddI: result = newIntNodeT(getInt(a) + getInt(b), n) + of mSubI: result = newIntNodeT(getInt(a) - getInt(b), n) + of mMulI: result = newIntNodeT(getInt(a) * getInt(b), n) of mMinI: if getInt(a) > getInt(b): result = newIntNodeT(getInt(b), n) else: result = newIntNodeT(getInt(a), n) of mMaxI: if getInt(a) > getInt(b): result = newIntNodeT(getInt(a), n) else: result = newIntNodeT(getInt(b), n) - of mShlI, mShlI64: + of mShlI: case skipTypes(n.typ, abstractRange).kind of tyInt8: result = newIntNodeT(int8(getInt(a)) shl int8(getInt(b)), n) of tyInt16: result = newIntNodeT(int16(getInt(a)) shl int16(getInt(b)), n) @@ -327,7 +327,7 @@ proc evalOp(m: TMagic, n, a, b, c: PNode): PNode = of tyInt64, tyInt, tyUInt..tyUInt64: result = newIntNodeT(`shl`(getInt(a), getInt(b)), n) else: internalError(n.info, "constant folding for shl") - of mShrI, mShrI64: + of mShrI: case skipTypes(n.typ, abstractRange).kind of tyInt8: result = newIntNodeT(int8(getInt(a)) shr int8(getInt(b)), n) of tyInt16: result = newIntNodeT(int16(getInt(a)) shr int16(getInt(b)), n) @@ -335,11 +335,11 @@ proc evalOp(m: TMagic, n, a, b, c: PNode): PNode = of tyInt64, tyInt, tyUInt..tyUInt64: result = newIntNodeT(`shr`(getInt(a), getInt(b)), n) else: internalError(n.info, "constant folding for shr") - of mDivI, mDivI64: + of mDivI: let y = getInt(b) if y != 0: result = newIntNodeT(getInt(a) div y, n) - of mModI, mModI64: + of mModI: let y = getInt(b) if y != 0: result = newIntNodeT(getInt(a) mod y, n) @@ -359,11 +359,11 @@ proc evalOp(m: TMagic, n, a, b, c: PNode): PNode = if getFloat(a) > getFloat(b): result = newFloatNodeT(getFloat(b), n) else: result = newFloatNodeT(getFloat(a), n) of mIsNil: result = newIntNodeT(ord(a.kind == nkNilLit), n) - of mLtI, mLtI64, mLtB, mLtEnum, mLtCh: + of mLtI, mLtB, mLtEnum, mLtCh: result = newIntNodeT(ord(getOrdValue(a) < getOrdValue(b)), n) - of mLeI, mLeI64, mLeB, mLeEnum, mLeCh: + of mLeI, mLeB, mLeEnum, mLeCh: result = newIntNodeT(ord(getOrdValue(a) <= getOrdValue(b)), n) - of mEqI, mEqI64, mEqB, mEqEnum, mEqCh: + of mEqI, mEqB, mEqEnum, mEqCh: result = newIntNodeT(ord(getOrdValue(a) == getOrdValue(b)), n) of mLtF64: result = newIntNodeT(ord(getFloat(a) < getFloat(b)), n) of mLeF64: result = newIntNodeT(ord(getFloat(a) <= getFloat(b)), n) @@ -375,9 +375,9 @@ proc evalOp(m: TMagic, n, a, b, c: PNode): PNode = result = newIntNodeT(ord(`<%`(getOrdValue(a), getOrdValue(b))), n) of mLeU, mLeU64: result = newIntNodeT(ord(`<=%`(getOrdValue(a), getOrdValue(b))), n) - of mBitandI, mBitandI64, mAnd: result = newIntNodeT(a.getInt and b.getInt, n) - of mBitorI, mBitorI64, mOr: result = newIntNodeT(getInt(a) or getInt(b), n) - of mBitxorI, mBitxorI64, mXor: result = newIntNodeT(a.getInt xor b.getInt, n) + of mBitandI, mAnd: result = newIntNodeT(a.getInt and b.getInt, n) + of mBitorI, mOr: result = newIntNodeT(getInt(a) or getInt(b), n) + of mBitxorI, mXor: result = newIntNodeT(a.getInt xor b.getInt, n) of mAddU: result = newIntNodeT(`+%`(getInt(a), getInt(b)), n) of mSubU: result = newIntNodeT(`-%`(getInt(a), getInt(b)), n) of mMulU: result = newIntNodeT(`*%`(getInt(a), getInt(b)), n) diff --git a/compiler/vmgen.nim b/compiler/vmgen.nim index 0743a4502..c68282fde 100644 --- a/compiler/vmgen.nim +++ b/compiler/vmgen.nim @@ -710,9 +710,9 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest; m: TMagic) = if dest < 0: dest = c.getTemp(n.typ) c.gABI(n, opcSubImmInt, dest, tmp, 1) c.freeTemp(tmp) - of mPred, mSubI, mSubI64: + of mPred, mSubI: c.genAddSubInt(n, dest, opcSubInt) - of mSucc, mAddI, mAddI64: + of mSucc, mAddI: c.genAddSubInt(n, dest, opcAddInt) of mInc, mDec: unused(n, dest) @@ -759,28 +759,28 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest; m: TMagic) = c.freeTemp(d) c.freeTemp(tmp) of mCard: genCard(c, n, dest) - of mMulI, mMulI64: genBinaryABCnarrow(c, n, dest, opcMulInt) - of mDivI, mDivI64: genBinaryABCnarrow(c, n, dest, opcDivInt) - of mModI, mModI64: genBinaryABCnarrow(c, n, dest, opcModInt) + of mMulI: genBinaryABCnarrow(c, n, dest, opcMulInt) + of mDivI: genBinaryABCnarrow(c, n, dest, opcDivInt) + of mModI: genBinaryABCnarrow(c, n, dest, opcModInt) of mAddF64: genBinaryABC(c, n, dest, opcAddFloat) of mSubF64: genBinaryABC(c, n, dest, opcSubFloat) of mMulF64: genBinaryABC(c, n, dest, opcMulFloat) of mDivF64: genBinaryABC(c, n, dest, opcDivFloat) - of mShrI, mShrI64: genBinaryABCnarrowU(c, n, dest, opcShrInt) - of mShlI, mShlI64: genBinaryABCnarrowU(c, n, dest, opcShlInt) - of mBitandI, mBitandI64: genBinaryABCnarrowU(c, n, dest, opcBitandInt) - of mBitorI, mBitorI64: genBinaryABCnarrowU(c, n, dest, opcBitorInt) - of mBitxorI, mBitxorI64: genBinaryABCnarrowU(c, n, dest, opcBitxorInt) + of mShrI: genBinaryABCnarrowU(c, n, dest, opcShrInt) + of mShlI: genBinaryABCnarrowU(c, n, dest, opcShlInt) + of mBitandI: genBinaryABCnarrowU(c, n, dest, opcBitandInt) + of mBitorI: genBinaryABCnarrowU(c, n, dest, opcBitorInt) + of mBitxorI: genBinaryABCnarrowU(c, n, dest, opcBitxorInt) of mAddU: genBinaryABCnarrowU(c, n, dest, opcAddu) of mSubU: genBinaryABCnarrowU(c, n, dest, opcSubu) of mMulU: genBinaryABCnarrowU(c, n, dest, opcMulu) of mDivU: genBinaryABCnarrowU(c, n, dest, opcDivu) of mModU: genBinaryABCnarrowU(c, n, dest, opcModu) - of mEqI, mEqI64, mEqB, mEqEnum, mEqCh: + of mEqI, mEqB, mEqEnum, mEqCh: genBinaryABC(c, n, dest, opcEqInt) - of mLeI, mLeI64, mLeEnum, mLeCh, mLeB: + of mLeI, mLeEnum, mLeCh, mLeB: genBinaryABC(c, n, dest, opcLeInt) - of mLtI, mLtI64, mLtEnum, mLtCh, mLtB: + of mLtI, mLtEnum, mLtCh, mLtB: genBinaryABC(c, n, dest, opcLtInt) of mEqF64: genBinaryABC(c, n, dest, opcEqFloat) of mLeF64: genBinaryABC(c, n, dest, opcLeFloat) @@ -796,7 +796,7 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest; m: TMagic) = genNarrow(c, n, dest) of mUnaryMinusF64: genUnaryABC(c, n, dest, opcUnaryMinusFloat) of mUnaryPlusI, mUnaryPlusF64: gen(c, n.sons[1], dest) - of mBitnotI, mBitnotI64: + of mBitnotI: genUnaryABC(c, n, dest, opcBitnotInt) genNarrowU(c, n, dest) of mZe8ToI, mZe8ToI64, mZe16ToI, mZe16ToI64, mZe32ToI64, mZeIToI64, @@ -1013,7 +1013,7 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest; m: TMagic) = c.gABC(n, opcCallSite, dest) of mNGenSym: genBinaryABC(c, n, dest, opcGenSym) of mMinI, mMaxI, mAbsF64, mMinF64, mMaxF64, mAbsI, - mAbsI64, mDotDot: + mDotDot: c.genCall(n, dest) of mExpandToAst: if n.len != 2: diff --git a/koch.nim b/koch.nim index fc292401a..83f363b1a 100644 --- a/koch.nim +++ b/koch.nim @@ -333,6 +333,9 @@ proc tests(args: string) = # we compile the tester with taintMode:on to have a basic # taint mode test :-) exec "nim cc --taintMode:on tests/testament/tester" + # Since tests take a long time (on my machine), and we want to defy Murhpys + # law - lets make sure the compiler really is freshly compiled! + exec "nim c --lib:lib -d:release --opt:speed compiler/nim.nim" let tester = quoteShell(getCurrentDir() / "tests/testament/tester".exe) let success = tryExec tester & " " & (args|"all") exec tester & " html" diff --git a/lib/impure/nre.nim b/lib/impure/nre.nim new file mode 100644 index 000000000..6f92b0d71 --- /dev/null +++ b/lib/impure/nre.nim @@ -0,0 +1,682 @@ +# +# Nim's Runtime Library +# (c) Copyright 2015 Nim Contributers +# +# See the file "copying.txt", included in this +# distribution, for details about the copyright. +# + + +from pcre import nil +import nre.private.util +import tables +import unsigned +from strutils import toLower, `%` +from math import ceil +import options +from unicode import runeLenAt + + +## What is NRE? +## ============ +## +## A regular expression library for Nim using PCRE to do the hard work. +## +## Licencing +## --------- +## +## PCRE has some additional terms that you must comply with if you use this module.:: +## +## > Copyright (c) 1997-2001 University of Cambridge +## > +## > Permission is granted to anyone to use this software for any purpose on any +## > computer system, and to redistribute it freely, subject to the following +## > restrictions: +## > +## > 1. This software is distributed in the hope that it will be useful, +## > but WITHOUT ANY WARRANTY; without even the implied warranty of +## > MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +## > +## > 2. The origin of this software must not be misrepresented, either by +## > explicit claim or by omission. In practice, this means that if you use +## > PCRE in software that you distribute to others, commercially or +## > otherwise, you must put a sentence like this +## > +## > Regular expression support is provided by the PCRE library package, +## > which is open source software, written by Philip Hazel, and copyright +## > by the University of Cambridge, England. +## > +## > somewhere reasonably visible in your documentation and in any relevant +## > files or online help data or similar. A reference to the ftp site for +## > the source, that is, to +## > +## > ftp://ftp.csx.cam.ac.uk/pub/software/programming/pcre/ +## > +## > should also be given in the documentation. However, this condition is not +## > intended to apply to whole chains of software. If package A includes PCRE, +## > it must acknowledge it, but if package B is software that includes package +## > A, the condition is not imposed on package B (unless it uses PCRE +## > independently). +## > +## > 3. Altered versions must be plainly marked as such, and must not be +## > misrepresented as being the original software. +## > +## > 4. If PCRE is embedded in any software that is released under the GNU +## > General Purpose Licence (GPL), or Lesser General Purpose Licence (LGPL), +## > then the terms of that licence shall supersede any condition above with +## > which it is incompatible. + + +# Type definitions {{{ +type + Regex* = ref object + ## Represents the pattern that things are matched against, constructed with + ## ``re(string)``. Examples: ``re"foo"``, ``re(r"(*ANYCRLF)(?x)foo # + ## comment".`` + ## + ## ``pattern: string`` + ## the string that was used to create the pattern. + ## + ## ``captureCount: int`` + ## the number of captures that the pattern has. + ## + ## ``captureNameId: Table[string, int]`` + ## a table from the capture names to their numeric id. + ## + ## + ## Options + ## ....... + ## + ## The following options may appear anywhere in the pattern, and they affect + ## the rest of it. + ## + ## - ``(?i)`` - case insensitive + ## - ``(?m)`` - multi-line: ``^`` and ``$`` match the beginning and end of + ## lines, not of the subject string + ## - ``(?s)`` - ``.`` also matches newline (*dotall*) + ## - ``(?U)`` - expressions are not greedy by default. ``?`` can be added + ## to a qualifier to make it greedy + ## - ``(?x)`` - whitespace and comments (``#``) are ignored (*extended*) + ## - ``(?X)`` - character escapes without special meaning (``\w`` vs. + ## ``\a``) are errors (*extra*) + ## + ## One or a combination of these options may appear only at the beginning + ## of the pattern: + ## + ## - ``(*UTF8)`` - treat both the pattern and subject as UTF-8 + ## - ``(*UCP)`` - Unicode character properties; ``\w`` matches ``я`` + ## - ``(*U)`` - a combination of the two options above + ## - ``(*FIRSTLINE*)`` - fails if there is not a match on the first line + ## - ``(*NO_AUTO_CAPTURE)`` - turn off auto-capture for groups; + ## ``(?<name>...)`` can be used to capture + ## - ``(*CR)`` - newlines are separated by ``\r`` + ## - ``(*LF)`` - newlines are separated by ``\n`` (UNIX default) + ## - ``(*CRLF)`` - newlines are separated by ``\r\n`` (Windows default) + ## - ``(*ANYCRLF)`` - newlines are separated by any of the above + ## - ``(*ANY)`` - newlines are separated by any of the above and Unicode + ## newlines: + ## + ## single characters VT (vertical tab, U+000B), FF (form feed, U+000C), + ## NEL (next line, U+0085), LS (line separator, U+2028), and PS + ## (paragraph separator, U+2029). For the 8-bit library, the last two + ## are recognized only in UTF-8 mode. + ## — man pcre + ## + ## - ``(*JAVASCRIPT_COMPAT)`` - JavaScript compatibility + ## - ``(*NO_STUDY)`` - turn off studying; study is enabled by default + ## + ## For more details on the leading option groups, see the `Option + ## Setting <http://man7.org/linux/man-pages/man3/pcresyntax.3.html#OPTION_SETTING>`__ + ## and the `Newline + ## Convention <http://man7.org/linux/man-pages/man3/pcresyntax.3.html#NEWLINE_CONVENTION>`__ + ## sections of the `PCRE syntax + ## manual <http://man7.org/linux/man-pages/man3/pcresyntax.3.html>`__. + pattern*: string ## not nil + pcreObj: ptr pcre.Pcre ## not nil + pcreExtra: ptr pcre.ExtraData ## nil + + captureNameToId: Table[string, int] + + RegexMatch* = object + ## Usually seen as Option[RegexMatch], it represents the result of an + ## execution. On failure, it is none, on success, it is some. + ## + ## ``pattern: Regex`` + ## the pattern that is being matched + ## + ## ``str: string`` + ## the string that was matched against + ## + ## ``captures[]: string`` + ## the string value of whatever was captured at that id. If the value + ## is invalid, then behavior is undefined. If the id is ``-1``, then + ## the whole match is returned. If the given capture was not matched, + ## ``nil`` is returned. + ## + ## - ``"abc".match(re"(\w)").captures[0] == "a"`` + ## - ``"abc".match(re"(?<letter>\w)").captures["letter"] == "a"`` + ## - ``"abc".match(re"(\w)\w").captures[-1] == "ab"`` + ## + ## ``captureBounds[]: Option[Slice[int]]`` + ## gets the bounds of the given capture according to the same rules as + ## the above. If the capture is not filled, then ``None`` is returned. + ## The bounds are both inclusive. + ## + ## - ``"abc".match(re"(\w)").captureBounds[0] == 0 .. 0`` + ## - ``"abc".match(re"").captureBounds[-1] == 0 .. -1`` + ## - ``"abc".match(re"abc").captureBounds[-1] == 0 .. 2`` + ## + ## ``match: string`` + ## the full text of the match. + ## + ## ``matchBounds: Slice[int]`` + ## the bounds of the match, as in ``captureBounds[]`` + ## + ## ``(captureBounds|captures).toTable`` + ## returns a table with each named capture as a key. + ## + ## ``(captureBounds|captures).toSeq`` + ## returns all the captures by their number. + ## + ## ``$: string`` + ## same as ``match`` + pattern*: Regex ## The regex doing the matching. + ## Not nil. + str*: string ## The string that was matched against. + ## Not nil. + pcreMatchBounds: seq[Slice[cint]] ## First item is the bounds of the match + ## Other items are the captures + ## `a` is inclusive start, `b` is exclusive end + + Captures* = distinct RegexMatch + CaptureBounds* = distinct RegexMatch + + RegexError* = ref object of Exception + + RegexInternalError* = ref object of RegexError + ## Internal error in the module, this probably means that there is a bug + + InvalidUnicodeError* = ref object of RegexError + ## Thrown when matching fails due to invalid unicode in strings + pos*: int ## the location of the invalid unicode in bytes + + SyntaxError* = ref object of RegexError + ## Thrown when there is a syntax error in the + ## regular expression string passed in + pos*: int ## the location of the syntax error in bytes + pattern*: string ## the pattern that caused the problem + + StudyError* = ref object of RegexError + ## Thrown when studying the regular expression failes + ## for whatever reason. The message contains the error + ## code. +# }}} + +proc getinfo[T](pattern: Regex, opt: cint): T = + let retcode = pcre.fullinfo(pattern.pcreObj, pattern.pcreExtra, opt, addr result) + + if retcode < 0: + # XXX Error message that doesn't expose implementation details + raise newException(FieldError, "Invalid getinfo for $1, errno $2" % [$opt, $retcode]) + +# Regex accessors {{{ +proc captureCount*(pattern: Regex): int = + return getinfo[cint](pattern, pcre.INFO_CAPTURECOUNT) + +proc captureNameId*(pattern: Regex): Table[string, int] = + return pattern.captureNameToId + +proc matchesCrLf(pattern: Regex): bool = + let flags = uint32(getinfo[culong](pattern, pcre.INFO_OPTIONS)) + let newlineFlags = flags and (pcre.NEWLINE_CRLF or + pcre.NEWLINE_ANY or + pcre.NEWLINE_ANYCRLF) + if newLineFlags > 0u32: + return true + + # get flags from build config + var confFlags: cint + if pcre.config(pcre.CONFIG_NEWLINE, addr confFlags) != 0: + assert(false, "CONFIG_NEWLINE apparently got screwed up") + + case confFlags + of 13: return false + of 10: return false + of (13 shl 8) or 10: return true + of -2: return true + of -1: return true + else: return false +# }}} + +# Capture accessors {{{ +proc captureBounds*(pattern: RegexMatch): CaptureBounds = return CaptureBounds(pattern) + +proc captures*(pattern: RegexMatch): Captures = return Captures(pattern) + +proc `[]`*(pattern: CaptureBounds, i: int): Option[Slice[int]] = + let pattern = RegexMatch(pattern) + if pattern.pcreMatchBounds[i + 1].a != -1: + let bounds = pattern.pcreMatchBounds[i + 1] + return some(int(bounds.a) .. int(bounds.b-1)) + else: + return none(Slice[int]) + +proc `[]`*(pattern: Captures, i: int): string = + let pattern = RegexMatch(pattern) + let bounds = pattern.captureBounds[i] + + if bounds.isSome: + let bounds = bounds.get + return pattern.str.substr(bounds.a, bounds.b) + else: + return nil + +proc match*(pattern: RegexMatch): string = + return pattern.captures[-1] + +proc matchBounds*(pattern: RegexMatch): Slice[int] = + return pattern.captureBounds[-1].get + +proc `[]`*(pattern: CaptureBounds, name: string): Option[Slice[int]] = + let pattern = RegexMatch(pattern) + return pattern.captureBounds[pattern.pattern.captureNameToId.fget(name)] + +proc `[]`*(pattern: Captures, name: string): string = + let pattern = RegexMatch(pattern) + return pattern.captures[pattern.pattern.captureNameToId.fget(name)] + +template toTableImpl(cond: bool): stmt {.immediate, dirty.} = + for key in RegexMatch(pattern).pattern.captureNameId.keys: + let nextVal = pattern[key] + if cond: + result[key] = default + else: + result[key] = nextVal + +proc toTable*(pattern: Captures, default: string = nil): Table[string, string] = + result = initTable[string, string]() + toTableImpl(nextVal == nil) + +proc toTable*(pattern: CaptureBounds, default = none(Slice[int])): + Table[string, Option[Slice[int]]] = + result = initTable[string, Option[Slice[int]]]() + toTableImpl(nextVal.isNone) + +template itemsImpl(cond: bool): stmt {.immediate, dirty.} = + for i in 0 .. <RegexMatch(pattern).pattern.captureCount: + let nextVal = pattern[i] + # done in this roundabout way to avoid multiple yields (potential code + # bloat) + let nextYieldVal = if cond: default else: nextVal + yield nextYieldVal + + +iterator items*(pattern: CaptureBounds, default = none(Slice[int])): Option[Slice[int]] = + itemsImpl(nextVal.isNone) + +iterator items*(pattern: Captures, default: string = nil): string = + itemsImpl(nextVal == nil) + +proc toSeq*(pattern: CaptureBounds, default = none(Slice[int])): seq[Option[Slice[int]]] = + accumulateResult(pattern.items(default)) + +proc toSeq*(pattern: Captures, default: string = nil): seq[string] = + accumulateResult(pattern.items(default)) + +proc `$`*(pattern: RegexMatch): string = + return pattern.captures[-1] + +proc `==`*(a, b: Regex): bool = + if not a.isNil and not b.isNil: + return a.pattern == b.pattern and + a.pcreObj == b.pcreObj and + a.pcreExtra == b.pcreExtra + else: + return system.`==`(a, b) + +proc `==`*(a, b: RegexMatch): bool = + return a.pattern == b.pattern and + a.str == b.str +# }}} + +# Creation & Destruction {{{ +# PCRE Options {{{ +const PcreOptions = { + "NEVER_UTF": pcre.NEVER_UTF, + "ANCHORED": pcre.ANCHORED, + "DOLLAR_ENDONLY": pcre.DOLLAR_ENDONLY, + "FIRSTLINE": pcre.FIRSTLINE, + "NO_AUTO_CAPTURE": pcre.NO_AUTO_CAPTURE, + "JAVASCRIPT_COMPAT": pcre.JAVASCRIPT_COMPAT, + "U": pcre.UTF8 or pcre.UCP +}.toTable + +# Options that are supported inside regular expressions themselves +const SkipOptions = [ + "LIMIT_MATCH=", "LIMIT_RECURSION=", "NO_AUTO_POSSESS", "NO_START_OPT", + "UTF8", "UTF16", "UTF32", "UTF", "UCP", + "CR", "LF", "CRLF", "ANYCRLF", "ANY", "BSR_ANYCRLF", "BSR_UNICODE" +] + +proc extractOptions(pattern: string): tuple[pattern: string, flags: int, study: bool] = + result = ("", 0, true) + + var optionStart = 0 + var equals = false + for i, c in pattern: + if optionStart == i: + if c != '(': + break + optionStart = i + + elif optionStart == i-1: + if c != '*': + break + + elif c == ')': + let name = pattern[optionStart+2 .. i-1] + if equals or name in SkipOptions: + result.pattern.add pattern[optionStart .. i] + elif PcreOptions.hasKey name: + result.flags = result.flags or PcreOptions[name] + elif name == "NO_STUDY": + result.study = false + else: + break + optionStart = i+1 + equals = false + + elif not equals: + if c == '=': + equals = true + if pattern[optionStart+2 .. i] notin SkipOptions: + break + elif c notin {'A'..'Z', '0'..'9', '_'}: + break + + result.pattern.add pattern[optionStart .. pattern.high] + +# }}} + +type UncheckedArray {.unchecked.}[T] = array[0 .. 0, T] + +proc destroyRegex(pattern: Regex) = + pcre.free_substring(cast[cstring](pattern.pcreObj)) + pattern.pcreObj = nil + if pattern.pcreExtra != nil: + pcre.free_study(pattern.pcreExtra) + +proc getNameToNumberTable(pattern: Regex): Table[string, int] = + let entryCount = getinfo[cint](pattern, pcre.INFO_NAMECOUNT) + let entrySize = getinfo[cint](pattern, pcre.INFO_NAMEENTRYSIZE) + let table = cast[ptr UncheckedArray[uint8]]( + getinfo[int](pattern, pcre.INFO_NAMETABLE)) + + result = initTable[string, int]() + + for i in 0 .. <entryCount: + let pos = i * entrySize + let num = (int(table[pos]) shl 8) or int(table[pos + 1]) - 1 + var name = "" + + var idx = 2 + while table[pos + idx] != 0: + name.add(char(table[pos + idx])) + idx += 1 + + result[name] = num + +proc initRegex(pattern: string, flags: int, study = true): Regex = + new(result, destroyRegex) + result.pattern = pattern + + var errorMsg: cstring + var errOffset: cint + + result.pcreObj = pcre.compile(cstring(pattern), + # better hope int is at least 4 bytes.. + cint(flags), addr errorMsg, + addr errOffset, nil) + if result.pcreObj == nil: + # failed to compile + raise SyntaxError(msg: $errorMsg, pos: errOffset, pattern: pattern) + + if study: + # XXX investigate JIT + result.pcreExtra = pcre.study(result.pcreObj, 0x0, addr errorMsg) + if errorMsg != nil: + raise StudyError(msg: $errorMsg) + + result.captureNameToId = result.getNameToNumberTable() + +proc re*(pattern: string): Regex = + let (pattern, flags, study) = extractOptions(pattern) + initRegex(pattern, flags, study) +# }}} + +# Operations {{{ +proc matchImpl(str: string, pattern: Regex, start, endpos: int, flags: int): Option[RegexMatch] = + var myResult = RegexMatch(pattern : pattern, str : str) + # See PCRE man pages. + # 2x capture count to make room for start-end pairs + # 1x capture count as slack space for PCRE + let vecsize = (pattern.captureCount() + 1) * 3 + # div 2 because each element is 2 cints long + myResult.pcreMatchBounds = newSeq[Slice[cint]](ceil(vecsize / 2).int) + myResult.pcreMatchBounds.setLen(vecsize div 3) + + let strlen = if endpos == int.high: str.len else: endpos+1 + doAssert(strlen <= str.len) # don't want buffer overflows + + let execRet = pcre.exec(pattern.pcreObj, + pattern.pcreExtra, + cstring(str), + cint(strlen), + cint(start), + cint(flags), + cast[ptr cint](addr myResult.pcreMatchBounds[0]), + cint(vecsize)) + if execRet >= 0: + return some(myResult) + + case execRet: + of pcre.ERROR_NOMATCH: + return none(RegexMatch) + of pcre.ERROR_NULL: + raise newException(AccessViolationError, "Expected non-null parameters") + of pcre.ERROR_BADOPTION: + raise RegexInternalError(msg : "Unknown pattern flag. Either a bug or " & + "outdated PCRE.") + of pcre.ERROR_BADUTF8, pcre.ERROR_SHORTUTF8, pcre.ERROR_BADUTF8_OFFSET: + raise InvalidUnicodeError(msg : "Invalid unicode byte sequence", + pos : myResult.pcreMatchBounds[0].a) + else: + raise RegexInternalError(msg : "Unknown internal error: " & $execRet) + +proc match*(str: string, pattern: Regex, start = 0, endpos = int.high): Option[RegexMatch] = + ## Like ```find(...)`` <#proc-find>`__, but anchored to the start of the + ## string. This means that ``"foo".match(re"f") == true``, but + ## ``"foo".match(re"o") == false``. + return str.matchImpl(pattern, start, endpos, pcre.ANCHORED) + +iterator findIter*(str: string, pattern: Regex, start = 0, endpos = int.high): RegexMatch = + ## Works the same as ```find(...)`` <#proc-find>`__, but finds every + ## non-overlapping match. ``"2222".find(re"22")`` is ``"22", "22"``, not + ## ``"22", "22", "22"``. + ## + ## Arguments are the same as ```find(...)`` <#proc-find>`__ + ## + ## Variants: + ## + ## - ``proc findAll(...)`` returns a ``seq[string]`` + # see pcredemo for explaination + let matchesCrLf = pattern.matchesCrLf() + let unicode = uint32(getinfo[culong](pattern, pcre.INFO_OPTIONS) and + pcre.UTF8) > 0u32 + let strlen = if endpos == int.high: str.len else: endpos+1 + + var offset = start + var match: Option[RegexMatch] + while true: + var flags = 0 + + if match.isSome and + match.get.matchBounds.a > match.get.matchBounds.b: + # 0-len match + flags = pcre.NOTEMPTY_ATSTART + + match = str.matchImpl(pattern, offset, endpos, flags) + + if match.isNone: + # either the end of the input or the string + # cannot be split here + if offset >= strlen: + break + + if matchesCrLf and offset < (str.len - 1) and + str[offset] == '\r' and str[offset + 1] == '\L': + # if PCRE treats CrLf as newline, skip both at the same time + offset += 2 + elif unicode: + # XXX what about invalid unicode? + offset += str.runeLenAt(offset) + assert(offset <= strlen) + else: + offset += 1 + else: + offset = match.get.matchBounds.b + 1 + + yield match.get + + +proc find*(str: string, pattern: Regex, start = 0, endpos = int.high): Option[RegexMatch] = + ## Finds the given pattern in the string between the end and start + ## positions. + ## + ## ``start`` + ## The start point at which to start matching. ``|abc`` is ``0``; + ## ``a|bc`` is ``1`` + ## + ## ``endpos`` + ## The maximum index for a match; ``int.high`` means the end of the + ## string, otherwise it’s an inclusive upper bound. + return str.matchImpl(pattern, start, endpos, 0) + +proc findAll*(str: string, pattern: Regex, start = 0, endpos = int.high): seq[string] = + result = @[] + for match in str.findIter(pattern, start, endpos): + result.add(match.match) + +proc split*(str: string, pattern: Regex, maxSplit = -1, start = 0): seq[string] = + ## Splits the string with the given regex. This works according to the + ## rules that Perl and Javascript use: + ## + ## - If the match is zero-width, then the string is still split: + ## ``"123".split(r"") == @["1", "2", "3"]``. + ## + ## - If the pattern has a capture in it, it is added after the string + ## split: ``"12".split(re"(\d)") == @["", "1", "", "2", ""]``. + ## + ## - If ``maxsplit != -1``, then the string will only be split + ## ``maxsplit - 1`` times. This means that there will be ``maxsplit`` + ## strings in the output seq. + ## ``"1.2.3".split(re"\.", maxsplit = 2) == @["1", "2.3"]`` + ## + ## ``start`` behaves the same as in ```find(...)`` <#proc-find>`__. + result = @[] + var lastIdx = start + var splits = 0 + var bounds = 0 .. 0 + + for match in str.findIter(pattern, start = start): + # bounds are inclusive: + # + # 0123456 + # ^^^ + # (1, 3) + bounds = match.matchBounds + + # "12".split("") would be @["", "1", "2"], but + # if we skip an empty first match, it's the correct + # @["1", "2"] + if bounds.a <= bounds.b or bounds.a > start: + result.add(str.substr(lastIdx, bounds.a - 1)) + splits += 1 + + lastIdx = bounds.b + 1 + + for cap in match.captures: + # if there are captures, include them in the result + result.add(cap) + + if splits == maxSplit - 1: + break + + # "12".split("\b") would be @["1", "2", ""], but + # if we skip an empty last match, it's the correct + # @["1", "2"] + if bounds.a <= bounds.b or bounds.b < str.high: + # last match: Each match takes the previous substring, + # but "1 2".split(/ /) needs to return @["1", "2"]. + # This handles "2" + result.add(str.substr(bounds.b + 1, str.high)) + +template replaceImpl(str: string, pattern: Regex, + replacement: expr): stmt {.immediate, dirty.} = + # XXX seems very similar to split, maybe I can reduce code duplication + # somehow? + result = "" + var lastIdx = 0 + for match {.inject.} in str.findIter(pattern): + let bounds = match.matchBounds + result.add(str.substr(lastIdx, bounds.a - 1)) + let nextVal = replacement + assert(nextVal != nil) + result.add(nextVal) + + lastIdx = bounds.b + 1 + + result.add(str.substr(lastIdx, str.len - 1)) + return result + +proc replace*(str: string, pattern: Regex, + subproc: proc (match: RegexMatch): string): string = + ## Replaces each match of Regex in the string with ``sub``, which should + ## never be or return ``nil``. + ## + ## If ``sub`` is a ``proc (RegexMatch): string``, then it is executed with + ## each match and the return value is the replacement value. + ## + ## If ``sub`` is a ``proc (string): string``, then it is executed with the + ## full text of the match and and the return value is the replacement + ## value. + ## + ## If ``sub`` is a string, the syntax is as follows: + ## + ## - ``$$`` - literal ``$`` + ## - ``$123`` - capture number ``123`` + ## - ``$foo`` - named capture ``foo`` + ## - ``${foo}`` - same as above + ## - ``$1$#`` - first and second captures + ## - ``$#`` - first capture + ## - ``$0`` - full match + ## + ## If a given capture is missing, a ``ValueError`` exception is thrown. + replaceImpl(str, pattern, subproc(match)) + +proc replace*(str: string, pattern: Regex, + subproc: proc (match: string): string): string = + replaceImpl(str, pattern, subproc(match.match)) + +proc replace*(str: string, pattern: Regex, sub: string): string = + # - 1 because the string numbers are 0-indexed + replaceImpl(str, pattern, + formatStr(sub, match.captures[name], match.captures[id - 1])) + +# }}} + +let SpecialCharMatcher = re"([\\+*?[^\]$(){}=!<>|:-])" +proc escapeRe*(str: string): string = + ## Escapes the string so it doesn’t match any special characters. + ## Incompatible with the Extra flag (``X``). + str.replace(SpecialCharMatcher, "\\$1") diff --git a/lib/impure/nre/.gitignore b/lib/impure/nre/.gitignore new file mode 100644 index 000000000..3d647a25e --- /dev/null +++ b/lib/impure/nre/.gitignore @@ -0,0 +1,9 @@ +# all executables +* +!*/ +!*.* +*.exe + +# Wildcard patterns. +*.swp +nimcache diff --git a/lib/impure/nre/private/util.nim b/lib/impure/nre/private/util.nim new file mode 100644 index 000000000..253bfada7 --- /dev/null +++ b/lib/impure/nre/private/util.nim @@ -0,0 +1,63 @@ +## INTERNAL FILE FOR USE ONLY BY nre.nim. +import tables + +proc fget*[K, V](self: Table[K, V], key: K): V = + if self.hasKey(key): + return self[key] + else: + raise newException(KeyError, "Key does not exist in table: " & $key) + +const Ident = {'a'..'z', 'A'..'Z', '0'..'9', '_', '\128'..'\255'} +const StartIdent = Ident - {'0'..'9'} + +proc checkNil(arg: string): string = + if arg == nil: + raise newException(ValueError, "Cannot use nil capture") + else: + return arg + +template formatStr*(howExpr, namegetter, idgetter: expr): expr = + let how = howExpr + var val = newStringOfCap(how.len) + var i = 0 + var lastNum = 1 + + while i < how.len: + if how[i] != '$': + val.add(how[i]) + i += 1 + else: + if how[i + 1] == '$': + val.add('$') + i += 2 + elif how[i + 1] == '#': + var id {.inject.} = lastNum + val.add(checkNil(idgetter)) + lastNum += 1 + i += 2 + elif how[i + 1] in {'0'..'9'}: + i += 1 + var id {.inject.} = 0 + while i < how.len and how[i] in {'0'..'9'}: + id += (id * 10) + (ord(how[i]) - ord('0')) + i += 1 + val.add(checkNil(idgetter)) + lastNum = id + 1 + elif how[i + 1] in StartIdent: + i += 1 + var name {.inject.} = "" + while i < how.len and how[i] in Ident: + name.add(how[i]) + i += 1 + val.add(checkNil(namegetter)) + elif how[i + 1] == '{': + i += 2 + var name {.inject.} = "" + while i < how.len and how[i] != '}': + name.add(how[i]) + i += 1 + i += 1 + val.add(checkNil(namegetter)) + else: + raise newException(Exception, "Syntax error in format string at " & $i) + val diff --git a/lib/impure/re.nim b/lib/impure/re.nim index 279f8aadd..5f9371f28 100644 --- a/lib/impure/re.nim +++ b/lib/impure/re.nim @@ -7,11 +7,8 @@ # distribution, for details about the copyright. # -## Regular expression support for Nim. Consider using the pegs module instead. -## -## There is an alternative regular expressions library with a more unified API: -## `nre <https://github.com/flaviut/nre>`_. It may be added to the standard -## library in the future, instead of `re`. +## Regular expression support for Nim. Deprecated. Consider using the ``nre`` +## or ``pegs`` modules instead. ## ## **Note:** The 're' proc defaults to the **extended regular expression ## syntax** which lets you use whitespace freely to make your regexes readable. @@ -49,7 +46,7 @@ type h: ptr Pcre e: ptr ExtraData - Regex* = ref RegexDesc ## a compiled regular expression + Regex* {.deprecated.} = ref RegexDesc ## a compiled regular expression RegexError* = object of ValueError ## is raised if the pattern is no valid regular expression. @@ -79,7 +76,7 @@ proc finalizeRegEx(x: Regex) = if not isNil(x.e): pcre.free_substring(cast[cstring](x.e)) -proc re*(s: string, flags = {reExtended, reStudy}): Regex = +proc re*(s: string, flags = {reExtended, reStudy}): Regex {.deprecated.} = ## Constructor of regular expressions. Note that Nim's ## extended raw string literals support this syntax ``re"[abc]"`` as ## a short form for ``re(r"[abc]")``. diff --git a/lib/pure/basic2d.nim b/lib/pure/basic2d.nim index d18e73c16..1392fdeba 100644 --- a/lib/pure/basic2d.nim +++ b/lib/pure/basic2d.nim @@ -64,7 +64,7 @@ type ## not used for geometric transformations in 2d. ax*,ay*,bx*,by*,tx*,ty*:float Point2d* = object - ## Implements a non-homegeneous 2d point stored as + ## Implements a non-homogeneous 2d point stored as ## an `x` coordinate and an `y` coordinate. x*,y*:float Vector2d* = object diff --git a/lib/pure/basic3d.nim b/lib/pure/basic3d.nim index b99357e3a..7fea54d58 100644 --- a/lib/pure/basic3d.nim +++ b/lib/pure/basic3d.nim @@ -53,7 +53,7 @@ type ## [ tx ty tz tw ] ax*,ay*,az*,aw*, bx*,by*,bz*,bw*, cx*,cy*,cz*,cw*, tx*,ty*,tz*,tw*:float Point3d* = object - ## Implements a non-homegeneous 2d point stored as + ## Implements a non-homogeneous 3d point stored as ## an `x` , `y` and `z` coordinate. x*,y*,z*:float Vector3d* = object diff --git a/lib/pure/concurrency/threadpool.nim b/lib/pure/concurrency/threadpool.nim index 749a2fa2d..247aa377c 100644 --- a/lib/pure/concurrency/threadpool.nim +++ b/lib/pure/concurrency/threadpool.nim @@ -290,7 +290,8 @@ proc slave(w: ptr Worker) {.thread.} = readyWorker = w signal(gSomeReady) await(w.taskArrived) - assert(not w.ready) + # XXX Somebody needs to look into this (why does this assertion fail in Visual Studio?) + when not defined(vcc): assert(not w.ready) w.f(w, w.data) if w.q.len != 0: w.cleanFlowVars if w.shutdown: diff --git a/lib/pure/unittest.nim b/lib/pure/unittest.nim index dbbd3cabc..407db0a51 100644 --- a/lib/pure/unittest.nim +++ b/lib/pure/unittest.nim @@ -39,7 +39,6 @@ when declared(stdout): when not defined(ECMAScript): import terminal - system.addQuitProc(resetAttributes) type TestStatus* = enum OK, FAILED diff --git a/lib/system.nim b/lib/system.nim index 949443dd9..2204a5436 100644 --- a/lib/system.nim +++ b/lib/system.nim @@ -677,35 +677,35 @@ proc `not` *(x: int): int {.magic: "BitnotI", noSideEffect.} proc `not` *(x: int8): int8 {.magic: "BitnotI", noSideEffect.} proc `not` *(x: int16): int16 {.magic: "BitnotI", noSideEffect.} proc `not` *(x: int32): int32 {.magic: "BitnotI", noSideEffect.} -proc `not` *(x: int64): int64 {.magic: "BitnotI64", noSideEffect.} +proc `not` *(x: int64): int64 {.magic: "BitnotI", noSideEffect.} ## computes the `bitwise complement` of the integer `x`. proc `+` *(x, y: int): int {.magic: "AddI", noSideEffect.} proc `+` *(x, y: int8): int8 {.magic: "AddI", noSideEffect.} proc `+` *(x, y: int16): int16 {.magic: "AddI", noSideEffect.} proc `+` *(x, y: int32): int32 {.magic: "AddI", noSideEffect.} -proc `+` *(x, y: int64): int64 {.magic: "AddI64", noSideEffect.} +proc `+` *(x, y: int64): int64 {.magic: "AddI", noSideEffect.} ## Binary `+` operator for an integer. proc `-` *(x, y: int): int {.magic: "SubI", noSideEffect.} proc `-` *(x, y: int8): int8 {.magic: "SubI", noSideEffect.} proc `-` *(x, y: int16): int16 {.magic: "SubI", noSideEffect.} proc `-` *(x, y: int32): int32 {.magic: "SubI", noSideEffect.} -proc `-` *(x, y: int64): int64 {.magic: "SubI64", noSideEffect.} +proc `-` *(x, y: int64): int64 {.magic: "SubI", noSideEffect.} ## Binary `-` operator for an integer. proc `*` *(x, y: int): int {.magic: "MulI", noSideEffect.} proc `*` *(x, y: int8): int8 {.magic: "MulI", noSideEffect.} proc `*` *(x, y: int16): int16 {.magic: "MulI", noSideEffect.} proc `*` *(x, y: int32): int32 {.magic: "MulI", noSideEffect.} -proc `*` *(x, y: int64): int64 {.magic: "MulI64", noSideEffect.} +proc `*` *(x, y: int64): int64 {.magic: "MulI", noSideEffect.} ## Binary `*` operator for an integer. proc `div` *(x, y: int): int {.magic: "DivI", noSideEffect.} proc `div` *(x, y: int8): int8 {.magic: "DivI", noSideEffect.} proc `div` *(x, y: int16): int16 {.magic: "DivI", noSideEffect.} proc `div` *(x, y: int32): int32 {.magic: "DivI", noSideEffect.} -proc `div` *(x, y: int64): int64 {.magic: "DivI64", noSideEffect.} +proc `div` *(x, y: int64): int64 {.magic: "DivI", noSideEffect.} ## computes the integer division. This is roughly the same as ## ``floor(x/y)``. ## @@ -718,7 +718,7 @@ proc `mod` *(x, y: int): int {.magic: "ModI", noSideEffect.} proc `mod` *(x, y: int8): int8 {.magic: "ModI", noSideEffect.} proc `mod` *(x, y: int16): int16 {.magic: "ModI", noSideEffect.} proc `mod` *(x, y: int32): int32 {.magic: "ModI", noSideEffect.} -proc `mod` *(x, y: int64): int64 {.magic: "ModI64", noSideEffect.} +proc `mod` *(x, y: int64): int64 {.magic: "ModI", noSideEffect.} ## computes the integer modulo operation. This is the same as ## ``x - (x div y) * y``. @@ -726,7 +726,7 @@ proc `shr` *(x, y: int): int {.magic: "ShrI", noSideEffect.} proc `shr` *(x, y: int8): int8 {.magic: "ShrI", noSideEffect.} proc `shr` *(x, y: int16): int16 {.magic: "ShrI", noSideEffect.} proc `shr` *(x, y: int32): int32 {.magic: "ShrI", noSideEffect.} -proc `shr` *(x, y: int64): int64 {.magic: "ShrI64", noSideEffect.} +proc `shr` *(x, y: int64): int64 {.magic: "ShrI", noSideEffect.} ## computes the `shift right` operation of `x` and `y`. ## ## .. code-block:: Nim @@ -738,49 +738,49 @@ proc `shl` *(x, y: int): int {.magic: "ShlI", noSideEffect.} proc `shl` *(x, y: int8): int8 {.magic: "ShlI", noSideEffect.} proc `shl` *(x, y: int16): int16 {.magic: "ShlI", noSideEffect.} proc `shl` *(x, y: int32): int32 {.magic: "ShlI", noSideEffect.} -proc `shl` *(x, y: int64): int64 {.magic: "ShlI64", noSideEffect.} +proc `shl` *(x, y: int64): int64 {.magic: "ShlI", noSideEffect.} ## computes the `shift left` operation of `x` and `y`. proc `and` *(x, y: int): int {.magic: "BitandI", noSideEffect.} proc `and` *(x, y: int8): int8 {.magic: "BitandI", noSideEffect.} proc `and` *(x, y: int16): int16 {.magic: "BitandI", noSideEffect.} proc `and` *(x, y: int32): int32 {.magic: "BitandI", noSideEffect.} -proc `and` *(x, y: int64): int64 {.magic: "BitandI64", noSideEffect.} +proc `and` *(x, y: int64): int64 {.magic: "BitandI", noSideEffect.} ## computes the `bitwise and` of numbers `x` and `y`. proc `or` *(x, y: int): int {.magic: "BitorI", noSideEffect.} proc `or` *(x, y: int8): int8 {.magic: "BitorI", noSideEffect.} proc `or` *(x, y: int16): int16 {.magic: "BitorI", noSideEffect.} proc `or` *(x, y: int32): int32 {.magic: "BitorI", noSideEffect.} -proc `or` *(x, y: int64): int64 {.magic: "BitorI64", noSideEffect.} +proc `or` *(x, y: int64): int64 {.magic: "BitorI", noSideEffect.} ## computes the `bitwise or` of numbers `x` and `y`. proc `xor` *(x, y: int): int {.magic: "BitxorI", noSideEffect.} proc `xor` *(x, y: int8): int8 {.magic: "BitxorI", noSideEffect.} proc `xor` *(x, y: int16): int16 {.magic: "BitxorI", noSideEffect.} proc `xor` *(x, y: int32): int32 {.magic: "BitxorI", noSideEffect.} -proc `xor` *(x, y: int64): int64 {.magic: "BitxorI64", noSideEffect.} +proc `xor` *(x, y: int64): int64 {.magic: "BitxorI", noSideEffect.} ## computes the `bitwise xor` of numbers `x` and `y`. proc `==` *(x, y: int): bool {.magic: "EqI", noSideEffect.} proc `==` *(x, y: int8): bool {.magic: "EqI", noSideEffect.} proc `==` *(x, y: int16): bool {.magic: "EqI", noSideEffect.} proc `==` *(x, y: int32): bool {.magic: "EqI", noSideEffect.} -proc `==` *(x, y: int64): bool {.magic: "EqI64", noSideEffect.} +proc `==` *(x, y: int64): bool {.magic: "EqI", noSideEffect.} ## Compares two integers for equality. proc `<=` *(x, y: int): bool {.magic: "LeI", noSideEffect.} proc `<=` *(x, y: int8): bool {.magic: "LeI", noSideEffect.} proc `<=` *(x, y: int16): bool {.magic: "LeI", noSideEffect.} proc `<=` *(x, y: int32): bool {.magic: "LeI", noSideEffect.} -proc `<=` *(x, y: int64): bool {.magic: "LeI64", noSideEffect.} +proc `<=` *(x, y: int64): bool {.magic: "LeI", noSideEffect.} ## Returns true iff `x` is less than or equal to `y`. proc `<` *(x, y: int): bool {.magic: "LtI", noSideEffect.} proc `<` *(x, y: int8): bool {.magic: "LtI", noSideEffect.} proc `<` *(x, y: int16): bool {.magic: "LtI", noSideEffect.} proc `<` *(x, y: int32): bool {.magic: "LtI", noSideEffect.} -proc `<` *(x, y: int64): bool {.magic: "LtI64", noSideEffect.} +proc `<` *(x, y: int64): bool {.magic: "LtI", noSideEffect.} ## Returns true iff `x` is less than `y`. type @@ -2304,7 +2304,7 @@ proc abs*(x: int16): int16 {.magic: "AbsI", noSideEffect.} = if x < 0: -x else: x proc abs*(x: int32): int32 {.magic: "AbsI", noSideEffect.} = if x < 0: -x else: x -proc abs*(x: int64): int64 {.magic: "AbsI64", noSideEffect.} = +proc abs*(x: int64): int64 {.magic: "AbsI", noSideEffect.} = ## returns the absolute value of `x`. If `x` is ``low(x)`` (that ## is -MININT for its type), an overflow exception is thrown (if overflow ## checking is turned on). diff --git a/lib/system/atomics.nim b/lib/system/atomics.nim index 0b16b63bf..158fe91bc 100644 --- a/lib/system/atomics.nim +++ b/lib/system/atomics.nim @@ -191,11 +191,11 @@ proc atomicDec*(memLoc: var int, x: int = 1): int = result = memLoc when defined(windows) and not someGcc: - proc interlockedCompareExchange(p: pointer; exchange, comparand: int32): int32 + proc interlockedCompareExchange(p: pointer; exchange, comparand: int): int {.importc: "InterlockedCompareExchange", header: "<windows.h>", cdecl.} proc cas*[T: bool|int|ptr](p: ptr T; oldValue, newValue: T): bool = - interlockedCompareExchange(p, newValue.int32, oldValue.int32) != 0 + interlockedCompareExchange(p, cast[int](newValue), cast[int](oldValue)) != 0 # XXX fix for 64 bit build else: # this is valid for GCC and Intel C++ diff --git a/lib/system/jssys.nim b/lib/system/jssys.nim index 4a26e1e08..f082023ee 100644 --- a/lib/system/jssys.nim +++ b/lib/system/jssys.nim @@ -517,62 +517,70 @@ proc isFatPointer(ti: PNimType): bool = tyArray, tyArrayConstr, tyTuple, tyOpenArray, tySet, tyVar, tyRef, tyPtr} -proc nimCopy(x: pointer, ti: PNimType): pointer {.compilerproc.} +proc nimCopy(dest, src: pointer, ti: PNimType): pointer {.compilerproc.} proc nimCopyAux(dest, src: pointer, n: ptr TNimNode) {.compilerproc.} = case n.kind of nkNone: sysAssert(false, "nimCopyAux") of nkSlot: - asm "`dest`[`n`.offset] = nimCopy(`src`[`n`.offset], `n`.typ);" + asm """ + var ddest = `dest`[`n`.offset]; + if (ddest === undefined) ddest = null; + `dest`[`n`.offset] = nimCopy(ddest, `src`[`n`.offset], `n`.typ); + """ of nkList: for i in 0..n.len-1: nimCopyAux(dest, src, n.sons[i]) of nkCase: asm """ - `dest`[`n`.offset] = nimCopy(`src`[`n`.offset], `n`.typ); + var ddest = `dest`[`n`.offset]; + if (ddest === undefined) ddest = null; + `dest`[`n`.offset] = nimCopy(ddest, `src`[`n`.offset], `n`.typ); for (var i = 0; i < `n`.sons.length; ++i) { nimCopyAux(`dest`, `src`, `n`.sons[i][1]); } """ -proc nimCopy(x: pointer, ti: PNimType): pointer = +proc nimCopy(dest, src: pointer, ti: PNimType): pointer = case ti.kind of tyPtr, tyRef, tyVar, tyNil: if not isFatPointer(ti): - result = x + result = src else: - asm """ - `result` = [null, 0]; - `result`[0] = `x`[0]; - `result`[1] = `x`[1]; - """ + asm "`result` = [`src`[0], `src`[1]];" of tySet: asm """ `result` = {}; - for (var key in `x`) { `result`[key] = `x`[key]; } + for (var key in `src`) { `result`[key] = `src`[key]; } """ of tyTuple, tyObject: - if ti.base != nil: result = nimCopy(x, ti.base) + if ti.base != nil: result = nimCopy(dest, src, ti.base) elif ti.kind == tyObject: - asm "`result` = {m_type: `ti`};" + asm "`result` = (`dest` === null) ? {m_type: `ti`} : `dest`;" else: - asm "`result` = {};" - nimCopyAux(result, x, ti.node) + asm "`result` = (`dest` === null) ? {} : `dest`;" + nimCopyAux(result, src, ti.node) of tySequence, tyArrayConstr, tyOpenArray, tyArray: asm """ - `result` = new Array(`x`.length); - for (var i = 0; i < `x`.length; ++i) { - `result`[i] = nimCopy(`x`[i], `ti`.base); + if (`dest` === null) { + `dest` = new Array(`src`.length); + } + else { + `dest`.length = `src`.length; + } + `result` = `dest`; + for (var i = 0; i < `src`.length; ++i) { + `result`[i] = nimCopy(`result`[i], `src`[i], `ti`.base); } """ of tyString: asm """ - if (`x` !== null) { - `result` = `x`.slice(0); + if (`src` !== null) { + `result` = `src`.slice(0); } """ else: - result = x + result = src proc genericReset(x: pointer, ti: PNimType): pointer {.compilerproc.} = case ti.kind @@ -611,7 +619,7 @@ proc arrayConstr(len: int, value: pointer, typ: PNimType): pointer {. # types are fake asm """ var result = new Array(`len`); - for (var i = 0; i < `len`; ++i) result[i] = nimCopy(`value`, `typ`); + for (var i = 0; i < `len`; ++i) result[i] = nimCopy(null, `value`, `typ`); return result; """ diff --git a/tests/js/tcopying.nim b/tests/js/tcopying.nim new file mode 100644 index 000000000..4f72d6ada --- /dev/null +++ b/tests/js/tcopying.nim @@ -0,0 +1,13 @@ +discard """ + output: '''123 +''' +""" + +type MyArray = array[1, int] + +proc changeArray(a: var MyArray) = + a = [123] + +var a : MyArray +changeArray(a) +echo a[0] diff --git a/tests/stdlib/nre/captures.nim b/tests/stdlib/nre/captures.nim new file mode 100644 index 000000000..4f3f15444 --- /dev/null +++ b/tests/stdlib/nre/captures.nim @@ -0,0 +1,59 @@ +import unittest, optional_nonstrict +include nre + +suite "captures": + test "map capture names to numbers": + check(getNameToNumberTable(re("(?<v1>1(?<v2>2(?<v3>3))(?'v4'4))()")) == + { "v1" : 0, "v2" : 1, "v3" : 2, "v4" : 3 }.toTable()) + + test "capture bounds are correct": + let ex1 = re("([0-9])") + check("1 23".find(ex1).matchBounds == 0 .. 0) + check("1 23".find(ex1).captureBounds[0].get == 0 .. 0) + check("1 23".find(ex1, 1).matchBounds == 2 .. 2) + check("1 23".find(ex1, 3).matchBounds == 3 .. 3) + + let ex2 = re("()()()()()()()()()()([0-9])") + check("824".find(ex2).captureBounds[0].get == 0 .. -1) + check("824".find(ex2).captureBounds[10].get == 0 .. 0) + + let ex3 = re("([0-9]+)") + check("824".find(ex3).captureBounds[0].get == 0 .. 2) + + test "named captures": + let ex1 = "foobar".find(re("(?<foo>foo)(?<bar>bar)")) + check(ex1.captures["foo"] == "foo") + check(ex1.captures["bar"] == "bar") + + let ex2 = "foo".find(re("(?<foo>foo)(?<bar>bar)?")) + check(ex2.captures["foo"] == "foo") + check(ex2.captures["bar"] == nil) + + test "named capture bounds": + let ex1 = "foo".find(re("(?<foo>foo)(?<bar>bar)?")) + check(ex1.captureBounds["foo"] == some(0..2)) + check(ex1.captureBounds["bar"] == none(Slice[int])) + + test "capture count": + let ex1 = re("(?<foo>foo)(?<bar>bar)?") + check(ex1.captureCount == 2) + check(ex1.captureNameId == {"foo" : 0, "bar" : 1}.toTable()) + + test "named capture table": + let ex1 = "foo".find(re("(?<foo>foo)(?<bar>bar)?")) + check(ex1.captures.toTable == {"foo" : "foo", "bar" : nil}.toTable()) + check(ex1.captureBounds.toTable == {"foo" : some(0..2), "bar" : none(Slice[int])}.toTable()) + check(ex1.captures.toTable("") == {"foo" : "foo", "bar" : ""}.toTable()) + + let ex2 = "foobar".find(re("(?<foo>foo)(?<bar>bar)?")) + check(ex2.captures.toTable == {"foo" : "foo", "bar" : "bar"}.toTable()) + + test "capture sequence": + let ex1 = "foo".find(re("(?<foo>foo)(?<bar>bar)?")) + check(ex1.captures.toSeq == @["foo", nil]) + check(ex1.captureBounds.toSeq == @[some(0..2), none(Slice[int])]) + check(ex1.captures.toSeq("") == @["foo", ""]) + + let ex2 = "foobar".find(re("(?<foo>foo)(?<bar>bar)?")) + check(ex2.captures.toSeq == @["foo", "bar"]) + diff --git a/tests/stdlib/nre/escape.nim b/tests/stdlib/nre/escape.nim new file mode 100644 index 000000000..db5e8a001 --- /dev/null +++ b/tests/stdlib/nre/escape.nim @@ -0,0 +1,7 @@ +import nre, unittest + +suite "escape strings": + test "escape strings": + check("123".escapeRe() == "123") + check("[]".escapeRe() == r"\[\]") + check("()".escapeRe() == r"\(\)") diff --git a/tests/stdlib/nre/find.nim b/tests/stdlib/nre/find.nim new file mode 100644 index 000000000..05bfb848a --- /dev/null +++ b/tests/stdlib/nre/find.nim @@ -0,0 +1,25 @@ +import unittest, sequtils, nre, optional_nonstrict + +suite "find": + test "find text": + check("3213a".find(re"[a-z]").match == "a") + check(toSeq(findIter("1 2 3 4 5 6 7 8 ", re" ")).map( + proc (a: RegexMatch): string = a.match + ) == @[" ", " ", " ", " ", " ", " ", " ", " "]) + + test "find bounds": + check(toSeq(findIter("1 2 3 4 5 ", re" ")).map( + proc (a: RegexMatch): Slice[int] = a.matchBounds + ) == @[1..1, 3..3, 5..5, 7..7, 9..9]) + + test "overlapping find": + check("222".findAll(re"22") == @["22"]) + check("2222".findAll(re"22") == @["22", "22"]) + + test "len 0 find": + check("".findAll(re"\ ") == newSeq[string]()) + check("".findAll(re"") == @[""]) + check("abc".findAll(re"") == @["", "", "", ""]) + check("word word".findAll(re"\b") == @["", "", "", ""]) + check("word\r\lword".findAll(re"(*ANYCRLF)(?m)$") == @["", ""]) + check("слово слово".findAll(re"(*U)\b") == @["", "", "", ""]) diff --git a/tests/stdlib/nre/init.nim b/tests/stdlib/nre/init.nim new file mode 100644 index 000000000..1a1470842 --- /dev/null +++ b/tests/stdlib/nre/init.nim @@ -0,0 +1,36 @@ +import unittest +include nre + +suite "Test NRE initialization": + test "correct intialization": + check(re("[0-9]+") != nil) + check(re("(?i)[0-9]+") != nil) + + test "options": + check(extractOptions("(*NEVER_UTF)") == + ("", pcre.NEVER_UTF, true)) + check(extractOptions("(*UTF8)(*ANCHORED)(*UCP)z") == + ("(*UTF8)(*UCP)z", pcre.ANCHORED, true)) + check(extractOptions("(*ANCHORED)(*UTF8)(*JAVASCRIPT_COMPAT)z") == + ("(*UTF8)z", pcre.ANCHORED or pcre.JAVASCRIPT_COMPAT, true)) + + check(extractOptions("(*NO_STUDY)(") == ("(", 0, false)) + + check(extractOptions("(*LIMIT_MATCH=6)(*ANCHORED)z") == + ("(*LIMIT_MATCH=6)z", pcre.ANCHORED, true)) + + test "incorrect options": + for s in ["CR", "(CR", "(*CR", "(*abc)", "(*abc)CR", + "(?i)", + "(*LIMIT_MATCH=5", "(*NO_AUTO_POSSESS=5)"]: + let ss = s & "(*NEVER_UTF)" + check(extractOptions(ss) == (ss, 0, true)) + + test "invalid regex": + expect(SyntaxError): discard re("[0-9") + try: + discard re("[0-9") + except SyntaxError: + let ex = SyntaxError(getCurrentException()) + check(ex.pos == 4) + check(ex.pattern == "[0-9") diff --git a/tests/stdlib/nre/match.nim b/tests/stdlib/nre/match.nim new file mode 100644 index 000000000..38ee5214b --- /dev/null +++ b/tests/stdlib/nre/match.nim @@ -0,0 +1,18 @@ +include nre, unittest, optional_nonstrict + +suite "match": + test "upper bound must be inclusive": + check("abc".match(re"abc", endpos = -1) == none(RegexMatch)) + check("abc".match(re"abc", endpos = 1) == none(RegexMatch)) + check("abc".match(re"abc", endpos = 2) != none(RegexMatch)) + + test "match examples": + check("abc".match(re"(\w)").captures[0] == "a") + check("abc".match(re"(?<letter>\w)").captures["letter"] == "a") + check("abc".match(re"(\w)\w").captures[-1] == "ab") + check("abc".match(re"(\w)").captureBounds[0].get == 0 .. 0) + check("abc".match(re"").captureBounds[-1].get == 0 .. -1) + check("abc".match(re"abc").captureBounds[-1].get == 0 .. 2) + + test "match test cases": + check("123".match(re"").matchBounds == 0 .. -1) diff --git a/tests/stdlib/nre/misc.nim b/tests/stdlib/nre/misc.nim new file mode 100644 index 000000000..f4a88b639 --- /dev/null +++ b/tests/stdlib/nre/misc.nim @@ -0,0 +1,16 @@ +import unittest, nre, strutils, optional_nonstrict + +suite "Misc tests": + test "unicode": + check("".find(re"(*UTF8)").match == "") + check("перевірка".replace(re"(*U)\w", "") == "") + + test "empty or non-empty match": + check("abc".findall(re"|.").join(":") == ":a::b::c:") + check("abc".findall(re".|").join(":") == "a:b:c:") + + check("abc".replace(re"|.", "x") == "xxxxxxx") + check("abc".replace(re".|", "x") == "xxxx") + + check("abc".split(re"|.").join(":") == ":::::") + check("abc".split(re".|").join(":") == ":::") diff --git a/tests/stdlib/nre/optional_nonstrict.nim b/tests/stdlib/nre/optional_nonstrict.nim new file mode 100644 index 000000000..d13f4fab7 --- /dev/null +++ b/tests/stdlib/nre/optional_nonstrict.nim @@ -0,0 +1,3 @@ +import options +converter option2val*[T](val: Option[T]): T = + return val.get() diff --git a/tests/stdlib/nre/replace.nim b/tests/stdlib/nre/replace.nim new file mode 100644 index 000000000..516fd4328 --- /dev/null +++ b/tests/stdlib/nre/replace.nim @@ -0,0 +1,20 @@ +include nre +import unittest + +suite "replace": + test "replace with 0-length strings": + check("".replace(re"1", proc (v: RegexMatch): string = "1") == "") + check(" ".replace(re"", proc (v: RegexMatch): string = "1") == "1 1") + check("".replace(re"", proc (v: RegexMatch): string = "1") == "1") + + test "regular replace": + check("123".replace(re"\d", "foo") == "foofoofoo") + check("123".replace(re"(\d)", "$1$1") == "112233") + check("123".replace(re"(\d)(\d)", "$1$2") == "123") + check("123".replace(re"(\d)(\d)", "$#$#") == "123") + check("123".replace(re"(?<foo>\d)(\d)", "$foo$#$#") == "1123") + check("123".replace(re"(?<foo>\d)(\d)", "${foo}$#$#") == "1123") + + test "replacing missing captures should throw instead of segfaulting": + expect ValueError: discard "ab".replace(re"(a)|(b)", "$1$2") + expect ValueError: discard "b".replace(re"(a)?(b)", "$1$2") diff --git a/tests/stdlib/nre/split.nim b/tests/stdlib/nre/split.nim new file mode 100644 index 000000000..8064e40b7 --- /dev/null +++ b/tests/stdlib/nre/split.nim @@ -0,0 +1,52 @@ +import unittest, strutils +include nre + +suite "string splitting": + test "splitting strings": + check("1 2 3 4 5 6 ".split(re" ") == @["1", "2", "3", "4", "5", "6", ""]) + check("1 2 ".split(re(" ")) == @["1", "", "2", "", ""]) + check("1 2".split(re(" ")) == @["1", "2"]) + check("foo".split(re("foo")) == @["", ""]) + check("".split(re"foo") == @[""]) + + test "captured patterns": + check("12".split(re"(\d)") == @["", "1", "", "2", ""]) + + test "maxsplit": + check("123".split(re"", maxsplit = 2) == @["1", "23"]) + check("123".split(re"", maxsplit = 1) == @["123"]) + check("123".split(re"", maxsplit = -1) == @["1", "2", "3"]) + + test "split with 0-length match": + check("12345".split(re("")) == @["1", "2", "3", "4", "5"]) + check("".split(re"") == newSeq[string]()) + check("word word".split(re"\b") == @["word", " ", "word"]) + check("word\r\lword".split(re"(*ANYCRLF)(?m)$") == @["word", "\r\lword"]) + check("слово слово".split(re"(*U)(\b)") == @["", "слово", "", " ", "", "слово", ""]) + + test "perl split tests": + check("forty-two" .split(re"") .join(",") == "f,o,r,t,y,-,t,w,o") + check("forty-two" .split(re"", 3) .join(",") == "f,o,rty-two") + check("split this string" .split(re" ") .join(",") == "split,this,string") + check("split this string" .split(re" ", 2) .join(",") == "split,this string") + check("try$this$string" .split(re"\$") .join(",") == "try,this,string") + check("try$this$string" .split(re"\$", 2) .join(",") == "try,this$string") + check("comma, separated, values" .split(re", ") .join("|") == "comma|separated|values") + check("comma, separated, values" .split(re", ", 2) .join("|") == "comma|separated, values") + check("Perl6::Camelia::Test" .split(re"::") .join(",") == "Perl6,Camelia,Test") + check("Perl6::Camelia::Test" .split(re"::", 2) .join(",") == "Perl6,Camelia::Test") + check("split,me,please" .split(re",") .join("|") == "split|me|please") + check("split,me,please" .split(re",", 2) .join("|") == "split|me,please") + check("Hello World Goodbye Mars".split(re"\s+") .join(",") == "Hello,World,Goodbye,Mars") + check("Hello World Goodbye Mars".split(re"\s+", 3).join(",") == "Hello,World,Goodbye Mars") + check("Hello test" .split(re"(\s+)") .join(",") == "Hello, ,test") + check("this will be split" .split(re" ") .join(",") == "this,will,be,split") + check("this will be split" .split(re" ", 3) .join(",") == "this,will,be split") + check("a.b" .split(re"\.") .join(",") == "a,b") + check("" .split(re"") .len == 0) + check(":" .split(re"") .len == 1) + + test "start position": + check("abc".split(re"", start = 1) == @["b", "c"]) + check("abc".split(re"", start = 2) == @["c"]) + check("abc".split(re"", start = 3) == newSeq[string]()) diff --git a/tests/stdlib/tmitems.nim b/tests/stdlib/tmitems.nim index 2c0a0392a..544ad0334 100644 --- a/tests/stdlib/tmitems.nim +++ b/tests/stdlib/tmitems.nim @@ -11,8 +11,8 @@ fpqeew [11, 12, 13] [11, 12, 13] [11, 12, 13] -{"key1": 11, "key2": 12, "key3": 13} -[11, 12, 13] +{"key1":11,"key2":12,"key3":13} +[11,12,13] <Students> <Student Name="Aprilfoo" /> <Student Name="bar" /> diff --git a/tests/stdlib/tnre.nim b/tests/stdlib/tnre.nim new file mode 100644 index 000000000..85792b81e --- /dev/null +++ b/tests/stdlib/tnre.nim @@ -0,0 +1,9 @@ +import nre +import nre.init +import nre.captures +import nre.find +import nre.split +import nre.match +import nre.replace +import nre.escape +import nre.misc diff --git a/tests/testament/specs.nim b/tests/testament/specs.nim index 9306bf025..99640f22c 100644 --- a/tests/testament/specs.nim +++ b/tests/testament/specs.nim @@ -10,7 +10,7 @@ import parseutils, strutils, os, osproc, streams, parsecfg const - cmdTemplate* = r"nim $target --hints:on -d:testing $options $file" + cmdTemplate* = r"compiler" / "nim $target --lib:lib --hints:on -d:testing $options $file" type TTestAction* = enum diff --git a/tests/testament/tester.nim b/tests/testament/tester.nim index 62dafee80..704d7e76e 100644 --- a/tests/testament/tester.nim +++ b/tests/testament/tester.nim @@ -12,7 +12,7 @@ import parseutils, strutils, pegs, os, osproc, streams, parsecfg, json, marshal, backend, parseopt, specs, htmlgen, browsers, terminal, - algorithm, compiler/nodejs + algorithm, compiler/nodejs, re const resultsFile = "testresults.html" @@ -137,14 +137,18 @@ proc addResult(r: var TResults, test: TTest, expected = expected, given = given) r.data.addf("$#\t$#\t$#\t$#", name, expected, given, $success) - if success == reIgnored: - styledEcho styleBright, name, fgYellow, " [", $success, "]" - elif success != reSuccess: - styledEcho styleBright, name, fgRed, " [", $success, "]" - echo"Expected:" - styledEcho styleBright, expected - echo"Given:" - styledEcho styleBright, given + if success == reSuccess: + styledEcho fgGreen, "PASS: ", fgCyan, name + elif success == reIgnored: + styledEcho styleDim, fgYellow, "SKIP: ", styleBright, fgCyan, name + else: + styledEcho styleBright, fgRed, "FAIL: ", fgCyan, name + styledEcho styleBright, fgCyan, "Test \"", test.name, "\"", " in category \"", test.cat.string, "\"" + styledEcho styleBright, fgRed, "Failure: ", $success + styledEcho fgYellow, "Expected:" + styledEcho styleBright, expected, "\n" + styledEcho fgYellow, "Gotten:" + styledEcho styleBright, given, "\n" proc cmpMsgs(r: var TResults, expected, given: TSpec, test: TTest) = if strip(expected.msg) notin strip(given.msg): @@ -211,68 +215,101 @@ proc compilerOutputTests(test: TTest, given: var TSpec, expected: TSpec; if given.err == reSuccess: inc(r.passed) r.addResult(test, expectedmsg, givenmsg, given.err) +proc analyzeAndConsolidateOutput(s: string): string = + result = "" + let rows = s.splitLines + for i in 0 ..< rows.len: + if (let pos = find(rows[i], "Traceback (most recent call last)"); pos != -1): + result = substr(rows[i], pos) & "\n" + for i in i+1 ..< rows.len: + result.add rows[i] & "\n" + if not (rows[i] =~ re"^[^(]+\(\d+\)\s+"): + return + elif (let pos = find(rows[i], "SIGSEGV: Illegal storage access."); pos != -1): + result = substr(rows[i], pos) + return + proc testSpec(r: var TResults, test: TTest) = # major entry point for a single test let tname = test.name.addFileExt(".nim") inc(r.total) - styledEcho "Processing ", fgCyan, extractFilename(tname) var expected: TSpec if test.action != actionRunNoSpec: expected = parseSpec(tname) else: specDefaults expected expected.action = actionRunNoSpec + if expected.err == reIgnored: r.addResult(test, "", "", reIgnored) inc(r.skipped) - else: - case expected.action - of actionCompile: - var given = callCompiler(expected.cmd, test.name, - test.options & " --hint[Path]:off --hint[Processing]:off", test.target) - compilerOutputTests(test, given, expected, r) - of actionRun, actionRunNoSpec: - var given = callCompiler(expected.cmd, test.name, test.options, - test.target) - if given.err != reSuccess: - r.addResult(test, "", given.msg, given.err) - else: - var exeFile: string - if test.target == targetJS: - let (dir, file, ext) = splitFile(tname) - exeFile = dir / "nimcache" / file & ".js" - else: - exeFile = changeFileExt(tname, ExeExt) - if existsFile(exeFile): - let nodejs = findNodeJs() - if test.target == targetJS and nodejs == "": - r.addResult(test, expected.outp, "nodejs binary not in PATH", - reExeNotFound) - return - var (buf, exitCode) = execCmdEx( - (if test.target == targetJS: nodejs & " " else: "") & exeFile) - if exitCode != expected.exitCode: - r.addResult(test, "exitcode: " & $expected.exitCode, - "exitcode: " & $exitCode, reExitCodesDiffer) - else: - var bufB = strip(buf.string) - if expected.sortoutput: bufB = makeDeterministic(bufB) - if bufB != strip(expected.outp): - if not (expected.substr and expected.outp in bufB): - given.err = reOutputsDiffer - compilerOutputTests(test, given, expected, r) - else: - r.addResult(test, expected.outp, "executable not found", reExeNotFound) - of actionReject: - var given = callCompiler(expected.cmd, test.name, test.options, - test.target) - cmpMsgs(r, expected, given, test) + return + + case expected.action + of actionCompile: + var given = callCompiler(expected.cmd, test.name, + test.options & " --hint[Path]:off --hint[Processing]:off", test.target) + compilerOutputTests(test, given, expected, r) + of actionRun, actionRunNoSpec: + # In this branch of code "early return" pattern is clearer than deep + # nested conditionals - the empty rows in between to clarify the "danger" + var given = callCompiler(expected.cmd, test.name, test.options, + test.target) + + if given.err != reSuccess: + r.addResult(test, "", given.msg, given.err) + return + + let isJsTarget = test.target == targetJS + var exeFile: string + if isJsTarget: + let (dir, file, ext) = splitFile(tname) + exeFile = dir / "nimcache" / file & ".js" # *TODO* hardcoded "nimcache" + else: + exeFile = changeFileExt(tname, ExeExt) + + if not existsFile(exeFile): + r.addResult(test, expected.outp, "executable not found", reExeNotFound) + return + + let nodejs = if isJsTarget: findNodeJs() else: "" + if isJsTarget and nodejs == "": + r.addResult(test, expected.outp, "nodejs binary not in PATH", + reExeNotFound) + return + + let exeCmd = (if isJsTarget: nodejs & " " else: "") & exeFile + let (buf, exitCode) = execCmdEx(exeCmd) + let bufB = if expected.sortoutput: makeDeterministic(strip(buf.string)) + else: strip(buf.string) + let expectedOut = strip(expected.outp) + + if exitCode != expected.exitCode: + r.addResult(test, "exitcode: " & $expected.exitCode, + "exitcode: " & $exitCode & "\n\nOutput:\n" & + analyzeAndConsolidateOutput(bufB), + reExitCodesDiffer) + return + + if bufB != expectedOut: + if not (expected.substr and expectedOut in bufB): + given.err = reOutputsDiffer + r.addResult(test, expected.outp, bufB, reOutputsDiffer) + return + + compilerOutputTests(test, given, expected, r) + return + + of actionReject: + var given = callCompiler(expected.cmd, test.name, test.options, + test.target) + cmpMsgs(r, expected, given, test) + return proc testNoSpec(r: var TResults, test: TTest) = # does not extract the spec because the file is not supposed to have any let tname = test.name.addFileExt(".nim") inc(r.total) - styledEcho "Processing ", fgCyan, extractFilename(tname) let given = callCompiler(cmdTemplate, test.name, test.options, test.target) r.addResult(test, "", given.msg, given.err) if given.err == reSuccess: inc(r.passed) diff --git a/web/news.txt b/web/news.txt index 6a82b3f26..49b49a9f1 100644 --- a/web/news.txt +++ b/web/news.txt @@ -13,12 +13,17 @@ News Code accessing that property is deprecated and code using ``miliseconds`` during object initialization or as a named parameter of ``initInterval()`` will need to be updated. - - ``std.logging`` functions no longer do formatting and semantically treat their arguments just like ``echo`` does. Affected functions: ``log``, ``debug``, ``info``, ``warn``, ``error``, ``fatal``. Custom subtypes of ``Logger`` also need to be adjusted accordingly. + Library additions + ----------------- + + - The nre module has been added, providing a better interface to PCRE than + re. + Language Additions ------------------ |