merged devel into epc
This commit is contained in:
commit
f5cca89610
183 changed files with 5295 additions and 4043 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -41,4 +41,3 @@ xcuserdata/
|
||||||
/testresults.html
|
/testresults.html
|
||||||
/testresults.json
|
/testresults.json
|
||||||
testament.db
|
testament.db
|
||||||
/csources/
|
|
||||||
|
|
|
||||||
3
.gitmodules
vendored
Normal file
3
.gitmodules
vendored
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
[submodule "csources"]
|
||||||
|
path = csources
|
||||||
|
url = ../../nim-lang/csources.git
|
||||||
4
build.sh
Normal file → Executable file
4
build.sh
Normal file → Executable file
|
|
@ -2,8 +2,8 @@
|
||||||
set -e
|
set -e
|
||||||
set -x
|
set -x
|
||||||
|
|
||||||
if [ ! -d "csources" ]; then
|
if [ ! -e csources/.git ]; then
|
||||||
git clone --depth 1 https://github.com/nim-lang/csources.git
|
git submodule update --init --depth 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
cd "csources"
|
cd "csources"
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
#
|
#
|
||||||
#
|
#
|
||||||
# The Nim Compiler
|
# The Nim Compiler
|
||||||
# (c) Copyright 2013 Andreas Rumpf
|
# (c) Copyright 2015 Andreas Rumpf
|
||||||
#
|
#
|
||||||
# See the file "copying.txt", included in this
|
# See the file "copying.txt", included in this
|
||||||
# distribution, for details about the copyright.
|
# distribution, for details about the copyright.
|
||||||
|
|
@ -296,6 +296,7 @@ const
|
||||||
sfCompileToCpp* = sfInfixCall # compile the module as C++ code
|
sfCompileToCpp* = sfInfixCall # compile the module as C++ code
|
||||||
sfCompileToObjc* = sfNamedParamCall # compile the module as Objective-C code
|
sfCompileToObjc* = sfNamedParamCall # compile the module as Objective-C code
|
||||||
sfExperimental* = sfOverriden # module uses the .experimental switch
|
sfExperimental* = sfOverriden # module uses the .experimental switch
|
||||||
|
sfGoto* = sfOverriden # var is used for 'goto' code generation
|
||||||
|
|
||||||
const
|
const
|
||||||
# getting ready for the future expr/stmt merge
|
# getting ready for the future expr/stmt merge
|
||||||
|
|
@ -529,19 +530,20 @@ type
|
||||||
TMagic* = enum # symbols that require compiler magic:
|
TMagic* = enum # symbols that require compiler magic:
|
||||||
mNone,
|
mNone,
|
||||||
mDefined, mDefinedInScope, mCompiles,
|
mDefined, mDefinedInScope, mCompiles,
|
||||||
mLow, mHigh, mSizeOf, mTypeTrait, mIs, mOf, mAddr, mTypeOf, mRoof,
|
mLow, mHigh, mSizeOf, mTypeTrait, mIs, mOf, mAddr, mTypeOf, mRoof, mPlugin,
|
||||||
mEcho, mShallowCopy, mSlurp, mStaticExec,
|
mEcho, mShallowCopy, mSlurp, mStaticExec,
|
||||||
mParseExprToAst, mParseStmtToAst, mExpandToAst, mQuoteAst,
|
mParseExprToAst, mParseStmtToAst, mExpandToAst, mQuoteAst,
|
||||||
mUnaryLt, mInc, mDec, mOrd, mNew, mNewFinalize, mNewSeq, mLengthOpenArray,
|
mUnaryLt, mInc, mDec, mOrd, mNew, mNewFinalize, mNewSeq, mLengthOpenArray,
|
||||||
mLengthStr, mLengthArray, mLengthSeq, mIncl, mExcl, mCard, mChr, mGCref,
|
mLengthStr, mLengthArray, mLengthSeq, mXLenStr, mXLenSeq,
|
||||||
mGCunref,
|
mIncl, mExcl, mCard, mChr,
|
||||||
|
mGCref, mGCunref,
|
||||||
|
|
||||||
mAddI, mSubI, mMulI, mDivI, mModI, mAddI64, mSubI64, mMulI64,
|
mAddI, mSubI, mMulI, mDivI, mModI, mAddI64, mSubI64, mMulI64,
|
||||||
mDivI64, mModI64, mSucc, mPred,
|
mDivI64, mModI64, mSucc, mPred,
|
||||||
mAddF64, mSubF64, mMulF64, mDivF64,
|
mAddF64, mSubF64, mMulF64, mDivF64,
|
||||||
|
|
||||||
mShrI, mShlI, mBitandI, mBitorI, mBitxorI, mMinI, mMaxI,
|
mShrI, mShlI, mBitandI, mBitorI, mBitxorI, mMinI, mMaxI,
|
||||||
mShrI64, mShlI64, mBitandI64, mBitorI64, mBitxorI64, mMinI64, mMaxI64,
|
mShrI64, mShlI64, mBitandI64, mBitorI64, mBitxorI64,
|
||||||
mMinF64, mMaxF64, mAddU, mSubU, mMulU,
|
mMinF64, mMaxF64, mAddU, mSubU, mMulU,
|
||||||
mDivU, mModU, mEqI, mLeI,
|
mDivU, mModU, mEqI, mLeI,
|
||||||
mLtI,
|
mLtI,
|
||||||
|
|
@ -550,7 +552,7 @@ type
|
||||||
mEqEnum, mLeEnum, mLtEnum, mEqCh, mLeCh, mLtCh, mEqB, mLeB, mLtB, mEqRef,
|
mEqEnum, mLeEnum, mLtEnum, mEqCh, mLeCh, mLtCh, mEqB, mLeB, mLtB, mEqRef,
|
||||||
mEqUntracedRef, mLePtr, mLtPtr, mEqCString, mXor, mEqProc, mUnaryMinusI,
|
mEqUntracedRef, mLePtr, mLtPtr, mEqCString, mXor, mEqProc, mUnaryMinusI,
|
||||||
mUnaryMinusI64, mAbsI, mAbsI64, mNot,
|
mUnaryMinusI64, mAbsI, mAbsI64, mNot,
|
||||||
mUnaryPlusI, mBitnotI, mUnaryPlusI64,
|
mUnaryPlusI, mBitnotI,
|
||||||
mBitnotI64, mUnaryPlusF64, mUnaryMinusF64, mAbsF64, mZe8ToI, mZe8ToI64,
|
mBitnotI64, mUnaryPlusF64, mUnaryMinusF64, mAbsF64, mZe8ToI, mZe8ToI64,
|
||||||
mZe16ToI, mZe16ToI64, mZe32ToI64, mZeIToI64, mToU8, mToU16, mToU32,
|
mZe16ToI, mZe16ToI64, mZe32ToI64, mZeIToI64, mToU8, mToU16, mToU32,
|
||||||
mToFloat, mToBiggestFloat, mToInt, mToBiggestInt, mCharToStr, mBoolToStr,
|
mToFloat, mToBiggestFloat, mToInt, mToBiggestInt, mCharToStr, mBoolToStr,
|
||||||
|
|
@ -589,11 +591,12 @@ type
|
||||||
const
|
const
|
||||||
ctfeWhitelist* = {mNone, mUnaryLt, mSucc,
|
ctfeWhitelist* = {mNone, mUnaryLt, mSucc,
|
||||||
mPred, mInc, mDec, mOrd, mLengthOpenArray,
|
mPred, mInc, mDec, mOrd, mLengthOpenArray,
|
||||||
mLengthStr, mLengthArray, mLengthSeq, mIncl, mExcl, mCard, mChr,
|
mLengthStr, mLengthArray, mLengthSeq, mXLenStr, mXLenSeq,
|
||||||
|
mIncl, mExcl, mCard, mChr,
|
||||||
mAddI, mSubI, mMulI, mDivI, mModI, mAddI64, mSubI64, mMulI64,
|
mAddI, mSubI, mMulI, mDivI, mModI, mAddI64, mSubI64, mMulI64,
|
||||||
mDivI64, mModI64, mAddF64, mSubF64, mMulF64, mDivF64,
|
mDivI64, mModI64, mAddF64, mSubF64, mMulF64, mDivF64,
|
||||||
mShrI, mShlI, mBitandI, mBitorI, mBitxorI, mMinI, mMaxI,
|
mShrI, mShlI, mBitandI, mBitorI, mBitxorI, mMinI, mMaxI,
|
||||||
mShrI64, mShlI64, mBitandI64, mBitorI64, mBitxorI64, mMinI64, mMaxI64,
|
mShrI64, mShlI64, mBitandI64, mBitorI64, mBitxorI64,
|
||||||
mMinF64, mMaxF64, mAddU, mSubU, mMulU,
|
mMinF64, mMaxF64, mAddU, mSubU, mMulU,
|
||||||
mDivU, mModU, mEqI, mLeI,
|
mDivU, mModU, mEqI, mLeI,
|
||||||
mLtI,
|
mLtI,
|
||||||
|
|
@ -602,7 +605,7 @@ const
|
||||||
mEqEnum, mLeEnum, mLtEnum, mEqCh, mLeCh, mLtCh, mEqB, mLeB, mLtB, mEqRef,
|
mEqEnum, mLeEnum, mLtEnum, mEqCh, mLeCh, mLtCh, mEqB, mLeB, mLtB, mEqRef,
|
||||||
mEqProc, mEqUntracedRef, mLePtr, mLtPtr, mEqCString, mXor, mUnaryMinusI,
|
mEqProc, mEqUntracedRef, mLePtr, mLtPtr, mEqCString, mXor, mUnaryMinusI,
|
||||||
mUnaryMinusI64, mAbsI, mAbsI64, mNot,
|
mUnaryMinusI64, mAbsI, mAbsI64, mNot,
|
||||||
mUnaryPlusI, mBitnotI, mUnaryPlusI64,
|
mUnaryPlusI, mBitnotI,
|
||||||
mBitnotI64, mUnaryPlusF64, mUnaryMinusF64, mAbsF64, mZe8ToI, mZe8ToI64,
|
mBitnotI64, mUnaryPlusF64, mUnaryMinusF64, mAbsF64, mZe8ToI, mZe8ToI64,
|
||||||
mZe16ToI, mZe16ToI64, mZe32ToI64, mZeIToI64, mToU8, mToU16, mToU32,
|
mZe16ToI, mZe16ToI64, mZe32ToI64, mZeIToI64, mToU8, mToU16, mToU32,
|
||||||
mToFloat, mToBiggestFloat, mToInt, mToBiggestInt, mCharToStr, mBoolToStr,
|
mToFloat, mToBiggestFloat, mToInt, mToBiggestInt, mCharToStr, mBoolToStr,
|
||||||
|
|
@ -1170,7 +1173,9 @@ proc newType*(kind: TTypeKind, owner: PSym): PType =
|
||||||
result.lockLevel = UnspecifiedLockLevel
|
result.lockLevel = UnspecifiedLockLevel
|
||||||
when debugIds:
|
when debugIds:
|
||||||
registerId(result)
|
registerId(result)
|
||||||
#if result.id < 2000:
|
#if result.id == 92231:
|
||||||
|
# echo "KNID ", kind
|
||||||
|
# writeStackTrace()
|
||||||
# messageOut(typeKindToStr[kind] & ' has id: ' & toString(result.id))
|
# messageOut(typeKindToStr[kind] & ' has id: ' & toString(result.id))
|
||||||
|
|
||||||
proc mergeLoc(a: var TLoc, b: TLoc) =
|
proc mergeLoc(a: var TLoc, b: TLoc) =
|
||||||
|
|
|
||||||
|
|
@ -566,8 +566,6 @@ proc binaryArith(p: BProc, e: PNode, d: var TLoc, op: TMagic) =
|
||||||
"($4)($1 & $2)", # BitandI64
|
"($4)($1 & $2)", # BitandI64
|
||||||
"($4)($1 | $2)", # BitorI64
|
"($4)($1 | $2)", # BitorI64
|
||||||
"($4)($1 ^ $2)", # BitxorI64
|
"($4)($1 ^ $2)", # BitxorI64
|
||||||
"(($1 <= $2) ? $1 : $2)", # MinI64
|
|
||||||
"(($1 >= $2) ? $1 : $2)", # MaxI64
|
|
||||||
"(($1 <= $2) ? $1 : $2)", # MinF64
|
"(($1 <= $2) ? $1 : $2)", # MinF64
|
||||||
"(($1 >= $2) ? $1 : $2)", # MaxF64
|
"(($1 >= $2) ? $1 : $2)", # MaxF64
|
||||||
"($4)((NU$3)($1) + (NU$3)($2))", # AddU
|
"($4)((NU$3)($1) + (NU$3)($2))", # AddU
|
||||||
|
|
@ -640,7 +638,6 @@ proc unaryArith(p: BProc, e: PNode, d: var TLoc, op: TMagic) =
|
||||||
unArithTab: array[mNot..mToBiggestInt, string] = ["!($1)", # Not
|
unArithTab: array[mNot..mToBiggestInt, string] = ["!($1)", # Not
|
||||||
"$1", # UnaryPlusI
|
"$1", # UnaryPlusI
|
||||||
"($3)((NU$2) ~($1))", # BitnotI
|
"($3)((NU$2) ~($1))", # BitnotI
|
||||||
"$1", # UnaryPlusI64
|
|
||||||
"($3)((NU$2) ~($1))", # BitnotI64
|
"($3)((NU$2) ~($1))", # BitnotI64
|
||||||
"$1", # UnaryPlusF64
|
"$1", # UnaryPlusF64
|
||||||
"-($1)", # UnaryMinusF64
|
"-($1)", # UnaryMinusF64
|
||||||
|
|
@ -676,7 +673,7 @@ proc isCppRef(p: BProc; typ: PType): bool {.inline.} =
|
||||||
|
|
||||||
proc genDeref(p: BProc, e: PNode, d: var TLoc; enforceDeref=false) =
|
proc genDeref(p: BProc, e: PNode, d: var TLoc; enforceDeref=false) =
|
||||||
let mt = mapType(e.sons[0].typ)
|
let mt = mapType(e.sons[0].typ)
|
||||||
if (mt in {ctArray, ctPtrToArray} and not enforceDeref):
|
if mt in {ctArray, ctPtrToArray} and not enforceDeref:
|
||||||
# XXX the amount of hacks for C's arrays is incredible, maybe we should
|
# XXX the amount of hacks for C's arrays is incredible, maybe we should
|
||||||
# simply wrap them in a struct? --> Losing auto vectorization then?
|
# simply wrap them in a struct? --> Losing auto vectorization then?
|
||||||
#if e[0].kind != nkBracketExpr:
|
#if e[0].kind != nkBracketExpr:
|
||||||
|
|
@ -685,19 +682,29 @@ proc genDeref(p: BProc, e: PNode, d: var TLoc; enforceDeref=false) =
|
||||||
else:
|
else:
|
||||||
var a: TLoc
|
var a: TLoc
|
||||||
initLocExprSingleUse(p, e.sons[0], a)
|
initLocExprSingleUse(p, e.sons[0], a)
|
||||||
let typ = skipTypes(a.t, abstractInst)
|
if d.k == locNone:
|
||||||
case typ.kind
|
let typ = skipTypes(a.t, abstractInst)
|
||||||
of tyRef:
|
# dest = *a; <-- We do not know that 'dest' is on the heap!
|
||||||
d.s = OnHeap
|
# It is completely wrong to set 'd.s' here, unless it's not yet
|
||||||
of tyVar:
|
# been assigned to.
|
||||||
d.s = OnUnknown
|
case typ.kind
|
||||||
if tfVarIsPtr notin typ.flags and p.module.compileToCpp and
|
of tyRef:
|
||||||
e.kind == nkHiddenDeref:
|
d.s = OnHeap
|
||||||
|
of tyVar:
|
||||||
|
d.s = OnUnknown
|
||||||
|
if tfVarIsPtr notin typ.flags and p.module.compileToCpp and
|
||||||
|
e.kind == nkHiddenDeref:
|
||||||
|
putIntoDest(p, d, e.typ, rdLoc(a))
|
||||||
|
return
|
||||||
|
of tyPtr:
|
||||||
|
d.s = OnUnknown # BUGFIX!
|
||||||
|
else: internalError(e.info, "genDeref " & $a.t.kind)
|
||||||
|
elif p.module.compileToCpp:
|
||||||
|
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))
|
||||||
return
|
return
|
||||||
of tyPtr:
|
|
||||||
d.s = OnUnknown # BUGFIX!
|
|
||||||
else: internalError(e.info, "genDeref " & $a.t.kind)
|
|
||||||
if enforceDeref and mt == ctPtrToArray:
|
if enforceDeref and mt == ctPtrToArray:
|
||||||
# we lie about the type for better C interop: 'ptr array[3,T]' is
|
# we lie about the type for better C interop: 'ptr array[3,T]' is
|
||||||
# translated to 'ptr T', but for deref'ing this produces wrong code.
|
# translated to 'ptr T', but for deref'ing this produces wrong code.
|
||||||
|
|
@ -957,8 +964,11 @@ proc genEcho(p: BProc, n: PNode) =
|
||||||
var args: Rope = nil
|
var args: Rope = nil
|
||||||
var a: TLoc
|
var a: TLoc
|
||||||
for i in countup(0, n.len-1):
|
for i in countup(0, n.len-1):
|
||||||
initLocExpr(p, n.sons[i], a)
|
if n.sons[i].skipConv.kind == nkNilLit:
|
||||||
addf(args, ", $1? ($1)->data:\"nil\"", [rdLoc(a)])
|
add(args, ", \"nil\"")
|
||||||
|
else:
|
||||||
|
initLocExpr(p, n.sons[i], a)
|
||||||
|
addf(args, ", $1? ($1)->data:\"nil\"", [rdLoc(a)])
|
||||||
linefmt(p, cpsStmts, "printf($1$2);$n",
|
linefmt(p, cpsStmts, "printf($1$2);$n",
|
||||||
makeCString(repeat("%s", n.len) & tnl), args)
|
makeCString(repeat("%s", n.len) & tnl), args)
|
||||||
|
|
||||||
|
|
@ -1345,15 +1355,15 @@ proc genArrayLen(p: BProc, e: PNode, d: var TLoc, op: TMagic) =
|
||||||
else: unaryExpr(p, e, d, "$1Len0")
|
else: unaryExpr(p, e, d, "$1Len0")
|
||||||
of tyCString:
|
of tyCString:
|
||||||
useStringh(p.module)
|
useStringh(p.module)
|
||||||
if op == mHigh: unaryExpr(p, e, d, "(strlen($1)-1)")
|
if op == mHigh: unaryExpr(p, e, d, "($1 ? (strlen($1)-1) : -1)")
|
||||||
else: unaryExpr(p, e, d, "strlen($1)")
|
else: unaryExpr(p, e, d, "($1 ? strlen($1) : 0)")
|
||||||
of tyString, tySequence:
|
of tyString, tySequence:
|
||||||
if not p.module.compileToCpp:
|
if not p.module.compileToCpp:
|
||||||
if op == mHigh: unaryExpr(p, e, d, "($1->Sup.len-1)")
|
if op == mHigh: unaryExpr(p, e, d, "($1 ? ($1->Sup.len-1) : -1)")
|
||||||
else: unaryExpr(p, e, d, "$1->Sup.len")
|
else: unaryExpr(p, e, d, "($1 ? $1->Sup.len : 0)")
|
||||||
else:
|
else:
|
||||||
if op == mHigh: unaryExpr(p, e, d, "($1->len-1)")
|
if op == mHigh: unaryExpr(p, e, d, "($1 ? ($1->len-1) : -1)")
|
||||||
else: unaryExpr(p, e, d, "$1->len")
|
else: unaryExpr(p, e, d, "($1 ? $1->len : 0)")
|
||||||
of tyArray, tyArrayConstr:
|
of tyArray, tyArrayConstr:
|
||||||
# YYY: length(sideeffect) is optimized away incorrectly?
|
# YYY: length(sideeffect) is optimized away incorrectly?
|
||||||
if op == mHigh: putIntoDest(p, d, e.typ, rope(lastOrd(typ)))
|
if op == mHigh: putIntoDest(p, d, e.typ, rope(lastOrd(typ)))
|
||||||
|
|
@ -1714,6 +1724,11 @@ proc genMagicExpr(p: BProc, e: PNode, d: var TLoc, op: TMagic) =
|
||||||
of mOrd: genOrd(p, e, d)
|
of mOrd: genOrd(p, e, d)
|
||||||
of mLengthArray, mHigh, mLengthStr, mLengthSeq, mLengthOpenArray:
|
of mLengthArray, mHigh, mLengthStr, mLengthSeq, mLengthOpenArray:
|
||||||
genArrayLen(p, e, d, op)
|
genArrayLen(p, e, d, op)
|
||||||
|
of mXLenStr, mXLenSeq:
|
||||||
|
if not p.module.compileToCpp:
|
||||||
|
unaryExpr(p, e, d, "($1->Sup.len-1)")
|
||||||
|
else:
|
||||||
|
unaryExpr(p, e, d, "$1->len")
|
||||||
of mGCref: unaryStmt(p, e, d, "#nimGCref($1);$n")
|
of mGCref: unaryStmt(p, e, d, "#nimGCref($1);$n")
|
||||||
of mGCunref: unaryStmt(p, e, d, "#nimGCunref($1);$n")
|
of mGCunref: unaryStmt(p, e, d, "#nimGCunref($1);$n")
|
||||||
of mSetLengthStr: genSetLengthStr(p, e, d)
|
of mSetLengthStr: genSetLengthStr(p, e, d)
|
||||||
|
|
|
||||||
|
|
@ -175,9 +175,18 @@ proc genBreakState(p: BProc, n: PNode) =
|
||||||
|
|
||||||
proc genVarPrototypeAux(m: BModule, sym: PSym)
|
proc genVarPrototypeAux(m: BModule, sym: PSym)
|
||||||
|
|
||||||
|
proc genGotoVar(p: BProc; value: PNode) =
|
||||||
|
if value.kind notin {nkCharLit..nkUInt64Lit}:
|
||||||
|
localError(value.info, "'goto' target must be a literal value")
|
||||||
|
else:
|
||||||
|
lineF(p, cpsStmts, "goto NIMSTATE_$#;$n", [value.intVal.rope])
|
||||||
|
|
||||||
proc genSingleVar(p: BProc, a: PNode) =
|
proc genSingleVar(p: BProc, a: PNode) =
|
||||||
var v = a.sons[0].sym
|
var v = a.sons[0].sym
|
||||||
if sfCompileTime in v.flags: return
|
if {sfCompileTime, sfGoto} * v.flags != {}:
|
||||||
|
# translate 'var state {.goto.} = X' into 'goto LX':
|
||||||
|
if sfGoto in v.flags: genGotoVar(p, a.sons[2])
|
||||||
|
return
|
||||||
var targetProc = p
|
var targetProc = p
|
||||||
if sfGlobal in v.flags:
|
if sfGlobal in v.flags:
|
||||||
if v.flags * {sfImportc, sfExportc} == {sfImportc} and
|
if v.flags * {sfImportc, sfExportc} == {sfImportc} and
|
||||||
|
|
@ -365,6 +374,19 @@ proc genReturnStmt(p: BProc, t: PNode) =
|
||||||
linefmt(p, cpsStmts, "if ($1.status != 0) #popCurrentException();$n", safePoint)
|
linefmt(p, cpsStmts, "if ($1.status != 0) #popCurrentException();$n", safePoint)
|
||||||
lineF(p, cpsStmts, "goto BeforeRet;$n", [])
|
lineF(p, cpsStmts, "goto BeforeRet;$n", [])
|
||||||
|
|
||||||
|
proc genGotoForCase(p: BProc; caseStmt: PNode) =
|
||||||
|
for i in 1 .. <caseStmt.len:
|
||||||
|
startBlock(p)
|
||||||
|
let it = caseStmt.sons[i]
|
||||||
|
for j in 0 .. it.len-2:
|
||||||
|
if it.sons[j].kind == nkRange:
|
||||||
|
localError(it.info, "range notation not available for computed goto")
|
||||||
|
return
|
||||||
|
let val = getOrdValue(it.sons[j])
|
||||||
|
lineF(p, cpsStmts, "NIMSTATE_$#:$n", [val.rope])
|
||||||
|
genStmts(p, it.lastSon)
|
||||||
|
endBlock(p)
|
||||||
|
|
||||||
proc genComputedGoto(p: BProc; n: PNode) =
|
proc genComputedGoto(p: BProc; n: PNode) =
|
||||||
# first pass: Generate array of computed labels:
|
# first pass: Generate array of computed labels:
|
||||||
var casePos = -1
|
var casePos = -1
|
||||||
|
|
@ -529,12 +551,7 @@ proc genBreakStmt(p: BProc, t: PNode) =
|
||||||
lineF(p, cpsStmts, "goto $1;$n", [label])
|
lineF(p, cpsStmts, "goto $1;$n", [label])
|
||||||
|
|
||||||
proc getRaiseFrmt(p: BProc): string =
|
proc getRaiseFrmt(p: BProc): string =
|
||||||
if p.module.compileToCpp:
|
result = "#raiseException((#Exception*)$1, $2);$n"
|
||||||
result = "throw NimException($1, $2);$n"
|
|
||||||
elif getCompilerProc("Exception") != nil:
|
|
||||||
result = "#raiseException((#Exception*)$1, $2);$n"
|
|
||||||
else:
|
|
||||||
result = "#raiseException((#E_Base*)$1, $2);$n"
|
|
||||||
|
|
||||||
proc genRaiseStmt(p: BProc, t: PNode) =
|
proc genRaiseStmt(p: BProc, t: PNode) =
|
||||||
if p.inExceptBlock > 0:
|
if p.inExceptBlock > 0:
|
||||||
|
|
@ -737,7 +754,10 @@ proc genCase(p: BProc, t: PNode, d: var TLoc) =
|
||||||
genCaseGeneric(p, t, d, "if ($1 >= $2 && $1 <= $3) goto $4;$n",
|
genCaseGeneric(p, t, d, "if ($1 >= $2 && $1 <= $3) goto $4;$n",
|
||||||
"if ($1 == $2) goto $3;$n")
|
"if ($1 == $2) goto $3;$n")
|
||||||
else:
|
else:
|
||||||
genOrdinalCase(p, t, d)
|
if t.sons[0].kind == nkSym and sfGoto in t.sons[0].sym.flags:
|
||||||
|
genGotoForCase(p, t)
|
||||||
|
else:
|
||||||
|
genOrdinalCase(p, t, d)
|
||||||
|
|
||||||
proc hasGeneralExceptSection(t: PNode): bool =
|
proc hasGeneralExceptSection(t: PNode): bool =
|
||||||
var length = sonsLen(t)
|
var length = sonsLen(t)
|
||||||
|
|
@ -772,11 +792,8 @@ proc genTryCpp(p: BProc, t: PNode, d: var TLoc) =
|
||||||
# finallyPart();
|
# finallyPart();
|
||||||
if not isEmptyType(t.typ) and d.k == locNone:
|
if not isEmptyType(t.typ) and d.k == locNone:
|
||||||
getTemp(p, t.typ, d)
|
getTemp(p, t.typ, d)
|
||||||
var
|
|
||||||
exc: Rope
|
|
||||||
i, length, blen: int
|
|
||||||
genLineDir(p, t)
|
genLineDir(p, t)
|
||||||
exc = getTempName()
|
let exc = getTempName()
|
||||||
if getCompilerProc("Exception") != nil:
|
if getCompilerProc("Exception") != nil:
|
||||||
discard cgsym(p.module, "Exception")
|
discard cgsym(p.module, "Exception")
|
||||||
else:
|
else:
|
||||||
|
|
@ -784,20 +801,23 @@ proc genTryCpp(p: BProc, t: PNode, d: var TLoc) =
|
||||||
add(p.nestedTryStmts, t)
|
add(p.nestedTryStmts, t)
|
||||||
startBlock(p, "try {$n")
|
startBlock(p, "try {$n")
|
||||||
expr(p, t.sons[0], d)
|
expr(p, t.sons[0], d)
|
||||||
length = sonsLen(t)
|
let length = sonsLen(t)
|
||||||
endBlock(p, ropecg(p.module, "} catch (NimException& $1) {$n", [exc]))
|
endBlock(p, ropecg(p.module, "} catch (NimException& $1) {$n", [exc]))
|
||||||
if optStackTrace in p.options:
|
if optStackTrace in p.options:
|
||||||
linefmt(p, cpsStmts, "#setFrame((TFrame*)&F);$n")
|
linefmt(p, cpsStmts, "#setFrame((TFrame*)&FR);$n")
|
||||||
inc p.inExceptBlock
|
inc p.inExceptBlock
|
||||||
i = 1
|
var i = 1
|
||||||
var catchAllPresent = false
|
var catchAllPresent = false
|
||||||
while (i < length) and (t.sons[i].kind == nkExceptBranch):
|
while (i < length) and (t.sons[i].kind == nkExceptBranch):
|
||||||
blen = sonsLen(t.sons[i])
|
let blen = sonsLen(t.sons[i])
|
||||||
if i > 1: addf(p.s(cpsStmts), "else ", [])
|
if i > 1: addf(p.s(cpsStmts), "else ", [])
|
||||||
if blen == 1:
|
if blen == 1:
|
||||||
# general except section:
|
# general except section:
|
||||||
catchAllPresent = true
|
catchAllPresent = true
|
||||||
exprBlock(p, t.sons[i].sons[0], d)
|
startBlock(p)
|
||||||
|
expr(p, t.sons[i].sons[0], d)
|
||||||
|
linefmt(p, cpsStmts, "#popCurrentException();$n")
|
||||||
|
endBlock(p)
|
||||||
else:
|
else:
|
||||||
var orExpr: Rope = nil
|
var orExpr: Rope = nil
|
||||||
for j in countup(0, blen - 2):
|
for j in countup(0, blen - 2):
|
||||||
|
|
@ -807,7 +827,10 @@ proc genTryCpp(p: BProc, t: PNode, d: var TLoc) =
|
||||||
"#isObj($1.exp->m_type, $2)",
|
"#isObj($1.exp->m_type, $2)",
|
||||||
[exc, genTypeInfo(p.module, t.sons[i].sons[j].typ)])
|
[exc, genTypeInfo(p.module, t.sons[i].sons[j].typ)])
|
||||||
lineF(p, cpsStmts, "if ($1) ", [orExpr])
|
lineF(p, cpsStmts, "if ($1) ", [orExpr])
|
||||||
exprBlock(p, t.sons[i].sons[blen-1], d)
|
startBlock(p)
|
||||||
|
expr(p, t.sons[i].sons[blen-1], d)
|
||||||
|
linefmt(p, cpsStmts, "#popCurrentException();$n")
|
||||||
|
endBlock(p)
|
||||||
inc(i)
|
inc(i)
|
||||||
|
|
||||||
# reraise the exception if there was no catch all
|
# reraise the exception if there was no catch all
|
||||||
|
|
@ -887,7 +910,7 @@ proc genTry(p: BProc, t: PNode, d: var TLoc) =
|
||||||
startBlock(p, "else {$n")
|
startBlock(p, "else {$n")
|
||||||
linefmt(p, cpsStmts, "#popSafePoint();$n")
|
linefmt(p, cpsStmts, "#popSafePoint();$n")
|
||||||
if optStackTrace in p.options:
|
if optStackTrace in p.options:
|
||||||
linefmt(p, cpsStmts, "#setFrame((TFrame*)&F);$n")
|
linefmt(p, cpsStmts, "#setFrame((TFrame*)&FR);$n")
|
||||||
inc p.inExceptBlock
|
inc p.inExceptBlock
|
||||||
var i = 1
|
var i = 1
|
||||||
while (i < length) and (t.sons[i].kind == nkExceptBranch):
|
while (i < length) and (t.sons[i].kind == nkExceptBranch):
|
||||||
|
|
@ -976,12 +999,19 @@ proc genAsmStmt(p: BProc, t: PNode) =
|
||||||
else:
|
else:
|
||||||
lineF(p, cpsStmts, CC[cCompiler].asmStmtFrmt, [s])
|
lineF(p, cpsStmts, CC[cCompiler].asmStmtFrmt, [s])
|
||||||
|
|
||||||
|
proc determineSection(n: PNode): TCFileSection =
|
||||||
|
result = cfsProcHeaders
|
||||||
|
if n.len >= 1 and n.sons[0].kind in {nkStrLit..nkTripleStrLit}:
|
||||||
|
if n.sons[0].strVal.startsWith("/*TYPESECTION*/"): result = cfsTypes
|
||||||
|
elif n.sons[0].strVal.startsWith("/*VARSECTION*/"): result = cfsVars
|
||||||
|
|
||||||
proc genEmit(p: BProc, t: PNode) =
|
proc genEmit(p: BProc, t: PNode) =
|
||||||
var s = genAsmOrEmitStmt(p, t.sons[1])
|
var s = genAsmOrEmitStmt(p, t.sons[1])
|
||||||
if p.prc == nil:
|
if p.prc == nil:
|
||||||
# top level emit pragma?
|
# top level emit pragma?
|
||||||
genCLineDir(p.module.s[cfsProcHeaders], t.info)
|
let section = determineSection(t[1])
|
||||||
add(p.module.s[cfsProcHeaders], s)
|
genCLineDir(p.module.s[section], t.info)
|
||||||
|
add(p.module.s[section], s)
|
||||||
else:
|
else:
|
||||||
genLineDir(p, t)
|
genLineDir(p, t)
|
||||||
line(p, cpsStmts, s)
|
line(p, cpsStmts, s)
|
||||||
|
|
@ -1065,7 +1095,9 @@ proc asgnFieldDiscriminant(p: BProc, e: PNode) =
|
||||||
|
|
||||||
proc genAsgn(p: BProc, e: PNode, fastAsgn: bool) =
|
proc genAsgn(p: BProc, e: PNode, fastAsgn: bool) =
|
||||||
genLineDir(p, e)
|
genLineDir(p, e)
|
||||||
if not fieldDiscriminantCheckNeeded(p, e):
|
if e.sons[0].kind == nkSym and sfGoto in e.sons[0].sym.flags:
|
||||||
|
genGotoVar(p, e.sons[1])
|
||||||
|
elif not fieldDiscriminantCheckNeeded(p, e):
|
||||||
var a: TLoc
|
var a: TLoc
|
||||||
initLocExpr(p, e.sons[0], a)
|
initLocExpr(p, e.sons[0], a)
|
||||||
if fastAsgn: incl(a.flags, lfNoDeepCopy)
|
if fastAsgn: incl(a.flags, lfNoDeepCopy)
|
||||||
|
|
|
||||||
|
|
@ -11,20 +11,21 @@
|
||||||
|
|
||||||
# ------------------------- Name Mangling --------------------------------
|
# ------------------------- Name Mangling --------------------------------
|
||||||
|
|
||||||
proc mangleField(name: string): string =
|
|
||||||
result = mangle(name)
|
|
||||||
result[0] = result[0].toUpper # Mangling makes everything lowercase,
|
|
||||||
# but some identifiers are C keywords
|
|
||||||
|
|
||||||
proc isKeyword(w: PIdent): bool =
|
proc isKeyword(w: PIdent): bool =
|
||||||
# nimrod and C++ share some keywords
|
# Nim and C++ share some keywords
|
||||||
# it's more efficient to test the whole nimrod keywords range
|
# it's more efficient to test the whole Nim keywords range
|
||||||
case w.id
|
case w.id
|
||||||
of ccgKeywordsLow..ccgKeywordsHigh,
|
of ccgKeywordsLow..ccgKeywordsHigh,
|
||||||
nimKeywordsLow..nimKeywordsHigh,
|
nimKeywordsLow..nimKeywordsHigh,
|
||||||
ord(wInline): return true
|
ord(wInline): return true
|
||||||
else: return false
|
else: return false
|
||||||
|
|
||||||
|
proc mangleField(name: PIdent): string =
|
||||||
|
result = mangle(name.s)
|
||||||
|
if isKeyword(name):
|
||||||
|
result[0] = result[0].toUpper # Mangling makes everything lowercase,
|
||||||
|
# but some identifiers are C keywords
|
||||||
|
|
||||||
proc mangleName(s: PSym): Rope =
|
proc mangleName(s: PSym): Rope =
|
||||||
result = s.loc.r
|
result = s.loc.r
|
||||||
if result == nil:
|
if result == nil:
|
||||||
|
|
@ -110,7 +111,7 @@ proc mapSetType(typ: PType): TCTypeKind =
|
||||||
else: result = ctArray
|
else: result = ctArray
|
||||||
|
|
||||||
proc mapType(typ: PType): TCTypeKind =
|
proc mapType(typ: PType): TCTypeKind =
|
||||||
## Maps a nimrod type to a C type
|
## Maps a Nim type to a C type
|
||||||
case typ.kind
|
case typ.kind
|
||||||
of tyNone, tyStmt: result = ctVoid
|
of tyNone, tyStmt: result = ctVoid
|
||||||
of tyBool: result = ctBool
|
of tyBool: result = ctBool
|
||||||
|
|
@ -379,7 +380,7 @@ proc mangleRecFieldName(field: PSym, rectype: PType): Rope =
|
||||||
({sfImportc, sfExportc} * rectype.sym.flags != {}):
|
({sfImportc, sfExportc} * rectype.sym.flags != {}):
|
||||||
result = field.loc.r
|
result = field.loc.r
|
||||||
else:
|
else:
|
||||||
result = rope(mangleField(field.name.s))
|
result = rope(mangleField(field.name))
|
||||||
if result == nil: internalError(field.info, "mangleRecFieldName")
|
if result == nil: internalError(field.info, "mangleRecFieldName")
|
||||||
|
|
||||||
proc genRecordFieldsAux(m: BModule, n: PNode,
|
proc genRecordFieldsAux(m: BModule, n: PNode,
|
||||||
|
|
@ -642,7 +643,7 @@ proc getTypeDescAux(m: BModule, typ: PType, check: var IntSet): Rope =
|
||||||
result.add getTypeDescAux(m, typeInSlot, check)
|
result.add getTypeDescAux(m, typeInSlot, check)
|
||||||
else:
|
else:
|
||||||
inc i
|
inc i
|
||||||
|
|
||||||
if chunkStart != 0:
|
if chunkStart != 0:
|
||||||
result.add cppName.data.substr(chunkStart)
|
result.add cppName.data.substr(chunkStart)
|
||||||
else:
|
else:
|
||||||
|
|
|
||||||
|
|
@ -176,7 +176,7 @@ proc mangle*(name: string): string =
|
||||||
result = newStringOfCap(name.len)
|
result = newStringOfCap(name.len)
|
||||||
case name[0]
|
case name[0]
|
||||||
of Letters:
|
of Letters:
|
||||||
result.add(name[0].toLower)
|
result.add(name[0])
|
||||||
of Digits:
|
of Digits:
|
||||||
result.add("N" & name[0])
|
result.add("N" & name[0])
|
||||||
else:
|
else:
|
||||||
|
|
|
||||||
|
|
@ -319,7 +319,7 @@ proc processSwitch(switch, arg: string, pass: TCmdLinePass, info: TLineInfo) =
|
||||||
undefSymbol(arg)
|
undefSymbol(arg)
|
||||||
of "symbol":
|
of "symbol":
|
||||||
expectArg(switch, arg, pass, info)
|
expectArg(switch, arg, pass, info)
|
||||||
declareSymbol(arg)
|
# deprecated, do nothing
|
||||||
of "compile":
|
of "compile":
|
||||||
expectArg(switch, arg, pass, info)
|
expectArg(switch, arg, pass, info)
|
||||||
if pass in {passCmd2, passPP}: processCompile(arg)
|
if pass in {passCmd2, passPP}: processCompile(arg)
|
||||||
|
|
@ -488,7 +488,6 @@ proc processSwitch(switch, arg: string, pass: TCmdLinePass, info: TLineInfo) =
|
||||||
if theOS == osNone: localError(info, errUnknownOS, arg)
|
if theOS == osNone: localError(info, errUnknownOS, arg)
|
||||||
elif theOS != platform.hostOS:
|
elif theOS != platform.hostOS:
|
||||||
setTarget(theOS, targetCPU)
|
setTarget(theOS, targetCPU)
|
||||||
condsyms.initDefines()
|
|
||||||
of "cpu":
|
of "cpu":
|
||||||
expectArg(switch, arg, pass, info)
|
expectArg(switch, arg, pass, info)
|
||||||
if pass in {passCmd1, passPP}:
|
if pass in {passCmd1, passPP}:
|
||||||
|
|
@ -496,7 +495,6 @@ proc processSwitch(switch, arg: string, pass: TCmdLinePass, info: TLineInfo) =
|
||||||
if cpu == cpuNone: localError(info, errUnknownCPU, arg)
|
if cpu == cpuNone: localError(info, errUnknownCPU, arg)
|
||||||
elif cpu != platform.hostCPU:
|
elif cpu != platform.hostCPU:
|
||||||
setTarget(targetOS, cpu)
|
setTarget(targetOS, cpu)
|
||||||
condsyms.initDefines()
|
|
||||||
of "run", "r":
|
of "run", "r":
|
||||||
expectNoArg(switch, arg, pass, info)
|
expectNoArg(switch, arg, pass, info)
|
||||||
incl(gGlobalOptions, optRun)
|
incl(gGlobalOptions, optRun)
|
||||||
|
|
|
||||||
|
|
@ -9,71 +9,69 @@
|
||||||
|
|
||||||
# This module handles the conditional symbols.
|
# This module handles the conditional symbols.
|
||||||
|
|
||||||
import
|
import
|
||||||
strtabs, platform, strutils, idents
|
strtabs, platform, strutils, idents
|
||||||
|
|
||||||
# We need to use a PStringTable here as defined symbols are always guaranteed
|
# We need to use a StringTableRef here as defined symbols are always guaranteed
|
||||||
# to be style insensitive. Otherwise hell would break lose.
|
# to be style insensitive. Otherwise hell would break lose.
|
||||||
var gSymbols: StringTableRef
|
var gSymbols: StringTableRef
|
||||||
|
|
||||||
proc defineSymbol*(symbol: string) =
|
const
|
||||||
|
catNone = "false"
|
||||||
|
|
||||||
|
proc defineSymbol*(symbol: string) =
|
||||||
gSymbols[symbol] = "true"
|
gSymbols[symbol] = "true"
|
||||||
|
|
||||||
proc declareSymbol*(symbol: string) =
|
proc undefSymbol*(symbol: string) =
|
||||||
gSymbols[symbol] = "unknown"
|
gSymbols[symbol] = catNone
|
||||||
|
|
||||||
proc undefSymbol*(symbol: string) =
|
proc isDefined*(symbol: string): bool =
|
||||||
gSymbols[symbol] = "false"
|
|
||||||
|
|
||||||
proc isDefined*(symbol: string): bool =
|
|
||||||
if gSymbols.hasKey(symbol):
|
if gSymbols.hasKey(symbol):
|
||||||
result = gSymbols[symbol] == "true"
|
result = gSymbols[symbol] != catNone
|
||||||
|
elif cmpIgnoreStyle(symbol, CPU[targetCPU].name) == 0:
|
||||||
|
result = true
|
||||||
|
elif cmpIgnoreStyle(symbol, platform.OS[targetOS].name) == 0:
|
||||||
|
result = true
|
||||||
|
else:
|
||||||
|
case symbol.normalize
|
||||||
|
of "x86": result = targetCPU == cpuI386
|
||||||
|
of "itanium": result = targetCPU == cpuIa64
|
||||||
|
of "x8664": result = targetCPU == cpuAmd64
|
||||||
|
of "posix", "unix":
|
||||||
|
result = targetOS in {osLinux, osMorphos, osSkyos, osIrix, osPalmos,
|
||||||
|
osQnx, osAtari, osAix,
|
||||||
|
osHaiku, osVxWorks, osSolaris, osNetbsd,
|
||||||
|
osFreebsd, osOpenbsd, osMacosx}
|
||||||
|
of "bsd":
|
||||||
|
result = targetOS in {osNetbsd, osFreebsd, osOpenbsd}
|
||||||
|
of "emulatedthreadvars":
|
||||||
|
result = platform.OS[targetOS].props.contains(ospLacksThreadVars)
|
||||||
|
of "msdos": result = targetOS == osDos
|
||||||
|
of "mswindows", "win32": result = targetOS == osWindows
|
||||||
|
of "macintosh": result = targetOS in {osMacos, osMacosx}
|
||||||
|
of "sunos": result = targetOS == osSolaris
|
||||||
|
of "littleendian": result = CPU[targetCPU].endian == platform.littleEndian
|
||||||
|
of "bigendian": result = CPU[targetCPU].endian == platform.bigEndian
|
||||||
|
of "cpu8": result = CPU[targetCPU].bit == 8
|
||||||
|
of "cpu16": result = CPU[targetCPU].bit == 16
|
||||||
|
of "cpu32": result = CPU[targetCPU].bit == 32
|
||||||
|
of "cpu64": result = CPU[targetCPU].bit == 64
|
||||||
|
of "nimrawsetjmp":
|
||||||
|
result = targetOS in {osSolaris, osNetbsd, osFreebsd, osOpenbsd, osMacosx}
|
||||||
|
else: discard
|
||||||
|
|
||||||
proc isDefined*(symbol: PIdent): bool = isDefined(symbol.s)
|
proc isDefined*(symbol: PIdent): bool = isDefined(symbol.s)
|
||||||
proc isDeclared*(symbol: PIdent): bool = gSymbols.hasKey(symbol.s)
|
|
||||||
|
|
||||||
iterator definedSymbolNames*: string =
|
iterator definedSymbolNames*: string =
|
||||||
for key, val in pairs(gSymbols):
|
for key, val in pairs(gSymbols):
|
||||||
if val == "true": yield key
|
if val != catNone: yield key
|
||||||
|
|
||||||
proc countDefinedSymbols*(): int =
|
proc countDefinedSymbols*(): int =
|
||||||
result = 0
|
result = 0
|
||||||
for key, val in pairs(gSymbols):
|
for key, val in pairs(gSymbols):
|
||||||
if val == "true": inc(result)
|
if val != catNone: inc(result)
|
||||||
|
|
||||||
# For ease of bootstrapping, we keep them here and not in the global config
|
proc initDefines*() =
|
||||||
# file for now:
|
|
||||||
const
|
|
||||||
additionalSymbols = """
|
|
||||||
x86 itanium x8664
|
|
||||||
msdos mswindows win32 unix posix sunos bsd macintosh RISCOS hpux
|
|
||||||
mac
|
|
||||||
|
|
||||||
hppa hp9000 hp9000s300 hp9000s700 hp9000s800 hp9000s820 ELATE sparcv9
|
|
||||||
|
|
||||||
ecmascript js nimrodvm nimffi nimdoc cpp objc
|
|
||||||
gcc llvmgcc clang lcc bcc dmc wcc vcc tcc pcc ucc icl
|
|
||||||
boehmgc gcmarkandsweep gcgenerational nogc gcUseBitvectors
|
|
||||||
endb profiler
|
|
||||||
executable guiapp consoleapp library dll staticlib
|
|
||||||
|
|
||||||
quick
|
|
||||||
release debug
|
|
||||||
useWinAnsi useFork useNimRtl useMalloc useRealtimeGC ssl memProfiler
|
|
||||||
nodejs kwin nimfix
|
|
||||||
|
|
||||||
usesysassert usegcassert tinyC useFFI
|
|
||||||
useStdoutAsStdmsg createNimRtl
|
|
||||||
booting fulldebug corruption nimsuperops noSignalHandler useGnuReadline
|
|
||||||
noCaas noDocGen noBusyWaiting nativeStackTrace useNodeIds selftest
|
|
||||||
reportMissedDeadlines avoidTimeMachine useClone ignoreAllocationSize
|
|
||||||
debugExecProcesses pcreDll useLipzipSrc
|
|
||||||
preventDeadlocks UNICODE winUnicode trackGcHeaders posixRealtime
|
|
||||||
|
|
||||||
nimStdSetjmp nimRawSetjmp nimSigSetjmp
|
|
||||||
""".split
|
|
||||||
|
|
||||||
proc initDefines*() =
|
|
||||||
gSymbols = newStringTable(modeStyleInsensitive)
|
gSymbols = newStringTable(modeStyleInsensitive)
|
||||||
defineSymbol("nimrod") # 'nimrod' is always defined
|
defineSymbol("nimrod") # 'nimrod' is always defined
|
||||||
# for bootstrapping purposes and old code:
|
# for bootstrapping purposes and old code:
|
||||||
|
|
@ -90,58 +88,3 @@ proc initDefines*() =
|
||||||
defineSymbol("nimalias")
|
defineSymbol("nimalias")
|
||||||
defineSymbol("nimlocks")
|
defineSymbol("nimlocks")
|
||||||
defineSymbol("nimnode")
|
defineSymbol("nimnode")
|
||||||
|
|
||||||
# add platform specific symbols:
|
|
||||||
for c in low(CPU)..high(CPU):
|
|
||||||
declareSymbol("cpu" & $CPU[c].bit)
|
|
||||||
declareSymbol(normalize(EndianToStr[CPU[c].endian]))
|
|
||||||
declareSymbol(CPU[c].name)
|
|
||||||
for o in low(platform.OS)..high(platform.OS):
|
|
||||||
declareSymbol(platform.OS[o].name)
|
|
||||||
|
|
||||||
for a in additionalSymbols:
|
|
||||||
declareSymbol(a)
|
|
||||||
|
|
||||||
# -----------------------------------------------------------
|
|
||||||
case targetCPU
|
|
||||||
of cpuI386: defineSymbol("x86")
|
|
||||||
of cpuIa64: defineSymbol("itanium")
|
|
||||||
of cpuAmd64: defineSymbol("x8664")
|
|
||||||
else: discard
|
|
||||||
case targetOS
|
|
||||||
of osDos:
|
|
||||||
defineSymbol("msdos")
|
|
||||||
of osWindows:
|
|
||||||
defineSymbol("mswindows")
|
|
||||||
defineSymbol("win32")
|
|
||||||
of osLinux, osMorphos, osSkyos, osIrix, osPalmos, osQnx, osAtari, osAix,
|
|
||||||
osHaiku, osVxWorks:
|
|
||||||
# these are all 'unix-like'
|
|
||||||
defineSymbol("unix")
|
|
||||||
defineSymbol("posix")
|
|
||||||
of osSolaris:
|
|
||||||
defineSymbol("sunos")
|
|
||||||
defineSymbol("unix")
|
|
||||||
defineSymbol("posix")
|
|
||||||
of osNetbsd, osFreebsd, osOpenbsd:
|
|
||||||
defineSymbol("unix")
|
|
||||||
defineSymbol("bsd")
|
|
||||||
defineSymbol("posix")
|
|
||||||
of osMacos:
|
|
||||||
defineSymbol("macintosh")
|
|
||||||
of osMacosx:
|
|
||||||
defineSymbol("macintosh")
|
|
||||||
defineSymbol("unix")
|
|
||||||
defineSymbol("posix")
|
|
||||||
else: discard
|
|
||||||
defineSymbol("cpu" & $CPU[targetCPU].bit)
|
|
||||||
defineSymbol(normalize(EndianToStr[CPU[targetCPU].endian]))
|
|
||||||
defineSymbol(CPU[targetCPU].name)
|
|
||||||
defineSymbol(platform.OS[targetOS].name)
|
|
||||||
declareSymbol("emulatedthreadvars")
|
|
||||||
if platform.OS[targetOS].props.contains(ospLacksThreadVars):
|
|
||||||
defineSymbol("emulatedthreadvars")
|
|
||||||
case targetOS
|
|
||||||
of osSolaris, osNetbsd, osFreebsd, osOpenbsd, osMacosx:
|
|
||||||
defineSymbol("nimRawSetjmp")
|
|
||||||
else: discard
|
|
||||||
|
|
|
||||||
|
|
@ -15,9 +15,9 @@
|
||||||
import
|
import
|
||||||
lists, ropes, os, strutils, osproc, platform, condsyms, options, msgs, crc
|
lists, ropes, os, strutils, osproc, platform, condsyms, options, msgs, crc
|
||||||
|
|
||||||
type
|
type
|
||||||
TSystemCC* = enum
|
TSystemCC* = enum
|
||||||
ccNone, ccGcc, ccLLVM_Gcc, ccCLang, ccLcc, ccBcc, ccDmc, ccWcc, ccVcc,
|
ccNone, ccGcc, ccLLVM_Gcc, ccCLang, ccLcc, ccBcc, ccDmc, ccWcc, ccVcc,
|
||||||
ccTcc, ccPcc, ccUcc, ccIcl
|
ccTcc, ccPcc, ccUcc, ccIcl
|
||||||
TInfoCCProp* = enum # properties of the C compiler:
|
TInfoCCProp* = enum # properties of the C compiler:
|
||||||
hasSwitchRange, # CC allows ranges in switch statements (GNU C)
|
hasSwitchRange, # CC allows ranges in switch statements (GNU C)
|
||||||
|
|
@ -54,7 +54,7 @@ type
|
||||||
props: TInfoCCProps] # properties of the C compiler
|
props: TInfoCCProps] # properties of the C compiler
|
||||||
|
|
||||||
|
|
||||||
# Configuration settings for various compilers.
|
# Configuration settings for various compilers.
|
||||||
# When adding new compilers, the cmake sources could be a good reference:
|
# When adding new compilers, the cmake sources could be a good reference:
|
||||||
# http://cmake.org/gitweb?p=cmake.git;a=tree;f=Modules/Platform;
|
# http://cmake.org/gitweb?p=cmake.git;a=tree;f=Modules/Platform;
|
||||||
|
|
||||||
|
|
@ -136,7 +136,7 @@ compiler icl:
|
||||||
result = vcc()
|
result = vcc()
|
||||||
else:
|
else:
|
||||||
result = gcc()
|
result = gcc()
|
||||||
|
|
||||||
result.name = "icl"
|
result.name = "icl"
|
||||||
result.compilerExe = "icl"
|
result.compilerExe = "icl"
|
||||||
result.linkerExe = "icl"
|
result.linkerExe = "icl"
|
||||||
|
|
@ -317,7 +317,7 @@ compiler ucc:
|
||||||
packedPragma: "", # XXX: not supported yet
|
packedPragma: "", # XXX: not supported yet
|
||||||
props: {})
|
props: {})
|
||||||
|
|
||||||
const
|
const
|
||||||
CC*: array[succ(low(TSystemCC))..high(TSystemCC), TInfoCC] = [
|
CC*: array[succ(low(TSystemCC))..high(TSystemCC), TInfoCC] = [
|
||||||
gcc(),
|
gcc(),
|
||||||
llvmGcc(),
|
llvmGcc(),
|
||||||
|
|
@ -346,7 +346,7 @@ var
|
||||||
proc libNameTmpl(): string {.inline.} =
|
proc libNameTmpl(): string {.inline.} =
|
||||||
result = if targetOS == osWindows: "$1.lib" else: "lib$1.a"
|
result = if targetOS == osWindows: "$1.lib" else: "lib$1.a"
|
||||||
|
|
||||||
var
|
var
|
||||||
toLink, toCompile, externalToCompile: TLinkedList
|
toLink, toCompile, externalToCompile: TLinkedList
|
||||||
linkOptions: string = ""
|
linkOptions: string = ""
|
||||||
compileOptions: string = ""
|
compileOptions: string = ""
|
||||||
|
|
@ -355,8 +355,8 @@ var
|
||||||
proc nameToCC*(name: string): TSystemCC =
|
proc nameToCC*(name: string): TSystemCC =
|
||||||
## Returns the kind of compiler referred to by `name`, or ccNone
|
## Returns the kind of compiler referred to by `name`, or ccNone
|
||||||
## if the name doesn't refer to any known compiler.
|
## if the name doesn't refer to any known compiler.
|
||||||
for i in countup(succ(ccNone), high(TSystemCC)):
|
for i in countup(succ(ccNone), high(TSystemCC)):
|
||||||
if cmpIgnoreStyle(name, CC[i].name) == 0:
|
if cmpIgnoreStyle(name, CC[i].name) == 0:
|
||||||
return i
|
return i
|
||||||
result = ccNone
|
result = ccNone
|
||||||
|
|
||||||
|
|
@ -375,8 +375,8 @@ proc getConfigVar(c: TSystemCC, suffix: string): string =
|
||||||
|
|
||||||
if (platform.hostOS != targetOS or platform.hostCPU != targetCPU) and
|
if (platform.hostOS != targetOS or platform.hostCPU != targetCPU) and
|
||||||
optCompileOnly notin gGlobalOptions:
|
optCompileOnly notin gGlobalOptions:
|
||||||
let fullCCname = platform.CPU[targetCPU].name & '.' &
|
let fullCCname = platform.CPU[targetCPU].name & '.' &
|
||||||
platform.OS[targetOS].name & '.' &
|
platform.OS[targetOS].name & '.' &
|
||||||
CC[c].name & fullSuffix
|
CC[c].name & fullSuffix
|
||||||
result = getConfigVar(fullCCname)
|
result = getConfigVar(fullCCname)
|
||||||
if result.len == 0:
|
if result.len == 0:
|
||||||
|
|
@ -385,7 +385,7 @@ proc getConfigVar(c: TSystemCC, suffix: string): string =
|
||||||
else:
|
else:
|
||||||
result = getConfigVar(CC[c].name & fullSuffix)
|
result = getConfigVar(CC[c].name & fullSuffix)
|
||||||
|
|
||||||
proc setCC*(ccname: string) =
|
proc setCC*(ccname: string) =
|
||||||
cCompiler = nameToCC(ccname)
|
cCompiler = nameToCC(ccname)
|
||||||
if cCompiler == ccNone: rawMessage(errUnknownCcompiler, ccname)
|
if cCompiler == ccNone: rawMessage(errUnknownCcompiler, ccname)
|
||||||
compileOptions = getConfigVar(cCompiler, ".options.always")
|
compileOptions = getConfigVar(cCompiler, ".options.always")
|
||||||
|
|
@ -394,18 +394,18 @@ proc setCC*(ccname: string) =
|
||||||
for i in countup(low(CC), high(CC)): undefSymbol(CC[i].name)
|
for i in countup(low(CC), high(CC)): undefSymbol(CC[i].name)
|
||||||
defineSymbol(CC[cCompiler].name)
|
defineSymbol(CC[cCompiler].name)
|
||||||
|
|
||||||
proc addOpt(dest: var string, src: string) =
|
proc addOpt(dest: var string, src: string) =
|
||||||
if len(dest) == 0 or dest[len(dest)-1] != ' ': add(dest, " ")
|
if len(dest) == 0 or dest[len(dest)-1] != ' ': add(dest, " ")
|
||||||
add(dest, src)
|
add(dest, src)
|
||||||
|
|
||||||
proc addLinkOption*(option: string) =
|
proc addLinkOption*(option: string) =
|
||||||
addOpt(linkOptions, option)
|
addOpt(linkOptions, option)
|
||||||
|
|
||||||
proc addCompileOption*(option: string) =
|
proc addCompileOption*(option: string) =
|
||||||
if strutils.find(compileOptions, option, 0) < 0:
|
if strutils.find(compileOptions, option, 0) < 0:
|
||||||
addOpt(compileOptions, option)
|
addOpt(compileOptions, option)
|
||||||
|
|
||||||
proc initVars*() =
|
proc initVars*() =
|
||||||
# we need to define the symbol here, because ``CC`` may have never been set!
|
# we need to define the symbol here, because ``CC`` may have never been set!
|
||||||
for i in countup(low(CC), high(CC)): undefSymbol(CC[i].name)
|
for i in countup(low(CC), high(CC)): undefSymbol(CC[i].name)
|
||||||
defineSymbol(CC[cCompiler].name)
|
defineSymbol(CC[cCompiler].name)
|
||||||
|
|
@ -414,10 +414,10 @@ proc initVars*() =
|
||||||
if len(ccompilerpath) == 0:
|
if len(ccompilerpath) == 0:
|
||||||
ccompilerpath = getConfigVar(cCompiler, ".path")
|
ccompilerpath = getConfigVar(cCompiler, ".path")
|
||||||
|
|
||||||
proc completeCFilePath*(cfile: string, createSubDir: bool = true): string =
|
proc completeCFilePath*(cfile: string, createSubDir: bool = true): string =
|
||||||
result = completeGeneratedFilePath(cfile, createSubDir)
|
result = completeGeneratedFilePath(cfile, createSubDir)
|
||||||
|
|
||||||
proc toObjFile*(filename: string): string =
|
proc toObjFile*(filename: string): string =
|
||||||
# Object file for compilation
|
# Object file for compilation
|
||||||
result = changeFileExt(filename, CC[cCompiler].objExt)
|
result = changeFileExt(filename, CC[cCompiler].objExt)
|
||||||
|
|
||||||
|
|
@ -449,22 +449,22 @@ proc execExternalProgram*(cmd: string, prettyCmd = "") =
|
||||||
if execWithEcho(cmd, prettyCmd) != 0:
|
if execWithEcho(cmd, prettyCmd) != 0:
|
||||||
rawMessage(errExecutionOfProgramFailed, "")
|
rawMessage(errExecutionOfProgramFailed, "")
|
||||||
|
|
||||||
proc generateScript(projectFile: string, script: Rope) =
|
proc generateScript(projectFile: string, script: Rope) =
|
||||||
let (dir, name, ext) = splitFile(projectFile)
|
let (dir, name, ext) = splitFile(projectFile)
|
||||||
writeRope(script, dir / addFileExt("compile_" & name,
|
writeRope(script, dir / addFileExt("compile_" & name,
|
||||||
platform.OS[targetOS].scriptExt))
|
platform.OS[targetOS].scriptExt))
|
||||||
|
|
||||||
proc getOptSpeed(c: TSystemCC): string =
|
proc getOptSpeed(c: TSystemCC): string =
|
||||||
result = getConfigVar(c, ".options.speed")
|
result = getConfigVar(c, ".options.speed")
|
||||||
if result == "":
|
if result == "":
|
||||||
result = CC[c].optSpeed # use default settings from this file
|
result = CC[c].optSpeed # use default settings from this file
|
||||||
|
|
||||||
proc getDebug(c: TSystemCC): string =
|
proc getDebug(c: TSystemCC): string =
|
||||||
result = getConfigVar(c, ".options.debug")
|
result = getConfigVar(c, ".options.debug")
|
||||||
if result == "":
|
if result == "":
|
||||||
result = CC[c].debug # use default settings from this file
|
result = CC[c].debug # use default settings from this file
|
||||||
|
|
||||||
proc getOptSize(c: TSystemCC): string =
|
proc getOptSize(c: TSystemCC): string =
|
||||||
result = getConfigVar(c, ".options.size")
|
result = getConfigVar(c, ".options.size")
|
||||||
if result == "":
|
if result == "":
|
||||||
result = CC[c].optSize # use default settings from this file
|
result = CC[c].optSize # use default settings from this file
|
||||||
|
|
@ -476,7 +476,7 @@ proc noAbsolutePaths: bool {.inline.} =
|
||||||
# `optGenMapping` is included here for niminst.
|
# `optGenMapping` is included here for niminst.
|
||||||
result = gGlobalOptions * {optGenScript, optGenMapping} != {}
|
result = gGlobalOptions * {optGenScript, optGenMapping} != {}
|
||||||
|
|
||||||
const
|
const
|
||||||
specialFileA = 42
|
specialFileA = 42
|
||||||
specialFileB = 42
|
specialFileB = 42
|
||||||
|
|
||||||
|
|
@ -488,7 +488,7 @@ proc add(s: var string, many: openArray[string]) =
|
||||||
proc cFileSpecificOptions(cfilename: string): string =
|
proc cFileSpecificOptions(cfilename: string): string =
|
||||||
result = compileOptions
|
result = compileOptions
|
||||||
var trunk = splitFile(cfilename).name
|
var trunk = splitFile(cfilename).name
|
||||||
if optCDebug in gGlobalOptions:
|
if optCDebug in gGlobalOptions:
|
||||||
var key = trunk & ".debug"
|
var key = trunk & ".debug"
|
||||||
if existsConfigVar(key): addOpt(result, getConfigVar(key))
|
if existsConfigVar(key): addOpt(result, getConfigVar(key))
|
||||||
else: addOpt(result, getDebug(cCompiler))
|
else: addOpt(result, getDebug(cCompiler))
|
||||||
|
|
@ -528,17 +528,17 @@ proc getLinkerExe(compiler: TSystemCC): string =
|
||||||
elif gMixedMode and gCmd != cmdCompileToCpp: CC[compiler].cppCompiler
|
elif gMixedMode and gCmd != cmdCompileToCpp: CC[compiler].cppCompiler
|
||||||
else: compiler.getCompilerExe
|
else: compiler.getCompilerExe
|
||||||
|
|
||||||
proc getCompileCFileCmd*(cfilename: string, isExternal = false): string =
|
proc getCompileCFileCmd*(cfilename: string, isExternal = false): string =
|
||||||
var c = cCompiler
|
var c = cCompiler
|
||||||
var options = cFileSpecificOptions(cfilename)
|
var options = cFileSpecificOptions(cfilename)
|
||||||
var exe = getConfigVar(c, ".exe")
|
var exe = getConfigVar(c, ".exe")
|
||||||
if exe.len == 0: exe = c.getCompilerExe
|
if exe.len == 0: exe = c.getCompilerExe
|
||||||
|
|
||||||
if needsExeExt(): exe = addFileExt(exe, "exe")
|
if needsExeExt(): exe = addFileExt(exe, "exe")
|
||||||
if optGenDynLib in gGlobalOptions and
|
if optGenDynLib in gGlobalOptions and
|
||||||
ospNeedsPIC in platform.OS[targetOS].props:
|
ospNeedsPIC in platform.OS[targetOS].props:
|
||||||
add(options, ' ' & CC[c].pic)
|
add(options, ' ' & CC[c].pic)
|
||||||
|
|
||||||
var includeCmd, compilePattern: string
|
var includeCmd, compilePattern: string
|
||||||
if not noAbsolutePaths():
|
if not noAbsolutePaths():
|
||||||
# compute include paths:
|
# compute include paths:
|
||||||
|
|
@ -551,7 +551,7 @@ proc getCompileCFileCmd*(cfilename: string, isExternal = false): string =
|
||||||
else:
|
else:
|
||||||
includeCmd = ""
|
includeCmd = ""
|
||||||
compilePattern = c.getCompilerExe
|
compilePattern = c.getCompilerExe
|
||||||
|
|
||||||
var cfile = if noAbsolutePaths(): extractFilename(cfilename)
|
var cfile = if noAbsolutePaths(): extractFilename(cfilename)
|
||||||
else: cfilename
|
else: cfilename
|
||||||
var objfile = if not isExternal or noAbsolutePaths():
|
var objfile = if not isExternal or noAbsolutePaths():
|
||||||
|
|
@ -580,14 +580,14 @@ proc footprint(filename: string): TCrc32 =
|
||||||
extccomp.CC[extccomp.cCompiler].name ><
|
extccomp.CC[extccomp.cCompiler].name ><
|
||||||
getCompileCFileCmd(filename, true)
|
getCompileCFileCmd(filename, true)
|
||||||
|
|
||||||
proc externalFileChanged(filename: string): bool =
|
proc externalFileChanged(filename: string): bool =
|
||||||
if gCmd notin {cmdCompileToC, cmdCompileToCpp, cmdCompileToOC, cmdCompileToLLVM}:
|
if gCmd notin {cmdCompileToC, cmdCompileToCpp, cmdCompileToOC, cmdCompileToLLVM}:
|
||||||
return false
|
return false
|
||||||
|
|
||||||
var crcFile = toGeneratedFile(filename.withPackageName, "crc")
|
var crcFile = toGeneratedFile(filename.withPackageName, "crc")
|
||||||
var currentCrc = int(footprint(filename))
|
var currentCrc = int(footprint(filename))
|
||||||
var f: File
|
var f: File
|
||||||
if open(f, crcFile, fmRead):
|
if open(f, crcFile, fmRead):
|
||||||
var line = newStringOfCap(40)
|
var line = newStringOfCap(40)
|
||||||
if not f.readLine(line): line = "0"
|
if not f.readLine(line): line = "0"
|
||||||
close(f)
|
close(f)
|
||||||
|
|
@ -595,7 +595,7 @@ proc externalFileChanged(filename: string): bool =
|
||||||
result = oldCrc != currentCrc
|
result = oldCrc != currentCrc
|
||||||
else:
|
else:
|
||||||
result = true
|
result = true
|
||||||
if result:
|
if result:
|
||||||
if open(f, crcFile, fmWrite):
|
if open(f, crcFile, fmWrite):
|
||||||
f.writeln($currentCrc)
|
f.writeln($currentCrc)
|
||||||
close(f)
|
close(f)
|
||||||
|
|
@ -607,22 +607,22 @@ proc addExternalFileToCompile*(filename: string) =
|
||||||
proc compileCFile(list: TLinkedList, script: var Rope, cmds: var TStringSeq,
|
proc compileCFile(list: TLinkedList, script: var Rope, cmds: var TStringSeq,
|
||||||
prettyCmds: var TStringSeq, isExternal: bool) =
|
prettyCmds: var TStringSeq, isExternal: bool) =
|
||||||
var it = PStrEntry(list.head)
|
var it = PStrEntry(list.head)
|
||||||
while it != nil:
|
while it != nil:
|
||||||
inc(fileCounter) # call the C compiler for the .c file:
|
inc(fileCounter) # call the C compiler for the .c file:
|
||||||
var compileCmd = getCompileCFileCmd(it.data, isExternal)
|
var compileCmd = getCompileCFileCmd(it.data, isExternal)
|
||||||
if optCompileOnly notin gGlobalOptions:
|
if optCompileOnly notin gGlobalOptions:
|
||||||
add(cmds, compileCmd)
|
add(cmds, compileCmd)
|
||||||
let (dir, name, ext) = splitFile(it.data)
|
let (dir, name, ext) = splitFile(it.data)
|
||||||
add(prettyCmds, "CC: " & name)
|
add(prettyCmds, "CC: " & name)
|
||||||
if optGenScript in gGlobalOptions:
|
if optGenScript in gGlobalOptions:
|
||||||
add(script, compileCmd)
|
add(script, compileCmd)
|
||||||
add(script, tnl)
|
add(script, tnl)
|
||||||
it = PStrEntry(it.next)
|
it = PStrEntry(it.next)
|
||||||
|
|
||||||
proc callCCompiler*(projectfile: string) =
|
proc callCCompiler*(projectfile: string) =
|
||||||
var
|
var
|
||||||
linkCmd, buildgui, builddll: string
|
linkCmd, buildgui, builddll: string
|
||||||
if gGlobalOptions * {optCompileOnly, optGenScript} == {optCompileOnly}:
|
if gGlobalOptions * {optCompileOnly, optGenScript} == {optCompileOnly}:
|
||||||
return # speed up that call if only compiling and no script shall be
|
return # speed up that call if only compiling and no script shall be
|
||||||
# generated
|
# generated
|
||||||
fileCounter = 0
|
fileCounter = 0
|
||||||
|
|
@ -634,11 +634,11 @@ proc callCCompiler*(projectfile: string) =
|
||||||
echo prettyCmds[idx]
|
echo prettyCmds[idx]
|
||||||
compileCFile(toCompile, script, cmds, prettyCmds, false)
|
compileCFile(toCompile, script, cmds, prettyCmds, false)
|
||||||
compileCFile(externalToCompile, script, cmds, prettyCmds, true)
|
compileCFile(externalToCompile, script, cmds, prettyCmds, true)
|
||||||
if optCompileOnly notin gGlobalOptions:
|
if optCompileOnly notin gGlobalOptions:
|
||||||
if gNumberOfProcessors == 0: gNumberOfProcessors = countProcessors()
|
if gNumberOfProcessors == 0: gNumberOfProcessors = countProcessors()
|
||||||
var res = 0
|
var res = 0
|
||||||
if gNumberOfProcessors <= 1:
|
if gNumberOfProcessors <= 1:
|
||||||
for i in countup(0, high(cmds)):
|
for i in countup(0, high(cmds)):
|
||||||
res = execWithEcho(cmds[i])
|
res = execWithEcho(cmds[i])
|
||||||
if res != 0: rawMessage(errExecutionOfProgramFailed, [])
|
if res != 0: rawMessage(errExecutionOfProgramFailed, [])
|
||||||
elif optListCmd in gGlobalOptions or gVerbosity > 1:
|
elif optListCmd in gGlobalOptions or gVerbosity > 1:
|
||||||
|
|
@ -668,7 +668,8 @@ proc callCCompiler*(projectfile: string) =
|
||||||
it = PStrEntry(it.next)
|
it = PStrEntry(it.next)
|
||||||
|
|
||||||
if optGenStaticLib in gGlobalOptions:
|
if optGenStaticLib in gGlobalOptions:
|
||||||
linkCmd = CC[c].buildLib % ["libfile", (libNameTmpl() % gProjectName),
|
let name = splitFile(gProjectName).name
|
||||||
|
linkCmd = CC[c].buildLib % ["libfile", (libNameTmpl() % name),
|
||||||
"objfiles", objfiles]
|
"objfiles", objfiles]
|
||||||
else:
|
else:
|
||||||
var linkerExe = getConfigVar(c, ".linkerexe")
|
var linkerExe = getConfigVar(c, ".linkerexe")
|
||||||
|
|
@ -685,13 +686,13 @@ proc callCCompiler*(projectfile: string) =
|
||||||
else:
|
else:
|
||||||
exefile = splitFile(projectfile).name & platform.OS[targetOS].exeExt
|
exefile = splitFile(projectfile).name & platform.OS[targetOS].exeExt
|
||||||
builddll = ""
|
builddll = ""
|
||||||
if options.outFile.len > 0:
|
if options.outFile.len > 0:
|
||||||
exefile = options.outFile.expandTilde
|
exefile = options.outFile.expandTilde
|
||||||
if not noAbsolutePaths():
|
if not noAbsolutePaths():
|
||||||
if not exefile.isAbsolute():
|
if not exefile.isAbsolute():
|
||||||
exefile = joinPath(splitFile(projectfile).dir, exefile)
|
exefile = joinPath(splitFile(projectfile).dir, exefile)
|
||||||
exefile = quoteShell(exefile)
|
exefile = quoteShell(exefile)
|
||||||
let linkOptions = getLinkOptions() & " " &
|
let linkOptions = getLinkOptions() & " " &
|
||||||
getConfigVar(cCompiler, ".options.linker")
|
getConfigVar(cCompiler, ".options.linker")
|
||||||
linkCmd = quoteShell(linkCmd % ["builddll", builddll,
|
linkCmd = quoteShell(linkCmd % ["builddll", builddll,
|
||||||
"buildgui", buildgui, "options", linkOptions, "objfiles", objfiles,
|
"buildgui", buildgui, "options", linkOptions, "objfiles", objfiles,
|
||||||
|
|
@ -714,26 +715,26 @@ proc callCCompiler*(projectfile: string) =
|
||||||
add(script, tnl)
|
add(script, tnl)
|
||||||
generateScript(projectfile, script)
|
generateScript(projectfile, script)
|
||||||
|
|
||||||
proc genMappingFiles(list: TLinkedList): Rope =
|
proc genMappingFiles(list: TLinkedList): Rope =
|
||||||
var it = PStrEntry(list.head)
|
var it = PStrEntry(list.head)
|
||||||
while it != nil:
|
while it != nil:
|
||||||
addf(result, "--file:r\"$1\"$N", [rope(it.data)])
|
addf(result, "--file:r\"$1\"$N", [rope(it.data)])
|
||||||
it = PStrEntry(it.next)
|
it = PStrEntry(it.next)
|
||||||
|
|
||||||
proc writeMapping*(gSymbolMapping: Rope) =
|
proc writeMapping*(gSymbolMapping: Rope) =
|
||||||
if optGenMapping notin gGlobalOptions: return
|
if optGenMapping notin gGlobalOptions: return
|
||||||
var code = rope("[C_Files]\n")
|
var code = rope("[C_Files]\n")
|
||||||
add(code, genMappingFiles(toCompile))
|
add(code, genMappingFiles(toCompile))
|
||||||
add(code, genMappingFiles(externalToCompile))
|
add(code, genMappingFiles(externalToCompile))
|
||||||
add(code, "\n[C_Compiler]\nFlags=")
|
add(code, "\n[C_Compiler]\nFlags=")
|
||||||
add(code, strutils.escape(getCompileOptions()))
|
add(code, strutils.escape(getCompileOptions()))
|
||||||
|
|
||||||
add(code, "\n[Linker]\nFlags=")
|
add(code, "\n[Linker]\nFlags=")
|
||||||
add(code, strutils.escape(getLinkOptions() & " " &
|
add(code, strutils.escape(getLinkOptions() & " " &
|
||||||
getConfigVar(cCompiler, ".options.linker")))
|
getConfigVar(cCompiler, ".options.linker")))
|
||||||
|
|
||||||
add(code, "\n[Environment]\nlibpath=")
|
add(code, "\n[Environment]\nlibpath=")
|
||||||
add(code, strutils.escape(libpath))
|
add(code, strutils.escape(libpath))
|
||||||
|
|
||||||
addf(code, "\n[Symbols]$n$1", [gSymbolMapping])
|
addf(code, "\n[Symbols]$n$1", [gSymbolMapping])
|
||||||
writeRope(code, joinPath(gProjectPath, "mapping.txt"))
|
writeRope(code, joinPath(gProjectPath, "mapping.txt"))
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,8 @@ const
|
||||||
someLt = {mLtI, mLtI64, mLtF64, mLtU, mLtU64, mLtEnum,
|
someLt = {mLtI, mLtI64, mLtF64, mLtU, mLtU64, mLtEnum,
|
||||||
mLtCh, mLtB, mLtPtr, mLtStr}
|
mLtCh, mLtB, mLtPtr, mLtStr}
|
||||||
|
|
||||||
someLen = {mLengthOpenArray, mLengthStr, mLengthArray, mLengthSeq}
|
someLen = {mLengthOpenArray, mLengthStr, mLengthArray, mLengthSeq,
|
||||||
|
mXLenStr, mXLenSeq}
|
||||||
|
|
||||||
someIn = {mInRange, mInSet}
|
someIn = {mInRange, mInSet}
|
||||||
|
|
||||||
|
|
@ -34,8 +35,8 @@ const
|
||||||
someMul = {mMulI, mMulI64, mMulF64}
|
someMul = {mMulI, mMulI64, mMulF64}
|
||||||
someDiv = {mDivI, mDivI64, mDivF64}
|
someDiv = {mDivI, mDivI64, mDivF64}
|
||||||
someMod = {mModI, mModI64}
|
someMod = {mModI, mModI64}
|
||||||
someMax = {mMaxI, mMaxI64, mMaxF64}
|
someMax = {mMaxI, mMaxF64}
|
||||||
someMin = {mMinI, mMinI64, mMinF64}
|
someMin = {mMinI, mMinF64}
|
||||||
|
|
||||||
proc isValue(n: PNode): bool = n.kind in {nkCharLit..nkNilLit}
|
proc isValue(n: PNode): bool = n.kind in {nkCharLit..nkNilLit}
|
||||||
proc isLocation(n: PNode): bool = not n.isValue
|
proc isLocation(n: PNode): bool = not n.isValue
|
||||||
|
|
@ -122,7 +123,7 @@ proc neg(n: PNode): PNode =
|
||||||
let eAsNode = newIntNode(nkIntLit, e.sym.position)
|
let eAsNode = newIntNode(nkIntLit, e.sym.position)
|
||||||
if not inSet(n.sons[1], eAsNode): s.add eAsNode
|
if not inSet(n.sons[1], eAsNode): s.add eAsNode
|
||||||
result.sons[1] = s
|
result.sons[1] = s
|
||||||
elif lengthOrd(t) < 1000:
|
elif t.kind notin {tyString, tySequence} and lengthOrd(t) < 1000:
|
||||||
result.sons[1] = complement(n.sons[1])
|
result.sons[1] = complement(n.sons[1])
|
||||||
else:
|
else:
|
||||||
# not ({2, 3, 4}.contains(x)) x != 2 and x != 3 and x != 4
|
# not ({2, 3, 4}.contains(x)) x != 2 and x != 3 and x != 4
|
||||||
|
|
@ -907,5 +908,5 @@ proc buildProperFieldCheck(access, check: PNode): PNode =
|
||||||
proc checkFieldAccess*(m: TModel, n: PNode) =
|
proc checkFieldAccess*(m: TModel, n: PNode) =
|
||||||
for i in 1..n.len-1:
|
for i in 1..n.len-1:
|
||||||
let check = buildProperFieldCheck(n.sons[0], n.sons[i])
|
let check = buildProperFieldCheck(n.sons[0], n.sons[i])
|
||||||
if m.doesImply(check) != impYes:
|
if check != nil and m.doesImply(check) != impYes:
|
||||||
message(n.info, warnProveField, renderTree(n.sons[0])); break
|
message(n.info, warnProveField, renderTree(n.sons[0])); break
|
||||||
|
|
|
||||||
|
|
@ -47,7 +47,7 @@ Start: "doc/overview.html"
|
||||||
|
|
||||||
[Other]
|
[Other]
|
||||||
Files: "readme.txt;install.txt;contributors.txt;copying.txt"
|
Files: "readme.txt;install.txt;contributors.txt;copying.txt"
|
||||||
Files: "configure;makefile"
|
Files: "makefile"
|
||||||
Files: "*.ini"
|
Files: "*.ini"
|
||||||
Files: "koch.nim"
|
Files: "koch.nim"
|
||||||
|
|
||||||
|
|
@ -70,6 +70,10 @@ Files: "doc/*.nim"
|
||||||
Files: "doc/*.cfg"
|
Files: "doc/*.cfg"
|
||||||
Files: "compiler/nimfix/*.nim"
|
Files: "compiler/nimfix/*.nim"
|
||||||
Files: "compiler/nimfix/*.cfg"
|
Files: "compiler/nimfix/*.cfg"
|
||||||
|
Files: "compiler/nimsuggest/*.nim"
|
||||||
|
Files: "compiler/nimsuggest/*.cfg"
|
||||||
|
Files: "compiler/plugins/locals/*.nim"
|
||||||
|
Files: "compiler/plugins/active.nim"
|
||||||
Files: "tools/*.nim"
|
Files: "tools/*.nim"
|
||||||
Files: "tools/*.cfg"
|
Files: "tools/*.cfg"
|
||||||
Files: "tools/*.tmpl"
|
Files: "tools/*.tmpl"
|
||||||
|
|
@ -97,13 +101,8 @@ Files: "lib/pure/concurrency/*.cfg"
|
||||||
Files: "lib/impure/*.nim"
|
Files: "lib/impure/*.nim"
|
||||||
Files: "lib/wrappers/*.nim"
|
Files: "lib/wrappers/*.nim"
|
||||||
|
|
||||||
Files: "lib/wrappers/cairo/*.nim"
|
|
||||||
Files: "lib/wrappers/gtk/*.nim"
|
|
||||||
Files: "lib/wrappers/lua/*.nim"
|
|
||||||
Files: "lib/wrappers/opengl/*.nim"
|
|
||||||
Files: "lib/wrappers/readline/*.nim"
|
Files: "lib/wrappers/readline/*.nim"
|
||||||
Files: "lib/wrappers/sdl/*.nim"
|
Files: "lib/wrappers/sdl/*.nim"
|
||||||
Files: "lib/wrappers/x11/*.nim"
|
|
||||||
Files: "lib/wrappers/zip/*.nim"
|
Files: "lib/wrappers/zip/*.nim"
|
||||||
Files: "lib/wrappers/zip/libzip_all.c"
|
Files: "lib/wrappers/zip/libzip_all.c"
|
||||||
|
|
||||||
|
|
@ -115,8 +114,6 @@ Files: "lib/packages/docutils/*.nim"
|
||||||
|
|
||||||
[Other]
|
[Other]
|
||||||
Files: "examples/*.nim"
|
Files: "examples/*.nim"
|
||||||
Files: "examples/gtk/*.nim"
|
|
||||||
Files: "examples/0mq/*.nim"
|
|
||||||
Files: "examples/c++iface/*.nim"
|
Files: "examples/c++iface/*.nim"
|
||||||
Files: "examples/objciface/*.nim"
|
Files: "examples/objciface/*.nim"
|
||||||
Files: "examples/cross_calculator/"
|
Files: "examples/cross_calculator/"
|
||||||
|
|
@ -126,12 +123,109 @@ Files: "examples/*.txt"
|
||||||
Files: "examples/*.cfg"
|
Files: "examples/*.cfg"
|
||||||
Files: "examples/*.tmpl"
|
Files: "examples/*.tmpl"
|
||||||
|
|
||||||
|
Files: "tests/actiontable/*.nim"
|
||||||
|
Files: "tests/alias/*.nim"
|
||||||
|
Files: "tests/ambsym/*.nim"
|
||||||
|
Files: "tests/array/*.nim"
|
||||||
|
Files: "tests/assign/*.nim"
|
||||||
|
Files: "tests/astoverload/*.nim"
|
||||||
|
Files: "tests/async/*.nim"
|
||||||
|
Files: "tests/benchmarks/*.nim"
|
||||||
|
Files: "tests/bind/*.nim"
|
||||||
|
Files: "tests/borrow/*.nim"
|
||||||
|
Files: "tests/casestmt/*.nim"
|
||||||
|
Files: "tests/ccgbugs/*.nim"
|
||||||
|
Files: "tests/clearmsg/*.nim"
|
||||||
|
Files: "tests/closure/*.nim"
|
||||||
|
Files: "tests/cnstseq/*.nim"
|
||||||
|
Files: "tests/collections/*.nim"
|
||||||
|
Files: "tests/compiles/*.nim"
|
||||||
|
Files: "tests/concat/*.nim"
|
||||||
|
Files: "tests/concepts/*.nim"
|
||||||
|
Files: "tests/constr/*.nim"
|
||||||
|
Files: "tests/constraints/*.nim"
|
||||||
|
Files: "tests/controlflow/*.nim"
|
||||||
|
Files: "tests/converter/*.nim"
|
||||||
|
Files: "tests/cpp/*.nim"
|
||||||
|
Files: "tests/defaultprocparam/*.nim"
|
||||||
|
Files: "tests/deprecated/*.nim"
|
||||||
|
Files: "tests/destructor/*.nim"
|
||||||
|
Files: "tests/dir with space/*.nim"
|
||||||
|
Files: "tests/discard/*.nim"
|
||||||
|
Files: "tests/distinct/*.nim"
|
||||||
|
Files: "tests/dll/*.nim"
|
||||||
|
Files: "tests/effects/*.nim"
|
||||||
|
Files: "tests/enum/*.nim"
|
||||||
|
Files: "tests/exception/*.nim"
|
||||||
|
Files: "tests/exprs/*.nim"
|
||||||
|
Files: "tests/fields/*.nim"
|
||||||
|
Files: "tests/float/*.nim"
|
||||||
|
Files: "tests/friends/*.nim"
|
||||||
|
Files: "tests/gc/*.nim"
|
||||||
|
Files: "tests/generics/*.nim"
|
||||||
|
Files: "tests/gensym/*.nim"
|
||||||
|
Files: "tests/global/*.nim"
|
||||||
|
Files: "tests/implicit/*.nim"
|
||||||
|
Files: "tests/init/*.nim"
|
||||||
|
Files: "tests/iter/*.nim"
|
||||||
|
Files: "tests/js/*.nim"
|
||||||
|
Files: "tests/js/*.cfg"
|
||||||
|
Files: "tests/let/*.nim"
|
||||||
|
Files: "tests/lexer/*.nim"
|
||||||
|
Files: "tests/lookups/*.nim"
|
||||||
|
Files: "tests/macros/*.nim"
|
||||||
|
Files: "tests/magics/*.nim"
|
||||||
|
Files: "tests/metatype/*.nim"
|
||||||
|
Files: "tests/method/*.nim"
|
||||||
|
Files: "tests/misc/*.nim"
|
||||||
|
Files: "tests/modules/*.nim"
|
||||||
|
Files: "tests/namedparams/*.nim"
|
||||||
|
Files: "tests/notnil/*.nim"
|
||||||
|
Files: "tests/objects/*.nim"
|
||||||
|
Files: "tests/objvariant/*.nim"
|
||||||
|
Files: "tests/openarray/*.nim"
|
||||||
|
Files: "tests/osproc/*.nim"
|
||||||
|
Files: "tests/overflw/*.nim"
|
||||||
|
Files: "tests/overload/*.nim"
|
||||||
|
Files: "tests/parallel/*.nim"
|
||||||
|
Files: "tests/parallel/*.cfg"
|
||||||
|
Files: "tests/parser/*.nim"
|
||||||
|
Files: "tests/pragmas/*.nim"
|
||||||
|
Files: "tests/proc/*.nim"
|
||||||
|
Files: "tests/procvar/*.nim"
|
||||||
|
Files: "tests/range/*.nim"
|
||||||
|
Files: "tests/rodfiles/*.nim"
|
||||||
|
Files: "tests/seq/*.nim"
|
||||||
|
Files: "tests/sets/*.nim"
|
||||||
|
Files: "tests/showoff/*.nim"
|
||||||
|
Files: "tests/specialops/*.nim"
|
||||||
|
Files: "tests/stdlib/*.nim"
|
||||||
|
Files: "tests/system/*.nim"
|
||||||
|
Files: "tests/template/*.nim"
|
||||||
|
Files: "tests/testament/*.nim"
|
||||||
|
Files: "tests/testdata/*.nim"
|
||||||
|
Files: "tests/threads/*.nim"
|
||||||
|
Files: "tests/threads/*.cfg"
|
||||||
|
Files: "tests/trmacros/*.nim"
|
||||||
|
Files: "tests/tuples/*.nim"
|
||||||
|
Files: "tests/typerel/*.nim"
|
||||||
|
Files: "tests/types/*.nim"
|
||||||
|
Files: "tests/usingstmt/*.nim"
|
||||||
|
Files: "tests/varres/*.nim"
|
||||||
|
Files: "tests/varstmt/*.nim"
|
||||||
|
Files: "tests/vm/*.nim"
|
||||||
|
Files: "tests/readme.txt"
|
||||||
|
Files: "tests/testament/css/*.css"
|
||||||
|
Files: "tests/testament/*.cfg"
|
||||||
|
Files: "lib/pure/unidecode/unidecode.dat"
|
||||||
|
|
||||||
[Windows]
|
[Windows]
|
||||||
Files: "bin/nim.exe"
|
Files: "bin/nim.exe"
|
||||||
Files: "bin/nim_debug.exe"
|
|
||||||
Files: "bin/c2nim.exe"
|
Files: "bin/c2nim.exe"
|
||||||
Files: "bin/nimgrep.exe"
|
Files: "bin/nimgrep.exe"
|
||||||
|
Files: "bin/nimsuggest.exe"
|
||||||
|
Files: "bin/nimble.exe"
|
||||||
|
Files: "bin/*.dll"
|
||||||
|
|
||||||
Files: "dist/*.dll"
|
Files: "dist/*.dll"
|
||||||
Files: "koch.exe"
|
Files: "koch.exe"
|
||||||
|
|
@ -142,7 +236,7 @@ BinPath: r"bin;dist\mingw\bin;dist"
|
||||||
; Section | dir | zipFile | size hint (in KB) | url | exe start menu entry
|
; Section | dir | zipFile | size hint (in KB) | url | exe start menu entry
|
||||||
Download: r"Documentation|doc|docs.zip|13824|http://nim-lang.org/download/docs-${version}.zip|overview.html"
|
Download: r"Documentation|doc|docs.zip|13824|http://nim-lang.org/download/docs-${version}.zip|overview.html"
|
||||||
Download: r"C Compiler (MingW)|dist|mingw.zip|82944|http://nim-lang.org/download/${mingw}.zip"
|
Download: r"C Compiler (MingW)|dist|mingw.zip|82944|http://nim-lang.org/download/${mingw}.zip"
|
||||||
Download: r"Aporia IDE|dist|aporia.zip|97997|http://nim-lang.org/download/aporia-0.1.3.zip|aporia\bin\aporia.exe"
|
Download: r"Aporia IDE|dist|aporia.zip|97997|http://nim-lang.org/download/aporia-0.3.0.zip|aporia-0.3.0\bin\aporia.exe"
|
||||||
; for now only NSIS supports optional downloads
|
; for now only NSIS supports optional downloads
|
||||||
|
|
||||||
[UnixBin]
|
[UnixBin]
|
||||||
|
|
|
||||||
|
|
@ -124,7 +124,7 @@ proc newProc(globals: PGlobals, module: BModule, procDef: PNode,
|
||||||
|
|
||||||
const
|
const
|
||||||
MappedToObject = {tyObject, tyArray, tyArrayConstr, tyTuple, tyOpenArray,
|
MappedToObject = {tyObject, tyArray, tyArrayConstr, tyTuple, tyOpenArray,
|
||||||
tySet, tyVar, tyRef, tyPtr, tyBigNum, tyVarargs}
|
tySet, tyBigNum, tyVarargs}
|
||||||
|
|
||||||
proc mapType(typ: PType): TJSTypeKind =
|
proc mapType(typ: PType): TJSTypeKind =
|
||||||
let t = skipTypes(typ, abstractInst)
|
let t = skipTypes(typ, abstractInst)
|
||||||
|
|
@ -163,7 +163,8 @@ proc mangleName(s: PSym): Rope =
|
||||||
add(result, rope(s.id))
|
add(result, rope(s.id))
|
||||||
s.loc.r = result
|
s.loc.r = result
|
||||||
|
|
||||||
proc makeJSString(s: string): Rope = strutils.escape(s).rope
|
proc makeJSString(s: string): Rope =
|
||||||
|
(if s.isNil: "null".rope else: strutils.escape(s).rope)
|
||||||
|
|
||||||
include jstypes
|
include jstypes
|
||||||
|
|
||||||
|
|
@ -280,8 +281,6 @@ const # magic checked op; magic unchecked op; checked op; unchecked op
|
||||||
["", "", "($1 & $2)", "($1 & $2)"], # BitandI64
|
["", "", "($1 & $2)", "($1 & $2)"], # BitandI64
|
||||||
["", "", "($1 | $2)", "($1 | $2)"], # BitorI64
|
["", "", "($1 | $2)", "($1 | $2)"], # BitorI64
|
||||||
["", "", "($1 ^ $2)", "($1 ^ $2)"], # BitxorI64
|
["", "", "($1 ^ $2)", "($1 ^ $2)"], # BitxorI64
|
||||||
["nimMin", "nimMin", "nimMin($1, $2)", "nimMin($1, $2)"], # MinI64
|
|
||||||
["nimMax", "nimMax", "nimMax($1, $2)", "nimMax($1, $2)"], # MaxI64
|
|
||||||
["nimMin", "nimMin", "nimMin($1, $2)", "nimMin($1, $2)"], # MinF64
|
["nimMin", "nimMin", "nimMin($1, $2)", "nimMin($1, $2)"], # MinF64
|
||||||
["nimMax", "nimMax", "nimMax($1, $2)", "nimMax($1, $2)"], # MaxF64
|
["nimMax", "nimMax", "nimMax($1, $2)", "nimMax($1, $2)"], # MaxF64
|
||||||
["addU", "addU", "addU($1, $2)", "addU($1, $2)"], # addU
|
["addU", "addU", "addU($1, $2)", "addU($1, $2)"], # addU
|
||||||
|
|
@ -325,7 +324,6 @@ const # magic checked op; magic unchecked op; checked op; unchecked op
|
||||||
["", "", "!($1)", "!($1)"], # Not
|
["", "", "!($1)", "!($1)"], # Not
|
||||||
["", "", "+($1)", "+($1)"], # UnaryPlusI
|
["", "", "+($1)", "+($1)"], # UnaryPlusI
|
||||||
["", "", "~($1)", "~($1)"], # BitnotI
|
["", "", "~($1)", "~($1)"], # BitnotI
|
||||||
["", "", "+($1)", "+($1)"], # UnaryPlusI64
|
|
||||||
["", "", "~($1)", "~($1)"], # BitnotI64
|
["", "", "~($1)", "~($1)"], # BitnotI64
|
||||||
["", "", "+($1)", "+($1)"], # UnaryPlusF64
|
["", "", "+($1)", "+($1)"], # UnaryPlusF64
|
||||||
["", "", "-($1)", "-($1)"], # UnaryMinusF64
|
["", "", "-($1)", "-($1)"], # UnaryMinusF64
|
||||||
|
|
@ -382,8 +380,6 @@ const # magic checked op; magic unchecked op; checked op; unchecked op
|
||||||
["", "", "($1 & $2)", "($1 & $2)"], # BitandI64
|
["", "", "($1 & $2)", "($1 & $2)"], # BitandI64
|
||||||
["", "", "($1 | $2)", "($1 | $2)"], # BitorI64
|
["", "", "($1 | $2)", "($1 | $2)"], # BitorI64
|
||||||
["", "", "($1 ^ $2)", "($1 ^ $2)"], # BitxorI64
|
["", "", "($1 ^ $2)", "($1 ^ $2)"], # BitxorI64
|
||||||
["nimMin", "nimMin", "nimMin($1, $2)", "nimMin($1, $2)"], # MinI64
|
|
||||||
["nimMax", "nimMax", "nimMax($1, $2)", "nimMax($1, $2)"], # MaxI64
|
|
||||||
["nimMin", "nimMin", "nimMin($1, $2)", "nimMin($1, $2)"], # MinF64
|
["nimMin", "nimMin", "nimMin($1, $2)", "nimMin($1, $2)"], # MinF64
|
||||||
["nimMax", "nimMax", "nimMax($1, $2)", "nimMax($1, $2)"], # MaxF64
|
["nimMax", "nimMax", "nimMax($1, $2)", "nimMax($1, $2)"], # MaxF64
|
||||||
["addU", "addU", "addU($1, $2)", "addU($1, $2)"], # addU
|
["addU", "addU", "addU($1, $2)", "addU($1, $2)"], # addU
|
||||||
|
|
@ -427,7 +423,6 @@ const # magic checked op; magic unchecked op; checked op; unchecked op
|
||||||
["", "", "not ($1)", "not ($1)"], # Not
|
["", "", "not ($1)", "not ($1)"], # Not
|
||||||
["", "", "+($1)", "+($1)"], # UnaryPlusI
|
["", "", "+($1)", "+($1)"], # UnaryPlusI
|
||||||
["", "", "~($1)", "~($1)"], # BitnotI
|
["", "", "~($1)", "~($1)"], # BitnotI
|
||||||
["", "", "+($1)", "+($1)"], # UnaryPlusI64
|
|
||||||
["", "", "~($1)", "~($1)"], # BitnotI64
|
["", "", "~($1)", "~($1)"], # BitnotI64
|
||||||
["", "", "+($1)", "+($1)"], # UnaryPlusF64
|
["", "", "+($1)", "+($1)"], # UnaryPlusF64
|
||||||
["", "", "-($1)", "-($1)"], # UnaryMinusF64
|
["", "", "-($1)", "-($1)"], # UnaryMinusF64
|
||||||
|
|
@ -937,6 +932,13 @@ proc genArrayAccess(p: PProc, n: PNode, r: var TCompRes) =
|
||||||
r.address = nil
|
r.address = nil
|
||||||
r.kind = resExpr
|
r.kind = resExpr
|
||||||
|
|
||||||
|
proc isIndirect(v: PSym): bool =
|
||||||
|
result = {sfAddrTaken, sfGlobal} * v.flags != {} and
|
||||||
|
#(mapType(v.typ) != etyObject) and
|
||||||
|
{sfImportc, sfVolatile, sfExportc} * v.flags == {} and
|
||||||
|
v.kind notin {skProc, skConverter, skMethod, skIterator, skClosureIterator,
|
||||||
|
skConst, skTemp, skLet}
|
||||||
|
|
||||||
proc genAddr(p: PProc, n: PNode, r: var TCompRes) =
|
proc genAddr(p: PProc, n: PNode, r: var TCompRes) =
|
||||||
case n.sons[0].kind
|
case n.sons[0].kind
|
||||||
of nkSym:
|
of nkSym:
|
||||||
|
|
@ -945,12 +947,16 @@ proc genAddr(p: PProc, n: PNode, r: var TCompRes) =
|
||||||
case s.kind
|
case s.kind
|
||||||
of skVar, skLet, skResult:
|
of skVar, skLet, skResult:
|
||||||
r.kind = resExpr
|
r.kind = resExpr
|
||||||
if mapType(n.sons[0].typ) == etyObject:
|
let jsType = mapType(n.typ)
|
||||||
|
if jsType == etyObject:
|
||||||
# make addr() a no-op:
|
# make addr() a no-op:
|
||||||
r.typ = etyNone
|
r.typ = etyNone
|
||||||
r.res = s.loc.r
|
if isIndirect(s):
|
||||||
|
r.res = s.loc.r & "[0]"
|
||||||
|
else:
|
||||||
|
r.res = s.loc.r
|
||||||
r.address = nil
|
r.address = nil
|
||||||
elif {sfGlobal, sfAddrTaken} * s.flags != {}:
|
elif {sfGlobal, sfAddrTaken} * s.flags != {} or jsType == etyBaseIndex:
|
||||||
# for ease of code generation, we do not distinguish between
|
# for ease of code generation, we do not distinguish between
|
||||||
# sfAddrTaken and sfGlobal.
|
# sfAddrTaken and sfGlobal.
|
||||||
r.typ = etyBaseIndex
|
r.typ = etyBaseIndex
|
||||||
|
|
@ -992,7 +998,7 @@ proc genSym(p: PProc, n: PNode, r: var TCompRes) =
|
||||||
else:
|
else:
|
||||||
r.address = s.loc.r
|
r.address = s.loc.r
|
||||||
r.res = s.loc.r & "_Idx"
|
r.res = s.loc.r & "_Idx"
|
||||||
elif k != etyObject and {sfAddrTaken, sfGlobal} * s.flags != {}:
|
elif isIndirect(s):
|
||||||
r.res = "$1[0]" % [s.loc.r]
|
r.res = "$1[0]" % [s.loc.r]
|
||||||
else:
|
else:
|
||||||
r.res = s.loc.r
|
r.res = s.loc.r
|
||||||
|
|
@ -1124,7 +1130,7 @@ proc createVar(p: PProc, typ: PType, indirect: bool): Rope =
|
||||||
of tyRange, tyGenericInst:
|
of tyRange, tyGenericInst:
|
||||||
result = createVar(p, lastSon(typ), indirect)
|
result = createVar(p, lastSon(typ), indirect)
|
||||||
of tySet:
|
of tySet:
|
||||||
result = rope("{}")
|
result = putToSeq("{}", indirect)
|
||||||
of tyBool:
|
of tyBool:
|
||||||
result = putToSeq("false", indirect)
|
result = putToSeq("false", indirect)
|
||||||
of tyArray, tyArrayConstr:
|
of tyArray, tyArrayConstr:
|
||||||
|
|
@ -1144,6 +1150,7 @@ proc createVar(p: PProc, typ: PType, indirect: bool): Rope =
|
||||||
add(result, createVar(p, e, false))
|
add(result, createVar(p, e, false))
|
||||||
inc(i)
|
inc(i)
|
||||||
add(result, "]")
|
add(result, "]")
|
||||||
|
if indirect: result = "[$1]" % [result]
|
||||||
of tyTuple:
|
of tyTuple:
|
||||||
result = rope("{")
|
result = rope("{")
|
||||||
for i in 0.. <t.sonsLen:
|
for i in 0.. <t.sonsLen:
|
||||||
|
|
@ -1151,6 +1158,7 @@ proc createVar(p: PProc, typ: PType, indirect: bool): Rope =
|
||||||
addf(result, "Field$1: $2" | "Field$# = $#", [i.rope,
|
addf(result, "Field$1: $2" | "Field$# = $#", [i.rope,
|
||||||
createVar(p, t.sons[i], false)])
|
createVar(p, t.sons[i], false)])
|
||||||
add(result, "}")
|
add(result, "}")
|
||||||
|
if indirect: result = "[$1]" % [result]
|
||||||
of tyObject:
|
of tyObject:
|
||||||
result = rope("{")
|
result = rope("{")
|
||||||
var c = 0
|
var c = 0
|
||||||
|
|
@ -1161,6 +1169,7 @@ proc createVar(p: PProc, typ: PType, indirect: bool): Rope =
|
||||||
add(result, createRecordVarAux(p, t.n, c))
|
add(result, createRecordVarAux(p, t.n, c))
|
||||||
t = t.sons[0]
|
t = t.sons[0]
|
||||||
add(result, "}")
|
add(result, "}")
|
||||||
|
if indirect: result = "[$1]" % [result]
|
||||||
of tyVar, tyPtr, tyRef:
|
of tyVar, tyPtr, tyRef:
|
||||||
if mapType(t) == etyBaseIndex:
|
if mapType(t) == etyBaseIndex:
|
||||||
result = putToSeq("[null, 0]" | "{nil, 0}", indirect)
|
result = putToSeq("[null, 0]" | "{nil, 0}", indirect)
|
||||||
|
|
@ -1172,11 +1181,6 @@ proc createVar(p: PProc, typ: PType, indirect: bool): Rope =
|
||||||
internalError("createVar: " & $t.kind)
|
internalError("createVar: " & $t.kind)
|
||||||
result = nil
|
result = nil
|
||||||
|
|
||||||
proc isIndirect(v: PSym): bool =
|
|
||||||
result = {sfAddrTaken, sfGlobal} * v.flags != {} and
|
|
||||||
(mapType(v.typ) != etyObject) and
|
|
||||||
v.kind notin {skProc, skConverter, skMethod, skIterator, skClosureIterator}
|
|
||||||
|
|
||||||
proc genVarInit(p: PProc, v: PSym, n: PNode) =
|
proc genVarInit(p: PProc, v: PSym, n: PNode) =
|
||||||
var
|
var
|
||||||
a: TCompRes
|
a: TCompRes
|
||||||
|
|
@ -1207,7 +1211,7 @@ proc genVarInit(p: PProc, v: PSym, n: PNode) =
|
||||||
else:
|
else:
|
||||||
s = a.res
|
s = a.res
|
||||||
if isIndirect(v):
|
if isIndirect(v):
|
||||||
addf(p.body, "var $1 = [$2];$n" | "local $1 = {$2};$n", [v.loc.r, s])
|
addf(p.body, "var $1 = /**/[$2];$n" | "local $1 = {$2};$n", [v.loc.r, s])
|
||||||
else:
|
else:
|
||||||
addf(p.body, "var $1 = $2;$n" | "local $1 = $2;$n", [v.loc.r, s])
|
addf(p.body, "var $1 = $2;$n" | "local $1 = $2;$n", [v.loc.r, s])
|
||||||
|
|
||||||
|
|
@ -1239,7 +1243,7 @@ proc genNew(p: PProc, n: PNode) =
|
||||||
var a: TCompRes
|
var a: TCompRes
|
||||||
gen(p, n.sons[1], a)
|
gen(p, n.sons[1], a)
|
||||||
var t = skipTypes(n.sons[1].typ, abstractVar).sons[0]
|
var t = skipTypes(n.sons[1].typ, abstractVar).sons[0]
|
||||||
addf(p.body, "$1 = $2;$n", [a.res, createVar(p, t, true)])
|
addf(p.body, "$1 = $2;$n", [a.res, createVar(p, t, false)])
|
||||||
|
|
||||||
proc genNewSeq(p: PProc, n: PNode) =
|
proc genNewSeq(p: PProc, n: PNode) =
|
||||||
var x, y: TCompRes
|
var x, y: TCompRes
|
||||||
|
|
@ -1325,14 +1329,17 @@ proc genMagic(p: PProc, n: PNode, r: var TCompRes) =
|
||||||
# XXX: range checking?
|
# XXX: range checking?
|
||||||
if not (optOverflowCheck in p.options): unaryExpr(p, n, r, "", "$1 - 1")
|
if not (optOverflowCheck in p.options): unaryExpr(p, n, r, "", "$1 - 1")
|
||||||
else: unaryExpr(p, n, r, "subInt", "subInt($1, 1)")
|
else: unaryExpr(p, n, r, "subInt", "subInt($1, 1)")
|
||||||
of mAppendStrCh: binaryExpr(p, n, r, "addChar", "addChar($1, $2)")
|
of mAppendStrCh: binaryExpr(p, n, r, "addChar",
|
||||||
|
"if ($1 != null) { addChar($1, $2); } else { $1 = [$2, 0]; }")
|
||||||
of mAppendStrStr:
|
of mAppendStrStr:
|
||||||
if skipTypes(n.sons[1].typ, abstractVarRange).kind == tyCString:
|
if skipTypes(n.sons[1].typ, abstractVarRange).kind == tyCString:
|
||||||
binaryExpr(p, n, r, "", "$1 += $2")
|
binaryExpr(p, n, r, "", "if ($1 != null) { $1 += $2; } else { $1 = $2; }")
|
||||||
else:
|
else:
|
||||||
binaryExpr(p, n, r, "", "$1 = ($1.slice(0, -1)).concat($2)")
|
binaryExpr(p, n, r, "",
|
||||||
|
"if ($1 != null) { $1 = ($1.slice(0, -1)).concat($2); } else { $1 = $2;}")
|
||||||
# XXX: make a copy of $2, because of Javascript's sucking semantics
|
# XXX: make a copy of $2, because of Javascript's sucking semantics
|
||||||
of mAppendSeqElem: binaryExpr(p, n, r, "", "$1.push($2)")
|
of mAppendSeqElem: binaryExpr(p, n, r, "",
|
||||||
|
"if ($1 != null) { $1.push($2); } else { $1 = [$2]; }")
|
||||||
of mConStrStr: genConStrStr(p, n, r)
|
of mConStrStr: genConStrStr(p, n, r)
|
||||||
of mEqStr: binaryExpr(p, n, r, "eqStrings", "eqStrings($1, $2)")
|
of mEqStr: binaryExpr(p, n, r, "eqStrings", "eqStrings($1, $2)")
|
||||||
of mLeStr: binaryExpr(p, n, r, "cmpStrings", "(cmpStrings($1, $2) <= 0)")
|
of mLeStr: binaryExpr(p, n, r, "cmpStrings", "(cmpStrings($1, $2) <= 0)")
|
||||||
|
|
@ -1343,14 +1350,17 @@ proc genMagic(p: PProc, n: PNode, r: var TCompRes) =
|
||||||
of mSizeOf: r.res = rope(getSize(n.sons[1].typ))
|
of mSizeOf: r.res = rope(getSize(n.sons[1].typ))
|
||||||
of mChr, mArrToSeq: gen(p, n.sons[1], r) # nothing to do
|
of mChr, mArrToSeq: gen(p, n.sons[1], r) # nothing to do
|
||||||
of mOrd: genOrd(p, n, r)
|
of mOrd: genOrd(p, n, r)
|
||||||
of mLengthStr: unaryExpr(p, n, r, "", "($1.length-1)")
|
of mLengthStr: unaryExpr(p, n, r, "", "($1 != null ? $1.length-1 : 0)")
|
||||||
|
of mXLenStr: unaryExpr(p, n, r, "", "$1.length-1")
|
||||||
of mLengthSeq, mLengthOpenArray, mLengthArray:
|
of mLengthSeq, mLengthOpenArray, mLengthArray:
|
||||||
|
unaryExpr(p, n, r, "", "($1 != null ? $1.length : 0)")
|
||||||
|
of mXLenSeq:
|
||||||
unaryExpr(p, n, r, "", "$1.length")
|
unaryExpr(p, n, r, "", "$1.length")
|
||||||
of mHigh:
|
of mHigh:
|
||||||
if skipTypes(n.sons[1].typ, abstractVar).kind == tyString:
|
if skipTypes(n.sons[1].typ, abstractVar).kind == tyString:
|
||||||
unaryExpr(p, n, r, "", "($1.length-2)")
|
unaryExpr(p, n, r, "", "($1 != null ? ($1.length-2) : -1)")
|
||||||
else:
|
else:
|
||||||
unaryExpr(p, n, r, "", "($1.length-1)")
|
unaryExpr(p, n, r, "", "($1 != null ? ($1.length-1) : -1)")
|
||||||
of mInc:
|
of mInc:
|
||||||
if optOverflowCheck notin p.options: binaryExpr(p, n, r, "", "$1 += $2")
|
if optOverflowCheck notin p.options: binaryExpr(p, n, r, "", "$1 += $2")
|
||||||
else: binaryExpr(p, n, r, "addInt", "$1 = addInt($1, $2)")
|
else: binaryExpr(p, n, r, "addInt", "$1 = addInt($1, $2)")
|
||||||
|
|
|
||||||
|
|
@ -61,7 +61,7 @@ type
|
||||||
tkComma, tkSemiColon,
|
tkComma, tkSemiColon,
|
||||||
tkColon, tkColonColon, tkEquals, tkDot, tkDotDot,
|
tkColon, tkColonColon, tkEquals, tkDot, tkDotDot,
|
||||||
tkOpr, tkComment, tkAccent,
|
tkOpr, tkComment, tkAccent,
|
||||||
tkSpaces, tkInfixOpr, tkPrefixOpr, tkPostfixOpr,
|
tkSpaces, tkInfixOpr, tkPrefixOpr, tkPostfixOpr
|
||||||
|
|
||||||
TTokTypes* = set[TTokType]
|
TTokTypes* = set[TTokType]
|
||||||
|
|
||||||
|
|
@ -221,6 +221,10 @@ proc dispMessage(L: TLexer; info: TLineInfo; msg: TMsgKind; arg: string) =
|
||||||
proc lexMessage*(L: TLexer, msg: TMsgKind, arg = "") =
|
proc lexMessage*(L: TLexer, msg: TMsgKind, arg = "") =
|
||||||
L.dispMessage(getLineInfo(L), msg, arg)
|
L.dispMessage(getLineInfo(L), msg, arg)
|
||||||
|
|
||||||
|
proc lexMessageTok*(L: TLexer, msg: TMsgKind, tok: TToken, arg = "") =
|
||||||
|
var info = newLineInfo(L.fileIdx, tok.line, tok.col)
|
||||||
|
L.dispMessage(info, msg, arg)
|
||||||
|
|
||||||
proc lexMessagePos(L: var TLexer, msg: TMsgKind, pos: int, arg = "") =
|
proc lexMessagePos(L: var TLexer, msg: TMsgKind, pos: int, arg = "") =
|
||||||
var info = newLineInfo(L.fileIdx, L.lineNumber, pos - L.lineStart)
|
var info = newLineInfo(L.fileIdx, L.lineNumber, pos - L.lineStart)
|
||||||
L.dispMessage(info, msg, arg)
|
L.dispMessage(info, msg, arg)
|
||||||
|
|
@ -863,6 +867,15 @@ proc rawGetTok*(L: var TLexer, tok: var TToken) =
|
||||||
of '`':
|
of '`':
|
||||||
tok.tokType = tkAccent
|
tok.tokType = tkAccent
|
||||||
inc(L.bufpos)
|
inc(L.bufpos)
|
||||||
|
of '_':
|
||||||
|
inc(L.bufpos)
|
||||||
|
if L.buf[L.bufpos] notin SymChars:
|
||||||
|
tok.tokType = tkSymbol
|
||||||
|
tok.ident = getIdent("_")
|
||||||
|
else:
|
||||||
|
tok.literal = $c
|
||||||
|
tok.tokType = tkInvalid
|
||||||
|
lexMessage(L, errInvalidToken, c & " (\\" & $(ord(c)) & ')')
|
||||||
of '\"':
|
of '\"':
|
||||||
# check for extended raw string literal:
|
# check for extended raw string literal:
|
||||||
var rawMode = L.bufpos > 0 and L.buf[L.bufpos-1] in SymChars
|
var rawMode = L.bufpos > 0 and L.buf[L.bufpos-1] in SymChars
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,10 @@ const
|
||||||
import ast, astalgo, types, idents, magicsys, msgs, options
|
import ast, astalgo, types, idents, magicsys, msgs, options
|
||||||
from trees import getMagic
|
from trees import getMagic
|
||||||
|
|
||||||
|
proc newDeref*(n: PNode): PNode {.inline.} =
|
||||||
|
result = newNodeIT(nkHiddenDeref, n.info, n.typ.sons[0])
|
||||||
|
addSon(result, n)
|
||||||
|
|
||||||
proc newTupleAccess*(tup: PNode, i: int): PNode =
|
proc newTupleAccess*(tup: PNode, i: int): PNode =
|
||||||
result = newNodeIT(nkBracketExpr, tup.info, tup.typ.skipTypes(
|
result = newNodeIT(nkBracketExpr, tup.info, tup.typ.skipTypes(
|
||||||
abstractInst).sons[i])
|
abstractInst).sons[i])
|
||||||
|
|
|
||||||
|
|
@ -738,7 +738,7 @@ proc writeContext(lastinfo: TLineInfo) =
|
||||||
if msgContext[i] != lastinfo and msgContext[i] != info:
|
if msgContext[i] != lastinfo and msgContext[i] != info:
|
||||||
msgWriteln(PosContextFormat % [toMsgFilename(msgContext[i]),
|
msgWriteln(PosContextFormat % [toMsgFilename(msgContext[i]),
|
||||||
coordToStr(msgContext[i].line),
|
coordToStr(msgContext[i].line),
|
||||||
coordToStr(msgContext[i].col),
|
coordToStr(msgContext[i].col+1),
|
||||||
getMessageStr(errInstantiationFrom, "")])
|
getMessageStr(errInstantiationFrom, "")])
|
||||||
info = msgContext[i]
|
info = msgContext[i]
|
||||||
|
|
||||||
|
|
@ -781,7 +781,7 @@ proc formatMsg*(info: TLineInfo, msg: TMsgKind, arg: string): string =
|
||||||
of hintMin..hintMax: PosHintFormat
|
of hintMin..hintMax: PosHintFormat
|
||||||
else: PosErrorFormat
|
else: PosErrorFormat
|
||||||
result = frmt % [toMsgFilename(info), coordToStr(info.line),
|
result = frmt % [toMsgFilename(info), coordToStr(info.line),
|
||||||
coordToStr(info.col), getMessageStr(msg, arg)]
|
coordToStr(info.col+1), getMessageStr(msg, arg)]
|
||||||
|
|
||||||
proc liMessage(info: TLineInfo, msg: TMsgKind, arg: string,
|
proc liMessage(info: TLineInfo, msg: TMsgKind, arg: string,
|
||||||
eh: TErrorHandling) =
|
eh: TErrorHandling) =
|
||||||
|
|
@ -804,8 +804,11 @@ proc liMessage(info: TLineInfo, msg: TMsgKind, arg: string,
|
||||||
ignoreMsg = optHints notin gOptions or msg notin gNotes
|
ignoreMsg = optHints notin gOptions or msg notin gNotes
|
||||||
frmt = PosHintFormat
|
frmt = PosHintFormat
|
||||||
inc(gHintCounter)
|
inc(gHintCounter)
|
||||||
|
# NOTE: currently line info line numbers start with 1,
|
||||||
|
# but column numbers start with 0, however most editors expect
|
||||||
|
# first column to be 1, so we need to +1 here
|
||||||
let s = frmt % [toMsgFilename(info), coordToStr(info.line),
|
let s = frmt % [toMsgFilename(info), coordToStr(info.line),
|
||||||
coordToStr(info.col), getMessageStr(msg, arg)]
|
coordToStr(info.col+1), getMessageStr(msg, arg)]
|
||||||
if not ignoreMsg and not ignoreMsgBecauseOfIdeTools(msg):
|
if not ignoreMsg and not ignoreMsgBecauseOfIdeTools(msg):
|
||||||
msgWriteln(s)
|
msgWriteln(s)
|
||||||
if optPrintSurroundingSrc and msg in errMin..errMax:
|
if optPrintSurroundingSrc and msg in errMin..errMax:
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ hint[XDeclaredButNotUsed]:off
|
||||||
path:"$projectPath/.."
|
path:"$projectPath/.."
|
||||||
|
|
||||||
path:"$lib/packages/docutils"
|
path:"$lib/packages/docutils"
|
||||||
path:"$nim/compiler"
|
path:"../../compiler"
|
||||||
|
|
||||||
define:useStdoutAsStdmsg
|
define:useStdoutAsStdmsg
|
||||||
symbol:nimfix
|
symbol:nimfix
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ hint[XDeclaredButNotUsed]:off
|
||||||
path:"$projectPath/../.."
|
path:"$projectPath/../.."
|
||||||
|
|
||||||
path:"$lib/packages/docutils"
|
path:"$lib/packages/docutils"
|
||||||
path:"$nim/compiler"
|
path:"../../compiler"
|
||||||
|
|
||||||
define:useStdoutAsStdmsg
|
define:useStdoutAsStdmsg
|
||||||
define:nimsuggest
|
define:nimsuggest
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
#
|
#
|
||||||
#
|
#
|
||||||
# The Nim Compiler
|
# The Nim Compiler
|
||||||
# (c) Copyright 2013 Andreas Rumpf
|
# (c) Copyright 2015 Andreas Rumpf
|
||||||
#
|
#
|
||||||
# See the file "copying.txt", included in this
|
# See the file "copying.txt", included in this
|
||||||
# distribution, for details about the copyright.
|
# distribution, for details about the copyright.
|
||||||
|
|
|
||||||
|
|
@ -190,6 +190,8 @@ proc isAssignable*(owner: PSym, n: PNode): TAssignableResult =
|
||||||
result = arLocalLValue
|
result = arLocalLValue
|
||||||
else:
|
else:
|
||||||
result = arLValue
|
result = arLValue
|
||||||
|
elif n.sym.kind == skParam and n.sym.typ.kind == tyVar:
|
||||||
|
result = arLValue
|
||||||
elif n.sym.kind == skType:
|
elif n.sym.kind == skType:
|
||||||
let t = n.sym.typ.skipTypes({tyTypeDesc})
|
let t = n.sym.typ.skipTypes({tyTypeDesc})
|
||||||
if t.kind == tyVar: result = arStrange
|
if t.kind == tyVar: result = arStrange
|
||||||
|
|
|
||||||
|
|
@ -91,7 +91,7 @@ proc closeParser(p: var TParser) =
|
||||||
|
|
||||||
proc parMessage(p: TParser, msg: TMsgKind, arg = "") =
|
proc parMessage(p: TParser, msg: TMsgKind, arg = "") =
|
||||||
## Produce and emit the parser message `arg` to output.
|
## Produce and emit the parser message `arg` to output.
|
||||||
lexMessage(p.lex, msg, arg)
|
lexMessageTok(p.lex, msg, p.tok, arg)
|
||||||
|
|
||||||
proc parMessage(p: TParser, msg: TMsgKind, tok: TToken) =
|
proc parMessage(p: TParser, msg: TMsgKind, tok: TToken) =
|
||||||
## Produce and emit a parser message to output about the token `tok`
|
## Produce and emit a parser message to output about the token `tok`
|
||||||
|
|
@ -154,7 +154,7 @@ proc eat(p: var TParser, tokType: TTokType) =
|
||||||
if p.tok.tokType == tokType:
|
if p.tok.tokType == tokType:
|
||||||
getTok(p)
|
getTok(p)
|
||||||
else:
|
else:
|
||||||
lexMessage(p.lex, errTokenExpected, TokTypeToStr[tokType])
|
lexMessageTok(p.lex, errTokenExpected, p.tok, TokTypeToStr[tokType])
|
||||||
|
|
||||||
proc parLineInfo(p: TParser): TLineInfo =
|
proc parLineInfo(p: TParser): TLineInfo =
|
||||||
## Retrieve the line information associated with the parser's current state.
|
## Retrieve the line information associated with the parser's current state.
|
||||||
|
|
@ -212,7 +212,8 @@ proc getPrecedence(tok: TToken, strongSpaces: bool): int =
|
||||||
let relevantChar = tok.ident.s[0]
|
let relevantChar = tok.ident.s[0]
|
||||||
|
|
||||||
# arrow like?
|
# arrow like?
|
||||||
if L > 1 and tok.ident.s[L-1] == '>': return considerStrongSpaces(1)
|
if L > 1 and tok.ident.s[L-1] == '>' and
|
||||||
|
tok.ident.s[L-2] in {'-', '~', '='}: return considerStrongSpaces(1)
|
||||||
|
|
||||||
template considerAsgn(value: expr) =
|
template considerAsgn(value: expr) =
|
||||||
result = if tok.ident.s[L-1] == '=': 1 else: value
|
result = if tok.ident.s[L-1] == '=': 1 else: value
|
||||||
|
|
@ -388,7 +389,6 @@ proc exprList(p: var TParser, endTok: TTokType, result: PNode) =
|
||||||
if p.tok.tokType != tkComma: break
|
if p.tok.tokType != tkComma: break
|
||||||
getTok(p)
|
getTok(p)
|
||||||
optInd(p, a)
|
optInd(p, a)
|
||||||
eat(p, endTok)
|
|
||||||
|
|
||||||
proc dotExpr(p: var TParser, a: PNode): PNode =
|
proc dotExpr(p: var TParser, a: PNode): PNode =
|
||||||
#| dotExpr = expr '.' optInd symbol
|
#| dotExpr = expr '.' optInd symbol
|
||||||
|
|
@ -944,8 +944,7 @@ proc parseDoBlock(p: var TParser): PNode =
|
||||||
getTok(p)
|
getTok(p)
|
||||||
let params = parseParamList(p, retColon=false)
|
let params = parseParamList(p, retColon=false)
|
||||||
let pragmas = optPragmas(p)
|
let pragmas = optPragmas(p)
|
||||||
eat(p, tkColon)
|
colcom(p, result)
|
||||||
skipComment(p, result)
|
|
||||||
result = newProcNode(nkDo, info, parseStmt(p),
|
result = newProcNode(nkDo, info, parseStmt(p),
|
||||||
params = params,
|
params = params,
|
||||||
pragmas = pragmas)
|
pragmas = pragmas)
|
||||||
|
|
@ -1139,9 +1138,11 @@ proc parseMacroColon(p: var TParser, x: PNode): PNode =
|
||||||
result = makeCall(result)
|
result = makeCall(result)
|
||||||
getTok(p)
|
getTok(p)
|
||||||
skipComment(p, result)
|
skipComment(p, result)
|
||||||
|
let stmtList = newNodeP(nkStmtList, p)
|
||||||
if p.tok.tokType notin {tkOf, tkElif, tkElse, tkExcept}:
|
if p.tok.tokType notin {tkOf, tkElif, tkElse, tkExcept}:
|
||||||
let body = parseStmt(p)
|
let body = parseStmt(p)
|
||||||
addSon(result, makeStmtList(body))
|
stmtList.add body
|
||||||
|
#addSon(result, makeStmtList(body))
|
||||||
while sameInd(p):
|
while sameInd(p):
|
||||||
var b: PNode
|
var b: PNode
|
||||||
case p.tok.tokType
|
case p.tok.tokType
|
||||||
|
|
@ -1153,19 +1154,22 @@ proc parseMacroColon(p: var TParser, x: PNode): PNode =
|
||||||
getTok(p)
|
getTok(p)
|
||||||
optInd(p, b)
|
optInd(p, b)
|
||||||
addSon(b, parseExpr(p))
|
addSon(b, parseExpr(p))
|
||||||
eat(p, tkColon)
|
|
||||||
of tkExcept:
|
of tkExcept:
|
||||||
b = newNodeP(nkExceptBranch, p)
|
b = newNodeP(nkExceptBranch, p)
|
||||||
exprList(p, tkColon, b)
|
exprList(p, tkColon, b)
|
||||||
skipComment(p, b)
|
|
||||||
of tkElse:
|
of tkElse:
|
||||||
b = newNodeP(nkElse, p)
|
b = newNodeP(nkElse, p)
|
||||||
getTok(p)
|
getTok(p)
|
||||||
eat(p, tkColon)
|
|
||||||
else: break
|
else: break
|
||||||
|
eat(p, tkColon)
|
||||||
addSon(b, parseStmt(p))
|
addSon(b, parseStmt(p))
|
||||||
addSon(result, b)
|
addSon(stmtList, b)
|
||||||
if b.kind == nkElse: break
|
if b.kind == nkElse: break
|
||||||
|
if stmtList.len == 1 and stmtList[0].kind == nkStmtList:
|
||||||
|
# to keep backwards compatibility (see tests/vm/tstringnil)
|
||||||
|
result.add stmtList[0]
|
||||||
|
else:
|
||||||
|
result.add stmtList
|
||||||
|
|
||||||
proc parseExprStmt(p: var TParser): PNode =
|
proc parseExprStmt(p: var TParser): PNode =
|
||||||
#| exprStmt = simpleExpr
|
#| exprStmt = simpleExpr
|
||||||
|
|
@ -1310,8 +1314,7 @@ proc parseIfOrWhen(p: var TParser, kind: TNodeKind): PNode =
|
||||||
var branch = newNodeP(nkElifBranch, p)
|
var branch = newNodeP(nkElifBranch, p)
|
||||||
optInd(p, branch)
|
optInd(p, branch)
|
||||||
addSon(branch, parseExpr(p))
|
addSon(branch, parseExpr(p))
|
||||||
eat(p, tkColon)
|
colcom(p, branch)
|
||||||
skipComment(p, branch)
|
|
||||||
addSon(branch, parseStmt(p))
|
addSon(branch, parseStmt(p))
|
||||||
skipComment(p, branch)
|
skipComment(p, branch)
|
||||||
addSon(result, branch)
|
addSon(result, branch)
|
||||||
|
|
@ -1319,8 +1322,7 @@ proc parseIfOrWhen(p: var TParser, kind: TNodeKind): PNode =
|
||||||
if p.tok.tokType == tkElse and sameOrNoInd(p):
|
if p.tok.tokType == tkElse and sameOrNoInd(p):
|
||||||
var branch = newNodeP(nkElse, p)
|
var branch = newNodeP(nkElse, p)
|
||||||
eat(p, tkElse)
|
eat(p, tkElse)
|
||||||
eat(p, tkColon)
|
colcom(p, branch)
|
||||||
skipComment(p, branch)
|
|
||||||
addSon(branch, parseStmt(p))
|
addSon(branch, parseStmt(p))
|
||||||
addSon(result, branch)
|
addSon(result, branch)
|
||||||
|
|
||||||
|
|
@ -1368,13 +1370,11 @@ proc parseCase(p: var TParser): PNode =
|
||||||
getTok(p)
|
getTok(p)
|
||||||
optInd(p, b)
|
optInd(p, b)
|
||||||
addSon(b, parseExpr(p))
|
addSon(b, parseExpr(p))
|
||||||
eat(p, tkColon)
|
|
||||||
of tkElse:
|
of tkElse:
|
||||||
b = newNodeP(nkElse, p)
|
b = newNodeP(nkElse, p)
|
||||||
getTok(p)
|
getTok(p)
|
||||||
eat(p, tkColon)
|
|
||||||
else: break
|
else: break
|
||||||
skipComment(p, b)
|
colcom(p, b)
|
||||||
addSon(b, parseStmt(p))
|
addSon(b, parseStmt(p))
|
||||||
addSon(result, b)
|
addSon(result, b)
|
||||||
if b.kind == nkElse: break
|
if b.kind == nkElse: break
|
||||||
|
|
@ -1391,8 +1391,7 @@ proc parseTry(p: var TParser; isExpr: bool): PNode =
|
||||||
#| (optInd 'finally' colcom stmt)?
|
#| (optInd 'finally' colcom stmt)?
|
||||||
result = newNodeP(nkTryStmt, p)
|
result = newNodeP(nkTryStmt, p)
|
||||||
getTok(p)
|
getTok(p)
|
||||||
eat(p, tkColon)
|
colcom(p, result)
|
||||||
skipComment(p, result)
|
|
||||||
addSon(result, parseStmt(p))
|
addSon(result, parseStmt(p))
|
||||||
var b: PNode = nil
|
var b: PNode = nil
|
||||||
while sameOrNoInd(p) or isExpr:
|
while sameOrNoInd(p) or isExpr:
|
||||||
|
|
@ -1402,10 +1401,9 @@ proc parseTry(p: var TParser; isExpr: bool): PNode =
|
||||||
exprList(p, tkColon, b)
|
exprList(p, tkColon, b)
|
||||||
of tkFinally:
|
of tkFinally:
|
||||||
b = newNodeP(nkFinally, p)
|
b = newNodeP(nkFinally, p)
|
||||||
getTokNoInd(p)
|
getTok(p)
|
||||||
eat(p, tkColon)
|
|
||||||
else: break
|
else: break
|
||||||
skipComment(p, b)
|
colcom(p, b)
|
||||||
addSon(b, parseStmt(p))
|
addSon(b, parseStmt(p))
|
||||||
addSon(result, b)
|
addSon(result, b)
|
||||||
if b.kind == nkFinally: break
|
if b.kind == nkFinally: break
|
||||||
|
|
@ -1414,7 +1412,7 @@ proc parseTry(p: var TParser; isExpr: bool): PNode =
|
||||||
proc parseExceptBlock(p: var TParser, kind: TNodeKind): PNode =
|
proc parseExceptBlock(p: var TParser, kind: TNodeKind): PNode =
|
||||||
#| exceptBlock = 'except' colcom stmt
|
#| exceptBlock = 'except' colcom stmt
|
||||||
result = newNodeP(kind, p)
|
result = newNodeP(kind, p)
|
||||||
getTokNoInd(p)
|
getTok(p)
|
||||||
colcom(p, result)
|
colcom(p, result)
|
||||||
addSon(result, parseStmt(p))
|
addSon(result, parseStmt(p))
|
||||||
|
|
||||||
|
|
@ -1447,7 +1445,7 @@ proc parseStaticOrDefer(p: var TParser; k: TNodeKind): PNode =
|
||||||
#| staticStmt = 'static' colcom stmt
|
#| staticStmt = 'static' colcom stmt
|
||||||
#| deferStmt = 'defer' colcom stmt
|
#| deferStmt = 'defer' colcom stmt
|
||||||
result = newNodeP(k, p)
|
result = newNodeP(k, p)
|
||||||
getTokNoInd(p)
|
getTok(p)
|
||||||
colcom(p, result)
|
colcom(p, result)
|
||||||
addSon(result, parseStmt(p))
|
addSon(result, parseStmt(p))
|
||||||
|
|
||||||
|
|
@ -1628,7 +1626,7 @@ proc parseEnum(p: var TParser): PNode =
|
||||||
p.tok.tokType == tkEof:
|
p.tok.tokType == tkEof:
|
||||||
break
|
break
|
||||||
if result.len <= 1:
|
if result.len <= 1:
|
||||||
lexMessage(p.lex, errIdentifierExpected, prettyTok(p.tok))
|
lexMessageTok(p.lex, errIdentifierExpected, p.tok, prettyTok(p.tok))
|
||||||
|
|
||||||
proc parseObjectPart(p: var TParser): PNode
|
proc parseObjectPart(p: var TParser): PNode
|
||||||
proc parseObjectWhen(p: var TParser): PNode =
|
proc parseObjectWhen(p: var TParser): PNode =
|
||||||
|
|
@ -1686,9 +1684,8 @@ proc parseObjectCase(p: var TParser): PNode =
|
||||||
of tkElse:
|
of tkElse:
|
||||||
b = newNodeP(nkElse, p)
|
b = newNodeP(nkElse, p)
|
||||||
getTok(p)
|
getTok(p)
|
||||||
eat(p, tkColon)
|
|
||||||
else: break
|
else: break
|
||||||
skipComment(p, b)
|
colcom(p, b)
|
||||||
var fields = parseObjectPart(p)
|
var fields = parseObjectPart(p)
|
||||||
if fields.kind == nkEmpty:
|
if fields.kind == nkEmpty:
|
||||||
parMessage(p, errIdentifierExpected, p.tok)
|
parMessage(p, errIdentifierExpected, p.tok)
|
||||||
|
|
@ -1884,7 +1881,7 @@ proc simpleStmt(p: var TParser): PNode =
|
||||||
|
|
||||||
proc complexOrSimpleStmt(p: var TParser): PNode =
|
proc complexOrSimpleStmt(p: var TParser): PNode =
|
||||||
#| complexOrSimpleStmt = (ifStmt | whenStmt | whileStmt
|
#| complexOrSimpleStmt = (ifStmt | whenStmt | whileStmt
|
||||||
#| | tryStmt | finallyStmt | exceptStmt | forStmt
|
#| | tryStmt | forStmt
|
||||||
#| | blockStmt | staticStmt | deferStmt | asmStmt
|
#| | blockStmt | staticStmt | deferStmt | asmStmt
|
||||||
#| | 'proc' routine
|
#| | 'proc' routine
|
||||||
#| | 'method' routine
|
#| | 'method' routine
|
||||||
|
|
|
||||||
43
compiler/plugins.nim
Normal file
43
compiler/plugins.nim
Normal file
|
|
@ -0,0 +1,43 @@
|
||||||
|
#
|
||||||
|
#
|
||||||
|
# The Nim Compiler
|
||||||
|
# (c) Copyright 2015 Andreas Rumpf
|
||||||
|
#
|
||||||
|
# See the file "copying.txt", included in this
|
||||||
|
# distribution, for details about the copyright.
|
||||||
|
#
|
||||||
|
|
||||||
|
## Plugin support for the Nim compiler. Right now there are no plugins and they
|
||||||
|
## need to be build with the compiler, no DLL support.
|
||||||
|
|
||||||
|
import ast, semdata, idents
|
||||||
|
|
||||||
|
type
|
||||||
|
Transformation* = proc (c: PContext; n: PNode): PNode {.nimcall.}
|
||||||
|
Plugin = ref object
|
||||||
|
fn, module, package: PIdent
|
||||||
|
t: Transformation
|
||||||
|
next: Plugin
|
||||||
|
|
||||||
|
proc pluginMatches(p: Plugin; s: PSym): bool =
|
||||||
|
if s.name.id != p.fn.id: return false
|
||||||
|
let module = s.owner
|
||||||
|
if module == nil or module.kind != skModule or
|
||||||
|
module.name.id != p.module.id: return false
|
||||||
|
let package = module.owner
|
||||||
|
if package == nil or package.kind != skPackage or
|
||||||
|
package.name.id != p.package.id: return false
|
||||||
|
return true
|
||||||
|
|
||||||
|
var head: Plugin
|
||||||
|
|
||||||
|
proc getPlugin*(fn: PSym): Transformation =
|
||||||
|
var it = head
|
||||||
|
while it != nil:
|
||||||
|
if pluginMatches(it, fn): return it.t
|
||||||
|
it = it.next
|
||||||
|
|
||||||
|
proc registerPlugin*(package, module, fn: string; t: Transformation) =
|
||||||
|
let oldHead = head
|
||||||
|
head = Plugin(fn: getIdent(fn), module: getIdent(module),
|
||||||
|
package: getIdent(package), t: t, next: oldHead)
|
||||||
13
compiler/plugins/active.nim
Normal file
13
compiler/plugins/active.nim
Normal file
|
|
@ -0,0 +1,13 @@
|
||||||
|
#
|
||||||
|
#
|
||||||
|
# The Nim Compiler
|
||||||
|
# (c) Copyright 2015 Andreas Rumpf
|
||||||
|
#
|
||||||
|
# See the file "copying.txt", included in this
|
||||||
|
# distribution, for details about the copyright.
|
||||||
|
#
|
||||||
|
|
||||||
|
## Include file that imports all plugins that are active.
|
||||||
|
|
||||||
|
import
|
||||||
|
locals.locals
|
||||||
42
compiler/plugins/locals/locals.nim
Normal file
42
compiler/plugins/locals/locals.nim
Normal file
|
|
@ -0,0 +1,42 @@
|
||||||
|
#
|
||||||
|
#
|
||||||
|
# The Nim Compiler
|
||||||
|
# (c) Copyright 2015 Andreas Rumpf
|
||||||
|
#
|
||||||
|
# See the file "copying.txt", included in this
|
||||||
|
# distribution, for details about the copyright.
|
||||||
|
#
|
||||||
|
|
||||||
|
## The builtin 'system.locals' implemented as a plugin.
|
||||||
|
|
||||||
|
import plugins, ast, astalgo, magicsys, lookups, semdata, lowerings
|
||||||
|
|
||||||
|
proc semLocals(c: PContext, n: PNode): PNode =
|
||||||
|
var counter = 0
|
||||||
|
var tupleType = newTypeS(tyTuple, c)
|
||||||
|
result = newNodeIT(nkPar, n.info, tupleType)
|
||||||
|
tupleType.n = newNodeI(nkRecList, n.info)
|
||||||
|
# for now we skip openarrays ...
|
||||||
|
for scope in walkScopes(c.currentScope):
|
||||||
|
if scope == c.topLevelScope: break
|
||||||
|
for it in items(scope.symbols):
|
||||||
|
# XXX parameters' owners are wrong for generics; this caused some pain
|
||||||
|
# for closures too; we should finally fix it.
|
||||||
|
#if it.owner != c.p.owner: return result
|
||||||
|
if it.kind in skLocalVars and
|
||||||
|
it.typ.skipTypes({tyGenericInst, tyVar}).kind notin
|
||||||
|
{tyVarargs, tyOpenArray, tyTypeDesc, tyStatic, tyExpr, tyStmt, tyEmpty}:
|
||||||
|
|
||||||
|
var field = newSym(skField, it.name, getCurrOwner(), n.info)
|
||||||
|
field.typ = it.typ.skipTypes({tyGenericInst, tyVar})
|
||||||
|
field.position = counter
|
||||||
|
inc(counter)
|
||||||
|
|
||||||
|
addSon(tupleType.n, newSymNode(field))
|
||||||
|
addSonSkipIntLit(tupleType, field.typ)
|
||||||
|
|
||||||
|
var a = newSymNode(it, result.info)
|
||||||
|
if it.typ.skipTypes({tyGenericInst}).kind == tyVar: a = newDeref(a)
|
||||||
|
result.add(a)
|
||||||
|
|
||||||
|
registerPlugin("stdlib", "system", "locals", semLocals)
|
||||||
|
|
@ -60,7 +60,7 @@ const
|
||||||
varPragmas* = {wImportc, wExportc, wVolatile, wRegister, wThreadVar, wNodecl,
|
varPragmas* = {wImportc, wExportc, wVolatile, wRegister, wThreadVar, wNodecl,
|
||||||
wMagic, wHeader, wDeprecated, wCompilerproc, wDynlib, wExtern,
|
wMagic, wHeader, wDeprecated, wCompilerproc, wDynlib, wExtern,
|
||||||
wImportCpp, wImportObjC, wError, wNoInit, wCompileTime, wGlobal,
|
wImportCpp, wImportObjC, wError, wNoInit, wCompileTime, wGlobal,
|
||||||
wGensym, wInject, wCodegenDecl, wGuard}
|
wGensym, wInject, wCodegenDecl, wGuard, wGoto}
|
||||||
constPragmas* = {wImportc, wExportc, wHeader, wDeprecated, wMagic, wNodecl,
|
constPragmas* = {wImportc, wExportc, wHeader, wDeprecated, wMagic, wNodecl,
|
||||||
wExtern, wImportCpp, wImportObjC, wError, wGensym, wInject}
|
wExtern, wImportCpp, wImportObjC, wError, wGensym, wInject}
|
||||||
letPragmas* = varPragmas
|
letPragmas* = varPragmas
|
||||||
|
|
@ -843,6 +843,11 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: int,
|
||||||
invalidPragma(it)
|
invalidPragma(it)
|
||||||
else:
|
else:
|
||||||
sym.guard = pragmaGuard(c, it, sym.kind)
|
sym.guard = pragmaGuard(c, it, sym.kind)
|
||||||
|
of wGoto:
|
||||||
|
if sym == nil or sym.kind notin {skVar, skLet}:
|
||||||
|
invalidPragma(it)
|
||||||
|
else:
|
||||||
|
sym.flags.incl sfGoto
|
||||||
of wInjectStmt:
|
of wInjectStmt:
|
||||||
if it.kind != nkExprColonExpr:
|
if it.kind != nkExprColonExpr:
|
||||||
localError(it.info, errExprExpected)
|
localError(it.info, errExprExpected)
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,7 @@ import
|
||||||
procfind, lookups, rodread, pragmas, passes, semdata, semtypinst, sigmatch,
|
procfind, lookups, rodread, pragmas, passes, semdata, semtypinst, sigmatch,
|
||||||
intsets, transf, vmdef, vm, idgen, aliases, cgmeth, lambdalifting,
|
intsets, transf, vmdef, vm, idgen, aliases, cgmeth, lambdalifting,
|
||||||
evaltempl, patterns, parampatterns, sempass2, nimfix.pretty, semmacrosanity,
|
evaltempl, patterns, parampatterns, sempass2, nimfix.pretty, semmacrosanity,
|
||||||
semparallel, lowerings
|
semparallel, lowerings, plugins, plugins.active
|
||||||
|
|
||||||
when defined(nimfix):
|
when defined(nimfix):
|
||||||
import nimfix.prettybase
|
import nimfix.prettybase
|
||||||
|
|
@ -89,6 +89,10 @@ proc fitNode(c: PContext, formal: PType, arg: PNode): PNode =
|
||||||
let x = result.skipConv
|
let x = result.skipConv
|
||||||
if x.kind == nkPar and formal.kind != tyExpr:
|
if x.kind == nkPar and formal.kind != tyExpr:
|
||||||
changeType(x, formal, check=true)
|
changeType(x, formal, check=true)
|
||||||
|
else:
|
||||||
|
result = skipHiddenSubConv(result)
|
||||||
|
#result.typ = takeType(formal, arg.typ)
|
||||||
|
#echo arg.info, " picked ", result.typ.typeToString
|
||||||
|
|
||||||
proc inferWithMetatype(c: PContext, formal: PType,
|
proc inferWithMetatype(c: PContext, formal: PType,
|
||||||
arg: PNode, coerceDistincts = false): PNode
|
arg: PNode, coerceDistincts = false): PNode
|
||||||
|
|
|
||||||
|
|
@ -118,7 +118,6 @@ proc newOptionEntry*(): POptionEntry
|
||||||
proc newLib*(kind: TLibKind): PLib
|
proc newLib*(kind: TLibKind): PLib
|
||||||
proc addToLib*(lib: PLib, sym: PSym)
|
proc addToLib*(lib: PLib, sym: PSym)
|
||||||
proc makePtrType*(c: PContext, baseType: PType): PType
|
proc makePtrType*(c: PContext, baseType: PType): PType
|
||||||
proc makeVarType*(c: PContext, baseType: PType): PType
|
|
||||||
proc newTypeS*(kind: TTypeKind, c: PContext): PType
|
proc newTypeS*(kind: TTypeKind, c: PContext): PType
|
||||||
proc fillTypeS*(dest: PType, kind: TTypeKind, c: PContext)
|
proc fillTypeS*(dest: PType, kind: TTypeKind, c: PContext)
|
||||||
|
|
||||||
|
|
@ -213,9 +212,12 @@ proc makePtrType(c: PContext, baseType: PType): PType =
|
||||||
result = newTypeS(tyPtr, c)
|
result = newTypeS(tyPtr, c)
|
||||||
addSonSkipIntLit(result, baseType.assertNotNil)
|
addSonSkipIntLit(result, baseType.assertNotNil)
|
||||||
|
|
||||||
proc makeVarType(c: PContext, baseType: PType): PType =
|
proc makeVarType*(c: PContext, baseType: PType): PType =
|
||||||
result = newTypeS(tyVar, c)
|
if baseType.kind == tyVar:
|
||||||
addSonSkipIntLit(result, baseType.assertNotNil)
|
result = baseType
|
||||||
|
else:
|
||||||
|
result = newTypeS(tyVar, c)
|
||||||
|
addSonSkipIntLit(result, baseType.assertNotNil)
|
||||||
|
|
||||||
proc makeTypeDesc*(c: PContext, typ: PType): PType =
|
proc makeTypeDesc*(c: PContext, typ: PType): PType =
|
||||||
result = newTypeS(tyTypeDesc, c)
|
result = newTypeS(tyTypeDesc, c)
|
||||||
|
|
@ -247,6 +249,7 @@ proc makeAndType*(c: PContext, t1, t2: PType): PType =
|
||||||
propagateToOwner(result, t1)
|
propagateToOwner(result, t1)
|
||||||
propagateToOwner(result, t2)
|
propagateToOwner(result, t2)
|
||||||
result.flags.incl((t1.flags + t2.flags) * {tfHasStatic})
|
result.flags.incl((t1.flags + t2.flags) * {tfHasStatic})
|
||||||
|
result.flags.incl tfHasMeta
|
||||||
|
|
||||||
proc makeOrType*(c: PContext, t1, t2: PType): PType =
|
proc makeOrType*(c: PContext, t1, t2: PType): PType =
|
||||||
result = newTypeS(tyOr, c)
|
result = newTypeS(tyOr, c)
|
||||||
|
|
@ -254,12 +257,14 @@ proc makeOrType*(c: PContext, t1, t2: PType): PType =
|
||||||
propagateToOwner(result, t1)
|
propagateToOwner(result, t1)
|
||||||
propagateToOwner(result, t2)
|
propagateToOwner(result, t2)
|
||||||
result.flags.incl((t1.flags + t2.flags) * {tfHasStatic})
|
result.flags.incl((t1.flags + t2.flags) * {tfHasStatic})
|
||||||
|
result.flags.incl tfHasMeta
|
||||||
|
|
||||||
proc makeNotType*(c: PContext, t1: PType): PType =
|
proc makeNotType*(c: PContext, t1: PType): PType =
|
||||||
result = newTypeS(tyNot, c)
|
result = newTypeS(tyNot, c)
|
||||||
result.sons = @[t1]
|
result.sons = @[t1]
|
||||||
propagateToOwner(result, t1)
|
propagateToOwner(result, t1)
|
||||||
result.flags.incl(t1.flags * {tfHasStatic})
|
result.flags.incl(t1.flags * {tfHasStatic})
|
||||||
|
result.flags.incl tfHasMeta
|
||||||
|
|
||||||
proc nMinusOne*(n: PNode): PNode =
|
proc nMinusOne*(n: PNode): PNode =
|
||||||
result = newNode(nkCall, n.info, @[
|
result = newNode(nkCall, n.info, @[
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,7 @@ proc semFieldAccess(c: PContext, n: PNode, flags: TExprFlags = {}): PNode
|
||||||
proc semOperand(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
|
proc semOperand(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
|
||||||
# same as 'semExprWithType' but doesn't check for proc vars
|
# same as 'semExprWithType' but doesn't check for proc vars
|
||||||
result = semExpr(c, n, flags + {efOperand})
|
result = semExpr(c, n, flags + {efOperand})
|
||||||
if result.kind == nkEmpty:
|
if result.kind == nkEmpty and result.typ.isNil:
|
||||||
# do not produce another redundant error message:
|
# do not produce another redundant error message:
|
||||||
#raiseRecoverableError("")
|
#raiseRecoverableError("")
|
||||||
result = errorNode(c, n)
|
result = errorNode(c, n)
|
||||||
|
|
@ -389,7 +389,7 @@ proc isOpImpl(c: PContext, n: PNode): PNode =
|
||||||
maybeLiftType(t2, c, n.info)
|
maybeLiftType(t2, c, n.info)
|
||||||
var m: TCandidate
|
var m: TCandidate
|
||||||
initCandidate(c, m, t2)
|
initCandidate(c, m, t2)
|
||||||
let match = typeRel(m, t2, t1) != isNone
|
let match = typeRel(m, t2, t1) >= isSubtype # isNone
|
||||||
result = newIntNode(nkIntLit, ord(match))
|
result = newIntNode(nkIntLit, ord(match))
|
||||||
|
|
||||||
result.typ = n.typ
|
result.typ = n.typ
|
||||||
|
|
@ -447,6 +447,7 @@ proc changeType(n: PNode, newType: PType, check: bool) =
|
||||||
of nkPar:
|
of nkPar:
|
||||||
let tup = newType.skipTypes({tyGenericInst})
|
let tup = newType.skipTypes({tyGenericInst})
|
||||||
if tup.kind != tyTuple:
|
if tup.kind != tyTuple:
|
||||||
|
if tup.kind == tyObject: return
|
||||||
internalError(n.info, "changeType: no tuple type for constructor")
|
internalError(n.info, "changeType: no tuple type for constructor")
|
||||||
elif sonsLen(n) > 0 and n.sons[0].kind == nkExprColonExpr:
|
elif sonsLen(n) > 0 and n.sons[0].kind == nkExprColonExpr:
|
||||||
# named tuple?
|
# named tuple?
|
||||||
|
|
@ -535,43 +536,55 @@ proc semArrayConstr(c: PContext, n: PNode, flags: TExprFlags): PNode =
|
||||||
result.typ.sons[0] = makeRangeType(c, 0, sonsLen(result) - 1, n.info)
|
result.typ.sons[0] = makeRangeType(c, 0, sonsLen(result) - 1, n.info)
|
||||||
|
|
||||||
proc fixAbstractType(c: PContext, n: PNode) =
|
proc fixAbstractType(c: PContext, n: PNode) =
|
||||||
# XXX finally rewrite that crap!
|
for i in 1 .. < n.len:
|
||||||
for i in countup(1, sonsLen(n) - 1):
|
let it = n.sons[i]
|
||||||
var it = n.sons[i]
|
# do not get rid of nkHiddenSubConv for OpenArrays, the codegen needs it:
|
||||||
case it.kind
|
if it.kind == nkHiddenSubConv and
|
||||||
of nkHiddenStdConv, nkHiddenSubConv:
|
skipTypes(it.typ, abstractVar).kind notin {tyOpenArray, tyVarargs}:
|
||||||
if it.sons[1].kind == nkBracket:
|
if skipTypes(it.sons[1].typ, abstractVar).kind in
|
||||||
it.sons[1].typ = arrayConstrType(c, it.sons[1])
|
{tyNil, tyArrayConstr, tyTuple, tySet}:
|
||||||
#it.sons[1] = semArrayConstr(c, it.sons[1])
|
|
||||||
if skipTypes(it.typ, abstractVar).kind in {tyOpenArray, tyVarargs}:
|
|
||||||
#if n.sons[0].kind == nkSym and IdentEq(n.sons[0].sym.name, "[]="):
|
|
||||||
# debug(n)
|
|
||||||
|
|
||||||
var s = skipTypes(it.sons[1].typ, abstractVar)
|
|
||||||
if s.kind == tyArrayConstr and s.sons[1].kind == tyEmpty:
|
|
||||||
s = copyType(s, getCurrOwner(), false)
|
|
||||||
skipTypes(s, abstractVar).sons[1] = elemType(
|
|
||||||
skipTypes(it.typ, abstractVar))
|
|
||||||
it.sons[1].typ = s
|
|
||||||
elif s.kind == tySequence and s.sons[0].kind == tyEmpty:
|
|
||||||
s = copyType(s, getCurrOwner(), false)
|
|
||||||
skipTypes(s, abstractVar).sons[0] = elemType(
|
|
||||||
skipTypes(it.typ, abstractVar))
|
|
||||||
it.sons[1].typ = s
|
|
||||||
|
|
||||||
elif skipTypes(it.sons[1].typ, abstractVar).kind in
|
|
||||||
{tyNil, tyArrayConstr, tyTuple, tySet}:
|
|
||||||
var s = skipTypes(it.typ, abstractVar)
|
var s = skipTypes(it.typ, abstractVar)
|
||||||
if s.kind != tyExpr:
|
if s.kind != tyExpr:
|
||||||
changeType(it.sons[1], s, check=true)
|
changeType(it.sons[1], s, check=true)
|
||||||
n.sons[i] = it.sons[1]
|
n.sons[i] = it.sons[1]
|
||||||
of nkBracket:
|
when false:
|
||||||
# an implicitly constructed array (passed to an open array):
|
# XXX finally rewrite that crap!
|
||||||
n.sons[i] = semArrayConstr(c, it, {})
|
for i in countup(1, sonsLen(n) - 1):
|
||||||
else:
|
var it = n.sons[i]
|
||||||
discard
|
case it.kind
|
||||||
#if (it.typ == nil):
|
of nkHiddenStdConv, nkHiddenSubConv:
|
||||||
# InternalError(it.info, "fixAbstractType: " & renderTree(it))
|
if it.sons[1].kind == nkBracket:
|
||||||
|
it.sons[1].typ = arrayConstrType(c, it.sons[1])
|
||||||
|
#it.sons[1] = semArrayConstr(c, it.sons[1])
|
||||||
|
if skipTypes(it.typ, abstractVar).kind in {tyOpenArray, tyVarargs}:
|
||||||
|
#if n.sons[0].kind == nkSym and IdentEq(n.sons[0].sym.name, "[]="):
|
||||||
|
# debug(n)
|
||||||
|
|
||||||
|
var s = skipTypes(it.sons[1].typ, abstractVar)
|
||||||
|
if s.kind == tyArrayConstr and s.sons[1].kind == tyEmpty:
|
||||||
|
s = copyType(s, getCurrOwner(), false)
|
||||||
|
skipTypes(s, abstractVar).sons[1] = elemType(
|
||||||
|
skipTypes(it.typ, abstractVar))
|
||||||
|
it.sons[1].typ = s
|
||||||
|
elif s.kind == tySequence and s.sons[0].kind == tyEmpty:
|
||||||
|
s = copyType(s, getCurrOwner(), false)
|
||||||
|
skipTypes(s, abstractVar).sons[0] = elemType(
|
||||||
|
skipTypes(it.typ, abstractVar))
|
||||||
|
it.sons[1].typ = s
|
||||||
|
|
||||||
|
elif skipTypes(it.sons[1].typ, abstractVar).kind in
|
||||||
|
{tyNil, tyArrayConstr, tyTuple, tySet}:
|
||||||
|
var s = skipTypes(it.typ, abstractVar)
|
||||||
|
if s.kind != tyExpr:
|
||||||
|
changeType(it.sons[1], s, check=true)
|
||||||
|
n.sons[i] = it.sons[1]
|
||||||
|
of nkBracket:
|
||||||
|
# an implicitly constructed array (passed to an open array):
|
||||||
|
n.sons[i] = semArrayConstr(c, it, {})
|
||||||
|
else:
|
||||||
|
discard
|
||||||
|
#if (it.typ == nil):
|
||||||
|
# InternalError(it.info, "fixAbstractType: " & renderTree(it))
|
||||||
|
|
||||||
proc skipObjConv(n: PNode): PNode =
|
proc skipObjConv(n: PNode): PNode =
|
||||||
case n.kind
|
case n.kind
|
||||||
|
|
@ -2040,7 +2053,7 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
|
||||||
of nkEmpty, nkNone, nkCommentStmt:
|
of nkEmpty, nkNone, nkCommentStmt:
|
||||||
discard
|
discard
|
||||||
of nkNilLit:
|
of nkNilLit:
|
||||||
result.typ = getSysType(tyNil)
|
if result.typ == nil: result.typ = getSysType(tyNil)
|
||||||
of nkIntLit:
|
of nkIntLit:
|
||||||
if result.typ == nil: setIntLitType(result)
|
if result.typ == nil: setIntLitType(result)
|
||||||
of nkInt8Lit:
|
of nkInt8Lit:
|
||||||
|
|
|
||||||
|
|
@ -118,6 +118,9 @@ proc makeRange(typ: PType, first, last: BiggestInt): PType =
|
||||||
let lowerNode = newIntNode(nkIntLit, minA)
|
let lowerNode = newIntNode(nkIntLit, minA)
|
||||||
if typ.kind == tyInt and minA == maxA:
|
if typ.kind == tyInt and minA == maxA:
|
||||||
result = getIntLitType(lowerNode)
|
result = getIntLitType(lowerNode)
|
||||||
|
elif typ.kind in {tyUint, tyUInt64}:
|
||||||
|
# these are not ordinal types, so you get no subrange type for these:
|
||||||
|
result = typ
|
||||||
else:
|
else:
|
||||||
var n = newNode(nkRange)
|
var n = newNode(nkRange)
|
||||||
addSon(n, lowerNode)
|
addSon(n, lowerNode)
|
||||||
|
|
@ -135,8 +138,9 @@ proc makeRangeF(typ: PType, first, last: BiggestFloat): PType =
|
||||||
addSonSkipIntLit(result, skipTypes(typ, {tyRange}))
|
addSonSkipIntLit(result, skipTypes(typ, {tyRange}))
|
||||||
|
|
||||||
proc getIntervalType*(m: TMagic, n: PNode): PType =
|
proc getIntervalType*(m: TMagic, n: PNode): PType =
|
||||||
# Nimrod requires interval arithmetic for ``range`` types. Lots of tedious
|
# Nim requires interval arithmetic for ``range`` types. Lots of tedious
|
||||||
# work but the feature is very nice for reducing explicit conversions.
|
# work but the feature is very nice for reducing explicit conversions.
|
||||||
|
const ordIntLit = {nkIntLit..nkUInt64Lit}
|
||||||
result = n.typ
|
result = n.typ
|
||||||
|
|
||||||
template commutativeOp(opr: expr) {.immediate.} =
|
template commutativeOp(opr: expr) {.immediate.} =
|
||||||
|
|
@ -208,15 +212,15 @@ proc getIntervalType*(m: TMagic, n: PNode): PType =
|
||||||
var a = n.sons[1]
|
var a = n.sons[1]
|
||||||
var b = n.sons[2]
|
var b = n.sons[2]
|
||||||
# symmetrical:
|
# symmetrical:
|
||||||
if b.kind notin {nkIntLit..nkUInt32Lit}: swap(a, b)
|
if b.kind notin ordIntLit: swap(a, b)
|
||||||
if b.kind in {nkIntLit..nkUInt32Lit}:
|
if b.kind in ordIntLit:
|
||||||
let x = b.intVal|+|1
|
let x = b.intVal|+|1
|
||||||
if (x and -x) == x and x >= 0:
|
if (x and -x) == x and x >= 0:
|
||||||
result = makeRange(a.typ, 0, b.intVal)
|
result = makeRange(a.typ, 0, b.intVal)
|
||||||
of mModU:
|
of mModU:
|
||||||
let a = n.sons[1]
|
let a = n.sons[1]
|
||||||
let b = n.sons[2]
|
let b = n.sons[2]
|
||||||
if b.kind in {nkIntLit..nkUInt32Lit}:
|
if a.kind in ordIntLit:
|
||||||
if b.intVal >= 0:
|
if b.intVal >= 0:
|
||||||
result = makeRange(a.typ, 0, b.intVal-1)
|
result = makeRange(a.typ, 0, b.intVal-1)
|
||||||
else:
|
else:
|
||||||
|
|
@ -232,9 +236,9 @@ proc getIntervalType*(m: TMagic, n: PNode): PType =
|
||||||
result = makeRange(a.typ, b.intVal+1, -(b.intVal+1))
|
result = makeRange(a.typ, b.intVal+1, -(b.intVal+1))
|
||||||
of mDivI, mDivI64, mDivU:
|
of mDivI, mDivI64, mDivU:
|
||||||
binaryOp(`|div|`)
|
binaryOp(`|div|`)
|
||||||
of mMinI, mMinI64:
|
of mMinI:
|
||||||
commutativeOp(min)
|
commutativeOp(min)
|
||||||
of mMaxI, mMaxI64:
|
of mMaxI:
|
||||||
commutativeOp(max)
|
commutativeOp(max)
|
||||||
else: discard
|
else: discard
|
||||||
|
|
||||||
|
|
@ -282,10 +286,14 @@ proc evalOp(m: TMagic, n, a, b, c: PNode): PNode =
|
||||||
of mNot: result = newIntNodeT(1 - getInt(a), n)
|
of mNot: result = newIntNodeT(1 - getInt(a), n)
|
||||||
of mCard: result = newIntNodeT(nimsets.cardSet(a), n)
|
of mCard: result = newIntNodeT(nimsets.cardSet(a), n)
|
||||||
of mBitnotI, mBitnotI64: result = newIntNodeT(not getInt(a), n)
|
of mBitnotI, mBitnotI64: result = newIntNodeT(not getInt(a), n)
|
||||||
of mLengthStr: result = newIntNodeT(len(getStr(a)), n)
|
of mLengthStr, mXLenStr:
|
||||||
|
if a.kind == nkNilLit: result = newIntNodeT(0, n)
|
||||||
|
else: result = newIntNodeT(len(getStr(a)), n)
|
||||||
of mLengthArray: result = newIntNodeT(lengthOrd(a.typ), n)
|
of mLengthArray: result = newIntNodeT(lengthOrd(a.typ), n)
|
||||||
of mLengthSeq, mLengthOpenArray: result = newIntNodeT(sonsLen(a), n) # BUGFIX
|
of mLengthSeq, mLengthOpenArray, mXLenSeq:
|
||||||
of mUnaryPlusI, mUnaryPlusI64, mUnaryPlusF64: result = a # throw `+` away
|
if a.kind == nkNilLit: result = newIntNodeT(0, n)
|
||||||
|
else: result = newIntNodeT(sonsLen(a), n) # BUGFIX
|
||||||
|
of mUnaryPlusI, mUnaryPlusF64: result = a # throw `+` away
|
||||||
of mToFloat, mToBiggestFloat:
|
of mToFloat, mToBiggestFloat:
|
||||||
result = newFloatNodeT(toFloat(int(getInt(a))), n)
|
result = newFloatNodeT(toFloat(int(getInt(a))), n)
|
||||||
of mToInt, mToBiggestInt: result = newIntNodeT(system.toInt(getFloat(a)), n)
|
of mToInt, mToBiggestInt: result = newIntNodeT(system.toInt(getFloat(a)), n)
|
||||||
|
|
@ -305,10 +313,10 @@ proc evalOp(m: TMagic, n, a, b, c: PNode): PNode =
|
||||||
of mAddI, mAddI64: result = newIntNodeT(getInt(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 mSubI, mSubI64: result = newIntNodeT(getInt(a) - getInt(b), n)
|
||||||
of mMulI, mMulI64: result = newIntNodeT(getInt(a) * getInt(b), n)
|
of mMulI, mMulI64: result = newIntNodeT(getInt(a) * getInt(b), n)
|
||||||
of mMinI, mMinI64:
|
of mMinI:
|
||||||
if getInt(a) > getInt(b): result = newIntNodeT(getInt(b), n)
|
if getInt(a) > getInt(b): result = newIntNodeT(getInt(b), n)
|
||||||
else: result = newIntNodeT(getInt(a), n)
|
else: result = newIntNodeT(getInt(a), n)
|
||||||
of mMaxI, mMaxI64:
|
of mMaxI:
|
||||||
if getInt(a) > getInt(b): result = newIntNodeT(getInt(a), n)
|
if getInt(a) > getInt(b): result = newIntNodeT(getInt(a), n)
|
||||||
else: result = newIntNodeT(getInt(b), n)
|
else: result = newIntNodeT(getInt(b), n)
|
||||||
of mShlI, mShlI64:
|
of mShlI, mShlI64:
|
||||||
|
|
@ -426,7 +434,8 @@ proc evalOp(m: TMagic, n, a, b, c: PNode): PNode =
|
||||||
mExit, mInc, ast.mDec, mEcho, mSwap, mAppendStrCh,
|
mExit, mInc, ast.mDec, mEcho, mSwap, mAppendStrCh,
|
||||||
mAppendStrStr, mAppendSeqElem, mSetLengthStr, mSetLengthSeq,
|
mAppendStrStr, mAppendSeqElem, mSetLengthStr, mSetLengthSeq,
|
||||||
mParseExprToAst, mParseStmtToAst, mExpandToAst, mTypeTrait, mDotDot,
|
mParseExprToAst, mParseStmtToAst, mExpandToAst, mTypeTrait, mDotDot,
|
||||||
mNLen..mNError, mEqRef, mSlurp, mStaticExec, mNGenSym, mSpawn, mParallel:
|
mNLen..mNError, mEqRef, mSlurp, mStaticExec, mNGenSym, mSpawn,
|
||||||
|
mParallel, mPlugin:
|
||||||
discard
|
discard
|
||||||
else: internalError(a.info, "evalOp(" & $m & ')')
|
else: internalError(a.info, "evalOp(" & $m & ')')
|
||||||
|
|
||||||
|
|
@ -519,7 +528,7 @@ proc rangeCheck(n: PNode, value: BiggestInt) =
|
||||||
proc foldConv*(n, a: PNode; check = false): PNode =
|
proc foldConv*(n, a: PNode; check = false): PNode =
|
||||||
# XXX range checks?
|
# XXX range checks?
|
||||||
case skipTypes(n.typ, abstractRange).kind
|
case skipTypes(n.typ, abstractRange).kind
|
||||||
of tyInt..tyInt64:
|
of tyInt..tyInt64, tyUInt..tyUInt64:
|
||||||
case skipTypes(a.typ, abstractRange).kind
|
case skipTypes(a.typ, abstractRange).kind
|
||||||
of tyFloat..tyFloat64:
|
of tyFloat..tyFloat64:
|
||||||
result = newIntNodeT(int(getFloat(a)), n)
|
result = newIntNodeT(int(getFloat(a)), n)
|
||||||
|
|
@ -646,7 +655,7 @@ proc getConstExpr(m: PSym, n: PNode): PNode =
|
||||||
result = copyNode(n)
|
result = copyNode(n)
|
||||||
of nkIfExpr:
|
of nkIfExpr:
|
||||||
result = getConstIfExpr(m, n)
|
result = getConstIfExpr(m, n)
|
||||||
of nkCall, nkCommand, nkCallStrLit, nkPrefix, nkInfix:
|
of nkCallKinds:
|
||||||
if n.sons[0].kind != nkSym: return
|
if n.sons[0].kind != nkSym: return
|
||||||
var s = n.sons[0].sym
|
var s = n.sons[0].sym
|
||||||
if s.kind != skProc: return
|
if s.kind != skProc: return
|
||||||
|
|
|
||||||
|
|
@ -187,7 +187,9 @@ proc instantiateProcType(c: PContext, pt: TIdTable,
|
||||||
let param = copySym(oldParam)
|
let param = copySym(oldParam)
|
||||||
param.owner = prc
|
param.owner = prc
|
||||||
param.typ = result.sons[i]
|
param.typ = result.sons[i]
|
||||||
param.ast = oldParam.ast.copyTree
|
if oldParam.ast != nil:
|
||||||
|
param.ast = fitNode(c, param.typ, oldParam.ast)
|
||||||
|
|
||||||
# don't be lazy here and call replaceTypeVarsN(cl, originalParams[i])!
|
# don't be lazy here and call replaceTypeVarsN(cl, originalParams[i])!
|
||||||
result.n.sons[i] = newSymNode(param)
|
result.n.sons[i] = newSymNode(param)
|
||||||
addDecl(c, param)
|
addDecl(c, param)
|
||||||
|
|
|
||||||
|
|
@ -16,9 +16,9 @@ proc ithField(n: PNode, field: int): PSym =
|
||||||
result = nil
|
result = nil
|
||||||
case n.kind
|
case n.kind
|
||||||
of nkRecList:
|
of nkRecList:
|
||||||
for i in countup(0, sonsLen(n) - 1):
|
for i in countup(0, sonsLen(n) - 1):
|
||||||
result = ithField(n.sons[i], field-i)
|
result = ithField(n.sons[i], field-i)
|
||||||
if result != nil: return
|
if result != nil: return
|
||||||
of nkRecCase:
|
of nkRecCase:
|
||||||
if n.sons[0].kind != nkSym: internalError(n.info, "ithField")
|
if n.sons[0].kind != nkSym: internalError(n.info, "ithField")
|
||||||
result = ithField(n.sons[0], field-1)
|
result = ithField(n.sons[0], field-1)
|
||||||
|
|
@ -34,7 +34,7 @@ proc ithField(n: PNode, field: int): PSym =
|
||||||
else: discard
|
else: discard
|
||||||
|
|
||||||
proc annotateType*(n: PNode, t: PType) =
|
proc annotateType*(n: PNode, t: PType) =
|
||||||
let x = t.skipTypes(abstractInst)
|
let x = t.skipTypes(abstractInst+{tyRange})
|
||||||
# Note: x can be unequal to t and we need to be careful to use 't'
|
# Note: x can be unequal to t and we need to be careful to use 't'
|
||||||
# to not to skip tyGenericInst
|
# to not to skip tyGenericInst
|
||||||
case n.kind
|
case n.kind
|
||||||
|
|
@ -80,7 +80,7 @@ proc annotateType*(n: PNode, t: PType) =
|
||||||
if x.kind in {tyString, tyCString}:
|
if x.kind in {tyString, tyCString}:
|
||||||
n.typ = t
|
n.typ = t
|
||||||
else:
|
else:
|
||||||
globalError(n.info, "string literal must be of some string type")
|
globalError(n.info, "string literal must be of some string type")
|
||||||
of nkNilLit:
|
of nkNilLit:
|
||||||
if x.kind in NilableTypes:
|
if x.kind in NilableTypes:
|
||||||
n.typ = t
|
n.typ = t
|
||||||
|
|
|
||||||
|
|
@ -54,7 +54,7 @@ proc evalTypeTrait(trait: PNode, operand: PType, context: PSym): PNode =
|
||||||
result.typ = newType(tyString, context)
|
result.typ = newType(tyString, context)
|
||||||
result.info = trait.info
|
result.info = trait.info
|
||||||
of "arity":
|
of "arity":
|
||||||
result = newIntNode(nkIntLit, typ.n.len-1)
|
result = newIntNode(nkIntLit, typ.len - ord(typ.kind==tyProc))
|
||||||
result.typ = newType(tyInt, context)
|
result.typ = newType(tyInt, context)
|
||||||
result.info = trait.info
|
result.info = trait.info
|
||||||
else:
|
else:
|
||||||
|
|
@ -101,34 +101,6 @@ proc semBindSym(c: PContext, n: PNode): PNode =
|
||||||
else:
|
else:
|
||||||
localError(n.sons[1].info, errUndeclaredIdentifier, sl.strVal)
|
localError(n.sons[1].info, errUndeclaredIdentifier, sl.strVal)
|
||||||
|
|
||||||
proc semLocals(c: PContext, n: PNode): PNode =
|
|
||||||
var counter = 0
|
|
||||||
var tupleType = newTypeS(tyTuple, c)
|
|
||||||
result = newNodeIT(nkPar, n.info, tupleType)
|
|
||||||
tupleType.n = newNodeI(nkRecList, n.info)
|
|
||||||
# for now we skip openarrays ...
|
|
||||||
for scope in walkScopes(c.currentScope):
|
|
||||||
if scope == c.topLevelScope: break
|
|
||||||
for it in items(scope.symbols):
|
|
||||||
# XXX parameters' owners are wrong for generics; this caused some pain
|
|
||||||
# for closures too; we should finally fix it.
|
|
||||||
#if it.owner != c.p.owner: return result
|
|
||||||
if it.kind in skLocalVars and
|
|
||||||
it.typ.skipTypes({tyGenericInst, tyVar}).kind notin
|
|
||||||
{tyVarargs, tyOpenArray, tyTypeDesc, tyStatic, tyExpr, tyStmt, tyEmpty}:
|
|
||||||
|
|
||||||
var field = newSym(skField, it.name, getCurrOwner(), n.info)
|
|
||||||
field.typ = it.typ.skipTypes({tyGenericInst, tyVar})
|
|
||||||
field.position = counter
|
|
||||||
inc(counter)
|
|
||||||
|
|
||||||
addSon(tupleType.n, newSymNode(field))
|
|
||||||
addSonSkipIntLit(tupleType, field.typ)
|
|
||||||
|
|
||||||
var a = newSymNode(it, result.info)
|
|
||||||
if it.typ.skipTypes({tyGenericInst}).kind == tyVar: a = newDeref(a)
|
|
||||||
result.add(a)
|
|
||||||
|
|
||||||
proc semShallowCopy(c: PContext, n: PNode, flags: TExprFlags): PNode
|
proc semShallowCopy(c: PContext, n: PNode, flags: TExprFlags): PNode
|
||||||
|
|
||||||
proc isStrangeArray(t: PType): bool =
|
proc isStrangeArray(t: PType): bool =
|
||||||
|
|
@ -161,7 +133,6 @@ proc magicsAfterOverloadResolution(c: PContext, n: PNode,
|
||||||
of mHigh, mLow: result = semLowHigh(c, n, n[0].sym.magic)
|
of mHigh, mLow: result = semLowHigh(c, n, n[0].sym.magic)
|
||||||
of mShallowCopy: result = semShallowCopy(c, n, flags)
|
of mShallowCopy: result = semShallowCopy(c, n, flags)
|
||||||
of mNBindSym: result = semBindSym(c, n)
|
of mNBindSym: result = semBindSym(c, n)
|
||||||
of mLocals: result = semLocals(c, n)
|
|
||||||
of mProcCall:
|
of mProcCall:
|
||||||
result = n
|
result = n
|
||||||
result.typ = n[1].typ
|
result.typ = n[1].typ
|
||||||
|
|
@ -196,4 +167,11 @@ proc magicsAfterOverloadResolution(c: PContext, n: PNode,
|
||||||
result.add newSymNode(createMagic("-", mSubI), n.info)
|
result.add newSymNode(createMagic("-", mSubI), n.info)
|
||||||
result.add lenExprB
|
result.add lenExprB
|
||||||
result.add n.sons[1]
|
result.add n.sons[1]
|
||||||
|
of mPlugin:
|
||||||
|
let plugin = getPlugin(n[0].sym)
|
||||||
|
if plugin.isNil:
|
||||||
|
localError(n.info, "cannot find plugin " & n[0].sym.name.s)
|
||||||
|
result = n
|
||||||
|
else:
|
||||||
|
result = plugin(c, n)
|
||||||
else: result = n
|
else: result = n
|
||||||
|
|
|
||||||
|
|
@ -560,7 +560,10 @@ proc trackCase(tracked: PEffects, n: PNode) =
|
||||||
track(tracked, n.sons[0])
|
track(tracked, n.sons[0])
|
||||||
let oldState = tracked.init.len
|
let oldState = tracked.init.len
|
||||||
let oldFacts = tracked.guards.len
|
let oldFacts = tracked.guards.len
|
||||||
let interesting = interestingCaseExpr(n.sons[0]) and warnProveField in gNotes
|
let stringCase = skipTypes(n.sons[0].typ,
|
||||||
|
abstractVarRange-{tyTypeDesc}).kind in {tyFloat..tyFloat128, tyString}
|
||||||
|
let interesting = not stringCase and interestingCaseExpr(n.sons[0]) and
|
||||||
|
warnProveField in gNotes
|
||||||
var inter: TIntersection = @[]
|
var inter: TIntersection = @[]
|
||||||
var toCover = 0
|
var toCover = 0
|
||||||
for i in 1.. <n.len:
|
for i in 1.. <n.len:
|
||||||
|
|
@ -575,13 +578,8 @@ proc trackCase(tracked: PEffects, n: PNode) =
|
||||||
for i in oldState.. <tracked.init.len:
|
for i in oldState.. <tracked.init.len:
|
||||||
addToIntersection(inter, tracked.init[i])
|
addToIntersection(inter, tracked.init[i])
|
||||||
|
|
||||||
let exh = case skipTypes(n.sons[0].typ, abstractVarRange-{tyTypeDesc}).kind
|
|
||||||
of tyFloat..tyFloat128, tyString:
|
|
||||||
lastSon(n).kind == nkElse
|
|
||||||
else:
|
|
||||||
true
|
|
||||||
setLen(tracked.init, oldState)
|
setLen(tracked.init, oldState)
|
||||||
if exh:
|
if not stringCase or lastSon(n).kind == nkElse:
|
||||||
for id, count in items(inter):
|
for id, count in items(inter):
|
||||||
if count >= toCover: tracked.init.add id
|
if count >= toCover: tracked.init.add id
|
||||||
# else we can't merge
|
# else we can't merge
|
||||||
|
|
@ -714,8 +712,8 @@ proc track(tracked: PEffects, n: PNode) =
|
||||||
of nkVarSection, nkLetSection:
|
of nkVarSection, nkLetSection:
|
||||||
for child in n:
|
for child in n:
|
||||||
let last = lastSon(child)
|
let last = lastSon(child)
|
||||||
|
if last.kind != nkEmpty: track(tracked, last)
|
||||||
if child.kind == nkIdentDefs and last.kind != nkEmpty:
|
if child.kind == nkIdentDefs and last.kind != nkEmpty:
|
||||||
track(tracked, last)
|
|
||||||
for i in 0 .. child.len-3:
|
for i in 0 .. child.len-3:
|
||||||
initVar(tracked, child.sons[i], volatileCheck=false)
|
initVar(tracked, child.sons[i], volatileCheck=false)
|
||||||
addAsgnFact(tracked.guards, child.sons[i], last)
|
addAsgnFact(tracked.guards, child.sons[i], last)
|
||||||
|
|
|
||||||
|
|
@ -102,10 +102,6 @@ proc semDestructorCheck(c: PContext, n: PNode, flags: TExprFlags) {.inline.} =
|
||||||
c.p.owner.kind notin {skTemplate, skMacro}:
|
c.p.owner.kind notin {skTemplate, skMacro}:
|
||||||
localError(n.info, errGenerated, "value expected, but got a type")
|
localError(n.info, errGenerated, "value expected, but got a type")
|
||||||
|
|
||||||
proc newDeref(n: PNode): PNode {.inline.} =
|
|
||||||
result = newNodeIT(nkHiddenDeref, n.info, n.typ.sons[0])
|
|
||||||
addSon(result, n)
|
|
||||||
|
|
||||||
proc semExprBranch(c: PContext, n: PNode): PNode =
|
proc semExprBranch(c: PContext, n: PNode): PNode =
|
||||||
result = semExpr(c, n)
|
result = semExpr(c, n)
|
||||||
if result.typ != nil:
|
if result.typ != nil:
|
||||||
|
|
@ -373,6 +369,11 @@ proc addToVarSection(c: PContext; result: var PNode; orig, identDefs: PNode) =
|
||||||
else:
|
else:
|
||||||
result.add identDefs
|
result.add identDefs
|
||||||
|
|
||||||
|
proc isDiscardUnderscore(v: PSym): bool =
|
||||||
|
if v.name.s == "_":
|
||||||
|
v.flags.incl(sfGenSym)
|
||||||
|
result = true
|
||||||
|
|
||||||
proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode =
|
proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode =
|
||||||
var b: PNode
|
var b: PNode
|
||||||
result = copyNode(n)
|
result = copyNode(n)
|
||||||
|
|
@ -436,7 +437,8 @@ proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode =
|
||||||
|
|
||||||
for j in countup(0, length-3):
|
for j in countup(0, length-3):
|
||||||
var v = semIdentDef(c, a.sons[j], symkind)
|
var v = semIdentDef(c, a.sons[j], symkind)
|
||||||
if sfGenSym notin v.flags: addInterfaceDecl(c, v)
|
if sfGenSym notin v.flags and not isDiscardUnderscore(v):
|
||||||
|
addInterfaceDecl(c, v)
|
||||||
when oKeepVariableNames:
|
when oKeepVariableNames:
|
||||||
if c.inUnrolledContext > 0: v.flags.incl(sfShadowed)
|
if c.inUnrolledContext > 0: v.flags.incl(sfShadowed)
|
||||||
else:
|
else:
|
||||||
|
|
@ -551,7 +553,8 @@ proc semForVars(c: PContext, n: PNode): PNode =
|
||||||
if getCurrOwner().kind == skModule: incl(v.flags, sfGlobal)
|
if getCurrOwner().kind == skModule: incl(v.flags, sfGlobal)
|
||||||
v.typ = iter.sons[i]
|
v.typ = iter.sons[i]
|
||||||
n.sons[i] = newSymNode(v)
|
n.sons[i] = newSymNode(v)
|
||||||
if sfGenSym notin v.flags: addForVarDecl(c, v)
|
if sfGenSym notin v.flags and not isDiscardUnderscore(v):
|
||||||
|
addForVarDecl(c, v)
|
||||||
inc(c.p.nestedLoopCounter)
|
inc(c.p.nestedLoopCounter)
|
||||||
n.sons[length-1] = semStmt(c, n.sons[length-1])
|
n.sons[length-1] = semStmt(c, n.sons[length-1])
|
||||||
dec(c.p.nestedLoopCounter)
|
dec(c.p.nestedLoopCounter)
|
||||||
|
|
|
||||||
|
|
@ -261,7 +261,8 @@ proc semArray(c: PContext, n: PNode, prev: PType): PType =
|
||||||
if not isOrdinalType(indx):
|
if not isOrdinalType(indx):
|
||||||
localError(n.sons[1].info, errOrdinalTypeExpected)
|
localError(n.sons[1].info, errOrdinalTypeExpected)
|
||||||
elif enumHasHoles(indx):
|
elif enumHasHoles(indx):
|
||||||
localError(n.sons[1].info, errEnumXHasHoles, indx.sym.name.s)
|
localError(n.sons[1].info, errEnumXHasHoles,
|
||||||
|
typeToString(indx.skipTypes({tyRange})))
|
||||||
base = semTypeNode(c, n.sons[2], nil)
|
base = semTypeNode(c, n.sons[2], nil)
|
||||||
addSonSkipIntLit(result, base)
|
addSonSkipIntLit(result, base)
|
||||||
else:
|
else:
|
||||||
|
|
@ -628,7 +629,7 @@ proc skipGenericInvocation(t: PType): PType {.inline.} =
|
||||||
result = t
|
result = t
|
||||||
if result.kind == tyGenericInvocation:
|
if result.kind == tyGenericInvocation:
|
||||||
result = result.sons[0]
|
result = result.sons[0]
|
||||||
if result.kind == tyGenericBody:
|
while result.kind in {tyGenericInst, tyGenericBody}:
|
||||||
result = lastSon(result)
|
result = lastSon(result)
|
||||||
|
|
||||||
proc addInheritedFields(c: PContext, check: var IntSet, pos: var int,
|
proc addInheritedFields(c: PContext, check: var IntSet, pos: var int,
|
||||||
|
|
@ -834,7 +835,7 @@ proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode,
|
||||||
cp.kind = tyUserTypeClassInst
|
cp.kind = tyUserTypeClassInst
|
||||||
return addImplicitGeneric(cp)
|
return addImplicitGeneric(cp)
|
||||||
|
|
||||||
for i in 1 .. (paramType.sons.len - 2):
|
for i in 1 .. paramType.len-2:
|
||||||
var lifted = liftingWalk(paramType.sons[i])
|
var lifted = liftingWalk(paramType.sons[i])
|
||||||
if lifted != nil:
|
if lifted != nil:
|
||||||
paramType.sons[i] = lifted
|
paramType.sons[i] = lifted
|
||||||
|
|
@ -847,7 +848,7 @@ proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode,
|
||||||
result.shouldHaveMeta
|
result.shouldHaveMeta
|
||||||
|
|
||||||
of tyGenericInvocation:
|
of tyGenericInvocation:
|
||||||
for i in 1 .. <paramType.sonsLen:
|
for i in 1 .. <paramType.len:
|
||||||
let lifted = liftingWalk(paramType.sons[i])
|
let lifted = liftingWalk(paramType.sons[i])
|
||||||
if lifted != nil: paramType.sons[i] = lifted
|
if lifted != nil: paramType.sons[i] = lifted
|
||||||
when false:
|
when false:
|
||||||
|
|
|
||||||
|
|
@ -233,7 +233,9 @@ proc instCopyType*(cl: var TReplTypeVars, t: PType): PType =
|
||||||
# XXX: relying on allowMetaTypes is a kludge
|
# XXX: relying on allowMetaTypes is a kludge
|
||||||
result = copyType(t, t.owner, cl.allowMetaTypes)
|
result = copyType(t, t.owner, cl.allowMetaTypes)
|
||||||
result.flags.incl tfFromGeneric
|
result.flags.incl tfFromGeneric
|
||||||
result.flags.excl tfInstClearedFlags
|
if not (t.kind in tyMetaTypes or
|
||||||
|
(t.kind == tyStatic and t.n == nil)):
|
||||||
|
result.flags.excl tfInstClearedFlags
|
||||||
|
|
||||||
proc handleGenericInvocation(cl: var TReplTypeVars, t: PType): PType =
|
proc handleGenericInvocation(cl: var TReplTypeVars, t: PType): PType =
|
||||||
# tyGenericInvocation[A, tyGenericInvocation[A, B]]
|
# tyGenericInvocation[A, tyGenericInvocation[A, B]]
|
||||||
|
|
|
||||||
|
|
@ -147,6 +147,7 @@ proc copyCandidate(a: var TCandidate, b: TCandidate) =
|
||||||
|
|
||||||
proc sumGeneric(t: PType): int =
|
proc sumGeneric(t: PType): int =
|
||||||
var t = t
|
var t = t
|
||||||
|
var isvar = 1
|
||||||
while true:
|
while true:
|
||||||
case t.kind
|
case t.kind
|
||||||
of tyGenericInst, tyArray, tyRef, tyPtr, tyDistinct, tyArrayConstr,
|
of tyGenericInst, tyArray, tyRef, tyPtr, tyDistinct, tyArrayConstr,
|
||||||
|
|
@ -154,18 +155,20 @@ proc sumGeneric(t: PType): int =
|
||||||
t = t.lastSon
|
t = t.lastSon
|
||||||
inc result
|
inc result
|
||||||
of tyVar:
|
of tyVar:
|
||||||
# but do not make 'var T' more specific than 'T'!
|
|
||||||
t = t.sons[0]
|
t = t.sons[0]
|
||||||
|
inc result
|
||||||
|
inc isvar
|
||||||
of tyGenericInvocation, tyTuple:
|
of tyGenericInvocation, tyTuple:
|
||||||
result = ord(t.kind == tyGenericInvocation)
|
result += ord(t.kind == tyGenericInvocation)
|
||||||
for i in 0 .. <t.len: result += t.sons[i].sumGeneric
|
for i in 0 .. <t.len: result += t.sons[i].sumGeneric
|
||||||
break
|
break
|
||||||
of tyGenericParam, tyExpr, tyStatic, tyStmt, tyTypeDesc: break
|
of tyGenericParam, tyExpr, tyStatic, tyStmt, tyTypeDesc: break
|
||||||
of tyBool, tyChar, tyEnum, tyObject, tyProc, tyPointer,
|
of tyBool, tyChar, tyEnum, tyObject, tyProc, tyPointer,
|
||||||
tyString, tyCString, tyInt..tyInt64, tyFloat..tyFloat128,
|
tyString, tyCString, tyInt..tyInt64, tyFloat..tyFloat128,
|
||||||
tyUInt..tyUInt64:
|
tyUInt..tyUInt64:
|
||||||
return 1
|
return isvar
|
||||||
else: return 0
|
else:
|
||||||
|
return 0
|
||||||
|
|
||||||
#var ggDebug: bool
|
#var ggDebug: bool
|
||||||
|
|
||||||
|
|
@ -919,6 +922,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
|
||||||
|
|
||||||
of tyAnd:
|
of tyAnd:
|
||||||
considerPreviousT:
|
considerPreviousT:
|
||||||
|
result = isEqual
|
||||||
for branch in f.sons:
|
for branch in f.sons:
|
||||||
let x = typeRel(c, branch, aOrig)
|
let x = typeRel(c, branch, aOrig)
|
||||||
if x < isSubtype: return isNone
|
if x < isSubtype: return isNone
|
||||||
|
|
@ -1108,8 +1112,10 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
|
||||||
localError(f.n.info, errTypeExpected)
|
localError(f.n.info, errTypeExpected)
|
||||||
result = isNone
|
result = isNone
|
||||||
|
|
||||||
|
of tyNone:
|
||||||
|
if a.kind == tyNone: result = isEqual
|
||||||
else:
|
else:
|
||||||
internalAssert false
|
internalError " unknown type kind " & $f.kind
|
||||||
|
|
||||||
proc cmpTypes*(c: PContext, f, a: PType): TTypeRelation =
|
proc cmpTypes*(c: PContext, f, a: PType): TTypeRelation =
|
||||||
var m: TCandidate
|
var m: TCandidate
|
||||||
|
|
@ -1194,15 +1200,6 @@ proc isInlineIterator*(t: PType): bool =
|
||||||
result = t.kind == tyIter or
|
result = t.kind == tyIter or
|
||||||
(t.kind == tyBuiltInTypeClass and t.base.kind == tyIter)
|
(t.kind == tyBuiltInTypeClass and t.base.kind == tyIter)
|
||||||
|
|
||||||
proc isEmptyContainer*(t: PType): bool =
|
|
||||||
case t.kind
|
|
||||||
of tyExpr, tyNil: result = true
|
|
||||||
of tyArray, tyArrayConstr: result = t.sons[1].kind == tyEmpty
|
|
||||||
of tySet, tySequence, tyOpenArray, tyVarargs:
|
|
||||||
result = t.sons[0].kind == tyEmpty
|
|
||||||
of tyGenericInst: result = isEmptyContainer(t.lastSon)
|
|
||||||
else: result = false
|
|
||||||
|
|
||||||
proc incMatches(m: var TCandidate; r: TTypeRelation; convMatch = 1) =
|
proc incMatches(m: var TCandidate; r: TTypeRelation; convMatch = 1) =
|
||||||
case r
|
case r
|
||||||
of isConvertible, isIntConv: inc(m.convMatches, convMatch)
|
of isConvertible, isIntConv: inc(m.convMatches, convMatch)
|
||||||
|
|
@ -1307,7 +1304,7 @@ proc paramTypesMatchAux(m: var TCandidate, f, argType: PType,
|
||||||
if arg.typ == nil:
|
if arg.typ == nil:
|
||||||
result = arg
|
result = arg
|
||||||
elif skipTypes(arg.typ, abstractVar-{tyTypeDesc}).kind == tyTuple:
|
elif skipTypes(arg.typ, abstractVar-{tyTypeDesc}).kind == tyTuple:
|
||||||
result = implicitConv(nkHiddenStdConv, f, copyTree(arg), m, c)
|
result = implicitConv(nkHiddenSubConv, f, arg, m, c)
|
||||||
elif arg.typ.isEmptyContainer:
|
elif arg.typ.isEmptyContainer:
|
||||||
result = arg.copyTree
|
result = arg.copyTree
|
||||||
result.typ = getInstantiatedType(c, arg, m, f)
|
result.typ = getInstantiatedType(c, arg, m, f)
|
||||||
|
|
@ -1322,7 +1319,7 @@ proc paramTypesMatchAux(m: var TCandidate, f, argType: PType,
|
||||||
inc(m.exactMatches)
|
inc(m.exactMatches)
|
||||||
result = arg
|
result = arg
|
||||||
if skipTypes(f, abstractVar-{tyTypeDesc}).kind in {tyTuple}:
|
if skipTypes(f, abstractVar-{tyTypeDesc}).kind in {tyTuple}:
|
||||||
result = implicitConv(nkHiddenStdConv, f, arg, m, c)
|
result = implicitConv(nkHiddenSubConv, f, arg, m, c)
|
||||||
of isNone:
|
of isNone:
|
||||||
# do not do this in ``typeRel`` as it then can't infere T in ``ref T``:
|
# do not do this in ``typeRel`` as it then can't infere T in ``ref T``:
|
||||||
if a.kind in {tyProxy, tyUnknown}:
|
if a.kind in {tyProxy, tyUnknown}:
|
||||||
|
|
@ -1468,9 +1465,7 @@ proc matchesAux(c: PContext, n, nOrig: PNode,
|
||||||
m.state = csNoMatch
|
m.state = csNoMatch
|
||||||
return
|
return
|
||||||
if formal.typ.kind == tyVar:
|
if formal.typ.kind == tyVar:
|
||||||
if n.isLValue:
|
if not n.isLValue:
|
||||||
inc(m.genericMatches, 100)
|
|
||||||
else:
|
|
||||||
m.state = csNoMatch
|
m.state = csNoMatch
|
||||||
return
|
return
|
||||||
|
|
||||||
|
|
@ -1576,6 +1571,8 @@ proc matchesAux(c: PContext, n, nOrig: PNode,
|
||||||
#assert(container == nil)
|
#assert(container == nil)
|
||||||
if container.isNil:
|
if container.isNil:
|
||||||
container = newNodeIT(nkBracket, n.sons[a].info, arrayConstr(c, arg))
|
container = newNodeIT(nkBracket, n.sons[a].info, arrayConstr(c, arg))
|
||||||
|
else:
|
||||||
|
incrIndexType(container.typ)
|
||||||
addSon(container, arg)
|
addSon(container, arg)
|
||||||
setSon(m.call, formal.position + 1,
|
setSon(m.call, formal.position + 1,
|
||||||
implicitConv(nkHiddenStdConv, formal.typ, container, m, c))
|
implicitConv(nkHiddenStdConv, formal.typ, container, m, c))
|
||||||
|
|
|
||||||
|
|
@ -379,6 +379,9 @@ proc transformConv(c: PTransf, n: PNode): PTransNode =
|
||||||
result = transformSons(c, n)
|
result = transformSons(c, n)
|
||||||
of tyOpenArray, tyVarargs:
|
of tyOpenArray, tyVarargs:
|
||||||
result = transform(c, n.sons[1])
|
result = transform(c, n.sons[1])
|
||||||
|
PNode(result).typ = takeType(n.typ, n.sons[1].typ)
|
||||||
|
#echo n.info, " came here and produced ", typeToString(PNode(result).typ),
|
||||||
|
# " from ", typeToString(n.typ), " and ", typeToString(n.sons[1].typ)
|
||||||
of tyCString:
|
of tyCString:
|
||||||
if source.kind == tyString:
|
if source.kind == tyString:
|
||||||
result = newTransNode(nkStringToCString, n, 1)
|
result = newTransNode(nkStringToCString, n, 1)
|
||||||
|
|
@ -713,8 +716,7 @@ proc transform(c: PTransf, n: PNode): PTransNode =
|
||||||
add(result, PTransNode(newSymNode(labl)))
|
add(result, PTransNode(newSymNode(labl)))
|
||||||
of nkBreakStmt: result = transformBreak(c, n)
|
of nkBreakStmt: result = transformBreak(c, n)
|
||||||
of nkWhileStmt: result = transformWhile(c, n)
|
of nkWhileStmt: result = transformWhile(c, n)
|
||||||
of nkCall, nkHiddenCallConv, nkCommand, nkInfix, nkPrefix, nkPostfix,
|
of nkCallKinds:
|
||||||
nkCallStrLit:
|
|
||||||
result = transformCall(c, n)
|
result = transformCall(c, n)
|
||||||
of nkAddr, nkHiddenAddr:
|
of nkAddr, nkHiddenAddr:
|
||||||
result = transformAddrDeref(c, n, nkDerefExpr, nkHiddenDeref)
|
result = transformAddrDeref(c, n, nkDerefExpr, nkHiddenDeref)
|
||||||
|
|
|
||||||
|
|
@ -9,40 +9,40 @@
|
||||||
|
|
||||||
# tree helper routines
|
# tree helper routines
|
||||||
|
|
||||||
import
|
import
|
||||||
ast, astalgo, lexer, msgs, strutils, wordrecg
|
ast, astalgo, lexer, msgs, strutils, wordrecg
|
||||||
|
|
||||||
proc hasSon(father, son: PNode): bool =
|
proc hasSon(father, son: PNode): bool =
|
||||||
for i in countup(0, sonsLen(father) - 1):
|
for i in countup(0, sonsLen(father) - 1):
|
||||||
if father.sons[i] == son:
|
if father.sons[i] == son:
|
||||||
return true
|
return true
|
||||||
result = false
|
result = false
|
||||||
|
|
||||||
proc cyclicTreeAux(n, s: PNode): bool =
|
proc cyclicTreeAux(n, s: PNode): bool =
|
||||||
if n == nil:
|
if n == nil:
|
||||||
return false
|
return false
|
||||||
if hasSon(s, n):
|
if hasSon(s, n):
|
||||||
return true
|
return true
|
||||||
var m = sonsLen(s)
|
var m = sonsLen(s)
|
||||||
addSon(s, n)
|
addSon(s, n)
|
||||||
if not (n.kind in {nkEmpty..nkNilLit}):
|
if not (n.kind in {nkEmpty..nkNilLit}):
|
||||||
for i in countup(0, sonsLen(n) - 1):
|
for i in countup(0, sonsLen(n) - 1):
|
||||||
if cyclicTreeAux(n.sons[i], s):
|
if cyclicTreeAux(n.sons[i], s):
|
||||||
return true
|
return true
|
||||||
result = false
|
result = false
|
||||||
delSon(s, m)
|
delSon(s, m)
|
||||||
|
|
||||||
proc cyclicTree*(n: PNode): bool =
|
proc cyclicTree*(n: PNode): bool =
|
||||||
var s = newNodeI(nkEmpty, n.info)
|
var s = newNodeI(nkEmpty, n.info)
|
||||||
result = cyclicTreeAux(n, s)
|
result = cyclicTreeAux(n, s)
|
||||||
|
|
||||||
proc exprStructuralEquivalent*(a, b: PNode): bool =
|
proc exprStructuralEquivalent*(a, b: PNode): bool =
|
||||||
result = false
|
result = false
|
||||||
if a == b:
|
if a == b:
|
||||||
result = true
|
result = true
|
||||||
elif (a != nil) and (b != nil) and (a.kind == b.kind):
|
elif (a != nil) and (b != nil) and (a.kind == b.kind):
|
||||||
case a.kind
|
case a.kind
|
||||||
of nkSym:
|
of nkSym:
|
||||||
# don't go nuts here: same symbol as string is enough:
|
# don't go nuts here: same symbol as string is enough:
|
||||||
result = a.sym.name.id == b.sym.name.id
|
result = a.sym.name.id == b.sym.name.id
|
||||||
of nkIdent: result = a.ident.id == b.ident.id
|
of nkIdent: result = a.ident.id == b.ident.id
|
||||||
|
|
@ -50,12 +50,12 @@ proc exprStructuralEquivalent*(a, b: PNode): bool =
|
||||||
of nkFloatLit..nkFloat64Lit: result = a.floatVal == b.floatVal
|
of nkFloatLit..nkFloat64Lit: result = a.floatVal == b.floatVal
|
||||||
of nkStrLit..nkTripleStrLit: result = a.strVal == b.strVal
|
of nkStrLit..nkTripleStrLit: result = a.strVal == b.strVal
|
||||||
of nkEmpty, nkNilLit, nkType: result = true
|
of nkEmpty, nkNilLit, nkType: result = true
|
||||||
else:
|
else:
|
||||||
if sonsLen(a) == sonsLen(b):
|
if sonsLen(a) == sonsLen(b):
|
||||||
for i in countup(0, sonsLen(a) - 1):
|
for i in countup(0, sonsLen(a) - 1):
|
||||||
if not exprStructuralEquivalent(a.sons[i], b.sons[i]): return
|
if not exprStructuralEquivalent(a.sons[i], b.sons[i]): return
|
||||||
result = true
|
result = true
|
||||||
|
|
||||||
proc sameTree*(a, b: PNode): bool =
|
proc sameTree*(a, b: PNode): bool =
|
||||||
result = false
|
result = false
|
||||||
if a == b:
|
if a == b:
|
||||||
|
|
@ -66,7 +66,7 @@ proc sameTree*(a, b: PNode): bool =
|
||||||
if a.info.col != b.info.col:
|
if a.info.col != b.info.col:
|
||||||
return #if a.info.fileIndex <> b.info.fileIndex then exit;
|
return #if a.info.fileIndex <> b.info.fileIndex then exit;
|
||||||
case a.kind
|
case a.kind
|
||||||
of nkSym:
|
of nkSym:
|
||||||
# don't go nuts here: same symbol as string is enough:
|
# don't go nuts here: same symbol as string is enough:
|
||||||
result = a.sym.name.id == b.sym.name.id
|
result = a.sym.name.id == b.sym.name.id
|
||||||
of nkIdent: result = a.ident.id == b.ident.id
|
of nkIdent: result = a.ident.id == b.ident.id
|
||||||
|
|
@ -75,15 +75,15 @@ proc sameTree*(a, b: PNode): bool =
|
||||||
of nkStrLit..nkTripleStrLit: result = a.strVal == b.strVal
|
of nkStrLit..nkTripleStrLit: result = a.strVal == b.strVal
|
||||||
of nkEmpty, nkNilLit, nkType: result = true
|
of nkEmpty, nkNilLit, nkType: result = true
|
||||||
else:
|
else:
|
||||||
if sonsLen(a) == sonsLen(b):
|
if sonsLen(a) == sonsLen(b):
|
||||||
for i in countup(0, sonsLen(a) - 1):
|
for i in countup(0, sonsLen(a) - 1):
|
||||||
if not sameTree(a.sons[i], b.sons[i]): return
|
if not sameTree(a.sons[i], b.sons[i]): return
|
||||||
result = true
|
result = true
|
||||||
|
|
||||||
proc getProcSym*(call: PNode): PSym =
|
proc getProcSym*(call: PNode): PSym =
|
||||||
result = call.sons[0].sym
|
result = call.sons[0].sym
|
||||||
|
|
||||||
proc getOpSym*(op: PNode): PSym =
|
proc getOpSym*(op: PNode): PSym =
|
||||||
if op.kind notin {nkCall, nkHiddenCallConv, nkCommand, nkCallStrLit}:
|
if op.kind notin {nkCall, nkHiddenCallConv, nkCommand, nkCallStrLit}:
|
||||||
result = nil
|
result = nil
|
||||||
else:
|
else:
|
||||||
|
|
@ -91,25 +91,25 @@ proc getOpSym*(op: PNode): PSym =
|
||||||
elif op.sons[0].kind == nkSym: result = op.sons[0].sym
|
elif op.sons[0].kind == nkSym: result = op.sons[0].sym
|
||||||
else: result = nil
|
else: result = nil
|
||||||
|
|
||||||
proc getMagic*(op: PNode): TMagic =
|
proc getMagic*(op: PNode): TMagic =
|
||||||
case op.kind
|
case op.kind
|
||||||
of nkCallKinds:
|
of nkCallKinds:
|
||||||
case op.sons[0].kind
|
case op.sons[0].kind
|
||||||
of nkSym: result = op.sons[0].sym.magic
|
of nkSym: result = op.sons[0].sym.magic
|
||||||
else: result = mNone
|
else: result = mNone
|
||||||
else: result = mNone
|
else: result = mNone
|
||||||
|
|
||||||
proc treeToSym*(t: PNode): PSym =
|
proc treeToSym*(t: PNode): PSym =
|
||||||
result = t.sym
|
result = t.sym
|
||||||
|
|
||||||
proc isConstExpr*(n: PNode): bool =
|
proc isConstExpr*(n: PNode): bool =
|
||||||
result = (n.kind in
|
result = (n.kind in
|
||||||
{nkCharLit..nkInt64Lit, nkStrLit..nkTripleStrLit,
|
{nkCharLit..nkInt64Lit, nkStrLit..nkTripleStrLit,
|
||||||
nkFloatLit..nkFloat64Lit, nkNilLit}) or (nfAllConst in n.flags)
|
nkFloatLit..nkFloat64Lit, nkNilLit}) or (nfAllConst in n.flags)
|
||||||
|
|
||||||
proc isDeepConstExpr*(n: PNode): bool =
|
proc isDeepConstExpr*(n: PNode): bool =
|
||||||
case n.kind
|
case n.kind
|
||||||
of nkCharLit..nkInt64Lit, nkStrLit..nkTripleStrLit,
|
of nkCharLit..nkInt64Lit, nkStrLit..nkTripleStrLit,
|
||||||
nkFloatLit..nkFloat64Lit, nkNilLit:
|
nkFloatLit..nkFloat64Lit, nkNilLit:
|
||||||
result = true
|
result = true
|
||||||
of nkExprEqExpr, nkExprColonExpr, nkHiddenStdConv, nkHiddenSubConv:
|
of nkExprEqExpr, nkExprColonExpr, nkHiddenStdConv, nkHiddenSubConv:
|
||||||
|
|
@ -122,33 +122,33 @@ proc isDeepConstExpr*(n: PNode): bool =
|
||||||
result = n.typ.isNil or n.typ.skipTypes({tyGenericInst, tyDistinct}).kind != tyObject
|
result = n.typ.isNil or n.typ.skipTypes({tyGenericInst, tyDistinct}).kind != tyObject
|
||||||
else: discard
|
else: discard
|
||||||
|
|
||||||
proc flattenTreeAux(d, a: PNode, op: TMagic) =
|
proc flattenTreeAux(d, a: PNode, op: TMagic) =
|
||||||
if (getMagic(a) == op): # a is a "leaf", so add it:
|
if (getMagic(a) == op): # a is a "leaf", so add it:
|
||||||
for i in countup(1, sonsLen(a) - 1): # BUGFIX
|
for i in countup(1, sonsLen(a) - 1): # BUGFIX
|
||||||
flattenTreeAux(d, a.sons[i], op)
|
flattenTreeAux(d, a.sons[i], op)
|
||||||
else:
|
else:
|
||||||
addSon(d, copyTree(a))
|
addSon(d, copyTree(a))
|
||||||
|
|
||||||
proc flattenTree*(root: PNode, op: TMagic): PNode =
|
proc flattenTree*(root: PNode, op: TMagic): PNode =
|
||||||
result = copyNode(root)
|
result = copyNode(root)
|
||||||
if getMagic(root) == op:
|
if getMagic(root) == op:
|
||||||
# BUGFIX: forget to copy prc
|
# BUGFIX: forget to copy prc
|
||||||
addSon(result, copyNode(root.sons[0]))
|
addSon(result, copyNode(root.sons[0]))
|
||||||
flattenTreeAux(result, root, op)
|
flattenTreeAux(result, root, op)
|
||||||
|
|
||||||
proc swapOperands*(op: PNode) =
|
proc swapOperands*(op: PNode) =
|
||||||
var tmp = op.sons[1]
|
var tmp = op.sons[1]
|
||||||
op.sons[1] = op.sons[2]
|
op.sons[1] = op.sons[2]
|
||||||
op.sons[2] = tmp
|
op.sons[2] = tmp
|
||||||
|
|
||||||
proc isRange*(n: PNode): bool {.inline.} =
|
proc isRange*(n: PNode): bool {.inline.} =
|
||||||
if n.kind == nkInfix:
|
if n.kind in nkCallKinds:
|
||||||
if n[0].kind == nkIdent and n[0].ident.id == ord(wDotDot) or
|
if n[0].kind == nkIdent and n[0].ident.id == ord(wDotDot) or
|
||||||
n[0].kind in {nkClosedSymChoice, nkOpenSymChoice} and
|
n[0].kind in {nkClosedSymChoice, nkOpenSymChoice} and
|
||||||
n[0][1].sym.name.id == ord(wDotDot):
|
n[0][1].sym.name.id == ord(wDotDot):
|
||||||
result = true
|
result = true
|
||||||
|
|
||||||
proc whichPragma*(n: PNode): TSpecialWord =
|
proc whichPragma*(n: PNode): TSpecialWord =
|
||||||
let key = if n.kind == nkExprColonExpr: n.sons[0] else: n
|
let key = if n.kind == nkExprColonExpr: n.sons[0] else: n
|
||||||
if key.kind == nkIdent: result = whichKeyword(key.ident)
|
if key.kind == nkIdent: result = whichKeyword(key.ident)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -541,6 +541,9 @@ proc typeToString(typ: PType, prefer: TPreferedDesc = preferName): string =
|
||||||
of tyProc:
|
of tyProc:
|
||||||
result = if tfIterator in t.flags: "iterator (" else: "proc ("
|
result = if tfIterator in t.flags: "iterator (" else: "proc ("
|
||||||
for i in countup(1, sonsLen(t) - 1):
|
for i in countup(1, sonsLen(t) - 1):
|
||||||
|
if t.n != nil and i < t.n.len and t.n[i].kind == nkSym:
|
||||||
|
add(result, t.n[i].sym.name.s)
|
||||||
|
add(result, ": ")
|
||||||
add(result, typeToString(t.sons[i]))
|
add(result, typeToString(t.sons[i]))
|
||||||
if i < sonsLen(t) - 1: add(result, ", ")
|
if i < sonsLen(t) - 1: add(result, ", ")
|
||||||
add(result, ')')
|
add(result, ')')
|
||||||
|
|
@ -1436,3 +1439,45 @@ proc skipConv*(n: PNode): PNode =
|
||||||
proc skipConvTakeType*(n: PNode): PNode =
|
proc skipConvTakeType*(n: PNode): PNode =
|
||||||
result = n.skipConv
|
result = n.skipConv
|
||||||
result.typ = n.typ
|
result.typ = n.typ
|
||||||
|
|
||||||
|
proc isEmptyContainer*(t: PType): bool =
|
||||||
|
case t.kind
|
||||||
|
of tyExpr, tyNil: result = true
|
||||||
|
of tyArray, tyArrayConstr: result = t.sons[1].kind == tyEmpty
|
||||||
|
of tySet, tySequence, tyOpenArray, tyVarargs:
|
||||||
|
result = t.sons[0].kind == tyEmpty
|
||||||
|
of tyGenericInst: result = isEmptyContainer(t.lastSon)
|
||||||
|
else: result = false
|
||||||
|
|
||||||
|
proc takeType*(formal, arg: PType): PType =
|
||||||
|
# param: openArray[string] = []
|
||||||
|
# [] is an array constructor of length 0 of type string!
|
||||||
|
if arg.kind == tyNil:
|
||||||
|
# and not (formal.kind == tyProc and formal.callConv == ccClosure):
|
||||||
|
result = formal
|
||||||
|
elif formal.kind in {tyOpenArray, tyVarargs, tySequence} and
|
||||||
|
arg.isEmptyContainer:
|
||||||
|
let a = copyType(arg.skipTypes({tyGenericInst}), arg.owner, keepId=false)
|
||||||
|
a.sons[ord(arg.kind in {tyArray, tyArrayConstr})] = formal.sons[0]
|
||||||
|
result = a
|
||||||
|
elif formal.kind in {tyTuple, tySet} and arg.kind == formal.kind:
|
||||||
|
result = formal
|
||||||
|
else:
|
||||||
|
result = arg
|
||||||
|
|
||||||
|
proc skipHiddenSubConv*(n: PNode): PNode =
|
||||||
|
if n.kind == nkHiddenSubConv:
|
||||||
|
# param: openArray[string] = []
|
||||||
|
# [] is an array constructor of length 0 of type string!
|
||||||
|
let formal = n.typ
|
||||||
|
result = n.sons[1]
|
||||||
|
let arg = result.typ
|
||||||
|
let dest = takeType(formal, arg)
|
||||||
|
if dest == arg and formal.kind != tyExpr:
|
||||||
|
#echo n.info, " came here for ", formal.typeToString
|
||||||
|
result = n
|
||||||
|
else:
|
||||||
|
result = copyTree(result)
|
||||||
|
result.typ = dest
|
||||||
|
else:
|
||||||
|
result = n
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,8 @@ import ast except getstr
|
||||||
|
|
||||||
import
|
import
|
||||||
strutils, astalgo, msgs, vmdef, vmgen, nimsets, types, passes, unsigned,
|
strutils, astalgo, msgs, vmdef, vmgen, nimsets, types, passes, unsigned,
|
||||||
parser, vmdeps, idents, trees, renderer, options, transf, parseutils
|
parser, vmdeps, idents, trees, renderer, options, transf, parseutils,
|
||||||
|
vmmarshal
|
||||||
|
|
||||||
from semfold import leValueConv, ordinalValToString
|
from semfold import leValueConv, ordinalValToString
|
||||||
from evaltempl import evalTemplate
|
from evaltempl import evalTemplate
|
||||||
|
|
@ -371,11 +372,6 @@ template handleJmpBack() {.dirty.} =
|
||||||
globalError(c.debug[pc], errTooManyIterations)
|
globalError(c.debug[pc], errTooManyIterations)
|
||||||
dec(c.loopIterations)
|
dec(c.loopIterations)
|
||||||
|
|
||||||
proc skipColon(n: PNode): PNode =
|
|
||||||
result = n
|
|
||||||
if n.kind == nkExprColonExpr:
|
|
||||||
result = n.sons[1]
|
|
||||||
|
|
||||||
proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
|
proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
|
||||||
var pc = start
|
var pc = start
|
||||||
var tos = tos
|
var tos = tos
|
||||||
|
|
@ -1043,7 +1039,14 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
|
||||||
decodeB(rkNode)
|
decodeB(rkNode)
|
||||||
let newLen = regs[rb].intVal.int
|
let newLen = regs[rb].intVal.int
|
||||||
if regs[ra].node.isNil: stackTrace(c, tos, pc, errNilAccess)
|
if regs[ra].node.isNil: stackTrace(c, tos, pc, errNilAccess)
|
||||||
else: setLen(regs[ra].node.sons, newLen)
|
else:
|
||||||
|
let oldLen = regs[ra].node.len
|
||||||
|
setLen(regs[ra].node.sons, newLen)
|
||||||
|
if oldLen < newLen:
|
||||||
|
# XXX This is still not entirely correct
|
||||||
|
# set to default value:
|
||||||
|
for i in oldLen .. <newLen:
|
||||||
|
regs[ra].node.sons[i] = newNodeI(nkEmpty, c.debug[pc])
|
||||||
of opcSwap:
|
of opcSwap:
|
||||||
let rb = instr.regB
|
let rb = instr.regB
|
||||||
if regs[ra].kind == regs[rb].kind:
|
if regs[ra].kind == regs[rb].kind:
|
||||||
|
|
@ -1362,6 +1365,19 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
|
||||||
while typ.kind == tyTypeDesc and typ.len > 0: typ = typ.sons[0]
|
while typ.kind == tyTypeDesc and typ.len > 0: typ = typ.sons[0]
|
||||||
createStr regs[ra]
|
createStr regs[ra]
|
||||||
regs[ra].node.strVal = typ.typeToString(preferExported)
|
regs[ra].node.strVal = typ.typeToString(preferExported)
|
||||||
|
of opcMarshalLoad:
|
||||||
|
let ra = instr.regA
|
||||||
|
let rb = instr.regB
|
||||||
|
inc pc
|
||||||
|
let typ = c.types[c.code[pc].regBx - wordExcess]
|
||||||
|
putIntoReg(regs[ra], loadAny(regs[rb].node.strVal, typ))
|
||||||
|
of opcMarshalStore:
|
||||||
|
decodeB(rkNode)
|
||||||
|
inc pc
|
||||||
|
let typ = c.types[c.code[pc].regBx - wordExcess]
|
||||||
|
createStrKeepNode(regs[ra])
|
||||||
|
if regs[ra].node.strVal.isNil: regs[ra].node.strVal = newStringOfCap(1000)
|
||||||
|
storeAny(regs[ra].node.strVal, typ, regs[rb].regToNode)
|
||||||
inc pc
|
inc pc
|
||||||
|
|
||||||
proc execute(c: PCtx, start: int): PNode =
|
proc execute(c: PCtx, start: int): PNode =
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,7 @@ const
|
||||||
byteExcess* = 128 # we use excess-K for immediates
|
byteExcess* = 128 # we use excess-K for immediates
|
||||||
wordExcess* = 32768
|
wordExcess* = 32768
|
||||||
|
|
||||||
MaxLoopIterations* = 500_000 # max iterations of all loops
|
MaxLoopIterations* = 1500_000 # max iterations of all loops
|
||||||
|
|
||||||
|
|
||||||
type
|
type
|
||||||
|
|
@ -29,7 +29,7 @@ type
|
||||||
opcRet, # return
|
opcRet, # return
|
||||||
opcYldYoid, # yield with no value
|
opcYldYoid, # yield with no value
|
||||||
opcYldVal, # yield with a value
|
opcYldVal, # yield with a value
|
||||||
|
|
||||||
opcAsgnInt,
|
opcAsgnInt,
|
||||||
opcAsgnStr,
|
opcAsgnStr,
|
||||||
opcAsgnFloat,
|
opcAsgnFloat,
|
||||||
|
|
@ -48,8 +48,8 @@ type
|
||||||
opcWrDeref,
|
opcWrDeref,
|
||||||
opcWrStrIdx,
|
opcWrStrIdx,
|
||||||
opcLdStrIdx, # a = b[c]
|
opcLdStrIdx, # a = b[c]
|
||||||
|
|
||||||
opcAddInt,
|
opcAddInt,
|
||||||
opcAddImmInt,
|
opcAddImmInt,
|
||||||
opcSubInt,
|
opcSubInt,
|
||||||
opcSubImmInt,
|
opcSubImmInt,
|
||||||
|
|
@ -58,36 +58,37 @@ type
|
||||||
|
|
||||||
opcIncl, opcInclRange, opcExcl, opcCard, opcMulInt, opcDivInt, opcModInt,
|
opcIncl, opcInclRange, opcExcl, opcCard, opcMulInt, opcDivInt, opcModInt,
|
||||||
opcAddFloat, opcSubFloat, opcMulFloat, opcDivFloat, opcShrInt, opcShlInt,
|
opcAddFloat, opcSubFloat, opcMulFloat, opcDivFloat, opcShrInt, opcShlInt,
|
||||||
opcBitandInt, opcBitorInt, opcBitxorInt, opcAddu, opcSubu, opcMulu,
|
opcBitandInt, opcBitorInt, opcBitxorInt, opcAddu, opcSubu, opcMulu,
|
||||||
opcDivu, opcModu, opcEqInt, opcLeInt, opcLtInt, opcEqFloat,
|
opcDivu, opcModu, opcEqInt, opcLeInt, opcLtInt, opcEqFloat,
|
||||||
opcLeFloat, opcLtFloat, opcLeu, opcLtu, opcEqRef, opcEqNimrodNode, opcXor,
|
opcLeFloat, opcLtFloat, opcLeu, opcLtu, opcEqRef, opcEqNimrodNode, opcXor,
|
||||||
opcNot, opcUnaryMinusInt, opcUnaryMinusFloat, opcBitnotInt,
|
opcNot, opcUnaryMinusInt, opcUnaryMinusFloat, opcBitnotInt,
|
||||||
opcEqStr, opcLeStr, opcLtStr, opcEqSet, opcLeSet, opcLtSet,
|
opcEqStr, opcLeStr, opcLtStr, opcEqSet, opcLeSet, opcLtSet,
|
||||||
opcMulSet, opcPlusSet, opcMinusSet, opcSymdiffSet, opcConcatStr,
|
opcMulSet, opcPlusSet, opcMinusSet, opcSymdiffSet, opcConcatStr,
|
||||||
opcContainsSet, opcRepr, opcSetLenStr, opcSetLenSeq,
|
opcContainsSet, opcRepr, opcSetLenStr, opcSetLenSeq,
|
||||||
opcSwap, opcIsNil, opcOf, opcIs,
|
opcSwap, opcIsNil, opcOf, opcIs,
|
||||||
opcSubStr, opcParseFloat, opcConv, opcCast, opcQuit, opcReset,
|
opcSubStr, opcParseFloat, opcConv, opcCast,
|
||||||
|
opcQuit, opcReset,
|
||||||
opcNarrowS, opcNarrowU,
|
opcNarrowS, opcNarrowU,
|
||||||
|
|
||||||
opcAddStrCh,
|
opcAddStrCh,
|
||||||
opcAddStrStr,
|
opcAddStrStr,
|
||||||
opcAddSeqElem,
|
opcAddSeqElem,
|
||||||
opcRangeChck,
|
opcRangeChck,
|
||||||
|
|
||||||
opcNAdd,
|
opcNAdd,
|
||||||
opcNAddMultiple,
|
opcNAddMultiple,
|
||||||
opcNKind,
|
opcNKind,
|
||||||
opcNIntVal,
|
opcNIntVal,
|
||||||
opcNFloatVal,
|
opcNFloatVal,
|
||||||
opcNSymbol,
|
opcNSymbol,
|
||||||
opcNIdent,
|
opcNIdent,
|
||||||
opcNGetType,
|
opcNGetType,
|
||||||
opcNStrVal,
|
opcNStrVal,
|
||||||
|
|
||||||
opcNSetIntVal,
|
opcNSetIntVal,
|
||||||
opcNSetFloatVal, opcNSetSymbol, opcNSetIdent, opcNSetType, opcNSetStrVal,
|
opcNSetFloatVal, opcNSetSymbol, opcNSetIdent, opcNSetType, opcNSetStrVal,
|
||||||
opcNNewNimNode, opcNCopyNimNode, opcNCopyNimTree, opcNDel, opcGenSym,
|
opcNNewNimNode, opcNCopyNimNode, opcNCopyNimTree, opcNDel, opcGenSym,
|
||||||
|
|
||||||
opcSlurp,
|
opcSlurp,
|
||||||
opcGorge,
|
opcGorge,
|
||||||
opcParseExprToAst,
|
opcParseExprToAst,
|
||||||
|
|
@ -100,7 +101,7 @@ type
|
||||||
opcEqIdent,
|
opcEqIdent,
|
||||||
opcStrToIdent,
|
opcStrToIdent,
|
||||||
opcIdentToStr,
|
opcIdentToStr,
|
||||||
|
|
||||||
opcEcho,
|
opcEcho,
|
||||||
opcIndCall, # dest = call regStart, n; where regStart = fn, arg1, ...
|
opcIndCall, # dest = call regStart, n; where regStart = fn, arg1, ...
|
||||||
opcIndCallAsgn, # dest = call regStart, n; where regStart = fn, arg1, ...
|
opcIndCallAsgn, # dest = call regStart, n; where regStart = fn, arg1, ...
|
||||||
|
|
@ -110,7 +111,7 @@ type
|
||||||
opcNSetChild,
|
opcNSetChild,
|
||||||
opcCallSite,
|
opcCallSite,
|
||||||
opcNewStr,
|
opcNewStr,
|
||||||
|
|
||||||
opcTJmp, # jump Bx if A != 0
|
opcTJmp, # jump Bx if A != 0
|
||||||
opcFJmp, # jump Bx if A == 0
|
opcFJmp, # jump Bx if A == 0
|
||||||
opcJmp, # jump Bx
|
opcJmp, # jump Bx
|
||||||
|
|
@ -132,7 +133,8 @@ type
|
||||||
opcLdImmInt, # dest = immediate value
|
opcLdImmInt, # dest = immediate value
|
||||||
opcNBindSym,
|
opcNBindSym,
|
||||||
opcSetType, # dest.typ = types[Bx]
|
opcSetType, # dest.typ = types[Bx]
|
||||||
opcTypeTrait
|
opcTypeTrait,
|
||||||
|
opcMarshalLoad, opcMarshalStore
|
||||||
|
|
||||||
TBlock* = object
|
TBlock* = object
|
||||||
label*: PSym
|
label*: PSym
|
||||||
|
|
@ -178,13 +180,13 @@ type
|
||||||
slots*: pointer
|
slots*: pointer
|
||||||
currentException*: PNode
|
currentException*: PNode
|
||||||
VmCallback* = proc (args: VmArgs) {.closure.}
|
VmCallback* = proc (args: VmArgs) {.closure.}
|
||||||
|
|
||||||
PCtx* = ref TCtx
|
PCtx* = ref TCtx
|
||||||
TCtx* = object of passes.TPassContext # code gen context
|
TCtx* = object of passes.TPassContext # code gen context
|
||||||
code*: seq[TInstr]
|
code*: seq[TInstr]
|
||||||
debug*: seq[TLineInfo] # line info for every instruction; kept separate
|
debug*: seq[TLineInfo] # line info for every instruction; kept separate
|
||||||
# to not slow down interpretation
|
# to not slow down interpretation
|
||||||
globals*: PNode #
|
globals*: PNode #
|
||||||
constants*: PNode # constant data
|
constants*: PNode # constant data
|
||||||
types*: seq[PType] # some instructions reference types (e.g. 'except')
|
types*: seq[PType] # some instructions reference types (e.g. 'except')
|
||||||
currentExceptionA*, currentExceptionB*: PNode
|
currentExceptionA*, currentExceptionB*: PNode
|
||||||
|
|
@ -203,7 +205,7 @@ type
|
||||||
TPosition* = distinct int
|
TPosition* = distinct int
|
||||||
|
|
||||||
PEvalContext* = PCtx
|
PEvalContext* = PCtx
|
||||||
|
|
||||||
proc newCtx*(module: PSym): PCtx =
|
proc newCtx*(module: PSym): PCtx =
|
||||||
PCtx(code: @[], debug: @[],
|
PCtx(code: @[], debug: @[],
|
||||||
globals: newNode(nkStmtListExpr), constants: newNode(nkStmtList), types: @[],
|
globals: newNode(nkStmtListExpr), constants: newNode(nkStmtList), types: @[],
|
||||||
|
|
@ -221,7 +223,8 @@ proc registerCallback*(c: PCtx; name: string; callback: VmCallback) =
|
||||||
const
|
const
|
||||||
firstABxInstr* = opcTJmp
|
firstABxInstr* = opcTJmp
|
||||||
largeInstrs* = { # instructions which use 2 int32s instead of 1:
|
largeInstrs* = { # instructions which use 2 int32s instead of 1:
|
||||||
opcSubStr, opcConv, opcCast, opcNewSeq, opcOf}
|
opcSubStr, opcConv, opcCast, opcNewSeq, opcOf,
|
||||||
|
opcMarshalLoad, opcMarshalStore}
|
||||||
slotSomeTemp* = slotTempUnknown
|
slotSomeTemp* = slotTempUnknown
|
||||||
relativeJumps* = {opcTJmp, opcFJmp, opcJmp, opcJmpBack}
|
relativeJumps* = {opcTJmp, opcFJmp, opcJmp, opcJmpBack}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -144,7 +144,9 @@ proc mapTypeToAst(t: PType, info: TLineInfo; allowRecursion=false): PNode =
|
||||||
of tyIter: result = mapTypeToBracket("iter", t, info)
|
of tyIter: result = mapTypeToBracket("iter", t, info)
|
||||||
of tyProxy: result = atomicType"error"
|
of tyProxy: result = atomicType"error"
|
||||||
of tyBuiltInTypeClass: result = mapTypeToBracket("builtinTypeClass", t, info)
|
of tyBuiltInTypeClass: result = mapTypeToBracket("builtinTypeClass", t, info)
|
||||||
of tyUserTypeClass: result = mapTypeToBracket("userTypeClass", t, info)
|
of tyUserTypeClass:
|
||||||
|
result = mapTypeToBracket("concept", t, info)
|
||||||
|
result.add t.n.copyTree
|
||||||
of tyCompositeTypeClass: result = mapTypeToBracket("compositeTypeClass", t, info)
|
of tyCompositeTypeClass: result = mapTypeToBracket("compositeTypeClass", t, info)
|
||||||
of tyAnd: result = mapTypeToBracket("and", t, info)
|
of tyAnd: result = mapTypeToBracket("and", t, info)
|
||||||
of tyOr: result = mapTypeToBracket("or", t, info)
|
of tyOr: result = mapTypeToBracket("or", t, info)
|
||||||
|
|
|
||||||
|
|
@ -76,6 +76,11 @@ proc codeListing(c: PCtx, result: var string, start=0; last = -1) =
|
||||||
elif opc in {opcLdConst, opcAsgnConst}:
|
elif opc in {opcLdConst, opcAsgnConst}:
|
||||||
result.addf("\t$#\tr$#, $#", ($opc).substr(3), x.regA,
|
result.addf("\t$#\tr$#, $#", ($opc).substr(3), x.regA,
|
||||||
c.constants[x.regBx-wordExcess].renderTree)
|
c.constants[x.regBx-wordExcess].renderTree)
|
||||||
|
elif opc in {opcMarshalLoad, opcMarshalStore}:
|
||||||
|
let y = c.code[i+1]
|
||||||
|
result.addf("\t$#\tr$#, r$#, $#", ($opc).substr(3), x.regA, x.regB,
|
||||||
|
c.types[y.regBx-wordExcess].typeToString)
|
||||||
|
inc i
|
||||||
else:
|
else:
|
||||||
result.addf("\t$#\tr$#, $#", ($opc).substr(3), x.regA, x.regBx-wordExcess)
|
result.addf("\t$#\tr$#, $#", ($opc).substr(3), x.regA, x.regBx-wordExcess)
|
||||||
result.add("\t#")
|
result.add("\t#")
|
||||||
|
|
@ -696,8 +701,7 @@ proc genCard(c: PCtx; n: PNode; dest: var TDest) =
|
||||||
c.gABC(n, opcCard, dest, tmp)
|
c.gABC(n, opcCard, dest, tmp)
|
||||||
c.freeTemp(tmp)
|
c.freeTemp(tmp)
|
||||||
|
|
||||||
proc genMagic(c: PCtx; n: PNode; dest: var TDest) =
|
proc genMagic(c: PCtx; n: PNode; dest: var TDest; m: TMagic) =
|
||||||
let m = n.sons[0].sym.magic
|
|
||||||
case m
|
case m
|
||||||
of mAnd: c.genAndOr(n, opcFJmp, dest)
|
of mAnd: c.genAndOr(n, opcFJmp, dest)
|
||||||
of mOr: c.genAndOr(n, opcTJmp, dest)
|
of mOr: c.genAndOr(n, opcTJmp, dest)
|
||||||
|
|
@ -742,9 +746,9 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest) =
|
||||||
c.gABC(n, opcNewStr, dest, tmp)
|
c.gABC(n, opcNewStr, dest, tmp)
|
||||||
c.freeTemp(tmp)
|
c.freeTemp(tmp)
|
||||||
# XXX buggy
|
# XXX buggy
|
||||||
of mLengthOpenArray, mLengthArray, mLengthSeq:
|
of mLengthOpenArray, mLengthArray, mLengthSeq, mXLenSeq:
|
||||||
genUnaryABI(c, n, dest, opcLenSeq)
|
genUnaryABI(c, n, dest, opcLenSeq)
|
||||||
of mLengthStr:
|
of mLengthStr, mXLenStr:
|
||||||
genUnaryABI(c, n, dest, opcLenStr)
|
genUnaryABI(c, n, dest, opcLenStr)
|
||||||
of mIncl, mExcl:
|
of mIncl, mExcl:
|
||||||
unused(n, dest)
|
unused(n, dest)
|
||||||
|
|
@ -791,7 +795,7 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest) =
|
||||||
genUnaryABC(c, n, dest, opcUnaryMinusInt)
|
genUnaryABC(c, n, dest, opcUnaryMinusInt)
|
||||||
genNarrow(c, n, dest)
|
genNarrow(c, n, dest)
|
||||||
of mUnaryMinusF64: genUnaryABC(c, n, dest, opcUnaryMinusFloat)
|
of mUnaryMinusF64: genUnaryABC(c, n, dest, opcUnaryMinusFloat)
|
||||||
of mUnaryPlusI, mUnaryPlusI64, mUnaryPlusF64: gen(c, n.sons[1], dest)
|
of mUnaryPlusI, mUnaryPlusF64: gen(c, n.sons[1], dest)
|
||||||
of mBitnotI, mBitnotI64:
|
of mBitnotI, mBitnotI64:
|
||||||
genUnaryABC(c, n, dest, opcBitnotInt)
|
genUnaryABC(c, n, dest, opcBitnotInt)
|
||||||
genNarrowU(c, n, dest)
|
genNarrowU(c, n, dest)
|
||||||
|
|
@ -1008,7 +1012,7 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest) =
|
||||||
if dest < 0: dest = c.getTemp(n.typ)
|
if dest < 0: dest = c.getTemp(n.typ)
|
||||||
c.gABC(n, opcCallSite, dest)
|
c.gABC(n, opcCallSite, dest)
|
||||||
of mNGenSym: genBinaryABC(c, n, dest, opcGenSym)
|
of mNGenSym: genBinaryABC(c, n, dest, opcGenSym)
|
||||||
of mMinI, mMaxI, mMinI64, mMaxI64, mAbsF64, mMinF64, mMaxF64, mAbsI,
|
of mMinI, mMaxI, mAbsF64, mMinF64, mMaxF64, mAbsI,
|
||||||
mAbsI64, mDotDot:
|
mAbsI64, mDotDot:
|
||||||
c.genCall(n, dest)
|
c.genCall(n, dest)
|
||||||
of mExpandToAst:
|
of mExpandToAst:
|
||||||
|
|
@ -1028,6 +1032,22 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest) =
|
||||||
# mGCref, mGCunref,
|
# mGCref, mGCunref,
|
||||||
internalError(n.info, "cannot generate code for: " & $m)
|
internalError(n.info, "cannot generate code for: " & $m)
|
||||||
|
|
||||||
|
proc genMarshalLoad(c: PCtx, n: PNode, dest: var TDest) =
|
||||||
|
## Signature: proc to*[T](data: string): T
|
||||||
|
if dest < 0: dest = c.getTemp(n.typ)
|
||||||
|
var tmp = c.genx(n.sons[1])
|
||||||
|
c.gABC(n, opcMarshalLoad, dest, tmp)
|
||||||
|
c.gABx(n, opcMarshalLoad, 0, c.genType(n.typ))
|
||||||
|
c.freeTemp(tmp)
|
||||||
|
|
||||||
|
proc genMarshalStore(c: PCtx, n: PNode, dest: var TDest) =
|
||||||
|
## Signature: proc `$$`*[T](x: T): string
|
||||||
|
if dest < 0: dest = c.getTemp(n.typ)
|
||||||
|
var tmp = c.genx(n.sons[1])
|
||||||
|
c.gABC(n, opcMarshalStore, dest, tmp)
|
||||||
|
c.gABx(n, opcMarshalStore, 0, c.genType(n.sons[1].typ))
|
||||||
|
c.freeTemp(tmp)
|
||||||
|
|
||||||
const
|
const
|
||||||
atomicTypes = {tyBool, tyChar,
|
atomicTypes = {tyBool, tyChar,
|
||||||
tyExpr, tyStmt, tyTypeDesc, tyStatic,
|
tyExpr, tyStmt, tyTypeDesc, tyStatic,
|
||||||
|
|
@ -1364,7 +1384,7 @@ proc getNullValue(typ: PType, info: TLineInfo): PNode =
|
||||||
of tyCString, tyString:
|
of tyCString, tyString:
|
||||||
result = newNodeIT(nkStrLit, info, t)
|
result = newNodeIT(nkStrLit, info, t)
|
||||||
of tyVar, tyPointer, tyPtr, tySequence, tyExpr,
|
of tyVar, tyPointer, tyPtr, tySequence, tyExpr,
|
||||||
tyStmt, tyTypeDesc, tyStatic, tyRef:
|
tyStmt, tyTypeDesc, tyStatic, tyRef, tyNil:
|
||||||
result = newNodeIT(nkNilLit, info, t)
|
result = newNodeIT(nkNilLit, info, t)
|
||||||
of tyProc:
|
of tyProc:
|
||||||
if t.callConv != ccClosure:
|
if t.callConv != ccClosure:
|
||||||
|
|
@ -1391,7 +1411,7 @@ proc getNullValue(typ: PType, info: TLineInfo): PNode =
|
||||||
addSon(result, getNullValue(t.sons[i], info))
|
addSon(result, getNullValue(t.sons[i], info))
|
||||||
of tySet:
|
of tySet:
|
||||||
result = newNodeIT(nkCurly, info, t)
|
result = newNodeIT(nkCurly, info, t)
|
||||||
else: internalError("getNullValue: " & $t.kind)
|
else: internalError(info, "getNullValue: " & $t.kind)
|
||||||
|
|
||||||
proc ldNullOpcode(t: PType): TOpcode =
|
proc ldNullOpcode(t: PType): TOpcode =
|
||||||
if fitsRegister(t): opcLdNullReg else: opcLdNull
|
if fitsRegister(t): opcLdNullReg else: opcLdNull
|
||||||
|
|
@ -1533,6 +1553,15 @@ proc matches(s: PSym; x: string): bool =
|
||||||
dec L
|
dec L
|
||||||
result = true
|
result = true
|
||||||
|
|
||||||
|
proc matches(s: PSym; y: varargs[string]): bool =
|
||||||
|
var s = s
|
||||||
|
var L = y.len-1
|
||||||
|
while L >= 0:
|
||||||
|
if s == nil or y[L].cmpIgnoreStyle(s.name.s) != 0: return false
|
||||||
|
s = if sfFromGeneric in s.flags: s.owner.owner else: s.owner
|
||||||
|
dec L
|
||||||
|
result = true
|
||||||
|
|
||||||
proc procIsCallback(c: PCtx; s: PSym): bool =
|
proc procIsCallback(c: PCtx; s: PSym): bool =
|
||||||
if s.offset < -1: return true
|
if s.offset < -1: return true
|
||||||
var i = -2
|
var i = -2
|
||||||
|
|
@ -1570,8 +1599,17 @@ proc gen(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags = {}) =
|
||||||
else:
|
else:
|
||||||
internalError(n.info, "cannot generate code for: " & s.name.s)
|
internalError(n.info, "cannot generate code for: " & s.name.s)
|
||||||
of nkCallKinds:
|
of nkCallKinds:
|
||||||
if n.sons[0].kind == nkSym and n.sons[0].sym.magic != mNone:
|
if n.sons[0].kind == nkSym:
|
||||||
genMagic(c, n, dest)
|
let s = n.sons[0].sym
|
||||||
|
if s.magic != mNone:
|
||||||
|
genMagic(c, n, dest, s.magic)
|
||||||
|
elif matches(s, "stdlib", "marshal", "to"):
|
||||||
|
genMarshalLoad(c, n, dest)
|
||||||
|
elif matches(s, "stdlib", "marshal", "$$"):
|
||||||
|
genMarshalStore(c, n, dest)
|
||||||
|
else:
|
||||||
|
genCall(c, n, dest)
|
||||||
|
clearDest(c, n, dest)
|
||||||
else:
|
else:
|
||||||
genCall(c, n, dest)
|
genCall(c, n, dest)
|
||||||
clearDest(c, n, dest)
|
clearDest(c, n, dest)
|
||||||
|
|
@ -1610,7 +1648,8 @@ proc gen(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags = {}) =
|
||||||
genBreak(c, n)
|
genBreak(c, n)
|
||||||
of nkTryStmt: genTry(c, n, dest)
|
of nkTryStmt: genTry(c, n, dest)
|
||||||
of nkStmtList:
|
of nkStmtList:
|
||||||
unused(n, dest)
|
#unused(n, dest)
|
||||||
|
# XXX Fix this bug properly, lexim triggers it
|
||||||
for x in n: gen(c, x)
|
for x in n: gen(c, x)
|
||||||
of nkStmtListExpr:
|
of nkStmtListExpr:
|
||||||
let L = n.len-1
|
let L = n.len-1
|
||||||
|
|
|
||||||
283
compiler/vmmarshal.nim
Normal file
283
compiler/vmmarshal.nim
Normal file
|
|
@ -0,0 +1,283 @@
|
||||||
|
#
|
||||||
|
#
|
||||||
|
# The Nim Compiler
|
||||||
|
# (c) Copyright 2015 Andreas Rumpf
|
||||||
|
#
|
||||||
|
# See the file "copying.txt", included in this
|
||||||
|
# distribution, for details about the copyright.
|
||||||
|
#
|
||||||
|
|
||||||
|
## Implements marshaling for the VM.
|
||||||
|
|
||||||
|
import streams, json, intsets, tables, ast, astalgo, idents, types, msgs
|
||||||
|
|
||||||
|
proc ptrToInt(x: PNode): int {.inline.} =
|
||||||
|
result = cast[int](x) # don't skip alignment
|
||||||
|
|
||||||
|
proc getField(n: PNode; position: int): PSym =
|
||||||
|
case n.kind
|
||||||
|
of nkRecList:
|
||||||
|
for i in countup(0, sonsLen(n) - 1):
|
||||||
|
result = getField(n.sons[i], position)
|
||||||
|
if result != nil: return
|
||||||
|
of nkRecCase:
|
||||||
|
result = getField(n.sons[0], position)
|
||||||
|
if result != nil: return
|
||||||
|
for i in countup(1, sonsLen(n) - 1):
|
||||||
|
case n.sons[i].kind
|
||||||
|
of nkOfBranch, nkElse:
|
||||||
|
result = getField(lastSon(n.sons[i]), position)
|
||||||
|
if result != nil: return
|
||||||
|
else: internalError(n.info, "getField(record case branch)")
|
||||||
|
of nkSym:
|
||||||
|
if n.sym.position == position: result = n.sym
|
||||||
|
else: discard
|
||||||
|
|
||||||
|
proc storeAny(s: var string; t: PType; a: PNode; stored: var IntSet)
|
||||||
|
|
||||||
|
proc storeObj(s: var string; typ: PType; x: PNode; stored: var IntSet) =
|
||||||
|
internalAssert x.kind in {nkObjConstr, nkPar}
|
||||||
|
let start = ord(x.kind == nkObjConstr)
|
||||||
|
for i in countup(start, sonsLen(x) - 1):
|
||||||
|
if i > start: s.add(", ")
|
||||||
|
var it = x.sons[i]
|
||||||
|
if it.kind == nkExprColonExpr:
|
||||||
|
internalAssert it.sons[0].kind == nkSym
|
||||||
|
let field = it.sons[0].sym
|
||||||
|
s.add(escapeJson(field.name.s))
|
||||||
|
s.add(": ")
|
||||||
|
storeAny(s, field.typ, it.sons[1], stored)
|
||||||
|
elif typ.n != nil:
|
||||||
|
let field = getField(typ.n, i)
|
||||||
|
s.add(escapeJson(field.name.s))
|
||||||
|
s.add(": ")
|
||||||
|
storeAny(s, field.typ, it, stored)
|
||||||
|
|
||||||
|
proc skipColon*(n: PNode): PNode =
|
||||||
|
result = n
|
||||||
|
if n.kind == nkExprColonExpr:
|
||||||
|
result = n.sons[1]
|
||||||
|
|
||||||
|
proc storeAny(s: var string; t: PType; a: PNode; stored: var IntSet) =
|
||||||
|
case t.kind
|
||||||
|
of tyNone: assert false
|
||||||
|
of tyBool: s.add($(a.intVal != 0))
|
||||||
|
of tyChar:
|
||||||
|
let ch = char(a.intVal)
|
||||||
|
if ch < '\128':
|
||||||
|
s.add(escapeJson($ch))
|
||||||
|
else:
|
||||||
|
s.add($int(ch))
|
||||||
|
of tyArray, tySequence:
|
||||||
|
if t.kind == tySequence and a.kind == nkNilLit: s.add("null")
|
||||||
|
else:
|
||||||
|
s.add("[")
|
||||||
|
for i in 0 .. a.len-1:
|
||||||
|
if i > 0: s.add(", ")
|
||||||
|
storeAny(s, t.elemType, a[i], stored)
|
||||||
|
s.add("]")
|
||||||
|
of tyTuple:
|
||||||
|
s.add("{")
|
||||||
|
for i in 0.. <t.len:
|
||||||
|
if i > 0: s.add(", ")
|
||||||
|
s.add("\"Field" & $i)
|
||||||
|
s.add("\": ")
|
||||||
|
storeAny(s, t.sons[i], a[i].skipColon, stored)
|
||||||
|
s.add("}")
|
||||||
|
of tyObject:
|
||||||
|
s.add("{")
|
||||||
|
storeObj(s, t, a, stored)
|
||||||
|
s.add("}")
|
||||||
|
of tySet:
|
||||||
|
s.add("[")
|
||||||
|
for i in 0.. <a.len:
|
||||||
|
if i > 0: s.add(", ")
|
||||||
|
if a[i].kind == nkRange:
|
||||||
|
var x = copyNode(a[i][0])
|
||||||
|
storeAny(s, t.lastSon, x, stored)
|
||||||
|
while x.intVal+1 <= a[i][1].intVal:
|
||||||
|
s.add(", ")
|
||||||
|
storeAny(s, t.lastSon, x, stored)
|
||||||
|
inc x.intVal
|
||||||
|
else:
|
||||||
|
storeAny(s, t.lastSon, a[i], stored)
|
||||||
|
s.add("]")
|
||||||
|
of tyRange, tyGenericInst: storeAny(s, t.lastSon, a, stored)
|
||||||
|
of tyEnum:
|
||||||
|
# we need a slow linear search because of enums with holes:
|
||||||
|
for e in items(t.n):
|
||||||
|
if e.sym.position == a.intVal:
|
||||||
|
s.add e.sym.name.s.escapeJson
|
||||||
|
break
|
||||||
|
of tyPtr, tyRef:
|
||||||
|
var x = a
|
||||||
|
if isNil(x) or x.kind == nkNilLit: s.add("null")
|
||||||
|
elif stored.containsOrIncl(x.ptrToInt):
|
||||||
|
# already stored, so we simply write out the pointer as an int:
|
||||||
|
s.add($x.ptrToInt)
|
||||||
|
else:
|
||||||
|
# else as a [value, key] pair:
|
||||||
|
# (reversed order for convenient x[0] access!)
|
||||||
|
s.add("[")
|
||||||
|
s.add($x.ptrToInt)
|
||||||
|
s.add(", ")
|
||||||
|
storeAny(s, t.lastSon, a, stored)
|
||||||
|
s.add("]")
|
||||||
|
of tyString, tyCString:
|
||||||
|
if a.kind == nkNilLit or a.strVal.isNil: s.add("null")
|
||||||
|
else: s.add(escapeJson(a.strVal))
|
||||||
|
of tyInt..tyInt64, tyUInt..tyUInt64: s.add($a.intVal)
|
||||||
|
of tyFloat..tyFloat128: s.add($a.floatVal)
|
||||||
|
else:
|
||||||
|
internalError a.info, "cannot marshal at compile-time " & t.typeToString
|
||||||
|
|
||||||
|
proc storeAny*(s: var string; t: PType; a: PNode) =
|
||||||
|
var stored = initIntSet()
|
||||||
|
storeAny(s, t, a, stored)
|
||||||
|
|
||||||
|
proc loadAny(p: var JsonParser, t: PType,
|
||||||
|
tab: var Table[BiggestInt, PNode]): PNode =
|
||||||
|
case t.kind
|
||||||
|
of tyNone: assert false
|
||||||
|
of tyBool:
|
||||||
|
case p.kind
|
||||||
|
of jsonFalse: result = newIntNode(nkIntLit, 0)
|
||||||
|
of jsonTrue: result = newIntNode(nkIntLit, 1)
|
||||||
|
else: raiseParseErr(p, "'true' or 'false' expected for a bool")
|
||||||
|
next(p)
|
||||||
|
of tyChar:
|
||||||
|
if p.kind == jsonString:
|
||||||
|
var x = p.str
|
||||||
|
if x.len == 1:
|
||||||
|
result = newIntNode(nkIntLit, ord(x[0]))
|
||||||
|
next(p)
|
||||||
|
return
|
||||||
|
elif p.kind == jsonInt:
|
||||||
|
result = newIntNode(nkIntLit, getInt(p))
|
||||||
|
next(p)
|
||||||
|
return
|
||||||
|
raiseParseErr(p, "string of length 1 expected for a char")
|
||||||
|
of tyEnum:
|
||||||
|
if p.kind == jsonString:
|
||||||
|
for e in items(t.n):
|
||||||
|
if e.sym.name.s == p.str:
|
||||||
|
result = newIntNode(nkIntLit, e.sym.position)
|
||||||
|
next(p)
|
||||||
|
return
|
||||||
|
raiseParseErr(p, "string expected for an enum")
|
||||||
|
of tyArray:
|
||||||
|
if p.kind != jsonArrayStart: raiseParseErr(p, "'[' expected for an array")
|
||||||
|
next(p)
|
||||||
|
result = newNode(nkBracket)
|
||||||
|
while p.kind != jsonArrayEnd and p.kind != jsonEof:
|
||||||
|
result.add loadAny(p, t.elemType, tab)
|
||||||
|
if p.kind == jsonArrayEnd: next(p)
|
||||||
|
else: raiseParseErr(p, "']' end of array expected")
|
||||||
|
of tySequence:
|
||||||
|
case p.kind
|
||||||
|
of jsonNull:
|
||||||
|
result = newNode(nkNilLit)
|
||||||
|
next(p)
|
||||||
|
of jsonArrayStart:
|
||||||
|
next(p)
|
||||||
|
result = newNode(nkBracket)
|
||||||
|
while p.kind != jsonArrayEnd and p.kind != jsonEof:
|
||||||
|
result.add loadAny(p, t.elemType, tab)
|
||||||
|
if p.kind == jsonArrayEnd: next(p)
|
||||||
|
else: raiseParseErr(p, "")
|
||||||
|
else:
|
||||||
|
raiseParseErr(p, "'[' expected for a seq")
|
||||||
|
of tyTuple:
|
||||||
|
if p.kind != jsonObjectStart: raiseParseErr(p, "'{' expected for an object")
|
||||||
|
next(p)
|
||||||
|
result = newNode(nkPar)
|
||||||
|
var i = 0
|
||||||
|
while p.kind != jsonObjectEnd and p.kind != jsonEof:
|
||||||
|
if p.kind != jsonString:
|
||||||
|
raiseParseErr(p, "string expected for a field name")
|
||||||
|
next(p)
|
||||||
|
if i >= t.len:
|
||||||
|
raiseParseErr(p, "too many fields to tuple type " & typeToString(t))
|
||||||
|
result.add loadAny(p, t.sons[i], tab)
|
||||||
|
inc i
|
||||||
|
if p.kind == jsonObjectEnd: next(p)
|
||||||
|
else: raiseParseErr(p, "'}' end of object expected")
|
||||||
|
of tyObject:
|
||||||
|
if p.kind != jsonObjectStart: raiseParseErr(p, "'{' expected for an object")
|
||||||
|
next(p)
|
||||||
|
result = newNode(nkPar)
|
||||||
|
result.sons = @[]
|
||||||
|
while p.kind != jsonObjectEnd and p.kind != jsonEof:
|
||||||
|
if p.kind != jsonString:
|
||||||
|
raiseParseErr(p, "string expected for a field name")
|
||||||
|
let field = lookupInRecord(t.n, getIdent(p.str))
|
||||||
|
if field.isNil:
|
||||||
|
raiseParseErr(p, "unknown field for object of type " & typeToString(t))
|
||||||
|
next(p)
|
||||||
|
if field.position >= result.sons.len:
|
||||||
|
setLen(result.sons, field.position+1)
|
||||||
|
result.sons[field.position] = loadAny(p, field.typ, tab)
|
||||||
|
if p.kind == jsonObjectEnd: next(p)
|
||||||
|
else: raiseParseErr(p, "'}' end of object expected")
|
||||||
|
of tySet:
|
||||||
|
if p.kind != jsonArrayStart: raiseParseErr(p, "'[' expected for a set")
|
||||||
|
next(p)
|
||||||
|
result = newNode(nkCurly)
|
||||||
|
while p.kind != jsonArrayEnd and p.kind != jsonEof:
|
||||||
|
result.add loadAny(p, t.lastSon, tab)
|
||||||
|
next(p)
|
||||||
|
if p.kind == jsonArrayEnd: next(p)
|
||||||
|
else: raiseParseErr(p, "']' end of array expected")
|
||||||
|
of tyPtr, tyRef:
|
||||||
|
case p.kind
|
||||||
|
of jsonNull:
|
||||||
|
result = newNode(nkNilLit)
|
||||||
|
next(p)
|
||||||
|
of jsonInt:
|
||||||
|
result = tab[p.getInt]
|
||||||
|
if result.isNil:
|
||||||
|
raiseParseErr(p, "cannot load object with address " & $p.getInt)
|
||||||
|
next(p)
|
||||||
|
of jsonArrayStart:
|
||||||
|
next(p)
|
||||||
|
if p.kind == jsonInt:
|
||||||
|
let idx = p.getInt
|
||||||
|
next(p)
|
||||||
|
result = loadAny(p, t.lastSon, tab)
|
||||||
|
tab[idx] = result
|
||||||
|
else: raiseParseErr(p, "index for ref type expected")
|
||||||
|
if p.kind == jsonArrayEnd: next(p)
|
||||||
|
else: raiseParseErr(p, "']' end of ref-address pair expected")
|
||||||
|
else: raiseParseErr(p, "int for pointer type expected")
|
||||||
|
of tyString, tyCString:
|
||||||
|
case p.kind
|
||||||
|
of jsonNull:
|
||||||
|
result = newNode(nkNilLit)
|
||||||
|
next(p)
|
||||||
|
of jsonString:
|
||||||
|
result = newStrNode(nkStrLit, p.str)
|
||||||
|
next(p)
|
||||||
|
else: raiseParseErr(p, "string expected")
|
||||||
|
of tyInt..tyInt64, tyUInt..tyUInt64:
|
||||||
|
if p.kind == jsonInt:
|
||||||
|
result = newIntNode(nkIntLit, getInt(p))
|
||||||
|
next(p)
|
||||||
|
return
|
||||||
|
raiseParseErr(p, "int expected")
|
||||||
|
of tyFloat..tyFloat128:
|
||||||
|
if p.kind == jsonFloat:
|
||||||
|
result = newFloatNode(nkFloatLit, getFloat(p))
|
||||||
|
next(p)
|
||||||
|
return
|
||||||
|
raiseParseErr(p, "float expected")
|
||||||
|
of tyRange, tyGenericInst: result = loadAny(p, t.lastSon, tab)
|
||||||
|
else:
|
||||||
|
internalError "cannot marshal at compile-time " & t.typeToString
|
||||||
|
|
||||||
|
proc loadAny*(s: string; t: PType): PNode =
|
||||||
|
var tab = initTable[BiggestInt, PNode]()
|
||||||
|
var p: JsonParser
|
||||||
|
open(p, newStringStream(s), "unknown file")
|
||||||
|
next(p)
|
||||||
|
result = loadAny(p, t, tab)
|
||||||
|
close(p)
|
||||||
|
|
@ -10,7 +10,7 @@
|
||||||
# Unforunately this cannot be a module yet:
|
# Unforunately this cannot be a module yet:
|
||||||
#import vmdeps, vm
|
#import vmdeps, vm
|
||||||
from math import sqrt, ln, log10, log2, exp, round, arccos, arcsin,
|
from math import sqrt, ln, log10, log2, exp, round, arccos, arcsin,
|
||||||
arctan, arctan2, cos, cosh, hypot, sinh, sin, tan, tanh, pow, trunc,
|
arctan, arctan2, cos, cosh, hypot, sinh, sin, tan, tanh, pow, trunc,
|
||||||
floor, ceil, fmod
|
floor, ceil, fmod
|
||||||
|
|
||||||
from os import getEnv, existsEnv, dirExists, fileExists
|
from os import getEnv, existsEnv, dirExists, fileExists
|
||||||
|
|
|
||||||
|
|
@ -96,7 +96,7 @@ path="$lib/pure/unidecode"
|
||||||
|
|
||||||
# Configuration for the GNU C/C++ compiler:
|
# Configuration for the GNU C/C++ compiler:
|
||||||
@if windows:
|
@if windows:
|
||||||
#gcc.path = r"$nimrod\dist\mingw\bin"
|
#gcc.path = r"$nim\dist\mingw\bin"
|
||||||
@if gcc:
|
@if gcc:
|
||||||
tlsEmulation:on
|
tlsEmulation:on
|
||||||
@end
|
@end
|
||||||
|
|
|
||||||
1
csources
Submodule
1
csources
Submodule
|
|
@ -0,0 +1 @@
|
||||||
|
Subproject commit 15724e2e1f3e7749d508dfcd995e84fea2850802
|
||||||
|
|
@ -179,7 +179,7 @@ simpleStmt = ((returnStmt | raiseStmt | yieldStmt | discardStmt | breakStmt
|
||||||
| continueStmt | pragmaStmt | importStmt | exportStmt | fromStmt
|
| continueStmt | pragmaStmt | importStmt | exportStmt | fromStmt
|
||||||
| includeStmt | commentStmt) / exprStmt) COMMENT?
|
| includeStmt | commentStmt) / exprStmt) COMMENT?
|
||||||
complexOrSimpleStmt = (ifStmt | whenStmt | whileStmt
|
complexOrSimpleStmt = (ifStmt | whenStmt | whileStmt
|
||||||
| tryStmt | finallyStmt | exceptStmt | forStmt
|
| tryStmt | forStmt
|
||||||
| blockStmt | staticStmt | deferStmt | asmStmt
|
| blockStmt | staticStmt | deferStmt | asmStmt
|
||||||
| 'proc' routine
|
| 'proc' routine
|
||||||
| 'method' routine
|
| 'method' routine
|
||||||
|
|
|
||||||
47
doc/lib.txt
47
doc/lib.txt
|
|
@ -37,7 +37,7 @@ Core
|
||||||
|
|
||||||
* `unsigned <unsigned.html>`_
|
* `unsigned <unsigned.html>`_
|
||||||
This module implements basic arithmetic operators for unsigned integers.
|
This module implements basic arithmetic operators for unsigned integers.
|
||||||
To discourage users from using unsigned integers, it's not part
|
To discourage users from using unsigned integers, it's not part
|
||||||
of ``system``, but an extra import.
|
of ``system``, but an extra import.
|
||||||
|
|
||||||
* `threads <threads.html>`_
|
* `threads <threads.html>`_
|
||||||
|
|
@ -45,7 +45,7 @@ Core
|
||||||
import it explicitly.
|
import it explicitly.
|
||||||
|
|
||||||
* `channels <channels.html>`_
|
* `channels <channels.html>`_
|
||||||
Nim message passing support for threads. **Note**: This is part of the
|
Nim message passing support for threads. **Note**: This is part of the
|
||||||
system module. Do not import it explicitly.
|
system module. Do not import it explicitly.
|
||||||
|
|
||||||
* `locks <locks.html>`_
|
* `locks <locks.html>`_
|
||||||
|
|
@ -55,7 +55,7 @@ Core
|
||||||
Contains the AST API and documentation of Nim for writing macros.
|
Contains the AST API and documentation of Nim for writing macros.
|
||||||
|
|
||||||
* `typeinfo <typeinfo.html>`_
|
* `typeinfo <typeinfo.html>`_
|
||||||
Provides (unsafe) access to Nim's run time type information.
|
Provides (unsafe) access to Nim's run time type information.
|
||||||
|
|
||||||
* `typetraits <typetraits.html>`_
|
* `typetraits <typetraits.html>`_
|
||||||
This module defines compile-time reflection procs for working with types.
|
This module defines compile-time reflection procs for working with types.
|
||||||
|
|
@ -110,9 +110,9 @@ String handling
|
||||||
|
|
||||||
* `unicode <unicode.html>`_
|
* `unicode <unicode.html>`_
|
||||||
This module provides support to handle the Unicode UTF-8 encoding.
|
This module provides support to handle the Unicode UTF-8 encoding.
|
||||||
|
|
||||||
* `encodings <encodings.html>`_
|
* `encodings <encodings.html>`_
|
||||||
Converts between different character encodings. On UNIX, this uses
|
Converts between different character encodings. On UNIX, this uses
|
||||||
the ``iconv`` library, on Windows the Windows API.
|
the ``iconv`` library, on Windows the Windows API.
|
||||||
|
|
||||||
* `pegs <pegs.html>`_
|
* `pegs <pegs.html>`_
|
||||||
|
|
@ -159,7 +159,7 @@ Generic Operating System Services
|
||||||
may provide other implementations for this standard stream interface.
|
may provide other implementations for this standard stream interface.
|
||||||
|
|
||||||
* `marshal <marshal.html>`_
|
* `marshal <marshal.html>`_
|
||||||
Contains procs for serialization and deseralization of arbitrary Nim
|
Contains procs for serialization and deseralization of arbitrary Nim
|
||||||
data structures.
|
data structures.
|
||||||
|
|
||||||
* `terminal <terminal.html>`_
|
* `terminal <terminal.html>`_
|
||||||
|
|
@ -168,7 +168,7 @@ Generic Operating System Services
|
||||||
sequences and does not depend on any other module.
|
sequences and does not depend on any other module.
|
||||||
|
|
||||||
* `memfiles <memfiles.html>`_
|
* `memfiles <memfiles.html>`_
|
||||||
This module provides support for memory mapped files (Posix's ``mmap``)
|
This module provides support for memory mapped files (Posix's ``mmap``)
|
||||||
on the different operating systems.
|
on the different operating systems.
|
||||||
|
|
||||||
* `fsmonitor <fsmonitor.html>`_
|
* `fsmonitor <fsmonitor.html>`_
|
||||||
|
|
@ -228,7 +228,7 @@ Internet Protocols and Support
|
||||||
This module implements a simple HTTP client.
|
This module implements a simple HTTP client.
|
||||||
|
|
||||||
* `smtp <smtp.html>`_
|
* `smtp <smtp.html>`_
|
||||||
This module implement a simple SMTP client.
|
This module implement a simple SMTP client.
|
||||||
|
|
||||||
* `ftpclient <ftpclient.html>`_
|
* `ftpclient <ftpclient.html>`_
|
||||||
This module implements an FTP client.
|
This module implements an FTP client.
|
||||||
|
|
@ -346,7 +346,7 @@ XML Processing
|
||||||
This module parses an HTML document and creates its XML tree representation.
|
This module parses an HTML document and creates its XML tree representation.
|
||||||
|
|
||||||
* `htmlgen <htmlgen.html>`_
|
* `htmlgen <htmlgen.html>`_
|
||||||
This module implements a simple XML and HTML code
|
This module implements a simple XML and HTML code
|
||||||
generator. Each commonly used HTML tag has a corresponding macro
|
generator. Each commonly used HTML tag has a corresponding macro
|
||||||
that generates a string with its HTML representation.
|
that generates a string with its HTML representation.
|
||||||
|
|
||||||
|
|
@ -381,7 +381,7 @@ Miscellaneous
|
||||||
|
|
||||||
* `oids <oids.html>`_
|
* `oids <oids.html>`_
|
||||||
An OID is a global ID that consists of a timestamp,
|
An OID is a global ID that consists of a timestamp,
|
||||||
a unique counter and a random value. This combination should suffice to
|
a unique counter and a random value. This combination should suffice to
|
||||||
produce a globally distributed unique ID. This implementation was extracted
|
produce a globally distributed unique ID. This implementation was extracted
|
||||||
from the Mongodb interface and it thus binary compatible with a Mongo OID.
|
from the Mongodb interface and it thus binary compatible with a Mongo OID.
|
||||||
|
|
||||||
|
|
@ -453,12 +453,8 @@ Other
|
||||||
* `zipfiles <zipfiles.html>`_
|
* `zipfiles <zipfiles.html>`_
|
||||||
This module implements a zip archive creator/reader/modifier.
|
This module implements a zip archive creator/reader/modifier.
|
||||||
|
|
||||||
* `web <web.html>`_
|
|
||||||
This module contains simple high-level procedures for dealing with the
|
|
||||||
Web like loading the contents of a Web page from an URL.
|
|
||||||
|
|
||||||
* `ssl <ssl.html>`_
|
* `ssl <ssl.html>`_
|
||||||
This module provides an easy to use sockets-style
|
This module provides an easy to use sockets-style
|
||||||
Nim interface to the OpenSSL library.
|
Nim interface to the OpenSSL library.
|
||||||
|
|
||||||
* `rdstdin <rdstdin.html>`_
|
* `rdstdin <rdstdin.html>`_
|
||||||
|
|
@ -513,25 +509,6 @@ Regular expressions
|
||||||
Wrapper for the TRE library.
|
Wrapper for the TRE library.
|
||||||
|
|
||||||
|
|
||||||
Graphics libraries
|
|
||||||
------------------
|
|
||||||
|
|
||||||
* `sdl <sdl.html>`_
|
|
||||||
Part of the wrapper for SDL.
|
|
||||||
* `sdl_gfx <sdl_gfx.html>`_
|
|
||||||
Part of the wrapper for SDL.
|
|
||||||
* `sdl_image <sdl_image.html>`_
|
|
||||||
Part of the wrapper for SDL.
|
|
||||||
* `sdl_mixer <sdl_mixer.html>`_
|
|
||||||
Part of the wrapper for SDL.
|
|
||||||
* `sdl_net <sdl_net.html>`_
|
|
||||||
Part of the wrapper for SDL.
|
|
||||||
* `sdl_ttf <sdl_ttf.html>`_
|
|
||||||
Part of the wrapper for SDL.
|
|
||||||
* `smpeg <smpeg.html>`_
|
|
||||||
Part of the wrapper for SDL.
|
|
||||||
|
|
||||||
|
|
||||||
GUI libraries
|
GUI libraries
|
||||||
-------------
|
-------------
|
||||||
|
|
||||||
|
|
@ -591,7 +568,7 @@ Data Compression and Archiving
|
||||||
Scientific computing
|
Scientific computing
|
||||||
--------------------
|
--------------------
|
||||||
|
|
||||||
* `libsvm <libsvm.html>`_
|
* `libsvm <libsvm.html>`_
|
||||||
Low level wrapper for `lib svm <http://www.csie.ntu.edu.tw/~cjlin/libsvm/>`_.
|
Low level wrapper for `lib svm <http://www.csie.ntu.edu.tw/~cjlin/libsvm/>`_.
|
||||||
|
|
||||||
Nimble
|
Nimble
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ Statements and expressions
|
||||||
==========================
|
==========================
|
||||||
|
|
||||||
Nim uses the common statement/expression paradigm: Statements do not
|
Nim uses the common statement/expression paradigm: Statements do not
|
||||||
produce a value in contrast to expressions. However, some expressions are
|
produce a value in contrast to expressions. However, some expressions are
|
||||||
statements.
|
statements.
|
||||||
|
|
||||||
Statements are separated into `simple statements`:idx: and
|
Statements are separated into `simple statements`:idx: and
|
||||||
|
|
@ -16,9 +16,9 @@ statements always have to be intended. The details can be found in the grammar.
|
||||||
Statement list expression
|
Statement list expression
|
||||||
-------------------------
|
-------------------------
|
||||||
|
|
||||||
Statements can also occur in an expression context that looks
|
Statements can also occur in an expression context that looks
|
||||||
like ``(stmt1; stmt2; ...; ex)``. This is called
|
like ``(stmt1; stmt2; ...; ex)``. This is called
|
||||||
an statement list expression or ``(;)``. The type
|
an statement list expression or ``(;)``. The type
|
||||||
of ``(stmt1; stmt2; ...; ex)`` is the type of ``ex``. All the other statements
|
of ``(stmt1; stmt2; ...; ex)`` is the type of ``ex``. All the other statements
|
||||||
must be of type ``void``. (One can use ``discard`` to produce a ``void`` type.)
|
must be of type ``void``. (One can use ``discard`` to produce a ``void`` type.)
|
||||||
``(;)`` does not introduce a new scope.
|
``(;)`` does not introduce a new scope.
|
||||||
|
|
@ -30,24 +30,24 @@ Discard statement
|
||||||
Example:
|
Example:
|
||||||
|
|
||||||
.. code-block:: nim
|
.. code-block:: nim
|
||||||
proc p(x, y: int): int =
|
proc p(x, y: int): int =
|
||||||
result = x + y
|
result = x + y
|
||||||
|
|
||||||
discard p(3, 4) # discard the return value of `p`
|
discard p(3, 4) # discard the return value of `p`
|
||||||
|
|
||||||
The ``discard`` statement evaluates its expression for side-effects and
|
The ``discard`` statement evaluates its expression for side-effects and
|
||||||
throws the expression's resulting value away.
|
throws the expression's resulting value away.
|
||||||
|
|
||||||
Ignoring the return value of a procedure without using a discard statement is
|
Ignoring the return value of a procedure without using a discard statement is
|
||||||
a static error.
|
a static error.
|
||||||
|
|
||||||
The return value can be ignored implicitly if the called proc/iterator has
|
The return value can be ignored implicitly if the called proc/iterator has
|
||||||
been declared with the `discardable`:idx: pragma:
|
been declared with the `discardable`:idx: pragma:
|
||||||
|
|
||||||
.. code-block:: nim
|
.. code-block:: nim
|
||||||
proc p(x, y: int): int {.discardable.} =
|
proc p(x, y: int): int {.discardable.} =
|
||||||
result = x + y
|
result = x + y
|
||||||
|
|
||||||
p(3, 4) # now valid
|
p(3, 4) # now valid
|
||||||
|
|
||||||
An empty ``discard`` statement is often used as a null statement:
|
An empty ``discard`` statement is often used as a null statement:
|
||||||
|
|
@ -98,11 +98,11 @@ T = enum cast[T](0); this may be an invalid value
|
||||||
|
|
||||||
|
|
||||||
The implicit initialization can be avoided for optimization reasons with the
|
The implicit initialization can be avoided for optimization reasons with the
|
||||||
`noinit`:idx: pragma:
|
`noinit`:idx: pragma:
|
||||||
|
|
||||||
.. code-block:: nim
|
.. code-block:: nim
|
||||||
var
|
var
|
||||||
a {.noInit.}: array [0..1023, char]
|
a {.noInit.}: array [0..1023, char]
|
||||||
|
|
||||||
If a proc is annotated with the ``noinit`` pragma this refers to its implicit
|
If a proc is annotated with the ``noinit`` pragma this refers to its implicit
|
||||||
``result`` variable:
|
``result`` variable:
|
||||||
|
|
@ -113,13 +113,13 @@ If a proc is annotated with the ``noinit`` pragma this refers to its implicit
|
||||||
|
|
||||||
The implicit initialization can be also prevented by the `requiresInit`:idx:
|
The implicit initialization can be also prevented by the `requiresInit`:idx:
|
||||||
type pragma. The compiler requires an explicit initialization then. However
|
type pragma. The compiler requires an explicit initialization then. However
|
||||||
it does a `control flow analysis`:idx: to prove the variable has been
|
it does a `control flow analysis`:idx: to prove the variable has been
|
||||||
initialized and does not rely on syntactic properties:
|
initialized and does not rely on syntactic properties:
|
||||||
|
|
||||||
.. code-block:: nim
|
.. code-block:: nim
|
||||||
type
|
type
|
||||||
MyObject = object {.requiresInit.}
|
MyObject = object {.requiresInit.}
|
||||||
|
|
||||||
proc p() =
|
proc p() =
|
||||||
# the following is valid:
|
# the following is valid:
|
||||||
var x: MyObject
|
var x: MyObject
|
||||||
|
|
@ -129,11 +129,12 @@ initialized and does not rely on syntactic properties:
|
||||||
x = a()
|
x = a()
|
||||||
use x
|
use x
|
||||||
|
|
||||||
|
|
||||||
let statement
|
let statement
|
||||||
-------------
|
-------------
|
||||||
|
|
||||||
A ``let`` statement declares new local and global `single assignment`:idx:
|
A ``let`` statement declares new local and global `single assignment`:idx:
|
||||||
variables and binds a value to them. The syntax is the same as that of the ``var``
|
variables and binds a value to them. The syntax is the same as that of the ``var``
|
||||||
statement, except that the keyword ``var`` is replaced by the keyword ``let``.
|
statement, except that the keyword ``var`` is replaced by the keyword ``let``.
|
||||||
Let variables are not l-values and can thus not be passed to ``var`` parameters
|
Let variables are not l-values and can thus not be passed to ``var`` parameters
|
||||||
nor can their address be taken. They cannot be assigned new values.
|
nor can their address be taken. They cannot be assigned new values.
|
||||||
|
|
@ -141,6 +142,19 @@ nor can their address be taken. They cannot be assigned new values.
|
||||||
For let variables the same pragmas are available as for ordinary variables.
|
For let variables the same pragmas are available as for ordinary variables.
|
||||||
|
|
||||||
|
|
||||||
|
Tuple unpacking
|
||||||
|
---------------
|
||||||
|
|
||||||
|
In a ``var`` or ``let`` statement tuple unpacking can be performed. The special
|
||||||
|
identifier ``_`` can be used to ignore some parts of the tuple:
|
||||||
|
|
||||||
|
.. code-block:: nim
|
||||||
|
proc returnsTuple(): (int, int, int) = (4, 2, 3)
|
||||||
|
|
||||||
|
let (x, _, z) = returnsTuple()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Const section
|
Const section
|
||||||
-------------
|
-------------
|
||||||
|
|
||||||
|
|
@ -157,33 +171,33 @@ have no side-effect can be used in constant expressions too:
|
||||||
constEval = contains("abc", 'b') # computed at compile time!
|
constEval = contains("abc", 'b') # computed at compile time!
|
||||||
|
|
||||||
|
|
||||||
The rules for compile-time computability are:
|
The rules for compile-time computability are:
|
||||||
|
|
||||||
1. Literals are compile-time computable.
|
1. Literals are compile-time computable.
|
||||||
2. Type conversions are compile-time computable.
|
2. Type conversions are compile-time computable.
|
||||||
3. Procedure calls of the form ``p(X)`` are compile-time computable if
|
3. Procedure calls of the form ``p(X)`` are compile-time computable if
|
||||||
``p`` is a proc without side-effects (see the `noSideEffect pragma`_
|
``p`` is a proc without side-effects (see the `noSideEffect pragma`_
|
||||||
for details) and if ``X`` is a (possibly empty) list of compile-time
|
for details) and if ``X`` is a (possibly empty) list of compile-time
|
||||||
computable arguments.
|
computable arguments.
|
||||||
|
|
||||||
|
|
||||||
Constants cannot be of type ``ptr``, ``ref``, ``var`` or ``object``, nor can
|
Constants cannot be of type ``ptr``, ``ref``, ``var`` or ``object``, nor can
|
||||||
they contain such a type.
|
they contain such a type.
|
||||||
|
|
||||||
|
|
||||||
Static statement/expression
|
Static statement/expression
|
||||||
---------------------------
|
---------------------------
|
||||||
|
|
||||||
A static statement/expression can be used to enforce compile
|
A static statement/expression can be used to enforce compile
|
||||||
time evaluation explicitly. Enforced compile time evaluation can even evaluate
|
time evaluation explicitly. Enforced compile time evaluation can even evaluate
|
||||||
code that has side effects:
|
code that has side effects:
|
||||||
|
|
||||||
.. code-block::
|
.. code-block::
|
||||||
|
|
||||||
static:
|
static:
|
||||||
echo "echo at compile time"
|
echo "echo at compile time"
|
||||||
|
|
||||||
It's a static error if the compiler cannot perform the evaluation at compile
|
It's a static error if the compiler cannot perform the evaluation at compile
|
||||||
time.
|
time.
|
||||||
|
|
||||||
The current implementation poses some restrictions for compile time
|
The current implementation poses some restrictions for compile time
|
||||||
|
|
@ -217,7 +231,7 @@ the ``:`` are executed. This goes on until the last ``elif``. If all
|
||||||
conditions fail, the ``else`` part is executed. If there is no ``else``
|
conditions fail, the ``else`` part is executed. If there is no ``else``
|
||||||
part, execution continues with the statement after the ``if`` statement.
|
part, execution continues with the statement after the ``if`` statement.
|
||||||
|
|
||||||
The scoping for an ``if`` statement is slightly subtle to support an important
|
The scoping for an ``if`` statement is slightly subtle to support an important
|
||||||
use case. A new scope starts for the ``if``/``elif`` condition and ends after
|
use case. A new scope starts for the ``if``/``elif`` condition and ends after
|
||||||
the corresponding *then* block:
|
the corresponding *then* block:
|
||||||
|
|
||||||
|
|
@ -229,7 +243,7 @@ the corresponding *then* block:
|
||||||
else:
|
else:
|
||||||
# 'm' not declared here
|
# 'm' not declared here
|
||||||
|
|
||||||
In the example the scopes have been enclosed in ``{| |}``.
|
In the example the scopes have been enclosed in ``{| |}``.
|
||||||
|
|
||||||
|
|
||||||
Case statement
|
Case statement
|
||||||
|
|
@ -244,7 +258,7 @@ Example:
|
||||||
echo("permission denied")
|
echo("permission denied")
|
||||||
of "go-for-a-walk": echo("please yourself")
|
of "go-for-a-walk": echo("please yourself")
|
||||||
else: echo("unknown command")
|
else: echo("unknown command")
|
||||||
|
|
||||||
# indentation of the branches is also allowed; and so is an optional colon
|
# indentation of the branches is also allowed; and so is an optional colon
|
||||||
# after the selecting expression:
|
# after the selecting expression:
|
||||||
case readline(stdin):
|
case readline(stdin):
|
||||||
|
|
@ -252,15 +266,15 @@ Example:
|
||||||
echo("permission denied")
|
echo("permission denied")
|
||||||
of "go-for-a-walk": echo("please yourself")
|
of "go-for-a-walk": echo("please yourself")
|
||||||
else: echo("unknown command")
|
else: echo("unknown command")
|
||||||
|
|
||||||
|
|
||||||
The ``case`` statement is similar to the if statement, but it represents
|
The ``case`` statement is similar to the if statement, but it represents
|
||||||
a multi-branch selection. The expression after the keyword ``case`` is
|
a multi-branch selection. The expression after the keyword ``case`` is
|
||||||
evaluated and if its value is in a *slicelist* the corresponding statements
|
evaluated and if its value is in a *slicelist* the corresponding statements
|
||||||
(after the ``of`` keyword) are executed. If the value is not in any
|
(after the ``of`` keyword) are executed. If the value is not in any
|
||||||
given *slicelist* the ``else`` part is executed. If there is no ``else``
|
given *slicelist* the ``else`` part is executed. If there is no ``else``
|
||||||
part and not all possible values that ``expr`` can hold occur in a
|
part and not all possible values that ``expr`` can hold occur in a
|
||||||
``slicelist``, a static error occurs. This holds only for expressions of
|
``slicelist``, a static error occurs. This holds only for expressions of
|
||||||
ordinal types. "All possible values" of ``expr`` are determined by ``expr``'s
|
ordinal types. "All possible values" of ``expr`` are determined by ``expr``'s
|
||||||
type. To suppress the static error an ``else`` part with an
|
type. To suppress the static error an ``else`` part with an
|
||||||
empty ``discard`` statement should be used.
|
empty ``discard`` statement should be used.
|
||||||
|
|
@ -281,7 +295,7 @@ expanded into a list of its elements:
|
||||||
of SymChars, '_': echo "an identifier"
|
of SymChars, '_': echo "an identifier"
|
||||||
of '0'..'9': echo "a number"
|
of '0'..'9': echo "a number"
|
||||||
else: echo "other"
|
else: echo "other"
|
||||||
|
|
||||||
# is equivalent to:
|
# is equivalent to:
|
||||||
proc classify(s: string) =
|
proc classify(s: string) =
|
||||||
case s[0]
|
case s[0]
|
||||||
|
|
@ -580,14 +594,14 @@ A table constructor is syntactic sugar for an array constructor:
|
||||||
|
|
||||||
.. code-block:: nim
|
.. code-block:: nim
|
||||||
{"key1": "value1", "key2", "key3": "value2"}
|
{"key1": "value1", "key2", "key3": "value2"}
|
||||||
|
|
||||||
# is the same as:
|
# is the same as:
|
||||||
[("key1", "value1"), ("key2", "value2"), ("key3", "value2")]
|
[("key1", "value1"), ("key2", "value2"), ("key3", "value2")]
|
||||||
|
|
||||||
|
|
||||||
The empty table can be written ``{:}`` (in contrast to the empty set
|
The empty table can be written ``{:}`` (in contrast to the empty set
|
||||||
which is ``{}``) which is thus another way to write as the empty array
|
which is ``{}``) which is thus another way to write as the empty array
|
||||||
constructor ``[]``. This slightly unusal way of supporting tables
|
constructor ``[]``. This slightly unusal way of supporting tables
|
||||||
has lots of advantages:
|
has lots of advantages:
|
||||||
|
|
||||||
* The order of the (key,value)-pairs is preserved, thus it is easy to
|
* The order of the (key,value)-pairs is preserved, thus it is easy to
|
||||||
|
|
|
||||||
|
|
@ -15,8 +15,6 @@ Associativity
|
||||||
Binary operators whose first character is ``^`` are right-associative, all
|
Binary operators whose first character is ``^`` are right-associative, all
|
||||||
other binary operators are left-associative.
|
other binary operators are left-associative.
|
||||||
|
|
||||||
Operators ending in ``>`` but longer than a single character are
|
|
||||||
called `arrow like`:idx:.
|
|
||||||
|
|
||||||
|
|
||||||
Precedence
|
Precedence
|
||||||
|
|
@ -33,9 +31,12 @@ as ``(@x).abc`` whereas ``$x.abc`` is parsed as ``$(x.abc)``.
|
||||||
For binary operators that are not keywords the precedence is determined by the
|
For binary operators that are not keywords the precedence is determined by the
|
||||||
following rules:
|
following rules:
|
||||||
|
|
||||||
|
Operators ending in either ``->``, ``~>`` or ``=>`` are called
|
||||||
|
`arrow like`:idx:, and have the lowest precedence of all operators.
|
||||||
|
|
||||||
If the operator ends with ``=`` and its first character is none of
|
If the operator ends with ``=`` and its first character is none of
|
||||||
``<``, ``>``, ``!``, ``=``, ``~``, ``?``, it is an *assignment operator* which
|
``<``, ``>``, ``!``, ``=``, ``~``, ``?``, it is an *assignment operator* which
|
||||||
has the lowest precedence.
|
has the second lowest precedence.
|
||||||
|
|
||||||
Otherwise precedence is determined by the first character.
|
Otherwise precedence is determined by the first character.
|
||||||
|
|
||||||
|
|
@ -43,14 +44,14 @@ Otherwise precedence is determined by the first character.
|
||||||
Precedence level Operators First character Terminal symbol
|
Precedence level Operators First character Terminal symbol
|
||||||
================ =============================================== ================== ===============
|
================ =============================================== ================== ===============
|
||||||
10 (highest) ``$ ^`` OP10
|
10 (highest) ``$ ^`` OP10
|
||||||
9 ``* / div mod shl shr %`` ``* % \ /`` OP9
|
9 ``* / div mod shl shr %`` ``* % \ /`` OP9
|
||||||
8 ``+ -`` ``+ ~ |`` OP8
|
8 ``+ -`` ``+ - ~ |`` OP8
|
||||||
7 ``&`` ``&`` OP7
|
7 ``&`` ``&`` OP7
|
||||||
6 ``..`` ``.`` OP6
|
6 ``..`` ``.`` OP6
|
||||||
5 ``== <= < >= > != in notin is isnot not of`` ``= < > !`` OP5
|
5 ``== <= < >= > != in notin is isnot not of`` ``= < > !`` OP5
|
||||||
4 ``and`` OP4
|
4 ``and`` OP4
|
||||||
3 ``or xor`` OP3
|
3 ``or xor`` OP3
|
||||||
2 ``@ : ?`` OP2
|
2 ``@ : ?`` OP2
|
||||||
1 *assignment operator* (like ``+=``, ``*=``) OP1
|
1 *assignment operator* (like ``+=``, ``*=``) OP1
|
||||||
0 (lowest) *arrow like operator* (like ``->``, ``=>``) OP0
|
0 (lowest) *arrow like operator* (like ``->``, ``=>``) OP0
|
||||||
================ =============================================== ================== ===============
|
================ =============================================== ================== ===============
|
||||||
|
|
@ -67,7 +68,7 @@ is still parsed as ``1 + (3 * 4)``, but ``1+3 * 4`` is parsed as ``(1+3) * 4``:
|
||||||
|
|
||||||
.. code-block:: nim
|
.. code-block:: nim
|
||||||
#! strongSpaces
|
#! strongSpaces
|
||||||
if foo+4 * 4 == 8 and b&c | 9 ++
|
if foo+4 * 4 == 8 and b&c | 9 ++
|
||||||
bar:
|
bar:
|
||||||
echo ""
|
echo ""
|
||||||
# is parsed as
|
# is parsed as
|
||||||
|
|
|
||||||
|
|
@ -127,8 +127,8 @@ The signature has to be:
|
||||||
.. code-block:: nim
|
.. code-block:: nim
|
||||||
proc `=deepCopy`(x: T): T
|
proc `=deepCopy`(x: T): T
|
||||||
|
|
||||||
This mechanism is used by most data structures that support shared memory like
|
This mechanism will be used by most data structures that support shared memory
|
||||||
channels to implement thread safe automatic memory management.
|
like channels to implement thread safe automatic memory management.
|
||||||
|
|
||||||
The builtin ``deepCopy`` can even clone closures and their environments. See
|
The builtin ``deepCopy`` can even clone closures and their environments. See
|
||||||
the documentation of `spawn`_ for details.
|
the documentation of `spawn`_ for details.
|
||||||
|
|
|
||||||
21
doc/nimc.txt
21
doc/nimc.txt
|
|
@ -386,6 +386,25 @@ Example:
|
||||||
As can be seen from the example, to Nim symbols can be referred via backticks.
|
As can be seen from the example, to Nim symbols can be referred via backticks.
|
||||||
Use two backticks to produce a single verbatim backtick.
|
Use two backticks to produce a single verbatim backtick.
|
||||||
|
|
||||||
|
For a toplevel emit statement the section where in the generated C/C++ file
|
||||||
|
the code should be emitted can be influenced via the
|
||||||
|
prefixes ``/*TYPESECTION*/`` or ``/*VARSECTION*/``:
|
||||||
|
|
||||||
|
.. code-block:: Nim
|
||||||
|
{.emit: """/*TYPESECTION*/
|
||||||
|
struct Vector3 {
|
||||||
|
public:
|
||||||
|
Vector3(): x(5) {}
|
||||||
|
Vector3(float x_): x(x_) {}
|
||||||
|
float x;
|
||||||
|
};
|
||||||
|
""".}
|
||||||
|
|
||||||
|
type Vector3 {.importcpp: "Vector3", nodecl} = object
|
||||||
|
x: cfloat
|
||||||
|
|
||||||
|
proc constructVector3(a: cfloat): Vector3 {.importcpp: "Vector3(@)", nodecl}
|
||||||
|
|
||||||
|
|
||||||
ImportCpp pragma
|
ImportCpp pragma
|
||||||
----------------
|
----------------
|
||||||
|
|
@ -611,7 +630,7 @@ Produces:
|
||||||
Produces:
|
Produces:
|
||||||
|
|
||||||
.. code-block:: C
|
.. code-block:: C
|
||||||
|
|
||||||
std::vector<int>::iterator x;
|
std::vector<int>::iterator x;
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
174
doc/nimsuggest.txt
Normal file
174
doc/nimsuggest.txt
Normal file
|
|
@ -0,0 +1,174 @@
|
||||||
|
================================
|
||||||
|
Nim IDE Integration Guide
|
||||||
|
================================
|
||||||
|
|
||||||
|
:Author: Unknown
|
||||||
|
:Version: |nimversion|
|
||||||
|
|
||||||
|
.. contents::
|
||||||
|
|
||||||
|
|
||||||
|
Nim differs from many other compilers in that it is really fast,
|
||||||
|
and being so fast makes it suited to provide external queries for
|
||||||
|
text editors about the source code being written. Through the
|
||||||
|
``nimsuggest`` tool, any IDE
|
||||||
|
can query a ``.nim`` source file and obtain useful information like
|
||||||
|
definition of symbols or suggestions for completion.
|
||||||
|
|
||||||
|
This document will guide you through the available options. If you
|
||||||
|
want to look at practical examples of nimsuggest support you can look
|
||||||
|
at the
|
||||||
|
`various editor integrations <https://github.com/Araq/Nim/wiki/Editor-Support>`_
|
||||||
|
already available.
|
||||||
|
|
||||||
|
|
||||||
|
Installation
|
||||||
|
============
|
||||||
|
|
||||||
|
Nimsuggest is available as a Nimble package but currently does not install
|
||||||
|
properly via Nimble. As nimsuggest is part of the compiler it also doesn't make
|
||||||
|
too much sense as a Nimble package. Instead we will do the building manually::
|
||||||
|
|
||||||
|
cd compiler/nimsuggest
|
||||||
|
nim c -d:release nimsuggest
|
||||||
|
cp nimsuggest ../../bin
|
||||||
|
# OR: copy the nimsuggest binary to where your 'nim' binary is
|
||||||
|
cd ../..
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Nimsuggest invocation
|
||||||
|
=====================
|
||||||
|
|
||||||
|
Run it via ``nimsuggest --stdin myproject.nim``. Nimsuggest is a server that
|
||||||
|
takes queries that are related to ``myproject``. There is some support so that
|
||||||
|
you can throw random ``.nim`` files which are not part of ``myproject`` at
|
||||||
|
Nimsuggest too, but usually the query refer to modules/files that are part of
|
||||||
|
``myproject``.
|
||||||
|
|
||||||
|
``--stdin`` means that Nimsuggest reads the query from ``stdin``. This is great
|
||||||
|
for testing things out and playing with it but for an editor communication
|
||||||
|
via sockets is more reasonable so that is the default. It listens to port 6000
|
||||||
|
by default.
|
||||||
|
|
||||||
|
|
||||||
|
Specifying the location of the query
|
||||||
|
------------------------------------
|
||||||
|
|
||||||
|
Nimsuggest than waits for queries to process. A query consists of a
|
||||||
|
cryptic 3 letter "command" ``def`` or ``con`` or ``sug`` or ``use`` followed by
|
||||||
|
a location. A query location consists of:
|
||||||
|
|
||||||
|
|
||||||
|
``file.nim``
|
||||||
|
This is the name of the module or include file the query refers to.
|
||||||
|
|
||||||
|
``dirtyfile.nim``
|
||||||
|
This is optional.
|
||||||
|
|
||||||
|
The ``file`` paramater is enough for static analysis, but IDEs
|
||||||
|
tend to have *unsaved buffers* where the user may still be in
|
||||||
|
the middle of typing a line. In such situations the IDE can
|
||||||
|
save the current contents to a temporary file and then use the
|
||||||
|
``dirtyfile.nim`` option to tell Nimsuggest that ``foobar.nim`` should
|
||||||
|
be taken from ``temporary/foobar.nim``.
|
||||||
|
|
||||||
|
|
||||||
|
``line``
|
||||||
|
An integer with the line you are going to query. For the compiler
|
||||||
|
lines start at **1**.
|
||||||
|
|
||||||
|
``col``
|
||||||
|
An integer with the column you are going to query. For the
|
||||||
|
compiler columns start at **1**.
|
||||||
|
|
||||||
|
|
||||||
|
Definitions
|
||||||
|
-----------
|
||||||
|
|
||||||
|
The ``def`` Nimsuggest command performs a query about the definition
|
||||||
|
of a specific symbol. If available, Nimsuggest will answer with the
|
||||||
|
type, source file, line/column information and other accessory data
|
||||||
|
if available like a docstring. With this information an IDE can
|
||||||
|
provide the typical *Jump to definition* where a user puts the
|
||||||
|
cursor on a symbol or uses the mouse to select it and is redirected
|
||||||
|
to the place where the symbol is located.
|
||||||
|
|
||||||
|
Since Nim is implemented in Nim, one of the nice things of
|
||||||
|
this feature is that any user with an IDE supporting it can quickly
|
||||||
|
jump around the standard library implementation and see exactly
|
||||||
|
what a proc does, learning about the language and seeing real life
|
||||||
|
examples of how to write/implement specific features.
|
||||||
|
|
||||||
|
Nimsuggest will always answer with a single definition or none if it
|
||||||
|
can't find any valid symbol matching the position of the query.
|
||||||
|
|
||||||
|
|
||||||
|
Suggestions
|
||||||
|
-----------
|
||||||
|
|
||||||
|
The ``sug`` Nimsuggest command performs a query about possible
|
||||||
|
completion symbols at some point in the file.
|
||||||
|
|
||||||
|
The typical usage scenario for this option is to call it after the
|
||||||
|
user has typed the dot character for `the object oriented call
|
||||||
|
syntax <tut2.html#method-call-syntax>`_. Nimsuggest will try to return
|
||||||
|
the suggestions sorted first by scope (from innermost to outermost)
|
||||||
|
and then by item name.
|
||||||
|
|
||||||
|
|
||||||
|
Invocation context
|
||||||
|
------------------
|
||||||
|
|
||||||
|
The ``con`` Nimsuggest command is very similar to the suggestions
|
||||||
|
command, but instead of being used after the user has typed a dot
|
||||||
|
character, this one is meant to be used after the user has typed
|
||||||
|
an opening brace to start typing parameters.
|
||||||
|
|
||||||
|
|
||||||
|
Symbol usages
|
||||||
|
-------------
|
||||||
|
|
||||||
|
The ``use`` Nimsuggest command lists all usages of the symbol at
|
||||||
|
a position. IDEs can use this to find all the places in the file
|
||||||
|
where the symbol is used and offer the user to rename it in all
|
||||||
|
places at the same time.
|
||||||
|
|
||||||
|
For this kind of query the IDE will most likely ignore all the
|
||||||
|
type/signature info provided by Nimsuggest and concentrate on the
|
||||||
|
filename, line and column position of the multiple returned answers.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Parsing nimsuggest output
|
||||||
|
=========================
|
||||||
|
|
||||||
|
Nimsuggest output is always returned on single lines separated by
|
||||||
|
tab characters (``\t``). The values of each column are:
|
||||||
|
|
||||||
|
1. Three characters indicating the type of returned answer (e.g.
|
||||||
|
``def`` for definition, ``sug`` for suggestion, etc).
|
||||||
|
2. Type of the symbol. This can be ``skProc``, ``skLet``, and just
|
||||||
|
about any of the enums defined in the module ``compiler/ast.nim``.
|
||||||
|
3. Full qualitifed path of the symbol. If you are querying a symbol
|
||||||
|
defined in the ``proj.nim`` file, this would have the form
|
||||||
|
``proj.symbolName``.
|
||||||
|
4. Type/signature. For variables and enums this will contain the
|
||||||
|
type of the symbol, for procs, methods and templates this will
|
||||||
|
contain the full unique signature (e.g. ``proc (File)``).
|
||||||
|
5. Full path to the file containing the symbol.
|
||||||
|
6. Line where the symbol is located in the file. Lines start to
|
||||||
|
count at **1**.
|
||||||
|
7. Column where the symbol is located in the file. Columns start
|
||||||
|
to count at **1**.
|
||||||
|
8. Docstring for the symbol if available or the empty string. To
|
||||||
|
differentiate the docstring from end of answer,
|
||||||
|
the docstring is always provided enclosed in double quotes, and
|
||||||
|
if the docstring spans multiple lines, all following lines of the
|
||||||
|
docstring will start with a blank space to align visually with
|
||||||
|
the starting quote.
|
||||||
|
|
||||||
|
Also, you won't find raw ``\n`` characters breaking the one
|
||||||
|
answer per line format. Instead you will need to parse sequences
|
||||||
|
in the form ``\xHH``, where *HH* is a hexadecimal value (e.g.
|
||||||
|
newlines generate the sequence ``\x0A``).
|
||||||
|
|
@ -4,6 +4,11 @@ Tools available with Nim
|
||||||
|
|
||||||
The standard distribution ships with the following tools:
|
The standard distribution ships with the following tools:
|
||||||
|
|
||||||
|
- | `Nimsuggest for IDE support <nimsuggest.html>`_
|
||||||
|
| Through the ``nimsuggest`` tool, any IDE can query a ``.nim`` source file
|
||||||
|
and obtain useful information like definition of symbols or suggestions for
|
||||||
|
completion.
|
||||||
|
|
||||||
- | `Nim Installation Generator <niminst.html>`_
|
- | `Nim Installation Generator <niminst.html>`_
|
||||||
| How to generate a nice installer for your Nim program.
|
| How to generate a nice installer for your Nim program.
|
||||||
|
|
||||||
|
|
|
||||||
72
doc/tut1.txt
72
doc/tut1.txt
|
|
@ -16,7 +16,7 @@ Introduction
|
||||||
</p></blockquote>
|
</p></blockquote>
|
||||||
|
|
||||||
|
|
||||||
This document is a tutorial for the programming language *Nim*.
|
This document is a tutorial for the programming language *Nim*.
|
||||||
This tutorial assumes that you are familiar with basic programming concepts
|
This tutorial assumes that you are familiar with basic programming concepts
|
||||||
like variables, types or statements but is kept very basic. The `manual
|
like variables, types or statements but is kept very basic. The `manual
|
||||||
<manual.html>`_ contains many more examples of the advanced language features.
|
<manual.html>`_ contains many more examples of the advanced language features.
|
||||||
|
|
@ -50,7 +50,7 @@ Commonly used commands and switches have abbreviations, so you can also use::
|
||||||
nim c -r greetings.nim
|
nim c -r greetings.nim
|
||||||
|
|
||||||
To compile a release version use::
|
To compile a release version use::
|
||||||
|
|
||||||
nim c -d:release greetings.nim
|
nim c -d:release greetings.nim
|
||||||
|
|
||||||
By default the Nim compiler generates a large amount of runtime checks
|
By default the Nim compiler generates a large amount of runtime checks
|
||||||
|
|
@ -116,7 +116,7 @@ hash character ``#``. Documentation comments start with ``##``:
|
||||||
|
|
||||||
.. code-block:: nim
|
.. code-block:: nim
|
||||||
# A comment.
|
# A comment.
|
||||||
|
|
||||||
var myVariable: int ## a documentation comment
|
var myVariable: int ## a documentation comment
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -200,7 +200,7 @@ constant declaration at compile time:
|
||||||
|
|
||||||
.. code-block:: nim
|
.. code-block:: nim
|
||||||
const x = "abc" # the constant x contains the string "abc"
|
const x = "abc" # the constant x contains the string "abc"
|
||||||
|
|
||||||
Indentation can be used after the ``const`` keyword to list a whole section of
|
Indentation can be used after the ``const`` keyword to list a whole section of
|
||||||
constants:
|
constants:
|
||||||
|
|
||||||
|
|
@ -214,7 +214,7 @@ constants:
|
||||||
|
|
||||||
The let statement
|
The let statement
|
||||||
=================
|
=================
|
||||||
The ``let`` statement works like the ``var`` statement but the declared
|
The ``let`` statement works like the ``var`` statement but the declared
|
||||||
symbols are *single assignment* variables: After the initialization their
|
symbols are *single assignment* variables: After the initialization their
|
||||||
value cannot change:
|
value cannot change:
|
||||||
|
|
||||||
|
|
@ -228,7 +228,7 @@ and put it into a data section":
|
||||||
|
|
||||||
.. code-block::
|
.. code-block::
|
||||||
const input = readLine(stdin) # Error: constant expression expected
|
const input = readLine(stdin) # Error: constant expression expected
|
||||||
|
|
||||||
.. code-block::
|
.. code-block::
|
||||||
let input = readLine(stdin) # works
|
let input = readLine(stdin) # works
|
||||||
|
|
||||||
|
|
@ -310,8 +310,8 @@ the compiler that for every other value nothing should be done:
|
||||||
else: discard
|
else: discard
|
||||||
|
|
||||||
The empty `discard statement`_ is a *do nothing* statement. The compiler knows
|
The empty `discard statement`_ is a *do nothing* statement. The compiler knows
|
||||||
that a case statement with an else part cannot fail and thus the error
|
that a case statement with an else part cannot fail and thus the error
|
||||||
disappears. Note that it is impossible to cover all possible string values:
|
disappears. Note that it is impossible to cover all possible string values:
|
||||||
that is why string cases always need an ``else`` branch.
|
that is why string cases always need an ``else`` branch.
|
||||||
|
|
||||||
In general the case statement is used for subrange types or enumerations where
|
In general the case statement is used for subrange types or enumerations where
|
||||||
|
|
@ -406,7 +406,7 @@ The block's *label* (``myblock`` in the example) is optional.
|
||||||
Break statement
|
Break statement
|
||||||
---------------
|
---------------
|
||||||
A block can be left prematurely with a ``break`` statement. The break statement
|
A block can be left prematurely with a ``break`` statement. The break statement
|
||||||
can leave a ``while``, ``for``, or a ``block`` statement. It leaves the
|
can leave a ``while``, ``for``, or a ``block`` statement. It leaves the
|
||||||
innermost construct, unless a label of a block is given:
|
innermost construct, unless a label of a block is given:
|
||||||
|
|
||||||
.. code-block:: nim
|
.. code-block:: nim
|
||||||
|
|
@ -461,7 +461,7 @@ differences:
|
||||||
* The statements within a branch do not open a new scope.
|
* The statements within a branch do not open a new scope.
|
||||||
* The compiler checks the semantics and produces code *only* for the statements
|
* The compiler checks the semantics and produces code *only* for the statements
|
||||||
that belong to the first condition that evaluates to ``true``.
|
that belong to the first condition that evaluates to ``true``.
|
||||||
|
|
||||||
The ``when`` statement is useful for writing platform specific code, similar to
|
The ``when`` statement is useful for writing platform specific code, similar to
|
||||||
the ``#ifdef`` construct in the C programming language.
|
the ``#ifdef`` construct in the C programming language.
|
||||||
|
|
||||||
|
|
@ -486,14 +486,14 @@ to be indented, but single simple statements do not:
|
||||||
.. code-block:: nim
|
.. code-block:: nim
|
||||||
# no indentation needed for single assignment statement:
|
# no indentation needed for single assignment statement:
|
||||||
if x: x = false
|
if x: x = false
|
||||||
|
|
||||||
# indentation needed for nested if statement:
|
# indentation needed for nested if statement:
|
||||||
if x:
|
if x:
|
||||||
if y:
|
if y:
|
||||||
y = false
|
y = false
|
||||||
else:
|
else:
|
||||||
y = true
|
y = true
|
||||||
|
|
||||||
# indentation needed, because two statements follow the condition:
|
# indentation needed, because two statements follow the condition:
|
||||||
if x:
|
if x:
|
||||||
x = false
|
x = false
|
||||||
|
|
@ -514,7 +514,7 @@ contain indentation at certain places for better readability:
|
||||||
As a rule of thumb, indentation within expressions is allowed after operators,
|
As a rule of thumb, indentation within expressions is allowed after operators,
|
||||||
an open parenthesis and after commas.
|
an open parenthesis and after commas.
|
||||||
|
|
||||||
With parenthesis and semicolons ``(;)`` you can use statements where only
|
With parenthesis and semicolons ``(;)`` you can use statements where only
|
||||||
an expression is allowed:
|
an expression is allowed:
|
||||||
|
|
||||||
.. code-block:: nim
|
.. code-block:: nim
|
||||||
|
|
@ -560,45 +560,45 @@ Some terminology: in the example ``question`` is called a (formal) *parameter*,
|
||||||
|
|
||||||
Result variable
|
Result variable
|
||||||
---------------
|
---------------
|
||||||
A procedure that returns a value has an implicit ``result`` variable declared
|
A procedure that returns a value has an implicit ``result`` variable declared
|
||||||
that represents the return value. A ``return`` statement with no expression is a
|
that represents the return value. A ``return`` statement with no expression is a
|
||||||
shorthand for ``return result``. The ``result`` value is always returned
|
shorthand for ``return result``. The ``result`` value is always returned
|
||||||
automatically at the end a procedure if there is no ``return`` statement at
|
automatically at the end a procedure if there is no ``return`` statement at
|
||||||
the exit.
|
the exit.
|
||||||
|
|
||||||
.. code-block:: nim
|
.. code-block:: nim
|
||||||
proc sumTillNegative(x: varargs[int]): int =
|
proc sumTillNegative(x: varargs[int]): int =
|
||||||
for i in x:
|
for i in x:
|
||||||
if i < 0:
|
if i < 0:
|
||||||
return
|
return
|
||||||
result = result + i
|
result = result + i
|
||||||
|
|
||||||
echo sumTillNegative() # echos 0
|
echo sumTillNegative() # echos 0
|
||||||
echo sumTillNegative(3, 4, 5) # echos 12
|
echo sumTillNegative(3, 4, 5) # echos 12
|
||||||
echo sumTillNegative(3, 4 , -1 , 6) # echos 7
|
echo sumTillNegative(3, 4 , -1 , 6) # echos 7
|
||||||
|
|
||||||
The ``result`` variable is already implicitly declared at the start of the
|
The ``result`` variable is already implicitly declared at the start of the
|
||||||
function, so declaring it again with 'var result', for example, would shadow it
|
function, so declaring it again with 'var result', for example, would shadow it
|
||||||
with a normal variable of the same name. The result variable is also already
|
with a normal variable of the same name. The result variable is also already
|
||||||
initialised with the type's default value. Note that referential data types will
|
initialised with the type's default value. Note that referential data types will
|
||||||
be ``nil`` at the start of the procedure, and thus may require manual
|
be ``nil`` at the start of the procedure, and thus may require manual
|
||||||
initialisation.
|
initialisation.
|
||||||
|
|
||||||
|
|
||||||
Parameters
|
Parameters
|
||||||
----------
|
----------
|
||||||
Parameters are constant in the procedure body. By default, their value cannot be
|
Parameters are constant in the procedure body. By default, their value cannot be
|
||||||
changed because this allows the compiler to implement parameter passing in the
|
changed because this allows the compiler to implement parameter passing in the
|
||||||
most efficient way. If a mutable variable is needed inside the procedure, it has
|
most efficient way. If a mutable variable is needed inside the procedure, it has
|
||||||
to be declared with ``var`` in the procedure body. Shadowing the parameter name
|
to be declared with ``var`` in the procedure body. Shadowing the parameter name
|
||||||
is possible, and actually an idiom:
|
is possible, and actually an idiom:
|
||||||
|
|
||||||
.. code-block:: nim
|
.. code-block:: nim
|
||||||
proc printSeq(s: seq, nprinted: int = -1) =
|
proc printSeq(s: seq, nprinted: int = -1) =
|
||||||
var nprinted = if nprinted == -1: s.len else: min(nprinted, s.len)
|
var nprinted = if nprinted == -1: s.len else: min(nprinted, s.len)
|
||||||
for i in 0 .. <nprinted:
|
for i in 0 .. <nprinted:
|
||||||
echo s[i]
|
echo s[i]
|
||||||
|
|
||||||
If the procedure needs to modify the argument for the
|
If the procedure needs to modify the argument for the
|
||||||
caller, a ``var`` parameter can be used:
|
caller, a ``var`` parameter can be used:
|
||||||
|
|
||||||
|
|
@ -630,12 +630,12 @@ allow to silently throw away a return value:
|
||||||
|
|
||||||
|
|
||||||
The return value can be ignored implicitly if the called proc/iterator has
|
The return value can be ignored implicitly if the called proc/iterator has
|
||||||
been declared with the ``discardable`` pragma:
|
been declared with the ``discardable`` pragma:
|
||||||
|
|
||||||
.. code-block:: nim
|
.. code-block:: nim
|
||||||
proc p(x, y: int): int {.discardable.} =
|
proc p(x, y: int): int {.discardable.} =
|
||||||
return x + y
|
return x + y
|
||||||
|
|
||||||
p(3, 4) # now valid
|
p(3, 4) # now valid
|
||||||
|
|
||||||
The ``discard`` statement can also be used to create block comments as
|
The ``discard`` statement can also be used to create block comments as
|
||||||
|
|
@ -899,7 +899,7 @@ object on the heap, so there is a trade-off to be made here.
|
||||||
|
|
||||||
Integers
|
Integers
|
||||||
--------
|
--------
|
||||||
Nim has these integer types built-in:
|
Nim has these integer types built-in:
|
||||||
``int int8 int16 int32 int64 uint uint8 uint16 uint32 uint64``.
|
``int int8 int16 int32 int64 uint uint8 uint16 uint32 uint64``.
|
||||||
|
|
||||||
The default integer type is ``int``. Integer literals can have a *type suffix*
|
The default integer type is ``int``. Integer literals can have a *type suffix*
|
||||||
|
|
@ -1114,7 +1114,7 @@ Arrays
|
||||||
An array is a simple fixed length container. Each element in
|
An array is a simple fixed length container. Each element in
|
||||||
the array has the same type. The array's index type can be any ordinal type.
|
the array has the same type. The array's index type can be any ordinal type.
|
||||||
|
|
||||||
Arrays can be constructed via ``[]``:
|
Arrays can be constructed via ``[]``:
|
||||||
|
|
||||||
.. code-block:: nim
|
.. code-block:: nim
|
||||||
|
|
||||||
|
|
@ -1370,12 +1370,12 @@ integer.
|
||||||
var building: tuple[street: string, number: int]
|
var building: tuple[street: string, number: int]
|
||||||
building = ("Rue del Percebe", 13)
|
building = ("Rue del Percebe", 13)
|
||||||
echo(building.street)
|
echo(building.street)
|
||||||
|
|
||||||
# The following line does not compile, they are different tuples!
|
# The following line does not compile, they are different tuples!
|
||||||
#person = building
|
#person = building
|
||||||
# --> Error: type mismatch: got (tuple[street: string, number: int])
|
# --> Error: type mismatch: got (tuple[street: string, number: int])
|
||||||
# but expected 'Person'
|
# but expected 'Person'
|
||||||
|
|
||||||
# The following works because the field names and types are the same.
|
# The following works because the field names and types are the same.
|
||||||
var teacher: tuple[name: string, age: int] = ("Mark", 42)
|
var teacher: tuple[name: string, age: int] = ("Mark", 42)
|
||||||
person = teacher
|
person = teacher
|
||||||
|
|
@ -1450,13 +1450,13 @@ operators perform implicit dereferencing operations for reference types:
|
||||||
|
|
||||||
type
|
type
|
||||||
Node = ref NodeObj
|
Node = ref NodeObj
|
||||||
NodeObj = object
|
NodeObj = object
|
||||||
le, ri: PNode
|
le, ri: Node
|
||||||
data: int
|
data: int
|
||||||
var
|
var
|
||||||
n: Node
|
n: Node
|
||||||
new(n)
|
new(n)
|
||||||
n.data = 9
|
n.data = 9
|
||||||
# no need to write n[].data; in fact n[].data is highly discouraged!
|
# no need to write n[].data; in fact n[].data is highly discouraged!
|
||||||
|
|
||||||
To allocate a new traced object, the built-in procedure ``new`` has to be used.
|
To allocate a new traced object, the built-in procedure ``new`` has to be used.
|
||||||
|
|
@ -1559,9 +1559,9 @@ This is best illustrated by an example:
|
||||||
|
|
||||||
|
|
||||||
A symbol of a module *can* be *qualified* with the ``module.symbol`` syntax. If
|
A symbol of a module *can* be *qualified* with the ``module.symbol`` syntax. If
|
||||||
the symbol is ambiguous, it even *has* to be qualified. A symbol is ambiguous
|
the symbol is ambiguous, it even *has* to be qualified. A symbol is ambiguous
|
||||||
if it is defined in two (or more) different modules and both modules are
|
if it is defined in two (or more) different modules and both modules are
|
||||||
imported by a third one:
|
imported by a third one:
|
||||||
|
|
||||||
.. code-block:: nim
|
.. code-block:: nim
|
||||||
# Module A
|
# Module A
|
||||||
|
|
|
||||||
76
koch.nim
76
koch.nim
|
|
@ -81,7 +81,7 @@ proc exec(cmd: string, errorcode: int = QuitFailure) =
|
||||||
echo(cmd)
|
echo(cmd)
|
||||||
if execShellCmd(cmd) != 0: quit("FAILURE", errorcode)
|
if execShellCmd(cmd) != 0: quit("FAILURE", errorcode)
|
||||||
|
|
||||||
proc tryExec(cmd: string): bool =
|
proc tryExec(cmd: string): bool =
|
||||||
echo(cmd)
|
echo(cmd)
|
||||||
result = execShellCmd(cmd) == 0
|
result = execShellCmd(cmd) == 0
|
||||||
|
|
||||||
|
|
@ -96,7 +96,7 @@ proc copyExe(source, dest: string) =
|
||||||
const
|
const
|
||||||
compileNimInst = "-d:useLibzipSrc tools/niminst/niminst"
|
compileNimInst = "-d:useLibzipSrc tools/niminst/niminst"
|
||||||
|
|
||||||
proc csource(args: string) =
|
proc csource(args: string) =
|
||||||
exec("$4 cc $1 -r $3 --var:version=$2 --var:mingw=none csource --main:compiler/nim.nim compiler/installer.ini $1" %
|
exec("$4 cc $1 -r $3 --var:version=$2 --var:mingw=none csource --main:compiler/nim.nim compiler/installer.ini $1" %
|
||||||
[args, VersionAsString, compileNimInst, findNim()])
|
[args, VersionAsString, compileNimInst, findNim()])
|
||||||
|
|
||||||
|
|
@ -106,6 +106,12 @@ proc zip(args: string) =
|
||||||
exec("$# --var:version=$# --var:mingw=none --main:compiler/nim.nim zip compiler/installer.ini" %
|
exec("$# --var:version=$# --var:mingw=none --main:compiler/nim.nim zip compiler/installer.ini" %
|
||||||
["tools/niminst/niminst".exe, VersionAsString])
|
["tools/niminst/niminst".exe, VersionAsString])
|
||||||
|
|
||||||
|
proc targz(args: string) =
|
||||||
|
exec("$3 cc -r $2 --var:version=$1 --var:mingw=none --main:compiler/nim.nim scripts compiler/installer.ini" %
|
||||||
|
[VersionAsString, compileNimInst, findNim()])
|
||||||
|
exec("$# --var:version=$# --var:mingw=none --main:compiler/nim.nim targz compiler/installer.ini" %
|
||||||
|
["tools" / "niminst" / "niminst".exe, VersionAsString])
|
||||||
|
|
||||||
proc buildTool(toolname, args: string) =
|
proc buildTool(toolname, args: string) =
|
||||||
exec("$# cc $# $#" % [findNim(), args, toolname])
|
exec("$# cc $# $#" % [findNim(), args, toolname])
|
||||||
copyFile(dest="bin"/ splitFile(toolname).name.exe, source=toolname.exe)
|
copyFile(dest="bin"/ splitFile(toolname).name.exe, source=toolname.exe)
|
||||||
|
|
@ -113,14 +119,14 @@ proc buildTool(toolname, args: string) =
|
||||||
proc nsis(args: string) =
|
proc nsis(args: string) =
|
||||||
# make sure we have generated the niminst executables:
|
# make sure we have generated the niminst executables:
|
||||||
buildTool("tools/niminst/niminst", args)
|
buildTool("tools/niminst/niminst", args)
|
||||||
buildTool("tools/nimgrep", args)
|
#buildTool("tools/nimgrep", args)
|
||||||
# produce 'nimrod_debug.exe':
|
# produce 'nim_debug.exe':
|
||||||
exec "nim c compiler" / "nim.nim"
|
#exec "nim c compiler" / "nim.nim"
|
||||||
copyExe("compiler/nim".exe, "bin/nim_debug".exe)
|
#copyExe("compiler/nim".exe, "bin/nim_debug".exe)
|
||||||
exec(("tools" / "niminst" / "niminst --var:version=$# --var:mingw=mingw$#" &
|
exec(("tools" / "niminst" / "niminst --var:version=$# --var:mingw=mingw$#" &
|
||||||
" nsis compiler/nim") % [VersionAsString, $(sizeof(pointer)*8)])
|
" nsis compiler/installer.ini") % [VersionAsString, $(sizeof(pointer)*8)])
|
||||||
|
|
||||||
proc install(args: string) =
|
proc install(args: string) =
|
||||||
exec("$# cc -r $# --var:version=$# --var:mingw=none --main:compiler/nim.nim scripts compiler/installer.ini" %
|
exec("$# cc -r $# --var:version=$# --var:mingw=none --main:compiler/nim.nim scripts compiler/installer.ini" %
|
||||||
[findNim(), compileNimInst, VersionAsString])
|
[findNim(), compileNimInst, VersionAsString])
|
||||||
exec("sh ./install.sh $#" % args)
|
exec("sh ./install.sh $#" % args)
|
||||||
|
|
@ -139,12 +145,10 @@ proc pdf(args="") =
|
||||||
|
|
||||||
# -------------- boot ---------------------------------------------------------
|
# -------------- boot ---------------------------------------------------------
|
||||||
|
|
||||||
proc findStartNim: string =
|
proc findStartNim: string =
|
||||||
# we try several things before giving up:
|
# we try several things before giving up:
|
||||||
# * bin/nim
|
# * bin/nim
|
||||||
# * $PATH/nim
|
# * $PATH/nim
|
||||||
# * bin/nimrod
|
|
||||||
# * $PATH/nimrod
|
|
||||||
# If these fail, we try to build nim with the "build.(sh|bat)" script.
|
# If these fail, we try to build nim with the "build.(sh|bat)" script.
|
||||||
var nim = "nim".exe
|
var nim = "nim".exe
|
||||||
result = "bin" / nim
|
result = "bin" / nim
|
||||||
|
|
@ -152,34 +156,27 @@ proc findStartNim: string =
|
||||||
for dir in split(getEnv("PATH"), PathSep):
|
for dir in split(getEnv("PATH"), PathSep):
|
||||||
if existsFile(dir / nim): return dir / nim
|
if existsFile(dir / nim): return dir / nim
|
||||||
|
|
||||||
# try the old "nimrod.exe":
|
|
||||||
var nimrod = "nimrod".exe
|
|
||||||
result = "bin" / nimrod
|
|
||||||
if existsFile(result): return
|
|
||||||
for dir in split(getEnv("PATH"), PathSep):
|
|
||||||
if existsFile(dir / nim): return dir / nimrod
|
|
||||||
|
|
||||||
when defined(Posix):
|
when defined(Posix):
|
||||||
const buildScript = "build.sh"
|
const buildScript = "build.sh"
|
||||||
if existsFile(buildScript):
|
if existsFile(buildScript):
|
||||||
if tryExec("./" & buildScript): return "bin" / nim
|
if tryExec("./" & buildScript): return "bin" / nim
|
||||||
else:
|
else:
|
||||||
const buildScript = "build.bat"
|
const buildScript = "build.bat"
|
||||||
if existsFile(buildScript):
|
if existsFile(buildScript):
|
||||||
if tryExec(buildScript): return "bin" / nim
|
if tryExec(buildScript): return "bin" / nim
|
||||||
|
|
||||||
echo("Found no nim compiler and every attempt to build one failed!")
|
echo("Found no nim compiler and every attempt to build one failed!")
|
||||||
quit("FAILURE")
|
quit("FAILURE")
|
||||||
|
|
||||||
proc thVersion(i: int): string =
|
proc thVersion(i: int): string =
|
||||||
result = ("compiler" / "nim" & $i).exe
|
result = ("compiler" / "nim" & $i).exe
|
||||||
|
|
||||||
proc boot(args: string) =
|
proc boot(args: string) =
|
||||||
var output = "compiler" / "nim".exe
|
var output = "compiler" / "nim".exe
|
||||||
var finalDest = "bin" / "nim".exe
|
var finalDest = "bin" / "nim".exe
|
||||||
# default to use the 'c' command:
|
# default to use the 'c' command:
|
||||||
let bootOptions = if args.len == 0 or args.startsWith("-"): "c" else: ""
|
let bootOptions = if args.len == 0 or args.startsWith("-"): "c" else: ""
|
||||||
|
|
||||||
copyExe(findStartNim(), 0.thVersion)
|
copyExe(findStartNim(), 0.thVersion)
|
||||||
for i in 0..2:
|
for i in 0..2:
|
||||||
echo "iteration: ", i+1
|
echo "iteration: ", i+1
|
||||||
|
|
@ -204,7 +201,7 @@ const
|
||||||
".bzrignore", "nim", "nim.exe", "koch", "koch.exe", ".gitignore"
|
".bzrignore", "nim", "nim.exe", "koch", "koch.exe", ".gitignore"
|
||||||
]
|
]
|
||||||
|
|
||||||
proc cleanAux(dir: string) =
|
proc cleanAux(dir: string) =
|
||||||
for kind, path in walkDir(dir):
|
for kind, path in walkDir(dir):
|
||||||
case kind
|
case kind
|
||||||
of pcFile:
|
of pcFile:
|
||||||
|
|
@ -215,25 +212,25 @@ proc cleanAux(dir: string) =
|
||||||
removeFile(path)
|
removeFile(path)
|
||||||
of pcDir:
|
of pcDir:
|
||||||
case splitPath(path).tail
|
case splitPath(path).tail
|
||||||
of "nimcache":
|
of "nimcache":
|
||||||
echo "removing dir: ", path
|
echo "removing dir: ", path
|
||||||
removeDir(path)
|
removeDir(path)
|
||||||
of "dist", ".git", "icons": discard
|
of "dist", ".git", "icons": discard
|
||||||
else: cleanAux(path)
|
else: cleanAux(path)
|
||||||
else: discard
|
else: discard
|
||||||
|
|
||||||
proc removePattern(pattern: string) =
|
proc removePattern(pattern: string) =
|
||||||
for f in walkFiles(pattern):
|
for f in walkFiles(pattern):
|
||||||
echo "removing: ", f
|
echo "removing: ", f
|
||||||
removeFile(f)
|
removeFile(f)
|
||||||
|
|
||||||
proc clean(args: string) =
|
proc clean(args: string) =
|
||||||
if existsFile("koch.dat"): removeFile("koch.dat")
|
if existsFile("koch.dat"): removeFile("koch.dat")
|
||||||
removePattern("web/*.html")
|
removePattern("web/*.html")
|
||||||
removePattern("doc/*.html")
|
removePattern("doc/*.html")
|
||||||
cleanAux(getCurrentDir())
|
cleanAux(getCurrentDir())
|
||||||
for kind, path in walkDir(getCurrentDir() / "build"):
|
for kind, path in walkDir(getCurrentDir() / "build"):
|
||||||
if kind == pcDir:
|
if kind == pcDir:
|
||||||
echo "removing dir: ", path
|
echo "removing dir: ", path
|
||||||
removeDir(path)
|
removeDir(path)
|
||||||
|
|
||||||
|
|
@ -276,7 +273,7 @@ when defined(withUpdate):
|
||||||
"Local branch must be ahead of it. Exiting...")
|
"Local branch must be ahead of it. Exiting...")
|
||||||
else:
|
else:
|
||||||
quit("An error has occurred.")
|
quit("An error has occurred.")
|
||||||
|
|
||||||
else:
|
else:
|
||||||
echo("No repo or executable found!")
|
echo("No repo or executable found!")
|
||||||
when defined(haveZipLib):
|
when defined(haveZipLib):
|
||||||
|
|
@ -293,7 +290,7 @@ when defined(withUpdate):
|
||||||
quit("Error reading archive.")
|
quit("Error reading archive.")
|
||||||
else:
|
else:
|
||||||
quit("No failback available. Exiting...")
|
quit("No failback available. Exiting...")
|
||||||
|
|
||||||
echo("Starting update...")
|
echo("Starting update...")
|
||||||
boot(args)
|
boot(args)
|
||||||
echo("Update complete!")
|
echo("Update complete!")
|
||||||
|
|
@ -317,12 +314,14 @@ proc winRelease() =
|
||||||
#buildTool("tools/niminst/niminst", " -d:release")
|
#buildTool("tools/niminst/niminst", " -d:release")
|
||||||
buildTool("tools/nimgrep", " -d:release")
|
buildTool("tools/nimgrep", " -d:release")
|
||||||
buildTool("compiler/nimfix/nimfix", " -d:release")
|
buildTool("compiler/nimfix/nimfix", " -d:release")
|
||||||
|
buildTool("compiler/nimsuggest/nimsuggest", " -d:release")
|
||||||
|
|
||||||
|
#run7z("win32", "bin/nim.exe", "bin/c2nim.exe", "bin/nimgrep.exe",
|
||||||
|
# "bin/nimfix.exe",
|
||||||
|
# "bin/nimble.exe", "bin/*.dll",
|
||||||
|
# "config", "dist/*.dll", "examples", "lib",
|
||||||
|
# "readme.txt", "contributors.txt", "copying.txt")
|
||||||
|
|
||||||
run7z("win32", "bin/nim.exe", "bin/c2nim.exe", "bin/nimgrep.exe",
|
|
||||||
"bin/nimfix.exe",
|
|
||||||
"bin/nimble.exe", "bin/*.dll",
|
|
||||||
"config", "dist/*.dll", "examples", "lib",
|
|
||||||
"readme.txt", "contributors.txt", "copying.txt")
|
|
||||||
# second step: XXX build 64 bit version
|
# second step: XXX build 64 bit version
|
||||||
|
|
||||||
# -------------- tests --------------------------------------------------------
|
# -------------- tests --------------------------------------------------------
|
||||||
|
|
@ -346,8 +345,8 @@ proc temp(args: string) =
|
||||||
copyExe(output, finalDest)
|
copyExe(output, finalDest)
|
||||||
if args.len > 0: exec(finalDest & " " & args)
|
if args.len > 0: exec(finalDest & " " & args)
|
||||||
|
|
||||||
proc showHelp() =
|
proc showHelp() =
|
||||||
quit(HelpText % [VersionAsString & spaces(44-len(VersionAsString)),
|
quit(HelpText % [VersionAsString & spaces(44-len(VersionAsString)),
|
||||||
CompileDate, CompileTime], QuitSuccess)
|
CompileDate, CompileTime], QuitSuccess)
|
||||||
|
|
||||||
var op = initOptParser()
|
var op = initOptParser()
|
||||||
|
|
@ -366,6 +365,7 @@ of cmdArgument:
|
||||||
of "pdf": pdf()
|
of "pdf": pdf()
|
||||||
of "csource", "csources": csource(op.cmdLineRest)
|
of "csource", "csources": csource(op.cmdLineRest)
|
||||||
of "zip": zip(op.cmdLineRest)
|
of "zip": zip(op.cmdLineRest)
|
||||||
|
of "targz": targz(op.cmdLineRest)
|
||||||
of "nsis": nsis(op.cmdLineRest)
|
of "nsis": nsis(op.cmdLineRest)
|
||||||
of "install": install(op.cmdLineRest)
|
of "install": install(op.cmdLineRest)
|
||||||
of "test", "tests": tests(op.cmdLineRest)
|
of "test", "tests": tests(op.cmdLineRest)
|
||||||
|
|
|
||||||
|
|
@ -88,7 +88,9 @@ type
|
||||||
ntyBigNum,
|
ntyBigNum,
|
||||||
ntyConst, ntyMutable, ntyVarargs,
|
ntyConst, ntyMutable, ntyVarargs,
|
||||||
ntyIter,
|
ntyIter,
|
||||||
ntyError
|
ntyError,
|
||||||
|
ntyBuiltinTypeClass, ntyConcept, ntyConceptInst, ntyComposite,
|
||||||
|
ntyAnd, ntyOr, ntyNot
|
||||||
|
|
||||||
TNimTypeKinds* {.deprecated.} = set[NimTypeKind]
|
TNimTypeKinds* {.deprecated.} = set[NimTypeKind]
|
||||||
NimSymKind* = enum
|
NimSymKind* = enum
|
||||||
|
|
@ -162,6 +164,7 @@ proc kind*(n: NimNode): NimNodeKind {.magic: "NKind", noSideEffect.}
|
||||||
## returns the `kind` of the node `n`.
|
## returns the `kind` of the node `n`.
|
||||||
|
|
||||||
proc intVal*(n: NimNode): BiggestInt {.magic: "NIntVal", noSideEffect.}
|
proc intVal*(n: NimNode): BiggestInt {.magic: "NIntVal", noSideEffect.}
|
||||||
|
proc boolVal*(n: NimNode): bool {.compileTime, noSideEffect.} = n.intVal != 0
|
||||||
proc floatVal*(n: NimNode): BiggestFloat {.magic: "NFloatVal", noSideEffect.}
|
proc floatVal*(n: NimNode): BiggestFloat {.magic: "NFloatVal", noSideEffect.}
|
||||||
proc symbol*(n: NimNode): NimSym {.magic: "NSymbol", noSideEffect.}
|
proc symbol*(n: NimNode): NimSym {.magic: "NSymbol", noSideEffect.}
|
||||||
proc ident*(n: NimNode): NimIdent {.magic: "NIdent", noSideEffect.}
|
proc ident*(n: NimNode): NimIdent {.magic: "NIdent", noSideEffect.}
|
||||||
|
|
@ -355,6 +358,12 @@ proc expectLen*(n: NimNode, len: int) {.compileTime.} =
|
||||||
## macros that check its number of arguments.
|
## macros that check its number of arguments.
|
||||||
if n.len != len: error("macro expects a node with " & $len & " children")
|
if n.len != len: error("macro expects a node with " & $len & " children")
|
||||||
|
|
||||||
|
proc newTree*(kind: NimNodeKind,
|
||||||
|
children: varargs[NimNode]): NimNode {.compileTime.} =
|
||||||
|
## produces a new node with children.
|
||||||
|
result = newNimNode(kind)
|
||||||
|
result.add(children)
|
||||||
|
|
||||||
proc newCall*(theProc: NimNode,
|
proc newCall*(theProc: NimNode,
|
||||||
args: varargs[NimNode]): NimNode {.compileTime.} =
|
args: varargs[NimNode]): NimNode {.compileTime.} =
|
||||||
## produces a new call node. `theProc` is the proc that is called with
|
## produces a new call node. `theProc` is the proc that is called with
|
||||||
|
|
@ -389,6 +398,11 @@ proc newLit*(i: BiggestInt): NimNode {.compileTime.} =
|
||||||
result = newNimNode(nnkIntLit)
|
result = newNimNode(nnkIntLit)
|
||||||
result.intVal = i
|
result.intVal = i
|
||||||
|
|
||||||
|
proc newLit*(b: bool): NimNode {.compileTime.} =
|
||||||
|
## produces a new boolean literal node.
|
||||||
|
result = newNimNode(nnkIntLit)
|
||||||
|
result.intVal = ord(b)
|
||||||
|
|
||||||
proc newLit*(f: BiggestFloat): NimNode {.compileTime.} =
|
proc newLit*(f: BiggestFloat): NimNode {.compileTime.} =
|
||||||
## produces a new float literal node.
|
## produces a new float literal node.
|
||||||
result = newNimNode(nnkFloatLit)
|
result = newNimNode(nnkFloatLit)
|
||||||
|
|
|
||||||
|
|
@ -205,7 +205,7 @@ proc setEncoding*(connection: TDbConn, encoding: string): bool {.
|
||||||
exec(connection, sql"PRAGMA encoding = ?", [encoding])
|
exec(connection, sql"PRAGMA encoding = ?", [encoding])
|
||||||
result = connection.getValue(sql"PRAGMA encoding") == encoding
|
result = connection.getValue(sql"PRAGMA encoding") == encoding
|
||||||
|
|
||||||
when isMainModule:
|
when not defined(testing) and isMainModule:
|
||||||
var db = open("db.sql", "", "", "")
|
var db = open("db.sql", "", "", "")
|
||||||
exec(db, sql"create table tbl1(one varchar(10), two smallint)", [])
|
exec(db, sql"create table tbl1(one varchar(10), two smallint)", [])
|
||||||
exec(db, sql"insert into tbl1 values('hello!',10)", [])
|
exec(db, sql"insert into tbl1 values('hello!',10)", [])
|
||||||
|
|
|
||||||
|
|
@ -499,7 +499,7 @@ template withEvents*(surf: PSurface, event: expr, actions: stmt): stmt {.
|
||||||
if sdl.init(sdl.INIT_VIDEO) < 0: raiseEGraphics()
|
if sdl.init(sdl.INIT_VIDEO) < 0: raiseEGraphics()
|
||||||
if sdl_ttf.init() < 0: raiseEGraphics()
|
if sdl_ttf.init() < 0: raiseEGraphics()
|
||||||
|
|
||||||
when isMainModule:
|
when not defined(testing) and isMainModule:
|
||||||
var surf = newScreenSurface(800, 600)
|
var surf = newScreenSurface(800, 600)
|
||||||
|
|
||||||
surf.fillSurface(colWhite)
|
surf.fillSurface(colWhite)
|
||||||
|
|
|
||||||
|
|
@ -135,7 +135,7 @@ else:
|
||||||
var cur, old: Termios
|
var cur, old: Termios
|
||||||
discard fd.tcgetattr(cur.addr)
|
discard fd.tcgetattr(cur.addr)
|
||||||
old = cur
|
old = cur
|
||||||
cur.lflag = cur.lflag and not Tcflag(ECHO)
|
cur.c_lflag = cur.c_lflag and not Tcflag(ECHO)
|
||||||
discard fd.tcsetattr(TCSADRAIN, cur.addr)
|
discard fd.tcsetattr(TCSADRAIN, cur.addr)
|
||||||
stdout.write prompt
|
stdout.write prompt
|
||||||
result = stdin.readLine(password)
|
result = stdin.readLine(password)
|
||||||
|
|
|
||||||
|
|
@ -7,8 +7,11 @@
|
||||||
# distribution, for details about the copyright.
|
# distribution, for details about the copyright.
|
||||||
#
|
#
|
||||||
|
|
||||||
## Regular expression support for Nim. Consider using the pegs module
|
## Regular expression support for Nim. Consider using the pegs module instead.
|
||||||
## 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`.
|
||||||
##
|
##
|
||||||
## **Note:** The 're' proc defaults to the **extended regular expression
|
## **Note:** The 're' proc defaults to the **extended regular expression
|
||||||
## syntax** which lets you use whitespace freely to make your regexes readable.
|
## syntax** which lets you use whitespace freely to make your regexes readable.
|
||||||
|
|
@ -41,11 +44,11 @@ type
|
||||||
reExtended = 3, ## ignore whitespace and ``#`` comments
|
reExtended = 3, ## ignore whitespace and ``#`` comments
|
||||||
reStudy = 4 ## study the expression (may be omitted if the
|
reStudy = 4 ## study the expression (may be omitted if the
|
||||||
## expression will be used only once)
|
## expression will be used only once)
|
||||||
|
|
||||||
RegexDesc = object
|
RegexDesc = object
|
||||||
h: PPcre
|
h: ptr Pcre
|
||||||
e: ptr TExtra
|
e: ptr ExtraData
|
||||||
|
|
||||||
Regex* = ref RegexDesc ## a compiled regular expression
|
Regex* = ref RegexDesc ## a compiled regular expression
|
||||||
|
|
||||||
RegexError* = object of ValueError
|
RegexError* = object of ValueError
|
||||||
|
|
@ -60,7 +63,7 @@ proc raiseInvalidRegex(msg: string) {.noinline, noreturn.} =
|
||||||
e.msg = msg
|
e.msg = msg
|
||||||
raise e
|
raise e
|
||||||
|
|
||||||
proc rawCompile(pattern: string, flags: cint): PPcre =
|
proc rawCompile(pattern: string, flags: cint): ptr Pcre =
|
||||||
var
|
var
|
||||||
msg: cstring
|
msg: cstring
|
||||||
offset: cint
|
offset: cint
|
||||||
|
|
@ -84,7 +87,7 @@ proc re*(s: string, flags = {reExtended, reStudy}): Regex =
|
||||||
result.h = rawCompile(s, cast[cint](flags - {reStudy}))
|
result.h = rawCompile(s, cast[cint](flags - {reStudy}))
|
||||||
if reStudy in flags:
|
if reStudy in flags:
|
||||||
var msg: cstring
|
var msg: cstring
|
||||||
result.e = pcre.study(result.h, 0, msg)
|
result.e = pcre.study(result.h, 0, addr msg)
|
||||||
if not isNil(msg): raiseInvalidRegex($msg)
|
if not isNil(msg): raiseInvalidRegex($msg)
|
||||||
|
|
||||||
proc matchOrFind(s: string, pattern: Regex, matches: var openArray[string],
|
proc matchOrFind(s: string, pattern: Regex, matches: var openArray[string],
|
||||||
|
|
@ -143,8 +146,8 @@ proc findBounds*(s: string, pattern: Regex,
|
||||||
|
|
||||||
proc findBounds*(s: string, pattern: Regex,
|
proc findBounds*(s: string, pattern: Regex,
|
||||||
start = 0): tuple[first, last: int] =
|
start = 0): tuple[first, last: int] =
|
||||||
## returns the starting position of `pattern` in `s`. If it does not
|
## returns the starting position and end position of ``pattern`` in ``s``.
|
||||||
## match, ``(-1,0)`` is returned.
|
## If it does not match, ``(-1,0)`` is returned.
|
||||||
var
|
var
|
||||||
rtarray = initRtArray[cint](3)
|
rtarray = initRtArray[cint](3)
|
||||||
rawMatches = rtarray.getRawData
|
rawMatches = rtarray.getRawData
|
||||||
|
|
@ -413,22 +416,28 @@ proc escapeRe*(s: string): string =
|
||||||
result.add(toHex(ord(c), 2))
|
result.add(toHex(ord(c), 2))
|
||||||
|
|
||||||
const ## common regular expressions
|
const ## common regular expressions
|
||||||
reIdentifier* = r"\b[a-zA-Z_]+[a-zA-Z_0-9]*\b" ## describes an identifier
|
reIdentifier* {.deprecated.} = r"\b[a-zA-Z_]+[a-zA-Z_0-9]*\b"
|
||||||
reNatural* = r"\b\d+\b" ## describes a natural number
|
## describes an identifier
|
||||||
reInteger* = r"\b[-+]?\d+\b" ## describes an integer
|
reNatural* {.deprecated.} = r"\b\d+\b"
|
||||||
reHex* = r"\b0[xX][0-9a-fA-F]+\b" ## describes a hexadecimal number
|
## describes a natural number
|
||||||
reBinary* = r"\b0[bB][01]+\b" ## describes a binary number (example: 0b11101)
|
reInteger* {.deprecated.} = r"\b[-+]?\d+\b"
|
||||||
reOctal* = r"\b0[oO][0-7]+\b" ## describes an octal number (example: 0o777)
|
## describes an integer
|
||||||
reFloat* = r"\b[-+]?[0-9]*\.?[0-9]+([eE][-+]?[0-9]+)?\b"
|
reHex* {.deprecated.} = r"\b0[xX][0-9a-fA-F]+\b"
|
||||||
|
## describes a hexadecimal number
|
||||||
|
reBinary* {.deprecated.} = r"\b0[bB][01]+\b"
|
||||||
|
## describes a binary number (example: 0b11101)
|
||||||
|
reOctal* {.deprecated.} = r"\b0[oO][0-7]+\b"
|
||||||
|
## describes an octal number (example: 0o777)
|
||||||
|
reFloat* {.deprecated.} = r"\b[-+]?[0-9]*\.?[0-9]+([eE][-+]?[0-9]+)?\b"
|
||||||
## describes a floating point number
|
## describes a floating point number
|
||||||
reEmail* = r"\b[a-zA-Z0-9!#$%&'*+/=?^_`{|}~\-]+(?:\. &" &
|
reEmail* {.deprecated.} = r"\b[a-zA-Z0-9!#$%&'*+/=?^_`{|}~\-]+(?:\. &" &
|
||||||
r"[a-zA-Z0-9!#$%&'*+/=?^_`{|}~-]+)" &
|
r"[a-zA-Z0-9!#$%&'*+/=?^_`{|}~-]+)*@" &
|
||||||
r"*@(?:[a-zA-Z0-9](?:[a-zA-Z0-9-]*[a-zA-Z0-9])?\.)+" &
|
r"(?:[a-zA-Z0-9](?:[a-zA-Z0-9-]*[a-zA-Z0-9])?\.)+" &
|
||||||
r"(?:[a-zA-Z]{2}|com|org|" &
|
r"(?:[a-zA-Z]{2}|com|org|net|gov|mil|biz|" &
|
||||||
r"net|gov|mil|biz|info|mobi|name|aero|jobs|museum)\b"
|
r"info|mobi|name|aero|jobs|museum)\b"
|
||||||
## describes a common email address
|
## describes a common email address
|
||||||
reURL* = r"\b(http(s)?|ftp|gopher|telnet|file|notes|ms\-help):" &
|
reURL* {.deprecated.} = r"\b(http(s)?|ftp|gopher|telnet|file|notes|ms-help)" &
|
||||||
r"((//)|(\\\\))+[\w\d:#@%/;$()~_?\+\-\=\\\.\&]*\b"
|
r":((//)|(\\\\))+[\w\d:#@%/;$()~_?\+\-\=\\\.\&]*\b"
|
||||||
## describes an URL
|
## describes an URL
|
||||||
|
|
||||||
when isMainModule:
|
when isMainModule:
|
||||||
|
|
|
||||||
|
|
@ -82,7 +82,7 @@ proc close*(sock: TSecureSocket) =
|
||||||
ERR_print_errors_fp(stderr)
|
ERR_print_errors_fp(stderr)
|
||||||
raiseOSError(osLastError())
|
raiseOSError(osLastError())
|
||||||
|
|
||||||
when isMainModule:
|
when not defined(testing) and isMainModule:
|
||||||
var s: TSecureSocket
|
var s: TSecureSocket
|
||||||
echo connect(s, "smtp.gmail.com", 465)
|
echo connect(s, "smtp.gmail.com", 465)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -9,8 +9,8 @@
|
||||||
|
|
||||||
## This module implements a zip archive creator/reader/modifier.
|
## This module implements a zip archive creator/reader/modifier.
|
||||||
|
|
||||||
import
|
import
|
||||||
streams, libzip, times, os
|
streams, libzip, times, os, strutils
|
||||||
|
|
||||||
type
|
type
|
||||||
TZipArchive* = object of RootObj ## represents a zip archive
|
TZipArchive* = object of RootObj ## represents a zip archive
|
||||||
|
|
@ -18,14 +18,14 @@ type
|
||||||
w: PZip
|
w: PZip
|
||||||
|
|
||||||
|
|
||||||
proc zipError(z: var TZipArchive) =
|
proc zipError(z: var TZipArchive) =
|
||||||
var e: ref IOError
|
var e: ref IOError
|
||||||
new(e)
|
new(e)
|
||||||
e.msg = $zip_strerror(z.w)
|
e.msg = $zip_strerror(z.w)
|
||||||
raise e
|
raise e
|
||||||
|
|
||||||
proc open*(z: var TZipArchive, filename: string, mode: FileMode = fmRead): bool =
|
proc open*(z: var TZipArchive, filename: string, mode: FileMode = fmRead): bool =
|
||||||
## Opens a zip file for reading, writing or appending. All file modes are
|
## Opens a zip file for reading, writing or appending. All file modes are
|
||||||
## supported. Returns true iff successful, false otherwise.
|
## supported. Returns true iff successful, false otherwise.
|
||||||
var err, flags: int32
|
var err, flags: int32
|
||||||
case mode
|
case mode
|
||||||
|
|
@ -41,21 +41,21 @@ proc open*(z: var TZipArchive, filename: string, mode: FileMode = fmRead): bool
|
||||||
proc close*(z: var TZipArchive) =
|
proc close*(z: var TZipArchive) =
|
||||||
## Closes a zip file.
|
## Closes a zip file.
|
||||||
zip_close(z.w)
|
zip_close(z.w)
|
||||||
|
|
||||||
proc createDir*(z: var TZipArchive, dir: string) =
|
proc createDir*(z: var TZipArchive, dir: string) =
|
||||||
## Creates a directory within the `z` archive. This does not fail if the
|
## Creates a directory within the `z` archive. This does not fail if the
|
||||||
## directory already exists. Note that for adding a file like
|
## directory already exists. Note that for adding a file like
|
||||||
## ``"path1/path2/filename"`` it is not necessary
|
## ``"path1/path2/filename"`` it is not necessary
|
||||||
## to create the ``"path/path2"`` subdirectories - it will be done
|
## to create the ``"path/path2"`` subdirectories - it will be done
|
||||||
## automatically by ``addFile``.
|
## automatically by ``addFile``.
|
||||||
assert(z.mode != fmRead)
|
assert(z.mode != fmRead)
|
||||||
discard zip_add_dir(z.w, dir)
|
discard zip_add_dir(z.w, dir)
|
||||||
zip_error_clear(z.w)
|
zip_error_clear(z.w)
|
||||||
|
|
||||||
proc addFile*(z: var TZipArchive, dest, src: string) =
|
proc addFile*(z: var TZipArchive, dest, src: string) =
|
||||||
## Adds the file `src` to the archive `z` with the name `dest`. `dest`
|
## Adds the file `src` to the archive `z` with the name `dest`. `dest`
|
||||||
## may contain a path that will be created.
|
## may contain a path that will be created.
|
||||||
assert(z.mode != fmRead)
|
assert(z.mode != fmRead)
|
||||||
if not fileExists(src):
|
if not fileExists(src):
|
||||||
raise newException(IOError, "File '" & src & "' does not exist")
|
raise newException(IOError, "File '" & src & "' does not exist")
|
||||||
var zipsrc = zip_source_file(z.w, src, 0, -1)
|
var zipsrc = zip_source_file(z.w, src, 0, -1)
|
||||||
|
|
@ -67,21 +67,21 @@ proc addFile*(z: var TZipArchive, dest, src: string) =
|
||||||
zip_source_free(zipsrc)
|
zip_source_free(zipsrc)
|
||||||
zipError(z)
|
zipError(z)
|
||||||
|
|
||||||
proc addFile*(z: var TZipArchive, file: string) =
|
proc addFile*(z: var TZipArchive, file: string) =
|
||||||
## A shortcut for ``addFile(z, file, file)``, i.e. the name of the source is
|
## A shortcut for ``addFile(z, file, file)``, i.e. the name of the source is
|
||||||
## the name of the destination.
|
## the name of the destination.
|
||||||
addFile(z, file, file)
|
addFile(z, file, file)
|
||||||
|
|
||||||
proc mySourceCallback(state, data: pointer, len: int,
|
proc mySourceCallback(state, data: pointer, len: int,
|
||||||
cmd: TZipSourceCmd): int {.cdecl.} =
|
cmd: TZipSourceCmd): int {.cdecl.} =
|
||||||
var src = cast[Stream](state)
|
var src = cast[Stream](state)
|
||||||
case cmd
|
case cmd
|
||||||
of ZIP_SOURCE_OPEN:
|
of ZIP_SOURCE_OPEN:
|
||||||
if src.setPositionImpl != nil: setPosition(src, 0) # reset
|
if src.setPositionImpl != nil: setPosition(src, 0) # reset
|
||||||
of ZIP_SOURCE_READ:
|
of ZIP_SOURCE_READ:
|
||||||
result = readData(src, data, len)
|
result = readData(src, data, len)
|
||||||
of ZIP_SOURCE_CLOSE: close(src)
|
of ZIP_SOURCE_CLOSE: close(src)
|
||||||
of ZIP_SOURCE_STAT:
|
of ZIP_SOURCE_STAT:
|
||||||
var stat = cast[PZipStat](data)
|
var stat = cast[PZipStat](data)
|
||||||
zip_stat_init(stat)
|
zip_stat_init(stat)
|
||||||
stat.size = high(int32)-1 # we don't know the size
|
stat.size = high(int32)-1 # we don't know the size
|
||||||
|
|
@ -94,8 +94,8 @@ proc mySourceCallback(state, data: pointer, len: int,
|
||||||
result = 2*sizeof(cint)
|
result = 2*sizeof(cint)
|
||||||
of constZIP_SOURCE_FREE: GC_unref(src)
|
of constZIP_SOURCE_FREE: GC_unref(src)
|
||||||
else: assert(false)
|
else: assert(false)
|
||||||
|
|
||||||
proc addFile*(z: var TZipArchive, dest: string, src: Stream) =
|
proc addFile*(z: var TZipArchive, dest: string, src: Stream) =
|
||||||
## Adds a file named with `dest` to the archive `z`. `dest`
|
## Adds a file named with `dest` to the archive `z`. `dest`
|
||||||
## may contain a path. The file's content is read from the `src` stream.
|
## may contain a path. The file's content is read from the `src` stream.
|
||||||
assert(z.mode != fmRead)
|
assert(z.mode != fmRead)
|
||||||
|
|
@ -105,39 +105,45 @@ proc addFile*(z: var TZipArchive, dest: string, src: Stream) =
|
||||||
if zip_add(z.w, dest, zipsrc) < 0'i32:
|
if zip_add(z.w, dest, zipsrc) < 0'i32:
|
||||||
zip_source_free(zipsrc)
|
zip_source_free(zipsrc)
|
||||||
zipError(z)
|
zipError(z)
|
||||||
|
|
||||||
# -------------- zip file stream ---------------------------------------------
|
# -------------- zip file stream ---------------------------------------------
|
||||||
|
|
||||||
type
|
type
|
||||||
TZipFileStream = object of StreamObj
|
TZipFileStream = object of StreamObj
|
||||||
f: PZipFile
|
f: PZipFile
|
||||||
|
atEnd: bool
|
||||||
|
|
||||||
PZipFileStream* =
|
PZipFileStream* =
|
||||||
ref TZipFileStream ## a reader stream of a file within a zip archive
|
ref TZipFileStream ## a reader stream of a file within a zip archive
|
||||||
|
|
||||||
proc fsClose(s: Stream) = zip_fclose(PZipFileStream(s).f)
|
proc fsClose(s: Stream) = zip_fclose(PZipFileStream(s).f)
|
||||||
proc fsReadData(s: Stream, buffer: pointer, bufLen: int): int =
|
proc fsAtEnd(s: Stream): bool = PZipFileStream(s).atEnd
|
||||||
|
proc fsReadData(s: Stream, buffer: pointer, bufLen: int): int =
|
||||||
result = zip_fread(PZipFileStream(s).f, buffer, bufLen)
|
result = zip_fread(PZipFileStream(s).f, buffer, bufLen)
|
||||||
|
if result == 0:
|
||||||
|
PZipFileStream(s).atEnd = true
|
||||||
|
|
||||||
proc newZipFileStream(f: PZipFile): PZipFileStream =
|
proc newZipFileStream(f: PZipFile): PZipFileStream =
|
||||||
new(result)
|
new(result)
|
||||||
result.f = f
|
result.f = f
|
||||||
|
result.atEnd = false
|
||||||
result.closeImpl = fsClose
|
result.closeImpl = fsClose
|
||||||
result.readDataImpl = fsReadData
|
result.readDataImpl = fsReadData
|
||||||
|
result.atEndImpl = fsAtEnd
|
||||||
# other methods are nil!
|
# other methods are nil!
|
||||||
|
|
||||||
# ----------------------------------------------------------------------------
|
# ----------------------------------------------------------------------------
|
||||||
|
|
||||||
proc getStream*(z: var TZipArchive, filename: string): PZipFileStream =
|
proc getStream*(z: var TZipArchive, filename: string): PZipFileStream =
|
||||||
## returns a stream that can be used to read the file named `filename`
|
## returns a stream that can be used to read the file named `filename`
|
||||||
## from the archive `z`. Returns nil in case of an error.
|
## from the archive `z`. Returns nil in case of an error.
|
||||||
## The returned stream does not support the `setPosition`, `getPosition`,
|
## The returned stream does not support the `setPosition`, `getPosition`,
|
||||||
## `writeData` or `atEnd` methods.
|
## `writeData` or `atEnd` methods.
|
||||||
var x = zip_fopen(z.w, filename, 0'i32)
|
var x = zip_fopen(z.w, filename, 0'i32)
|
||||||
if x != nil: result = newZipFileStream(x)
|
if x != nil: result = newZipFileStream(x)
|
||||||
|
|
||||||
iterator walkFiles*(z: var TZipArchive): string =
|
iterator walkFiles*(z: var TZipArchive): string =
|
||||||
## walks over all files in the archive `z` and returns the filename
|
## walks over all files in the archive `z` and returns the filename
|
||||||
## (including the path).
|
## (including the path).
|
||||||
var i = 0'i32
|
var i = 0'i32
|
||||||
var num = zip_get_num_files(z.w)
|
var num = zip_get_num_files(z.w)
|
||||||
|
|
@ -158,12 +164,20 @@ proc extractFile*(z: var TZipArchive, srcFile: string, dest: Stream) =
|
||||||
|
|
||||||
proc extractFile*(z: var TZipArchive, srcFile: string, dest: string) =
|
proc extractFile*(z: var TZipArchive, srcFile: string, dest: string) =
|
||||||
## extracts a file from the zip archive `z` to the destination filename.
|
## extracts a file from the zip archive `z` to the destination filename.
|
||||||
var file = newFileStream(dest, fmReadWrite)
|
var file = newFileStream(dest, fmWrite)
|
||||||
extractFile(z, srcFile, file)
|
extractFile(z, srcFile, file)
|
||||||
file.close()
|
file.close()
|
||||||
|
|
||||||
proc extractAll*(z: var TZipArchive, dest: string) =
|
proc extractAll*(z: var TZipArchive, dest: string) =
|
||||||
## extracts all files from archive `z` to the destination directory.
|
## extracts all files from archive `z` to the destination directory.
|
||||||
for file in walkFiles(z):
|
for file in walkFiles(z):
|
||||||
extractFile(z, file, dest / extractFilename(file))
|
if file.endsWith("/"):
|
||||||
|
createDir(dest / file)
|
||||||
|
else:
|
||||||
|
extractFile(z, file, dest / file)
|
||||||
|
|
||||||
|
when not defined(testing) and isMainModule:
|
||||||
|
var zip: TZipArchive
|
||||||
|
if not zip.open("nim-0.11.0.zip"):
|
||||||
|
raise newException(IOError, "opening zip failed")
|
||||||
|
zip.extractAll("test")
|
||||||
|
|
|
||||||
|
|
@ -152,10 +152,12 @@ type
|
||||||
DocumentObj {.importc.} = object of NodeObj
|
DocumentObj {.importc.} = object of NodeObj
|
||||||
alinkColor*: cstring
|
alinkColor*: cstring
|
||||||
bgColor*: cstring
|
bgColor*: cstring
|
||||||
|
body*: Element
|
||||||
charset*: cstring
|
charset*: cstring
|
||||||
cookie*: cstring
|
cookie*: cstring
|
||||||
defaultCharset*: cstring
|
defaultCharset*: cstring
|
||||||
fgColor*: cstring
|
fgColor*: cstring
|
||||||
|
head*: Element
|
||||||
lastModified*: cstring
|
lastModified*: cstring
|
||||||
linkColor*: cstring
|
linkColor*: cstring
|
||||||
referrer*: cstring
|
referrer*: cstring
|
||||||
|
|
|
||||||
|
|
@ -343,15 +343,15 @@ struct TFrame {
|
||||||
};
|
};
|
||||||
|
|
||||||
#define nimfr(proc, file) \
|
#define nimfr(proc, file) \
|
||||||
TFrame F; \
|
TFrame FR; \
|
||||||
F.procname = proc; F.filename = file; F.line = 0; F.len = 0; nimFrame(&F);
|
FR.procname = proc; FR.filename = file; FR.line = 0; FR.len = 0; nimFrame(&FR);
|
||||||
|
|
||||||
#define nimfrs(proc, file, slots, length) \
|
#define nimfrs(proc, file, slots, length) \
|
||||||
struct {TFrame* prev;NCSTRING procname;NI line;NCSTRING filename; NI len; TVarSlot s[slots];} F; \
|
struct {TFrame* prev;NCSTRING procname;NI line;NCSTRING filename; NI len; TVarSlot s[slots];} FR; \
|
||||||
F.procname = proc; F.filename = file; F.line = 0; F.len = length; nimFrame((TFrame*)&F);
|
FR.procname = proc; FR.filename = file; FR.line = 0; FR.len = length; nimFrame((TFrame*)&FR);
|
||||||
|
|
||||||
#define nimln(n, file) \
|
#define nimln(n, file) \
|
||||||
F.line = n; F.filename = file;
|
FR.line = n; FR.filename = file;
|
||||||
|
|
||||||
#define NIM_POSIX_INIT __attribute__((constructor))
|
#define NIM_POSIX_INIT __attribute__((constructor))
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -564,7 +564,7 @@ proc fixupEmbeddedRef(n, a, b: PRstNode) =
|
||||||
|
|
||||||
proc parsePostfix(p: var TRstParser, n: PRstNode): PRstNode =
|
proc parsePostfix(p: var TRstParser, n: PRstNode): PRstNode =
|
||||||
result = n
|
result = n
|
||||||
if isInlineMarkupEnd(p, "_"):
|
if isInlineMarkupEnd(p, "_") or isInlineMarkupEnd(p, "__"):
|
||||||
inc(p.idx)
|
inc(p.idx)
|
||||||
if p.tok[p.idx-2].symbol == "`" and p.tok[p.idx-3].symbol == ">":
|
if p.tok[p.idx-2].symbol == "`" and p.tok[p.idx-3].symbol == ">":
|
||||||
var a = newRstNode(rnInner)
|
var a = newRstNode(rnInner)
|
||||||
|
|
|
||||||
|
|
@ -34,14 +34,14 @@ type
|
||||||
TOutputTarget* = enum ## which document type to generate
|
TOutputTarget* = enum ## which document type to generate
|
||||||
outHtml, # output is HTML
|
outHtml, # output is HTML
|
||||||
outLatex # output is Latex
|
outLatex # output is Latex
|
||||||
|
|
||||||
TTocEntry = object
|
TTocEntry = object
|
||||||
n*: PRstNode
|
n*: PRstNode
|
||||||
refname*, header*: string
|
refname*, header*: string
|
||||||
|
|
||||||
TMetaEnum* = enum
|
TMetaEnum* = enum
|
||||||
metaNone, metaTitle, metaSubtitle, metaAuthor, metaVersion
|
metaNone, metaTitle, metaSubtitle, metaAuthor, metaVersion
|
||||||
|
|
||||||
TRstGenerator* = object of RootObj
|
TRstGenerator* = object of RootObj
|
||||||
target*: TOutputTarget
|
target*: TOutputTarget
|
||||||
config*: StringTableRef
|
config*: StringTableRef
|
||||||
|
|
@ -60,7 +60,7 @@ type
|
||||||
seenIndexTerms: Table[string, int] ## \
|
seenIndexTerms: Table[string, int] ## \
|
||||||
## Keeps count of same text index terms to generate different identifiers
|
## Keeps count of same text index terms to generate different identifiers
|
||||||
## for hyperlinks. See renderIndexTerm proc for details.
|
## for hyperlinks. See renderIndexTerm proc for details.
|
||||||
|
|
||||||
PDoc = var TRstGenerator ## Alias to type less.
|
PDoc = var TRstGenerator ## Alias to type less.
|
||||||
|
|
||||||
CodeBlockParams = object ## Stores code block params.
|
CodeBlockParams = object ## Stores code block params.
|
||||||
|
|
@ -136,7 +136,7 @@ proc initRstGenerator*(g: var TRstGenerator, target: TOutputTarget,
|
||||||
g.currentSection = "Module " & fileParts.name
|
g.currentSection = "Module " & fileParts.name
|
||||||
g.seenIndexTerms = initTable[string, int]()
|
g.seenIndexTerms = initTable[string, int]()
|
||||||
g.msgHandler = msgHandler
|
g.msgHandler = msgHandler
|
||||||
|
|
||||||
let s = config["split.item.toc"]
|
let s = config["split.item.toc"]
|
||||||
if s != "": g.splitAfter = parseInt(s)
|
if s != "": g.splitAfter = parseInt(s)
|
||||||
for i in low(g.meta)..high(g.meta): g.meta[i] = ""
|
for i in low(g.meta)..high(g.meta): g.meta[i] = ""
|
||||||
|
|
@ -147,23 +147,23 @@ proc writeIndexFile*(g: var TRstGenerator, outfile: string) =
|
||||||
## You previously need to add entries to the index with the `setIndexTerm()
|
## You previously need to add entries to the index with the `setIndexTerm()
|
||||||
## <#setIndexTerm>`_ proc. If the index is empty the file won't be created.
|
## <#setIndexTerm>`_ proc. If the index is empty the file won't be created.
|
||||||
if g.theIndex.len > 0: writeFile(outfile, g.theIndex)
|
if g.theIndex.len > 0: writeFile(outfile, g.theIndex)
|
||||||
|
|
||||||
proc addXmlChar(dest: var string, c: char) =
|
proc addXmlChar(dest: var string, c: char) =
|
||||||
case c
|
case c
|
||||||
of '&': add(dest, "&")
|
of '&': add(dest, "&")
|
||||||
of '<': add(dest, "<")
|
of '<': add(dest, "<")
|
||||||
of '>': add(dest, ">")
|
of '>': add(dest, ">")
|
||||||
of '\"': add(dest, """)
|
of '\"': add(dest, """)
|
||||||
else: add(dest, c)
|
else: add(dest, c)
|
||||||
|
|
||||||
proc addRtfChar(dest: var string, c: char) =
|
proc addRtfChar(dest: var string, c: char) =
|
||||||
case c
|
case c
|
||||||
of '{': add(dest, "\\{")
|
of '{': add(dest, "\\{")
|
||||||
of '}': add(dest, "\\}")
|
of '}': add(dest, "\\}")
|
||||||
of '\\': add(dest, "\\\\")
|
of '\\': add(dest, "\\\\")
|
||||||
else: add(dest, c)
|
else: add(dest, c)
|
||||||
|
|
||||||
proc addTexChar(dest: var string, c: char) =
|
proc addTexChar(dest: var string, c: char) =
|
||||||
case c
|
case c
|
||||||
of '_': add(dest, "\\_")
|
of '_': add(dest, "\\_")
|
||||||
of '{': add(dest, "\\symbol{123}")
|
of '{': add(dest, "\\symbol{123}")
|
||||||
|
|
@ -183,54 +183,54 @@ proc addTexChar(dest: var string, c: char) =
|
||||||
|
|
||||||
var splitter*: string = "<wbr />"
|
var splitter*: string = "<wbr />"
|
||||||
|
|
||||||
proc escChar*(target: TOutputTarget, dest: var string, c: char) {.inline.} =
|
proc escChar*(target: TOutputTarget, dest: var string, c: char) {.inline.} =
|
||||||
case target
|
case target
|
||||||
of outHtml: addXmlChar(dest, c)
|
of outHtml: addXmlChar(dest, c)
|
||||||
of outLatex: addTexChar(dest, c)
|
of outLatex: addTexChar(dest, c)
|
||||||
|
|
||||||
proc nextSplitPoint*(s: string, start: int): int =
|
proc nextSplitPoint*(s: string, start: int): int =
|
||||||
result = start
|
result = start
|
||||||
while result < len(s) + 0:
|
while result < len(s) + 0:
|
||||||
case s[result]
|
case s[result]
|
||||||
of '_': return
|
of '_': return
|
||||||
of 'a'..'z':
|
of 'a'..'z':
|
||||||
if result + 1 < len(s) + 0:
|
if result + 1 < len(s) + 0:
|
||||||
if s[result + 1] in {'A'..'Z'}: return
|
if s[result + 1] in {'A'..'Z'}: return
|
||||||
else: discard
|
else: discard
|
||||||
inc(result)
|
inc(result)
|
||||||
dec(result) # last valid index
|
dec(result) # last valid index
|
||||||
|
|
||||||
proc esc*(target: TOutputTarget, s: string, splitAfter = -1): string =
|
proc esc*(target: TOutputTarget, s: string, splitAfter = -1): string =
|
||||||
result = ""
|
result = ""
|
||||||
if splitAfter >= 0:
|
if splitAfter >= 0:
|
||||||
var partLen = 0
|
var partLen = 0
|
||||||
var j = 0
|
var j = 0
|
||||||
while j < len(s):
|
while j < len(s):
|
||||||
var k = nextSplitPoint(s, j)
|
var k = nextSplitPoint(s, j)
|
||||||
if (splitter != " ") or (partLen + k - j + 1 > splitAfter):
|
if (splitter != " ") or (partLen + k - j + 1 > splitAfter):
|
||||||
partLen = 0
|
partLen = 0
|
||||||
add(result, splitter)
|
add(result, splitter)
|
||||||
for i in countup(j, k): escChar(target, result, s[i])
|
for i in countup(j, k): escChar(target, result, s[i])
|
||||||
inc(partLen, k - j + 1)
|
inc(partLen, k - j + 1)
|
||||||
j = k + 1
|
j = k + 1
|
||||||
else:
|
else:
|
||||||
for i in countup(0, len(s) - 1): escChar(target, result, s[i])
|
for i in countup(0, len(s) - 1): escChar(target, result, s[i])
|
||||||
|
|
||||||
|
|
||||||
proc disp(target: TOutputTarget, xml, tex: string): string =
|
proc disp(target: TOutputTarget, xml, tex: string): string =
|
||||||
if target != outLatex: result = xml
|
if target != outLatex: result = xml
|
||||||
else: result = tex
|
else: result = tex
|
||||||
|
|
||||||
proc dispF(target: TOutputTarget, xml, tex: string,
|
proc dispF(target: TOutputTarget, xml, tex: string,
|
||||||
args: varargs[string]): string =
|
args: varargs[string]): string =
|
||||||
if target != outLatex: result = xml % args
|
if target != outLatex: result = xml % args
|
||||||
else: result = tex % args
|
else: result = tex % args
|
||||||
|
|
||||||
proc dispA(target: TOutputTarget, dest: var string,
|
proc dispA(target: TOutputTarget, dest: var string,
|
||||||
xml, tex: string, args: varargs[string]) =
|
xml, tex: string, args: varargs[string]) =
|
||||||
if target != outLatex: addf(dest, xml, args)
|
if target != outLatex: addf(dest, xml, args)
|
||||||
else: addf(dest, tex, args)
|
else: addf(dest, tex, args)
|
||||||
|
|
||||||
proc `or`(x, y: string): string {.inline.} =
|
proc `or`(x, y: string): string {.inline.} =
|
||||||
result = if x.isNil: y else: x
|
result = if x.isNil: y else: x
|
||||||
|
|
||||||
|
|
@ -248,7 +248,7 @@ proc renderRstToOut*(d: var TRstGenerator, n: PRstNode, result: var string)
|
||||||
## renderRstToOut(gen, rst, generatedHTML)
|
## renderRstToOut(gen, rst, generatedHTML)
|
||||||
## echo generatedHTML
|
## echo generatedHTML
|
||||||
|
|
||||||
proc renderAux(d: PDoc, n: PRstNode, result: var string) =
|
proc renderAux(d: PDoc, n: PRstNode, result: var string) =
|
||||||
for i in countup(0, len(n)-1): renderRstToOut(d, n.sons[i], result)
|
for i in countup(0, len(n)-1): renderRstToOut(d, n.sons[i], result)
|
||||||
|
|
||||||
proc renderAux(d: PDoc, n: PRstNode, frmtA, frmtB: string, result: var string) =
|
proc renderAux(d: PDoc, n: PRstNode, frmtA, frmtB: string, result: var string) =
|
||||||
|
|
@ -347,7 +347,7 @@ proc renderIndexTerm*(d: PDoc, n: PRstNode, result: var string) =
|
||||||
var term = ""
|
var term = ""
|
||||||
renderAux(d, n, term)
|
renderAux(d, n, term)
|
||||||
setIndexTerm(d, id, term, d.currentSection)
|
setIndexTerm(d, id, term, d.currentSection)
|
||||||
dispA(d.target, result, "<span id=\"$1\">$2</span>", "$2\\label{$1}",
|
dispA(d.target, result, "<span id=\"$1\">$2</span>", "$2\\label{$1}",
|
||||||
[id, term])
|
[id, term])
|
||||||
|
|
||||||
type
|
type
|
||||||
|
|
@ -656,7 +656,7 @@ proc mergeIndexes*(dir: string): string =
|
||||||
result.add("<h2>API symbols</h2>\n")
|
result.add("<h2>API symbols</h2>\n")
|
||||||
result.add(generateSymbolIndex(symbols))
|
result.add(generateSymbolIndex(symbols))
|
||||||
|
|
||||||
|
|
||||||
# ----------------------------------------------------------------------------
|
# ----------------------------------------------------------------------------
|
||||||
|
|
||||||
proc stripTOCHTML(s: string): string =
|
proc stripTOCHTML(s: string): string =
|
||||||
|
|
@ -677,7 +677,7 @@ proc stripTOCHTML(s: string): string =
|
||||||
result.delete(first, last)
|
result.delete(first, last)
|
||||||
first = result.find('<', first)
|
first = result.find('<', first)
|
||||||
|
|
||||||
proc renderHeadline(d: PDoc, n: PRstNode, result: var string) =
|
proc renderHeadline(d: PDoc, n: PRstNode, result: var string) =
|
||||||
var tmp = ""
|
var tmp = ""
|
||||||
for i in countup(0, len(n) - 1): renderRstToOut(d, n.sons[i], tmp)
|
for i in countup(0, len(n) - 1): renderRstToOut(d, n.sons[i], tmp)
|
||||||
d.currentSection = tmp
|
d.currentSection = tmp
|
||||||
|
|
@ -700,9 +700,9 @@ proc renderHeadline(d: PDoc, n: PRstNode, result: var string) =
|
||||||
"id=\"$2\" href=\"#$2\">$3</a></h$1>", "\\rsth$4{$3}\\label{$2}\n",
|
"id=\"$2\" href=\"#$2\">$3</a></h$1>", "\\rsth$4{$3}\\label{$2}\n",
|
||||||
[$n.level, d.tocPart[length].refname, tmp, $chr(n.level - 1 + ord('A'))])
|
[$n.level, d.tocPart[length].refname, tmp, $chr(n.level - 1 + ord('A'))])
|
||||||
else:
|
else:
|
||||||
dispA(d.target, result, "\n<h$1 id=\"$2\">$3</h$1>",
|
dispA(d.target, result, "\n<h$1 id=\"$2\">$3</h$1>",
|
||||||
"\\rsth$4{$3}\\label{$2}\n", [
|
"\\rsth$4{$3}\\label{$2}\n", [
|
||||||
$n.level, refname, tmp,
|
$n.level, refname, tmp,
|
||||||
$chr(n.level - 1 + ord('A'))])
|
$chr(n.level - 1 + ord('A'))])
|
||||||
|
|
||||||
# Generate index entry using spaces to indicate TOC level for the output HTML.
|
# Generate index entry using spaces to indicate TOC level for the output HTML.
|
||||||
|
|
@ -710,7 +710,7 @@ proc renderHeadline(d: PDoc, n: PRstNode, result: var string) =
|
||||||
setIndexTerm(d, refname, tmp.stripTOCHTML,
|
setIndexTerm(d, refname, tmp.stripTOCHTML,
|
||||||
spaces(max(0, n.level)) & tmp)
|
spaces(max(0, n.level)) & tmp)
|
||||||
|
|
||||||
proc renderOverline(d: PDoc, n: PRstNode, result: var string) =
|
proc renderOverline(d: PDoc, n: PRstNode, result: var string) =
|
||||||
if d.meta[metaTitle].len == 0:
|
if d.meta[metaTitle].len == 0:
|
||||||
for i in countup(0, len(n)-1):
|
for i in countup(0, len(n)-1):
|
||||||
renderRstToOut(d, n.sons[i], d.meta[metaTitle])
|
renderRstToOut(d, n.sons[i], d.meta[metaTitle])
|
||||||
|
|
@ -723,14 +723,14 @@ proc renderOverline(d: PDoc, n: PRstNode, result: var string) =
|
||||||
var tmp = ""
|
var tmp = ""
|
||||||
for i in countup(0, len(n) - 1): renderRstToOut(d, n.sons[i], tmp)
|
for i in countup(0, len(n) - 1): renderRstToOut(d, n.sons[i], tmp)
|
||||||
d.currentSection = tmp
|
d.currentSection = tmp
|
||||||
dispA(d.target, result, "<h$1 id=\"$2\"><center>$3</center></h$1>",
|
dispA(d.target, result, "<h$1 id=\"$2\"><center>$3</center></h$1>",
|
||||||
"\\rstov$4{$3}\\label{$2}\n", [$n.level,
|
"\\rstov$4{$3}\\label{$2}\n", [$n.level,
|
||||||
rstnodeToRefname(n), tmp, $chr(n.level - 1 + ord('A'))])
|
rstnodeToRefname(n), tmp, $chr(n.level - 1 + ord('A'))])
|
||||||
|
|
||||||
|
|
||||||
proc renderTocEntry(d: PDoc, e: TTocEntry, result: var string) =
|
|
||||||
|
proc renderTocEntry(d: PDoc, e: TTocEntry, result: var string) =
|
||||||
dispA(d.target, result,
|
dispA(d.target, result,
|
||||||
"<li><a class=\"reference\" id=\"$1_toc\" href=\"#$1\">$2</a></li>\n",
|
"<li><a class=\"reference\" id=\"$1_toc\" href=\"#$1\">$2</a></li>\n",
|
||||||
"\\item\\label{$1_toc} $2\\ref{$1}\n", [e.refname, e.header])
|
"\\item\\label{$1_toc} $2\\ref{$1}\n", [e.refname, e.header])
|
||||||
|
|
||||||
proc renderTocEntries*(d: var TRstGenerator, j: var int, lvl: int,
|
proc renderTocEntries*(d: var TRstGenerator, j: var int, lvl: int,
|
||||||
|
|
@ -759,33 +759,33 @@ proc renderImage(d: PDoc, n: PRstNode, result: var string) =
|
||||||
var options = ""
|
var options = ""
|
||||||
var s = getFieldValue(n, "scale")
|
var s = getFieldValue(n, "scale")
|
||||||
if s.valid: dispA(d.target, options, " scale=\"$1\"", " scale=$1", [strip(s)])
|
if s.valid: dispA(d.target, options, " scale=\"$1\"", " scale=$1", [strip(s)])
|
||||||
|
|
||||||
s = getFieldValue(n, "height")
|
s = getFieldValue(n, "height")
|
||||||
if s.valid: dispA(d.target, options, " height=\"$1\"", " height=$1", [strip(s)])
|
if s.valid: dispA(d.target, options, " height=\"$1\"", " height=$1", [strip(s)])
|
||||||
|
|
||||||
s = getFieldValue(n, "width")
|
s = getFieldValue(n, "width")
|
||||||
if s.valid: dispA(d.target, options, " width=\"$1\"", " width=$1", [strip(s)])
|
if s.valid: dispA(d.target, options, " width=\"$1\"", " width=$1", [strip(s)])
|
||||||
|
|
||||||
s = getFieldValue(n, "alt")
|
s = getFieldValue(n, "alt")
|
||||||
if s.valid: dispA(d.target, options, " alt=\"$1\"", "", [strip(s)])
|
if s.valid: dispA(d.target, options, " alt=\"$1\"", "", [strip(s)])
|
||||||
|
|
||||||
s = getFieldValue(n, "align")
|
s = getFieldValue(n, "align")
|
||||||
if s.valid: dispA(d.target, options, " align=\"$1\"", "", [strip(s)])
|
if s.valid: dispA(d.target, options, " align=\"$1\"", "", [strip(s)])
|
||||||
|
|
||||||
if options.len > 0: options = dispF(d.target, "$1", "[$1]", [options])
|
if options.len > 0: options = dispF(d.target, "$1", "[$1]", [options])
|
||||||
|
|
||||||
let arg = getArgument(n)
|
let arg = getArgument(n)
|
||||||
if arg.valid:
|
if arg.valid:
|
||||||
dispA(d.target, result, "<img src=\"$1\"$2 />", "\\includegraphics$2{$1}",
|
dispA(d.target, result, "<img src=\"$1\"$2 />", "\\includegraphics$2{$1}",
|
||||||
[arg, options])
|
[arg, options])
|
||||||
if len(n) >= 3: renderRstToOut(d, n.sons[2], result)
|
if len(n) >= 3: renderRstToOut(d, n.sons[2], result)
|
||||||
|
|
||||||
proc renderSmiley(d: PDoc, n: PRstNode, result: var string) =
|
proc renderSmiley(d: PDoc, n: PRstNode, result: var string) =
|
||||||
dispA(d.target, result,
|
dispA(d.target, result,
|
||||||
"""<img src="$1" width="15"
|
"""<img src="$1" width="15"
|
||||||
height="17" hspace="2" vspace="2" class="smiley" />""",
|
height="17" hspace="2" vspace="2" class="smiley" />""",
|
||||||
"\\includegraphics{$1}", [d.config["doc.smiley_format"] % n.text])
|
"\\includegraphics{$1}", [d.config["doc.smiley_format"] % n.text])
|
||||||
|
|
||||||
proc parseCodeBlockField(d: PDoc, n: PRstNode, params: var CodeBlockParams) =
|
proc parseCodeBlockField(d: PDoc, n: PRstNode, params: var CodeBlockParams) =
|
||||||
## Parses useful fields which can appear before a code block.
|
## Parses useful fields which can appear before a code block.
|
||||||
##
|
##
|
||||||
|
|
@ -880,11 +880,11 @@ proc renderCodeBlock(d: PDoc, n: PRstNode, result: var string) =
|
||||||
else:
|
else:
|
||||||
var g: TGeneralTokenizer
|
var g: TGeneralTokenizer
|
||||||
initGeneralTokenizer(g, m.text)
|
initGeneralTokenizer(g, m.text)
|
||||||
while true:
|
while true:
|
||||||
getNextToken(g, params.lang)
|
getNextToken(g, params.lang)
|
||||||
case g.kind
|
case g.kind
|
||||||
of gtEof: break
|
of gtEof: break
|
||||||
of gtNone, gtWhitespace:
|
of gtNone, gtWhitespace:
|
||||||
add(result, substr(m.text, g.start, g.length + g.start - 1))
|
add(result, substr(m.text, g.start, g.length + g.start - 1))
|
||||||
else:
|
else:
|
||||||
dispA(d.target, result, "<span class=\"$2\">$1</span>", "\\span$2{$1}", [
|
dispA(d.target, result, "<span class=\"$2\">$1</span>", "\\span$2{$1}", [
|
||||||
|
|
@ -893,36 +893,36 @@ proc renderCodeBlock(d: PDoc, n: PRstNode, result: var string) =
|
||||||
deinitGeneralTokenizer(g)
|
deinitGeneralTokenizer(g)
|
||||||
dispA(d.target, result, blockEnd, "\n\\end{rstpre}\n")
|
dispA(d.target, result, blockEnd, "\n\\end{rstpre}\n")
|
||||||
|
|
||||||
proc renderContainer(d: PDoc, n: PRstNode, result: var string) =
|
proc renderContainer(d: PDoc, n: PRstNode, result: var string) =
|
||||||
var tmp = ""
|
var tmp = ""
|
||||||
renderRstToOut(d, n.sons[2], tmp)
|
renderRstToOut(d, n.sons[2], tmp)
|
||||||
var arg = strip(getArgument(n))
|
var arg = strip(getArgument(n))
|
||||||
if arg == "":
|
if arg == "":
|
||||||
dispA(d.target, result, "<div>$1</div>", "$1", [tmp])
|
dispA(d.target, result, "<div>$1</div>", "$1", [tmp])
|
||||||
else:
|
else:
|
||||||
dispA(d.target, result, "<div class=\"$1\">$2</div>", "$2", [arg, tmp])
|
dispA(d.target, result, "<div class=\"$1\">$2</div>", "$2", [arg, tmp])
|
||||||
|
|
||||||
proc texColumns(n: PRstNode): string =
|
proc texColumns(n: PRstNode): string =
|
||||||
result = ""
|
result = ""
|
||||||
for i in countup(1, len(n)): add(result, "|X")
|
for i in countup(1, len(n)): add(result, "|X")
|
||||||
|
|
||||||
proc renderField(d: PDoc, n: PRstNode, result: var string) =
|
proc renderField(d: PDoc, n: PRstNode, result: var string) =
|
||||||
var b = false
|
var b = false
|
||||||
if d.target == outLatex:
|
if d.target == outLatex:
|
||||||
var fieldname = addNodes(n.sons[0])
|
var fieldname = addNodes(n.sons[0])
|
||||||
var fieldval = esc(d.target, strip(addNodes(n.sons[1])))
|
var fieldval = esc(d.target, strip(addNodes(n.sons[1])))
|
||||||
if cmpIgnoreStyle(fieldname, "author") == 0 or
|
if cmpIgnoreStyle(fieldname, "author") == 0 or
|
||||||
cmpIgnoreStyle(fieldname, "authors") == 0:
|
cmpIgnoreStyle(fieldname, "authors") == 0:
|
||||||
if d.meta[metaAuthor].len == 0:
|
if d.meta[metaAuthor].len == 0:
|
||||||
d.meta[metaAuthor] = fieldval
|
d.meta[metaAuthor] = fieldval
|
||||||
b = true
|
b = true
|
||||||
elif cmpIgnoreStyle(fieldname, "version") == 0:
|
elif cmpIgnoreStyle(fieldname, "version") == 0:
|
||||||
if d.meta[metaVersion].len == 0:
|
if d.meta[metaVersion].len == 0:
|
||||||
d.meta[metaVersion] = fieldval
|
d.meta[metaVersion] = fieldval
|
||||||
b = true
|
b = true
|
||||||
if not b:
|
if not b:
|
||||||
renderAux(d, n, "<tr>$1</tr>\n", "$1", result)
|
renderAux(d, n, "<tr>$1</tr>\n", "$1", result)
|
||||||
|
|
||||||
proc renderRstToOut(d: PDoc, n: PRstNode, result: var string) =
|
proc renderRstToOut(d: PDoc, n: PRstNode, result: var string) =
|
||||||
if n == nil: return
|
if n == nil: return
|
||||||
case n.kind
|
case n.kind
|
||||||
|
|
@ -947,54 +947,54 @@ proc renderRstToOut(d: PDoc, n: PRstNode, result: var string) =
|
||||||
of rnDefBody: renderAux(d, n, "<dd>$1</dd>\n", "$1\n", result)
|
of rnDefBody: renderAux(d, n, "<dd>$1</dd>\n", "$1\n", result)
|
||||||
of rnFieldList:
|
of rnFieldList:
|
||||||
var tmp = ""
|
var tmp = ""
|
||||||
for i in countup(0, len(n) - 1):
|
for i in countup(0, len(n) - 1):
|
||||||
renderRstToOut(d, n.sons[i], tmp)
|
renderRstToOut(d, n.sons[i], tmp)
|
||||||
if tmp.len != 0:
|
if tmp.len != 0:
|
||||||
dispA(d.target, result,
|
dispA(d.target, result,
|
||||||
"<table class=\"docinfo\" frame=\"void\" rules=\"none\">" &
|
"<table class=\"docinfo\" frame=\"void\" rules=\"none\">" &
|
||||||
"<col class=\"docinfo-name\" />" &
|
"<col class=\"docinfo-name\" />" &
|
||||||
"<col class=\"docinfo-content\" />" &
|
"<col class=\"docinfo-content\" />" &
|
||||||
"<tbody valign=\"top\">$1" &
|
"<tbody valign=\"top\">$1" &
|
||||||
"</tbody></table>",
|
"</tbody></table>",
|
||||||
"\\begin{description}$1\\end{description}\n",
|
"\\begin{description}$1\\end{description}\n",
|
||||||
[tmp])
|
[tmp])
|
||||||
of rnField: renderField(d, n, result)
|
of rnField: renderField(d, n, result)
|
||||||
of rnFieldName:
|
of rnFieldName:
|
||||||
renderAux(d, n, "<th class=\"docinfo-name\">$1:</th>",
|
renderAux(d, n, "<th class=\"docinfo-name\">$1:</th>",
|
||||||
"\\item[$1:]", result)
|
"\\item[$1:]", result)
|
||||||
of rnFieldBody:
|
of rnFieldBody:
|
||||||
renderAux(d, n, "<td>$1</td>", " $1\n", result)
|
renderAux(d, n, "<td>$1</td>", " $1\n", result)
|
||||||
of rnIndex:
|
of rnIndex:
|
||||||
renderRstToOut(d, n.sons[2], result)
|
renderRstToOut(d, n.sons[2], result)
|
||||||
of rnOptionList:
|
of rnOptionList:
|
||||||
renderAux(d, n, "<table frame=\"void\">$1</table>",
|
renderAux(d, n, "<table frame=\"void\">$1</table>",
|
||||||
"\\begin{description}\n$1\\end{description}\n", result)
|
"\\begin{description}\n$1\\end{description}\n", result)
|
||||||
of rnOptionListItem:
|
of rnOptionListItem:
|
||||||
renderAux(d, n, "<tr>$1</tr>\n", "$1", result)
|
renderAux(d, n, "<tr>$1</tr>\n", "$1", result)
|
||||||
of rnOptionGroup:
|
of rnOptionGroup:
|
||||||
renderAux(d, n, "<th align=\"left\">$1</th>", "\\item[$1]", result)
|
renderAux(d, n, "<th align=\"left\">$1</th>", "\\item[$1]", result)
|
||||||
of rnDescription:
|
of rnDescription:
|
||||||
renderAux(d, n, "<td align=\"left\">$1</td>\n", " $1\n", result)
|
renderAux(d, n, "<td align=\"left\">$1</td>\n", " $1\n", result)
|
||||||
of rnOption, rnOptionString, rnOptionArgument:
|
of rnOption, rnOptionString, rnOptionArgument:
|
||||||
doAssert false, "renderRstToOut"
|
doAssert false, "renderRstToOut"
|
||||||
of rnLiteralBlock:
|
of rnLiteralBlock:
|
||||||
renderAux(d, n, "<pre>$1</pre>\n",
|
renderAux(d, n, "<pre>$1</pre>\n",
|
||||||
"\\begin{rstpre}\n$1\n\\end{rstpre}\n", result)
|
"\\begin{rstpre}\n$1\n\\end{rstpre}\n", result)
|
||||||
of rnQuotedLiteralBlock:
|
of rnQuotedLiteralBlock:
|
||||||
doAssert false, "renderRstToOut"
|
doAssert false, "renderRstToOut"
|
||||||
of rnLineBlock:
|
of rnLineBlock:
|
||||||
renderAux(d, n, "<p>$1</p>", "$1\n\n", result)
|
renderAux(d, n, "<p>$1</p>", "$1\n\n", result)
|
||||||
of rnLineBlockItem:
|
of rnLineBlockItem:
|
||||||
renderAux(d, n, "$1<br />", "$1\\\\\n", result)
|
renderAux(d, n, "$1<br />", "$1\\\\\n", result)
|
||||||
of rnBlockQuote:
|
of rnBlockQuote:
|
||||||
renderAux(d, n, "<blockquote><p>$1</p></blockquote>\n",
|
renderAux(d, n, "<blockquote><p>$1</p></blockquote>\n",
|
||||||
"\\begin{quote}$1\\end{quote}\n", result)
|
"\\begin{quote}$1\\end{quote}\n", result)
|
||||||
of rnTable, rnGridTable:
|
of rnTable, rnGridTable:
|
||||||
renderAux(d, n,
|
renderAux(d, n,
|
||||||
"<table border=\"1\" class=\"docutils\">$1</table>",
|
"<table border=\"1\" class=\"docutils\">$1</table>",
|
||||||
"\\begin{table}\\begin{rsttab}{" &
|
"\\begin{table}\\begin{rsttab}{" &
|
||||||
texColumns(n) & "|}\n\\hline\n$1\\end{rsttab}\\end{table}", result)
|
texColumns(n) & "|}\n\\hline\n$1\\end{rsttab}\\end{table}", result)
|
||||||
of rnTableRow:
|
of rnTableRow:
|
||||||
if len(n) >= 1:
|
if len(n) >= 1:
|
||||||
if d.target == outLatex:
|
if d.target == outLatex:
|
||||||
#var tmp = ""
|
#var tmp = ""
|
||||||
|
|
@ -1007,25 +1007,25 @@ proc renderRstToOut(d: PDoc, n: PRstNode, result: var string) =
|
||||||
result.add("<tr>")
|
result.add("<tr>")
|
||||||
renderAux(d, n, result)
|
renderAux(d, n, result)
|
||||||
result.add("</tr>\n")
|
result.add("</tr>\n")
|
||||||
of rnTableDataCell:
|
of rnTableDataCell:
|
||||||
renderAux(d, n, "<td>$1</td>", "$1", result)
|
renderAux(d, n, "<td>$1</td>", "$1", result)
|
||||||
of rnTableHeaderCell:
|
of rnTableHeaderCell:
|
||||||
renderAux(d, n, "<th>$1</th>", "\\textbf{$1}", result)
|
renderAux(d, n, "<th>$1</th>", "\\textbf{$1}", result)
|
||||||
of rnLabel:
|
of rnLabel:
|
||||||
doAssert false, "renderRstToOut" # used for footnotes and other
|
doAssert false, "renderRstToOut" # used for footnotes and other
|
||||||
of rnFootnote:
|
of rnFootnote:
|
||||||
doAssert false, "renderRstToOut" # a footnote
|
doAssert false, "renderRstToOut" # a footnote
|
||||||
of rnCitation:
|
of rnCitation:
|
||||||
doAssert false, "renderRstToOut" # similar to footnote
|
doAssert false, "renderRstToOut" # similar to footnote
|
||||||
of rnRef:
|
of rnRef:
|
||||||
var tmp = ""
|
var tmp = ""
|
||||||
renderAux(d, n, tmp)
|
renderAux(d, n, tmp)
|
||||||
dispA(d.target, result,
|
dispA(d.target, result,
|
||||||
"<a class=\"reference external\" href=\"#$2\">$1</a>",
|
"<a class=\"reference external\" href=\"#$2\">$1</a>",
|
||||||
"$1\\ref{$2}", [tmp, rstnodeToRefname(n)])
|
"$1\\ref{$2}", [tmp, rstnodeToRefname(n)])
|
||||||
of rnStandaloneHyperlink:
|
of rnStandaloneHyperlink:
|
||||||
renderAux(d, n,
|
renderAux(d, n,
|
||||||
"<a class=\"reference external\" href=\"$1\">$1</a>",
|
"<a class=\"reference external\" href=\"$1\">$1</a>",
|
||||||
"\\href{$1}{$1}", result)
|
"\\href{$1}{$1}", result)
|
||||||
of rnHyperlink:
|
of rnHyperlink:
|
||||||
var tmp0 = ""
|
var tmp0 = ""
|
||||||
|
|
@ -1042,11 +1042,11 @@ proc renderRstToOut(d: PDoc, n: PRstNode, result: var string) =
|
||||||
of rnRawLatex:
|
of rnRawLatex:
|
||||||
if d.target == outLatex:
|
if d.target == outLatex:
|
||||||
result.add addNodes(lastSon(n))
|
result.add addNodes(lastSon(n))
|
||||||
|
|
||||||
of rnImage, rnFigure: renderImage(d, n, result)
|
of rnImage, rnFigure: renderImage(d, n, result)
|
||||||
of rnCodeBlock: renderCodeBlock(d, n, result)
|
of rnCodeBlock: renderCodeBlock(d, n, result)
|
||||||
of rnContainer: renderContainer(d, n, result)
|
of rnContainer: renderContainer(d, n, result)
|
||||||
of rnSubstitutionReferences, rnSubstitutionDef:
|
of rnSubstitutionReferences, rnSubstitutionDef:
|
||||||
renderAux(d, n, "|$1|", "|$1|", result)
|
renderAux(d, n, "|$1|", "|$1|", result)
|
||||||
of rnDirective:
|
of rnDirective:
|
||||||
renderAux(d, n, "", "", result)
|
renderAux(d, n, "", "", result)
|
||||||
|
|
@ -1063,15 +1063,15 @@ proc renderRstToOut(d: PDoc, n: PRstNode, result: var string) =
|
||||||
of rnStrongEmphasis:
|
of rnStrongEmphasis:
|
||||||
renderAux(d, n, "<strong>$1</strong>", "\\textbf{$1}", result)
|
renderAux(d, n, "<strong>$1</strong>", "\\textbf{$1}", result)
|
||||||
of rnTripleEmphasis:
|
of rnTripleEmphasis:
|
||||||
renderAux(d, n, "<strong><em>$1</em></strong>",
|
renderAux(d, n, "<strong><em>$1</em></strong>",
|
||||||
"\\textbf{emph{$1}}", result)
|
"\\textbf{emph{$1}}", result)
|
||||||
of rnInterpretedText:
|
of rnInterpretedText:
|
||||||
renderAux(d, n, "<cite>$1</cite>", "\\emph{$1}", result)
|
renderAux(d, n, "<cite>$1</cite>", "\\emph{$1}", result)
|
||||||
of rnIdx:
|
of rnIdx:
|
||||||
renderIndexTerm(d, n, result)
|
renderIndexTerm(d, n, result)
|
||||||
of rnInlineLiteral:
|
of rnInlineLiteral:
|
||||||
renderAux(d, n,
|
renderAux(d, n,
|
||||||
"<tt class=\"docutils literal\"><span class=\"pre\">$1</span></tt>",
|
"<tt class=\"docutils literal\"><span class=\"pre\">$1</span></tt>",
|
||||||
"\\texttt{$1}", result)
|
"\\texttt{$1}", result)
|
||||||
of rnSmiley: renderSmiley(d, n, result)
|
of rnSmiley: renderSmiley(d, n, result)
|
||||||
of rnLeaf: result.add(esc(d.target, n.text))
|
of rnLeaf: result.add(esc(d.target, n.text))
|
||||||
|
|
@ -1082,55 +1082,55 @@ proc renderRstToOut(d: PDoc, n: PRstNode, result: var string) =
|
||||||
|
|
||||||
# -----------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------
|
||||||
|
|
||||||
proc getVarIdx(varnames: openArray[string], id: string): int =
|
proc getVarIdx(varnames: openArray[string], id: string): int =
|
||||||
for i in countup(0, high(varnames)):
|
for i in countup(0, high(varnames)):
|
||||||
if cmpIgnoreStyle(varnames[i], id) == 0:
|
if cmpIgnoreStyle(varnames[i], id) == 0:
|
||||||
return i
|
return i
|
||||||
result = -1
|
result = -1
|
||||||
|
|
||||||
proc formatNamedVars*(frmt: string, varnames: openArray[string],
|
proc formatNamedVars*(frmt: string, varnames: openArray[string],
|
||||||
varvalues: openArray[string]): string =
|
varvalues: openArray[string]): string =
|
||||||
var i = 0
|
var i = 0
|
||||||
var L = len(frmt)
|
var L = len(frmt)
|
||||||
result = ""
|
result = ""
|
||||||
var num = 0
|
var num = 0
|
||||||
while i < L:
|
while i < L:
|
||||||
if frmt[i] == '$':
|
if frmt[i] == '$':
|
||||||
inc(i) # skip '$'
|
inc(i) # skip '$'
|
||||||
case frmt[i]
|
case frmt[i]
|
||||||
of '#':
|
of '#':
|
||||||
add(result, varvalues[num])
|
add(result, varvalues[num])
|
||||||
inc(num)
|
inc(num)
|
||||||
inc(i)
|
inc(i)
|
||||||
of '$':
|
of '$':
|
||||||
add(result, "$")
|
add(result, "$")
|
||||||
inc(i)
|
inc(i)
|
||||||
of '0'..'9':
|
of '0'..'9':
|
||||||
var j = 0
|
var j = 0
|
||||||
while true:
|
while true:
|
||||||
j = (j * 10) + ord(frmt[i]) - ord('0')
|
j = (j * 10) + ord(frmt[i]) - ord('0')
|
||||||
inc(i)
|
inc(i)
|
||||||
if i > L-1 or frmt[i] notin {'0'..'9'}: break
|
if i > L-1 or frmt[i] notin {'0'..'9'}: break
|
||||||
if j > high(varvalues) + 1:
|
if j > high(varvalues) + 1:
|
||||||
raise newException(ValueError, "invalid index: " & $j)
|
raise newException(ValueError, "invalid index: " & $j)
|
||||||
num = j
|
num = j
|
||||||
add(result, varvalues[j - 1])
|
add(result, varvalues[j - 1])
|
||||||
of 'A'..'Z', 'a'..'z', '\x80'..'\xFF':
|
of 'A'..'Z', 'a'..'z', '\x80'..'\xFF':
|
||||||
var id = ""
|
var id = ""
|
||||||
while true:
|
while true:
|
||||||
add(id, frmt[i])
|
add(id, frmt[i])
|
||||||
inc(i)
|
inc(i)
|
||||||
if frmt[i] notin {'A'..'Z', '_', 'a'..'z', '\x80'..'\xFF'}: break
|
if frmt[i] notin {'A'..'Z', '_', 'a'..'z', '\x80'..'\xFF'}: break
|
||||||
var idx = getVarIdx(varnames, id)
|
var idx = getVarIdx(varnames, id)
|
||||||
if idx >= 0:
|
if idx >= 0:
|
||||||
add(result, varvalues[idx])
|
add(result, varvalues[idx])
|
||||||
else:
|
else:
|
||||||
raise newException(ValueError, "unknown substitution var: " & id)
|
raise newException(ValueError, "unknown substitution var: " & id)
|
||||||
of '{':
|
of '{':
|
||||||
var id = ""
|
var id = ""
|
||||||
inc(i)
|
inc(i)
|
||||||
while frmt[i] != '}':
|
while frmt[i] != '}':
|
||||||
if frmt[i] == '\0':
|
if frmt[i] == '\0':
|
||||||
raise newException(ValueError, "'}' expected")
|
raise newException(ValueError, "'}' expected")
|
||||||
add(id, frmt[i])
|
add(id, frmt[i])
|
||||||
inc(i)
|
inc(i)
|
||||||
|
|
@ -1138,12 +1138,12 @@ proc formatNamedVars*(frmt: string, varnames: openArray[string],
|
||||||
# search for the variable:
|
# search for the variable:
|
||||||
var idx = getVarIdx(varnames, id)
|
var idx = getVarIdx(varnames, id)
|
||||||
if idx >= 0: add(result, varvalues[idx])
|
if idx >= 0: add(result, varvalues[idx])
|
||||||
else:
|
else:
|
||||||
raise newException(ValueError, "unknown substitution var: " & id)
|
raise newException(ValueError, "unknown substitution var: " & id)
|
||||||
else:
|
else:
|
||||||
raise newException(ValueError, "unknown substitution: $" & $frmt[i])
|
raise newException(ValueError, "unknown substitution: $" & $frmt[i])
|
||||||
var start = i
|
var start = i
|
||||||
while i < L:
|
while i < L:
|
||||||
if frmt[i] != '$': inc(i)
|
if frmt[i] != '$': inc(i)
|
||||||
else: break
|
else: break
|
||||||
if i-1 >= start: add(result, substr(frmt, start, i - 1))
|
if i-1 >= start: add(result, substr(frmt, start, i - 1))
|
||||||
|
|
@ -1163,10 +1163,10 @@ proc defaultConfig*(): StringTableRef =
|
||||||
## pages, while this proc returns just the content for procs like
|
## pages, while this proc returns just the content for procs like
|
||||||
## ``rstToHtml`` to generate the bare minimum HTML.
|
## ``rstToHtml`` to generate the bare minimum HTML.
|
||||||
result = newStringTable(modeStyleInsensitive)
|
result = newStringTable(modeStyleInsensitive)
|
||||||
|
|
||||||
template setConfigVar(key, val: expr) =
|
template setConfigVar(key, val: expr) =
|
||||||
result[key] = val
|
result[key] = val
|
||||||
|
|
||||||
# If you need to modify these values, it might be worth updating the template
|
# If you need to modify these values, it might be worth updating the template
|
||||||
# file in config/nimdoc.cfg.
|
# file in config/nimdoc.cfg.
|
||||||
setConfigVar("split.item.toc", "20")
|
setConfigVar("split.item.toc", "20")
|
||||||
|
|
@ -1214,7 +1214,7 @@ $content
|
||||||
|
|
||||||
# ---------- forum ---------------------------------------------------------
|
# ---------- forum ---------------------------------------------------------
|
||||||
|
|
||||||
proc rstToHtml*(s: string, options: TRstParseOptions,
|
proc rstToHtml*(s: string, options: TRstParseOptions,
|
||||||
config: StringTableRef): string =
|
config: StringTableRef): string =
|
||||||
## Converts an input rst string into embeddable HTML.
|
## Converts an input rst string into embeddable HTML.
|
||||||
##
|
##
|
||||||
|
|
@ -1236,13 +1236,13 @@ proc rstToHtml*(s: string, options: TRstParseOptions,
|
||||||
## output you have to create your own ``TRstGenerator`` with
|
## output you have to create your own ``TRstGenerator`` with
|
||||||
## ``initRstGenerator`` and related procs.
|
## ``initRstGenerator`` and related procs.
|
||||||
|
|
||||||
proc myFindFile(filename: string): string =
|
proc myFindFile(filename: string): string =
|
||||||
# we don't find any files in online mode:
|
# we don't find any files in online mode:
|
||||||
result = ""
|
result = ""
|
||||||
|
|
||||||
const filen = "input"
|
const filen = "input"
|
||||||
var d: TRstGenerator
|
var d: TRstGenerator
|
||||||
initRstGenerator(d, outHtml, config, filen, options, myFindFile,
|
initRstGenerator(d, outHtml, config, filen, options, myFindFile,
|
||||||
rst.defaultMsgHandler)
|
rst.defaultMsgHandler)
|
||||||
var dummyHasToc = false
|
var dummyHasToc = false
|
||||||
var rst = rstParse(s, filen, 0, 1, dummyHasToc, options)
|
var rst = rstParse(s, filen, 0, 1, dummyHasToc, options)
|
||||||
|
|
@ -1251,5 +1251,6 @@ proc rstToHtml*(s: string, options: TRstParseOptions,
|
||||||
|
|
||||||
|
|
||||||
when isMainModule:
|
when isMainModule:
|
||||||
echo rstToHtml("*Hello* **world**!", {},
|
assert rstToHtml("*Hello* **world**!", {},
|
||||||
newStringTable(modeStyleInsensitive))
|
newStringTable(modeStyleInsensitive)) ==
|
||||||
|
"<em>Hello</em> <strong>world</strong>!"
|
||||||
|
|
|
||||||
|
|
@ -10,28 +10,25 @@
|
||||||
{.deadCodeElim: on.}
|
{.deadCodeElim: on.}
|
||||||
import posix
|
import posix
|
||||||
|
|
||||||
type
|
type
|
||||||
Speed* = cuint
|
Speed* = cuint
|
||||||
Tcflag* = cuint
|
Tcflag* = cuint
|
||||||
|
|
||||||
const
|
const
|
||||||
NCCS* = 32
|
NCCS* = 32
|
||||||
|
|
||||||
type
|
type
|
||||||
Termios* = object {.importc: "struct termios", header: "<termios.h>", final, pure.}
|
Termios* {.importc: "struct termios", header: "<termios.h>".} = object
|
||||||
iflag*: Tcflag # input mode flags
|
c_iflag*: Tcflag # input mode flags
|
||||||
oflag*: Tcflag # output mode flags
|
c_oflag*: Tcflag # output mode flags
|
||||||
cflag*: Tcflag # control mode flags
|
c_cflag*: Tcflag # control mode flags
|
||||||
lflag*: Tcflag # local mode flags
|
c_lflag*: Tcflag # local mode flags
|
||||||
line*: cuchar # line discipline
|
c_line*: cuchar # line discipline
|
||||||
cc*: array[NCCS, cuchar] # control characters
|
c_cc*: array[NCCS, cuchar] # control characters
|
||||||
ispeed*: Speed # input speed
|
|
||||||
ospeed*: Speed # output speed
|
|
||||||
|
|
||||||
|
|
||||||
# cc characters
|
# cc characters
|
||||||
|
|
||||||
const
|
const
|
||||||
VINTR* = 0
|
VINTR* = 0
|
||||||
VQUIT* = 1
|
VQUIT* = 1
|
||||||
VERASE* = 2
|
VERASE* = 2
|
||||||
|
|
@ -50,9 +47,9 @@ const
|
||||||
VLNEXT* = 15
|
VLNEXT* = 15
|
||||||
VEOL2* = 16
|
VEOL2* = 16
|
||||||
|
|
||||||
# iflag bits
|
# iflag bits
|
||||||
|
|
||||||
const
|
const
|
||||||
IGNBRK* = 1
|
IGNBRK* = 1
|
||||||
BRKINT* = 2
|
BRKINT* = 2
|
||||||
IGNPAR* = 4
|
IGNPAR* = 4
|
||||||
|
|
@ -69,9 +66,9 @@ const
|
||||||
IMAXBEL* = 20000
|
IMAXBEL* = 20000
|
||||||
IUTF8* = 40000
|
IUTF8* = 40000
|
||||||
|
|
||||||
# oflag bits
|
# oflag bits
|
||||||
|
|
||||||
const
|
const
|
||||||
OPOST* = 1
|
OPOST* = 1
|
||||||
OLCUC* = 2
|
OLCUC* = 2
|
||||||
ONLCR* = 4
|
ONLCR* = 4
|
||||||
|
|
@ -104,9 +101,9 @@ const
|
||||||
VT1* = 40000
|
VT1* = 40000
|
||||||
XTABS* = 14000
|
XTABS* = 14000
|
||||||
|
|
||||||
# cflag bit meaning
|
# cflag bit meaning
|
||||||
|
|
||||||
const
|
const
|
||||||
CBAUD* = 10017
|
CBAUD* = 10017
|
||||||
B0* = 0
|
B0* = 0
|
||||||
B50* = 1
|
B50* = 1
|
||||||
|
|
@ -158,9 +155,9 @@ const
|
||||||
CMSPAR* = 0o010000000000
|
CMSPAR* = 0o010000000000
|
||||||
CRTSCTS* = 0o020000000000
|
CRTSCTS* = 0o020000000000
|
||||||
|
|
||||||
# lflag bits
|
# lflag bits
|
||||||
|
|
||||||
const
|
const
|
||||||
ISIG* = 1
|
ISIG* = 1
|
||||||
ICANON* = 2
|
ICANON* = 2
|
||||||
XCASE* = 4
|
XCASE* = 4
|
||||||
|
|
@ -178,87 +175,87 @@ const
|
||||||
IEXTEN* = 0o000000100000
|
IEXTEN* = 0o000000100000
|
||||||
EXTPROC* = 0o000000200000
|
EXTPROC* = 0o000000200000
|
||||||
|
|
||||||
# tcflow() and TCXONC use these
|
# tcflow() and TCXONC use these
|
||||||
|
|
||||||
const
|
const
|
||||||
TCOOFF* = 0
|
TCOOFF* = 0
|
||||||
TCOON* = 1
|
TCOON* = 1
|
||||||
TCIOFF* = 2
|
TCIOFF* = 2
|
||||||
TCION* = 3
|
TCION* = 3
|
||||||
|
|
||||||
# tcflush() and TCFLSH use these
|
# tcflush() and TCFLSH use these
|
||||||
|
|
||||||
const
|
const
|
||||||
TCIFLUSH* = 0
|
TCIFLUSH* = 0
|
||||||
TCOFLUSH* = 1
|
TCOFLUSH* = 1
|
||||||
TCIOFLUSH* = 2
|
TCIOFLUSH* = 2
|
||||||
|
|
||||||
# tcsetattr uses these
|
# tcsetattr uses these
|
||||||
|
|
||||||
const
|
const
|
||||||
TCSANOW* = 0
|
TCSANOW* = 0
|
||||||
TCSADRAIN* = 1
|
TCSADRAIN* = 1
|
||||||
TCSAFLUSH* = 2
|
TCSAFLUSH* = 2
|
||||||
|
|
||||||
# Compare a character C to a value VAL from the `cc' array in a
|
# Compare a character C to a value VAL from the `cc' array in a
|
||||||
# `struct termios'. If VAL is _POSIX_VDISABLE, no character can match it.
|
# `struct termios'. If VAL is _POSIX_VDISABLE, no character can match it.
|
||||||
|
|
||||||
template cceq*(val, c: expr): expr =
|
template cceq*(val, c: expr): expr =
|
||||||
c == val and val != POSIX_VDISABLE
|
c == val and val != POSIX_VDISABLE
|
||||||
|
|
||||||
# Return the output baud rate stored in *TERMIOS_P.
|
# Return the output baud rate stored in *TERMIOS_P.
|
||||||
|
|
||||||
proc cfGetOspeed*(termios: ptr Termios): Speed {.importc: "cfgetospeed",
|
proc cfGetOspeed*(termios: ptr Termios): Speed {.importc: "cfgetospeed",
|
||||||
header: "<termios.h>".}
|
header: "<termios.h>".}
|
||||||
# Return the input baud rate stored in *TERMIOS_P.
|
# Return the input baud rate stored in *TERMIOS_P.
|
||||||
|
|
||||||
proc cfGetIspeed*(termios: ptr Termios): Speed {.importc: "cfgetispeed",
|
proc cfGetIspeed*(termios: ptr Termios): Speed {.importc: "cfgetispeed",
|
||||||
header: "<termios.h>".}
|
header: "<termios.h>".}
|
||||||
# Set the output baud rate stored in *TERMIOS_P to SPEED.
|
# Set the output baud rate stored in *TERMIOS_P to SPEED.
|
||||||
|
|
||||||
proc cfSetOspeed*(termios: ptr Termios; speed: Speed): cint {.
|
proc cfSetOspeed*(termios: ptr Termios; speed: Speed): cint {.
|
||||||
importc: "cfsetospeed", header: "<termios.h>".}
|
importc: "cfsetospeed", header: "<termios.h>".}
|
||||||
# Set the input baud rate stored in *TERMIOS_P to SPEED.
|
# Set the input baud rate stored in *TERMIOS_P to SPEED.
|
||||||
|
|
||||||
proc cfSetIspeed*(termios: ptr Termios; speed: Speed): cint {.
|
proc cfSetIspeed*(termios: ptr Termios; speed: Speed): cint {.
|
||||||
importc: "cfsetispeed", header: "<termios.h>".}
|
importc: "cfsetispeed", header: "<termios.h>".}
|
||||||
# Set both the input and output baud rates in *TERMIOS_OP to SPEED.
|
# Set both the input and output baud rates in *TERMIOS_OP to SPEED.
|
||||||
|
|
||||||
proc cfSetSpeed*(termios: ptr Termios; speed: Speed): cint {.
|
proc cfSetSpeed*(termios: ptr Termios; speed: Speed): cint {.
|
||||||
importc: "cfsetspeed", header: "<termios.h>".}
|
importc: "cfsetspeed", header: "<termios.h>".}
|
||||||
# Put the state of FD into *TERMIOS_P.
|
# Put the state of FD into *TERMIOS_P.
|
||||||
|
|
||||||
proc tcGetAttr*(fd: cint; termios: ptr Termios): cint {.
|
proc tcGetAttr*(fd: cint; termios: ptr Termios): cint {.
|
||||||
importc: "tcgetattr", header: "<termios.h>".}
|
importc: "tcgetattr", header: "<termios.h>".}
|
||||||
# Set the state of FD to *TERMIOS_P.
|
# Set the state of FD to *TERMIOS_P.
|
||||||
# Values for OPTIONAL_ACTIONS (TCSA*) are in <bits/termios.h>.
|
# Values for OPTIONAL_ACTIONS (TCSA*) are in <bits/termios.h>.
|
||||||
|
|
||||||
proc tcSetAttr*(fd: cint; optional_actions: cint; termios: ptr Termios): cint {.
|
proc tcSetAttr*(fd: cint; optional_actions: cint; termios: ptr Termios): cint {.
|
||||||
importc: "tcsetattr", header: "<termios.h>".}
|
importc: "tcsetattr", header: "<termios.h>".}
|
||||||
# Set *TERMIOS_P to indicate raw mode.
|
# Set *TERMIOS_P to indicate raw mode.
|
||||||
|
|
||||||
proc cfMakeRaw*(termios: ptr Termios) {.importc: "cfmakeraw",
|
proc cfMakeRaw*(termios: ptr Termios) {.importc: "cfmakeraw",
|
||||||
header: "<termios.h>".}
|
header: "<termios.h>".}
|
||||||
# Send zero bits on FD.
|
# Send zero bits on FD.
|
||||||
|
|
||||||
proc tcSendBreak*(fd: cint; duration: cint): cint {.importc: "tcsendbreak",
|
proc tcSendBreak*(fd: cint; duration: cint): cint {.importc: "tcsendbreak",
|
||||||
header: "<termios.h>".}
|
header: "<termios.h>".}
|
||||||
# Wait for pending output to be written on FD.
|
# Wait for pending output to be written on FD.
|
||||||
#
|
#
|
||||||
# This function is a cancellation point and therefore not marked with
|
# This function is a cancellation point and therefore not marked with
|
||||||
# .
|
# .
|
||||||
|
|
||||||
proc tcDrain*(fd: cint): cint {.importc: "tcdrain", header: "<termios.h>".}
|
proc tcDrain*(fd: cint): cint {.importc: "tcdrain", header: "<termios.h>".}
|
||||||
# Flush pending data on FD.
|
# Flush pending data on FD.
|
||||||
# Values for QUEUE_SELECTOR (TC{I,O,IO}FLUSH) are in <bits/termios.h>.
|
# Values for QUEUE_SELECTOR (TC{I,O,IO}FLUSH) are in <bits/termios.h>.
|
||||||
|
|
||||||
proc tcFlush*(fd: cint; queue_selector: cint): cint {.importc: "tcflush",
|
proc tcFlush*(fd: cint; queue_selector: cint): cint {.importc: "tcflush",
|
||||||
header: "<termios.h>".}
|
header: "<termios.h>".}
|
||||||
# Suspend or restart transmission on FD.
|
# Suspend or restart transmission on FD.
|
||||||
# Values for ACTION (TC[IO]{OFF,ON}) are in <bits/termios.h>.
|
# Values for ACTION (TC[IO]{OFF,ON}) are in <bits/termios.h>.
|
||||||
|
|
||||||
proc tcFlow*(fd: cint; action: cint): cint {.importc: "tcflow",
|
proc tcFlow*(fd: cint; action: cint): cint {.importc: "tcflow",
|
||||||
header: "<termios.h>".}
|
header: "<termios.h>".}
|
||||||
# Get process group ID for session leader for controlling terminal FD.
|
# Get process group ID for session leader for controlling terminal FD.
|
||||||
|
|
||||||
proc tcGetSid*(fd: cint): TPid {.importc: "tcgetsid", header: "<termios.h>".}
|
proc tcGetSid*(fd: cint): TPid {.importc: "tcgetsid", header: "<termios.h>".}
|
||||||
|
|
|
||||||
|
|
@ -221,7 +221,7 @@ proc spawn*[TIn](p: var TActorPool[TIn, void], input: TIn,
|
||||||
setupTask()
|
setupTask()
|
||||||
schedule()
|
schedule()
|
||||||
|
|
||||||
when isMainModule:
|
when not defined(testing) and isMainModule:
|
||||||
var
|
var
|
||||||
a: TActorPool[int, void]
|
a: TActorPool[int, void]
|
||||||
createActorPool(a)
|
createActorPool(a)
|
||||||
|
|
|
||||||
|
|
@ -40,8 +40,8 @@ proc reverse*[T](a: var openArray[T]) =
|
||||||
proc reversed*[T](a: openArray[T], first, last: Natural): seq[T] =
|
proc reversed*[T](a: openArray[T], first, last: Natural): seq[T] =
|
||||||
## returns the reverse of the array `a[first..last]`.
|
## returns the reverse of the array `a[first..last]`.
|
||||||
result = newSeq[T](last - first + 1)
|
result = newSeq[T](last - first + 1)
|
||||||
var x = first
|
var x = first.int
|
||||||
var y = last
|
var y = last.int
|
||||||
while x <= last:
|
while x <= last:
|
||||||
result[x] = a[y]
|
result[x] = a[y]
|
||||||
dec(y)
|
dec(y)
|
||||||
|
|
|
||||||
|
|
@ -634,6 +634,93 @@ when defined(windows) or defined(nimdoc):
|
||||||
# free ``ol``.
|
# free ``ol``.
|
||||||
return retFuture
|
return retFuture
|
||||||
|
|
||||||
|
proc recvInto*(socket: TAsyncFD, buf: cstring, size: int,
|
||||||
|
flags = {SocketFlag.SafeDisconn}): Future[int] =
|
||||||
|
## Reads **up to** ``size`` bytes from ``socket`` into ``buf``, which must
|
||||||
|
## at least be of that size. Returned future will complete once all the
|
||||||
|
## data requested is read, a part of the data has been read, or the socket
|
||||||
|
## has disconnected in which case the future will complete with a value of
|
||||||
|
## ``0``.
|
||||||
|
##
|
||||||
|
## **Warning**: The ``Peek`` socket flag is not supported on Windows.
|
||||||
|
|
||||||
|
|
||||||
|
# Things to note:
|
||||||
|
# * When WSARecv completes immediately then ``bytesReceived`` is very
|
||||||
|
# unreliable.
|
||||||
|
# * Still need to implement message-oriented socket disconnection,
|
||||||
|
# '\0' in the message currently signifies a socket disconnect. Who
|
||||||
|
# knows what will happen when someone sends that to our socket.
|
||||||
|
verifyPresence(socket)
|
||||||
|
assert SocketFlag.Peek notin flags, "Peek not supported on Windows."
|
||||||
|
|
||||||
|
var retFuture = newFuture[int]("recvInto")
|
||||||
|
|
||||||
|
#buf[] = '\0'
|
||||||
|
var dataBuf: TWSABuf
|
||||||
|
dataBuf.buf = buf
|
||||||
|
dataBuf.len = size
|
||||||
|
|
||||||
|
var bytesReceived: Dword
|
||||||
|
var flagsio = flags.toOSFlags().Dword
|
||||||
|
var ol = PCustomOverlapped()
|
||||||
|
GC_ref(ol)
|
||||||
|
ol.data = TCompletionData(fd: socket, cb:
|
||||||
|
proc (fd: TAsyncFD, bytesCount: Dword, errcode: OSErrorCode) =
|
||||||
|
if not retFuture.finished:
|
||||||
|
if errcode == OSErrorCode(-1):
|
||||||
|
if bytesCount == 0 and dataBuf.buf[0] == '\0':
|
||||||
|
retFuture.complete(0)
|
||||||
|
else:
|
||||||
|
retFuture.complete(bytesCount)
|
||||||
|
else:
|
||||||
|
if flags.isDisconnectionError(errcode):
|
||||||
|
retFuture.complete(0)
|
||||||
|
else:
|
||||||
|
retFuture.fail(newException(OSError, osErrorMsg(errcode)))
|
||||||
|
if dataBuf.buf != nil:
|
||||||
|
dataBuf.buf = nil
|
||||||
|
)
|
||||||
|
|
||||||
|
let ret = WSARecv(socket.SocketHandle, addr dataBuf, 1, addr bytesReceived,
|
||||||
|
addr flagsio, cast[POVERLAPPED](ol), nil)
|
||||||
|
if ret == -1:
|
||||||
|
let err = osLastError()
|
||||||
|
if err.int32 != ERROR_IO_PENDING:
|
||||||
|
if dataBuf.buf != nil:
|
||||||
|
dataBuf.buf = nil
|
||||||
|
GC_unref(ol)
|
||||||
|
if flags.isDisconnectionError(err):
|
||||||
|
retFuture.complete(0)
|
||||||
|
else:
|
||||||
|
retFuture.fail(newException(OSError, osErrorMsg(err)))
|
||||||
|
elif ret == 0 and bytesReceived == 0 and dataBuf.buf[0] == '\0':
|
||||||
|
# We have to ensure that the buffer is empty because WSARecv will tell
|
||||||
|
# us immediately when it was disconnected, even when there is still
|
||||||
|
# data in the buffer.
|
||||||
|
# We want to give the user as much data as we can. So we only return
|
||||||
|
# the empty string (which signals a disconnection) when there is
|
||||||
|
# nothing left to read.
|
||||||
|
retFuture.complete(0)
|
||||||
|
# TODO: "For message-oriented sockets, where a zero byte message is often
|
||||||
|
# allowable, a failure with an error code of WSAEDISCON is used to
|
||||||
|
# indicate graceful closure."
|
||||||
|
# ~ http://msdn.microsoft.com/en-us/library/ms741688%28v=vs.85%29.aspx
|
||||||
|
else:
|
||||||
|
# Request to read completed immediately.
|
||||||
|
# From my tests bytesReceived isn't reliable.
|
||||||
|
let realSize =
|
||||||
|
if bytesReceived == 0:
|
||||||
|
size
|
||||||
|
else:
|
||||||
|
bytesReceived
|
||||||
|
assert realSize <= size
|
||||||
|
retFuture.complete(realSize)
|
||||||
|
# We don't deallocate ``ol`` here because even though this completed
|
||||||
|
# immediately poll will still be notified about its completion and it will
|
||||||
|
# free ``ol``.
|
||||||
|
return retFuture
|
||||||
|
|
||||||
proc send*(socket: TAsyncFD, data: string,
|
proc send*(socket: TAsyncFD, data: string,
|
||||||
flags = {SocketFlag.SafeDisconn}): Future[void] =
|
flags = {SocketFlag.SafeDisconn}): Future[void] =
|
||||||
## Sends ``data`` to ``socket``. The returned future will complete once all
|
## Sends ``data`` to ``socket``. The returned future will complete once all
|
||||||
|
|
@ -983,6 +1070,30 @@ else:
|
||||||
addRead(socket, cb)
|
addRead(socket, cb)
|
||||||
return retFuture
|
return retFuture
|
||||||
|
|
||||||
|
proc recvInto*(socket: TAsyncFD, buf: cstring, size: int,
|
||||||
|
flags = {SocketFlag.SafeDisconn}): Future[int] =
|
||||||
|
var retFuture = newFuture[int]("recvInto")
|
||||||
|
|
||||||
|
proc cb(sock: TAsyncFD): bool =
|
||||||
|
result = true
|
||||||
|
let res = recv(sock.SocketHandle, buf, size.cint,
|
||||||
|
flags.toOSFlags())
|
||||||
|
if res < 0:
|
||||||
|
let lastError = osLastError()
|
||||||
|
if lastError.int32 notin {EINTR, EWOULDBLOCK, EAGAIN}:
|
||||||
|
if flags.isDisconnectionError(lastError):
|
||||||
|
retFuture.complete(0)
|
||||||
|
else:
|
||||||
|
retFuture.fail(newException(OSError, osErrorMsg(lastError)))
|
||||||
|
else:
|
||||||
|
result = false # We still want this callback to be called.
|
||||||
|
else:
|
||||||
|
retFuture.complete(res)
|
||||||
|
# TODO: The following causes a massive slowdown.
|
||||||
|
#if not cb(socket):
|
||||||
|
addRead(socket, cb)
|
||||||
|
return retFuture
|
||||||
|
|
||||||
proc send*(socket: TAsyncFD, data: string,
|
proc send*(socket: TAsyncFD, data: string,
|
||||||
flags = {SocketFlag.SafeDisconn}): Future[void] =
|
flags = {SocketFlag.SafeDisconn}): Future[void] =
|
||||||
var retFuture = newFuture[void]("send")
|
var retFuture = newFuture[void]("send")
|
||||||
|
|
|
||||||
|
|
@ -300,7 +300,7 @@ proc newAsyncFtpClient*(address: string, port = Port(21),
|
||||||
result.dsockConnected = false
|
result.dsockConnected = false
|
||||||
result.csock = newAsyncSocket()
|
result.csock = newAsyncSocket()
|
||||||
|
|
||||||
when isMainModule:
|
when not defined(testing) and isMainModule:
|
||||||
var ftp = newAsyncFtpClient("example.com", user = "test", pass = "test")
|
var ftp = newAsyncFtpClient("example.com", user = "test", pass = "test")
|
||||||
proc main(ftp: AsyncFtpClient) {.async.} =
|
proc main(ftp: AsyncFtpClient) {.async.} =
|
||||||
await ftp.connect()
|
await ftp.connect()
|
||||||
|
|
|
||||||
|
|
@ -23,8 +23,7 @@
|
||||||
## proc cb(req: Request) {.async.} =
|
## proc cb(req: Request) {.async.} =
|
||||||
## await req.respond(Http200, "Hello World")
|
## await req.respond(Http200, "Hello World")
|
||||||
##
|
##
|
||||||
## asyncCheck server.serve(Port(8080), cb)
|
## waitFor server.serve(Port(8080), cb)
|
||||||
## runForever()
|
|
||||||
|
|
||||||
import strtabs, asyncnet, asyncdispatch, parseutils, uri, strutils
|
import strtabs, asyncnet, asyncdispatch, parseutils, uri, strutils
|
||||||
type
|
type
|
||||||
|
|
@ -109,22 +108,19 @@ proc sendHeaders*(req: Request, headers: StringTableRef): Future[void] =
|
||||||
addHeaders(msg, headers)
|
addHeaders(msg, headers)
|
||||||
return req.client.send(msg)
|
return req.client.send(msg)
|
||||||
|
|
||||||
proc respond*(req: Request, code: HttpCode,
|
proc respond*(req: Request, code: HttpCode, content: string,
|
||||||
content: string, headers = newStringTable()) {.async.} =
|
headers: StringTableRef = nil): Future[void] =
|
||||||
## Responds to the request with the specified ``HttpCode``, headers and
|
## Responds to the request with the specified ``HttpCode``, headers and
|
||||||
## content.
|
## content.
|
||||||
##
|
##
|
||||||
## This procedure will **not** close the client socket.
|
## This procedure will **not** close the client socket.
|
||||||
var customHeaders = headers
|
|
||||||
customHeaders["Content-Length"] = $content.len
|
|
||||||
var msg = "HTTP/1.1 " & $code & "\c\L"
|
var msg = "HTTP/1.1 " & $code & "\c\L"
|
||||||
msg.addHeaders(customHeaders)
|
|
||||||
await req.client.send(msg & "\c\L" & content)
|
|
||||||
|
|
||||||
proc newRequest(): Request =
|
if headers != nil:
|
||||||
result.headers = newStringTable(modeCaseInsensitive)
|
msg.addHeaders(headers)
|
||||||
result.hostname = ""
|
msg.add("Content-Length: " & $content.len & "\c\L\c\L")
|
||||||
result.body = ""
|
msg.add(content)
|
||||||
|
result = req.client.send(msg)
|
||||||
|
|
||||||
proc parseHeader(line: string): tuple[key, value: string] =
|
proc parseHeader(line: string): tuple[key, value: string] =
|
||||||
var i = 0
|
var i = 0
|
||||||
|
|
@ -149,59 +145,65 @@ proc sendStatus(client: AsyncSocket, status: string): Future[void] =
|
||||||
proc processClient(client: AsyncSocket, address: string,
|
proc processClient(client: AsyncSocket, address: string,
|
||||||
callback: proc (request: Request):
|
callback: proc (request: Request):
|
||||||
Future[void] {.closure, gcsafe.}) {.async.} =
|
Future[void] {.closure, gcsafe.}) {.async.} =
|
||||||
|
var request: Request
|
||||||
|
request.url = initUri()
|
||||||
|
request.headers = newStringTable(modeCaseInsensitive)
|
||||||
|
var line = newStringOfCap(80)
|
||||||
|
var key, value = ""
|
||||||
|
|
||||||
while not client.isClosed:
|
while not client.isClosed:
|
||||||
# GET /path HTTP/1.1
|
# GET /path HTTP/1.1
|
||||||
# Header: val
|
# Header: val
|
||||||
# \n
|
# \n
|
||||||
var request = newRequest()
|
request.headers.clear(modeCaseInsensitive)
|
||||||
request.hostname = address
|
request.hostname.shallowCopy(address)
|
||||||
assert client != nil
|
assert client != nil
|
||||||
request.client = client
|
request.client = client
|
||||||
|
|
||||||
# First line - GET /path HTTP/1.1
|
# First line - GET /path HTTP/1.1
|
||||||
let line = await client.recvLine() # TODO: Timeouts.
|
line.setLen(0)
|
||||||
|
await client.recvLineInto(addr line) # TODO: Timeouts.
|
||||||
if line == "":
|
if line == "":
|
||||||
client.close()
|
client.close()
|
||||||
return
|
return
|
||||||
let lineParts = line.split(' ')
|
|
||||||
if lineParts.len != 3:
|
|
||||||
await request.respond(Http400, "Invalid request. Got: " & line)
|
|
||||||
continue
|
|
||||||
|
|
||||||
let reqMethod = lineParts[0]
|
var i = 0
|
||||||
let path = lineParts[1]
|
for linePart in line.split(' '):
|
||||||
let protocol = lineParts[2]
|
case i
|
||||||
|
of 0: request.reqMethod.shallowCopy(linePart.normalize)
|
||||||
|
of 1: parseUri(linePart, request.url)
|
||||||
|
of 2:
|
||||||
|
try:
|
||||||
|
request.protocol = parseProtocol(linePart)
|
||||||
|
except ValueError:
|
||||||
|
asyncCheck request.respond(Http400,
|
||||||
|
"Invalid request protocol. Got: " & linePart)
|
||||||
|
continue
|
||||||
|
else:
|
||||||
|
await request.respond(Http400, "Invalid request. Got: " & line)
|
||||||
|
continue
|
||||||
|
inc i
|
||||||
|
|
||||||
# Headers
|
# Headers
|
||||||
var i = 0
|
|
||||||
while true:
|
while true:
|
||||||
i = 0
|
i = 0
|
||||||
let headerLine = await client.recvLine()
|
line.setLen(0)
|
||||||
if headerLine == "":
|
await client.recvLineInto(addr line)
|
||||||
|
|
||||||
|
if line == "":
|
||||||
client.close(); return
|
client.close(); return
|
||||||
if headerLine == "\c\L": break
|
if line == "\c\L": break
|
||||||
# TODO: Compiler crash
|
let (key, value) = parseHeader(line)
|
||||||
#let (key, value) = parseHeader(headerLine)
|
request.headers[key] = value
|
||||||
let kv = parseHeader(headerLine)
|
|
||||||
request.headers[kv.key] = kv.value
|
|
||||||
|
|
||||||
request.reqMethod = reqMethod
|
if request.reqMethod == "post":
|
||||||
request.url = parseUri(path)
|
|
||||||
try:
|
|
||||||
request.protocol = protocol.parseProtocol()
|
|
||||||
except ValueError:
|
|
||||||
asyncCheck request.respond(Http400, "Invalid request protocol. Got: " &
|
|
||||||
protocol)
|
|
||||||
continue
|
|
||||||
|
|
||||||
if reqMethod.normalize == "post":
|
|
||||||
# Check for Expect header
|
# Check for Expect header
|
||||||
if request.headers.hasKey("Expect"):
|
if request.headers.hasKey("Expect"):
|
||||||
if request.headers["Expect"].toLower == "100-continue":
|
if request.headers["Expect"].toLower == "100-continue":
|
||||||
await client.sendStatus("100 Continue")
|
await client.sendStatus("100 Continue")
|
||||||
else:
|
else:
|
||||||
await client.sendStatus("417 Expectation Failed")
|
await client.sendStatus("417 Expectation Failed")
|
||||||
|
|
||||||
# Read the body
|
# Read the body
|
||||||
# - Check for Content-length header
|
# - Check for Content-length header
|
||||||
if request.headers.hasKey("Content-Length"):
|
if request.headers.hasKey("Content-Length"):
|
||||||
|
|
@ -215,11 +217,11 @@ proc processClient(client: AsyncSocket, address: string,
|
||||||
await request.respond(Http400, "Bad Request. No Content-Length.")
|
await request.respond(Http400, "Bad Request. No Content-Length.")
|
||||||
continue
|
continue
|
||||||
|
|
||||||
case reqMethod.normalize
|
case request.reqMethod
|
||||||
of "get", "post", "head", "put", "delete", "trace", "options", "connect", "patch":
|
of "get", "post", "head", "put", "delete", "trace", "options", "connect", "patch":
|
||||||
await callback(request)
|
await callback(request)
|
||||||
else:
|
else:
|
||||||
await request.respond(Http400, "Invalid request method. Got: " & reqMethod)
|
await request.respond(Http400, "Invalid request method. Got: " & request.reqMethod)
|
||||||
|
|
||||||
# Persistent connections
|
# Persistent connections
|
||||||
if (request.protocol == HttpVer11 and
|
if (request.protocol == HttpVer11 and
|
||||||
|
|
@ -247,7 +249,7 @@ proc serve*(server: AsyncHttpServer, port: Port,
|
||||||
server.socket.setSockOpt(OptReuseAddr, true)
|
server.socket.setSockOpt(OptReuseAddr, true)
|
||||||
server.socket.bindAddr(port, address)
|
server.socket.bindAddr(port, address)
|
||||||
server.socket.listen()
|
server.socket.listen()
|
||||||
|
|
||||||
while true:
|
while true:
|
||||||
# TODO: Causes compiler crash.
|
# TODO: Causes compiler crash.
|
||||||
#var (address, client) = await server.socket.acceptAddr()
|
#var (address, client) = await server.socket.acceptAddr()
|
||||||
|
|
@ -260,7 +262,7 @@ proc close*(server: AsyncHttpServer) =
|
||||||
## Terminates the async http server instance.
|
## Terminates the async http server instance.
|
||||||
server.socket.close()
|
server.socket.close()
|
||||||
|
|
||||||
when isMainModule:
|
when not defined(testing) and isMainModule:
|
||||||
proc main =
|
proc main =
|
||||||
var server = newAsyncHttpServer()
|
var server = newAsyncHttpServer()
|
||||||
proc cb(req: Request) {.async.} =
|
proc cb(req: Request) {.async.} =
|
||||||
|
|
|
||||||
|
|
@ -660,7 +660,7 @@ proc len*(disp: Dispatcher): int =
|
||||||
## Retrieves the amount of delegates in ``disp``.
|
## Retrieves the amount of delegates in ``disp``.
|
||||||
return disp.delegates.len
|
return disp.delegates.len
|
||||||
|
|
||||||
when isMainModule:
|
when not defined(testing) and isMainModule:
|
||||||
|
|
||||||
proc testConnect(s: AsyncSocket, no: int) =
|
proc testConnect(s: AsyncSocket, no: int) =
|
||||||
echo("Connected! " & $no)
|
echo("Connected! " & $no)
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,7 @@
|
||||||
##
|
##
|
||||||
## Chat server
|
## Chat server
|
||||||
## ^^^^^^^^^^^
|
## ^^^^^^^^^^^
|
||||||
##
|
##
|
||||||
## The following example demonstrates a simple chat server.
|
## The following example demonstrates a simple chat server.
|
||||||
##
|
##
|
||||||
## .. code-block::nim
|
## .. code-block::nim
|
||||||
|
|
@ -182,26 +182,30 @@ proc connect*(socket: AsyncSocket, address: string, port: Port,
|
||||||
sslSetConnectState(socket.sslHandle)
|
sslSetConnectState(socket.sslHandle)
|
||||||
sslLoop(socket, flags, sslDoHandshake(socket.sslHandle))
|
sslLoop(socket, flags, sslDoHandshake(socket.sslHandle))
|
||||||
|
|
||||||
proc readInto(buf: cstring, size: int, socket: AsyncSocket,
|
template readInto(buf: cstring, size: int, socket: AsyncSocket,
|
||||||
flags: set[SocketFlag]): Future[int] {.async.} =
|
flags: set[SocketFlag]): int =
|
||||||
|
## Reads **up to** ``size`` bytes from ``socket`` into ``buf``. Note that
|
||||||
|
## this is a template and not a proc.
|
||||||
|
var res = 0
|
||||||
if socket.isSsl:
|
if socket.isSsl:
|
||||||
when defined(ssl):
|
when defined(ssl):
|
||||||
# SSL mode.
|
# SSL mode.
|
||||||
sslLoop(socket, flags,
|
sslLoop(socket, flags,
|
||||||
sslRead(socket.sslHandle, buf, size.cint))
|
sslRead(socket.sslHandle, buf, size.cint))
|
||||||
result = opResult
|
res = opResult
|
||||||
else:
|
else:
|
||||||
var data = await recv(socket.fd.TAsyncFD, size, flags)
|
var recvIntoFut = recvInto(socket.fd.TAsyncFD, buf, size, flags)
|
||||||
if data.len != 0:
|
yield recvIntoFut
|
||||||
copyMem(buf, addr data[0], data.len)
|
|
||||||
# Not in SSL mode.
|
# Not in SSL mode.
|
||||||
result = data.len
|
res = recvIntoFut.read()
|
||||||
|
res
|
||||||
|
|
||||||
proc readIntoBuf(socket: AsyncSocket,
|
template readIntoBuf(socket: AsyncSocket,
|
||||||
flags: set[SocketFlag]): Future[int] {.async.} =
|
flags: set[SocketFlag]): int =
|
||||||
result = await readInto(addr socket.buffer[0], BufferSize, socket, flags)
|
var size = readInto(addr socket.buffer[0], BufferSize, socket, flags)
|
||||||
socket.currPos = 0
|
socket.currPos = 0
|
||||||
socket.bufLen = result
|
socket.bufLen = size
|
||||||
|
size
|
||||||
|
|
||||||
proc recv*(socket: AsyncSocket, size: int,
|
proc recv*(socket: AsyncSocket, size: int,
|
||||||
flags = {SocketFlag.SafeDisconn}): Future[string] {.async.} =
|
flags = {SocketFlag.SafeDisconn}): Future[string] {.async.} =
|
||||||
|
|
@ -222,10 +226,11 @@ proc recv*(socket: AsyncSocket, size: int,
|
||||||
## to be read then the future will complete with a value of ``""``.
|
## to be read then the future will complete with a value of ``""``.
|
||||||
if socket.isBuffered:
|
if socket.isBuffered:
|
||||||
result = newString(size)
|
result = newString(size)
|
||||||
|
shallow(result)
|
||||||
let originalBufPos = socket.currPos
|
let originalBufPos = socket.currPos
|
||||||
|
|
||||||
if socket.bufLen == 0:
|
if socket.bufLen == 0:
|
||||||
let res = await socket.readIntoBuf(flags - {SocketFlag.Peek})
|
let res = socket.readIntoBuf(flags - {SocketFlag.Peek})
|
||||||
if res == 0:
|
if res == 0:
|
||||||
result.setLen(0)
|
result.setLen(0)
|
||||||
return
|
return
|
||||||
|
|
@ -236,7 +241,7 @@ proc recv*(socket: AsyncSocket, size: int,
|
||||||
if SocketFlag.Peek in flags:
|
if SocketFlag.Peek in flags:
|
||||||
# We don't want to get another buffer if we're peeking.
|
# We don't want to get another buffer if we're peeking.
|
||||||
break
|
break
|
||||||
let res = await socket.readIntoBuf(flags - {SocketFlag.Peek})
|
let res = socket.readIntoBuf(flags - {SocketFlag.Peek})
|
||||||
if res == 0:
|
if res == 0:
|
||||||
break
|
break
|
||||||
|
|
||||||
|
|
@ -251,7 +256,7 @@ proc recv*(socket: AsyncSocket, size: int,
|
||||||
result.setLen(read)
|
result.setLen(read)
|
||||||
else:
|
else:
|
||||||
result = newString(size)
|
result = newString(size)
|
||||||
let read = await readInto(addr result[0], size, socket, flags)
|
let read = readInto(addr result[0], size, socket, flags)
|
||||||
result.setLen(read)
|
result.setLen(read)
|
||||||
|
|
||||||
proc send*(socket: AsyncSocket, data: string,
|
proc send*(socket: AsyncSocket, data: string,
|
||||||
|
|
@ -302,15 +307,14 @@ proc accept*(socket: AsyncSocket,
|
||||||
retFut.complete(future.read.client)
|
retFut.complete(future.read.client)
|
||||||
return retFut
|
return retFut
|
||||||
|
|
||||||
proc recvLine*(socket: AsyncSocket,
|
proc recvLineInto*(socket: AsyncSocket, resString: ptr string,
|
||||||
flags = {SocketFlag.SafeDisconn}): Future[string] {.async.} =
|
flags = {SocketFlag.SafeDisconn}) {.async.} =
|
||||||
## Reads a line of data from ``socket``. Returned future will complete once
|
## Reads a line of data from ``socket`` into ``resString``.
|
||||||
## a full line is read or an error occurs.
|
|
||||||
##
|
##
|
||||||
## If a full line is read ``\r\L`` is not
|
## If a full line is read ``\r\L`` is not
|
||||||
## added to ``line``, however if solely ``\r\L`` is read then ``line``
|
## added to ``line``, however if solely ``\r\L`` is read then ``line``
|
||||||
## will be set to it.
|
## will be set to it.
|
||||||
##
|
##
|
||||||
## If the socket is disconnected, ``line`` will be set to ``""``.
|
## If the socket is disconnected, ``line`` will be set to ``""``.
|
||||||
##
|
##
|
||||||
## If the socket is disconnected in the middle of a line (before ``\r\L``
|
## If the socket is disconnected in the middle of a line (before ``\r\L``
|
||||||
|
|
@ -318,27 +322,32 @@ proc recvLine*(socket: AsyncSocket,
|
||||||
## The partial line **will be lost**.
|
## The partial line **will be lost**.
|
||||||
##
|
##
|
||||||
## **Warning**: The ``Peek`` flag is not yet implemented.
|
## **Warning**: The ``Peek`` flag is not yet implemented.
|
||||||
##
|
##
|
||||||
## **Warning**: ``recvLine`` on unbuffered sockets assumes that the protocol
|
## **Warning**: ``recvLineInto`` on unbuffered sockets assumes that the
|
||||||
## uses ``\r\L`` to delimit a new line.
|
## protocol uses ``\r\L`` to delimit a new line.
|
||||||
template addNLIfEmpty(): stmt =
|
##
|
||||||
if result.len == 0:
|
## **Warning**: ``recvLineInto`` currently uses a raw pointer to a string for
|
||||||
result.add("\c\L")
|
## performance reasons. This will likely change soon to use FutureVars.
|
||||||
assert SocketFlag.Peek notin flags ## TODO:
|
assert SocketFlag.Peek notin flags ## TODO:
|
||||||
|
result = newFuture[void]("asyncnet.recvLineInto")
|
||||||
|
|
||||||
|
template addNLIfEmpty(): stmt =
|
||||||
|
if resString[].len == 0:
|
||||||
|
resString[].add("\c\L")
|
||||||
|
|
||||||
if socket.isBuffered:
|
if socket.isBuffered:
|
||||||
result = ""
|
|
||||||
if socket.bufLen == 0:
|
if socket.bufLen == 0:
|
||||||
let res = await socket.readIntoBuf(flags)
|
let res = socket.readIntoBuf(flags)
|
||||||
if res == 0:
|
if res == 0:
|
||||||
return
|
return
|
||||||
|
|
||||||
var lastR = false
|
var lastR = false
|
||||||
while true:
|
while true:
|
||||||
if socket.currPos >= socket.bufLen:
|
if socket.currPos >= socket.bufLen:
|
||||||
let res = await socket.readIntoBuf(flags)
|
let res = socket.readIntoBuf(flags)
|
||||||
if res == 0:
|
if res == 0:
|
||||||
result = ""
|
resString[].setLen(0)
|
||||||
break
|
return
|
||||||
|
|
||||||
case socket.buffer[socket.currPos]
|
case socket.buffer[socket.currPos]
|
||||||
of '\r':
|
of '\r':
|
||||||
|
|
@ -353,24 +362,53 @@ proc recvLine*(socket: AsyncSocket,
|
||||||
socket.currPos.inc()
|
socket.currPos.inc()
|
||||||
return
|
return
|
||||||
else:
|
else:
|
||||||
result.add socket.buffer[socket.currPos]
|
resString[].add socket.buffer[socket.currPos]
|
||||||
socket.currPos.inc()
|
socket.currPos.inc()
|
||||||
else:
|
else:
|
||||||
result = ""
|
|
||||||
var c = ""
|
var c = ""
|
||||||
while true:
|
while true:
|
||||||
c = await recv(socket, 1, flags)
|
let recvFut = recv(socket, 1, flags)
|
||||||
|
c = recvFut.read()
|
||||||
if c.len == 0:
|
if c.len == 0:
|
||||||
return ""
|
resString[].setLen(0)
|
||||||
|
return
|
||||||
if c == "\r":
|
if c == "\r":
|
||||||
c = await recv(socket, 1, flags) # Skip \L
|
let recvFut = recv(socket, 1, flags) # Skip \L
|
||||||
|
c = recvFut.read()
|
||||||
assert c == "\L"
|
assert c == "\L"
|
||||||
addNLIfEmpty()
|
addNLIfEmpty()
|
||||||
return
|
return
|
||||||
elif c == "\L":
|
elif c == "\L":
|
||||||
addNLIfEmpty()
|
addNLIfEmpty()
|
||||||
return
|
return
|
||||||
add(result.string, c)
|
resString[].add c
|
||||||
|
|
||||||
|
proc recvLine*(socket: AsyncSocket,
|
||||||
|
flags = {SocketFlag.SafeDisconn}): Future[string] {.async.} =
|
||||||
|
## Reads a line of data from ``socket``. Returned future will complete once
|
||||||
|
## a full line is read or an error occurs.
|
||||||
|
##
|
||||||
|
## If a full line is read ``\r\L`` is not
|
||||||
|
## added to ``line``, however if solely ``\r\L`` is read then ``line``
|
||||||
|
## will be set to it.
|
||||||
|
##
|
||||||
|
## If the socket is disconnected, ``line`` will be set to ``""``.
|
||||||
|
##
|
||||||
|
## If the socket is disconnected in the middle of a line (before ``\r\L``
|
||||||
|
## is read) then line will be set to ``""``.
|
||||||
|
## The partial line **will be lost**.
|
||||||
|
##
|
||||||
|
## **Warning**: The ``Peek`` flag is not yet implemented.
|
||||||
|
##
|
||||||
|
## **Warning**: ``recvLine`` on unbuffered sockets assumes that the protocol
|
||||||
|
## uses ``\r\L`` to delimit a new line.
|
||||||
|
template addNLIfEmpty(): stmt =
|
||||||
|
if result.len == 0:
|
||||||
|
result.add("\c\L")
|
||||||
|
assert SocketFlag.Peek notin flags ## TODO:
|
||||||
|
|
||||||
|
result = ""
|
||||||
|
await socket.recvLineInto(addr result, flags)
|
||||||
|
|
||||||
proc listen*(socket: AsyncSocket, backlog = SOMAXCONN) {.tags: [ReadIOEffect].} =
|
proc listen*(socket: AsyncSocket, backlog = SOMAXCONN) {.tags: [ReadIOEffect].} =
|
||||||
## Marks ``socket`` as accepting connections.
|
## Marks ``socket`` as accepting connections.
|
||||||
|
|
@ -458,7 +496,7 @@ proc isClosed*(socket: AsyncSocket): bool =
|
||||||
## Determines whether the socket has been closed.
|
## Determines whether the socket has been closed.
|
||||||
return socket.closed
|
return socket.closed
|
||||||
|
|
||||||
when isMainModule:
|
when not defined(testing) and isMainModule:
|
||||||
type
|
type
|
||||||
TestCases = enum
|
TestCases = enum
|
||||||
HighClient, LowClient, LowServer
|
HighClient, LowClient, LowServer
|
||||||
|
|
@ -500,11 +538,11 @@ when isMainModule:
|
||||||
proc (future: Future[void]) =
|
proc (future: Future[void]) =
|
||||||
echo("Send")
|
echo("Send")
|
||||||
client.close()
|
client.close()
|
||||||
|
|
||||||
var f = accept(sock)
|
var f = accept(sock)
|
||||||
f.callback = onAccept
|
f.callback = onAccept
|
||||||
|
|
||||||
var f = accept(sock)
|
var f = accept(sock)
|
||||||
f.callback = onAccept
|
f.callback = onAccept
|
||||||
runForever()
|
runForever()
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -16,33 +16,33 @@ import times
|
||||||
## Vectors are implemented as direction vectors, ie. when transformed with a matrix
|
## Vectors are implemented as direction vectors, ie. when transformed with a matrix
|
||||||
## the translation part of matrix is ignored. The coordinate system used is
|
## the translation part of matrix is ignored. The coordinate system used is
|
||||||
## right handed, because its compatible with 2d coordinate system (rotation around
|
## right handed, because its compatible with 2d coordinate system (rotation around
|
||||||
## zaxis equals 2d rotation).
|
## zaxis equals 2d rotation).
|
||||||
## Operators `+` , `-` , `*` , `/` , `+=` , `-=` , `*=` and `/=` are implemented
|
## Operators `+` , `-` , `*` , `/` , `+=` , `-=` , `*=` and `/=` are implemented
|
||||||
## for vectors and scalars.
|
## for vectors and scalars.
|
||||||
##
|
##
|
||||||
##
|
##
|
||||||
## Quick start example:
|
## Quick start example:
|
||||||
##
|
##
|
||||||
## # Create a matrix which first rotates, then scales and at last translates
|
## # Create a matrix which first rotates, then scales and at last translates
|
||||||
##
|
##
|
||||||
## var m:TMatrix3d=rotate(PI,vector3d(1,1,2.5)) & scale(2.0) & move(100.0,200.0,300.0)
|
## var m:TMatrix3d=rotate(PI,vector3d(1,1,2.5)) & scale(2.0) & move(100.0,200.0,300.0)
|
||||||
##
|
##
|
||||||
## # Create a 3d point at (100,150,200) and a vector (5,2,3)
|
## # Create a 3d point at (100,150,200) and a vector (5,2,3)
|
||||||
##
|
##
|
||||||
## var pt:TPoint3d=point3d(100.0,150.0,200.0)
|
## var pt:TPoint3d=point3d(100.0,150.0,200.0)
|
||||||
##
|
##
|
||||||
## var vec:TVector3d=vector3d(5.0,2.0,3.0)
|
## var vec:TVector3d=vector3d(5.0,2.0,3.0)
|
||||||
##
|
##
|
||||||
##
|
##
|
||||||
## pt &= m # transforms pt in place
|
## pt &= m # transforms pt in place
|
||||||
##
|
##
|
||||||
## var pt2:TPoint3d=pt & m #concatenates pt with m and returns a new point
|
## var pt2:TPoint3d=pt & m #concatenates pt with m and returns a new point
|
||||||
##
|
##
|
||||||
## var vec2:TVector3d=vec & m #concatenates vec with m and returns a new vector
|
## var vec2:TVector3d=vec & m #concatenates vec with m and returns a new vector
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
type
|
type
|
||||||
TMatrix3d* =object
|
TMatrix3d* =object
|
||||||
## Implements a row major 3d matrix, which means
|
## Implements a row major 3d matrix, which means
|
||||||
## transformations are applied the order they are concatenated.
|
## transformations are applied the order they are concatenated.
|
||||||
|
|
@ -53,12 +53,12 @@ type
|
||||||
## [ tx ty tz tw ]
|
## [ tx ty tz tw ]
|
||||||
ax*,ay*,az*,aw*, bx*,by*,bz*,bw*, cx*,cy*,cz*,cw*, tx*,ty*,tz*,tw*:float
|
ax*,ay*,az*,aw*, bx*,by*,bz*,bw*, cx*,cy*,cz*,cw*, tx*,ty*,tz*,tw*:float
|
||||||
TPoint3d* = object
|
TPoint3d* = object
|
||||||
## Implements a non-homegeneous 2d point stored as
|
## Implements a non-homegeneous 2d point stored as
|
||||||
## an `x` , `y` and `z` coordinate.
|
## an `x` , `y` and `z` coordinate.
|
||||||
x*,y*,z*:float
|
x*,y*,z*:float
|
||||||
TVector3d* = object
|
TVector3d* = object
|
||||||
## Implements a 3d **direction vector** stored as
|
## Implements a 3d **direction vector** stored as
|
||||||
## an `x` , `y` and `z` coordinate. Direction vector means,
|
## an `x` , `y` and `z` coordinate. Direction vector means,
|
||||||
## that when transforming a vector with a matrix, the translational
|
## that when transforming a vector with a matrix, the translational
|
||||||
## part of the matrix is ignored.
|
## part of the matrix is ignored.
|
||||||
x*,y*,z*:float
|
x*,y*,z*:float
|
||||||
|
|
@ -67,7 +67,7 @@ type
|
||||||
|
|
||||||
# Some forward declarations
|
# Some forward declarations
|
||||||
proc matrix3d*(ax,ay,az,aw,bx,by,bz,bw,cx,cy,cz,cw,tx,ty,tz,tw:float):TMatrix3d {.noInit.}
|
proc matrix3d*(ax,ay,az,aw,bx,by,bz,bw,cx,cy,cz,cw,tx,ty,tz,tw:float):TMatrix3d {.noInit.}
|
||||||
## Creates a new 4x4 3d transformation matrix.
|
## Creates a new 4x4 3d transformation matrix.
|
||||||
## `ax` , `ay` , `az` is the local x axis.
|
## `ax` , `ay` , `az` is the local x axis.
|
||||||
## `bx` , `by` , `bz` is the local y axis.
|
## `bx` , `by` , `bz` is the local y axis.
|
||||||
## `cx` , `cy` , `cz` is the local z axis.
|
## `cx` , `cy` , `cz` is the local z axis.
|
||||||
|
|
@ -76,7 +76,7 @@ proc vector3d*(x,y,z:float):TVector3d {.noInit,inline.}
|
||||||
## Returns a new 3d vector (`x`,`y`,`z`)
|
## Returns a new 3d vector (`x`,`y`,`z`)
|
||||||
proc point3d*(x,y,z:float):TPoint3d {.noInit,inline.}
|
proc point3d*(x,y,z:float):TPoint3d {.noInit,inline.}
|
||||||
## Returns a new 4d point (`x`,`y`,`z`)
|
## Returns a new 4d point (`x`,`y`,`z`)
|
||||||
proc tryNormalize*(v:var TVector3d):bool
|
proc tryNormalize*(v:var TVector3d):bool
|
||||||
## Modifies `v` to have a length of 1.0, keeping its angle.
|
## Modifies `v` to have a length of 1.0, keeping its angle.
|
||||||
## If `v` has zero length (and thus no angle), it is left unmodified and false is
|
## If `v` has zero length (and thus no angle), it is left unmodified and false is
|
||||||
## returned, otherwise true is returned.
|
## returned, otherwise true is returned.
|
||||||
|
|
@ -85,7 +85,7 @@ proc tryNormalize*(v:var TVector3d):bool
|
||||||
|
|
||||||
let
|
let
|
||||||
IDMATRIX*:TMatrix3d=matrix3d(
|
IDMATRIX*:TMatrix3d=matrix3d(
|
||||||
1.0,0.0,0.0,0.0,
|
1.0,0.0,0.0,0.0,
|
||||||
0.0,1.0,0.0,0.0,
|
0.0,1.0,0.0,0.0,
|
||||||
0.0,0.0,1.0,0.0,
|
0.0,0.0,1.0,0.0,
|
||||||
0.0,0.0,0.0,1.0)
|
0.0,0.0,0.0,1.0)
|
||||||
|
|
@ -114,20 +114,20 @@ proc safeArccos(v:float):float=
|
||||||
## due to rounding issues
|
## due to rounding issues
|
||||||
return arccos(clamp(v,-1.0,1.0))
|
return arccos(clamp(v,-1.0,1.0))
|
||||||
|
|
||||||
template makeBinOpVector(s:expr)=
|
template makeBinOpVector(s:expr)=
|
||||||
## implements binary operators + , - , * and / for vectors
|
## implements binary operators + , - , * and / for vectors
|
||||||
proc s*(a,b:TVector3d):TVector3d {.inline,noInit.} =
|
proc s*(a,b:TVector3d):TVector3d {.inline,noInit.} =
|
||||||
vector3d(s(a.x,b.x),s(a.y,b.y),s(a.z,b.z))
|
vector3d(s(a.x,b.x),s(a.y,b.y),s(a.z,b.z))
|
||||||
proc s*(a:TVector3d,b:float):TVector3d {.inline,noInit.} =
|
proc s*(a:TVector3d,b:float):TVector3d {.inline,noInit.} =
|
||||||
vector3d(s(a.x,b),s(a.y,b),s(a.z,b))
|
vector3d(s(a.x,b),s(a.y,b),s(a.z,b))
|
||||||
proc s*(a:float,b:TVector3d):TVector3d {.inline,noInit.} =
|
proc s*(a:float,b:TVector3d):TVector3d {.inline,noInit.} =
|
||||||
vector3d(s(a,b.x),s(a,b.y),s(a,b.z))
|
vector3d(s(a,b.x),s(a,b.y),s(a,b.z))
|
||||||
|
|
||||||
template makeBinOpAssignVector(s:expr)=
|
template makeBinOpAssignVector(s:expr)=
|
||||||
## implements inplace binary operators += , -= , /= and *= for vectors
|
## implements inplace binary operators += , -= , /= and *= for vectors
|
||||||
proc s*(a:var TVector3d,b:TVector3d) {.inline.} =
|
proc s*(a:var TVector3d,b:TVector3d) {.inline.} =
|
||||||
s(a.x,b.x) ; s(a.y,b.y) ; s(a.z,b.z)
|
s(a.x,b.x) ; s(a.y,b.y) ; s(a.z,b.z)
|
||||||
proc s*(a:var TVector3d,b:float) {.inline.} =
|
proc s*(a:var TVector3d,b:float) {.inline.} =
|
||||||
s(a.x,b) ; s(a.y,b) ; s(a.z,b)
|
s(a.x,b) ; s(a.y,b) ; s(a.z,b)
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -188,20 +188,20 @@ proc scale*(s:float):TMatrix3d {.noInit.} =
|
||||||
|
|
||||||
proc scale*(s:float,org:TPoint3d):TMatrix3d {.noInit.} =
|
proc scale*(s:float,org:TPoint3d):TMatrix3d {.noInit.} =
|
||||||
## Returns a new scaling matrix using, `org` as scale origin.
|
## Returns a new scaling matrix using, `org` as scale origin.
|
||||||
result.setElements(s,0,0,0, 0,s,0,0, 0,0,s,0,
|
result.setElements(s,0,0,0, 0,s,0,0, 0,0,s,0,
|
||||||
org.x-s*org.x,org.y-s*org.y,org.z-s*org.z,1.0)
|
org.x-s*org.x,org.y-s*org.y,org.z-s*org.z,1.0)
|
||||||
|
|
||||||
proc stretch*(sx,sy,sz:float):TMatrix3d {.noInit.} =
|
proc stretch*(sx,sy,sz:float):TMatrix3d {.noInit.} =
|
||||||
## Returns new a stretch matrix, which is a
|
## Returns new a stretch matrix, which is a
|
||||||
## scale matrix with non uniform scale in x,y and z.
|
## scale matrix with non uniform scale in x,y and z.
|
||||||
result.setElements(sx,0,0,0, 0,sy,0,0, 0,0,sz,0, 0,0,0,1)
|
result.setElements(sx,0,0,0, 0,sy,0,0, 0,0,sz,0, 0,0,0,1)
|
||||||
|
|
||||||
proc stretch*(sx,sy,sz:float,org:TPoint3d):TMatrix3d {.noInit.} =
|
proc stretch*(sx,sy,sz:float,org:TPoint3d):TMatrix3d {.noInit.} =
|
||||||
## Returns a new stretch matrix, which is a
|
## Returns a new stretch matrix, which is a
|
||||||
## scale matrix with non uniform scale in x,y and z.
|
## scale matrix with non uniform scale in x,y and z.
|
||||||
## `org` is used as stretch origin.
|
## `org` is used as stretch origin.
|
||||||
result.setElements(sx,0,0,0, 0,sy,0,0, 0,0,sz,0, org.x-sx*org.x,org.y-sy*org.y,org.z-sz*org.z,1)
|
result.setElements(sx,0,0,0, 0,sy,0,0, 0,0,sz,0, org.x-sx*org.x,org.y-sy*org.y,org.z-sz*org.z,1)
|
||||||
|
|
||||||
proc move*(dx,dy,dz:float):TMatrix3d {.noInit.} =
|
proc move*(dx,dy,dz:float):TMatrix3d {.noInit.} =
|
||||||
## Returns a new translation matrix.
|
## Returns a new translation matrix.
|
||||||
result.setElements(1,0,0,0, 0,1,0,0, 0,0,1,0, dx,dy,dz,1)
|
result.setElements(1,0,0,0, 0,1,0,0, 0,0,1,0, dx,dy,dz,1)
|
||||||
|
|
@ -235,7 +235,7 @@ proc rotate*(angle:float,axis:TVector3d):TMatrix3d {.noInit.}=
|
||||||
uvomc=normax.x*normax.y*omc
|
uvomc=normax.x*normax.y*omc
|
||||||
uwomc=normax.x*normax.z*omc
|
uwomc=normax.x*normax.z*omc
|
||||||
vwomc=normax.y*normax.z*omc
|
vwomc=normax.y*normax.z*omc
|
||||||
|
|
||||||
result.setElements(
|
result.setElements(
|
||||||
u2+(1.0-u2)*cs, uvomc+wsi, uwomc-vsi, 0.0,
|
u2+(1.0-u2)*cs, uvomc+wsi, uwomc-vsi, 0.0,
|
||||||
uvomc-wsi, v2+(1.0-v2)*cs, vwomc+usi, 0.0,
|
uvomc-wsi, v2+(1.0-v2)*cs, vwomc+usi, 0.0,
|
||||||
|
|
@ -248,11 +248,11 @@ proc rotate*(angle:float,org:TPoint3d,axis:TVector3d):TMatrix3d {.noInit.}=
|
||||||
|
|
||||||
# see PDF document http://inside.mines.edu/~gmurray/ArbitraryAxisRotation/ArbitraryAxisRotation.pdf
|
# see PDF document http://inside.mines.edu/~gmurray/ArbitraryAxisRotation/ArbitraryAxisRotation.pdf
|
||||||
# for how this is computed
|
# for how this is computed
|
||||||
|
|
||||||
var normax=axis
|
var normax=axis
|
||||||
if not normax.tryNormalize: #simplifies matrix computation below a lot
|
if not normax.tryNormalize: #simplifies matrix computation below a lot
|
||||||
raise newException(DivByZeroError,"Cannot rotate around zero length axis")
|
raise newException(DivByZeroError,"Cannot rotate around zero length axis")
|
||||||
|
|
||||||
let
|
let
|
||||||
u=normax.x
|
u=normax.x
|
||||||
v=normax.y
|
v=normax.y
|
||||||
|
|
@ -272,7 +272,7 @@ proc rotate*(angle:float,org:TPoint3d,axis:TVector3d):TMatrix3d {.noInit.}=
|
||||||
uvomc=normax.x*normax.y*omc
|
uvomc=normax.x*normax.y*omc
|
||||||
uwomc=normax.x*normax.z*omc
|
uwomc=normax.x*normax.z*omc
|
||||||
vwomc=normax.y*normax.z*omc
|
vwomc=normax.y*normax.z*omc
|
||||||
|
|
||||||
result.setElements(
|
result.setElements(
|
||||||
u2+(v2+w2)*cs, uvomc+wsi, uwomc-vsi, 0.0,
|
u2+(v2+w2)*cs, uvomc+wsi, uwomc-vsi, 0.0,
|
||||||
uvomc-wsi, v2+(u2+w2)*cs, vwomc+usi, 0.0,
|
uvomc-wsi, v2+(u2+w2)*cs, vwomc+usi, 0.0,
|
||||||
|
|
@ -305,7 +305,7 @@ proc rotateY*(angle:float):TMatrix3d {.noInit.}=
|
||||||
0,1,0,0,
|
0,1,0,0,
|
||||||
s,0,c,0,
|
s,0,c,0,
|
||||||
0,0,0,1)
|
0,0,0,1)
|
||||||
|
|
||||||
proc rotateZ*(angle:float):TMatrix3d {.noInit.}=
|
proc rotateZ*(angle:float):TMatrix3d {.noInit.}=
|
||||||
## Creates a matrix that rotates around the z-axis with `angle` radians,
|
## Creates a matrix that rotates around the z-axis with `angle` radians,
|
||||||
## which is also called a 'yaw' matrix.
|
## which is also called a 'yaw' matrix.
|
||||||
|
|
@ -317,19 +317,19 @@ proc rotateZ*(angle:float):TMatrix3d {.noInit.}=
|
||||||
-s,c,0,0,
|
-s,c,0,0,
|
||||||
0,0,1,0,
|
0,0,1,0,
|
||||||
0,0,0,1)
|
0,0,0,1)
|
||||||
|
|
||||||
proc isUniform*(m:TMatrix3d,tol=1.0e-6):bool=
|
proc isUniform*(m:TMatrix3d,tol=1.0e-6):bool=
|
||||||
## Checks if the transform is uniform, that is
|
## Checks if the transform is uniform, that is
|
||||||
## perpendicular axes of equal length, which means (for example)
|
## perpendicular axes of equal length, which means (for example)
|
||||||
## it cannot transform a sphere into an ellipsoid.
|
## it cannot transform a sphere into an ellipsoid.
|
||||||
## `tol` is used as tolerance for both equal length comparison
|
## `tol` is used as tolerance for both equal length comparison
|
||||||
## and perpendicular comparison.
|
## and perpendicular comparison.
|
||||||
|
|
||||||
#dot product=0 means perpendicular coord. system, check xaxis vs yaxis and xaxis vs zaxis
|
#dot product=0 means perpendicular coord. system, check xaxis vs yaxis and xaxis vs zaxis
|
||||||
if abs(m.ax*m.bx+m.ay*m.by+m.az*m.bz)<=tol and # x vs y
|
if abs(m.ax*m.bx+m.ay*m.by+m.az*m.bz)<=tol and # x vs y
|
||||||
abs(m.ax*m.cx+m.ay*m.cy+m.az*m.cz)<=tol and #x vs z
|
abs(m.ax*m.cx+m.ay*m.cy+m.az*m.cz)<=tol and #x vs z
|
||||||
abs(m.bx*m.cx+m.by*m.cy+m.bz*m.cz)<=tol: #y vs z
|
abs(m.bx*m.cx+m.by*m.cy+m.bz*m.cz)<=tol: #y vs z
|
||||||
|
|
||||||
#subtract squared lengths of axes to check if uniform scaling:
|
#subtract squared lengths of axes to check if uniform scaling:
|
||||||
let
|
let
|
||||||
sqxlen=(m.ax*m.ax+m.ay*m.ay+m.az*m.az)
|
sqxlen=(m.ax*m.ax+m.ay*m.ay+m.az*m.az)
|
||||||
|
|
@ -340,16 +340,16 @@ proc isUniform*(m:TMatrix3d,tol=1.0e-6):bool=
|
||||||
return false
|
return false
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
proc mirror*(planeperp:TVector3d):TMatrix3d {.noInit.}=
|
proc mirror*(planeperp:TVector3d):TMatrix3d {.noInit.}=
|
||||||
## Creates a matrix that mirrors over the plane that has `planeperp` as normal,
|
## Creates a matrix that mirrors over the plane that has `planeperp` as normal,
|
||||||
## and passes through origo. `planeperp` does not need to be normalized.
|
## and passes through origo. `planeperp` does not need to be normalized.
|
||||||
|
|
||||||
# https://en.wikipedia.org/wiki/Transformation_matrix
|
# https://en.wikipedia.org/wiki/Transformation_matrix
|
||||||
var n=planeperp
|
var n=planeperp
|
||||||
if not n.tryNormalize:
|
if not n.tryNormalize:
|
||||||
raise newException(DivByZeroError,"Cannot mirror over a plane with a zero length normal")
|
raise newException(DivByZeroError,"Cannot mirror over a plane with a zero length normal")
|
||||||
|
|
||||||
let
|
let
|
||||||
a=n.x
|
a=n.x
|
||||||
b=n.y
|
b=n.y
|
||||||
|
|
@ -357,7 +357,7 @@ proc mirror*(planeperp:TVector3d):TMatrix3d {.noInit.}=
|
||||||
ab=a*b
|
ab=a*b
|
||||||
ac=a*c
|
ac=a*c
|
||||||
bc=b*c
|
bc=b*c
|
||||||
|
|
||||||
result.setElements(
|
result.setElements(
|
||||||
1-2*a*a , -2*ab,-2*ac,0,
|
1-2*a*a , -2*ab,-2*ac,0,
|
||||||
-2*ab , 1-2*b*b, -2*bc, 0,
|
-2*ab , 1-2*b*b, -2*bc, 0,
|
||||||
|
|
@ -376,7 +376,7 @@ proc mirror*(org:TPoint3d,planeperp:TVector3d):TMatrix3d {.noInit.}=
|
||||||
var n=planeperp
|
var n=planeperp
|
||||||
if not n.tryNormalize:
|
if not n.tryNormalize:
|
||||||
raise newException(DivByZeroError,"Cannot mirror over a plane with a zero length normal")
|
raise newException(DivByZeroError,"Cannot mirror over a plane with a zero length normal")
|
||||||
|
|
||||||
let
|
let
|
||||||
a=n.x
|
a=n.x
|
||||||
b=n.y
|
b=n.y
|
||||||
|
|
@ -390,7 +390,7 @@ proc mirror*(org:TPoint3d,planeperp:TVector3d):TMatrix3d {.noInit.}=
|
||||||
tx=org.x
|
tx=org.x
|
||||||
ty=org.y
|
ty=org.y
|
||||||
tz=org.z
|
tz=org.z
|
||||||
|
|
||||||
result.setElements(
|
result.setElements(
|
||||||
1-2*aa , -2*ab,-2*ac,0,
|
1-2*aa , -2*ab,-2*ac,0,
|
||||||
-2*ab , 1-2*bb, -2*bc, 0,
|
-2*ab , 1-2*bb, -2*bc, 0,
|
||||||
|
|
@ -402,8 +402,8 @@ proc mirror*(org:TPoint3d,planeperp:TVector3d):TMatrix3d {.noInit.}=
|
||||||
|
|
||||||
proc determinant*(m:TMatrix3d):float=
|
proc determinant*(m:TMatrix3d):float=
|
||||||
## Computes the determinant of matrix `m`.
|
## Computes the determinant of matrix `m`.
|
||||||
|
|
||||||
# This computation is gotten from ratsimp(optimize(determinant(m)))
|
# This computation is gotten from ratsimp(optimize(determinant(m)))
|
||||||
# in maxima CAS
|
# in maxima CAS
|
||||||
let
|
let
|
||||||
O1=m.cx*m.tw-m.cw*m.tx
|
O1=m.cx*m.tw-m.cw*m.tx
|
||||||
|
|
@ -423,10 +423,10 @@ proc inverse*(m:TMatrix3d):TMatrix3d {.noInit.}=
|
||||||
## Computes the inverse of matrix `m`. If the matrix
|
## Computes the inverse of matrix `m`. If the matrix
|
||||||
## determinant is zero, thus not invertible, a EDivByZero
|
## determinant is zero, thus not invertible, a EDivByZero
|
||||||
## will be raised.
|
## will be raised.
|
||||||
|
|
||||||
# this computation comes from optimize(invert(m)) in maxima CAS
|
# this computation comes from optimize(invert(m)) in maxima CAS
|
||||||
|
|
||||||
let
|
let
|
||||||
det=m.determinant
|
det=m.determinant
|
||||||
O2=m.cy*m.tw-m.cw*m.ty
|
O2=m.cy*m.tw-m.cw*m.ty
|
||||||
O3=m.cz*m.tw-m.cw*m.tz
|
O3=m.cz*m.tw-m.cw*m.tz
|
||||||
|
|
@ -464,7 +464,7 @@ proc inverse*(m:TMatrix3d):TMatrix3d {.noInit.}=
|
||||||
proc equals*(m1:TMatrix3d,m2:TMatrix3d,tol=1.0e-6):bool=
|
proc equals*(m1:TMatrix3d,m2:TMatrix3d,tol=1.0e-6):bool=
|
||||||
## Checks if all elements of `m1`and `m2` is equal within
|
## Checks if all elements of `m1`and `m2` is equal within
|
||||||
## a given tolerance `tol`.
|
## a given tolerance `tol`.
|
||||||
return
|
return
|
||||||
abs(m1.ax-m2.ax)<=tol and
|
abs(m1.ax-m2.ax)<=tol and
|
||||||
abs(m1.ay-m2.ay)<=tol and
|
abs(m1.ay-m2.ay)<=tol and
|
||||||
abs(m1.az-m2.az)<=tol and
|
abs(m1.az-m2.az)<=tol and
|
||||||
|
|
@ -486,11 +486,11 @@ proc `=~`*(m1,m2:TMatrix3d):bool=
|
||||||
## Checks if `m1` and `m2` is approximately equal, using a
|
## Checks if `m1` and `m2` is approximately equal, using a
|
||||||
## tolerance of 1e-6.
|
## tolerance of 1e-6.
|
||||||
equals(m1,m2)
|
equals(m1,m2)
|
||||||
|
|
||||||
proc transpose*(m:TMatrix3d):TMatrix3d {.noInit.}=
|
proc transpose*(m:TMatrix3d):TMatrix3d {.noInit.}=
|
||||||
## Returns the transpose of `m`
|
## Returns the transpose of `m`
|
||||||
result.setElements(m.ax,m.bx,m.cx,m.tx,m.ay,m.by,m.cy,m.ty,m.az,m.bz,m.cz,m.tz,m.aw,m.bw,m.cw,m.tw)
|
result.setElements(m.ax,m.bx,m.cx,m.tx,m.ay,m.by,m.cy,m.ty,m.az,m.bz,m.cz,m.tz,m.aw,m.bw,m.cw,m.tw)
|
||||||
|
|
||||||
proc getXAxis*(m:TMatrix3d):TVector3d {.noInit.}=
|
proc getXAxis*(m:TMatrix3d):TVector3d {.noInit.}=
|
||||||
## Gets the local x axis of `m`
|
## Gets the local x axis of `m`
|
||||||
result.x=m.ax
|
result.x=m.ax
|
||||||
|
|
@ -509,26 +509,26 @@ proc getZAxis*(m:TMatrix3d):TVector3d {.noInit.}=
|
||||||
result.y=m.cy
|
result.y=m.cy
|
||||||
result.z=m.cz
|
result.z=m.cz
|
||||||
|
|
||||||
|
|
||||||
proc `$`*(m:TMatrix3d):string=
|
proc `$`*(m:TMatrix3d):string=
|
||||||
## String representation of `m`
|
## String representation of `m`
|
||||||
return rtos(m.ax) & "," & rtos(m.ay) & "," &rtos(m.az) & "," & rtos(m.aw) &
|
return rtos(m.ax) & "," & rtos(m.ay) & "," & rtos(m.az) & "," & rtos(m.aw) &
|
||||||
"\n" & rtos(m.bx) & "," & rtos(m.by) & "," &rtos(m.bz) & "," & rtos(m.bw) &
|
"\n" & rtos(m.bx) & "," & rtos(m.by) & "," & rtos(m.bz) & "," & rtos(m.bw) &
|
||||||
"\n" & rtos(m.cx) & "," & rtos(m.cy) & "," &rtos(m.cz) & "," & rtos(m.cw) &
|
"\n" & rtos(m.cx) & "," & rtos(m.cy) & "," & rtos(m.cz) & "," & rtos(m.cw) &
|
||||||
"\n" & rtos(m.tx) & "," & rtos(m.ty) & "," &rtos(m.tz) & "," & rtos(m.tw)
|
"\n" & rtos(m.tx) & "," & rtos(m.ty) & "," & rtos(m.tz) & "," & rtos(m.tw)
|
||||||
|
|
||||||
proc apply*(m:TMatrix3d, x,y,z:var float, translate=false)=
|
proc apply*(m:TMatrix3d, x,y,z:var float, translate=false)=
|
||||||
## Applies transformation `m` onto `x` , `y` , `z` , optionally
|
## Applies transformation `m` onto `x` , `y` , `z` , optionally
|
||||||
## using the translation part of the matrix.
|
## using the translation part of the matrix.
|
||||||
let
|
let
|
||||||
oldx=x
|
oldx=x
|
||||||
oldy=y
|
oldy=y
|
||||||
oldz=z
|
oldz=z
|
||||||
|
|
||||||
x=m.cx*oldz+m.bx*oldy+m.ax*oldx
|
x=m.cx*oldz+m.bx*oldy+m.ax*oldx
|
||||||
y=m.cy*oldz+m.by*oldy+m.ay*oldx
|
y=m.cy*oldz+m.by*oldy+m.ay*oldx
|
||||||
z=m.cz*oldz+m.bz*oldy+m.az*oldx
|
z=m.cz*oldz+m.bz*oldy+m.az*oldx
|
||||||
|
|
||||||
if translate:
|
if translate:
|
||||||
x+=m.tx
|
x+=m.tx
|
||||||
y+=m.ty
|
y+=m.ty
|
||||||
|
|
@ -552,13 +552,13 @@ proc `len=`*(v:var TVector3d,newlen:float) {.noInit.} =
|
||||||
## an arbitrary vector of the requested length is returned.
|
## an arbitrary vector of the requested length is returned.
|
||||||
|
|
||||||
let fac=newlen/v.len
|
let fac=newlen/v.len
|
||||||
|
|
||||||
if newlen==0.0:
|
if newlen==0.0:
|
||||||
v.x=0.0
|
v.x=0.0
|
||||||
v.y=0.0
|
v.y=0.0
|
||||||
v.z=0.0
|
v.z=0.0
|
||||||
return
|
return
|
||||||
|
|
||||||
if fac==Inf or fac==NegInf:
|
if fac==Inf or fac==NegInf:
|
||||||
#to short for float accuracy
|
#to short for float accuracy
|
||||||
#do as good as possible:
|
#do as good as possible:
|
||||||
|
|
@ -588,7 +588,7 @@ proc `&` *(v:TVector3d,m:TMatrix3d):TVector3d {.noInit.} =
|
||||||
## Concatenate vector `v` with a transformation matrix.
|
## Concatenate vector `v` with a transformation matrix.
|
||||||
## Transforming a vector ignores the translational part
|
## Transforming a vector ignores the translational part
|
||||||
## of the matrix.
|
## of the matrix.
|
||||||
|
|
||||||
# | AX AY AZ AW |
|
# | AX AY AZ AW |
|
||||||
# | X Y Z 1 | * | BX BY BZ BW |
|
# | X Y Z 1 | * | BX BY BZ BW |
|
||||||
# | CX CY CZ CW |
|
# | CX CY CZ CW |
|
||||||
|
|
@ -605,12 +605,12 @@ proc `&=` *(v:var TVector3d,m:TMatrix3d) {.noInit.} =
|
||||||
## Applies transformation `m` onto `v` in place.
|
## Applies transformation `m` onto `v` in place.
|
||||||
## Transforming a vector ignores the translational part
|
## Transforming a vector ignores the translational part
|
||||||
## of the matrix.
|
## of the matrix.
|
||||||
|
|
||||||
# | AX AY AZ AW |
|
# | AX AY AZ AW |
|
||||||
# | X Y Z 1 | * | BX BY BZ BW |
|
# | X Y Z 1 | * | BX BY BZ BW |
|
||||||
# | CX CY CZ CW |
|
# | CX CY CZ CW |
|
||||||
# | 0 0 0 1 |
|
# | 0 0 0 1 |
|
||||||
|
|
||||||
let
|
let
|
||||||
newx=m.cx*v.z+m.bx*v.y+m.ax*v.x
|
newx=m.cx*v.z+m.bx*v.y+m.ax*v.x
|
||||||
newy=m.cy*v.z+m.by*v.y+m.ay*v.x
|
newy=m.cy*v.z+m.by*v.y+m.ay*v.x
|
||||||
|
|
@ -620,38 +620,38 @@ proc `&=` *(v:var TVector3d,m:TMatrix3d) {.noInit.} =
|
||||||
|
|
||||||
proc transformNorm*(v:var TVector3d,m:TMatrix3d)=
|
proc transformNorm*(v:var TVector3d,m:TMatrix3d)=
|
||||||
## Applies a normal direction transformation `m` onto `v` in place.
|
## Applies a normal direction transformation `m` onto `v` in place.
|
||||||
## The resulting vector is *not* normalized. Transforming a vector ignores the
|
## The resulting vector is *not* normalized. Transforming a vector ignores the
|
||||||
## translational part of the matrix. If the matrix is not invertible
|
## translational part of the matrix. If the matrix is not invertible
|
||||||
## (determinant=0), an EDivByZero will be raised.
|
## (determinant=0), an EDivByZero will be raised.
|
||||||
|
|
||||||
# transforming a normal is done by transforming
|
# transforming a normal is done by transforming
|
||||||
# by the transpose of the inverse of the original matrix
|
# by the transpose of the inverse of the original matrix
|
||||||
|
|
||||||
# Major reason this simple function is here is that this function can be optimized in the future,
|
# Major reason this simple function is here is that this function can be optimized in the future,
|
||||||
# (possibly by hardware) as well as having a consistent API with the 2d version.
|
# (possibly by hardware) as well as having a consistent API with the 2d version.
|
||||||
v&=transpose(inverse(m))
|
v&=transpose(inverse(m))
|
||||||
|
|
||||||
proc transformInv*(v:var TVector3d,m:TMatrix3d)=
|
proc transformInv*(v:var TVector3d,m:TMatrix3d)=
|
||||||
## Applies the inverse of `m` on vector `v`. Transforming a vector ignores
|
## Applies the inverse of `m` on vector `v`. Transforming a vector ignores
|
||||||
## the translational part of the matrix. Transforming a vector ignores the
|
## the translational part of the matrix. Transforming a vector ignores the
|
||||||
## translational part of the matrix.
|
## translational part of the matrix.
|
||||||
## If the matrix is not invertible (determinant=0), an EDivByZero
|
## If the matrix is not invertible (determinant=0), an EDivByZero
|
||||||
## will be raised.
|
## will be raised.
|
||||||
|
|
||||||
# Major reason this simple function is here is that this function can be optimized in the future,
|
# Major reason this simple function is here is that this function can be optimized in the future,
|
||||||
# (possibly by hardware) as well as having a consistent API with the 2d version.
|
# (possibly by hardware) as well as having a consistent API with the 2d version.
|
||||||
v&=m.inverse
|
v&=m.inverse
|
||||||
|
|
||||||
proc transformNormInv*(vec:var TVector3d,m:TMatrix3d)=
|
proc transformNormInv*(vec:var TVector3d,m:TMatrix3d)=
|
||||||
## Applies an inverse normal direction transformation `m` onto `v` in place.
|
## Applies an inverse normal direction transformation `m` onto `v` in place.
|
||||||
## This is faster than creating an inverse
|
## This is faster than creating an inverse
|
||||||
## matrix and transformNorm(...) it. Transforming a vector ignores the
|
## matrix and transformNorm(...) it. Transforming a vector ignores the
|
||||||
## translational part of the matrix.
|
## translational part of the matrix.
|
||||||
|
|
||||||
# see vector2d:s equivalent for a deeper look how/why this works
|
# see vector2d:s equivalent for a deeper look how/why this works
|
||||||
vec&=m.transpose
|
vec&=m.transpose
|
||||||
|
|
||||||
proc tryNormalize*(v:var TVector3d):bool=
|
proc tryNormalize*(v:var TVector3d):bool=
|
||||||
## Modifies `v` to have a length of 1.0, keeping its angle.
|
## Modifies `v` to have a length of 1.0, keeping its angle.
|
||||||
## If `v` has zero length (and thus no angle), it is left unmodified and false is
|
## If `v` has zero length (and thus no angle), it is left unmodified and false is
|
||||||
## returned, otherwise true is returned.
|
## returned, otherwise true is returned.
|
||||||
|
|
@ -663,26 +663,26 @@ proc tryNormalize*(v:var TVector3d):bool=
|
||||||
v.x/=mag
|
v.x/=mag
|
||||||
v.y/=mag
|
v.y/=mag
|
||||||
v.z/=mag
|
v.z/=mag
|
||||||
|
|
||||||
return true
|
return true
|
||||||
|
|
||||||
proc normalize*(v:var TVector3d) {.inline.}=
|
proc normalize*(v:var TVector3d) {.inline.}=
|
||||||
## Modifies `v` to have a length of 1.0, keeping its angle.
|
## Modifies `v` to have a length of 1.0, keeping its angle.
|
||||||
## If `v` has zero length, an EDivByZero will be raised.
|
## If `v` has zero length, an EDivByZero will be raised.
|
||||||
if not tryNormalize(v):
|
if not tryNormalize(v):
|
||||||
raise newException(DivByZeroError,"Cannot normalize zero length vector")
|
raise newException(DivByZeroError,"Cannot normalize zero length vector")
|
||||||
|
|
||||||
proc rotate*(vec:var TVector3d,angle:float,axis:TVector3d)=
|
proc rotate*(vec:var TVector3d,angle:float,axis:TVector3d)=
|
||||||
## Rotates `vec` in place, with `angle` radians over `axis`, which passes
|
## Rotates `vec` in place, with `angle` radians over `axis`, which passes
|
||||||
## through origo.
|
## through origo.
|
||||||
|
|
||||||
# see PDF document http://inside.mines.edu/~gmurray/ArbitraryAxisRotation/ArbitraryAxisRotation.pdf
|
# see PDF document http://inside.mines.edu/~gmurray/ArbitraryAxisRotation/ArbitraryAxisRotation.pdf
|
||||||
# for how this is computed
|
# for how this is computed
|
||||||
|
|
||||||
var normax=axis
|
var normax=axis
|
||||||
if not normax.tryNormalize:
|
if not normax.tryNormalize:
|
||||||
raise newException(DivByZeroError,"Cannot rotate around zero length axis")
|
raise newException(DivByZeroError,"Cannot rotate around zero length axis")
|
||||||
|
|
||||||
let
|
let
|
||||||
cs=cos(angle)
|
cs=cos(angle)
|
||||||
si=sin(angle)
|
si=sin(angle)
|
||||||
|
|
@ -694,11 +694,11 @@ proc rotate*(vec:var TVector3d,angle:float,axis:TVector3d)=
|
||||||
y=vec.y
|
y=vec.y
|
||||||
z=vec.z
|
z=vec.z
|
||||||
uxyzomc=(u*x+v*y+w*z)*omc
|
uxyzomc=(u*x+v*y+w*z)*omc
|
||||||
|
|
||||||
vec.x=u*uxyzomc+x*cs+(v*z-w*y)*si
|
vec.x=u*uxyzomc+x*cs+(v*z-w*y)*si
|
||||||
vec.y=v*uxyzomc+y*cs+(w*x-u*z)*si
|
vec.y=v*uxyzomc+y*cs+(w*x-u*z)*si
|
||||||
vec.z=w*uxyzomc+z*cs+(u*y-v*x)*si
|
vec.z=w*uxyzomc+z*cs+(u*y-v*x)*si
|
||||||
|
|
||||||
proc scale*(v:var TVector3d,s:float)=
|
proc scale*(v:var TVector3d,s:float)=
|
||||||
## Scales the vector in place with factor `s`
|
## Scales the vector in place with factor `s`
|
||||||
v.x*=s
|
v.x*=s
|
||||||
|
|
@ -713,12 +713,12 @@ proc stretch*(v:var TVector3d,sx,sy,sz:float)=
|
||||||
|
|
||||||
proc mirror*(v:var TVector3d,planeperp:TVector3d)=
|
proc mirror*(v:var TVector3d,planeperp:TVector3d)=
|
||||||
## Computes the mirrored vector of `v` over the plane
|
## Computes the mirrored vector of `v` over the plane
|
||||||
## that has `planeperp` as normal direction.
|
## that has `planeperp` as normal direction.
|
||||||
## `planeperp` does not need to be normalized.
|
## `planeperp` does not need to be normalized.
|
||||||
|
|
||||||
var n=planeperp
|
var n=planeperp
|
||||||
n.normalize
|
n.normalize
|
||||||
|
|
||||||
let
|
let
|
||||||
x=v.x
|
x=v.x
|
||||||
y=v.y
|
y=v.y
|
||||||
|
|
@ -729,7 +729,7 @@ proc mirror*(v:var TVector3d,planeperp:TVector3d)=
|
||||||
ac=a*c
|
ac=a*c
|
||||||
ab=a*b
|
ab=a*b
|
||||||
bc=b*c
|
bc=b*c
|
||||||
|
|
||||||
v.x= -2*(ac*z+ab*y+a*a*x)+x
|
v.x= -2*(ac*z+ab*y+a*a*x)+x
|
||||||
v.y= -2*(bc*z+b*b*y+ab*x)+y
|
v.y= -2*(bc*z+b*b*y+ab*x)+y
|
||||||
v.z= -2*(c*c*z+bc*y+ac*x)+z
|
v.z= -2*(c*c*z+bc*y+ac*x)+z
|
||||||
|
|
@ -740,7 +740,7 @@ proc `-` *(v:TVector3d):TVector3d=
|
||||||
result.x= -v.x
|
result.x= -v.x
|
||||||
result.y= -v.y
|
result.y= -v.y
|
||||||
result.z= -v.z
|
result.z= -v.z
|
||||||
|
|
||||||
# declare templated binary operators
|
# declare templated binary operators
|
||||||
makeBinOpVector(`+`)
|
makeBinOpVector(`+`)
|
||||||
makeBinOpVector(`-`)
|
makeBinOpVector(`-`)
|
||||||
|
|
@ -752,7 +752,7 @@ makeBinOpAssignVector(`*=`)
|
||||||
makeBinOpAssignVector(`/=`)
|
makeBinOpAssignVector(`/=`)
|
||||||
|
|
||||||
proc dot*(v1,v2:TVector3d):float {.inline.}=
|
proc dot*(v1,v2:TVector3d):float {.inline.}=
|
||||||
## Computes the dot product of two vectors.
|
## Computes the dot product of two vectors.
|
||||||
## Returns 0.0 if the vectors are perpendicular.
|
## Returns 0.0 if the vectors are perpendicular.
|
||||||
return v1.x*v2.x+v1.y*v2.y+v1.z*v2.z
|
return v1.x*v2.x+v1.y*v2.y+v1.z*v2.z
|
||||||
|
|
||||||
|
|
@ -769,12 +769,12 @@ proc cross*(v1,v2:TVector3d):TVector3d {.inline.}=
|
||||||
proc equals*(v1,v2:TVector3d,tol=1.0e-6):bool=
|
proc equals*(v1,v2:TVector3d,tol=1.0e-6):bool=
|
||||||
## Checks if two vectors approximately equals with a tolerance.
|
## Checks if two vectors approximately equals with a tolerance.
|
||||||
return abs(v2.x-v1.x)<=tol and abs(v2.y-v1.y)<=tol and abs(v2.z-v1.z)<=tol
|
return abs(v2.x-v1.x)<=tol and abs(v2.y-v1.y)<=tol and abs(v2.z-v1.z)<=tol
|
||||||
|
|
||||||
proc `=~` *(v1,v2:TVector3d):bool=
|
proc `=~` *(v1,v2:TVector3d):bool=
|
||||||
## Checks if two vectors approximately equals with a
|
## Checks if two vectors approximately equals with a
|
||||||
## hardcoded tolerance 1e-6
|
## hardcoded tolerance 1e-6
|
||||||
equals(v1,v2)
|
equals(v1,v2)
|
||||||
|
|
||||||
proc angleTo*(v1,v2:TVector3d):float=
|
proc angleTo*(v1,v2:TVector3d):float=
|
||||||
## Returns the smallest angle between v1 and v2,
|
## Returns the smallest angle between v1 and v2,
|
||||||
## which is in range 0-PI
|
## which is in range 0-PI
|
||||||
|
|
@ -801,7 +801,7 @@ proc arbitraryAxis*(norm:TVector3d):TMatrix3d {.noInit.}=
|
||||||
ay=cross(norm,ax)
|
ay=cross(norm,ax)
|
||||||
ay.normalize()
|
ay.normalize()
|
||||||
az=cross(ax,ay)
|
az=cross(ax,ay)
|
||||||
|
|
||||||
result.setElements(
|
result.setElements(
|
||||||
ax.x,ax.y,ax.z,0.0,
|
ax.x,ax.y,ax.z,0.0,
|
||||||
ay.x,ay.y,ay.z,0.0,
|
ay.x,ay.y,ay.z,0.0,
|
||||||
|
|
@ -811,20 +811,20 @@ proc arbitraryAxis*(norm:TVector3d):TMatrix3d {.noInit.}=
|
||||||
proc bisect*(v1,v2:TVector3d):TVector3d {.noInit.}=
|
proc bisect*(v1,v2:TVector3d):TVector3d {.noInit.}=
|
||||||
## Computes the bisector between v1 and v2 as a normalized vector.
|
## Computes the bisector between v1 and v2 as a normalized vector.
|
||||||
## If one of the input vectors has zero length, a normalized version
|
## If one of the input vectors has zero length, a normalized version
|
||||||
## of the other is returned. If both input vectors has zero length,
|
## of the other is returned. If both input vectors has zero length,
|
||||||
## an arbitrary normalized vector `v1` is returned.
|
## an arbitrary normalized vector `v1` is returned.
|
||||||
var
|
var
|
||||||
vmag1=v1.len
|
vmag1=v1.len
|
||||||
vmag2=v2.len
|
vmag2=v2.len
|
||||||
|
|
||||||
# zero length vector equals arbitrary vector, just change
|
# zero length vector equals arbitrary vector, just change
|
||||||
# magnitude to one to avoid zero division
|
# magnitude to one to avoid zero division
|
||||||
if vmag1==0.0:
|
if vmag1==0.0:
|
||||||
if vmag2==0: #both are zero length return any normalized vector
|
if vmag2==0: #both are zero length return any normalized vector
|
||||||
return XAXIS
|
return XAXIS
|
||||||
vmag1=1.0
|
vmag1=1.0
|
||||||
if vmag2==0.0: vmag2=1.0
|
if vmag2==0.0: vmag2=1.0
|
||||||
|
|
||||||
let
|
let
|
||||||
x1=v1.x/vmag1
|
x1=v1.x/vmag1
|
||||||
y1=v1.y/vmag1
|
y1=v1.y/vmag1
|
||||||
|
|
@ -832,14 +832,14 @@ proc bisect*(v1,v2:TVector3d):TVector3d {.noInit.}=
|
||||||
x2=v2.x/vmag2
|
x2=v2.x/vmag2
|
||||||
y2=v2.y/vmag2
|
y2=v2.y/vmag2
|
||||||
z2=v2.z/vmag2
|
z2=v2.z/vmag2
|
||||||
|
|
||||||
result.x=(x1 + x2) * 0.5
|
result.x=(x1 + x2) * 0.5
|
||||||
result.y=(y1 + y2) * 0.5
|
result.y=(y1 + y2) * 0.5
|
||||||
result.z=(z1 + z2) * 0.5
|
result.z=(z1 + z2) * 0.5
|
||||||
|
|
||||||
if not result.tryNormalize():
|
if not result.tryNormalize():
|
||||||
# This can happen if vectors are colinear. In this special case
|
# This can happen if vectors are colinear. In this special case
|
||||||
# there are actually inifinitely many bisectors, we select just
|
# there are actually inifinitely many bisectors, we select just
|
||||||
# one of them.
|
# one of them.
|
||||||
result=v1.cross(XAXIS)
|
result=v1.cross(XAXIS)
|
||||||
if result.sqrLen<1.0e-9:
|
if result.sqrLen<1.0e-9:
|
||||||
|
|
@ -857,14 +857,14 @@ proc point3d*(x,y,z:float):TPoint3d=
|
||||||
result.x=x
|
result.x=x
|
||||||
result.y=y
|
result.y=y
|
||||||
result.z=z
|
result.z=z
|
||||||
|
|
||||||
proc sqrDist*(a,b:TPoint3d):float=
|
proc sqrDist*(a,b:TPoint3d):float=
|
||||||
## Computes the squared distance between `a`and `b`
|
## Computes the squared distance between `a`and `b`
|
||||||
let dx=b.x-a.x
|
let dx=b.x-a.x
|
||||||
let dy=b.y-a.y
|
let dy=b.y-a.y
|
||||||
let dz=b.z-a.z
|
let dz=b.z-a.z
|
||||||
result=dx*dx+dy*dy+dz*dz
|
result=dx*dx+dy*dy+dz*dz
|
||||||
|
|
||||||
proc dist*(a,b:TPoint3d):float {.inline.}=
|
proc dist*(a,b:TPoint3d):float {.inline.}=
|
||||||
## Computes the absolute distance between `a`and `b`
|
## Computes the absolute distance between `a`and `b`
|
||||||
result=sqrt(sqrDist(a,b))
|
result=sqrt(sqrDist(a,b))
|
||||||
|
|
@ -876,7 +876,7 @@ proc `$` *(p:TPoint3d):string=
|
||||||
result.add(rtos(p.y))
|
result.add(rtos(p.y))
|
||||||
result.add(",")
|
result.add(",")
|
||||||
result.add(rtos(p.z))
|
result.add(rtos(p.z))
|
||||||
|
|
||||||
proc `&`*(p:TPoint3d,m:TMatrix3d):TPoint3d=
|
proc `&`*(p:TPoint3d,m:TMatrix3d):TPoint3d=
|
||||||
## Concatenates a point `p` with a transform `m`,
|
## Concatenates a point `p` with a transform `m`,
|
||||||
## resulting in a new, transformed point.
|
## resulting in a new, transformed point.
|
||||||
|
|
@ -893,18 +893,18 @@ proc `&=` *(p:var TPoint3d,m:TMatrix3d)=
|
||||||
p.x=m.cx*z+m.bx*y+m.ax*x+m.tx
|
p.x=m.cx*z+m.bx*y+m.ax*x+m.tx
|
||||||
p.y=m.cy*z+m.by*y+m.ay*x+m.ty
|
p.y=m.cy*z+m.by*y+m.ay*x+m.ty
|
||||||
p.z=m.cz*z+m.bz*y+m.az*x+m.tz
|
p.z=m.cz*z+m.bz*y+m.az*x+m.tz
|
||||||
|
|
||||||
proc transformInv*(p:var TPoint3d,m:TMatrix3d)=
|
proc transformInv*(p:var TPoint3d,m:TMatrix3d)=
|
||||||
## Applies the inverse of transformation `m` onto `p` in place.
|
## Applies the inverse of transformation `m` onto `p` in place.
|
||||||
## If the matrix is not invertable (determinant=0) , EDivByZero will
|
## If the matrix is not invertable (determinant=0) , EDivByZero will
|
||||||
## be raised.
|
## be raised.
|
||||||
|
|
||||||
# can possibly be more optimized in the future so use this function when possible
|
# can possibly be more optimized in the future so use this function when possible
|
||||||
p&=inverse(m)
|
p&=inverse(m)
|
||||||
|
|
||||||
|
|
||||||
proc `+`*(p:TPoint3d,v:TVector3d):TPoint3d {.noInit,inline.} =
|
proc `+`*(p:TPoint3d,v:TVector3d):TPoint3d {.noInit,inline.} =
|
||||||
## Adds a vector `v` to a point `p`, resulting
|
## Adds a vector `v` to a point `p`, resulting
|
||||||
## in a new point.
|
## in a new point.
|
||||||
result.x=p.x+v.x
|
result.x=p.x+v.x
|
||||||
result.y=p.y+v.y
|
result.y=p.y+v.y
|
||||||
|
|
@ -917,7 +917,7 @@ proc `+=`*(p:var TPoint3d,v:TVector3d) {.noInit,inline.} =
|
||||||
p.z+=v.z
|
p.z+=v.z
|
||||||
|
|
||||||
proc `-`*(p:TPoint3d,v:TVector3d):TPoint3d {.noInit,inline.} =
|
proc `-`*(p:TPoint3d,v:TVector3d):TPoint3d {.noInit,inline.} =
|
||||||
## Subtracts a vector `v` from a point `p`, resulting
|
## Subtracts a vector `v` from a point `p`, resulting
|
||||||
## in a new point.
|
## in a new point.
|
||||||
result.x=p.x-v.x
|
result.x=p.x-v.x
|
||||||
result.y=p.y-v.y
|
result.y=p.y-v.y
|
||||||
|
|
@ -933,37 +933,37 @@ proc `-=`*(p:var TPoint3d,v:TVector3d) {.noInit,inline.} =
|
||||||
## Subtracts a vector `v` from a point `p` in place.
|
## Subtracts a vector `v` from a point `p` in place.
|
||||||
p.x-=v.x
|
p.x-=v.x
|
||||||
p.y-=v.y
|
p.y-=v.y
|
||||||
p.z-=v.z
|
p.z-=v.z
|
||||||
|
|
||||||
proc equals(p1,p2:TPoint3d,tol=1.0e-6):bool {.inline.}=
|
proc equals(p1,p2:TPoint3d,tol=1.0e-6):bool {.inline.}=
|
||||||
## Checks if two points approximately equals with a tolerance.
|
## Checks if two points approximately equals with a tolerance.
|
||||||
return abs(p2.x-p1.x)<=tol and abs(p2.y-p1.y)<=tol and abs(p2.z-p1.z)<=tol
|
return abs(p2.x-p1.x)<=tol and abs(p2.y-p1.y)<=tol and abs(p2.z-p1.z)<=tol
|
||||||
|
|
||||||
proc `=~`*(p1,p2:TPoint3d):bool {.inline.}=
|
proc `=~`*(p1,p2:TPoint3d):bool {.inline.}=
|
||||||
## Checks if two vectors approximately equals with a
|
## Checks if two vectors approximately equals with a
|
||||||
## hardcoded tolerance 1e-6
|
## hardcoded tolerance 1e-6
|
||||||
equals(p1,p2)
|
equals(p1,p2)
|
||||||
|
|
||||||
proc rotate*(p:var TPoint3d,rad:float,axis:TVector3d)=
|
proc rotate*(p:var TPoint3d,rad:float,axis:TVector3d)=
|
||||||
## Rotates point `p` in place `rad` radians about an axis
|
## Rotates point `p` in place `rad` radians about an axis
|
||||||
## passing through origo.
|
## passing through origo.
|
||||||
|
|
||||||
var v=vector3d(p.x,p.y,p.z)
|
var v=vector3d(p.x,p.y,p.z)
|
||||||
v.rotate(rad,axis) # reuse this code here since doing the same thing and quite complicated
|
v.rotate(rad,axis) # reuse this code here since doing the same thing and quite complicated
|
||||||
p.x=v.x
|
p.x=v.x
|
||||||
p.y=v.y
|
p.y=v.y
|
||||||
p.z=v.z
|
p.z=v.z
|
||||||
|
|
||||||
proc rotate*(p:var TPoint3d,angle:float,org:TPoint3d,axis:TVector3d)=
|
proc rotate*(p:var TPoint3d,angle:float,org:TPoint3d,axis:TVector3d)=
|
||||||
## Rotates point `p` in place `rad` radians about an axis
|
## Rotates point `p` in place `rad` radians about an axis
|
||||||
## passing through `org`
|
## passing through `org`
|
||||||
|
|
||||||
# see PDF document http://inside.mines.edu/~gmurray/ArbitraryAxisRotation/ArbitraryAxisRotation.pdf
|
# see PDF document http://inside.mines.edu/~gmurray/ArbitraryAxisRotation/ArbitraryAxisRotation.pdf
|
||||||
# for how this is computed
|
# for how this is computed
|
||||||
|
|
||||||
var normax=axis
|
var normax=axis
|
||||||
normax.normalize
|
normax.normalize
|
||||||
|
|
||||||
let
|
let
|
||||||
cs=cos(angle)
|
cs=cos(angle)
|
||||||
omc=1.0-cs
|
omc=1.0-cs
|
||||||
|
|
@ -987,17 +987,17 @@ proc rotate*(p:var TPoint3d,angle:float,org:TPoint3d,axis:TVector3d)=
|
||||||
bv=b*v
|
bv=b*v
|
||||||
cw=c*w
|
cw=c*w
|
||||||
uxmvymwz=ux-vy-wz
|
uxmvymwz=ux-vy-wz
|
||||||
|
|
||||||
p.x=(a*(vv+ww)-u*(bv+cw-uxmvymwz))*omc + x*cs + (b*w+v*z-c*v-w*y)*si
|
p.x=(a*(vv+ww)-u*(bv+cw-uxmvymwz))*omc + x*cs + (b*w+v*z-c*v-w*y)*si
|
||||||
p.y=(b*(uu+ww)-v*(au+cw-uxmvymwz))*omc + y*cs + (c*u-a*w+w*x-u*z)*si
|
p.y=(b*(uu+ww)-v*(au+cw-uxmvymwz))*omc + y*cs + (c*u-a*w+w*x-u*z)*si
|
||||||
p.z=(c*(uu+vv)-w*(au+bv-uxmvymwz))*omc + z*cs + (a*v+u*y-b*u-v*x)*si
|
p.z=(c*(uu+vv)-w*(au+bv-uxmvymwz))*omc + z*cs + (a*v+u*y-b*u-v*x)*si
|
||||||
|
|
||||||
proc scale*(p:var TPoint3d,fac:float) {.inline.}=
|
proc scale*(p:var TPoint3d,fac:float) {.inline.}=
|
||||||
## Scales a point in place `fac` times with world origo as origin.
|
## Scales a point in place `fac` times with world origo as origin.
|
||||||
p.x*=fac
|
p.x*=fac
|
||||||
p.y*=fac
|
p.y*=fac
|
||||||
p.z*=fac
|
p.z*=fac
|
||||||
|
|
||||||
proc scale*(p:var TPoint3d,fac:float,org:TPoint3d){.inline.}=
|
proc scale*(p:var TPoint3d,fac:float,org:TPoint3d){.inline.}=
|
||||||
## Scales the point in place `fac` times with `org` as origin.
|
## Scales the point in place `fac` times with `org` as origin.
|
||||||
p.x=(p.x - org.x) * fac + org.x
|
p.x=(p.x - org.x) * fac + org.x
|
||||||
|
|
@ -1005,7 +1005,7 @@ proc scale*(p:var TPoint3d,fac:float,org:TPoint3d){.inline.}=
|
||||||
p.z=(p.z - org.z) * fac + org.z
|
p.z=(p.z - org.z) * fac + org.z
|
||||||
|
|
||||||
proc stretch*(p:var TPoint3d,facx,facy,facz:float){.inline.}=
|
proc stretch*(p:var TPoint3d,facx,facy,facz:float){.inline.}=
|
||||||
## Scales a point in place non uniformly `facx` , `facy` , `facz` times
|
## Scales a point in place non uniformly `facx` , `facy` , `facz` times
|
||||||
## with world origo as origin.
|
## with world origo as origin.
|
||||||
p.x*=facx
|
p.x*=facx
|
||||||
p.y*=facy
|
p.y*=facy
|
||||||
|
|
@ -1017,7 +1017,7 @@ proc stretch*(p:var TPoint3d,facx,facy,facz:float,org:TPoint3d){.inline.}=
|
||||||
p.x=(p.x - org.x) * facx + org.x
|
p.x=(p.x - org.x) * facx + org.x
|
||||||
p.y=(p.y - org.y) * facy + org.y
|
p.y=(p.y - org.y) * facy + org.y
|
||||||
p.z=(p.z - org.z) * facz + org.z
|
p.z=(p.z - org.z) * facz + org.z
|
||||||
|
|
||||||
|
|
||||||
proc move*(p:var TPoint3d,dx,dy,dz:float){.inline.}=
|
proc move*(p:var TPoint3d,dx,dy,dz:float){.inline.}=
|
||||||
## Translates a point `dx` , `dy` , `dz` in place.
|
## Translates a point `dx` , `dy` , `dz` in place.
|
||||||
|
|
@ -1033,7 +1033,7 @@ proc move*(p:var TPoint3d,v:TVector3d){.inline.}=
|
||||||
|
|
||||||
proc area*(a,b,c:TPoint3d):float {.inline.}=
|
proc area*(a,b,c:TPoint3d):float {.inline.}=
|
||||||
## Computes the area of the triangle thru points `a` , `b` and `c`
|
## Computes the area of the triangle thru points `a` , `b` and `c`
|
||||||
|
|
||||||
# The area of a planar 3d quadliteral is the magnitude of the cross
|
# The area of a planar 3d quadliteral is the magnitude of the cross
|
||||||
# product of two edge vectors. Taking this time 0.5 gives the triangle area.
|
# product of two edge vectors. Taking this time 0.5 gives the triangle area.
|
||||||
return cross(b-a,c-a).len*0.5
|
return cross(b-a,c-a).len*0.5
|
||||||
|
|
|
||||||
|
|
@ -525,7 +525,7 @@ proc get*[K,V](table: var PConcTable[K,V], key: var K): V =
|
||||||
|
|
||||||
|
|
||||||
#Tests ----------------------------
|
#Tests ----------------------------
|
||||||
when isMainModule:
|
when not defined(testing) and isMainModule:
|
||||||
import locks, times, mersenne
|
import locks, times, mersenne
|
||||||
|
|
||||||
const
|
const
|
||||||
|
|
|
||||||
|
|
@ -286,18 +286,19 @@ proc `$`*[T](c: CritBitTree[T]): string =
|
||||||
result.add("}")
|
result.add("}")
|
||||||
|
|
||||||
when isMainModule:
|
when isMainModule:
|
||||||
|
import sequtils
|
||||||
|
|
||||||
var r: CritBitTree[void]
|
var r: CritBitTree[void]
|
||||||
r.incl "abc"
|
r.incl "abc"
|
||||||
r.incl "xyz"
|
r.incl "xyz"
|
||||||
r.incl "def"
|
r.incl "def"
|
||||||
r.incl "definition"
|
r.incl "definition"
|
||||||
r.incl "prefix"
|
r.incl "prefix"
|
||||||
|
|
||||||
doAssert r.contains"def"
|
doAssert r.contains"def"
|
||||||
#r.del "def"
|
|
||||||
|
|
||||||
for w in r.items:
|
r.excl "def"
|
||||||
echo w
|
|
||||||
|
|
||||||
for w in r.itemsWithPrefix("de"):
|
|
||||||
echo w
|
|
||||||
|
|
||||||
|
assert toSeq(r.items) == @["abc", "definition", "prefix", "xyz"]
|
||||||
|
|
||||||
|
assert toSeq(r.itemsWithPrefix("de")) == @["definition"]
|
||||||
|
|
|
||||||
|
|
@ -198,14 +198,21 @@ proc empty*(s: IntSet): bool {.inline, deprecated.} =
|
||||||
result = s.counter == 0
|
result = s.counter == 0
|
||||||
|
|
||||||
when isMainModule:
|
when isMainModule:
|
||||||
|
import sequtils, algorithm
|
||||||
|
|
||||||
var x = initIntSet()
|
var x = initIntSet()
|
||||||
x.incl(1)
|
x.incl(1)
|
||||||
x.incl(2)
|
x.incl(2)
|
||||||
x.incl(7)
|
x.incl(7)
|
||||||
x.incl(1056)
|
x.incl(1056)
|
||||||
for e in items(x): echo e
|
|
||||||
|
|
||||||
var y: TIntSet
|
var xs = toSeq(items(x))
|
||||||
|
xs.sort(cmp[int])
|
||||||
|
assert xs == @[1, 2, 7, 1056]
|
||||||
|
|
||||||
|
var y: IntSet
|
||||||
assign(y, x)
|
assign(y, x)
|
||||||
for e in items(y): echo e
|
var ys = toSeq(items(y))
|
||||||
|
ys.sort(cmp[int])
|
||||||
|
assert ys == @[1, 2, 7, 1056]
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -492,9 +492,8 @@ when isMainModule:
|
||||||
|
|
||||||
block: # filter iterator test
|
block: # filter iterator test
|
||||||
let numbers = @[1, 4, 5, 8, 9, 7, 4]
|
let numbers = @[1, 4, 5, 8, 9, 7, 4]
|
||||||
for n in filter(numbers, proc (x: int): bool = x mod 2 == 0):
|
assert toSeq(filter(numbers, proc (x: int): bool = x mod 2 == 0)) ==
|
||||||
echo($n)
|
@[4, 8, 4]
|
||||||
# echoes 4, 8, 4 in separate lines
|
|
||||||
|
|
||||||
block: # keepIf test
|
block: # keepIf test
|
||||||
var floats = @[13.0, 12.5, 5.8, 2.0, 6.1, 9.9, 10.1]
|
var floats = @[13.0, 12.5, 5.8, 2.0, 6.1, 9.9, 10.1]
|
||||||
|
|
@ -616,4 +615,5 @@ when isMainModule:
|
||||||
#doAssert a.repeat(-1) == @[] # will not compile!
|
#doAssert a.repeat(-1) == @[] # will not compile!
|
||||||
doAssert b.repeat(3) == @[]
|
doAssert b.repeat(3) == @[]
|
||||||
|
|
||||||
echo "Finished doc tests"
|
when not defined(testing):
|
||||||
|
echo "Finished doc tests"
|
||||||
|
|
|
||||||
|
|
@ -970,6 +970,7 @@ when isMainModule and not defined(release):
|
||||||
if s <= i or mustRehash(s, i):
|
if s <= i or mustRehash(s, i):
|
||||||
echo "performance issue: rightSize() will not elide enlarge() at ", i
|
echo "performance issue: rightSize() will not elide enlarge() at ", i
|
||||||
|
|
||||||
echo "Micro tests run successfully."
|
when not defined(testing):
|
||||||
|
echo "Micro tests run successfully."
|
||||||
|
|
||||||
testModule()
|
testModule()
|
||||||
|
|
|
||||||
|
|
@ -819,15 +819,18 @@ proc enlarge[A](t: var CountTable[A]) =
|
||||||
swap(t.data, n)
|
swap(t.data, n)
|
||||||
|
|
||||||
proc `[]=`*[A](t: var CountTable[A], key: A, val: int) =
|
proc `[]=`*[A](t: var CountTable[A], key: A, val: int) =
|
||||||
## puts a (key, value)-pair into `t`. `val` has to be positive.
|
## puts a (key, value)-pair into `t`.
|
||||||
assert val > 0
|
assert val > 0
|
||||||
var h = rawGet(t, key)
|
var h = rawGet(t, key)
|
||||||
if h >= 0:
|
if h >= 0:
|
||||||
t.data[h].val = val
|
t.data[h].val = val
|
||||||
else:
|
else:
|
||||||
h = -1 - h
|
if mustRehash(len(t.data), t.counter): enlarge(t)
|
||||||
t.data[h].key = key
|
rawInsert(t, t.data, key, val)
|
||||||
t.data[h].val = val
|
inc(t.counter)
|
||||||
|
#h = -1 - h
|
||||||
|
#t.data[h].key = key
|
||||||
|
#t.data[h].val = val
|
||||||
|
|
||||||
proc initCountTable*[A](initialSize=64): CountTable[A] =
|
proc initCountTable*[A](initialSize=64): CountTable[A] =
|
||||||
## creates a new count table that is empty.
|
## creates a new count table that is empty.
|
||||||
|
|
@ -984,6 +987,22 @@ proc sort*[A](t: CountTableRef[A]) =
|
||||||
## `t` in the sorted order.
|
## `t` in the sorted order.
|
||||||
t[].sort
|
t[].sort
|
||||||
|
|
||||||
|
proc merge*[A](s: var CountTable[A], t: CountTable[A]) =
|
||||||
|
## merges the second table into the first one
|
||||||
|
for key, value in t:
|
||||||
|
s.inc(key, value)
|
||||||
|
|
||||||
|
proc merge*[A](s, t: CountTable[A]): CountTable[A] =
|
||||||
|
## merges the two tables into a new one
|
||||||
|
result = initCountTable[A](nextPowerOfTwo(max(s.len, t.len)))
|
||||||
|
for table in @[s, t]:
|
||||||
|
for key, value in table:
|
||||||
|
result.inc(key, value)
|
||||||
|
|
||||||
|
proc merge*[A](s, t: CountTableRef[A]) =
|
||||||
|
## merges the second table into the first one
|
||||||
|
s[].merge(t[])
|
||||||
|
|
||||||
when isMainModule:
|
when isMainModule:
|
||||||
type
|
type
|
||||||
Person = object
|
Person = object
|
||||||
|
|
@ -1012,3 +1031,48 @@ when isMainModule:
|
||||||
s2[p2] = 45_000
|
s2[p2] = 45_000
|
||||||
s3[p1] = 30_000
|
s3[p1] = 30_000
|
||||||
s3[p2] = 45_000
|
s3[p2] = 45_000
|
||||||
|
|
||||||
|
var
|
||||||
|
t1 = initCountTable[string]()
|
||||||
|
t2 = initCountTable[string]()
|
||||||
|
t1.inc("foo")
|
||||||
|
t1.inc("bar", 2)
|
||||||
|
t1.inc("baz", 3)
|
||||||
|
t2.inc("foo", 4)
|
||||||
|
t2.inc("bar")
|
||||||
|
t2.inc("baz", 11)
|
||||||
|
merge(t1, t2)
|
||||||
|
assert(t1["foo"] == 5)
|
||||||
|
assert(t1["bar"] == 3)
|
||||||
|
assert(t1["baz"] == 14)
|
||||||
|
|
||||||
|
let
|
||||||
|
t1r = newCountTable[string]()
|
||||||
|
t2r = newCountTable[string]()
|
||||||
|
t1r.inc("foo")
|
||||||
|
t1r.inc("bar", 2)
|
||||||
|
t1r.inc("baz", 3)
|
||||||
|
t2r.inc("foo", 4)
|
||||||
|
t2r.inc("bar")
|
||||||
|
t2r.inc("baz", 11)
|
||||||
|
merge(t1r, t2r)
|
||||||
|
assert(t1r["foo"] == 5)
|
||||||
|
assert(t1r["bar"] == 3)
|
||||||
|
assert(t1r["baz"] == 14)
|
||||||
|
|
||||||
|
var
|
||||||
|
t1l = initCountTable[string]()
|
||||||
|
t2l = initCountTable[string]()
|
||||||
|
t1l.inc("foo")
|
||||||
|
t1l.inc("bar", 2)
|
||||||
|
t1l.inc("baz", 3)
|
||||||
|
t2l.inc("foo", 4)
|
||||||
|
t2l.inc("bar")
|
||||||
|
t2l.inc("baz", 11)
|
||||||
|
let
|
||||||
|
t1merging = t1l
|
||||||
|
t2merging = t2l
|
||||||
|
let merged = merge(t1merging, t2merging)
|
||||||
|
assert(merged["foo"] == 5)
|
||||||
|
assert(merged["bar"] == 3)
|
||||||
|
assert(merged["baz"] == 14)
|
||||||
|
|
|
||||||
|
|
@ -78,7 +78,7 @@ proc advice*(s: var ThreadPoolState): ThreadPoolAdvice =
|
||||||
result = doNothing
|
result = doNothing
|
||||||
inc s.calls
|
inc s.calls
|
||||||
|
|
||||||
when isMainModule:
|
when not defined(testing) and isMainModule:
|
||||||
proc busyLoop() =
|
proc busyLoop() =
|
||||||
while true:
|
while true:
|
||||||
discard random(80)
|
discard random(80)
|
||||||
|
|
|
||||||
|
|
@ -300,7 +300,7 @@ proc setMinPoolSize*(size: range[1..MaxThreadPoolSize]) =
|
||||||
minPoolSize = size
|
minPoolSize = size
|
||||||
|
|
||||||
proc setMaxPoolSize*(size: range[1..MaxThreadPoolSize]) =
|
proc setMaxPoolSize*(size: range[1..MaxThreadPoolSize]) =
|
||||||
## sets the minimal thread pool size. The default value of this
|
## sets the maximal thread pool size. The default value of this
|
||||||
## is ``MaxThreadPoolSize``.
|
## is ``MaxThreadPoolSize``.
|
||||||
maxPoolSize = size
|
maxPoolSize = size
|
||||||
if currentPoolSize > maxPoolSize:
|
if currentPoolSize > maxPoolSize:
|
||||||
|
|
|
||||||
|
|
@ -56,6 +56,12 @@ proc setCookie*(key, value: string, expires: TimeInfo,
|
||||||
when isMainModule:
|
when isMainModule:
|
||||||
var tim = Time(int(getTime()) + 76 * (60 * 60 * 24))
|
var tim = Time(int(getTime()) + 76 * (60 * 60 * 24))
|
||||||
|
|
||||||
echo(setCookie("test", "value", tim.getGMTime()))
|
let cookie = setCookie("test", "value", tim.getGMTime())
|
||||||
|
when not defined(testing):
|
||||||
|
echo cookie
|
||||||
|
let start = "Set-Cookie: test=value; Expires="
|
||||||
|
assert cookie[0..start.high] == start
|
||||||
|
|
||||||
echo parseCookies("uid=1; kp=2")
|
let table = parseCookies("uid=1; kp=2")
|
||||||
|
assert table["uid"] == "1"
|
||||||
|
assert table["kp"] == "2"
|
||||||
|
|
|
||||||
|
|
@ -451,7 +451,7 @@ proc convert*(s: string, destEncoding = "UTF-8",
|
||||||
finally:
|
finally:
|
||||||
close(c)
|
close(c)
|
||||||
|
|
||||||
when isMainModule:
|
when not defined(testing) and isMainModule:
|
||||||
let
|
let
|
||||||
orig = "öäüß"
|
orig = "öäüß"
|
||||||
cp1252 = convert(orig, "CP1252", "UTF-8")
|
cp1252 = convert(orig, "CP1252", "UTF-8")
|
||||||
|
|
|
||||||
|
|
@ -198,7 +198,7 @@ proc register*(d: Dispatcher, monitor: FSMonitor,
|
||||||
var deleg = toDelegate(monitor)
|
var deleg = toDelegate(monitor)
|
||||||
d.register(deleg)
|
d.register(deleg)
|
||||||
|
|
||||||
when isMainModule:
|
when not defined(testing) and isMainModule:
|
||||||
proc main =
|
proc main =
|
||||||
var disp = newDispatcher()
|
var disp = newDispatcher()
|
||||||
var monitor = newMonitor()
|
var monitor = newMonitor()
|
||||||
|
|
|
||||||
|
|
@ -593,7 +593,7 @@ proc register*(d: Dispatcher, ftp: AsyncFTPClient): Delegate {.discardable.} =
|
||||||
ftp.disp = d
|
ftp.disp = d
|
||||||
return ftp.disp.register(ftp.csock)
|
return ftp.disp.register(ftp.csock)
|
||||||
|
|
||||||
when isMainModule:
|
when not defined(testing) and isMainModule:
|
||||||
proc main =
|
proc main =
|
||||||
var d = newDispatcher()
|
var d = newDispatcher()
|
||||||
let hev =
|
let hev =
|
||||||
|
|
@ -629,7 +629,7 @@ when isMainModule:
|
||||||
if not d.poll(): break
|
if not d.poll(): break
|
||||||
main()
|
main()
|
||||||
|
|
||||||
when isMainModule and false:
|
when not defined(testing) and isMainModule:
|
||||||
var ftp = ftpClient("example.com", user = "foo", pass = "bar")
|
var ftp = ftpClient("example.com", user = "foo", pass = "bar")
|
||||||
ftp.connect()
|
ftp.connect()
|
||||||
echo ftp.pwd()
|
echo ftp.pwd()
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@
|
||||||
|
|
||||||
## The ``gentabs`` module implements an efficient hash table that is a
|
## The ``gentabs`` module implements an efficient hash table that is a
|
||||||
## key-value mapping. The keys are required to be strings, but the values
|
## key-value mapping. The keys are required to be strings, but the values
|
||||||
## may be any Nim or user defined type. This module supports matching
|
## may be any Nim or user defined type. This module supports matching
|
||||||
## of keys in case-sensitive, case-insensitive and style-insensitive modes.
|
## of keys in case-sensitive, case-insensitive and style-insensitive modes.
|
||||||
|
|
||||||
{.deprecated.}
|
{.deprecated.}
|
||||||
|
|
@ -22,7 +22,7 @@ type
|
||||||
modeCaseSensitive, ## case sensitive matching of keys
|
modeCaseSensitive, ## case sensitive matching of keys
|
||||||
modeCaseInsensitive, ## case insensitive matching of keys
|
modeCaseInsensitive, ## case insensitive matching of keys
|
||||||
modeStyleInsensitive ## style sensitive matching of keys
|
modeStyleInsensitive ## style sensitive matching of keys
|
||||||
|
|
||||||
TGenKeyValuePair[T] = tuple[key: string, val: T]
|
TGenKeyValuePair[T] = tuple[key: string, val: T]
|
||||||
TGenKeyValuePairSeq[T] = seq[TGenKeyValuePair[T]]
|
TGenKeyValuePairSeq[T] = seq[TGenKeyValuePair[T]]
|
||||||
TGenTable*[T] = object of RootObj
|
TGenTable*[T] = object of RootObj
|
||||||
|
|
@ -83,7 +83,7 @@ proc rawGet[T](tbl: PGenTable[T], key: string): int =
|
||||||
h = nextTry(h, high(tbl.data))
|
h = nextTry(h, high(tbl.data))
|
||||||
result = - 1
|
result = - 1
|
||||||
|
|
||||||
proc rawInsert[T](tbl: PGenTable[T], data: var TGenKeyValuePairSeq[T],
|
proc rawInsert[T](tbl: PGenTable[T], data: var TGenKeyValuePairSeq[T],
|
||||||
key: string, val: T) =
|
key: string, val: T) =
|
||||||
var h: THash
|
var h: THash
|
||||||
h = myhash(tbl, key) and high(data)
|
h = myhash(tbl, key) and high(data)
|
||||||
|
|
@ -96,7 +96,7 @@ proc enlarge[T](tbl: PGenTable[T]) =
|
||||||
var n: TGenKeyValuePairSeq[T]
|
var n: TGenKeyValuePairSeq[T]
|
||||||
newSeq(n, len(tbl.data) * growthFactor)
|
newSeq(n, len(tbl.data) * growthFactor)
|
||||||
for i in countup(0, high(tbl.data)):
|
for i in countup(0, high(tbl.data)):
|
||||||
if not isNil(tbl.data[i].key):
|
if not isNil(tbl.data[i].key):
|
||||||
rawInsert[T](tbl, n, tbl.data[i].key, tbl.data[i].val)
|
rawInsert[T](tbl, n, tbl.data[i].key, tbl.data[i].val)
|
||||||
swap(tbl.data, n)
|
swap(tbl.data, n)
|
||||||
|
|
||||||
|
|
@ -141,20 +141,20 @@ when isMainModule:
|
||||||
assert(not x.hasKey("NOPE")) # ...but key "NOPE" is not in the table.
|
assert(not x.hasKey("NOPE")) # ...but key "NOPE" is not in the table.
|
||||||
for k,v in pairs(x): # make sure the 'pairs' iterator works
|
for k,v in pairs(x): # make sure the 'pairs' iterator works
|
||||||
assert(x[k]==v)
|
assert(x[k]==v)
|
||||||
|
|
||||||
#
|
#
|
||||||
# Verify a table of user-defined types
|
# Verify a table of user-defined types
|
||||||
#
|
#
|
||||||
type
|
type
|
||||||
TMyType = tuple[first, second: string] # a pair of strings
|
TMyType = tuple[first, second: string] # a pair of strings
|
||||||
|
|
||||||
var y = newGenTable[TMyType](modeCaseInsensitive) # hash table where each
|
var y = newGenTable[TMyType](modeCaseInsensitive) # hash table where each
|
||||||
# value is TMyType tuple
|
# value is TMyType tuple
|
||||||
|
|
||||||
#var junk: TMyType = ("OK", "Here")
|
#var junk: TMyType = ("OK", "Here")
|
||||||
|
|
||||||
#echo junk.first, " ", junk.second
|
#echo junk.first, " ", junk.second
|
||||||
|
|
||||||
y["Hello"] = ("Hello", "World")
|
y["Hello"] = ("Hello", "World")
|
||||||
y["Goodbye"] = ("Goodbye", "Everyone")
|
y["Goodbye"] = ("Goodbye", "Everyone")
|
||||||
#y["Hello"] = TMyType( ("Hello", "World") )
|
#y["Hello"] = TMyType( ("Hello", "World") )
|
||||||
|
|
@ -163,31 +163,44 @@ when isMainModule:
|
||||||
assert( not isNil(y["Hello"].first) )
|
assert( not isNil(y["Hello"].first) )
|
||||||
assert( y["Hello"].first == "Hello" )
|
assert( y["Hello"].first == "Hello" )
|
||||||
assert( y["Hello"].second == "World" )
|
assert( y["Hello"].second == "World" )
|
||||||
|
|
||||||
#
|
#
|
||||||
# Verify table of tables
|
# Verify table of tables
|
||||||
#
|
#
|
||||||
var z: PGenTable[ PGenTable[int] ] # hash table where each value is
|
var z: PGenTable[ PGenTable[int] ] # hash table where each value is
|
||||||
# a hash table of ints
|
# a hash table of ints
|
||||||
|
|
||||||
z = newGenTable[PGenTable[int]](modeCaseInsensitive)
|
z = newGenTable[PGenTable[int]](modeCaseInsensitive)
|
||||||
z["first"] = newGenTable[int](modeCaseInsensitive)
|
z["first"] = newGenTable[int](modeCaseInsensitive)
|
||||||
z["first"]["one"] = 1
|
z["first"]["one"] = 1
|
||||||
z["first"]["two"] = 2
|
z["first"]["two"] = 2
|
||||||
z["first"]["three"] = 3
|
z["first"]["three"] = 3
|
||||||
|
|
||||||
z["second"] = newGenTable[int](modeCaseInsensitive)
|
z["second"] = newGenTable[int](modeCaseInsensitive)
|
||||||
z["second"]["red"] = 10
|
z["second"]["red"] = 10
|
||||||
z["second"]["blue"] = 20
|
z["second"]["blue"] = 20
|
||||||
|
|
||||||
assert(len(z) == 2) # length of outer table
|
assert(len(z) == 2) # length of outer table
|
||||||
assert(len(z["first"]) == 3) # length of "first" table
|
assert(len(z["first"]) == 3) # length of "first" table
|
||||||
assert(len(z["second"]) == 2) # length of "second" table
|
assert(len(z["second"]) == 2) # length of "second" table
|
||||||
assert( z["first"]["one"] == 1) # retrieve from first inner table
|
assert( z["first"]["one"] == 1) # retrieve from first inner table
|
||||||
assert( z["second"]["red"] == 10) # retrieve from second inner table
|
assert( z["second"]["red"] == 10) # retrieve from second inner table
|
||||||
|
|
||||||
for k,v in pairs(z):
|
when false:
|
||||||
echo( "$# ($#) ->" % [k,$len(v)] )
|
# disabled: depends on hash order:
|
||||||
#for k2,v2 in pairs(v):
|
var output = ""
|
||||||
# echo( " $# <-> $#" % [k2,$v2] )
|
for k, v in pairs(z):
|
||||||
echo()
|
output.add( "$# ($#) ->\L" % [k,$len(v)] )
|
||||||
|
for k2,v2 in pairs(v):
|
||||||
|
output.add( " $# <-> $#\L" % [k2,$v2] )
|
||||||
|
|
||||||
|
let expected = unindent """
|
||||||
|
first (3) ->
|
||||||
|
two <-> 2
|
||||||
|
three <-> 3
|
||||||
|
one <-> 1
|
||||||
|
second (2) ->
|
||||||
|
red <-> 10
|
||||||
|
blue <-> 20
|
||||||
|
"""
|
||||||
|
assert output == expected
|
||||||
|
|
|
||||||
|
|
@ -37,29 +37,29 @@
|
||||||
## h = h !& hash(x.bar)
|
## h = h !& hash(x.bar)
|
||||||
## result = !$h
|
## result = !$h
|
||||||
|
|
||||||
import
|
import
|
||||||
strutils
|
strutils
|
||||||
|
|
||||||
type
|
type
|
||||||
THash* = int ## a hash value; hash tables using these values should
|
THash* = int ## a hash value; hash tables using these values should
|
||||||
## always have a size of a power of two and can use the ``and``
|
## always have a size of a power of two and can use the ``and``
|
||||||
## operator instead of ``mod`` for truncation of the hash value.
|
## operator instead of ``mod`` for truncation of the hash value.
|
||||||
|
|
||||||
proc `!&`*(h: THash, val: int): THash {.inline.} =
|
proc `!&`*(h: THash, val: int): THash {.inline.} =
|
||||||
## mixes a hash value `h` with `val` to produce a new hash value. This is
|
## mixes a hash value `h` with `val` to produce a new hash value. This is
|
||||||
## only needed if you need to implement a hash proc for a new datatype.
|
## only needed if you need to implement a hash proc for a new datatype.
|
||||||
result = h +% val
|
result = h +% val
|
||||||
result = result +% result shl 10
|
result = result +% result shl 10
|
||||||
result = result xor (result shr 6)
|
result = result xor (result shr 6)
|
||||||
|
|
||||||
proc `!$`*(h: THash): THash {.inline.} =
|
proc `!$`*(h: THash): THash {.inline.} =
|
||||||
## finishes the computation of the hash value. This is
|
## finishes the computation of the hash value. This is
|
||||||
## only needed if you need to implement a hash proc for a new datatype.
|
## only needed if you need to implement a hash proc for a new datatype.
|
||||||
result = h +% h shl 3
|
result = h +% h shl 3
|
||||||
result = result xor (result shr 11)
|
result = result xor (result shr 11)
|
||||||
result = result +% result shl 15
|
result = result +% result shl 15
|
||||||
|
|
||||||
proc hashData*(data: pointer, size: int): THash =
|
proc hashData*(data: pointer, size: int): THash =
|
||||||
## hashes an array of bytes of size `size`
|
## hashes an array of bytes of size `size`
|
||||||
var h: THash = 0
|
var h: THash = 0
|
||||||
when defined(js):
|
when defined(js):
|
||||||
|
|
@ -69,7 +69,7 @@ proc hashData*(data: pointer, size: int): THash =
|
||||||
var p = cast[cstring](data)
|
var p = cast[cstring](data)
|
||||||
var i = 0
|
var i = 0
|
||||||
var s = size
|
var s = size
|
||||||
while s > 0:
|
while s > 0:
|
||||||
h = h !& ord(p[i])
|
h = h !& ord(p[i])
|
||||||
inc(i)
|
inc(i)
|
||||||
dec(s)
|
dec(s)
|
||||||
|
|
@ -78,7 +78,7 @@ proc hashData*(data: pointer, size: int): THash =
|
||||||
when defined(js):
|
when defined(js):
|
||||||
var objectID = 0
|
var objectID = 0
|
||||||
|
|
||||||
proc hash*(x: pointer): THash {.inline.} =
|
proc hash*(x: pointer): THash {.inline.} =
|
||||||
## efficient hashing of pointers
|
## efficient hashing of pointers
|
||||||
when defined(js):
|
when defined(js):
|
||||||
asm """
|
asm """
|
||||||
|
|
@ -93,7 +93,7 @@ proc hash*(x: pointer): THash {.inline.} =
|
||||||
"""
|
"""
|
||||||
else:
|
else:
|
||||||
result = (cast[THash](x)) shr 3 # skip the alignment
|
result = (cast[THash](x)) shr 3 # skip the alignment
|
||||||
|
|
||||||
when not defined(booting):
|
when not defined(booting):
|
||||||
proc hash*[T: proc](x: T): THash {.inline.} =
|
proc hash*[T: proc](x: T): THash {.inline.} =
|
||||||
## efficient hashing of proc vars; closures are supported too.
|
## efficient hashing of proc vars; closures are supported too.
|
||||||
|
|
@ -101,58 +101,65 @@ when not defined(booting):
|
||||||
result = hash(rawProc(x)) !& hash(rawEnv(x))
|
result = hash(rawProc(x)) !& hash(rawEnv(x))
|
||||||
else:
|
else:
|
||||||
result = hash(pointer(x))
|
result = hash(pointer(x))
|
||||||
|
|
||||||
proc hash*(x: int): THash {.inline.} =
|
proc hash*(x: int): THash {.inline.} =
|
||||||
## efficient hashing of integers
|
## efficient hashing of integers
|
||||||
result = x
|
result = x
|
||||||
|
|
||||||
proc hash*(x: int64): THash {.inline.} =
|
proc hash*(x: int64): THash {.inline.} =
|
||||||
## efficient hashing of integers
|
## efficient hashing of integers
|
||||||
result = toU32(x)
|
result = toU32(x)
|
||||||
|
|
||||||
proc hash*(x: char): THash {.inline.} =
|
proc hash*(x: char): THash {.inline.} =
|
||||||
## efficient hashing of characters
|
## efficient hashing of characters
|
||||||
result = ord(x)
|
result = ord(x)
|
||||||
|
|
||||||
proc hash*(x: string): THash =
|
proc hash*(x: string): THash =
|
||||||
## efficient hashing of strings
|
## efficient hashing of strings
|
||||||
var h: THash = 0
|
var h: THash = 0
|
||||||
for i in 0..x.len-1:
|
for i in 0..x.len-1:
|
||||||
h = h !& ord(x[i])
|
h = h !& ord(x[i])
|
||||||
result = !$h
|
result = !$h
|
||||||
|
|
||||||
proc hashIgnoreStyle*(x: string): THash =
|
proc hashIgnoreStyle*(x: string): THash =
|
||||||
## efficient hashing of strings; style is ignored
|
## efficient hashing of strings; style is ignored
|
||||||
var h: THash = 0
|
var h: THash = 0
|
||||||
for i in 0..x.len-1:
|
for i in 0..x.len-1:
|
||||||
var c = x[i]
|
var c = x[i]
|
||||||
if c == '_':
|
if c == '_':
|
||||||
continue # skip _
|
continue # skip _
|
||||||
if c in {'A'..'Z'}:
|
if c in {'A'..'Z'}:
|
||||||
c = chr(ord(c) + (ord('a') - ord('A'))) # toLower()
|
c = chr(ord(c) + (ord('a') - ord('A'))) # toLower()
|
||||||
h = h !& ord(c)
|
h = h !& ord(c)
|
||||||
result = !$h
|
result = !$h
|
||||||
|
|
||||||
proc hashIgnoreCase*(x: string): THash =
|
proc hashIgnoreCase*(x: string): THash =
|
||||||
## efficient hashing of strings; case is ignored
|
## efficient hashing of strings; case is ignored
|
||||||
var h: THash = 0
|
var h: THash = 0
|
||||||
for i in 0..x.len-1:
|
for i in 0..x.len-1:
|
||||||
var c = x[i]
|
var c = x[i]
|
||||||
if c in {'A'..'Z'}:
|
if c in {'A'..'Z'}:
|
||||||
c = chr(ord(c) + (ord('a') - ord('A'))) # toLower()
|
c = chr(ord(c) + (ord('a') - ord('A'))) # toLower()
|
||||||
h = h !& ord(c)
|
h = h !& ord(c)
|
||||||
result = !$h
|
result = !$h
|
||||||
|
|
||||||
proc hash*[T: tuple](x: T): THash =
|
|
||||||
## efficient hashing of tuples.
|
|
||||||
for f in fields(x):
|
|
||||||
result = result !& hash(f)
|
|
||||||
result = !$result
|
|
||||||
|
|
||||||
proc hash*(x: float): THash {.inline.} =
|
proc hash*(x: float): THash {.inline.} =
|
||||||
var y = x + 1.0
|
var y = x + 1.0
|
||||||
result = cast[ptr THash](addr(y))[]
|
result = cast[ptr THash](addr(y))[]
|
||||||
|
|
||||||
|
|
||||||
|
# Forward declarations before methods that hash containers. This allows
|
||||||
|
# containers to contain other containers
|
||||||
|
proc hash*[A](x: openArray[A]): THash
|
||||||
|
proc hash*[A](x: set[A]): THash
|
||||||
|
|
||||||
|
|
||||||
|
proc hash*[T: tuple](x: T): THash =
|
||||||
|
## efficient hashing of tuples.
|
||||||
|
for f in fields(x):
|
||||||
|
result = result !& hash(f)
|
||||||
|
result = !$result
|
||||||
|
|
||||||
proc hash*[A](x: openArray[A]): THash =
|
proc hash*[A](x: openArray[A]): THash =
|
||||||
for it in items(x): result = result !& hash(it)
|
for it in items(x): result = result !& hash(it)
|
||||||
result = !$result
|
result = !$result
|
||||||
|
|
@ -160,3 +167,4 @@ proc hash*[A](x: openArray[A]): THash =
|
||||||
proc hash*[A](x: set[A]): THash =
|
proc hash*[A](x: set[A]): THash =
|
||||||
for it in items(x): result = result !& hash(it)
|
for it in items(x): result = result !& hash(it)
|
||||||
result = !$result
|
result = !$result
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -483,7 +483,8 @@ macro `var`*(e: expr): expr {.immediate.} =
|
||||||
result = xmlCheckedTag(e, "var", commonAttr)
|
result = xmlCheckedTag(e, "var", commonAttr)
|
||||||
|
|
||||||
when isMainModule:
|
when isMainModule:
|
||||||
var nim = "Nim"
|
let nim = "Nim"
|
||||||
echo h1(a(href="http://nim-lang.org", nim))
|
assert h1(a(href="http://nim-lang.org", nim)) ==
|
||||||
echo form(action="test", `accept-charset` = "Content-Type")
|
"""<h1><a href="http://nim-lang.org">Nim</a></h1>"""
|
||||||
|
assert form(action="test", `accept-charset` = "Content-Type") ==
|
||||||
|
"""<form action="test" accept-charset="Content-Type"></form>"""
|
||||||
|
|
|
||||||
|
|
@ -593,7 +593,7 @@ proc loadHtml*(path: string): XmlNode =
|
||||||
var errors: seq[string] = @[]
|
var errors: seq[string] = @[]
|
||||||
result = loadHtml(path, errors)
|
result = loadHtml(path, errors)
|
||||||
|
|
||||||
when isMainModule:
|
when not defined(testing) and isMainModule:
|
||||||
import os
|
import os
|
||||||
|
|
||||||
var errors: seq[string] = @[]
|
var errors: seq[string] = @[]
|
||||||
|
|
|
||||||
|
|
@ -819,7 +819,7 @@ proc get*(client: AsyncHttpClient, url: string): Future[Response] {.async.} =
|
||||||
result = await client.request(redirectTo, httpGET)
|
result = await client.request(redirectTo, httpGET)
|
||||||
lastUrl = redirectTo
|
lastUrl = redirectTo
|
||||||
|
|
||||||
when isMainModule:
|
when not defined(testing) and isMainModule:
|
||||||
when true:
|
when true:
|
||||||
# Async
|
# Async
|
||||||
proc main() {.async.} =
|
proc main() {.async.} =
|
||||||
|
|
|
||||||
|
|
@ -514,7 +514,7 @@ proc close*(h: PAsyncHTTPServer) =
|
||||||
## Closes the ``PAsyncHTTPServer``.
|
## Closes the ``PAsyncHTTPServer``.
|
||||||
h.asyncSocket.close()
|
h.asyncSocket.close()
|
||||||
|
|
||||||
when isMainModule:
|
when not defined(testing) and isMainModule:
|
||||||
var counter = 0
|
var counter = 0
|
||||||
|
|
||||||
var s: TServer
|
var s: TServer
|
||||||
|
|
|
||||||
|
|
@ -808,22 +808,25 @@ proc `[]=`*(obj: JsonNode, key: string, val: JsonNode) =
|
||||||
return
|
return
|
||||||
obj.fields.add((key, val))
|
obj.fields.add((key, val))
|
||||||
|
|
||||||
proc `{}`*(node: JsonNode, key: string): JsonNode =
|
proc `{}`*(node: JsonNode, keys: varargs[string]): JsonNode =
|
||||||
## Transverses the node and gets the given value. If any of the
|
## Traverses the node and gets the given value. If any of the
|
||||||
## names does not exist, returns nil
|
## keys do not exist, returns nil. Also returns nil if one of the
|
||||||
|
## intermediate data structures is not an object
|
||||||
result = node
|
result = node
|
||||||
if isNil(node): return nil
|
for key in keys:
|
||||||
result = result[key]
|
if isNil(result) or result.kind!=JObject:
|
||||||
|
return nil
|
||||||
|
result=result[key]
|
||||||
|
|
||||||
proc `{}=`*(node: JsonNode, names: varargs[string], value: JsonNode) =
|
proc `{}=`*(node: JsonNode, keys: varargs[string], value: JsonNode) =
|
||||||
## Transverses the node and tries to set the value at the given location
|
## Traverses the node and tries to set the value at the given location
|
||||||
## to `value` If any of the names are missing, they are added
|
## to `value` If any of the keys are missing, they are added
|
||||||
var node = node
|
var node = node
|
||||||
for i in 0..(names.len-2):
|
for i in 0..(keys.len-2):
|
||||||
if isNil(node[names[i]]):
|
if isNil(node[keys[i]]):
|
||||||
node[names[i]] = newJObject()
|
node[keys[i]] = newJObject()
|
||||||
node = node[names[i]]
|
node = node[keys[i]]
|
||||||
node[names[names.len-1]] = value
|
node[keys[keys.len-1]] = value
|
||||||
|
|
||||||
proc delete*(obj: JsonNode, key: string) =
|
proc delete*(obj: JsonNode, key: string) =
|
||||||
## Deletes ``obj[key]`` preserving the order of the other (key, value)-pairs.
|
## Deletes ``obj[key]`` preserving the order of the other (key, value)-pairs.
|
||||||
|
|
@ -1150,19 +1153,22 @@ when false:
|
||||||
when isMainModule:
|
when isMainModule:
|
||||||
#var node = parse("{ \"test\": null }")
|
#var node = parse("{ \"test\": null }")
|
||||||
#echo(node.existsKey("test56"))
|
#echo(node.existsKey("test56"))
|
||||||
|
|
||||||
var parsed = parseFile("tests/testdata/jsontest.json")
|
var parsed = parseFile("tests/testdata/jsontest.json")
|
||||||
var parsed2 = parseFile("tests/testdata/jsontest2.json")
|
var parsed2 = parseFile("tests/testdata/jsontest2.json")
|
||||||
echo(parsed)
|
|
||||||
echo()
|
when not defined(testing):
|
||||||
echo(pretty(parsed, 2))
|
echo(parsed)
|
||||||
echo()
|
echo()
|
||||||
echo(parsed["keyÄÖöoßß"])
|
echo(pretty(parsed, 2))
|
||||||
echo()
|
echo()
|
||||||
echo(pretty(parsed2))
|
echo(parsed["keyÄÖöoßß"])
|
||||||
try:
|
echo()
|
||||||
echo(parsed["key2"][12123])
|
echo(pretty(parsed2))
|
||||||
raise newException(ValueError, "That line was expected to fail")
|
try:
|
||||||
except IndexError: echo()
|
echo(parsed["key2"][12123])
|
||||||
|
raise newException(ValueError, "That line was expected to fail")
|
||||||
|
except IndexError: echo()
|
||||||
|
|
||||||
let testJson = parseJson"""{ "a": [1, 2, 3, 4], "b": "asd" }"""
|
let testJson = parseJson"""{ "a": [1, 2, 3, 4], "b": "asd" }"""
|
||||||
# nil passthrough
|
# nil passthrough
|
||||||
|
|
@ -1186,9 +1192,17 @@ when isMainModule:
|
||||||
except:
|
except:
|
||||||
assert(false, "EInvalidIndex thrown for valid index")
|
assert(false, "EInvalidIndex thrown for valid index")
|
||||||
|
|
||||||
|
assert(testJson{"b"}.str=="asd", "Couldn't fetch a singly nested key with {}")
|
||||||
|
assert(isNil(testJson{"nonexistent"}), "Non-existent keys should return nil")
|
||||||
|
assert(parsed2{"repository", "description"}.str=="IRC Library for Haskell", "Couldn't fetch via multiply nested key using {}")
|
||||||
|
assert(isNil(testJson{"a", "b"}), "Indexing through a list should return nil")
|
||||||
|
assert(isNil(testJson{"a", "b"}), "Indexing through a list should return nil")
|
||||||
|
assert(testJson{"a"}==parseJson"[1, 2, 3, 4]", "Didn't return a non-JObject when there was one to be found")
|
||||||
|
assert(isNil(parseJson("[1, 2, 3]"){"foo"}), "Indexing directly into a list should return nil")
|
||||||
|
|
||||||
# Generator:
|
# Generator:
|
||||||
var j = %* [{"name": "John", "age": 30}, {"name": "Susan", "age": 31}]
|
var j = %* [{"name": "John", "age": 30}, {"name": "Susan", "age": 31}]
|
||||||
assert j == %[%{"name": %"John", "age": %30}, %{"name": %"Susan", "age": %31}]
|
assert j == %[%{"name": %"John", "age": %30}, %{"name": %"Susan", "age": %31}]
|
||||||
|
|
||||||
var j2 = %*
|
var j2 = %*
|
||||||
[
|
[
|
||||||
|
|
@ -1216,12 +1230,13 @@ when isMainModule:
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
assert j3 == %[%{"name": %"John", "age": %30}, %{"name": %"Susan", "age": %31}]
|
assert j3 == %[%{"name": %"John", "age": %30}, %{"name": %"Susan", "age": %31}]
|
||||||
|
|
||||||
discard """
|
when not defined(testing):
|
||||||
while true:
|
discard """
|
||||||
var json = stdin.readLine()
|
while true:
|
||||||
var node = parse(json)
|
var json = stdin.readLine()
|
||||||
echo(node)
|
var node = parse(json)
|
||||||
echo()
|
echo(node)
|
||||||
echo()
|
echo()
|
||||||
"""
|
echo()
|
||||||
|
"""
|
||||||
|
|
|
||||||
|
|
@ -278,7 +278,7 @@ proc getLogFilter*(): Level =
|
||||||
|
|
||||||
# --------------
|
# --------------
|
||||||
|
|
||||||
when isMainModule:
|
when not defined(testing) and isMainModule:
|
||||||
var L = newConsoleLogger()
|
var L = newConsoleLogger()
|
||||||
var fL = newFileLogger("test.log", fmtStr = verboseFmtStr)
|
var fL = newFileLogger("test.log", fmtStr = verboseFmtStr)
|
||||||
var rL = newRollingFileLogger("rolling.log", fmtStr = verboseFmtStr)
|
var rL = newRollingFileLogger("rolling.log", fmtStr = verboseFmtStr)
|
||||||
|
|
|
||||||
|
|
@ -15,8 +15,8 @@
|
||||||
## type than its compiletime type:
|
## type than its compiletime type:
|
||||||
##
|
##
|
||||||
## .. code-block:: nim
|
## .. code-block:: nim
|
||||||
##
|
##
|
||||||
## type
|
## type
|
||||||
## TA = object
|
## TA = object
|
||||||
## TB = object of TA
|
## TB = object of TA
|
||||||
## f: int
|
## f: int
|
||||||
|
|
@ -28,6 +28,8 @@
|
||||||
## new(b)
|
## new(b)
|
||||||
## a = b
|
## a = b
|
||||||
## echo($$a[]) # produces "{}", not "{f: 0}"
|
## echo($$a[]) # produces "{}", not "{f: 0}"
|
||||||
|
##
|
||||||
|
## **Note**: The ``to`` and ``$$`` operations are available at compile-time!
|
||||||
|
|
||||||
import streams, typeinfo, json, intsets, tables
|
import streams, typeinfo, json, intsets, tables
|
||||||
|
|
||||||
|
|
@ -38,7 +40,12 @@ proc storeAny(s: Stream, a: TAny, stored: var IntSet) =
|
||||||
case a.kind
|
case a.kind
|
||||||
of akNone: assert false
|
of akNone: assert false
|
||||||
of akBool: s.write($getBool(a))
|
of akBool: s.write($getBool(a))
|
||||||
of akChar: s.write(escapeJson($getChar(a)))
|
of akChar:
|
||||||
|
let ch = getChar(a)
|
||||||
|
if ch < '\128':
|
||||||
|
s.write(escapeJson($ch))
|
||||||
|
else:
|
||||||
|
s.write($int(ch))
|
||||||
of akArray, akSequence:
|
of akArray, akSequence:
|
||||||
if a.kind == akSequence and isNil(a): s.write("null")
|
if a.kind == akSequence and isNil(a): s.write("null")
|
||||||
else:
|
else:
|
||||||
|
|
@ -92,7 +99,7 @@ proc storeAny(s: Stream, a: TAny, stored: var IntSet) =
|
||||||
proc loadAny(p: var JsonParser, a: TAny, t: var Table[BiggestInt, pointer]) =
|
proc loadAny(p: var JsonParser, a: TAny, t: var Table[BiggestInt, pointer]) =
|
||||||
case a.kind
|
case a.kind
|
||||||
of akNone: assert false
|
of akNone: assert false
|
||||||
of akBool:
|
of akBool:
|
||||||
case p.kind
|
case p.kind
|
||||||
of jsonFalse: setBiggestInt(a, 0)
|
of jsonFalse: setBiggestInt(a, 0)
|
||||||
of jsonTrue: setBiggestInt(a, 1)
|
of jsonTrue: setBiggestInt(a, 1)
|
||||||
|
|
@ -105,8 +112,12 @@ proc loadAny(p: var JsonParser, a: TAny, t: var Table[BiggestInt, pointer]) =
|
||||||
setBiggestInt(a, ord(x[0]))
|
setBiggestInt(a, ord(x[0]))
|
||||||
next(p)
|
next(p)
|
||||||
return
|
return
|
||||||
|
elif p.kind == jsonInt:
|
||||||
|
setBiggestInt(a, getInt(p))
|
||||||
|
next(p)
|
||||||
|
return
|
||||||
raiseParseErr(p, "string of length 1 expected for a char")
|
raiseParseErr(p, "string of length 1 expected for a char")
|
||||||
of akEnum:
|
of akEnum:
|
||||||
if p.kind == jsonString:
|
if p.kind == jsonString:
|
||||||
setBiggestInt(a, getEnumOrdinal(a, p.str))
|
setBiggestInt(a, getEnumOrdinal(a, p.str))
|
||||||
next(p)
|
next(p)
|
||||||
|
|
@ -122,7 +133,7 @@ proc loadAny(p: var JsonParser, a: TAny, t: var Table[BiggestInt, pointer]) =
|
||||||
if p.kind == jsonArrayEnd: next(p)
|
if p.kind == jsonArrayEnd: next(p)
|
||||||
else: raiseParseErr(p, "']' end of array expected")
|
else: raiseParseErr(p, "']' end of array expected")
|
||||||
of akSequence:
|
of akSequence:
|
||||||
case p.kind
|
case p.kind
|
||||||
of jsonNull:
|
of jsonNull:
|
||||||
setPointer(a, nil)
|
setPointer(a, nil)
|
||||||
next(p)
|
next(p)
|
||||||
|
|
@ -143,7 +154,7 @@ proc loadAny(p: var JsonParser, a: TAny, t: var Table[BiggestInt, pointer]) =
|
||||||
if p.kind != jsonObjectStart: raiseParseErr(p, "'{' expected for an object")
|
if p.kind != jsonObjectStart: raiseParseErr(p, "'{' expected for an object")
|
||||||
next(p)
|
next(p)
|
||||||
while p.kind != jsonObjectEnd and p.kind != jsonEof:
|
while p.kind != jsonObjectEnd and p.kind != jsonEof:
|
||||||
if p.kind != jsonString:
|
if p.kind != jsonString:
|
||||||
raiseParseErr(p, "string expected for a field name")
|
raiseParseErr(p, "string expected for a field name")
|
||||||
var fieldName = p.str
|
var fieldName = p.str
|
||||||
next(p)
|
next(p)
|
||||||
|
|
@ -160,7 +171,7 @@ proc loadAny(p: var JsonParser, a: TAny, t: var Table[BiggestInt, pointer]) =
|
||||||
if p.kind == jsonArrayEnd: next(p)
|
if p.kind == jsonArrayEnd: next(p)
|
||||||
else: raiseParseErr(p, "']' end of array expected")
|
else: raiseParseErr(p, "']' end of array expected")
|
||||||
of akPtr, akRef:
|
of akPtr, akRef:
|
||||||
case p.kind
|
case p.kind
|
||||||
of jsonNull:
|
of jsonNull:
|
||||||
setPointer(a, nil)
|
setPointer(a, nil)
|
||||||
next(p)
|
next(p)
|
||||||
|
|
@ -170,7 +181,7 @@ proc loadAny(p: var JsonParser, a: TAny, t: var Table[BiggestInt, pointer]) =
|
||||||
of jsonArrayStart:
|
of jsonArrayStart:
|
||||||
next(p)
|
next(p)
|
||||||
if a.kind == akRef: invokeNew(a)
|
if a.kind == akRef: invokeNew(a)
|
||||||
else: setPointer(a, alloc0(a.baseTypeSize))
|
else: setPointer(a, alloc0(a.baseTypeSize))
|
||||||
if p.kind == jsonInt:
|
if p.kind == jsonInt:
|
||||||
t[p.getInt] = getPointer(a)
|
t[p.getInt] = getPointer(a)
|
||||||
next(p)
|
next(p)
|
||||||
|
|
@ -179,8 +190,8 @@ proc loadAny(p: var JsonParser, a: TAny, t: var Table[BiggestInt, pointer]) =
|
||||||
if p.kind == jsonArrayEnd: next(p)
|
if p.kind == jsonArrayEnd: next(p)
|
||||||
else: raiseParseErr(p, "']' end of ref-address pair expected")
|
else: raiseParseErr(p, "']' end of ref-address pair expected")
|
||||||
else: raiseParseErr(p, "int for pointer type expected")
|
else: raiseParseErr(p, "int for pointer type expected")
|
||||||
of akProc, akPointer, akCString:
|
of akProc, akPointer, akCString:
|
||||||
case p.kind
|
case p.kind
|
||||||
of jsonNull:
|
of jsonNull:
|
||||||
setPointer(a, nil)
|
setPointer(a, nil)
|
||||||
next(p)
|
next(p)
|
||||||
|
|
@ -189,7 +200,7 @@ proc loadAny(p: var JsonParser, a: TAny, t: var Table[BiggestInt, pointer]) =
|
||||||
next(p)
|
next(p)
|
||||||
else: raiseParseErr(p, "int for pointer type expected")
|
else: raiseParseErr(p, "int for pointer type expected")
|
||||||
of akString:
|
of akString:
|
||||||
case p.kind
|
case p.kind
|
||||||
of jsonNull:
|
of jsonNull:
|
||||||
setPointer(a, nil)
|
setPointer(a, nil)
|
||||||
next(p)
|
next(p)
|
||||||
|
|
@ -197,7 +208,7 @@ proc loadAny(p: var JsonParser, a: TAny, t: var Table[BiggestInt, pointer]) =
|
||||||
setString(a, p.str)
|
setString(a, p.str)
|
||||||
next(p)
|
next(p)
|
||||||
else: raiseParseErr(p, "string expected")
|
else: raiseParseErr(p, "string expected")
|
||||||
of akInt..akInt64, akUInt..akUInt64:
|
of akInt..akInt64, akUInt..akUInt64:
|
||||||
if p.kind == jsonInt:
|
if p.kind == jsonInt:
|
||||||
setBiggestInt(a, getInt(p))
|
setBiggestInt(a, getInt(p))
|
||||||
next(p)
|
next(p)
|
||||||
|
|
@ -243,22 +254,22 @@ proc to*[T](data: string): T =
|
||||||
## reads data and transforms it to a ``T``.
|
## reads data and transforms it to a ``T``.
|
||||||
var tab = initTable[BiggestInt, pointer]()
|
var tab = initTable[BiggestInt, pointer]()
|
||||||
loadAny(newStringStream(data), toAny(result), tab)
|
loadAny(newStringStream(data), toAny(result), tab)
|
||||||
|
|
||||||
when isMainModule:
|
when not defined(testing) and isMainModule:
|
||||||
template testit(x: expr) = echo($$to[type(x)]($$x))
|
template testit(x: expr) = echo($$to[type(x)]($$x))
|
||||||
|
|
||||||
var x: array[0..4, array[0..4, string]] = [
|
var x: array[0..4, array[0..4, string]] = [
|
||||||
["test", "1", "2", "3", "4"], ["test", "1", "2", "3", "4"],
|
["test", "1", "2", "3", "4"], ["test", "1", "2", "3", "4"],
|
||||||
["test", "1", "2", "3", "4"], ["test", "1", "2", "3", "4"],
|
["test", "1", "2", "3", "4"], ["test", "1", "2", "3", "4"],
|
||||||
["test", "1", "2", "3", "4"]]
|
["test", "1", "2", "3", "4"]]
|
||||||
testit(x)
|
testit(x)
|
||||||
var test2: tuple[name: string, s: uint] = ("tuple test", 56u)
|
var test2: tuple[name: string, s: uint] = ("tuple test", 56u)
|
||||||
testit(test2)
|
testit(test2)
|
||||||
|
|
||||||
type
|
type
|
||||||
TE = enum
|
TE = enum
|
||||||
blah, blah2
|
blah, blah2
|
||||||
|
|
||||||
TestObj = object
|
TestObj = object
|
||||||
test, asd: int
|
test, asd: int
|
||||||
case test2: TE
|
case test2: TE
|
||||||
|
|
@ -266,7 +277,7 @@ when isMainModule:
|
||||||
help: string
|
help: string
|
||||||
else:
|
else:
|
||||||
nil
|
nil
|
||||||
|
|
||||||
PNode = ref TNode
|
PNode = ref TNode
|
||||||
TNode = object
|
TNode = object
|
||||||
next, prev: PNode
|
next, prev: PNode
|
||||||
|
|
@ -294,7 +305,7 @@ when isMainModule:
|
||||||
test4.a = "ref string test: A"
|
test4.a = "ref string test: A"
|
||||||
test4.b = "ref string test: B"
|
test4.b = "ref string test: B"
|
||||||
testit(test4)
|
testit(test4)
|
||||||
|
|
||||||
var test5 = @[(0,1),(2,3),(4,5)]
|
var test5 = @[(0,1),(2,3),(4,5)]
|
||||||
testit(test5)
|
testit(test5)
|
||||||
|
|
||||||
|
|
@ -305,7 +316,7 @@ when isMainModule:
|
||||||
echo($$test7)
|
echo($$test7)
|
||||||
testit(test7)
|
testit(test7)
|
||||||
|
|
||||||
type
|
type
|
||||||
TA {.inheritable.} = object
|
TA {.inheritable.} = object
|
||||||
TB = object of TA
|
TB = object of TA
|
||||||
f: int
|
f: int
|
||||||
|
|
|
||||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue