merged devel into epc

This commit is contained in:
Simon Hafner 2015-05-08 06:40:34 +05:00
commit f5cca89610
183 changed files with 5295 additions and 4043 deletions

1
.gitignore vendored
View file

@ -41,4 +41,3 @@ xcuserdata/
/testresults.html /testresults.html
/testresults.json /testresults.json
testament.db testament.db
/csources/

3
.gitmodules vendored Normal file
View file

@ -0,0 +1,3 @@
[submodule "csources"]
path = csources
url = ../../nim-lang/csources.git

4
build.sh Normal file → Executable file
View 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"

View file

@ -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) =

View file

@ -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)

View file

@ -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)

View file

@ -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:

View file

@ -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:

View file

@ -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)

View file

@ -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

View file

@ -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"))

View file

@ -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

View file

@ -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]

View file

@ -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)")

View file

@ -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

View file

@ -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])

View file

@ -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:

View file

@ -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

View file

@ -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

View file

@ -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.

View file

@ -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

View file

@ -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
View 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)

View 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

View 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)

View file

@ -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)

View file

@ -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

View file

@ -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, @[

View file

@ -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:

View file

@ -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

View file

@ -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)

View file

@ -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

View file

@ -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

View file

@ -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)

View file

@ -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)

View file

@ -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:

View file

@ -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]]

View file

@ -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))

View file

@ -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)

View file

@ -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)

View file

@ -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

View file

@ -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 =

View file

@ -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}

View file

@ -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)

View file

@ -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
View 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)

View file

@ -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

View file

@ -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

@ -0,0 +1 @@
Subproject commit 15724e2e1f3e7749d508dfcd995e84fea2850802

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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.

View file

@ -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
View 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``).

View file

@ -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.

View file

@ -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

View file

@ -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)

View file

@ -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)

View file

@ -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)", [])

View file

@ -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)

View file

@ -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)

View file

@ -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:

View file

@ -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)

View file

@ -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")

View file

@ -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

View file

@ -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))

View file

@ -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)

View file

@ -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, "&amp;") of '&': add(dest, "&amp;")
of '<': add(dest, "&lt;") of '<': add(dest, "&lt;")
of '>': add(dest, "&gt;") of '>': add(dest, "&gt;")
of '\"': add(dest, "&quot;") of '\"': add(dest, "&quot;")
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>!"

View file

@ -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>".}

View file

@ -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)

View file

@ -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)

View file

@ -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")

View file

@ -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()

View file

@ -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.} =

View file

@ -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)

View file

@ -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()

View file

@ -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

View file

@ -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

View file

@ -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"]

View file

@ -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]

View file

@ -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"

View file

@ -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()

View file

@ -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)

View file

@ -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)

View file

@ -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:

View file

@ -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"

View file

@ -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")

View file

@ -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()

View file

@ -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()

View file

@ -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

View file

@ -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

View file

@ -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>"""

View file

@ -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] = @[]

View file

@ -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.} =

View file

@ -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

View file

@ -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()
"""

View file

@ -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)

View file

@ -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