mirror of
https://github.com/nim-lang/Nim.git
synced 2026-06-05 03:14:08 +00:00
merged devel into epc
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -41,4 +41,3 @@ xcuserdata/
|
||||
/testresults.html
|
||||
/testresults.json
|
||||
testament.db
|
||||
/csources/
|
||||
|
||||
3
.gitmodules
vendored
Normal file
3
.gitmodules
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
[submodule "csources"]
|
||||
path = csources
|
||||
url = ../../nim-lang/csources.git
|
||||
4
build.sh
Normal file → Executable file
4
build.sh
Normal file → Executable file
@@ -2,8 +2,8 @@
|
||||
set -e
|
||||
set -x
|
||||
|
||||
if [ ! -d "csources" ]; then
|
||||
git clone --depth 1 https://github.com/nim-lang/csources.git
|
||||
if [ ! -e csources/.git ]; then
|
||||
git submodule update --init --depth 1
|
||||
fi
|
||||
|
||||
cd "csources"
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#
|
||||
#
|
||||
# The Nim Compiler
|
||||
# (c) Copyright 2013 Andreas Rumpf
|
||||
# (c) Copyright 2015 Andreas Rumpf
|
||||
#
|
||||
# See the file "copying.txt", included in this
|
||||
# distribution, for details about the copyright.
|
||||
@@ -296,6 +296,7 @@ const
|
||||
sfCompileToCpp* = sfInfixCall # compile the module as C++ code
|
||||
sfCompileToObjc* = sfNamedParamCall # compile the module as Objective-C code
|
||||
sfExperimental* = sfOverriden # module uses the .experimental switch
|
||||
sfGoto* = sfOverriden # var is used for 'goto' code generation
|
||||
|
||||
const
|
||||
# getting ready for the future expr/stmt merge
|
||||
@@ -529,19 +530,20 @@ type
|
||||
TMagic* = enum # symbols that require compiler magic:
|
||||
mNone,
|
||||
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,
|
||||
mParseExprToAst, mParseStmtToAst, mExpandToAst, mQuoteAst,
|
||||
mUnaryLt, mInc, mDec, mOrd, mNew, mNewFinalize, mNewSeq, mLengthOpenArray,
|
||||
mLengthStr, mLengthArray, mLengthSeq, mIncl, mExcl, mCard, mChr, mGCref,
|
||||
mGCunref,
|
||||
mLengthStr, mLengthArray, mLengthSeq, mXLenStr, mXLenSeq,
|
||||
mIncl, mExcl, mCard, mChr,
|
||||
mGCref, mGCunref,
|
||||
|
||||
mAddI, mSubI, mMulI, mDivI, mModI, mAddI64, mSubI64, mMulI64,
|
||||
mDivI64, mModI64, mSucc, mPred,
|
||||
mAddF64, mSubF64, mMulF64, mDivF64,
|
||||
|
||||
mShrI, mShlI, mBitandI, mBitorI, mBitxorI, mMinI, mMaxI,
|
||||
mShrI64, mShlI64, mBitandI64, mBitorI64, mBitxorI64, mMinI64, mMaxI64,
|
||||
mShrI64, mShlI64, mBitandI64, mBitorI64, mBitxorI64,
|
||||
mMinF64, mMaxF64, mAddU, mSubU, mMulU,
|
||||
mDivU, mModU, mEqI, mLeI,
|
||||
mLtI,
|
||||
@@ -550,7 +552,7 @@ type
|
||||
mEqEnum, mLeEnum, mLtEnum, mEqCh, mLeCh, mLtCh, mEqB, mLeB, mLtB, mEqRef,
|
||||
mEqUntracedRef, mLePtr, mLtPtr, mEqCString, mXor, mEqProc, mUnaryMinusI,
|
||||
mUnaryMinusI64, mAbsI, mAbsI64, mNot,
|
||||
mUnaryPlusI, mBitnotI, mUnaryPlusI64,
|
||||
mUnaryPlusI, mBitnotI,
|
||||
mBitnotI64, mUnaryPlusF64, mUnaryMinusF64, mAbsF64, mZe8ToI, mZe8ToI64,
|
||||
mZe16ToI, mZe16ToI64, mZe32ToI64, mZeIToI64, mToU8, mToU16, mToU32,
|
||||
mToFloat, mToBiggestFloat, mToInt, mToBiggestInt, mCharToStr, mBoolToStr,
|
||||
@@ -589,11 +591,12 @@ type
|
||||
const
|
||||
ctfeWhitelist* = {mNone, mUnaryLt, mSucc,
|
||||
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,
|
||||
mDivI64, mModI64, mAddF64, mSubF64, mMulF64, mDivF64,
|
||||
mShrI, mShlI, mBitandI, mBitorI, mBitxorI, mMinI, mMaxI,
|
||||
mShrI64, mShlI64, mBitandI64, mBitorI64, mBitxorI64, mMinI64, mMaxI64,
|
||||
mShrI64, mShlI64, mBitandI64, mBitorI64, mBitxorI64,
|
||||
mMinF64, mMaxF64, mAddU, mSubU, mMulU,
|
||||
mDivU, mModU, mEqI, mLeI,
|
||||
mLtI,
|
||||
@@ -602,7 +605,7 @@ const
|
||||
mEqEnum, mLeEnum, mLtEnum, mEqCh, mLeCh, mLtCh, mEqB, mLeB, mLtB, mEqRef,
|
||||
mEqProc, mEqUntracedRef, mLePtr, mLtPtr, mEqCString, mXor, mUnaryMinusI,
|
||||
mUnaryMinusI64, mAbsI, mAbsI64, mNot,
|
||||
mUnaryPlusI, mBitnotI, mUnaryPlusI64,
|
||||
mUnaryPlusI, mBitnotI,
|
||||
mBitnotI64, mUnaryPlusF64, mUnaryMinusF64, mAbsF64, mZe8ToI, mZe8ToI64,
|
||||
mZe16ToI, mZe16ToI64, mZe32ToI64, mZeIToI64, mToU8, mToU16, mToU32,
|
||||
mToFloat, mToBiggestFloat, mToInt, mToBiggestInt, mCharToStr, mBoolToStr,
|
||||
@@ -1170,7 +1173,9 @@ proc newType*(kind: TTypeKind, owner: PSym): PType =
|
||||
result.lockLevel = UnspecifiedLockLevel
|
||||
when debugIds:
|
||||
registerId(result)
|
||||
#if result.id < 2000:
|
||||
#if result.id == 92231:
|
||||
# echo "KNID ", kind
|
||||
# writeStackTrace()
|
||||
# messageOut(typeKindToStr[kind] & ' has id: ' & toString(result.id))
|
||||
|
||||
proc mergeLoc(a: var TLoc, b: TLoc) =
|
||||
|
||||
@@ -566,8 +566,6 @@ proc binaryArith(p: BProc, e: PNode, d: var TLoc, op: TMagic) =
|
||||
"($4)($1 & $2)", # BitandI64
|
||||
"($4)($1 | $2)", # BitorI64
|
||||
"($4)($1 ^ $2)", # BitxorI64
|
||||
"(($1 <= $2) ? $1 : $2)", # MinI64
|
||||
"(($1 >= $2) ? $1 : $2)", # MaxI64
|
||||
"(($1 <= $2) ? $1 : $2)", # MinF64
|
||||
"(($1 >= $2) ? $1 : $2)", # MaxF64
|
||||
"($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
|
||||
"$1", # UnaryPlusI
|
||||
"($3)((NU$2) ~($1))", # BitnotI
|
||||
"$1", # UnaryPlusI64
|
||||
"($3)((NU$2) ~($1))", # BitnotI64
|
||||
"$1", # UnaryPlusF64
|
||||
"-($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) =
|
||||
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
|
||||
# simply wrap them in a struct? --> Losing auto vectorization then?
|
||||
#if e[0].kind != nkBracketExpr:
|
||||
@@ -685,19 +682,29 @@ proc genDeref(p: BProc, e: PNode, d: var TLoc; enforceDeref=false) =
|
||||
else:
|
||||
var a: TLoc
|
||||
initLocExprSingleUse(p, e.sons[0], a)
|
||||
let typ = skipTypes(a.t, abstractInst)
|
||||
case typ.kind
|
||||
of tyRef:
|
||||
d.s = OnHeap
|
||||
of tyVar:
|
||||
d.s = OnUnknown
|
||||
if tfVarIsPtr notin typ.flags and p.module.compileToCpp and
|
||||
e.kind == nkHiddenDeref:
|
||||
if d.k == locNone:
|
||||
let typ = skipTypes(a.t, abstractInst)
|
||||
# dest = *a; <-- We do not know that 'dest' is on the heap!
|
||||
# It is completely wrong to set 'd.s' here, unless it's not yet
|
||||
# been assigned to.
|
||||
case typ.kind
|
||||
of tyRef:
|
||||
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))
|
||||
return
|
||||
of tyPtr:
|
||||
d.s = OnUnknown # BUGFIX!
|
||||
else: internalError(e.info, "genDeref " & $a.t.kind)
|
||||
if enforceDeref and mt == ctPtrToArray:
|
||||
# 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.
|
||||
@@ -957,8 +964,11 @@ proc genEcho(p: BProc, n: PNode) =
|
||||
var args: Rope = nil
|
||||
var a: TLoc
|
||||
for i in countup(0, n.len-1):
|
||||
initLocExpr(p, n.sons[i], a)
|
||||
addf(args, ", $1? ($1)->data:\"nil\"", [rdLoc(a)])
|
||||
if n.sons[i].skipConv.kind == nkNilLit:
|
||||
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",
|
||||
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")
|
||||
of tyCString:
|
||||
useStringh(p.module)
|
||||
if op == mHigh: unaryExpr(p, e, d, "(strlen($1)-1)")
|
||||
else: unaryExpr(p, e, d, "strlen($1)")
|
||||
if op == mHigh: unaryExpr(p, e, d, "($1 ? (strlen($1)-1) : -1)")
|
||||
else: unaryExpr(p, e, d, "($1 ? strlen($1) : 0)")
|
||||
of tyString, tySequence:
|
||||
if not p.module.compileToCpp:
|
||||
if op == mHigh: unaryExpr(p, e, d, "($1->Sup.len-1)")
|
||||
else: unaryExpr(p, e, d, "$1->Sup.len")
|
||||
if op == mHigh: unaryExpr(p, e, d, "($1 ? ($1->Sup.len-1) : -1)")
|
||||
else: unaryExpr(p, e, d, "($1 ? $1->Sup.len : 0)")
|
||||
else:
|
||||
if op == mHigh: unaryExpr(p, e, d, "($1->len-1)")
|
||||
else: unaryExpr(p, e, d, "$1->len")
|
||||
if op == mHigh: unaryExpr(p, e, d, "($1 ? ($1->len-1) : -1)")
|
||||
else: unaryExpr(p, e, d, "($1 ? $1->len : 0)")
|
||||
of tyArray, tyArrayConstr:
|
||||
# YYY: length(sideeffect) is optimized away incorrectly?
|
||||
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 mLengthArray, mHigh, mLengthStr, mLengthSeq, mLengthOpenArray:
|
||||
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 mGCunref: unaryStmt(p, e, d, "#nimGCunref($1);$n")
|
||||
of mSetLengthStr: genSetLengthStr(p, e, d)
|
||||
|
||||
@@ -175,9 +175,18 @@ proc genBreakState(p: BProc, n: PNode) =
|
||||
|
||||
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) =
|
||||
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
|
||||
if sfGlobal in v.flags:
|
||||
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)
|
||||
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) =
|
||||
# first pass: Generate array of computed labels:
|
||||
var casePos = -1
|
||||
@@ -529,12 +551,7 @@ proc genBreakStmt(p: BProc, t: PNode) =
|
||||
lineF(p, cpsStmts, "goto $1;$n", [label])
|
||||
|
||||
proc getRaiseFrmt(p: BProc): string =
|
||||
if p.module.compileToCpp:
|
||||
result = "throw NimException($1, $2);$n"
|
||||
elif getCompilerProc("Exception") != nil:
|
||||
result = "#raiseException((#Exception*)$1, $2);$n"
|
||||
else:
|
||||
result = "#raiseException((#E_Base*)$1, $2);$n"
|
||||
result = "#raiseException((#Exception*)$1, $2);$n"
|
||||
|
||||
proc genRaiseStmt(p: BProc, t: PNode) =
|
||||
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",
|
||||
"if ($1 == $2) goto $3;$n")
|
||||
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 =
|
||||
var length = sonsLen(t)
|
||||
@@ -772,11 +792,8 @@ proc genTryCpp(p: BProc, t: PNode, d: var TLoc) =
|
||||
# finallyPart();
|
||||
if not isEmptyType(t.typ) and d.k == locNone:
|
||||
getTemp(p, t.typ, d)
|
||||
var
|
||||
exc: Rope
|
||||
i, length, blen: int
|
||||
genLineDir(p, t)
|
||||
exc = getTempName()
|
||||
let exc = getTempName()
|
||||
if getCompilerProc("Exception") != nil:
|
||||
discard cgsym(p.module, "Exception")
|
||||
else:
|
||||
@@ -784,20 +801,23 @@ proc genTryCpp(p: BProc, t: PNode, d: var TLoc) =
|
||||
add(p.nestedTryStmts, t)
|
||||
startBlock(p, "try {$n")
|
||||
expr(p, t.sons[0], d)
|
||||
length = sonsLen(t)
|
||||
let length = sonsLen(t)
|
||||
endBlock(p, ropecg(p.module, "} catch (NimException& $1) {$n", [exc]))
|
||||
if optStackTrace in p.options:
|
||||
linefmt(p, cpsStmts, "#setFrame((TFrame*)&F);$n")
|
||||
linefmt(p, cpsStmts, "#setFrame((TFrame*)&FR);$n")
|
||||
inc p.inExceptBlock
|
||||
i = 1
|
||||
var i = 1
|
||||
var catchAllPresent = false
|
||||
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 blen == 1:
|
||||
# general except section:
|
||||
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:
|
||||
var orExpr: Rope = nil
|
||||
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)",
|
||||
[exc, genTypeInfo(p.module, t.sons[i].sons[j].typ)])
|
||||
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)
|
||||
|
||||
# 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")
|
||||
linefmt(p, cpsStmts, "#popSafePoint();$n")
|
||||
if optStackTrace in p.options:
|
||||
linefmt(p, cpsStmts, "#setFrame((TFrame*)&F);$n")
|
||||
linefmt(p, cpsStmts, "#setFrame((TFrame*)&FR);$n")
|
||||
inc p.inExceptBlock
|
||||
var i = 1
|
||||
while (i < length) and (t.sons[i].kind == nkExceptBranch):
|
||||
@@ -976,12 +999,19 @@ proc genAsmStmt(p: BProc, t: PNode) =
|
||||
else:
|
||||
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) =
|
||||
var s = genAsmOrEmitStmt(p, t.sons[1])
|
||||
if p.prc == nil:
|
||||
# top level emit pragma?
|
||||
genCLineDir(p.module.s[cfsProcHeaders], t.info)
|
||||
add(p.module.s[cfsProcHeaders], s)
|
||||
let section = determineSection(t[1])
|
||||
genCLineDir(p.module.s[section], t.info)
|
||||
add(p.module.s[section], s)
|
||||
else:
|
||||
genLineDir(p, t)
|
||||
line(p, cpsStmts, s)
|
||||
@@ -1065,7 +1095,9 @@ proc asgnFieldDiscriminant(p: BProc, e: PNode) =
|
||||
|
||||
proc genAsgn(p: BProc, e: PNode, fastAsgn: bool) =
|
||||
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
|
||||
initLocExpr(p, e.sons[0], a)
|
||||
if fastAsgn: incl(a.flags, lfNoDeepCopy)
|
||||
|
||||
@@ -11,20 +11,21 @@
|
||||
|
||||
# ------------------------- 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 =
|
||||
# nimrod and C++ share some keywords
|
||||
# it's more efficient to test the whole nimrod keywords range
|
||||
# Nim and C++ share some keywords
|
||||
# it's more efficient to test the whole Nim keywords range
|
||||
case w.id
|
||||
of ccgKeywordsLow..ccgKeywordsHigh,
|
||||
nimKeywordsLow..nimKeywordsHigh,
|
||||
ord(wInline): return true
|
||||
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 =
|
||||
result = s.loc.r
|
||||
if result == nil:
|
||||
@@ -110,7 +111,7 @@ proc mapSetType(typ: PType): TCTypeKind =
|
||||
else: result = ctArray
|
||||
|
||||
proc mapType(typ: PType): TCTypeKind =
|
||||
## Maps a nimrod type to a C type
|
||||
## Maps a Nim type to a C type
|
||||
case typ.kind
|
||||
of tyNone, tyStmt: result = ctVoid
|
||||
of tyBool: result = ctBool
|
||||
@@ -379,7 +380,7 @@ proc mangleRecFieldName(field: PSym, rectype: PType): Rope =
|
||||
({sfImportc, sfExportc} * rectype.sym.flags != {}):
|
||||
result = field.loc.r
|
||||
else:
|
||||
result = rope(mangleField(field.name.s))
|
||||
result = rope(mangleField(field.name))
|
||||
if result == nil: internalError(field.info, "mangleRecFieldName")
|
||||
|
||||
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)
|
||||
else:
|
||||
inc i
|
||||
|
||||
|
||||
if chunkStart != 0:
|
||||
result.add cppName.data.substr(chunkStart)
|
||||
else:
|
||||
|
||||
@@ -176,7 +176,7 @@ proc mangle*(name: string): string =
|
||||
result = newStringOfCap(name.len)
|
||||
case name[0]
|
||||
of Letters:
|
||||
result.add(name[0].toLower)
|
||||
result.add(name[0])
|
||||
of Digits:
|
||||
result.add("N" & name[0])
|
||||
else:
|
||||
|
||||
@@ -319,7 +319,7 @@ proc processSwitch(switch, arg: string, pass: TCmdLinePass, info: TLineInfo) =
|
||||
undefSymbol(arg)
|
||||
of "symbol":
|
||||
expectArg(switch, arg, pass, info)
|
||||
declareSymbol(arg)
|
||||
# deprecated, do nothing
|
||||
of "compile":
|
||||
expectArg(switch, arg, pass, info)
|
||||
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)
|
||||
elif theOS != platform.hostOS:
|
||||
setTarget(theOS, targetCPU)
|
||||
condsyms.initDefines()
|
||||
of "cpu":
|
||||
expectArg(switch, arg, pass, info)
|
||||
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)
|
||||
elif cpu != platform.hostCPU:
|
||||
setTarget(targetOS, cpu)
|
||||
condsyms.initDefines()
|
||||
of "run", "r":
|
||||
expectNoArg(switch, arg, pass, info)
|
||||
incl(gGlobalOptions, optRun)
|
||||
|
||||
@@ -9,71 +9,69 @@
|
||||
|
||||
# This module handles the conditional symbols.
|
||||
|
||||
import
|
||||
import
|
||||
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.
|
||||
var gSymbols: StringTableRef
|
||||
|
||||
proc defineSymbol*(symbol: string) =
|
||||
const
|
||||
catNone = "false"
|
||||
|
||||
proc defineSymbol*(symbol: string) =
|
||||
gSymbols[symbol] = "true"
|
||||
|
||||
proc declareSymbol*(symbol: string) =
|
||||
gSymbols[symbol] = "unknown"
|
||||
proc undefSymbol*(symbol: string) =
|
||||
gSymbols[symbol] = catNone
|
||||
|
||||
proc undefSymbol*(symbol: string) =
|
||||
gSymbols[symbol] = "false"
|
||||
|
||||
proc isDefined*(symbol: string): bool =
|
||||
proc isDefined*(symbol: string): bool =
|
||||
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 isDeclared*(symbol: PIdent): bool = gSymbols.hasKey(symbol.s)
|
||||
|
||||
iterator definedSymbolNames*: string =
|
||||
for key, val in pairs(gSymbols):
|
||||
if val == "true": yield key
|
||||
if val != catNone: yield key
|
||||
|
||||
proc countDefinedSymbols*(): int =
|
||||
proc countDefinedSymbols*(): int =
|
||||
result = 0
|
||||
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
|
||||
# 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*() =
|
||||
proc initDefines*() =
|
||||
gSymbols = newStringTable(modeStyleInsensitive)
|
||||
defineSymbol("nimrod") # 'nimrod' is always defined
|
||||
# for bootstrapping purposes and old code:
|
||||
@@ -90,58 +88,3 @@ proc initDefines*() =
|
||||
defineSymbol("nimalias")
|
||||
defineSymbol("nimlocks")
|
||||
defineSymbol("nimnode")
|
||||
|
||||
# add platform specific symbols:
|
||||
for c in low(CPU)..high(CPU):
|
||||
declareSymbol("cpu" & $CPU[c].bit)
|
||||
declareSymbol(normalize(EndianToStr[CPU[c].endian]))
|
||||
declareSymbol(CPU[c].name)
|
||||
for o in low(platform.OS)..high(platform.OS):
|
||||
declareSymbol(platform.OS[o].name)
|
||||
|
||||
for a in additionalSymbols:
|
||||
declareSymbol(a)
|
||||
|
||||
# -----------------------------------------------------------
|
||||
case targetCPU
|
||||
of cpuI386: defineSymbol("x86")
|
||||
of cpuIa64: defineSymbol("itanium")
|
||||
of cpuAmd64: defineSymbol("x8664")
|
||||
else: discard
|
||||
case targetOS
|
||||
of osDos:
|
||||
defineSymbol("msdos")
|
||||
of osWindows:
|
||||
defineSymbol("mswindows")
|
||||
defineSymbol("win32")
|
||||
of osLinux, osMorphos, osSkyos, osIrix, osPalmos, osQnx, osAtari, osAix,
|
||||
osHaiku, osVxWorks:
|
||||
# these are all 'unix-like'
|
||||
defineSymbol("unix")
|
||||
defineSymbol("posix")
|
||||
of osSolaris:
|
||||
defineSymbol("sunos")
|
||||
defineSymbol("unix")
|
||||
defineSymbol("posix")
|
||||
of osNetbsd, osFreebsd, osOpenbsd:
|
||||
defineSymbol("unix")
|
||||
defineSymbol("bsd")
|
||||
defineSymbol("posix")
|
||||
of osMacos:
|
||||
defineSymbol("macintosh")
|
||||
of osMacosx:
|
||||
defineSymbol("macintosh")
|
||||
defineSymbol("unix")
|
||||
defineSymbol("posix")
|
||||
else: discard
|
||||
defineSymbol("cpu" & $CPU[targetCPU].bit)
|
||||
defineSymbol(normalize(EndianToStr[CPU[targetCPU].endian]))
|
||||
defineSymbol(CPU[targetCPU].name)
|
||||
defineSymbol(platform.OS[targetOS].name)
|
||||
declareSymbol("emulatedthreadvars")
|
||||
if platform.OS[targetOS].props.contains(ospLacksThreadVars):
|
||||
defineSymbol("emulatedthreadvars")
|
||||
case targetOS
|
||||
of osSolaris, osNetbsd, osFreebsd, osOpenbsd, osMacosx:
|
||||
defineSymbol("nimRawSetjmp")
|
||||
else: discard
|
||||
|
||||
@@ -15,9 +15,9 @@
|
||||
import
|
||||
lists, ropes, os, strutils, osproc, platform, condsyms, options, msgs, crc
|
||||
|
||||
type
|
||||
TSystemCC* = enum
|
||||
ccNone, ccGcc, ccLLVM_Gcc, ccCLang, ccLcc, ccBcc, ccDmc, ccWcc, ccVcc,
|
||||
type
|
||||
TSystemCC* = enum
|
||||
ccNone, ccGcc, ccLLVM_Gcc, ccCLang, ccLcc, ccBcc, ccDmc, ccWcc, ccVcc,
|
||||
ccTcc, ccPcc, ccUcc, ccIcl
|
||||
TInfoCCProp* = enum # properties of the C compiler:
|
||||
hasSwitchRange, # CC allows ranges in switch statements (GNU C)
|
||||
@@ -54,7 +54,7 @@ type
|
||||
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:
|
||||
# http://cmake.org/gitweb?p=cmake.git;a=tree;f=Modules/Platform;
|
||||
|
||||
@@ -136,7 +136,7 @@ compiler icl:
|
||||
result = vcc()
|
||||
else:
|
||||
result = gcc()
|
||||
|
||||
|
||||
result.name = "icl"
|
||||
result.compilerExe = "icl"
|
||||
result.linkerExe = "icl"
|
||||
@@ -317,7 +317,7 @@ compiler ucc:
|
||||
packedPragma: "", # XXX: not supported yet
|
||||
props: {})
|
||||
|
||||
const
|
||||
const
|
||||
CC*: array[succ(low(TSystemCC))..high(TSystemCC), TInfoCC] = [
|
||||
gcc(),
|
||||
llvmGcc(),
|
||||
@@ -346,7 +346,7 @@ var
|
||||
proc libNameTmpl(): string {.inline.} =
|
||||
result = if targetOS == osWindows: "$1.lib" else: "lib$1.a"
|
||||
|
||||
var
|
||||
var
|
||||
toLink, toCompile, externalToCompile: TLinkedList
|
||||
linkOptions: string = ""
|
||||
compileOptions: string = ""
|
||||
@@ -355,8 +355,8 @@ var
|
||||
proc nameToCC*(name: string): TSystemCC =
|
||||
## Returns the kind of compiler referred to by `name`, or ccNone
|
||||
## if the name doesn't refer to any known compiler.
|
||||
for i in countup(succ(ccNone), high(TSystemCC)):
|
||||
if cmpIgnoreStyle(name, CC[i].name) == 0:
|
||||
for i in countup(succ(ccNone), high(TSystemCC)):
|
||||
if cmpIgnoreStyle(name, CC[i].name) == 0:
|
||||
return i
|
||||
result = ccNone
|
||||
|
||||
@@ -375,8 +375,8 @@ proc getConfigVar(c: TSystemCC, suffix: string): string =
|
||||
|
||||
if (platform.hostOS != targetOS or platform.hostCPU != targetCPU) and
|
||||
optCompileOnly notin gGlobalOptions:
|
||||
let fullCCname = platform.CPU[targetCPU].name & '.' &
|
||||
platform.OS[targetOS].name & '.' &
|
||||
let fullCCname = platform.CPU[targetCPU].name & '.' &
|
||||
platform.OS[targetOS].name & '.' &
|
||||
CC[c].name & fullSuffix
|
||||
result = getConfigVar(fullCCname)
|
||||
if result.len == 0:
|
||||
@@ -385,7 +385,7 @@ proc getConfigVar(c: TSystemCC, suffix: string): string =
|
||||
else:
|
||||
result = getConfigVar(CC[c].name & fullSuffix)
|
||||
|
||||
proc setCC*(ccname: string) =
|
||||
proc setCC*(ccname: string) =
|
||||
cCompiler = nameToCC(ccname)
|
||||
if cCompiler == ccNone: rawMessage(errUnknownCcompiler, ccname)
|
||||
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)
|
||||
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, " ")
|
||||
add(dest, src)
|
||||
|
||||
proc addLinkOption*(option: string) =
|
||||
addOpt(linkOptions, option)
|
||||
|
||||
proc addCompileOption*(option: string) =
|
||||
if strutils.find(compileOptions, option, 0) < 0:
|
||||
proc addCompileOption*(option: string) =
|
||||
if strutils.find(compileOptions, option, 0) < 0:
|
||||
addOpt(compileOptions, option)
|
||||
|
||||
proc initVars*() =
|
||||
proc initVars*() =
|
||||
# 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)
|
||||
defineSymbol(CC[cCompiler].name)
|
||||
@@ -414,10 +414,10 @@ proc initVars*() =
|
||||
if len(ccompilerpath) == 0:
|
||||
ccompilerpath = getConfigVar(cCompiler, ".path")
|
||||
|
||||
proc completeCFilePath*(cfile: string, createSubDir: bool = true): string =
|
||||
proc completeCFilePath*(cfile: string, createSubDir: bool = true): string =
|
||||
result = completeGeneratedFilePath(cfile, createSubDir)
|
||||
|
||||
proc toObjFile*(filename: string): string =
|
||||
proc toObjFile*(filename: string): string =
|
||||
# Object file for compilation
|
||||
result = changeFileExt(filename, CC[cCompiler].objExt)
|
||||
|
||||
@@ -449,22 +449,22 @@ proc execExternalProgram*(cmd: string, prettyCmd = "") =
|
||||
if execWithEcho(cmd, prettyCmd) != 0:
|
||||
rawMessage(errExecutionOfProgramFailed, "")
|
||||
|
||||
proc generateScript(projectFile: string, script: Rope) =
|
||||
proc generateScript(projectFile: string, script: Rope) =
|
||||
let (dir, name, ext) = splitFile(projectFile)
|
||||
writeRope(script, dir / addFileExt("compile_" & name,
|
||||
writeRope(script, dir / addFileExt("compile_" & name,
|
||||
platform.OS[targetOS].scriptExt))
|
||||
|
||||
proc getOptSpeed(c: TSystemCC): string =
|
||||
proc getOptSpeed(c: TSystemCC): string =
|
||||
result = getConfigVar(c, ".options.speed")
|
||||
if result == "":
|
||||
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")
|
||||
if result == "":
|
||||
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")
|
||||
if result == "":
|
||||
result = CC[c].optSize # use default settings from this file
|
||||
@@ -476,7 +476,7 @@ proc noAbsolutePaths: bool {.inline.} =
|
||||
# `optGenMapping` is included here for niminst.
|
||||
result = gGlobalOptions * {optGenScript, optGenMapping} != {}
|
||||
|
||||
const
|
||||
const
|
||||
specialFileA = 42
|
||||
specialFileB = 42
|
||||
|
||||
@@ -488,7 +488,7 @@ proc add(s: var string, many: openArray[string]) =
|
||||
proc cFileSpecificOptions(cfilename: string): string =
|
||||
result = compileOptions
|
||||
var trunk = splitFile(cfilename).name
|
||||
if optCDebug in gGlobalOptions:
|
||||
if optCDebug in gGlobalOptions:
|
||||
var key = trunk & ".debug"
|
||||
if existsConfigVar(key): addOpt(result, getConfigVar(key))
|
||||
else: addOpt(result, getDebug(cCompiler))
|
||||
@@ -528,17 +528,17 @@ proc getLinkerExe(compiler: TSystemCC): string =
|
||||
elif gMixedMode and gCmd != cmdCompileToCpp: CC[compiler].cppCompiler
|
||||
else: compiler.getCompilerExe
|
||||
|
||||
proc getCompileCFileCmd*(cfilename: string, isExternal = false): string =
|
||||
proc getCompileCFileCmd*(cfilename: string, isExternal = false): string =
|
||||
var c = cCompiler
|
||||
var options = cFileSpecificOptions(cfilename)
|
||||
var exe = getConfigVar(c, ".exe")
|
||||
if exe.len == 0: exe = c.getCompilerExe
|
||||
|
||||
|
||||
if needsExeExt(): exe = addFileExt(exe, "exe")
|
||||
if optGenDynLib in gGlobalOptions and
|
||||
ospNeedsPIC in platform.OS[targetOS].props:
|
||||
add(options, ' ' & CC[c].pic)
|
||||
|
||||
|
||||
var includeCmd, compilePattern: string
|
||||
if not noAbsolutePaths():
|
||||
# compute include paths:
|
||||
@@ -551,7 +551,7 @@ proc getCompileCFileCmd*(cfilename: string, isExternal = false): string =
|
||||
else:
|
||||
includeCmd = ""
|
||||
compilePattern = c.getCompilerExe
|
||||
|
||||
|
||||
var cfile = if noAbsolutePaths(): extractFilename(cfilename)
|
||||
else: cfilename
|
||||
var objfile = if not isExternal or noAbsolutePaths():
|
||||
@@ -580,14 +580,14 @@ proc footprint(filename: string): TCrc32 =
|
||||
extccomp.CC[extccomp.cCompiler].name ><
|
||||
getCompileCFileCmd(filename, true)
|
||||
|
||||
proc externalFileChanged(filename: string): bool =
|
||||
proc externalFileChanged(filename: string): bool =
|
||||
if gCmd notin {cmdCompileToC, cmdCompileToCpp, cmdCompileToOC, cmdCompileToLLVM}:
|
||||
return false
|
||||
|
||||
var crcFile = toGeneratedFile(filename.withPackageName, "crc")
|
||||
var currentCrc = int(footprint(filename))
|
||||
var f: File
|
||||
if open(f, crcFile, fmRead):
|
||||
if open(f, crcFile, fmRead):
|
||||
var line = newStringOfCap(40)
|
||||
if not f.readLine(line): line = "0"
|
||||
close(f)
|
||||
@@ -595,7 +595,7 @@ proc externalFileChanged(filename: string): bool =
|
||||
result = oldCrc != currentCrc
|
||||
else:
|
||||
result = true
|
||||
if result:
|
||||
if result:
|
||||
if open(f, crcFile, fmWrite):
|
||||
f.writeln($currentCrc)
|
||||
close(f)
|
||||
@@ -607,22 +607,22 @@ proc addExternalFileToCompile*(filename: string) =
|
||||
proc compileCFile(list: TLinkedList, script: var Rope, cmds: var TStringSeq,
|
||||
prettyCmds: var TStringSeq, isExternal: bool) =
|
||||
var it = PStrEntry(list.head)
|
||||
while it != nil:
|
||||
while it != nil:
|
||||
inc(fileCounter) # call the C compiler for the .c file:
|
||||
var compileCmd = getCompileCFileCmd(it.data, isExternal)
|
||||
if optCompileOnly notin gGlobalOptions:
|
||||
if optCompileOnly notin gGlobalOptions:
|
||||
add(cmds, compileCmd)
|
||||
let (dir, name, ext) = splitFile(it.data)
|
||||
add(prettyCmds, "CC: " & name)
|
||||
if optGenScript in gGlobalOptions:
|
||||
if optGenScript in gGlobalOptions:
|
||||
add(script, compileCmd)
|
||||
add(script, tnl)
|
||||
it = PStrEntry(it.next)
|
||||
|
||||
proc callCCompiler*(projectfile: string) =
|
||||
var
|
||||
var
|
||||
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
|
||||
# generated
|
||||
fileCounter = 0
|
||||
@@ -634,11 +634,11 @@ proc callCCompiler*(projectfile: string) =
|
||||
echo prettyCmds[idx]
|
||||
compileCFile(toCompile, script, cmds, prettyCmds, false)
|
||||
compileCFile(externalToCompile, script, cmds, prettyCmds, true)
|
||||
if optCompileOnly notin gGlobalOptions:
|
||||
if optCompileOnly notin gGlobalOptions:
|
||||
if gNumberOfProcessors == 0: gNumberOfProcessors = countProcessors()
|
||||
var res = 0
|
||||
if gNumberOfProcessors <= 1:
|
||||
for i in countup(0, high(cmds)):
|
||||
if gNumberOfProcessors <= 1:
|
||||
for i in countup(0, high(cmds)):
|
||||
res = execWithEcho(cmds[i])
|
||||
if res != 0: rawMessage(errExecutionOfProgramFailed, [])
|
||||
elif optListCmd in gGlobalOptions or gVerbosity > 1:
|
||||
@@ -668,7 +668,8 @@ proc callCCompiler*(projectfile: string) =
|
||||
it = PStrEntry(it.next)
|
||||
|
||||
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]
|
||||
else:
|
||||
var linkerExe = getConfigVar(c, ".linkerexe")
|
||||
@@ -685,13 +686,13 @@ proc callCCompiler*(projectfile: string) =
|
||||
else:
|
||||
exefile = splitFile(projectfile).name & platform.OS[targetOS].exeExt
|
||||
builddll = ""
|
||||
if options.outFile.len > 0:
|
||||
if options.outFile.len > 0:
|
||||
exefile = options.outFile.expandTilde
|
||||
if not noAbsolutePaths():
|
||||
if not exefile.isAbsolute():
|
||||
exefile = joinPath(splitFile(projectfile).dir, exefile)
|
||||
exefile = quoteShell(exefile)
|
||||
let linkOptions = getLinkOptions() & " " &
|
||||
let linkOptions = getLinkOptions() & " " &
|
||||
getConfigVar(cCompiler, ".options.linker")
|
||||
linkCmd = quoteShell(linkCmd % ["builddll", builddll,
|
||||
"buildgui", buildgui, "options", linkOptions, "objfiles", objfiles,
|
||||
@@ -714,26 +715,26 @@ proc callCCompiler*(projectfile: string) =
|
||||
add(script, tnl)
|
||||
generateScript(projectfile, script)
|
||||
|
||||
proc genMappingFiles(list: TLinkedList): Rope =
|
||||
proc genMappingFiles(list: TLinkedList): Rope =
|
||||
var it = PStrEntry(list.head)
|
||||
while it != nil:
|
||||
while it != nil:
|
||||
addf(result, "--file:r\"$1\"$N", [rope(it.data)])
|
||||
it = PStrEntry(it.next)
|
||||
|
||||
proc writeMapping*(gSymbolMapping: Rope) =
|
||||
if optGenMapping notin gGlobalOptions: return
|
||||
proc writeMapping*(gSymbolMapping: Rope) =
|
||||
if optGenMapping notin gGlobalOptions: return
|
||||
var code = rope("[C_Files]\n")
|
||||
add(code, genMappingFiles(toCompile))
|
||||
add(code, genMappingFiles(externalToCompile))
|
||||
add(code, "\n[C_Compiler]\nFlags=")
|
||||
add(code, strutils.escape(getCompileOptions()))
|
||||
|
||||
|
||||
add(code, "\n[Linker]\nFlags=")
|
||||
add(code, strutils.escape(getLinkOptions() & " " &
|
||||
add(code, strutils.escape(getLinkOptions() & " " &
|
||||
getConfigVar(cCompiler, ".options.linker")))
|
||||
|
||||
add(code, "\n[Environment]\nlibpath=")
|
||||
add(code, strutils.escape(libpath))
|
||||
|
||||
|
||||
addf(code, "\n[Symbols]$n$1", [gSymbolMapping])
|
||||
writeRope(code, joinPath(gProjectPath, "mapping.txt"))
|
||||
|
||||
@@ -22,7 +22,8 @@ const
|
||||
someLt = {mLtI, mLtI64, mLtF64, mLtU, mLtU64, mLtEnum,
|
||||
mLtCh, mLtB, mLtPtr, mLtStr}
|
||||
|
||||
someLen = {mLengthOpenArray, mLengthStr, mLengthArray, mLengthSeq}
|
||||
someLen = {mLengthOpenArray, mLengthStr, mLengthArray, mLengthSeq,
|
||||
mXLenStr, mXLenSeq}
|
||||
|
||||
someIn = {mInRange, mInSet}
|
||||
|
||||
@@ -34,8 +35,8 @@ const
|
||||
someMul = {mMulI, mMulI64, mMulF64}
|
||||
someDiv = {mDivI, mDivI64, mDivF64}
|
||||
someMod = {mModI, mModI64}
|
||||
someMax = {mMaxI, mMaxI64, mMaxF64}
|
||||
someMin = {mMinI, mMinI64, mMinF64}
|
||||
someMax = {mMaxI, mMaxF64}
|
||||
someMin = {mMinI, mMinF64}
|
||||
|
||||
proc isValue(n: PNode): bool = n.kind in {nkCharLit..nkNilLit}
|
||||
proc isLocation(n: PNode): bool = not n.isValue
|
||||
@@ -122,7 +123,7 @@ proc neg(n: PNode): PNode =
|
||||
let eAsNode = newIntNode(nkIntLit, e.sym.position)
|
||||
if not inSet(n.sons[1], eAsNode): s.add eAsNode
|
||||
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])
|
||||
else:
|
||||
# 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) =
|
||||
for i in 1..n.len-1:
|
||||
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
|
||||
|
||||
@@ -47,7 +47,7 @@ Start: "doc/overview.html"
|
||||
|
||||
[Other]
|
||||
Files: "readme.txt;install.txt;contributors.txt;copying.txt"
|
||||
Files: "configure;makefile"
|
||||
Files: "makefile"
|
||||
Files: "*.ini"
|
||||
Files: "koch.nim"
|
||||
|
||||
@@ -70,6 +70,10 @@ Files: "doc/*.nim"
|
||||
Files: "doc/*.cfg"
|
||||
Files: "compiler/nimfix/*.nim"
|
||||
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/*.cfg"
|
||||
Files: "tools/*.tmpl"
|
||||
@@ -97,13 +101,8 @@ Files: "lib/pure/concurrency/*.cfg"
|
||||
Files: "lib/impure/*.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/sdl/*.nim"
|
||||
Files: "lib/wrappers/x11/*.nim"
|
||||
Files: "lib/wrappers/zip/*.nim"
|
||||
Files: "lib/wrappers/zip/libzip_all.c"
|
||||
|
||||
@@ -115,8 +114,6 @@ Files: "lib/packages/docutils/*.nim"
|
||||
|
||||
[Other]
|
||||
Files: "examples/*.nim"
|
||||
Files: "examples/gtk/*.nim"
|
||||
Files: "examples/0mq/*.nim"
|
||||
Files: "examples/c++iface/*.nim"
|
||||
Files: "examples/objciface/*.nim"
|
||||
Files: "examples/cross_calculator/"
|
||||
@@ -126,12 +123,109 @@ Files: "examples/*.txt"
|
||||
Files: "examples/*.cfg"
|
||||
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]
|
||||
Files: "bin/nim.exe"
|
||||
Files: "bin/nim_debug.exe"
|
||||
Files: "bin/c2nim.exe"
|
||||
Files: "bin/nimgrep.exe"
|
||||
Files: "bin/nimsuggest.exe"
|
||||
Files: "bin/nimble.exe"
|
||||
Files: "bin/*.dll"
|
||||
|
||||
Files: "dist/*.dll"
|
||||
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
|
||||
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"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
|
||||
|
||||
[UnixBin]
|
||||
|
||||
@@ -124,7 +124,7 @@ proc newProc(globals: PGlobals, module: BModule, procDef: PNode,
|
||||
|
||||
const
|
||||
MappedToObject = {tyObject, tyArray, tyArrayConstr, tyTuple, tyOpenArray,
|
||||
tySet, tyVar, tyRef, tyPtr, tyBigNum, tyVarargs}
|
||||
tySet, tyBigNum, tyVarargs}
|
||||
|
||||
proc mapType(typ: PType): TJSTypeKind =
|
||||
let t = skipTypes(typ, abstractInst)
|
||||
@@ -163,7 +163,8 @@ proc mangleName(s: PSym): Rope =
|
||||
add(result, rope(s.id))
|
||||
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
|
||||
|
||||
@@ -280,8 +281,6 @@ const # magic checked op; magic unchecked op; checked op; unchecked op
|
||||
["", "", "($1 & $2)", "($1 & $2)"], # BitandI64
|
||||
["", "", "($1 | $2)", "($1 | $2)"], # BitorI64
|
||||
["", "", "($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
|
||||
["nimMax", "nimMax", "nimMax($1, $2)", "nimMax($1, $2)"], # MaxF64
|
||||
["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)"], # UnaryPlusI
|
||||
["", "", "~($1)", "~($1)"], # BitnotI
|
||||
["", "", "+($1)", "+($1)"], # UnaryPlusI64
|
||||
["", "", "~($1)", "~($1)"], # BitnotI64
|
||||
["", "", "+($1)", "+($1)"], # UnaryPlusF64
|
||||
["", "", "-($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)"], # BitorI64
|
||||
["", "", "($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
|
||||
["nimMax", "nimMax", "nimMax($1, $2)", "nimMax($1, $2)"], # MaxF64
|
||||
["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
|
||||
["", "", "+($1)", "+($1)"], # UnaryPlusI
|
||||
["", "", "~($1)", "~($1)"], # BitnotI
|
||||
["", "", "+($1)", "+($1)"], # UnaryPlusI64
|
||||
["", "", "~($1)", "~($1)"], # BitnotI64
|
||||
["", "", "+($1)", "+($1)"], # UnaryPlusF64
|
||||
["", "", "-($1)", "-($1)"], # UnaryMinusF64
|
||||
@@ -937,6 +932,13 @@ proc genArrayAccess(p: PProc, n: PNode, r: var TCompRes) =
|
||||
r.address = nil
|
||||
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) =
|
||||
case n.sons[0].kind
|
||||
of nkSym:
|
||||
@@ -945,12 +947,16 @@ proc genAddr(p: PProc, n: PNode, r: var TCompRes) =
|
||||
case s.kind
|
||||
of skVar, skLet, skResult:
|
||||
r.kind = resExpr
|
||||
if mapType(n.sons[0].typ) == etyObject:
|
||||
let jsType = mapType(n.typ)
|
||||
if jsType == etyObject:
|
||||
# make addr() a no-op:
|
||||
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
|
||||
elif {sfGlobal, sfAddrTaken} * s.flags != {}:
|
||||
elif {sfGlobal, sfAddrTaken} * s.flags != {} or jsType == etyBaseIndex:
|
||||
# for ease of code generation, we do not distinguish between
|
||||
# sfAddrTaken and sfGlobal.
|
||||
r.typ = etyBaseIndex
|
||||
@@ -992,7 +998,7 @@ proc genSym(p: PProc, n: PNode, r: var TCompRes) =
|
||||
else:
|
||||
r.address = s.loc.r
|
||||
r.res = s.loc.r & "_Idx"
|
||||
elif k != etyObject and {sfAddrTaken, sfGlobal} * s.flags != {}:
|
||||
elif isIndirect(s):
|
||||
r.res = "$1[0]" % [s.loc.r]
|
||||
else:
|
||||
r.res = s.loc.r
|
||||
@@ -1124,7 +1130,7 @@ proc createVar(p: PProc, typ: PType, indirect: bool): Rope =
|
||||
of tyRange, tyGenericInst:
|
||||
result = createVar(p, lastSon(typ), indirect)
|
||||
of tySet:
|
||||
result = rope("{}")
|
||||
result = putToSeq("{}", indirect)
|
||||
of tyBool:
|
||||
result = putToSeq("false", indirect)
|
||||
of tyArray, tyArrayConstr:
|
||||
@@ -1144,6 +1150,7 @@ proc createVar(p: PProc, typ: PType, indirect: bool): Rope =
|
||||
add(result, createVar(p, e, false))
|
||||
inc(i)
|
||||
add(result, "]")
|
||||
if indirect: result = "[$1]" % [result]
|
||||
of tyTuple:
|
||||
result = rope("{")
|
||||
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,
|
||||
createVar(p, t.sons[i], false)])
|
||||
add(result, "}")
|
||||
if indirect: result = "[$1]" % [result]
|
||||
of tyObject:
|
||||
result = rope("{")
|
||||
var c = 0
|
||||
@@ -1161,6 +1169,7 @@ proc createVar(p: PProc, typ: PType, indirect: bool): Rope =
|
||||
add(result, createRecordVarAux(p, t.n, c))
|
||||
t = t.sons[0]
|
||||
add(result, "}")
|
||||
if indirect: result = "[$1]" % [result]
|
||||
of tyVar, tyPtr, tyRef:
|
||||
if mapType(t) == etyBaseIndex:
|
||||
result = putToSeq("[null, 0]" | "{nil, 0}", indirect)
|
||||
@@ -1172,11 +1181,6 @@ proc createVar(p: PProc, typ: PType, indirect: bool): Rope =
|
||||
internalError("createVar: " & $t.kind)
|
||||
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) =
|
||||
var
|
||||
a: TCompRes
|
||||
@@ -1207,7 +1211,7 @@ proc genVarInit(p: PProc, v: PSym, n: PNode) =
|
||||
else:
|
||||
s = a.res
|
||||
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:
|
||||
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
|
||||
gen(p, n.sons[1], a)
|
||||
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) =
|
||||
var x, y: TCompRes
|
||||
@@ -1325,14 +1329,17 @@ proc genMagic(p: PProc, n: PNode, r: var TCompRes) =
|
||||
# XXX: range checking?
|
||||
if not (optOverflowCheck in p.options): unaryExpr(p, n, r, "", "$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:
|
||||
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:
|
||||
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
|
||||
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 mEqStr: binaryExpr(p, n, r, "eqStrings", "eqStrings($1, $2)")
|
||||
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 mChr, mArrToSeq: gen(p, n.sons[1], r) # nothing to do
|
||||
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:
|
||||
unaryExpr(p, n, r, "", "($1 != null ? $1.length : 0)")
|
||||
of mXLenSeq:
|
||||
unaryExpr(p, n, r, "", "$1.length")
|
||||
of mHigh:
|
||||
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:
|
||||
unaryExpr(p, n, r, "", "($1.length-1)")
|
||||
unaryExpr(p, n, r, "", "($1 != null ? ($1.length-1) : -1)")
|
||||
of mInc:
|
||||
if optOverflowCheck notin p.options: binaryExpr(p, n, r, "", "$1 += $2")
|
||||
else: binaryExpr(p, n, r, "addInt", "$1 = addInt($1, $2)")
|
||||
|
||||
@@ -61,7 +61,7 @@ type
|
||||
tkComma, tkSemiColon,
|
||||
tkColon, tkColonColon, tkEquals, tkDot, tkDotDot,
|
||||
tkOpr, tkComment, tkAccent,
|
||||
tkSpaces, tkInfixOpr, tkPrefixOpr, tkPostfixOpr,
|
||||
tkSpaces, tkInfixOpr, tkPrefixOpr, tkPostfixOpr
|
||||
|
||||
TTokTypes* = set[TTokType]
|
||||
|
||||
@@ -221,6 +221,10 @@ proc dispMessage(L: TLexer; info: TLineInfo; msg: TMsgKind; arg: string) =
|
||||
proc lexMessage*(L: TLexer, msg: TMsgKind, 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 = "") =
|
||||
var info = newLineInfo(L.fileIdx, L.lineNumber, pos - L.lineStart)
|
||||
L.dispMessage(info, msg, arg)
|
||||
@@ -863,6 +867,15 @@ proc rawGetTok*(L: var TLexer, tok: var TToken) =
|
||||
of '`':
|
||||
tok.tokType = tkAccent
|
||||
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 '\"':
|
||||
# check for extended raw string literal:
|
||||
var rawMode = L.bufpos > 0 and L.buf[L.bufpos-1] in SymChars
|
||||
|
||||
@@ -15,6 +15,10 @@ const
|
||||
import ast, astalgo, types, idents, magicsys, msgs, options
|
||||
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 =
|
||||
result = newNodeIT(nkBracketExpr, tup.info, tup.typ.skipTypes(
|
||||
abstractInst).sons[i])
|
||||
|
||||
@@ -738,7 +738,7 @@ proc writeContext(lastinfo: TLineInfo) =
|
||||
if msgContext[i] != lastinfo and msgContext[i] != info:
|
||||
msgWriteln(PosContextFormat % [toMsgFilename(msgContext[i]),
|
||||
coordToStr(msgContext[i].line),
|
||||
coordToStr(msgContext[i].col),
|
||||
coordToStr(msgContext[i].col+1),
|
||||
getMessageStr(errInstantiationFrom, "")])
|
||||
info = msgContext[i]
|
||||
|
||||
@@ -781,7 +781,7 @@ proc formatMsg*(info: TLineInfo, msg: TMsgKind, arg: string): string =
|
||||
of hintMin..hintMax: PosHintFormat
|
||||
else: PosErrorFormat
|
||||
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,
|
||||
eh: TErrorHandling) =
|
||||
@@ -804,8 +804,11 @@ proc liMessage(info: TLineInfo, msg: TMsgKind, arg: string,
|
||||
ignoreMsg = optHints notin gOptions or msg notin gNotes
|
||||
frmt = PosHintFormat
|
||||
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),
|
||||
coordToStr(info.col), getMessageStr(msg, arg)]
|
||||
coordToStr(info.col+1), getMessageStr(msg, arg)]
|
||||
if not ignoreMsg and not ignoreMsgBecauseOfIdeTools(msg):
|
||||
msgWriteln(s)
|
||||
if optPrintSurroundingSrc and msg in errMin..errMax:
|
||||
|
||||
@@ -5,7 +5,7 @@ hint[XDeclaredButNotUsed]:off
|
||||
path:"$projectPath/.."
|
||||
|
||||
path:"$lib/packages/docutils"
|
||||
path:"$nim/compiler"
|
||||
path:"../../compiler"
|
||||
|
||||
define:useStdoutAsStdmsg
|
||||
symbol:nimfix
|
||||
|
||||
@@ -6,7 +6,7 @@ hint[XDeclaredButNotUsed]:off
|
||||
path:"$projectPath/../.."
|
||||
|
||||
path:"$lib/packages/docutils"
|
||||
path:"$nim/compiler"
|
||||
path:"../../compiler"
|
||||
|
||||
define:useStdoutAsStdmsg
|
||||
define:nimsuggest
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#
|
||||
#
|
||||
# The Nim Compiler
|
||||
# (c) Copyright 2013 Andreas Rumpf
|
||||
# (c) Copyright 2015 Andreas Rumpf
|
||||
#
|
||||
# See the file "copying.txt", included in this
|
||||
# distribution, for details about the copyright.
|
||||
|
||||
@@ -190,6 +190,8 @@ proc isAssignable*(owner: PSym, n: PNode): TAssignableResult =
|
||||
result = arLocalLValue
|
||||
else:
|
||||
result = arLValue
|
||||
elif n.sym.kind == skParam and n.sym.typ.kind == tyVar:
|
||||
result = arLValue
|
||||
elif n.sym.kind == skType:
|
||||
let t = n.sym.typ.skipTypes({tyTypeDesc})
|
||||
if t.kind == tyVar: result = arStrange
|
||||
|
||||
@@ -91,7 +91,7 @@ proc closeParser(p: var TParser) =
|
||||
|
||||
proc parMessage(p: TParser, msg: TMsgKind, arg = "") =
|
||||
## 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) =
|
||||
## 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:
|
||||
getTok(p)
|
||||
else:
|
||||
lexMessage(p.lex, errTokenExpected, TokTypeToStr[tokType])
|
||||
lexMessageTok(p.lex, errTokenExpected, p.tok, TokTypeToStr[tokType])
|
||||
|
||||
proc parLineInfo(p: TParser): TLineInfo =
|
||||
## 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]
|
||||
|
||||
# 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) =
|
||||
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
|
||||
getTok(p)
|
||||
optInd(p, a)
|
||||
eat(p, endTok)
|
||||
|
||||
proc dotExpr(p: var TParser, a: PNode): PNode =
|
||||
#| dotExpr = expr '.' optInd symbol
|
||||
@@ -944,8 +944,7 @@ proc parseDoBlock(p: var TParser): PNode =
|
||||
getTok(p)
|
||||
let params = parseParamList(p, retColon=false)
|
||||
let pragmas = optPragmas(p)
|
||||
eat(p, tkColon)
|
||||
skipComment(p, result)
|
||||
colcom(p, result)
|
||||
result = newProcNode(nkDo, info, parseStmt(p),
|
||||
params = params,
|
||||
pragmas = pragmas)
|
||||
@@ -1139,9 +1138,11 @@ proc parseMacroColon(p: var TParser, x: PNode): PNode =
|
||||
result = makeCall(result)
|
||||
getTok(p)
|
||||
skipComment(p, result)
|
||||
let stmtList = newNodeP(nkStmtList, p)
|
||||
if p.tok.tokType notin {tkOf, tkElif, tkElse, tkExcept}:
|
||||
let body = parseStmt(p)
|
||||
addSon(result, makeStmtList(body))
|
||||
stmtList.add body
|
||||
#addSon(result, makeStmtList(body))
|
||||
while sameInd(p):
|
||||
var b: PNode
|
||||
case p.tok.tokType
|
||||
@@ -1153,19 +1154,22 @@ proc parseMacroColon(p: var TParser, x: PNode): PNode =
|
||||
getTok(p)
|
||||
optInd(p, b)
|
||||
addSon(b, parseExpr(p))
|
||||
eat(p, tkColon)
|
||||
of tkExcept:
|
||||
b = newNodeP(nkExceptBranch, p)
|
||||
exprList(p, tkColon, b)
|
||||
skipComment(p, b)
|
||||
of tkElse:
|
||||
b = newNodeP(nkElse, p)
|
||||
getTok(p)
|
||||
eat(p, tkColon)
|
||||
else: break
|
||||
eat(p, tkColon)
|
||||
addSon(b, parseStmt(p))
|
||||
addSon(result, b)
|
||||
addSon(stmtList, b)
|
||||
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 =
|
||||
#| exprStmt = simpleExpr
|
||||
@@ -1310,8 +1314,7 @@ proc parseIfOrWhen(p: var TParser, kind: TNodeKind): PNode =
|
||||
var branch = newNodeP(nkElifBranch, p)
|
||||
optInd(p, branch)
|
||||
addSon(branch, parseExpr(p))
|
||||
eat(p, tkColon)
|
||||
skipComment(p, branch)
|
||||
colcom(p, branch)
|
||||
addSon(branch, parseStmt(p))
|
||||
skipComment(p, branch)
|
||||
addSon(result, branch)
|
||||
@@ -1319,8 +1322,7 @@ proc parseIfOrWhen(p: var TParser, kind: TNodeKind): PNode =
|
||||
if p.tok.tokType == tkElse and sameOrNoInd(p):
|
||||
var branch = newNodeP(nkElse, p)
|
||||
eat(p, tkElse)
|
||||
eat(p, tkColon)
|
||||
skipComment(p, branch)
|
||||
colcom(p, branch)
|
||||
addSon(branch, parseStmt(p))
|
||||
addSon(result, branch)
|
||||
|
||||
@@ -1368,13 +1370,11 @@ proc parseCase(p: var TParser): PNode =
|
||||
getTok(p)
|
||||
optInd(p, b)
|
||||
addSon(b, parseExpr(p))
|
||||
eat(p, tkColon)
|
||||
of tkElse:
|
||||
b = newNodeP(nkElse, p)
|
||||
getTok(p)
|
||||
eat(p, tkColon)
|
||||
else: break
|
||||
skipComment(p, b)
|
||||
colcom(p, b)
|
||||
addSon(b, parseStmt(p))
|
||||
addSon(result, b)
|
||||
if b.kind == nkElse: break
|
||||
@@ -1391,8 +1391,7 @@ proc parseTry(p: var TParser; isExpr: bool): PNode =
|
||||
#| (optInd 'finally' colcom stmt)?
|
||||
result = newNodeP(nkTryStmt, p)
|
||||
getTok(p)
|
||||
eat(p, tkColon)
|
||||
skipComment(p, result)
|
||||
colcom(p, result)
|
||||
addSon(result, parseStmt(p))
|
||||
var b: PNode = nil
|
||||
while sameOrNoInd(p) or isExpr:
|
||||
@@ -1402,10 +1401,9 @@ proc parseTry(p: var TParser; isExpr: bool): PNode =
|
||||
exprList(p, tkColon, b)
|
||||
of tkFinally:
|
||||
b = newNodeP(nkFinally, p)
|
||||
getTokNoInd(p)
|
||||
eat(p, tkColon)
|
||||
getTok(p)
|
||||
else: break
|
||||
skipComment(p, b)
|
||||
colcom(p, b)
|
||||
addSon(b, parseStmt(p))
|
||||
addSon(result, b)
|
||||
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 =
|
||||
#| exceptBlock = 'except' colcom stmt
|
||||
result = newNodeP(kind, p)
|
||||
getTokNoInd(p)
|
||||
getTok(p)
|
||||
colcom(p, result)
|
||||
addSon(result, parseStmt(p))
|
||||
|
||||
@@ -1447,7 +1445,7 @@ proc parseStaticOrDefer(p: var TParser; k: TNodeKind): PNode =
|
||||
#| staticStmt = 'static' colcom stmt
|
||||
#| deferStmt = 'defer' colcom stmt
|
||||
result = newNodeP(k, p)
|
||||
getTokNoInd(p)
|
||||
getTok(p)
|
||||
colcom(p, result)
|
||||
addSon(result, parseStmt(p))
|
||||
|
||||
@@ -1628,7 +1626,7 @@ proc parseEnum(p: var TParser): PNode =
|
||||
p.tok.tokType == tkEof:
|
||||
break
|
||||
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 parseObjectWhen(p: var TParser): PNode =
|
||||
@@ -1686,9 +1684,8 @@ proc parseObjectCase(p: var TParser): PNode =
|
||||
of tkElse:
|
||||
b = newNodeP(nkElse, p)
|
||||
getTok(p)
|
||||
eat(p, tkColon)
|
||||
else: break
|
||||
skipComment(p, b)
|
||||
colcom(p, b)
|
||||
var fields = parseObjectPart(p)
|
||||
if fields.kind == nkEmpty:
|
||||
parMessage(p, errIdentifierExpected, p.tok)
|
||||
@@ -1884,7 +1881,7 @@ proc simpleStmt(p: var TParser): PNode =
|
||||
|
||||
proc complexOrSimpleStmt(p: var TParser): PNode =
|
||||
#| complexOrSimpleStmt = (ifStmt | whenStmt | whileStmt
|
||||
#| | tryStmt | finallyStmt | exceptStmt | forStmt
|
||||
#| | tryStmt | forStmt
|
||||
#| | blockStmt | staticStmt | deferStmt | asmStmt
|
||||
#| | 'proc' routine
|
||||
#| | 'method' routine
|
||||
|
||||
43
compiler/plugins.nim
Normal file
43
compiler/plugins.nim
Normal file
@@ -0,0 +1,43 @@
|
||||
#
|
||||
#
|
||||
# The Nim Compiler
|
||||
# (c) Copyright 2015 Andreas Rumpf
|
||||
#
|
||||
# See the file "copying.txt", included in this
|
||||
# distribution, for details about the copyright.
|
||||
#
|
||||
|
||||
## Plugin support for the Nim compiler. Right now there are no plugins and they
|
||||
## need to be build with the compiler, no DLL support.
|
||||
|
||||
import ast, semdata, idents
|
||||
|
||||
type
|
||||
Transformation* = proc (c: PContext; n: PNode): PNode {.nimcall.}
|
||||
Plugin = ref object
|
||||
fn, module, package: PIdent
|
||||
t: Transformation
|
||||
next: Plugin
|
||||
|
||||
proc pluginMatches(p: Plugin; s: PSym): bool =
|
||||
if s.name.id != p.fn.id: return false
|
||||
let module = s.owner
|
||||
if module == nil or module.kind != skModule or
|
||||
module.name.id != p.module.id: return false
|
||||
let package = module.owner
|
||||
if package == nil or package.kind != skPackage or
|
||||
package.name.id != p.package.id: return false
|
||||
return true
|
||||
|
||||
var head: Plugin
|
||||
|
||||
proc getPlugin*(fn: PSym): Transformation =
|
||||
var it = head
|
||||
while it != nil:
|
||||
if pluginMatches(it, fn): return it.t
|
||||
it = it.next
|
||||
|
||||
proc registerPlugin*(package, module, fn: string; t: Transformation) =
|
||||
let oldHead = head
|
||||
head = Plugin(fn: getIdent(fn), module: getIdent(module),
|
||||
package: getIdent(package), t: t, next: oldHead)
|
||||
13
compiler/plugins/active.nim
Normal file
13
compiler/plugins/active.nim
Normal file
@@ -0,0 +1,13 @@
|
||||
#
|
||||
#
|
||||
# The Nim Compiler
|
||||
# (c) Copyright 2015 Andreas Rumpf
|
||||
#
|
||||
# See the file "copying.txt", included in this
|
||||
# distribution, for details about the copyright.
|
||||
#
|
||||
|
||||
## Include file that imports all plugins that are active.
|
||||
|
||||
import
|
||||
locals.locals
|
||||
42
compiler/plugins/locals/locals.nim
Normal file
42
compiler/plugins/locals/locals.nim
Normal file
@@ -0,0 +1,42 @@
|
||||
#
|
||||
#
|
||||
# The Nim Compiler
|
||||
# (c) Copyright 2015 Andreas Rumpf
|
||||
#
|
||||
# See the file "copying.txt", included in this
|
||||
# distribution, for details about the copyright.
|
||||
#
|
||||
|
||||
## The builtin 'system.locals' implemented as a plugin.
|
||||
|
||||
import plugins, ast, astalgo, magicsys, lookups, semdata, lowerings
|
||||
|
||||
proc semLocals(c: PContext, n: PNode): PNode =
|
||||
var counter = 0
|
||||
var tupleType = newTypeS(tyTuple, c)
|
||||
result = newNodeIT(nkPar, n.info, tupleType)
|
||||
tupleType.n = newNodeI(nkRecList, n.info)
|
||||
# for now we skip openarrays ...
|
||||
for scope in walkScopes(c.currentScope):
|
||||
if scope == c.topLevelScope: break
|
||||
for it in items(scope.symbols):
|
||||
# XXX parameters' owners are wrong for generics; this caused some pain
|
||||
# for closures too; we should finally fix it.
|
||||
#if it.owner != c.p.owner: return result
|
||||
if it.kind in skLocalVars and
|
||||
it.typ.skipTypes({tyGenericInst, tyVar}).kind notin
|
||||
{tyVarargs, tyOpenArray, tyTypeDesc, tyStatic, tyExpr, tyStmt, tyEmpty}:
|
||||
|
||||
var field = newSym(skField, it.name, getCurrOwner(), n.info)
|
||||
field.typ = it.typ.skipTypes({tyGenericInst, tyVar})
|
||||
field.position = counter
|
||||
inc(counter)
|
||||
|
||||
addSon(tupleType.n, newSymNode(field))
|
||||
addSonSkipIntLit(tupleType, field.typ)
|
||||
|
||||
var a = newSymNode(it, result.info)
|
||||
if it.typ.skipTypes({tyGenericInst}).kind == tyVar: a = newDeref(a)
|
||||
result.add(a)
|
||||
|
||||
registerPlugin("stdlib", "system", "locals", semLocals)
|
||||
@@ -60,7 +60,7 @@ const
|
||||
varPragmas* = {wImportc, wExportc, wVolatile, wRegister, wThreadVar, wNodecl,
|
||||
wMagic, wHeader, wDeprecated, wCompilerproc, wDynlib, wExtern,
|
||||
wImportCpp, wImportObjC, wError, wNoInit, wCompileTime, wGlobal,
|
||||
wGensym, wInject, wCodegenDecl, wGuard}
|
||||
wGensym, wInject, wCodegenDecl, wGuard, wGoto}
|
||||
constPragmas* = {wImportc, wExportc, wHeader, wDeprecated, wMagic, wNodecl,
|
||||
wExtern, wImportCpp, wImportObjC, wError, wGensym, wInject}
|
||||
letPragmas* = varPragmas
|
||||
@@ -843,6 +843,11 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: int,
|
||||
invalidPragma(it)
|
||||
else:
|
||||
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:
|
||||
if it.kind != nkExprColonExpr:
|
||||
localError(it.info, errExprExpected)
|
||||
|
||||
@@ -16,7 +16,7 @@ import
|
||||
procfind, lookups, rodread, pragmas, passes, semdata, semtypinst, sigmatch,
|
||||
intsets, transf, vmdef, vm, idgen, aliases, cgmeth, lambdalifting,
|
||||
evaltempl, patterns, parampatterns, sempass2, nimfix.pretty, semmacrosanity,
|
||||
semparallel, lowerings
|
||||
semparallel, lowerings, plugins, plugins.active
|
||||
|
||||
when defined(nimfix):
|
||||
import nimfix.prettybase
|
||||
@@ -89,6 +89,10 @@ proc fitNode(c: PContext, formal: PType, arg: PNode): PNode =
|
||||
let x = result.skipConv
|
||||
if x.kind == nkPar and formal.kind != tyExpr:
|
||||
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,
|
||||
arg: PNode, coerceDistincts = false): PNode
|
||||
|
||||
@@ -118,7 +118,6 @@ proc newOptionEntry*(): POptionEntry
|
||||
proc newLib*(kind: TLibKind): PLib
|
||||
proc addToLib*(lib: PLib, sym: PSym)
|
||||
proc makePtrType*(c: PContext, baseType: PType): PType
|
||||
proc makeVarType*(c: PContext, baseType: PType): PType
|
||||
proc newTypeS*(kind: TTypeKind, c: PContext): PType
|
||||
proc fillTypeS*(dest: PType, kind: TTypeKind, c: PContext)
|
||||
|
||||
@@ -213,9 +212,12 @@ proc makePtrType(c: PContext, baseType: PType): PType =
|
||||
result = newTypeS(tyPtr, c)
|
||||
addSonSkipIntLit(result, baseType.assertNotNil)
|
||||
|
||||
proc makeVarType(c: PContext, baseType: PType): PType =
|
||||
result = newTypeS(tyVar, c)
|
||||
addSonSkipIntLit(result, baseType.assertNotNil)
|
||||
proc makeVarType*(c: PContext, baseType: PType): PType =
|
||||
if baseType.kind == tyVar:
|
||||
result = baseType
|
||||
else:
|
||||
result = newTypeS(tyVar, c)
|
||||
addSonSkipIntLit(result, baseType.assertNotNil)
|
||||
|
||||
proc makeTypeDesc*(c: PContext, typ: PType): PType =
|
||||
result = newTypeS(tyTypeDesc, c)
|
||||
@@ -247,6 +249,7 @@ proc makeAndType*(c: PContext, t1, t2: PType): PType =
|
||||
propagateToOwner(result, t1)
|
||||
propagateToOwner(result, t2)
|
||||
result.flags.incl((t1.flags + t2.flags) * {tfHasStatic})
|
||||
result.flags.incl tfHasMeta
|
||||
|
||||
proc makeOrType*(c: PContext, t1, t2: PType): PType =
|
||||
result = newTypeS(tyOr, c)
|
||||
@@ -254,12 +257,14 @@ proc makeOrType*(c: PContext, t1, t2: PType): PType =
|
||||
propagateToOwner(result, t1)
|
||||
propagateToOwner(result, t2)
|
||||
result.flags.incl((t1.flags + t2.flags) * {tfHasStatic})
|
||||
result.flags.incl tfHasMeta
|
||||
|
||||
proc makeNotType*(c: PContext, t1: PType): PType =
|
||||
result = newTypeS(tyNot, c)
|
||||
result.sons = @[t1]
|
||||
propagateToOwner(result, t1)
|
||||
result.flags.incl(t1.flags * {tfHasStatic})
|
||||
result.flags.incl tfHasMeta
|
||||
|
||||
proc nMinusOne*(n: PNode): PNode =
|
||||
result = newNode(nkCall, n.info, @[
|
||||
|
||||
@@ -24,7 +24,7 @@ proc semFieldAccess(c: PContext, n: PNode, flags: TExprFlags = {}): PNode
|
||||
proc semOperand(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
|
||||
# same as 'semExprWithType' but doesn't check for proc vars
|
||||
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:
|
||||
#raiseRecoverableError("")
|
||||
result = errorNode(c, n)
|
||||
@@ -389,7 +389,7 @@ proc isOpImpl(c: PContext, n: PNode): PNode =
|
||||
maybeLiftType(t2, c, n.info)
|
||||
var m: TCandidate
|
||||
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.typ = n.typ
|
||||
@@ -447,6 +447,7 @@ proc changeType(n: PNode, newType: PType, check: bool) =
|
||||
of nkPar:
|
||||
let tup = newType.skipTypes({tyGenericInst})
|
||||
if tup.kind != tyTuple:
|
||||
if tup.kind == tyObject: return
|
||||
internalError(n.info, "changeType: no tuple type for constructor")
|
||||
elif sonsLen(n) > 0 and n.sons[0].kind == nkExprColonExpr:
|
||||
# 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)
|
||||
|
||||
proc fixAbstractType(c: PContext, n: PNode) =
|
||||
# XXX finally rewrite that crap!
|
||||
for i in countup(1, sonsLen(n) - 1):
|
||||
var it = n.sons[i]
|
||||
case it.kind
|
||||
of nkHiddenStdConv, nkHiddenSubConv:
|
||||
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}:
|
||||
for i in 1 .. < n.len:
|
||||
let it = n.sons[i]
|
||||
# do not get rid of nkHiddenSubConv for OpenArrays, the codegen needs it:
|
||||
if it.kind == nkHiddenSubConv and
|
||||
skipTypes(it.typ, abstractVar).kind notin {tyOpenArray, tyVarargs}:
|
||||
if 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))
|
||||
when false:
|
||||
# XXX finally rewrite that crap!
|
||||
for i in countup(1, sonsLen(n) - 1):
|
||||
var it = n.sons[i]
|
||||
case it.kind
|
||||
of nkHiddenStdConv, nkHiddenSubConv:
|
||||
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 =
|
||||
case n.kind
|
||||
@@ -2040,7 +2053,7 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
|
||||
of nkEmpty, nkNone, nkCommentStmt:
|
||||
discard
|
||||
of nkNilLit:
|
||||
result.typ = getSysType(tyNil)
|
||||
if result.typ == nil: result.typ = getSysType(tyNil)
|
||||
of nkIntLit:
|
||||
if result.typ == nil: setIntLitType(result)
|
||||
of nkInt8Lit:
|
||||
|
||||
@@ -118,6 +118,9 @@ proc makeRange(typ: PType, first, last: BiggestInt): PType =
|
||||
let lowerNode = newIntNode(nkIntLit, minA)
|
||||
if typ.kind == tyInt and minA == maxA:
|
||||
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:
|
||||
var n = newNode(nkRange)
|
||||
addSon(n, lowerNode)
|
||||
@@ -135,8 +138,9 @@ proc makeRangeF(typ: PType, first, last: BiggestFloat): PType =
|
||||
addSonSkipIntLit(result, skipTypes(typ, {tyRange}))
|
||||
|
||||
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.
|
||||
const ordIntLit = {nkIntLit..nkUInt64Lit}
|
||||
result = n.typ
|
||||
|
||||
template commutativeOp(opr: expr) {.immediate.} =
|
||||
@@ -208,15 +212,15 @@ proc getIntervalType*(m: TMagic, n: PNode): PType =
|
||||
var a = n.sons[1]
|
||||
var b = n.sons[2]
|
||||
# symmetrical:
|
||||
if b.kind notin {nkIntLit..nkUInt32Lit}: swap(a, b)
|
||||
if b.kind in {nkIntLit..nkUInt32Lit}:
|
||||
if b.kind notin ordIntLit: swap(a, b)
|
||||
if b.kind in ordIntLit:
|
||||
let x = b.intVal|+|1
|
||||
if (x and -x) == x and x >= 0:
|
||||
result = makeRange(a.typ, 0, b.intVal)
|
||||
of mModU:
|
||||
let a = n.sons[1]
|
||||
let b = n.sons[2]
|
||||
if b.kind in {nkIntLit..nkUInt32Lit}:
|
||||
if a.kind in ordIntLit:
|
||||
if b.intVal >= 0:
|
||||
result = makeRange(a.typ, 0, b.intVal-1)
|
||||
else:
|
||||
@@ -232,9 +236,9 @@ proc getIntervalType*(m: TMagic, n: PNode): PType =
|
||||
result = makeRange(a.typ, b.intVal+1, -(b.intVal+1))
|
||||
of mDivI, mDivI64, mDivU:
|
||||
binaryOp(`|div|`)
|
||||
of mMinI, mMinI64:
|
||||
of mMinI:
|
||||
commutativeOp(min)
|
||||
of mMaxI, mMaxI64:
|
||||
of mMaxI:
|
||||
commutativeOp(max)
|
||||
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 mCard: result = newIntNodeT(nimsets.cardSet(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 mLengthSeq, mLengthOpenArray: result = newIntNodeT(sonsLen(a), n) # BUGFIX
|
||||
of mUnaryPlusI, mUnaryPlusI64, mUnaryPlusF64: result = a # throw `+` away
|
||||
of mLengthSeq, mLengthOpenArray, mXLenSeq:
|
||||
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:
|
||||
result = newFloatNodeT(toFloat(int(getInt(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 mSubI, mSubI64: 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)
|
||||
else: result = newIntNodeT(getInt(a), n)
|
||||
of mMaxI, mMaxI64:
|
||||
of mMaxI:
|
||||
if getInt(a) > getInt(b): result = newIntNodeT(getInt(a), n)
|
||||
else: result = newIntNodeT(getInt(b), n)
|
||||
of mShlI, mShlI64:
|
||||
@@ -426,7 +434,8 @@ proc evalOp(m: TMagic, n, a, b, c: PNode): PNode =
|
||||
mExit, mInc, ast.mDec, mEcho, mSwap, mAppendStrCh,
|
||||
mAppendStrStr, mAppendSeqElem, mSetLengthStr, mSetLengthSeq,
|
||||
mParseExprToAst, mParseStmtToAst, mExpandToAst, mTypeTrait, mDotDot,
|
||||
mNLen..mNError, mEqRef, mSlurp, mStaticExec, mNGenSym, mSpawn, mParallel:
|
||||
mNLen..mNError, mEqRef, mSlurp, mStaticExec, mNGenSym, mSpawn,
|
||||
mParallel, mPlugin:
|
||||
discard
|
||||
else: internalError(a.info, "evalOp(" & $m & ')')
|
||||
|
||||
@@ -519,7 +528,7 @@ proc rangeCheck(n: PNode, value: BiggestInt) =
|
||||
proc foldConv*(n, a: PNode; check = false): PNode =
|
||||
# XXX range checks?
|
||||
case skipTypes(n.typ, abstractRange).kind
|
||||
of tyInt..tyInt64:
|
||||
of tyInt..tyInt64, tyUInt..tyUInt64:
|
||||
case skipTypes(a.typ, abstractRange).kind
|
||||
of tyFloat..tyFloat64:
|
||||
result = newIntNodeT(int(getFloat(a)), n)
|
||||
@@ -646,7 +655,7 @@ proc getConstExpr(m: PSym, n: PNode): PNode =
|
||||
result = copyNode(n)
|
||||
of nkIfExpr:
|
||||
result = getConstIfExpr(m, n)
|
||||
of nkCall, nkCommand, nkCallStrLit, nkPrefix, nkInfix:
|
||||
of nkCallKinds:
|
||||
if n.sons[0].kind != nkSym: return
|
||||
var s = n.sons[0].sym
|
||||
if s.kind != skProc: return
|
||||
|
||||
@@ -187,7 +187,9 @@ proc instantiateProcType(c: PContext, pt: TIdTable,
|
||||
let param = copySym(oldParam)
|
||||
param.owner = prc
|
||||
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])!
|
||||
result.n.sons[i] = newSymNode(param)
|
||||
addDecl(c, param)
|
||||
|
||||
@@ -16,9 +16,9 @@ proc ithField(n: PNode, field: int): PSym =
|
||||
result = nil
|
||||
case n.kind
|
||||
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)
|
||||
if result != nil: return
|
||||
if result != nil: return
|
||||
of nkRecCase:
|
||||
if n.sons[0].kind != nkSym: internalError(n.info, "ithField")
|
||||
result = ithField(n.sons[0], field-1)
|
||||
@@ -34,7 +34,7 @@ proc ithField(n: PNode, field: int): PSym =
|
||||
else: discard
|
||||
|
||||
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'
|
||||
# to not to skip tyGenericInst
|
||||
case n.kind
|
||||
@@ -80,7 +80,7 @@ proc annotateType*(n: PNode, t: PType) =
|
||||
if x.kind in {tyString, tyCString}:
|
||||
n.typ = t
|
||||
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:
|
||||
if x.kind in NilableTypes:
|
||||
n.typ = t
|
||||
|
||||
@@ -54,7 +54,7 @@ proc evalTypeTrait(trait: PNode, operand: PType, context: PSym): PNode =
|
||||
result.typ = newType(tyString, context)
|
||||
result.info = trait.info
|
||||
of "arity":
|
||||
result = newIntNode(nkIntLit, typ.n.len-1)
|
||||
result = newIntNode(nkIntLit, typ.len - ord(typ.kind==tyProc))
|
||||
result.typ = newType(tyInt, context)
|
||||
result.info = trait.info
|
||||
else:
|
||||
@@ -101,34 +101,6 @@ proc semBindSym(c: PContext, n: PNode): PNode =
|
||||
else:
|
||||
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 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 mShallowCopy: result = semShallowCopy(c, n, flags)
|
||||
of mNBindSym: result = semBindSym(c, n)
|
||||
of mLocals: result = semLocals(c, n)
|
||||
of mProcCall:
|
||||
result = n
|
||||
result.typ = n[1].typ
|
||||
@@ -196,4 +167,11 @@ proc magicsAfterOverloadResolution(c: PContext, n: PNode,
|
||||
result.add newSymNode(createMagic("-", mSubI), n.info)
|
||||
result.add lenExprB
|
||||
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
|
||||
|
||||
@@ -560,7 +560,10 @@ proc trackCase(tracked: PEffects, n: PNode) =
|
||||
track(tracked, n.sons[0])
|
||||
let oldState = tracked.init.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 toCover = 0
|
||||
for i in 1.. <n.len:
|
||||
@@ -575,13 +578,8 @@ proc trackCase(tracked: PEffects, n: PNode) =
|
||||
for i in oldState.. <tracked.init.len:
|
||||
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)
|
||||
if exh:
|
||||
if not stringCase or lastSon(n).kind == nkElse:
|
||||
for id, count in items(inter):
|
||||
if count >= toCover: tracked.init.add id
|
||||
# else we can't merge
|
||||
@@ -714,8 +712,8 @@ proc track(tracked: PEffects, n: PNode) =
|
||||
of nkVarSection, nkLetSection:
|
||||
for child in n:
|
||||
let last = lastSon(child)
|
||||
if last.kind != nkEmpty: track(tracked, last)
|
||||
if child.kind == nkIdentDefs and last.kind != nkEmpty:
|
||||
track(tracked, last)
|
||||
for i in 0 .. child.len-3:
|
||||
initVar(tracked, child.sons[i], volatileCheck=false)
|
||||
addAsgnFact(tracked.guards, child.sons[i], last)
|
||||
|
||||
@@ -102,10 +102,6 @@ proc semDestructorCheck(c: PContext, n: PNode, flags: TExprFlags) {.inline.} =
|
||||
c.p.owner.kind notin {skTemplate, skMacro}:
|
||||
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 =
|
||||
result = semExpr(c, n)
|
||||
if result.typ != nil:
|
||||
@@ -373,6 +369,11 @@ proc addToVarSection(c: PContext; result: var PNode; orig, identDefs: PNode) =
|
||||
else:
|
||||
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 =
|
||||
var b: PNode
|
||||
result = copyNode(n)
|
||||
@@ -436,7 +437,8 @@ proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode =
|
||||
|
||||
for j in countup(0, length-3):
|
||||
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:
|
||||
if c.inUnrolledContext > 0: v.flags.incl(sfShadowed)
|
||||
else:
|
||||
@@ -551,7 +553,8 @@ proc semForVars(c: PContext, n: PNode): PNode =
|
||||
if getCurrOwner().kind == skModule: incl(v.flags, sfGlobal)
|
||||
v.typ = iter.sons[i]
|
||||
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)
|
||||
n.sons[length-1] = semStmt(c, n.sons[length-1])
|
||||
dec(c.p.nestedLoopCounter)
|
||||
|
||||
@@ -261,7 +261,8 @@ proc semArray(c: PContext, n: PNode, prev: PType): PType =
|
||||
if not isOrdinalType(indx):
|
||||
localError(n.sons[1].info, errOrdinalTypeExpected)
|
||||
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)
|
||||
addSonSkipIntLit(result, base)
|
||||
else:
|
||||
@@ -628,7 +629,7 @@ proc skipGenericInvocation(t: PType): PType {.inline.} =
|
||||
result = t
|
||||
if result.kind == tyGenericInvocation:
|
||||
result = result.sons[0]
|
||||
if result.kind == tyGenericBody:
|
||||
while result.kind in {tyGenericInst, tyGenericBody}:
|
||||
result = lastSon(result)
|
||||
|
||||
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
|
||||
return addImplicitGeneric(cp)
|
||||
|
||||
for i in 1 .. (paramType.sons.len - 2):
|
||||
for i in 1 .. paramType.len-2:
|
||||
var lifted = liftingWalk(paramType.sons[i])
|
||||
if lifted != nil:
|
||||
paramType.sons[i] = lifted
|
||||
@@ -847,7 +848,7 @@ proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode,
|
||||
result.shouldHaveMeta
|
||||
|
||||
of tyGenericInvocation:
|
||||
for i in 1 .. <paramType.sonsLen:
|
||||
for i in 1 .. <paramType.len:
|
||||
let lifted = liftingWalk(paramType.sons[i])
|
||||
if lifted != nil: paramType.sons[i] = lifted
|
||||
when false:
|
||||
|
||||
@@ -233,7 +233,9 @@ proc instCopyType*(cl: var TReplTypeVars, t: PType): PType =
|
||||
# XXX: relying on allowMetaTypes is a kludge
|
||||
result = copyType(t, t.owner, cl.allowMetaTypes)
|
||||
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 =
|
||||
# tyGenericInvocation[A, tyGenericInvocation[A, B]]
|
||||
|
||||
@@ -147,6 +147,7 @@ proc copyCandidate(a: var TCandidate, b: TCandidate) =
|
||||
|
||||
proc sumGeneric(t: PType): int =
|
||||
var t = t
|
||||
var isvar = 1
|
||||
while true:
|
||||
case t.kind
|
||||
of tyGenericInst, tyArray, tyRef, tyPtr, tyDistinct, tyArrayConstr,
|
||||
@@ -154,18 +155,20 @@ proc sumGeneric(t: PType): int =
|
||||
t = t.lastSon
|
||||
inc result
|
||||
of tyVar:
|
||||
# but do not make 'var T' more specific than 'T'!
|
||||
t = t.sons[0]
|
||||
inc result
|
||||
inc isvar
|
||||
of tyGenericInvocation, tyTuple:
|
||||
result = ord(t.kind == tyGenericInvocation)
|
||||
result += ord(t.kind == tyGenericInvocation)
|
||||
for i in 0 .. <t.len: result += t.sons[i].sumGeneric
|
||||
break
|
||||
of tyGenericParam, tyExpr, tyStatic, tyStmt, tyTypeDesc: break
|
||||
of tyBool, tyChar, tyEnum, tyObject, tyProc, tyPointer,
|
||||
tyString, tyCString, tyInt..tyInt64, tyFloat..tyFloat128,
|
||||
tyUInt..tyUInt64:
|
||||
return 1
|
||||
else: return 0
|
||||
return isvar
|
||||
else:
|
||||
return 0
|
||||
|
||||
#var ggDebug: bool
|
||||
|
||||
@@ -919,6 +922,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
|
||||
|
||||
of tyAnd:
|
||||
considerPreviousT:
|
||||
result = isEqual
|
||||
for branch in f.sons:
|
||||
let x = typeRel(c, branch, aOrig)
|
||||
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)
|
||||
result = isNone
|
||||
|
||||
of tyNone:
|
||||
if a.kind == tyNone: result = isEqual
|
||||
else:
|
||||
internalAssert false
|
||||
internalError " unknown type kind " & $f.kind
|
||||
|
||||
proc cmpTypes*(c: PContext, f, a: PType): TTypeRelation =
|
||||
var m: TCandidate
|
||||
@@ -1194,15 +1200,6 @@ proc isInlineIterator*(t: PType): bool =
|
||||
result = t.kind == tyIter or
|
||||
(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) =
|
||||
case r
|
||||
of isConvertible, isIntConv: inc(m.convMatches, convMatch)
|
||||
@@ -1307,7 +1304,7 @@ proc paramTypesMatchAux(m: var TCandidate, f, argType: PType,
|
||||
if arg.typ == nil:
|
||||
result = arg
|
||||
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:
|
||||
result = arg.copyTree
|
||||
result.typ = getInstantiatedType(c, arg, m, f)
|
||||
@@ -1322,7 +1319,7 @@ proc paramTypesMatchAux(m: var TCandidate, f, argType: PType,
|
||||
inc(m.exactMatches)
|
||||
result = arg
|
||||
if skipTypes(f, abstractVar-{tyTypeDesc}).kind in {tyTuple}:
|
||||
result = implicitConv(nkHiddenStdConv, f, arg, m, c)
|
||||
result = implicitConv(nkHiddenSubConv, f, arg, m, c)
|
||||
of isNone:
|
||||
# do not do this in ``typeRel`` as it then can't infere T in ``ref T``:
|
||||
if a.kind in {tyProxy, tyUnknown}:
|
||||
@@ -1468,9 +1465,7 @@ proc matchesAux(c: PContext, n, nOrig: PNode,
|
||||
m.state = csNoMatch
|
||||
return
|
||||
if formal.typ.kind == tyVar:
|
||||
if n.isLValue:
|
||||
inc(m.genericMatches, 100)
|
||||
else:
|
||||
if not n.isLValue:
|
||||
m.state = csNoMatch
|
||||
return
|
||||
|
||||
@@ -1576,6 +1571,8 @@ proc matchesAux(c: PContext, n, nOrig: PNode,
|
||||
#assert(container == nil)
|
||||
if container.isNil:
|
||||
container = newNodeIT(nkBracket, n.sons[a].info, arrayConstr(c, arg))
|
||||
else:
|
||||
incrIndexType(container.typ)
|
||||
addSon(container, arg)
|
||||
setSon(m.call, formal.position + 1,
|
||||
implicitConv(nkHiddenStdConv, formal.typ, container, m, c))
|
||||
|
||||
@@ -379,6 +379,9 @@ proc transformConv(c: PTransf, n: PNode): PTransNode =
|
||||
result = transformSons(c, n)
|
||||
of tyOpenArray, tyVarargs:
|
||||
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:
|
||||
if source.kind == tyString:
|
||||
result = newTransNode(nkStringToCString, n, 1)
|
||||
@@ -713,8 +716,7 @@ proc transform(c: PTransf, n: PNode): PTransNode =
|
||||
add(result, PTransNode(newSymNode(labl)))
|
||||
of nkBreakStmt: result = transformBreak(c, n)
|
||||
of nkWhileStmt: result = transformWhile(c, n)
|
||||
of nkCall, nkHiddenCallConv, nkCommand, nkInfix, nkPrefix, nkPostfix,
|
||||
nkCallStrLit:
|
||||
of nkCallKinds:
|
||||
result = transformCall(c, n)
|
||||
of nkAddr, nkHiddenAddr:
|
||||
result = transformAddrDeref(c, n, nkDerefExpr, nkHiddenDeref)
|
||||
|
||||
@@ -9,40 +9,40 @@
|
||||
|
||||
# tree helper routines
|
||||
|
||||
import
|
||||
import
|
||||
ast, astalgo, lexer, msgs, strutils, wordrecg
|
||||
|
||||
proc hasSon(father, son: PNode): bool =
|
||||
for i in countup(0, sonsLen(father) - 1):
|
||||
if father.sons[i] == son:
|
||||
proc hasSon(father, son: PNode): bool =
|
||||
for i in countup(0, sonsLen(father) - 1):
|
||||
if father.sons[i] == son:
|
||||
return true
|
||||
result = false
|
||||
|
||||
proc cyclicTreeAux(n, s: PNode): bool =
|
||||
if n == nil:
|
||||
proc cyclicTreeAux(n, s: PNode): bool =
|
||||
if n == nil:
|
||||
return false
|
||||
if hasSon(s, n):
|
||||
if hasSon(s, n):
|
||||
return true
|
||||
var m = sonsLen(s)
|
||||
addSon(s, n)
|
||||
if not (n.kind in {nkEmpty..nkNilLit}):
|
||||
for i in countup(0, sonsLen(n) - 1):
|
||||
if cyclicTreeAux(n.sons[i], s):
|
||||
if not (n.kind in {nkEmpty..nkNilLit}):
|
||||
for i in countup(0, sonsLen(n) - 1):
|
||||
if cyclicTreeAux(n.sons[i], s):
|
||||
return true
|
||||
result = false
|
||||
delSon(s, m)
|
||||
|
||||
proc cyclicTree*(n: PNode): bool =
|
||||
proc cyclicTree*(n: PNode): bool =
|
||||
var s = newNodeI(nkEmpty, n.info)
|
||||
result = cyclicTreeAux(n, s)
|
||||
|
||||
proc exprStructuralEquivalent*(a, b: PNode): bool =
|
||||
proc exprStructuralEquivalent*(a, b: PNode): bool =
|
||||
result = false
|
||||
if a == b:
|
||||
if a == b:
|
||||
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
|
||||
of nkSym:
|
||||
of nkSym:
|
||||
# don't go nuts here: same symbol as string is enough:
|
||||
result = a.sym.name.id == b.sym.name.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 nkStrLit..nkTripleStrLit: result = a.strVal == b.strVal
|
||||
of nkEmpty, nkNilLit, nkType: result = true
|
||||
else:
|
||||
if sonsLen(a) == sonsLen(b):
|
||||
for i in countup(0, sonsLen(a) - 1):
|
||||
if not exprStructuralEquivalent(a.sons[i], b.sons[i]): return
|
||||
else:
|
||||
if sonsLen(a) == sonsLen(b):
|
||||
for i in countup(0, sonsLen(a) - 1):
|
||||
if not exprStructuralEquivalent(a.sons[i], b.sons[i]): return
|
||||
result = true
|
||||
|
||||
|
||||
proc sameTree*(a, b: PNode): bool =
|
||||
result = false
|
||||
if a == b:
|
||||
@@ -66,7 +66,7 @@ proc sameTree*(a, b: PNode): bool =
|
||||
if a.info.col != b.info.col:
|
||||
return #if a.info.fileIndex <> b.info.fileIndex then exit;
|
||||
case a.kind
|
||||
of nkSym:
|
||||
of nkSym:
|
||||
# don't go nuts here: same symbol as string is enough:
|
||||
result = a.sym.name.id == b.sym.name.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 nkEmpty, nkNilLit, nkType: result = true
|
||||
else:
|
||||
if sonsLen(a) == sonsLen(b):
|
||||
for i in countup(0, sonsLen(a) - 1):
|
||||
if not sameTree(a.sons[i], b.sons[i]): return
|
||||
if sonsLen(a) == sonsLen(b):
|
||||
for i in countup(0, sonsLen(a) - 1):
|
||||
if not sameTree(a.sons[i], b.sons[i]): return
|
||||
result = true
|
||||
|
||||
proc getProcSym*(call: PNode): PSym =
|
||||
|
||||
proc getProcSym*(call: PNode): PSym =
|
||||
result = call.sons[0].sym
|
||||
|
||||
proc getOpSym*(op: PNode): PSym =
|
||||
proc getOpSym*(op: PNode): PSym =
|
||||
if op.kind notin {nkCall, nkHiddenCallConv, nkCommand, nkCallStrLit}:
|
||||
result = nil
|
||||
else:
|
||||
@@ -91,25 +91,25 @@ proc getOpSym*(op: PNode): PSym =
|
||||
elif op.sons[0].kind == nkSym: result = op.sons[0].sym
|
||||
else: result = nil
|
||||
|
||||
proc getMagic*(op: PNode): TMagic =
|
||||
proc getMagic*(op: PNode): TMagic =
|
||||
case op.kind
|
||||
of nkCallKinds:
|
||||
case op.sons[0].kind
|
||||
of nkSym: result = op.sons[0].sym.magic
|
||||
else: result = mNone
|
||||
else: result = mNone
|
||||
|
||||
proc treeToSym*(t: PNode): PSym =
|
||||
|
||||
proc treeToSym*(t: PNode): PSym =
|
||||
result = t.sym
|
||||
|
||||
proc isConstExpr*(n: PNode): bool =
|
||||
proc isConstExpr*(n: PNode): bool =
|
||||
result = (n.kind in
|
||||
{nkCharLit..nkInt64Lit, nkStrLit..nkTripleStrLit,
|
||||
{nkCharLit..nkInt64Lit, nkStrLit..nkTripleStrLit,
|
||||
nkFloatLit..nkFloat64Lit, nkNilLit}) or (nfAllConst in n.flags)
|
||||
|
||||
proc isDeepConstExpr*(n: PNode): bool =
|
||||
case n.kind
|
||||
of nkCharLit..nkInt64Lit, nkStrLit..nkTripleStrLit,
|
||||
of nkCharLit..nkInt64Lit, nkStrLit..nkTripleStrLit,
|
||||
nkFloatLit..nkFloat64Lit, nkNilLit:
|
||||
result = true
|
||||
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
|
||||
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:
|
||||
for i in countup(1, sonsLen(a) - 1): # BUGFIX
|
||||
flattenTreeAux(d, a.sons[i], op)
|
||||
else:
|
||||
else:
|
||||
addSon(d, copyTree(a))
|
||||
|
||||
proc flattenTree*(root: PNode, op: TMagic): PNode =
|
||||
|
||||
proc flattenTree*(root: PNode, op: TMagic): PNode =
|
||||
result = copyNode(root)
|
||||
if getMagic(root) == op:
|
||||
# BUGFIX: forget to copy prc
|
||||
addSon(result, copyNode(root.sons[0]))
|
||||
flattenTreeAux(result, root, op)
|
||||
|
||||
proc swapOperands*(op: PNode) =
|
||||
proc swapOperands*(op: PNode) =
|
||||
var tmp = op.sons[1]
|
||||
op.sons[1] = op.sons[2]
|
||||
op.sons[2] = tmp
|
||||
|
||||
proc isRange*(n: PNode): bool {.inline.} =
|
||||
if n.kind == nkInfix:
|
||||
proc isRange*(n: PNode): bool {.inline.} =
|
||||
if n.kind in nkCallKinds:
|
||||
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):
|
||||
result = true
|
||||
|
||||
proc whichPragma*(n: PNode): TSpecialWord =
|
||||
proc whichPragma*(n: PNode): TSpecialWord =
|
||||
let key = if n.kind == nkExprColonExpr: n.sons[0] else: n
|
||||
if key.kind == nkIdent: result = whichKeyword(key.ident)
|
||||
|
||||
|
||||
@@ -541,6 +541,9 @@ proc typeToString(typ: PType, prefer: TPreferedDesc = preferName): string =
|
||||
of tyProc:
|
||||
result = if tfIterator in t.flags: "iterator (" else: "proc ("
|
||||
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]))
|
||||
if i < sonsLen(t) - 1: add(result, ", ")
|
||||
add(result, ')')
|
||||
@@ -1436,3 +1439,45 @@ proc skipConv*(n: PNode): PNode =
|
||||
proc skipConvTakeType*(n: PNode): PNode =
|
||||
result = n.skipConv
|
||||
result.typ = n.typ
|
||||
|
||||
proc isEmptyContainer*(t: PType): bool =
|
||||
case t.kind
|
||||
of tyExpr, tyNil: result = true
|
||||
of tyArray, tyArrayConstr: result = t.sons[1].kind == tyEmpty
|
||||
of tySet, tySequence, tyOpenArray, tyVarargs:
|
||||
result = t.sons[0].kind == tyEmpty
|
||||
of tyGenericInst: result = isEmptyContainer(t.lastSon)
|
||||
else: result = false
|
||||
|
||||
proc takeType*(formal, arg: PType): PType =
|
||||
# param: openArray[string] = []
|
||||
# [] is an array constructor of length 0 of type string!
|
||||
if arg.kind == tyNil:
|
||||
# and not (formal.kind == tyProc and formal.callConv == ccClosure):
|
||||
result = formal
|
||||
elif formal.kind in {tyOpenArray, tyVarargs, tySequence} and
|
||||
arg.isEmptyContainer:
|
||||
let a = copyType(arg.skipTypes({tyGenericInst}), arg.owner, keepId=false)
|
||||
a.sons[ord(arg.kind in {tyArray, tyArrayConstr})] = formal.sons[0]
|
||||
result = a
|
||||
elif formal.kind in {tyTuple, tySet} and arg.kind == formal.kind:
|
||||
result = formal
|
||||
else:
|
||||
result = arg
|
||||
|
||||
proc skipHiddenSubConv*(n: PNode): PNode =
|
||||
if n.kind == nkHiddenSubConv:
|
||||
# param: openArray[string] = []
|
||||
# [] is an array constructor of length 0 of type string!
|
||||
let formal = n.typ
|
||||
result = n.sons[1]
|
||||
let arg = result.typ
|
||||
let dest = takeType(formal, arg)
|
||||
if dest == arg and formal.kind != tyExpr:
|
||||
#echo n.info, " came here for ", formal.typeToString
|
||||
result = n
|
||||
else:
|
||||
result = copyTree(result)
|
||||
result.typ = dest
|
||||
else:
|
||||
result = n
|
||||
|
||||
@@ -16,7 +16,8 @@ import ast except getstr
|
||||
|
||||
import
|
||||
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 evaltempl import evalTemplate
|
||||
@@ -371,11 +372,6 @@ template handleJmpBack() {.dirty.} =
|
||||
globalError(c.debug[pc], errTooManyIterations)
|
||||
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 =
|
||||
var pc = start
|
||||
var tos = tos
|
||||
@@ -1043,7 +1039,14 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
|
||||
decodeB(rkNode)
|
||||
let newLen = regs[rb].intVal.int
|
||||
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:
|
||||
let rb = instr.regB
|
||||
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]
|
||||
createStr regs[ra]
|
||||
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
|
||||
|
||||
proc execute(c: PCtx, start: int): PNode =
|
||||
|
||||
@@ -16,7 +16,7 @@ const
|
||||
byteExcess* = 128 # we use excess-K for immediates
|
||||
wordExcess* = 32768
|
||||
|
||||
MaxLoopIterations* = 500_000 # max iterations of all loops
|
||||
MaxLoopIterations* = 1500_000 # max iterations of all loops
|
||||
|
||||
|
||||
type
|
||||
@@ -29,7 +29,7 @@ type
|
||||
opcRet, # return
|
||||
opcYldYoid, # yield with no value
|
||||
opcYldVal, # yield with a value
|
||||
|
||||
|
||||
opcAsgnInt,
|
||||
opcAsgnStr,
|
||||
opcAsgnFloat,
|
||||
@@ -48,8 +48,8 @@ type
|
||||
opcWrDeref,
|
||||
opcWrStrIdx,
|
||||
opcLdStrIdx, # a = b[c]
|
||||
|
||||
opcAddInt,
|
||||
|
||||
opcAddInt,
|
||||
opcAddImmInt,
|
||||
opcSubInt,
|
||||
opcSubImmInt,
|
||||
@@ -58,36 +58,37 @@ type
|
||||
|
||||
opcIncl, opcInclRange, opcExcl, opcCard, opcMulInt, opcDivInt, opcModInt,
|
||||
opcAddFloat, opcSubFloat, opcMulFloat, opcDivFloat, opcShrInt, opcShlInt,
|
||||
opcBitandInt, opcBitorInt, opcBitxorInt, opcAddu, opcSubu, opcMulu,
|
||||
opcDivu, opcModu, opcEqInt, opcLeInt, opcLtInt, opcEqFloat,
|
||||
opcLeFloat, opcLtFloat, opcLeu, opcLtu, opcEqRef, opcEqNimrodNode, opcXor,
|
||||
opcNot, opcUnaryMinusInt, opcUnaryMinusFloat, opcBitnotInt,
|
||||
opcBitandInt, opcBitorInt, opcBitxorInt, opcAddu, opcSubu, opcMulu,
|
||||
opcDivu, opcModu, opcEqInt, opcLeInt, opcLtInt, opcEqFloat,
|
||||
opcLeFloat, opcLtFloat, opcLeu, opcLtu, opcEqRef, opcEqNimrodNode, opcXor,
|
||||
opcNot, opcUnaryMinusInt, opcUnaryMinusFloat, opcBitnotInt,
|
||||
opcEqStr, opcLeStr, opcLtStr, opcEqSet, opcLeSet, opcLtSet,
|
||||
opcMulSet, opcPlusSet, opcMinusSet, opcSymdiffSet, opcConcatStr,
|
||||
opcContainsSet, opcRepr, opcSetLenStr, opcSetLenSeq,
|
||||
opcSwap, opcIsNil, opcOf, opcIs,
|
||||
opcSubStr, opcParseFloat, opcConv, opcCast, opcQuit, opcReset,
|
||||
opcSubStr, opcParseFloat, opcConv, opcCast,
|
||||
opcQuit, opcReset,
|
||||
opcNarrowS, opcNarrowU,
|
||||
|
||||
|
||||
opcAddStrCh,
|
||||
opcAddStrStr,
|
||||
opcAddSeqElem,
|
||||
opcRangeChck,
|
||||
|
||||
|
||||
opcNAdd,
|
||||
opcNAddMultiple,
|
||||
opcNKind,
|
||||
opcNIntVal,
|
||||
opcNFloatVal,
|
||||
opcNSymbol,
|
||||
opcNKind,
|
||||
opcNIntVal,
|
||||
opcNFloatVal,
|
||||
opcNSymbol,
|
||||
opcNIdent,
|
||||
opcNGetType,
|
||||
opcNStrVal,
|
||||
|
||||
|
||||
opcNSetIntVal,
|
||||
opcNSetFloatVal, opcNSetSymbol, opcNSetIdent, opcNSetType, opcNSetStrVal,
|
||||
opcNNewNimNode, opcNCopyNimNode, opcNCopyNimTree, opcNDel, opcGenSym,
|
||||
|
||||
|
||||
opcSlurp,
|
||||
opcGorge,
|
||||
opcParseExprToAst,
|
||||
@@ -100,7 +101,7 @@ type
|
||||
opcEqIdent,
|
||||
opcStrToIdent,
|
||||
opcIdentToStr,
|
||||
|
||||
|
||||
opcEcho,
|
||||
opcIndCall, # dest = call regStart, n; where regStart = fn, arg1, ...
|
||||
opcIndCallAsgn, # dest = call regStart, n; where regStart = fn, arg1, ...
|
||||
@@ -110,7 +111,7 @@ type
|
||||
opcNSetChild,
|
||||
opcCallSite,
|
||||
opcNewStr,
|
||||
|
||||
|
||||
opcTJmp, # jump Bx if A != 0
|
||||
opcFJmp, # jump Bx if A == 0
|
||||
opcJmp, # jump Bx
|
||||
@@ -132,7 +133,8 @@ type
|
||||
opcLdImmInt, # dest = immediate value
|
||||
opcNBindSym,
|
||||
opcSetType, # dest.typ = types[Bx]
|
||||
opcTypeTrait
|
||||
opcTypeTrait,
|
||||
opcMarshalLoad, opcMarshalStore
|
||||
|
||||
TBlock* = object
|
||||
label*: PSym
|
||||
@@ -178,13 +180,13 @@ type
|
||||
slots*: pointer
|
||||
currentException*: PNode
|
||||
VmCallback* = proc (args: VmArgs) {.closure.}
|
||||
|
||||
|
||||
PCtx* = ref TCtx
|
||||
TCtx* = object of passes.TPassContext # code gen context
|
||||
code*: seq[TInstr]
|
||||
debug*: seq[TLineInfo] # line info for every instruction; kept separate
|
||||
# to not slow down interpretation
|
||||
globals*: PNode #
|
||||
globals*: PNode #
|
||||
constants*: PNode # constant data
|
||||
types*: seq[PType] # some instructions reference types (e.g. 'except')
|
||||
currentExceptionA*, currentExceptionB*: PNode
|
||||
@@ -203,7 +205,7 @@ type
|
||||
TPosition* = distinct int
|
||||
|
||||
PEvalContext* = PCtx
|
||||
|
||||
|
||||
proc newCtx*(module: PSym): PCtx =
|
||||
PCtx(code: @[], debug: @[],
|
||||
globals: newNode(nkStmtListExpr), constants: newNode(nkStmtList), types: @[],
|
||||
@@ -221,7 +223,8 @@ proc registerCallback*(c: PCtx; name: string; callback: VmCallback) =
|
||||
const
|
||||
firstABxInstr* = opcTJmp
|
||||
largeInstrs* = { # instructions which use 2 int32s instead of 1:
|
||||
opcSubStr, opcConv, opcCast, opcNewSeq, opcOf}
|
||||
opcSubStr, opcConv, opcCast, opcNewSeq, opcOf,
|
||||
opcMarshalLoad, opcMarshalStore}
|
||||
slotSomeTemp* = slotTempUnknown
|
||||
relativeJumps* = {opcTJmp, opcFJmp, opcJmp, opcJmpBack}
|
||||
|
||||
|
||||
@@ -144,7 +144,9 @@ proc mapTypeToAst(t: PType, info: TLineInfo; allowRecursion=false): PNode =
|
||||
of tyIter: result = mapTypeToBracket("iter", t, info)
|
||||
of tyProxy: result = atomicType"error"
|
||||
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 tyAnd: result = mapTypeToBracket("and", t, info)
|
||||
of tyOr: result = mapTypeToBracket("or", t, info)
|
||||
|
||||
@@ -76,6 +76,11 @@ proc codeListing(c: PCtx, result: var string, start=0; last = -1) =
|
||||
elif opc in {opcLdConst, opcAsgnConst}:
|
||||
result.addf("\t$#\tr$#, $#", ($opc).substr(3), x.regA,
|
||||
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:
|
||||
result.addf("\t$#\tr$#, $#", ($opc).substr(3), x.regA, x.regBx-wordExcess)
|
||||
result.add("\t#")
|
||||
@@ -696,8 +701,7 @@ proc genCard(c: PCtx; n: PNode; dest: var TDest) =
|
||||
c.gABC(n, opcCard, dest, tmp)
|
||||
c.freeTemp(tmp)
|
||||
|
||||
proc genMagic(c: PCtx; n: PNode; dest: var TDest) =
|
||||
let m = n.sons[0].sym.magic
|
||||
proc genMagic(c: PCtx; n: PNode; dest: var TDest; m: TMagic) =
|
||||
case m
|
||||
of mAnd: c.genAndOr(n, opcFJmp, 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.freeTemp(tmp)
|
||||
# XXX buggy
|
||||
of mLengthOpenArray, mLengthArray, mLengthSeq:
|
||||
of mLengthOpenArray, mLengthArray, mLengthSeq, mXLenSeq:
|
||||
genUnaryABI(c, n, dest, opcLenSeq)
|
||||
of mLengthStr:
|
||||
of mLengthStr, mXLenStr:
|
||||
genUnaryABI(c, n, dest, opcLenStr)
|
||||
of mIncl, mExcl:
|
||||
unused(n, dest)
|
||||
@@ -791,7 +795,7 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest) =
|
||||
genUnaryABC(c, n, dest, opcUnaryMinusInt)
|
||||
genNarrow(c, n, dest)
|
||||
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:
|
||||
genUnaryABC(c, n, dest, opcBitnotInt)
|
||||
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)
|
||||
c.gABC(n, opcCallSite, dest)
|
||||
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:
|
||||
c.genCall(n, dest)
|
||||
of mExpandToAst:
|
||||
@@ -1028,6 +1032,22 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest) =
|
||||
# mGCref, mGCunref,
|
||||
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
|
||||
atomicTypes = {tyBool, tyChar,
|
||||
tyExpr, tyStmt, tyTypeDesc, tyStatic,
|
||||
@@ -1364,7 +1384,7 @@ proc getNullValue(typ: PType, info: TLineInfo): PNode =
|
||||
of tyCString, tyString:
|
||||
result = newNodeIT(nkStrLit, info, t)
|
||||
of tyVar, tyPointer, tyPtr, tySequence, tyExpr,
|
||||
tyStmt, tyTypeDesc, tyStatic, tyRef:
|
||||
tyStmt, tyTypeDesc, tyStatic, tyRef, tyNil:
|
||||
result = newNodeIT(nkNilLit, info, t)
|
||||
of tyProc:
|
||||
if t.callConv != ccClosure:
|
||||
@@ -1391,7 +1411,7 @@ proc getNullValue(typ: PType, info: TLineInfo): PNode =
|
||||
addSon(result, getNullValue(t.sons[i], info))
|
||||
of tySet:
|
||||
result = newNodeIT(nkCurly, info, t)
|
||||
else: internalError("getNullValue: " & $t.kind)
|
||||
else: internalError(info, "getNullValue: " & $t.kind)
|
||||
|
||||
proc ldNullOpcode(t: PType): TOpcode =
|
||||
if fitsRegister(t): opcLdNullReg else: opcLdNull
|
||||
@@ -1533,6 +1553,15 @@ proc matches(s: PSym; x: string): bool =
|
||||
dec L
|
||||
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 =
|
||||
if s.offset < -1: return true
|
||||
var i = -2
|
||||
@@ -1570,8 +1599,17 @@ proc gen(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags = {}) =
|
||||
else:
|
||||
internalError(n.info, "cannot generate code for: " & s.name.s)
|
||||
of nkCallKinds:
|
||||
if n.sons[0].kind == nkSym and n.sons[0].sym.magic != mNone:
|
||||
genMagic(c, n, dest)
|
||||
if n.sons[0].kind == nkSym:
|
||||
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:
|
||||
genCall(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)
|
||||
of nkTryStmt: genTry(c, n, dest)
|
||||
of nkStmtList:
|
||||
unused(n, dest)
|
||||
#unused(n, dest)
|
||||
# XXX Fix this bug properly, lexim triggers it
|
||||
for x in n: gen(c, x)
|
||||
of nkStmtListExpr:
|
||||
let L = n.len-1
|
||||
|
||||
283
compiler/vmmarshal.nim
Normal file
283
compiler/vmmarshal.nim
Normal file
@@ -0,0 +1,283 @@
|
||||
#
|
||||
#
|
||||
# The Nim Compiler
|
||||
# (c) Copyright 2015 Andreas Rumpf
|
||||
#
|
||||
# See the file "copying.txt", included in this
|
||||
# distribution, for details about the copyright.
|
||||
#
|
||||
|
||||
## Implements marshaling for the VM.
|
||||
|
||||
import streams, json, intsets, tables, ast, astalgo, idents, types, msgs
|
||||
|
||||
proc ptrToInt(x: PNode): int {.inline.} =
|
||||
result = cast[int](x) # don't skip alignment
|
||||
|
||||
proc getField(n: PNode; position: int): PSym =
|
||||
case n.kind
|
||||
of nkRecList:
|
||||
for i in countup(0, sonsLen(n) - 1):
|
||||
result = getField(n.sons[i], position)
|
||||
if result != nil: return
|
||||
of nkRecCase:
|
||||
result = getField(n.sons[0], position)
|
||||
if result != nil: return
|
||||
for i in countup(1, sonsLen(n) - 1):
|
||||
case n.sons[i].kind
|
||||
of nkOfBranch, nkElse:
|
||||
result = getField(lastSon(n.sons[i]), position)
|
||||
if result != nil: return
|
||||
else: internalError(n.info, "getField(record case branch)")
|
||||
of nkSym:
|
||||
if n.sym.position == position: result = n.sym
|
||||
else: discard
|
||||
|
||||
proc storeAny(s: var string; t: PType; a: PNode; stored: var IntSet)
|
||||
|
||||
proc storeObj(s: var string; typ: PType; x: PNode; stored: var IntSet) =
|
||||
internalAssert x.kind in {nkObjConstr, nkPar}
|
||||
let start = ord(x.kind == nkObjConstr)
|
||||
for i in countup(start, sonsLen(x) - 1):
|
||||
if i > start: s.add(", ")
|
||||
var it = x.sons[i]
|
||||
if it.kind == nkExprColonExpr:
|
||||
internalAssert it.sons[0].kind == nkSym
|
||||
let field = it.sons[0].sym
|
||||
s.add(escapeJson(field.name.s))
|
||||
s.add(": ")
|
||||
storeAny(s, field.typ, it.sons[1], stored)
|
||||
elif typ.n != nil:
|
||||
let field = getField(typ.n, i)
|
||||
s.add(escapeJson(field.name.s))
|
||||
s.add(": ")
|
||||
storeAny(s, field.typ, it, stored)
|
||||
|
||||
proc skipColon*(n: PNode): PNode =
|
||||
result = n
|
||||
if n.kind == nkExprColonExpr:
|
||||
result = n.sons[1]
|
||||
|
||||
proc storeAny(s: var string; t: PType; a: PNode; stored: var IntSet) =
|
||||
case t.kind
|
||||
of tyNone: assert false
|
||||
of tyBool: s.add($(a.intVal != 0))
|
||||
of tyChar:
|
||||
let ch = char(a.intVal)
|
||||
if ch < '\128':
|
||||
s.add(escapeJson($ch))
|
||||
else:
|
||||
s.add($int(ch))
|
||||
of tyArray, tySequence:
|
||||
if t.kind == tySequence and a.kind == nkNilLit: s.add("null")
|
||||
else:
|
||||
s.add("[")
|
||||
for i in 0 .. a.len-1:
|
||||
if i > 0: s.add(", ")
|
||||
storeAny(s, t.elemType, a[i], stored)
|
||||
s.add("]")
|
||||
of tyTuple:
|
||||
s.add("{")
|
||||
for i in 0.. <t.len:
|
||||
if i > 0: s.add(", ")
|
||||
s.add("\"Field" & $i)
|
||||
s.add("\": ")
|
||||
storeAny(s, t.sons[i], a[i].skipColon, stored)
|
||||
s.add("}")
|
||||
of tyObject:
|
||||
s.add("{")
|
||||
storeObj(s, t, a, stored)
|
||||
s.add("}")
|
||||
of tySet:
|
||||
s.add("[")
|
||||
for i in 0.. <a.len:
|
||||
if i > 0: s.add(", ")
|
||||
if a[i].kind == nkRange:
|
||||
var x = copyNode(a[i][0])
|
||||
storeAny(s, t.lastSon, x, stored)
|
||||
while x.intVal+1 <= a[i][1].intVal:
|
||||
s.add(", ")
|
||||
storeAny(s, t.lastSon, x, stored)
|
||||
inc x.intVal
|
||||
else:
|
||||
storeAny(s, t.lastSon, a[i], stored)
|
||||
s.add("]")
|
||||
of tyRange, tyGenericInst: storeAny(s, t.lastSon, a, stored)
|
||||
of tyEnum:
|
||||
# we need a slow linear search because of enums with holes:
|
||||
for e in items(t.n):
|
||||
if e.sym.position == a.intVal:
|
||||
s.add e.sym.name.s.escapeJson
|
||||
break
|
||||
of tyPtr, tyRef:
|
||||
var x = a
|
||||
if isNil(x) or x.kind == nkNilLit: s.add("null")
|
||||
elif stored.containsOrIncl(x.ptrToInt):
|
||||
# already stored, so we simply write out the pointer as an int:
|
||||
s.add($x.ptrToInt)
|
||||
else:
|
||||
# else as a [value, key] pair:
|
||||
# (reversed order for convenient x[0] access!)
|
||||
s.add("[")
|
||||
s.add($x.ptrToInt)
|
||||
s.add(", ")
|
||||
storeAny(s, t.lastSon, a, stored)
|
||||
s.add("]")
|
||||
of tyString, tyCString:
|
||||
if a.kind == nkNilLit or a.strVal.isNil: s.add("null")
|
||||
else: s.add(escapeJson(a.strVal))
|
||||
of tyInt..tyInt64, tyUInt..tyUInt64: s.add($a.intVal)
|
||||
of tyFloat..tyFloat128: s.add($a.floatVal)
|
||||
else:
|
||||
internalError a.info, "cannot marshal at compile-time " & t.typeToString
|
||||
|
||||
proc storeAny*(s: var string; t: PType; a: PNode) =
|
||||
var stored = initIntSet()
|
||||
storeAny(s, t, a, stored)
|
||||
|
||||
proc loadAny(p: var JsonParser, t: PType,
|
||||
tab: var Table[BiggestInt, PNode]): PNode =
|
||||
case t.kind
|
||||
of tyNone: assert false
|
||||
of tyBool:
|
||||
case p.kind
|
||||
of jsonFalse: result = newIntNode(nkIntLit, 0)
|
||||
of jsonTrue: result = newIntNode(nkIntLit, 1)
|
||||
else: raiseParseErr(p, "'true' or 'false' expected for a bool")
|
||||
next(p)
|
||||
of tyChar:
|
||||
if p.kind == jsonString:
|
||||
var x = p.str
|
||||
if x.len == 1:
|
||||
result = newIntNode(nkIntLit, ord(x[0]))
|
||||
next(p)
|
||||
return
|
||||
elif p.kind == jsonInt:
|
||||
result = newIntNode(nkIntLit, getInt(p))
|
||||
next(p)
|
||||
return
|
||||
raiseParseErr(p, "string of length 1 expected for a char")
|
||||
of tyEnum:
|
||||
if p.kind == jsonString:
|
||||
for e in items(t.n):
|
||||
if e.sym.name.s == p.str:
|
||||
result = newIntNode(nkIntLit, e.sym.position)
|
||||
next(p)
|
||||
return
|
||||
raiseParseErr(p, "string expected for an enum")
|
||||
of tyArray:
|
||||
if p.kind != jsonArrayStart: raiseParseErr(p, "'[' expected for an array")
|
||||
next(p)
|
||||
result = newNode(nkBracket)
|
||||
while p.kind != jsonArrayEnd and p.kind != jsonEof:
|
||||
result.add loadAny(p, t.elemType, tab)
|
||||
if p.kind == jsonArrayEnd: next(p)
|
||||
else: raiseParseErr(p, "']' end of array expected")
|
||||
of tySequence:
|
||||
case p.kind
|
||||
of jsonNull:
|
||||
result = newNode(nkNilLit)
|
||||
next(p)
|
||||
of jsonArrayStart:
|
||||
next(p)
|
||||
result = newNode(nkBracket)
|
||||
while p.kind != jsonArrayEnd and p.kind != jsonEof:
|
||||
result.add loadAny(p, t.elemType, tab)
|
||||
if p.kind == jsonArrayEnd: next(p)
|
||||
else: raiseParseErr(p, "")
|
||||
else:
|
||||
raiseParseErr(p, "'[' expected for a seq")
|
||||
of tyTuple:
|
||||
if p.kind != jsonObjectStart: raiseParseErr(p, "'{' expected for an object")
|
||||
next(p)
|
||||
result = newNode(nkPar)
|
||||
var i = 0
|
||||
while p.kind != jsonObjectEnd and p.kind != jsonEof:
|
||||
if p.kind != jsonString:
|
||||
raiseParseErr(p, "string expected for a field name")
|
||||
next(p)
|
||||
if i >= t.len:
|
||||
raiseParseErr(p, "too many fields to tuple type " & typeToString(t))
|
||||
result.add loadAny(p, t.sons[i], tab)
|
||||
inc i
|
||||
if p.kind == jsonObjectEnd: next(p)
|
||||
else: raiseParseErr(p, "'}' end of object expected")
|
||||
of tyObject:
|
||||
if p.kind != jsonObjectStart: raiseParseErr(p, "'{' expected for an object")
|
||||
next(p)
|
||||
result = newNode(nkPar)
|
||||
result.sons = @[]
|
||||
while p.kind != jsonObjectEnd and p.kind != jsonEof:
|
||||
if p.kind != jsonString:
|
||||
raiseParseErr(p, "string expected for a field name")
|
||||
let field = lookupInRecord(t.n, getIdent(p.str))
|
||||
if field.isNil:
|
||||
raiseParseErr(p, "unknown field for object of type " & typeToString(t))
|
||||
next(p)
|
||||
if field.position >= result.sons.len:
|
||||
setLen(result.sons, field.position+1)
|
||||
result.sons[field.position] = loadAny(p, field.typ, tab)
|
||||
if p.kind == jsonObjectEnd: next(p)
|
||||
else: raiseParseErr(p, "'}' end of object expected")
|
||||
of tySet:
|
||||
if p.kind != jsonArrayStart: raiseParseErr(p, "'[' expected for a set")
|
||||
next(p)
|
||||
result = newNode(nkCurly)
|
||||
while p.kind != jsonArrayEnd and p.kind != jsonEof:
|
||||
result.add loadAny(p, t.lastSon, tab)
|
||||
next(p)
|
||||
if p.kind == jsonArrayEnd: next(p)
|
||||
else: raiseParseErr(p, "']' end of array expected")
|
||||
of tyPtr, tyRef:
|
||||
case p.kind
|
||||
of jsonNull:
|
||||
result = newNode(nkNilLit)
|
||||
next(p)
|
||||
of jsonInt:
|
||||
result = tab[p.getInt]
|
||||
if result.isNil:
|
||||
raiseParseErr(p, "cannot load object with address " & $p.getInt)
|
||||
next(p)
|
||||
of jsonArrayStart:
|
||||
next(p)
|
||||
if p.kind == jsonInt:
|
||||
let idx = p.getInt
|
||||
next(p)
|
||||
result = loadAny(p, t.lastSon, tab)
|
||||
tab[idx] = result
|
||||
else: raiseParseErr(p, "index for ref type expected")
|
||||
if p.kind == jsonArrayEnd: next(p)
|
||||
else: raiseParseErr(p, "']' end of ref-address pair expected")
|
||||
else: raiseParseErr(p, "int for pointer type expected")
|
||||
of tyString, tyCString:
|
||||
case p.kind
|
||||
of jsonNull:
|
||||
result = newNode(nkNilLit)
|
||||
next(p)
|
||||
of jsonString:
|
||||
result = newStrNode(nkStrLit, p.str)
|
||||
next(p)
|
||||
else: raiseParseErr(p, "string expected")
|
||||
of tyInt..tyInt64, tyUInt..tyUInt64:
|
||||
if p.kind == jsonInt:
|
||||
result = newIntNode(nkIntLit, getInt(p))
|
||||
next(p)
|
||||
return
|
||||
raiseParseErr(p, "int expected")
|
||||
of tyFloat..tyFloat128:
|
||||
if p.kind == jsonFloat:
|
||||
result = newFloatNode(nkFloatLit, getFloat(p))
|
||||
next(p)
|
||||
return
|
||||
raiseParseErr(p, "float expected")
|
||||
of tyRange, tyGenericInst: result = loadAny(p, t.lastSon, tab)
|
||||
else:
|
||||
internalError "cannot marshal at compile-time " & t.typeToString
|
||||
|
||||
proc loadAny*(s: string; t: PType): PNode =
|
||||
var tab = initTable[BiggestInt, PNode]()
|
||||
var p: JsonParser
|
||||
open(p, newStringStream(s), "unknown file")
|
||||
next(p)
|
||||
result = loadAny(p, t, tab)
|
||||
close(p)
|
||||
@@ -10,7 +10,7 @@
|
||||
# Unforunately this cannot be a module yet:
|
||||
#import vmdeps, vm
|
||||
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
|
||||
|
||||
from os import getEnv, existsEnv, dirExists, fileExists
|
||||
|
||||
@@ -96,7 +96,7 @@ path="$lib/pure/unidecode"
|
||||
|
||||
# Configuration for the GNU C/C++ compiler:
|
||||
@if windows:
|
||||
#gcc.path = r"$nimrod\dist\mingw\bin"
|
||||
#gcc.path = r"$nim\dist\mingw\bin"
|
||||
@if gcc:
|
||||
tlsEmulation:on
|
||||
@end
|
||||
|
||||
1
csources
Submodule
1
csources
Submodule
Submodule csources added at 15724e2e1f
@@ -179,7 +179,7 @@ simpleStmt = ((returnStmt | raiseStmt | yieldStmt | discardStmt | breakStmt
|
||||
| continueStmt | pragmaStmt | importStmt | exportStmt | fromStmt
|
||||
| includeStmt | commentStmt) / exprStmt) COMMENT?
|
||||
complexOrSimpleStmt = (ifStmt | whenStmt | whileStmt
|
||||
| tryStmt | finallyStmt | exceptStmt | forStmt
|
||||
| tryStmt | forStmt
|
||||
| blockStmt | staticStmt | deferStmt | asmStmt
|
||||
| 'proc' routine
|
||||
| 'method' routine
|
||||
|
||||
47
doc/lib.txt
47
doc/lib.txt
@@ -37,7 +37,7 @@ Core
|
||||
|
||||
* `unsigned <unsigned.html>`_
|
||||
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.
|
||||
|
||||
* `threads <threads.html>`_
|
||||
@@ -45,7 +45,7 @@ Core
|
||||
import it explicitly.
|
||||
|
||||
* `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.
|
||||
|
||||
* `locks <locks.html>`_
|
||||
@@ -55,7 +55,7 @@ Core
|
||||
Contains the AST API and documentation of Nim for writing macros.
|
||||
|
||||
* `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>`_
|
||||
This module defines compile-time reflection procs for working with types.
|
||||
@@ -110,9 +110,9 @@ String handling
|
||||
|
||||
* `unicode <unicode.html>`_
|
||||
This module provides support to handle the Unicode UTF-8 encoding.
|
||||
|
||||
|
||||
* `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.
|
||||
|
||||
* `pegs <pegs.html>`_
|
||||
@@ -159,7 +159,7 @@ Generic Operating System Services
|
||||
may provide other implementations for this standard stream interface.
|
||||
|
||||
* `marshal <marshal.html>`_
|
||||
Contains procs for serialization and deseralization of arbitrary Nim
|
||||
Contains procs for serialization and deseralization of arbitrary Nim
|
||||
data structures.
|
||||
|
||||
* `terminal <terminal.html>`_
|
||||
@@ -168,7 +168,7 @@ Generic Operating System Services
|
||||
sequences and does not depend on any other module.
|
||||
|
||||
* `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.
|
||||
|
||||
* `fsmonitor <fsmonitor.html>`_
|
||||
@@ -228,7 +228,7 @@ Internet Protocols and Support
|
||||
This module implements a simple HTTP client.
|
||||
|
||||
* `smtp <smtp.html>`_
|
||||
This module implement a simple SMTP client.
|
||||
This module implement a simple SMTP client.
|
||||
|
||||
* `ftpclient <ftpclient.html>`_
|
||||
This module implements an FTP client.
|
||||
@@ -346,7 +346,7 @@ XML Processing
|
||||
This module parses an HTML document and creates its XML tree representation.
|
||||
|
||||
* `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
|
||||
that generates a string with its HTML representation.
|
||||
|
||||
@@ -381,7 +381,7 @@ Miscellaneous
|
||||
|
||||
* `oids <oids.html>`_
|
||||
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
|
||||
from the Mongodb interface and it thus binary compatible with a Mongo OID.
|
||||
|
||||
@@ -453,12 +453,8 @@ Other
|
||||
* `zipfiles <zipfiles.html>`_
|
||||
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>`_
|
||||
This module provides an easy to use sockets-style
|
||||
This module provides an easy to use sockets-style
|
||||
Nim interface to the OpenSSL library.
|
||||
|
||||
* `rdstdin <rdstdin.html>`_
|
||||
@@ -513,25 +509,6 @@ Regular expressions
|
||||
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
|
||||
-------------
|
||||
|
||||
@@ -591,7 +568,7 @@ Data Compression and Archiving
|
||||
Scientific computing
|
||||
--------------------
|
||||
|
||||
* `libsvm <libsvm.html>`_
|
||||
* `libsvm <libsvm.html>`_
|
||||
Low level wrapper for `lib svm <http://www.csie.ntu.edu.tw/~cjlin/libsvm/>`_.
|
||||
|
||||
Nimble
|
||||
|
||||
@@ -2,7 +2,7 @@ Statements and expressions
|
||||
==========================
|
||||
|
||||
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 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
|
||||
-------------------------
|
||||
|
||||
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
|
||||
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
|
||||
must be of type ``void``. (One can use ``discard`` to produce a ``void`` type.)
|
||||
``(;)`` does not introduce a new scope.
|
||||
@@ -30,24 +30,24 @@ Discard statement
|
||||
Example:
|
||||
|
||||
.. code-block:: nim
|
||||
proc p(x, y: int): int =
|
||||
proc p(x, y: int): int =
|
||||
result = x + y
|
||||
|
||||
discard p(3, 4) # discard the return value of `p`
|
||||
|
||||
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
|
||||
a static error.
|
||||
|
||||
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
|
||||
proc p(x, y: int): int {.discardable.} =
|
||||
proc p(x, y: int): int {.discardable.} =
|
||||
result = x + y
|
||||
|
||||
|
||||
p(3, 4) # now valid
|
||||
|
||||
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
|
||||
`noinit`:idx: pragma:
|
||||
`noinit`:idx: pragma:
|
||||
|
||||
.. code-block:: nim
|
||||
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
|
||||
``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:
|
||||
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:
|
||||
|
||||
.. code-block:: nim
|
||||
type
|
||||
MyObject = object {.requiresInit.}
|
||||
|
||||
|
||||
proc p() =
|
||||
# the following is valid:
|
||||
var x: MyObject
|
||||
@@ -129,11 +129,12 @@ initialized and does not rely on syntactic properties:
|
||||
x = a()
|
||||
use x
|
||||
|
||||
|
||||
let statement
|
||||
-------------
|
||||
|
||||
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``.
|
||||
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.
|
||||
@@ -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.
|
||||
|
||||
|
||||
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
|
||||
-------------
|
||||
|
||||
@@ -157,33 +171,33 @@ have no side-effect can be used in constant expressions too:
|
||||
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.
|
||||
2. Type conversions are compile-time computable.
|
||||
3. Procedure calls of the form ``p(X)`` are compile-time computable if
|
||||
``p`` is a proc without side-effects (see the `noSideEffect pragma`_
|
||||
for details) and if ``X`` is a (possibly empty) list of compile-time
|
||||
``p`` is a proc without side-effects (see the `noSideEffect pragma`_
|
||||
for details) and if ``X`` is a (possibly empty) list of compile-time
|
||||
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.
|
||||
|
||||
|
||||
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
|
||||
code that has side effects:
|
||||
code that has side effects:
|
||||
|
||||
.. code-block::
|
||||
|
||||
static:
|
||||
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.
|
||||
|
||||
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``
|
||||
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
|
||||
the corresponding *then* block:
|
||||
|
||||
@@ -229,7 +243,7 @@ the corresponding *then* block:
|
||||
else:
|
||||
# 'm' not declared here
|
||||
|
||||
In the example the scopes have been enclosed in ``{| |}``.
|
||||
In the example the scopes have been enclosed in ``{| |}``.
|
||||
|
||||
|
||||
Case statement
|
||||
@@ -244,7 +258,7 @@ Example:
|
||||
echo("permission denied")
|
||||
of "go-for-a-walk": echo("please yourself")
|
||||
else: echo("unknown command")
|
||||
|
||||
|
||||
# indentation of the branches is also allowed; and so is an optional colon
|
||||
# after the selecting expression:
|
||||
case readline(stdin):
|
||||
@@ -252,15 +266,15 @@ Example:
|
||||
echo("permission denied")
|
||||
of "go-for-a-walk": echo("please yourself")
|
||||
else: echo("unknown command")
|
||||
|
||||
|
||||
|
||||
The ``case`` statement is similar to the if statement, but it represents
|
||||
a multi-branch selection. The expression after the keyword ``case`` is
|
||||
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
|
||||
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
|
||||
``slicelist``, a static error occurs. This holds only for expressions of
|
||||
part and not all possible values that ``expr`` can hold occur in a
|
||||
``slicelist``, a static error occurs. This holds only for expressions of
|
||||
ordinal types. "All possible values" of ``expr`` are determined by ``expr``'s
|
||||
type. To suppress the static error an ``else`` part with an
|
||||
empty ``discard`` statement should be used.
|
||||
@@ -281,7 +295,7 @@ expanded into a list of its elements:
|
||||
of SymChars, '_': echo "an identifier"
|
||||
of '0'..'9': echo "a number"
|
||||
else: echo "other"
|
||||
|
||||
|
||||
# is equivalent to:
|
||||
proc classify(s: string) =
|
||||
case s[0]
|
||||
@@ -580,14 +594,14 @@ A table constructor is syntactic sugar for an array constructor:
|
||||
|
||||
.. code-block:: nim
|
||||
{"key1": "value1", "key2", "key3": "value2"}
|
||||
|
||||
|
||||
# is the same as:
|
||||
[("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
|
||||
constructor ``[]``. This slightly unusal way of supporting tables
|
||||
constructor ``[]``. This slightly unusal way of supporting tables
|
||||
has lots of advantages:
|
||||
|
||||
* The order of the (key,value)-pairs is preserved, thus it is easy to
|
||||
|
||||
@@ -15,8 +15,6 @@ Associativity
|
||||
Binary operators whose first character is ``^`` are right-associative, all
|
||||
other binary operators are left-associative.
|
||||
|
||||
Operators ending in ``>`` but longer than a single character are
|
||||
called `arrow like`:idx:.
|
||||
|
||||
|
||||
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
|
||||
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
|
||||
``<``, ``>``, ``!``, ``=``, ``~``, ``?``, it is an *assignment operator* which
|
||||
has the lowest precedence.
|
||||
has the second lowest precedence.
|
||||
|
||||
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
|
||||
================ =============================================== ================== ===============
|
||||
10 (highest) ``$ ^`` OP10
|
||||
9 ``* / div mod shl shr %`` ``* % \ /`` OP9
|
||||
8 ``+ -`` ``+ ~ |`` OP8
|
||||
9 ``* / div mod shl shr %`` ``* % \ /`` OP9
|
||||
8 ``+ -`` ``+ - ~ |`` OP8
|
||||
7 ``&`` ``&`` OP7
|
||||
6 ``..`` ``.`` OP6
|
||||
5 ``== <= < >= > != in notin is isnot not of`` ``= < > !`` OP5
|
||||
5 ``== <= < >= > != in notin is isnot not of`` ``= < > !`` OP5
|
||||
4 ``and`` OP4
|
||||
3 ``or xor`` OP3
|
||||
2 ``@ : ?`` OP2
|
||||
2 ``@ : ?`` OP2
|
||||
1 *assignment operator* (like ``+=``, ``*=``) OP1
|
||||
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
|
||||
#! strongSpaces
|
||||
if foo+4 * 4 == 8 and b&c | 9 ++
|
||||
if foo+4 * 4 == 8 and b&c | 9 ++
|
||||
bar:
|
||||
echo ""
|
||||
# is parsed as
|
||||
|
||||
@@ -127,8 +127,8 @@ The signature has to be:
|
||||
.. code-block:: nim
|
||||
proc `=deepCopy`(x: T): T
|
||||
|
||||
This mechanism is used by most data structures that support shared memory like
|
||||
channels to implement thread safe automatic memory management.
|
||||
This mechanism will be used by most data structures that support shared memory
|
||||
like channels to implement thread safe automatic memory management.
|
||||
|
||||
The builtin ``deepCopy`` can even clone closures and their environments. See
|
||||
the documentation of `spawn`_ for details.
|
||||
|
||||
21
doc/nimc.txt
21
doc/nimc.txt
@@ -386,6 +386,25 @@ Example:
|
||||
As can be seen from the example, to Nim symbols can be referred via backticks.
|
||||
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
|
||||
----------------
|
||||
@@ -611,7 +630,7 @@ Produces:
|
||||
Produces:
|
||||
|
||||
.. code-block:: C
|
||||
|
||||
|
||||
std::vector<int>::iterator x;
|
||||
|
||||
|
||||
|
||||
174
doc/nimsuggest.txt
Normal file
174
doc/nimsuggest.txt
Normal file
@@ -0,0 +1,174 @@
|
||||
================================
|
||||
Nim IDE Integration Guide
|
||||
================================
|
||||
|
||||
:Author: Unknown
|
||||
:Version: |nimversion|
|
||||
|
||||
.. contents::
|
||||
|
||||
|
||||
Nim differs from many other compilers in that it is really fast,
|
||||
and being so fast makes it suited to provide external queries for
|
||||
text editors about the source code being written. Through the
|
||||
``nimsuggest`` tool, any IDE
|
||||
can query a ``.nim`` source file and obtain useful information like
|
||||
definition of symbols or suggestions for completion.
|
||||
|
||||
This document will guide you through the available options. If you
|
||||
want to look at practical examples of nimsuggest support you can look
|
||||
at the
|
||||
`various editor integrations <https://github.com/Araq/Nim/wiki/Editor-Support>`_
|
||||
already available.
|
||||
|
||||
|
||||
Installation
|
||||
============
|
||||
|
||||
Nimsuggest is available as a Nimble package but currently does not install
|
||||
properly via Nimble. As nimsuggest is part of the compiler it also doesn't make
|
||||
too much sense as a Nimble package. Instead we will do the building manually::
|
||||
|
||||
cd compiler/nimsuggest
|
||||
nim c -d:release nimsuggest
|
||||
cp nimsuggest ../../bin
|
||||
# OR: copy the nimsuggest binary to where your 'nim' binary is
|
||||
cd ../..
|
||||
|
||||
|
||||
|
||||
Nimsuggest invocation
|
||||
=====================
|
||||
|
||||
Run it via ``nimsuggest --stdin myproject.nim``. Nimsuggest is a server that
|
||||
takes queries that are related to ``myproject``. There is some support so that
|
||||
you can throw random ``.nim`` files which are not part of ``myproject`` at
|
||||
Nimsuggest too, but usually the query refer to modules/files that are part of
|
||||
``myproject``.
|
||||
|
||||
``--stdin`` means that Nimsuggest reads the query from ``stdin``. This is great
|
||||
for testing things out and playing with it but for an editor communication
|
||||
via sockets is more reasonable so that is the default. It listens to port 6000
|
||||
by default.
|
||||
|
||||
|
||||
Specifying the location of the query
|
||||
------------------------------------
|
||||
|
||||
Nimsuggest than waits for queries to process. A query consists of a
|
||||
cryptic 3 letter "command" ``def`` or ``con`` or ``sug`` or ``use`` followed by
|
||||
a location. A query location consists of:
|
||||
|
||||
|
||||
``file.nim``
|
||||
This is the name of the module or include file the query refers to.
|
||||
|
||||
``dirtyfile.nim``
|
||||
This is optional.
|
||||
|
||||
The ``file`` paramater is enough for static analysis, but IDEs
|
||||
tend to have *unsaved buffers* where the user may still be in
|
||||
the middle of typing a line. In such situations the IDE can
|
||||
save the current contents to a temporary file and then use the
|
||||
``dirtyfile.nim`` option to tell Nimsuggest that ``foobar.nim`` should
|
||||
be taken from ``temporary/foobar.nim``.
|
||||
|
||||
|
||||
``line``
|
||||
An integer with the line you are going to query. For the compiler
|
||||
lines start at **1**.
|
||||
|
||||
``col``
|
||||
An integer with the column you are going to query. For the
|
||||
compiler columns start at **1**.
|
||||
|
||||
|
||||
Definitions
|
||||
-----------
|
||||
|
||||
The ``def`` Nimsuggest command performs a query about the definition
|
||||
of a specific symbol. If available, Nimsuggest will answer with the
|
||||
type, source file, line/column information and other accessory data
|
||||
if available like a docstring. With this information an IDE can
|
||||
provide the typical *Jump to definition* where a user puts the
|
||||
cursor on a symbol or uses the mouse to select it and is redirected
|
||||
to the place where the symbol is located.
|
||||
|
||||
Since Nim is implemented in Nim, one of the nice things of
|
||||
this feature is that any user with an IDE supporting it can quickly
|
||||
jump around the standard library implementation and see exactly
|
||||
what a proc does, learning about the language and seeing real life
|
||||
examples of how to write/implement specific features.
|
||||
|
||||
Nimsuggest will always answer with a single definition or none if it
|
||||
can't find any valid symbol matching the position of the query.
|
||||
|
||||
|
||||
Suggestions
|
||||
-----------
|
||||
|
||||
The ``sug`` Nimsuggest command performs a query about possible
|
||||
completion symbols at some point in the file.
|
||||
|
||||
The typical usage scenario for this option is to call it after the
|
||||
user has typed the dot character for `the object oriented call
|
||||
syntax <tut2.html#method-call-syntax>`_. Nimsuggest will try to return
|
||||
the suggestions sorted first by scope (from innermost to outermost)
|
||||
and then by item name.
|
||||
|
||||
|
||||
Invocation context
|
||||
------------------
|
||||
|
||||
The ``con`` Nimsuggest command is very similar to the suggestions
|
||||
command, but instead of being used after the user has typed a dot
|
||||
character, this one is meant to be used after the user has typed
|
||||
an opening brace to start typing parameters.
|
||||
|
||||
|
||||
Symbol usages
|
||||
-------------
|
||||
|
||||
The ``use`` Nimsuggest command lists all usages of the symbol at
|
||||
a position. IDEs can use this to find all the places in the file
|
||||
where the symbol is used and offer the user to rename it in all
|
||||
places at the same time.
|
||||
|
||||
For this kind of query the IDE will most likely ignore all the
|
||||
type/signature info provided by Nimsuggest and concentrate on the
|
||||
filename, line and column position of the multiple returned answers.
|
||||
|
||||
|
||||
|
||||
Parsing nimsuggest output
|
||||
=========================
|
||||
|
||||
Nimsuggest output is always returned on single lines separated by
|
||||
tab characters (``\t``). The values of each column are:
|
||||
|
||||
1. Three characters indicating the type of returned answer (e.g.
|
||||
``def`` for definition, ``sug`` for suggestion, etc).
|
||||
2. Type of the symbol. This can be ``skProc``, ``skLet``, and just
|
||||
about any of the enums defined in the module ``compiler/ast.nim``.
|
||||
3. Full qualitifed path of the symbol. If you are querying a symbol
|
||||
defined in the ``proj.nim`` file, this would have the form
|
||||
``proj.symbolName``.
|
||||
4. Type/signature. For variables and enums this will contain the
|
||||
type of the symbol, for procs, methods and templates this will
|
||||
contain the full unique signature (e.g. ``proc (File)``).
|
||||
5. Full path to the file containing the symbol.
|
||||
6. Line where the symbol is located in the file. Lines start to
|
||||
count at **1**.
|
||||
7. Column where the symbol is located in the file. Columns start
|
||||
to count at **1**.
|
||||
8. Docstring for the symbol if available or the empty string. To
|
||||
differentiate the docstring from end of answer,
|
||||
the docstring is always provided enclosed in double quotes, and
|
||||
if the docstring spans multiple lines, all following lines of the
|
||||
docstring will start with a blank space to align visually with
|
||||
the starting quote.
|
||||
|
||||
Also, you won't find raw ``\n`` characters breaking the one
|
||||
answer per line format. Instead you will need to parse sequences
|
||||
in the form ``\xHH``, where *HH* is a hexadecimal value (e.g.
|
||||
newlines generate the sequence ``\x0A``).
|
||||
@@ -4,6 +4,11 @@ Tools available with Nim
|
||||
|
||||
The standard distribution ships with the following tools:
|
||||
|
||||
- | `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>`_
|
||||
| How to generate a nice installer for your Nim program.
|
||||
|
||||
|
||||
72
doc/tut1.txt
72
doc/tut1.txt
@@ -16,7 +16,7 @@ Introduction
|
||||
</p></blockquote>
|
||||
|
||||
|
||||
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
|
||||
like variables, types or statements but is kept very basic. The `manual
|
||||
<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
|
||||
|
||||
To compile a release version use::
|
||||
|
||||
|
||||
nim c -d:release greetings.nim
|
||||
|
||||
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
|
||||
# A comment.
|
||||
|
||||
|
||||
var myVariable: int ## a documentation comment
|
||||
|
||||
|
||||
@@ -200,7 +200,7 @@ constant declaration at compile time:
|
||||
|
||||
.. code-block:: nim
|
||||
const x = "abc" # the constant x contains the string "abc"
|
||||
|
||||
|
||||
Indentation can be used after the ``const`` keyword to list a whole section of
|
||||
constants:
|
||||
|
||||
@@ -214,7 +214,7 @@ constants:
|
||||
|
||||
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
|
||||
value cannot change:
|
||||
|
||||
@@ -228,7 +228,7 @@ and put it into a data section":
|
||||
|
||||
.. code-block::
|
||||
const input = readLine(stdin) # Error: constant expression expected
|
||||
|
||||
|
||||
.. code-block::
|
||||
let input = readLine(stdin) # works
|
||||
|
||||
@@ -310,8 +310,8 @@ the compiler that for every other value nothing should be done:
|
||||
else: discard
|
||||
|
||||
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
|
||||
disappears. Note that it is impossible to cover all possible string values:
|
||||
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:
|
||||
that is why string cases always need an ``else`` branch.
|
||||
|
||||
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
|
||||
---------------
|
||||
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:
|
||||
|
||||
.. code-block:: nim
|
||||
@@ -461,7 +461,7 @@ differences:
|
||||
* The statements within a branch do not open a new scope.
|
||||
* The compiler checks the semantics and produces code *only* for the statements
|
||||
that belong to the first condition that evaluates to ``true``.
|
||||
|
||||
|
||||
The ``when`` statement is useful for writing platform specific code, similar to
|
||||
the ``#ifdef`` construct in the C programming language.
|
||||
|
||||
@@ -486,14 +486,14 @@ to be indented, but single simple statements do not:
|
||||
.. code-block:: nim
|
||||
# no indentation needed for single assignment statement:
|
||||
if x: x = false
|
||||
|
||||
|
||||
# indentation needed for nested if statement:
|
||||
if x:
|
||||
if y:
|
||||
y = false
|
||||
else:
|
||||
y = true
|
||||
|
||||
|
||||
# indentation needed, because two statements follow the condition:
|
||||
if x:
|
||||
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,
|
||||
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:
|
||||
|
||||
.. code-block:: nim
|
||||
@@ -560,45 +560,45 @@ Some terminology: in the example ``question`` is called a (formal) *parameter*,
|
||||
|
||||
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
|
||||
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
|
||||
the exit.
|
||||
|
||||
.. code-block:: nim
|
||||
proc sumTillNegative(x: varargs[int]): int =
|
||||
proc sumTillNegative(x: varargs[int]): int =
|
||||
for i in x:
|
||||
if i < 0:
|
||||
return
|
||||
result = result + i
|
||||
|
||||
result = result + i
|
||||
|
||||
echo sumTillNegative() # echos 0
|
||||
echo sumTillNegative(3, 4, 5) # echos 12
|
||||
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
|
||||
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
|
||||
be ``nil`` at the start of the procedure, and thus may require manual
|
||||
initialisation.
|
||||
|
||||
|
||||
|
||||
Parameters
|
||||
----------
|
||||
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
|
||||
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
|
||||
proc printSeq(s: seq, nprinted: int = -1) =
|
||||
var nprinted = if nprinted == -1: s.len else: min(nprinted, s.len)
|
||||
for i in 0 .. <nprinted:
|
||||
echo s[i]
|
||||
|
||||
|
||||
If the procedure needs to modify the argument for the
|
||||
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
|
||||
been declared with the ``discardable`` pragma:
|
||||
been declared with the ``discardable`` pragma:
|
||||
|
||||
.. code-block:: nim
|
||||
proc p(x, y: int): int {.discardable.} =
|
||||
proc p(x, y: int): int {.discardable.} =
|
||||
return x + y
|
||||
|
||||
|
||||
p(3, 4) # now valid
|
||||
|
||||
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
|
||||
--------
|
||||
Nim has these integer types built-in:
|
||||
Nim has these integer types built-in:
|
||||
``int int8 int16 int32 int64 uint uint8 uint16 uint32 uint64``.
|
||||
|
||||
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
|
||||
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
|
||||
|
||||
@@ -1370,12 +1370,12 @@ integer.
|
||||
var building: tuple[street: string, number: int]
|
||||
building = ("Rue del Percebe", 13)
|
||||
echo(building.street)
|
||||
|
||||
|
||||
# The following line does not compile, they are different tuples!
|
||||
#person = building
|
||||
# --> Error: type mismatch: got (tuple[street: string, number: int])
|
||||
# but expected 'Person'
|
||||
|
||||
|
||||
# The following works because the field names and types are the same.
|
||||
var teacher: tuple[name: string, age: int] = ("Mark", 42)
|
||||
person = teacher
|
||||
@@ -1450,13 +1450,13 @@ operators perform implicit dereferencing operations for reference types:
|
||||
|
||||
type
|
||||
Node = ref NodeObj
|
||||
NodeObj = object
|
||||
le, ri: PNode
|
||||
NodeObj = object
|
||||
le, ri: Node
|
||||
data: int
|
||||
var
|
||||
n: Node
|
||||
new(n)
|
||||
n.data = 9
|
||||
n.data = 9
|
||||
# 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.
|
||||
@@ -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
|
||||
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
|
||||
imported by a third one:
|
||||
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
|
||||
imported by a third one:
|
||||
|
||||
.. code-block:: nim
|
||||
# Module A
|
||||
|
||||
76
koch.nim
76
koch.nim
@@ -81,7 +81,7 @@ proc exec(cmd: string, errorcode: int = QuitFailure) =
|
||||
echo(cmd)
|
||||
if execShellCmd(cmd) != 0: quit("FAILURE", errorcode)
|
||||
|
||||
proc tryExec(cmd: string): bool =
|
||||
proc tryExec(cmd: string): bool =
|
||||
echo(cmd)
|
||||
result = execShellCmd(cmd) == 0
|
||||
|
||||
@@ -96,7 +96,7 @@ proc copyExe(source, dest: string) =
|
||||
const
|
||||
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" %
|
||||
[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" %
|
||||
["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) =
|
||||
exec("$# cc $# $#" % [findNim(), args, toolname])
|
||||
copyFile(dest="bin"/ splitFile(toolname).name.exe, source=toolname.exe)
|
||||
@@ -113,14 +119,14 @@ proc buildTool(toolname, args: string) =
|
||||
proc nsis(args: string) =
|
||||
# make sure we have generated the niminst executables:
|
||||
buildTool("tools/niminst/niminst", args)
|
||||
buildTool("tools/nimgrep", args)
|
||||
# produce 'nimrod_debug.exe':
|
||||
exec "nim c compiler" / "nim.nim"
|
||||
copyExe("compiler/nim".exe, "bin/nim_debug".exe)
|
||||
#buildTool("tools/nimgrep", args)
|
||||
# produce 'nim_debug.exe':
|
||||
#exec "nim c compiler" / "nim.nim"
|
||||
#copyExe("compiler/nim".exe, "bin/nim_debug".exe)
|
||||
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" %
|
||||
[findNim(), compileNimInst, VersionAsString])
|
||||
exec("sh ./install.sh $#" % args)
|
||||
@@ -139,12 +145,10 @@ proc pdf(args="") =
|
||||
|
||||
# -------------- boot ---------------------------------------------------------
|
||||
|
||||
proc findStartNim: string =
|
||||
proc findStartNim: string =
|
||||
# we try several things before giving up:
|
||||
# * bin/nim
|
||||
# * $PATH/nim
|
||||
# * bin/nimrod
|
||||
# * $PATH/nimrod
|
||||
# If these fail, we try to build nim with the "build.(sh|bat)" script.
|
||||
var nim = "nim".exe
|
||||
result = "bin" / nim
|
||||
@@ -152,34 +156,27 @@ proc findStartNim: string =
|
||||
for dir in split(getEnv("PATH"), PathSep):
|
||||
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):
|
||||
const buildScript = "build.sh"
|
||||
if existsFile(buildScript):
|
||||
if existsFile(buildScript):
|
||||
if tryExec("./" & buildScript): return "bin" / nim
|
||||
else:
|
||||
const buildScript = "build.bat"
|
||||
if existsFile(buildScript):
|
||||
if existsFile(buildScript):
|
||||
if tryExec(buildScript): return "bin" / nim
|
||||
|
||||
echo("Found no nim compiler and every attempt to build one failed!")
|
||||
quit("FAILURE")
|
||||
|
||||
proc thVersion(i: int): string =
|
||||
proc thVersion(i: int): string =
|
||||
result = ("compiler" / "nim" & $i).exe
|
||||
|
||||
|
||||
proc boot(args: string) =
|
||||
var output = "compiler" / "nim".exe
|
||||
var finalDest = "bin" / "nim".exe
|
||||
# default to use the 'c' command:
|
||||
let bootOptions = if args.len == 0 or args.startsWith("-"): "c" else: ""
|
||||
|
||||
|
||||
copyExe(findStartNim(), 0.thVersion)
|
||||
for i in 0..2:
|
||||
echo "iteration: ", i+1
|
||||
@@ -204,7 +201,7 @@ const
|
||||
".bzrignore", "nim", "nim.exe", "koch", "koch.exe", ".gitignore"
|
||||
]
|
||||
|
||||
proc cleanAux(dir: string) =
|
||||
proc cleanAux(dir: string) =
|
||||
for kind, path in walkDir(dir):
|
||||
case kind
|
||||
of pcFile:
|
||||
@@ -215,25 +212,25 @@ proc cleanAux(dir: string) =
|
||||
removeFile(path)
|
||||
of pcDir:
|
||||
case splitPath(path).tail
|
||||
of "nimcache":
|
||||
of "nimcache":
|
||||
echo "removing dir: ", path
|
||||
removeDir(path)
|
||||
of "dist", ".git", "icons": discard
|
||||
else: cleanAux(path)
|
||||
else: discard
|
||||
|
||||
proc removePattern(pattern: string) =
|
||||
for f in walkFiles(pattern):
|
||||
proc removePattern(pattern: string) =
|
||||
for f in walkFiles(pattern):
|
||||
echo "removing: ", f
|
||||
removeFile(f)
|
||||
|
||||
proc clean(args: string) =
|
||||
proc clean(args: string) =
|
||||
if existsFile("koch.dat"): removeFile("koch.dat")
|
||||
removePattern("web/*.html")
|
||||
removePattern("doc/*.html")
|
||||
cleanAux(getCurrentDir())
|
||||
for kind, path in walkDir(getCurrentDir() / "build"):
|
||||
if kind == pcDir:
|
||||
if kind == pcDir:
|
||||
echo "removing dir: ", path
|
||||
removeDir(path)
|
||||
|
||||
@@ -276,7 +273,7 @@ when defined(withUpdate):
|
||||
"Local branch must be ahead of it. Exiting...")
|
||||
else:
|
||||
quit("An error has occurred.")
|
||||
|
||||
|
||||
else:
|
||||
echo("No repo or executable found!")
|
||||
when defined(haveZipLib):
|
||||
@@ -293,7 +290,7 @@ when defined(withUpdate):
|
||||
quit("Error reading archive.")
|
||||
else:
|
||||
quit("No failback available. Exiting...")
|
||||
|
||||
|
||||
echo("Starting update...")
|
||||
boot(args)
|
||||
echo("Update complete!")
|
||||
@@ -317,12 +314,14 @@ proc winRelease() =
|
||||
#buildTool("tools/niminst/niminst", " -d:release")
|
||||
buildTool("tools/nimgrep", " -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
|
||||
|
||||
# -------------- tests --------------------------------------------------------
|
||||
@@ -346,8 +345,8 @@ proc temp(args: string) =
|
||||
copyExe(output, finalDest)
|
||||
if args.len > 0: exec(finalDest & " " & args)
|
||||
|
||||
proc showHelp() =
|
||||
quit(HelpText % [VersionAsString & spaces(44-len(VersionAsString)),
|
||||
proc showHelp() =
|
||||
quit(HelpText % [VersionAsString & spaces(44-len(VersionAsString)),
|
||||
CompileDate, CompileTime], QuitSuccess)
|
||||
|
||||
var op = initOptParser()
|
||||
@@ -366,6 +365,7 @@ of cmdArgument:
|
||||
of "pdf": pdf()
|
||||
of "csource", "csources": csource(op.cmdLineRest)
|
||||
of "zip": zip(op.cmdLineRest)
|
||||
of "targz": targz(op.cmdLineRest)
|
||||
of "nsis": nsis(op.cmdLineRest)
|
||||
of "install": install(op.cmdLineRest)
|
||||
of "test", "tests": tests(op.cmdLineRest)
|
||||
|
||||
@@ -88,7 +88,9 @@ type
|
||||
ntyBigNum,
|
||||
ntyConst, ntyMutable, ntyVarargs,
|
||||
ntyIter,
|
||||
ntyError
|
||||
ntyError,
|
||||
ntyBuiltinTypeClass, ntyConcept, ntyConceptInst, ntyComposite,
|
||||
ntyAnd, ntyOr, ntyNot
|
||||
|
||||
TNimTypeKinds* {.deprecated.} = set[NimTypeKind]
|
||||
NimSymKind* = enum
|
||||
@@ -162,6 +164,7 @@ proc kind*(n: NimNode): NimNodeKind {.magic: "NKind", noSideEffect.}
|
||||
## returns the `kind` of the node `n`.
|
||||
|
||||
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 symbol*(n: NimNode): NimSym {.magic: "NSymbol", 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.
|
||||
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,
|
||||
args: varargs[NimNode]): NimNode {.compileTime.} =
|
||||
## 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.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.} =
|
||||
## produces a new float literal node.
|
||||
result = newNimNode(nnkFloatLit)
|
||||
|
||||
@@ -205,7 +205,7 @@ proc setEncoding*(connection: TDbConn, encoding: string): bool {.
|
||||
exec(connection, sql"PRAGMA encoding = ?", [encoding])
|
||||
result = connection.getValue(sql"PRAGMA encoding") == encoding
|
||||
|
||||
when isMainModule:
|
||||
when not defined(testing) and isMainModule:
|
||||
var db = open("db.sql", "", "", "")
|
||||
exec(db, sql"create table tbl1(one varchar(10), two smallint)", [])
|
||||
exec(db, sql"insert into tbl1 values('hello!',10)", [])
|
||||
|
||||
@@ -499,7 +499,7 @@ template withEvents*(surf: PSurface, event: expr, actions: stmt): stmt {.
|
||||
if sdl.init(sdl.INIT_VIDEO) < 0: raiseEGraphics()
|
||||
if sdl_ttf.init() < 0: raiseEGraphics()
|
||||
|
||||
when isMainModule:
|
||||
when not defined(testing) and isMainModule:
|
||||
var surf = newScreenSurface(800, 600)
|
||||
|
||||
surf.fillSurface(colWhite)
|
||||
|
||||
@@ -135,7 +135,7 @@ else:
|
||||
var cur, old: Termios
|
||||
discard fd.tcgetattr(cur.addr)
|
||||
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)
|
||||
stdout.write prompt
|
||||
result = stdin.readLine(password)
|
||||
|
||||
@@ -7,8 +7,11 @@
|
||||
# distribution, for details about the copyright.
|
||||
#
|
||||
|
||||
## Regular expression support for Nim. Consider using the pegs module
|
||||
## instead.
|
||||
## Regular expression support for Nim. Consider using the pegs module instead.
|
||||
##
|
||||
## There is an alternative regular expressions library with a more unified API:
|
||||
## `nre <https://github.com/flaviut/nre>`_. It may be added to the standard
|
||||
## library in the future, instead of `re`.
|
||||
##
|
||||
## **Note:** The 're' proc defaults to the **extended regular expression
|
||||
## syntax** which lets you use whitespace freely to make your regexes readable.
|
||||
@@ -41,11 +44,11 @@ type
|
||||
reExtended = 3, ## ignore whitespace and ``#`` comments
|
||||
reStudy = 4 ## study the expression (may be omitted if the
|
||||
## expression will be used only once)
|
||||
|
||||
RegexDesc = object
|
||||
h: PPcre
|
||||
e: ptr TExtra
|
||||
|
||||
|
||||
RegexDesc = object
|
||||
h: ptr Pcre
|
||||
e: ptr ExtraData
|
||||
|
||||
Regex* = ref RegexDesc ## a compiled regular expression
|
||||
|
||||
RegexError* = object of ValueError
|
||||
@@ -60,7 +63,7 @@ proc raiseInvalidRegex(msg: string) {.noinline, noreturn.} =
|
||||
e.msg = msg
|
||||
raise e
|
||||
|
||||
proc rawCompile(pattern: string, flags: cint): PPcre =
|
||||
proc rawCompile(pattern: string, flags: cint): ptr Pcre =
|
||||
var
|
||||
msg: cstring
|
||||
offset: cint
|
||||
@@ -84,7 +87,7 @@ proc re*(s: string, flags = {reExtended, reStudy}): Regex =
|
||||
result.h = rawCompile(s, cast[cint](flags - {reStudy}))
|
||||
if reStudy in flags:
|
||||
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)
|
||||
|
||||
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,
|
||||
start = 0): tuple[first, last: int] =
|
||||
## returns the starting position of `pattern` in `s`. If it does not
|
||||
## match, ``(-1,0)`` is returned.
|
||||
## returns the starting position and end position of ``pattern`` in ``s``.
|
||||
## If it does not match, ``(-1,0)`` is returned.
|
||||
var
|
||||
rtarray = initRtArray[cint](3)
|
||||
rawMatches = rtarray.getRawData
|
||||
@@ -413,22 +416,28 @@ proc escapeRe*(s: string): string =
|
||||
result.add(toHex(ord(c), 2))
|
||||
|
||||
const ## common regular expressions
|
||||
reIdentifier* = r"\b[a-zA-Z_]+[a-zA-Z_0-9]*\b" ## describes an identifier
|
||||
reNatural* = r"\b\d+\b" ## describes a natural number
|
||||
reInteger* = r"\b[-+]?\d+\b" ## describes an integer
|
||||
reHex* = r"\b0[xX][0-9a-fA-F]+\b" ## describes a hexadecimal number
|
||||
reBinary* = r"\b0[bB][01]+\b" ## describes a binary number (example: 0b11101)
|
||||
reOctal* = r"\b0[oO][0-7]+\b" ## describes an octal number (example: 0o777)
|
||||
reFloat* = r"\b[-+]?[0-9]*\.?[0-9]+([eE][-+]?[0-9]+)?\b"
|
||||
reIdentifier* {.deprecated.} = r"\b[a-zA-Z_]+[a-zA-Z_0-9]*\b"
|
||||
## describes an identifier
|
||||
reNatural* {.deprecated.} = r"\b\d+\b"
|
||||
## describes a natural number
|
||||
reInteger* {.deprecated.} = r"\b[-+]?\d+\b"
|
||||
## describes an integer
|
||||
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
|
||||
reEmail* = r"\b[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-Z]{2}|com|org|" &
|
||||
r"net|gov|mil|biz|info|mobi|name|aero|jobs|museum)\b"
|
||||
reEmail* {.deprecated.} = r"\b[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-Z]{2}|com|org|net|gov|mil|biz|" &
|
||||
r"info|mobi|name|aero|jobs|museum)\b"
|
||||
## describes a common email address
|
||||
reURL* = r"\b(http(s)?|ftp|gopher|telnet|file|notes|ms\-help):" &
|
||||
r"((//)|(\\\\))+[\w\d:#@%/;$()~_?\+\-\=\\\.\&]*\b"
|
||||
reURL* {.deprecated.} = r"\b(http(s)?|ftp|gopher|telnet|file|notes|ms-help)" &
|
||||
r":((//)|(\\\\))+[\w\d:#@%/;$()~_?\+\-\=\\\.\&]*\b"
|
||||
## describes an URL
|
||||
|
||||
when isMainModule:
|
||||
|
||||
@@ -82,7 +82,7 @@ proc close*(sock: TSecureSocket) =
|
||||
ERR_print_errors_fp(stderr)
|
||||
raiseOSError(osLastError())
|
||||
|
||||
when isMainModule:
|
||||
when not defined(testing) and isMainModule:
|
||||
var s: TSecureSocket
|
||||
echo connect(s, "smtp.gmail.com", 465)
|
||||
|
||||
|
||||
@@ -9,8 +9,8 @@
|
||||
|
||||
## This module implements a zip archive creator/reader/modifier.
|
||||
|
||||
import
|
||||
streams, libzip, times, os
|
||||
import
|
||||
streams, libzip, times, os, strutils
|
||||
|
||||
type
|
||||
TZipArchive* = object of RootObj ## represents a zip archive
|
||||
@@ -18,14 +18,14 @@ type
|
||||
w: PZip
|
||||
|
||||
|
||||
proc zipError(z: var TZipArchive) =
|
||||
proc zipError(z: var TZipArchive) =
|
||||
var e: ref IOError
|
||||
new(e)
|
||||
e.msg = $zip_strerror(z.w)
|
||||
raise e
|
||||
|
||||
|
||||
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.
|
||||
var err, flags: int32
|
||||
case mode
|
||||
@@ -41,21 +41,21 @@ proc open*(z: var TZipArchive, filename: string, mode: FileMode = fmRead): bool
|
||||
proc close*(z: var TZipArchive) =
|
||||
## Closes a zip file.
|
||||
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
|
||||
## 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
|
||||
## to create the ``"path/path2"`` subdirectories - it will be done
|
||||
## automatically by ``addFile``.
|
||||
assert(z.mode != fmRead)
|
||||
## to create the ``"path/path2"`` subdirectories - it will be done
|
||||
## automatically by ``addFile``.
|
||||
assert(z.mode != fmRead)
|
||||
discard zip_add_dir(z.w, dir)
|
||||
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`
|
||||
## may contain a path that will be created.
|
||||
assert(z.mode != fmRead)
|
||||
## may contain a path that will be created.
|
||||
assert(z.mode != fmRead)
|
||||
if not fileExists(src):
|
||||
raise newException(IOError, "File '" & src & "' does not exist")
|
||||
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)
|
||||
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
|
||||
## the name of the destination.
|
||||
addFile(z, file, file)
|
||||
|
||||
proc mySourceCallback(state, data: pointer, len: int,
|
||||
cmd: TZipSourceCmd): int {.cdecl.} =
|
||||
|
||||
proc mySourceCallback(state, data: pointer, len: int,
|
||||
cmd: TZipSourceCmd): int {.cdecl.} =
|
||||
var src = cast[Stream](state)
|
||||
case cmd
|
||||
of ZIP_SOURCE_OPEN:
|
||||
of ZIP_SOURCE_OPEN:
|
||||
if src.setPositionImpl != nil: setPosition(src, 0) # reset
|
||||
of ZIP_SOURCE_READ:
|
||||
result = readData(src, data, len)
|
||||
of ZIP_SOURCE_CLOSE: close(src)
|
||||
of ZIP_SOURCE_STAT:
|
||||
of ZIP_SOURCE_STAT:
|
||||
var stat = cast[PZipStat](data)
|
||||
zip_stat_init(stat)
|
||||
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)
|
||||
of constZIP_SOURCE_FREE: GC_unref(src)
|
||||
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`
|
||||
## may contain a path. The file's content is read from the `src` stream.
|
||||
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:
|
||||
zip_source_free(zipsrc)
|
||||
zipError(z)
|
||||
|
||||
|
||||
# -------------- zip file stream ---------------------------------------------
|
||||
|
||||
type
|
||||
TZipFileStream = object of StreamObj
|
||||
f: PZipFile
|
||||
atEnd: bool
|
||||
|
||||
PZipFileStream* =
|
||||
ref TZipFileStream ## a reader stream of a file within a zip archive
|
||||
PZipFileStream* =
|
||||
ref TZipFileStream ## a reader stream of a file within a zip archive
|
||||
|
||||
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)
|
||||
if result == 0:
|
||||
PZipFileStream(s).atEnd = true
|
||||
|
||||
proc newZipFileStream(f: PZipFile): PZipFileStream =
|
||||
proc newZipFileStream(f: PZipFile): PZipFileStream =
|
||||
new(result)
|
||||
result.f = f
|
||||
result.atEnd = false
|
||||
result.closeImpl = fsClose
|
||||
result.readDataImpl = fsReadData
|
||||
result.atEndImpl = fsAtEnd
|
||||
# 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`
|
||||
## 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.
|
||||
var x = zip_fopen(z.w, filename, 0'i32)
|
||||
if x != nil: result = newZipFileStream(x)
|
||||
|
||||
iterator walkFiles*(z: var TZipArchive): string =
|
||||
## walks over all files in the archive `z` and returns the filename
|
||||
|
||||
iterator walkFiles*(z: var TZipArchive): string =
|
||||
## walks over all files in the archive `z` and returns the filename
|
||||
## (including the path).
|
||||
var i = 0'i32
|
||||
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) =
|
||||
## 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)
|
||||
file.close()
|
||||
|
||||
proc extractAll*(z: var TZipArchive, dest: string) =
|
||||
## extracts all files from archive `z` to the destination directory.
|
||||
for file in walkFiles(z):
|
||||
extractFile(z, file, dest / extractFilename(file))
|
||||
if file.endsWith("/"):
|
||||
createDir(dest / file)
|
||||
else:
|
||||
extractFile(z, file, dest / file)
|
||||
|
||||
when not defined(testing) and isMainModule:
|
||||
var zip: TZipArchive
|
||||
if not zip.open("nim-0.11.0.zip"):
|
||||
raise newException(IOError, "opening zip failed")
|
||||
zip.extractAll("test")
|
||||
|
||||
@@ -152,10 +152,12 @@ type
|
||||
DocumentObj {.importc.} = object of NodeObj
|
||||
alinkColor*: cstring
|
||||
bgColor*: cstring
|
||||
body*: Element
|
||||
charset*: cstring
|
||||
cookie*: cstring
|
||||
defaultCharset*: cstring
|
||||
fgColor*: cstring
|
||||
head*: Element
|
||||
lastModified*: cstring
|
||||
linkColor*: cstring
|
||||
referrer*: cstring
|
||||
|
||||
@@ -343,15 +343,15 @@ struct TFrame {
|
||||
};
|
||||
|
||||
#define nimfr(proc, file) \
|
||||
TFrame F; \
|
||||
F.procname = proc; F.filename = file; F.line = 0; F.len = 0; nimFrame(&F);
|
||||
TFrame FR; \
|
||||
FR.procname = proc; FR.filename = file; FR.line = 0; FR.len = 0; nimFrame(&FR);
|
||||
|
||||
#define nimfrs(proc, file, slots, length) \
|
||||
struct {TFrame* prev;NCSTRING procname;NI line;NCSTRING filename; NI len; TVarSlot s[slots];} F; \
|
||||
F.procname = proc; F.filename = file; F.line = 0; F.len = length; nimFrame((TFrame*)&F);
|
||||
struct {TFrame* prev;NCSTRING procname;NI line;NCSTRING filename; NI len; TVarSlot s[slots];} FR; \
|
||||
FR.procname = proc; FR.filename = file; FR.line = 0; FR.len = length; nimFrame((TFrame*)&FR);
|
||||
|
||||
#define nimln(n, file) \
|
||||
F.line = n; F.filename = file;
|
||||
FR.line = n; FR.filename = file;
|
||||
|
||||
#define NIM_POSIX_INIT __attribute__((constructor))
|
||||
|
||||
|
||||
@@ -564,7 +564,7 @@ proc fixupEmbeddedRef(n, a, b: PRstNode) =
|
||||
|
||||
proc parsePostfix(p: var TRstParser, n: PRstNode): PRstNode =
|
||||
result = n
|
||||
if isInlineMarkupEnd(p, "_"):
|
||||
if isInlineMarkupEnd(p, "_") or isInlineMarkupEnd(p, "__"):
|
||||
inc(p.idx)
|
||||
if p.tok[p.idx-2].symbol == "`" and p.tok[p.idx-3].symbol == ">":
|
||||
var a = newRstNode(rnInner)
|
||||
|
||||
@@ -34,14 +34,14 @@ type
|
||||
TOutputTarget* = enum ## which document type to generate
|
||||
outHtml, # output is HTML
|
||||
outLatex # output is Latex
|
||||
|
||||
TTocEntry = object
|
||||
|
||||
TTocEntry = object
|
||||
n*: PRstNode
|
||||
refname*, header*: string
|
||||
|
||||
TMetaEnum* = enum
|
||||
TMetaEnum* = enum
|
||||
metaNone, metaTitle, metaSubtitle, metaAuthor, metaVersion
|
||||
|
||||
|
||||
TRstGenerator* = object of RootObj
|
||||
target*: TOutputTarget
|
||||
config*: StringTableRef
|
||||
@@ -60,7 +60,7 @@ type
|
||||
seenIndexTerms: Table[string, int] ## \
|
||||
## Keeps count of same text index terms to generate different identifiers
|
||||
## for hyperlinks. See renderIndexTerm proc for details.
|
||||
|
||||
|
||||
PDoc = var TRstGenerator ## Alias to type less.
|
||||
|
||||
CodeBlockParams = object ## Stores code block params.
|
||||
@@ -136,7 +136,7 @@ proc initRstGenerator*(g: var TRstGenerator, target: TOutputTarget,
|
||||
g.currentSection = "Module " & fileParts.name
|
||||
g.seenIndexTerms = initTable[string, int]()
|
||||
g.msgHandler = msgHandler
|
||||
|
||||
|
||||
let s = config["split.item.toc"]
|
||||
if s != "": g.splitAfter = parseInt(s)
|
||||
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()
|
||||
## <#setIndexTerm>`_ proc. If the index is empty the file won't be created.
|
||||
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
|
||||
of '&': add(dest, "&")
|
||||
of '<': add(dest, "<")
|
||||
of '>': add(dest, ">")
|
||||
of '\"': add(dest, """)
|
||||
else: add(dest, c)
|
||||
|
||||
proc addRtfChar(dest: var string, c: char) =
|
||||
|
||||
proc addRtfChar(dest: var string, c: char) =
|
||||
case c
|
||||
of '{': add(dest, "\\{")
|
||||
of '}': add(dest, "\\}")
|
||||
of '\\': add(dest, "\\\\")
|
||||
else: add(dest, c)
|
||||
|
||||
proc addTexChar(dest: var string, c: char) =
|
||||
|
||||
proc addTexChar(dest: var string, c: char) =
|
||||
case c
|
||||
of '_': add(dest, "\\_")
|
||||
of '{': add(dest, "\\symbol{123}")
|
||||
@@ -183,54 +183,54 @@ proc addTexChar(dest: var string, c: char) =
|
||||
|
||||
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
|
||||
of outHtml: addXmlChar(dest, c)
|
||||
of outLatex: addTexChar(dest, c)
|
||||
|
||||
proc nextSplitPoint*(s: string, start: int): int =
|
||||
|
||||
proc nextSplitPoint*(s: string, start: int): int =
|
||||
result = start
|
||||
while result < len(s) + 0:
|
||||
while result < len(s) + 0:
|
||||
case s[result]
|
||||
of '_': return
|
||||
of 'a'..'z':
|
||||
if result + 1 < len(s) + 0:
|
||||
if s[result + 1] in {'A'..'Z'}: return
|
||||
of '_': return
|
||||
of 'a'..'z':
|
||||
if result + 1 < len(s) + 0:
|
||||
if s[result + 1] in {'A'..'Z'}: return
|
||||
else: discard
|
||||
inc(result)
|
||||
dec(result) # last valid index
|
||||
|
||||
proc esc*(target: TOutputTarget, s: string, splitAfter = -1): string =
|
||||
|
||||
proc esc*(target: TOutputTarget, s: string, splitAfter = -1): string =
|
||||
result = ""
|
||||
if splitAfter >= 0:
|
||||
if splitAfter >= 0:
|
||||
var partLen = 0
|
||||
var j = 0
|
||||
while j < len(s):
|
||||
while j < len(s):
|
||||
var k = nextSplitPoint(s, j)
|
||||
if (splitter != " ") or (partLen + k - j + 1 > splitAfter):
|
||||
if (splitter != " ") or (partLen + k - j + 1 > splitAfter):
|
||||
partLen = 0
|
||||
add(result, splitter)
|
||||
for i in countup(j, k): escChar(target, result, s[i])
|
||||
inc(partLen, k - j + 1)
|
||||
j = k + 1
|
||||
else:
|
||||
else:
|
||||
for i in countup(0, len(s) - 1): escChar(target, result, s[i])
|
||||
|
||||
|
||||
proc disp(target: TOutputTarget, xml, tex: string): string =
|
||||
if target != outLatex: result = xml
|
||||
if target != outLatex: result = xml
|
||||
else: result = tex
|
||||
|
||||
proc dispF(target: TOutputTarget, xml, tex: string,
|
||||
args: varargs[string]): string =
|
||||
if target != outLatex: result = xml % args
|
||||
|
||||
proc dispF(target: TOutputTarget, xml, tex: string,
|
||||
args: varargs[string]): string =
|
||||
if target != outLatex: result = xml % 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]) =
|
||||
if target != outLatex: addf(dest, xml, args)
|
||||
else: addf(dest, tex, args)
|
||||
|
||||
|
||||
proc `or`(x, y: string): string {.inline.} =
|
||||
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)
|
||||
## 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)
|
||||
|
||||
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 = ""
|
||||
renderAux(d, n, term)
|
||||
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])
|
||||
|
||||
type
|
||||
@@ -656,7 +656,7 @@ proc mergeIndexes*(dir: string): string =
|
||||
result.add("<h2>API symbols</h2>\n")
|
||||
result.add(generateSymbolIndex(symbols))
|
||||
|
||||
|
||||
|
||||
# ----------------------------------------------------------------------------
|
||||
|
||||
proc stripTOCHTML(s: string): string =
|
||||
@@ -677,7 +677,7 @@ proc stripTOCHTML(s: string): string =
|
||||
result.delete(first, last)
|
||||
first = result.find('<', first)
|
||||
|
||||
proc renderHeadline(d: PDoc, n: PRstNode, result: var string) =
|
||||
proc renderHeadline(d: PDoc, n: PRstNode, result: var string) =
|
||||
var tmp = ""
|
||||
for i in countup(0, len(n) - 1): renderRstToOut(d, n.sons[i], 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",
|
||||
[$n.level, d.tocPart[length].refname, tmp, $chr(n.level - 1 + ord('A'))])
|
||||
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", [
|
||||
$n.level, refname, tmp,
|
||||
$n.level, refname, tmp,
|
||||
$chr(n.level - 1 + ord('A'))])
|
||||
|
||||
# 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,
|
||||
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:
|
||||
for i in countup(0, len(n)-1):
|
||||
renderRstToOut(d, n.sons[i], d.meta[metaTitle])
|
||||
@@ -723,14 +723,14 @@ proc renderOverline(d: PDoc, n: PRstNode, result: var string) =
|
||||
var tmp = ""
|
||||
for i in countup(0, len(n) - 1): renderRstToOut(d, n.sons[i], 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,
|
||||
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,
|
||||
"<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])
|
||||
|
||||
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 s = getFieldValue(n, "scale")
|
||||
if s.valid: dispA(d.target, options, " scale=\"$1\"", " scale=$1", [strip(s)])
|
||||
|
||||
|
||||
s = getFieldValue(n, "height")
|
||||
if s.valid: dispA(d.target, options, " height=\"$1\"", " height=$1", [strip(s)])
|
||||
|
||||
|
||||
s = getFieldValue(n, "width")
|
||||
if s.valid: dispA(d.target, options, " width=\"$1\"", " width=$1", [strip(s)])
|
||||
|
||||
|
||||
s = getFieldValue(n, "alt")
|
||||
if s.valid: dispA(d.target, options, " alt=\"$1\"", "", [strip(s)])
|
||||
|
||||
|
||||
s = getFieldValue(n, "align")
|
||||
if s.valid: dispA(d.target, options, " align=\"$1\"", "", [strip(s)])
|
||||
|
||||
|
||||
if options.len > 0: options = dispF(d.target, "$1", "[$1]", [options])
|
||||
|
||||
let arg = getArgument(n)
|
||||
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])
|
||||
if len(n) >= 3: renderRstToOut(d, n.sons[2], result)
|
||||
|
||||
|
||||
proc renderSmiley(d: PDoc, n: PRstNode, result: var string) =
|
||||
dispA(d.target, result,
|
||||
"""<img src="$1" width="15"
|
||||
"""<img src="$1" width="15"
|
||||
height="17" hspace="2" vspace="2" class="smiley" />""",
|
||||
"\\includegraphics{$1}", [d.config["doc.smiley_format"] % n.text])
|
||||
|
||||
|
||||
proc parseCodeBlockField(d: PDoc, n: PRstNode, params: var CodeBlockParams) =
|
||||
## Parses useful fields which can appear before a code block.
|
||||
##
|
||||
@@ -880,11 +880,11 @@ proc renderCodeBlock(d: PDoc, n: PRstNode, result: var string) =
|
||||
else:
|
||||
var g: TGeneralTokenizer
|
||||
initGeneralTokenizer(g, m.text)
|
||||
while true:
|
||||
while true:
|
||||
getNextToken(g, params.lang)
|
||||
case g.kind
|
||||
of gtEof: break
|
||||
of gtNone, gtWhitespace:
|
||||
of gtEof: break
|
||||
of gtNone, gtWhitespace:
|
||||
add(result, substr(m.text, g.start, g.length + g.start - 1))
|
||||
else:
|
||||
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)
|
||||
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 = ""
|
||||
renderRstToOut(d, n.sons[2], tmp)
|
||||
var arg = strip(getArgument(n))
|
||||
if arg == "":
|
||||
if arg == "":
|
||||
dispA(d.target, result, "<div>$1</div>", "$1", [tmp])
|
||||
else:
|
||||
dispA(d.target, result, "<div class=\"$1\">$2</div>", "$2", [arg, tmp])
|
||||
|
||||
proc texColumns(n: PRstNode): string =
|
||||
|
||||
proc texColumns(n: PRstNode): string =
|
||||
result = ""
|
||||
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
|
||||
if d.target == outLatex:
|
||||
if d.target == outLatex:
|
||||
var fieldname = addNodes(n.sons[0])
|
||||
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:
|
||||
if d.meta[metaAuthor].len == 0:
|
||||
d.meta[metaAuthor] = fieldval
|
||||
b = true
|
||||
elif cmpIgnoreStyle(fieldname, "version") == 0:
|
||||
elif cmpIgnoreStyle(fieldname, "version") == 0:
|
||||
if d.meta[metaVersion].len == 0:
|
||||
d.meta[metaVersion] = fieldval
|
||||
b = true
|
||||
if not b:
|
||||
renderAux(d, n, "<tr>$1</tr>\n", "$1", result)
|
||||
|
||||
|
||||
proc renderRstToOut(d: PDoc, n: PRstNode, result: var string) =
|
||||
if n == nil: return
|
||||
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 rnFieldList:
|
||||
var tmp = ""
|
||||
for i in countup(0, len(n) - 1):
|
||||
for i in countup(0, len(n) - 1):
|
||||
renderRstToOut(d, n.sons[i], tmp)
|
||||
if tmp.len != 0:
|
||||
if tmp.len != 0:
|
||||
dispA(d.target, result,
|
||||
"<table class=\"docinfo\" frame=\"void\" rules=\"none\">" &
|
||||
"<col class=\"docinfo-name\" />" &
|
||||
"<col class=\"docinfo-content\" />" &
|
||||
"<col class=\"docinfo-content\" />" &
|
||||
"<tbody valign=\"top\">$1" &
|
||||
"</tbody></table>",
|
||||
"\\begin{description}$1\\end{description}\n",
|
||||
"</tbody></table>",
|
||||
"\\begin{description}$1\\end{description}\n",
|
||||
[tmp])
|
||||
of rnField: renderField(d, n, result)
|
||||
of rnFieldName:
|
||||
of rnFieldName:
|
||||
renderAux(d, n, "<th class=\"docinfo-name\">$1:</th>",
|
||||
"\\item[$1:]", result)
|
||||
of rnFieldBody:
|
||||
of rnFieldBody:
|
||||
renderAux(d, n, "<td>$1</td>", " $1\n", result)
|
||||
of rnIndex:
|
||||
of rnIndex:
|
||||
renderRstToOut(d, n.sons[2], result)
|
||||
of rnOptionList:
|
||||
renderAux(d, n, "<table frame=\"void\">$1</table>",
|
||||
of rnOptionList:
|
||||
renderAux(d, n, "<table frame=\"void\">$1</table>",
|
||||
"\\begin{description}\n$1\\end{description}\n", result)
|
||||
of rnOptionListItem:
|
||||
of rnOptionListItem:
|
||||
renderAux(d, n, "<tr>$1</tr>\n", "$1", result)
|
||||
of rnOptionGroup:
|
||||
of rnOptionGroup:
|
||||
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)
|
||||
of rnOption, rnOptionString, rnOptionArgument:
|
||||
of rnOption, rnOptionString, rnOptionArgument:
|
||||
doAssert false, "renderRstToOut"
|
||||
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)
|
||||
of rnQuotedLiteralBlock:
|
||||
of rnQuotedLiteralBlock:
|
||||
doAssert false, "renderRstToOut"
|
||||
of rnLineBlock:
|
||||
of rnLineBlock:
|
||||
renderAux(d, n, "<p>$1</p>", "$1\n\n", result)
|
||||
of rnLineBlockItem:
|
||||
of rnLineBlockItem:
|
||||
renderAux(d, n, "$1<br />", "$1\\\\\n", result)
|
||||
of rnBlockQuote:
|
||||
renderAux(d, n, "<blockquote><p>$1</p></blockquote>\n",
|
||||
of rnBlockQuote:
|
||||
renderAux(d, n, "<blockquote><p>$1</p></blockquote>\n",
|
||||
"\\begin{quote}$1\\end{quote}\n", result)
|
||||
of rnTable, rnGridTable:
|
||||
renderAux(d, n,
|
||||
"<table border=\"1\" class=\"docutils\">$1</table>",
|
||||
of rnTable, rnGridTable:
|
||||
renderAux(d, n,
|
||||
"<table border=\"1\" class=\"docutils\">$1</table>",
|
||||
"\\begin{table}\\begin{rsttab}{" &
|
||||
texColumns(n) & "|}\n\\hline\n$1\\end{rsttab}\\end{table}", result)
|
||||
of rnTableRow:
|
||||
of rnTableRow:
|
||||
if len(n) >= 1:
|
||||
if d.target == outLatex:
|
||||
#var tmp = ""
|
||||
@@ -1007,25 +1007,25 @@ proc renderRstToOut(d: PDoc, n: PRstNode, result: var string) =
|
||||
result.add("<tr>")
|
||||
renderAux(d, n, result)
|
||||
result.add("</tr>\n")
|
||||
of rnTableDataCell:
|
||||
of rnTableDataCell:
|
||||
renderAux(d, n, "<td>$1</td>", "$1", result)
|
||||
of rnTableHeaderCell:
|
||||
of rnTableHeaderCell:
|
||||
renderAux(d, n, "<th>$1</th>", "\\textbf{$1}", result)
|
||||
of rnLabel:
|
||||
of rnLabel:
|
||||
doAssert false, "renderRstToOut" # used for footnotes and other
|
||||
of rnFootnote:
|
||||
of rnFootnote:
|
||||
doAssert false, "renderRstToOut" # a footnote
|
||||
of rnCitation:
|
||||
of rnCitation:
|
||||
doAssert false, "renderRstToOut" # similar to footnote
|
||||
of rnRef:
|
||||
of rnRef:
|
||||
var tmp = ""
|
||||
renderAux(d, n, tmp)
|
||||
dispA(d.target, result,
|
||||
"<a class=\"reference external\" href=\"#$2\">$1</a>",
|
||||
"$1\\ref{$2}", [tmp, rstnodeToRefname(n)])
|
||||
of rnStandaloneHyperlink:
|
||||
renderAux(d, n,
|
||||
"<a class=\"reference external\" href=\"$1\">$1</a>",
|
||||
of rnStandaloneHyperlink:
|
||||
renderAux(d, n,
|
||||
"<a class=\"reference external\" href=\"$1\">$1</a>",
|
||||
"\\href{$1}{$1}", result)
|
||||
of rnHyperlink:
|
||||
var tmp0 = ""
|
||||
@@ -1042,11 +1042,11 @@ proc renderRstToOut(d: PDoc, n: PRstNode, result: var string) =
|
||||
of rnRawLatex:
|
||||
if d.target == outLatex:
|
||||
result.add addNodes(lastSon(n))
|
||||
|
||||
|
||||
of rnImage, rnFigure: renderImage(d, n, result)
|
||||
of rnCodeBlock: renderCodeBlock(d, n, result)
|
||||
of rnContainer: renderContainer(d, n, result)
|
||||
of rnSubstitutionReferences, rnSubstitutionDef:
|
||||
of rnSubstitutionReferences, rnSubstitutionDef:
|
||||
renderAux(d, n, "|$1|", "|$1|", result)
|
||||
of rnDirective:
|
||||
renderAux(d, n, "", "", result)
|
||||
@@ -1063,15 +1063,15 @@ proc renderRstToOut(d: PDoc, n: PRstNode, result: var string) =
|
||||
of rnStrongEmphasis:
|
||||
renderAux(d, n, "<strong>$1</strong>", "\\textbf{$1}", result)
|
||||
of rnTripleEmphasis:
|
||||
renderAux(d, n, "<strong><em>$1</em></strong>",
|
||||
renderAux(d, n, "<strong><em>$1</em></strong>",
|
||||
"\\textbf{emph{$1}}", result)
|
||||
of rnInterpretedText:
|
||||
renderAux(d, n, "<cite>$1</cite>", "\\emph{$1}", result)
|
||||
of rnIdx:
|
||||
renderIndexTerm(d, n, result)
|
||||
of rnInlineLiteral:
|
||||
renderAux(d, n,
|
||||
"<tt class=\"docutils literal\"><span class=\"pre\">$1</span></tt>",
|
||||
of rnInlineLiteral:
|
||||
renderAux(d, n,
|
||||
"<tt class=\"docutils literal\"><span class=\"pre\">$1</span></tt>",
|
||||
"\\texttt{$1}", result)
|
||||
of rnSmiley: renderSmiley(d, n, result)
|
||||
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 =
|
||||
for i in countup(0, high(varnames)):
|
||||
if cmpIgnoreStyle(varnames[i], id) == 0:
|
||||
proc getVarIdx(varnames: openArray[string], id: string): int =
|
||||
for i in countup(0, high(varnames)):
|
||||
if cmpIgnoreStyle(varnames[i], id) == 0:
|
||||
return i
|
||||
result = -1
|
||||
|
||||
proc formatNamedVars*(frmt: string, varnames: openArray[string],
|
||||
varvalues: openArray[string]): string =
|
||||
proc formatNamedVars*(frmt: string, varnames: openArray[string],
|
||||
varvalues: openArray[string]): string =
|
||||
var i = 0
|
||||
var L = len(frmt)
|
||||
result = ""
|
||||
var num = 0
|
||||
while i < L:
|
||||
if frmt[i] == '$':
|
||||
while i < L:
|
||||
if frmt[i] == '$':
|
||||
inc(i) # skip '$'
|
||||
case frmt[i]
|
||||
of '#':
|
||||
of '#':
|
||||
add(result, varvalues[num])
|
||||
inc(num)
|
||||
inc(i)
|
||||
of '$':
|
||||
of '$':
|
||||
add(result, "$")
|
||||
inc(i)
|
||||
of '0'..'9':
|
||||
of '0'..'9':
|
||||
var j = 0
|
||||
while true:
|
||||
while true:
|
||||
j = (j * 10) + ord(frmt[i]) - ord('0')
|
||||
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:
|
||||
raise newException(ValueError, "invalid index: " & $j)
|
||||
num = j
|
||||
add(result, varvalues[j - 1])
|
||||
of 'A'..'Z', 'a'..'z', '\x80'..'\xFF':
|
||||
of 'A'..'Z', 'a'..'z', '\x80'..'\xFF':
|
||||
var id = ""
|
||||
while true:
|
||||
while true:
|
||||
add(id, frmt[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)
|
||||
if idx >= 0:
|
||||
if idx >= 0:
|
||||
add(result, varvalues[idx])
|
||||
else:
|
||||
raise newException(ValueError, "unknown substitution var: " & id)
|
||||
of '{':
|
||||
of '{':
|
||||
var id = ""
|
||||
inc(i)
|
||||
while frmt[i] != '}':
|
||||
if frmt[i] == '\0':
|
||||
while frmt[i] != '}':
|
||||
if frmt[i] == '\0':
|
||||
raise newException(ValueError, "'}' expected")
|
||||
add(id, frmt[i])
|
||||
inc(i)
|
||||
@@ -1138,12 +1138,12 @@ proc formatNamedVars*(frmt: string, varnames: openArray[string],
|
||||
# search for the variable:
|
||||
var idx = getVarIdx(varnames, id)
|
||||
if idx >= 0: add(result, varvalues[idx])
|
||||
else:
|
||||
else:
|
||||
raise newException(ValueError, "unknown substitution var: " & id)
|
||||
else:
|
||||
raise newException(ValueError, "unknown substitution: $" & $frmt[i])
|
||||
var start = i
|
||||
while i < L:
|
||||
while i < L:
|
||||
if frmt[i] != '$': inc(i)
|
||||
else: break
|
||||
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
|
||||
## ``rstToHtml`` to generate the bare minimum HTML.
|
||||
result = newStringTable(modeStyleInsensitive)
|
||||
|
||||
|
||||
template setConfigVar(key, val: expr) =
|
||||
result[key] = val
|
||||
|
||||
|
||||
# If you need to modify these values, it might be worth updating the template
|
||||
# file in config/nimdoc.cfg.
|
||||
setConfigVar("split.item.toc", "20")
|
||||
@@ -1214,7 +1214,7 @@ $content
|
||||
|
||||
# ---------- forum ---------------------------------------------------------
|
||||
|
||||
proc rstToHtml*(s: string, options: TRstParseOptions,
|
||||
proc rstToHtml*(s: string, options: TRstParseOptions,
|
||||
config: StringTableRef): string =
|
||||
## 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
|
||||
## ``initRstGenerator`` and related procs.
|
||||
|
||||
proc myFindFile(filename: string): string =
|
||||
proc myFindFile(filename: string): string =
|
||||
# we don't find any files in online mode:
|
||||
result = ""
|
||||
|
||||
const filen = "input"
|
||||
var d: TRstGenerator
|
||||
initRstGenerator(d, outHtml, config, filen, options, myFindFile,
|
||||
initRstGenerator(d, outHtml, config, filen, options, myFindFile,
|
||||
rst.defaultMsgHandler)
|
||||
var dummyHasToc = false
|
||||
var rst = rstParse(s, filen, 0, 1, dummyHasToc, options)
|
||||
@@ -1251,5 +1251,6 @@ proc rstToHtml*(s: string, options: TRstParseOptions,
|
||||
|
||||
|
||||
when isMainModule:
|
||||
echo rstToHtml("*Hello* **world**!", {},
|
||||
newStringTable(modeStyleInsensitive))
|
||||
assert rstToHtml("*Hello* **world**!", {},
|
||||
newStringTable(modeStyleInsensitive)) ==
|
||||
"<em>Hello</em> <strong>world</strong>!"
|
||||
|
||||
@@ -10,28 +10,25 @@
|
||||
{.deadCodeElim: on.}
|
||||
import posix
|
||||
|
||||
type
|
||||
type
|
||||
Speed* = cuint
|
||||
Tcflag* = cuint
|
||||
|
||||
const
|
||||
const
|
||||
NCCS* = 32
|
||||
|
||||
type
|
||||
Termios* = object {.importc: "struct termios", header: "<termios.h>", final, pure.}
|
||||
iflag*: Tcflag # input mode flags
|
||||
oflag*: Tcflag # output mode flags
|
||||
cflag*: Tcflag # control mode flags
|
||||
lflag*: Tcflag # local mode flags
|
||||
line*: cuchar # line discipline
|
||||
cc*: array[NCCS, cuchar] # control characters
|
||||
ispeed*: Speed # input speed
|
||||
ospeed*: Speed # output speed
|
||||
|
||||
type
|
||||
Termios* {.importc: "struct termios", header: "<termios.h>".} = object
|
||||
c_iflag*: Tcflag # input mode flags
|
||||
c_oflag*: Tcflag # output mode flags
|
||||
c_cflag*: Tcflag # control mode flags
|
||||
c_lflag*: Tcflag # local mode flags
|
||||
c_line*: cuchar # line discipline
|
||||
c_cc*: array[NCCS, cuchar] # control characters
|
||||
|
||||
# cc characters
|
||||
# cc characters
|
||||
|
||||
const
|
||||
const
|
||||
VINTR* = 0
|
||||
VQUIT* = 1
|
||||
VERASE* = 2
|
||||
@@ -50,9 +47,9 @@ const
|
||||
VLNEXT* = 15
|
||||
VEOL2* = 16
|
||||
|
||||
# iflag bits
|
||||
# iflag bits
|
||||
|
||||
const
|
||||
const
|
||||
IGNBRK* = 1
|
||||
BRKINT* = 2
|
||||
IGNPAR* = 4
|
||||
@@ -69,9 +66,9 @@ const
|
||||
IMAXBEL* = 20000
|
||||
IUTF8* = 40000
|
||||
|
||||
# oflag bits
|
||||
# oflag bits
|
||||
|
||||
const
|
||||
const
|
||||
OPOST* = 1
|
||||
OLCUC* = 2
|
||||
ONLCR* = 4
|
||||
@@ -104,9 +101,9 @@ const
|
||||
VT1* = 40000
|
||||
XTABS* = 14000
|
||||
|
||||
# cflag bit meaning
|
||||
# cflag bit meaning
|
||||
|
||||
const
|
||||
const
|
||||
CBAUD* = 10017
|
||||
B0* = 0
|
||||
B50* = 1
|
||||
@@ -158,9 +155,9 @@ const
|
||||
CMSPAR* = 0o010000000000
|
||||
CRTSCTS* = 0o020000000000
|
||||
|
||||
# lflag bits
|
||||
# lflag bits
|
||||
|
||||
const
|
||||
const
|
||||
ISIG* = 1
|
||||
ICANON* = 2
|
||||
XCASE* = 4
|
||||
@@ -178,87 +175,87 @@ const
|
||||
IEXTEN* = 0o000000100000
|
||||
EXTPROC* = 0o000000200000
|
||||
|
||||
# tcflow() and TCXONC use these
|
||||
# tcflow() and TCXONC use these
|
||||
|
||||
const
|
||||
const
|
||||
TCOOFF* = 0
|
||||
TCOON* = 1
|
||||
TCIOFF* = 2
|
||||
TCION* = 3
|
||||
|
||||
# tcflush() and TCFLSH use these
|
||||
# tcflush() and TCFLSH use these
|
||||
|
||||
const
|
||||
const
|
||||
TCIFLUSH* = 0
|
||||
TCOFLUSH* = 1
|
||||
TCIOFLUSH* = 2
|
||||
|
||||
# tcsetattr uses these
|
||||
# tcsetattr uses these
|
||||
|
||||
const
|
||||
const
|
||||
TCSANOW* = 0
|
||||
TCSADRAIN* = 1
|
||||
TCSAFLUSH* = 2
|
||||
|
||||
# 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
|
||||
|
||||
# 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>".}
|
||||
# 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>".}
|
||||
# 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 {.
|
||||
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 {.
|
||||
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 {.
|
||||
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 {.
|
||||
importc: "tcgetattr", header: "<termios.h>".}
|
||||
# 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 {.
|
||||
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>".}
|
||||
# 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>".}
|
||||
# Wait for pending output to be written on FD.
|
||||
#
|
||||
# This function is a cancellation point and therefore not marked with
|
||||
# .
|
||||
# .
|
||||
|
||||
proc tcDrain*(fd: cint): cint {.importc: "tcdrain", header: "<termios.h>".}
|
||||
# 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>".}
|
||||
# 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>".}
|
||||
# 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>".}
|
||||
|
||||
@@ -221,7 +221,7 @@ proc spawn*[TIn](p: var TActorPool[TIn, void], input: TIn,
|
||||
setupTask()
|
||||
schedule()
|
||||
|
||||
when isMainModule:
|
||||
when not defined(testing) and isMainModule:
|
||||
var
|
||||
a: TActorPool[int, void]
|
||||
createActorPool(a)
|
||||
|
||||
@@ -40,8 +40,8 @@ proc reverse*[T](a: var openArray[T]) =
|
||||
proc reversed*[T](a: openArray[T], first, last: Natural): seq[T] =
|
||||
## returns the reverse of the array `a[first..last]`.
|
||||
result = newSeq[T](last - first + 1)
|
||||
var x = first
|
||||
var y = last
|
||||
var x = first.int
|
||||
var y = last.int
|
||||
while x <= last:
|
||||
result[x] = a[y]
|
||||
dec(y)
|
||||
|
||||
@@ -634,6 +634,93 @@ when defined(windows) or defined(nimdoc):
|
||||
# free ``ol``.
|
||||
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,
|
||||
flags = {SocketFlag.SafeDisconn}): Future[void] =
|
||||
## Sends ``data`` to ``socket``. The returned future will complete once all
|
||||
@@ -983,6 +1070,30 @@ else:
|
||||
addRead(socket, cb)
|
||||
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,
|
||||
flags = {SocketFlag.SafeDisconn}): Future[void] =
|
||||
var retFuture = newFuture[void]("send")
|
||||
|
||||
@@ -300,7 +300,7 @@ proc newAsyncFtpClient*(address: string, port = Port(21),
|
||||
result.dsockConnected = false
|
||||
result.csock = newAsyncSocket()
|
||||
|
||||
when isMainModule:
|
||||
when not defined(testing) and isMainModule:
|
||||
var ftp = newAsyncFtpClient("example.com", user = "test", pass = "test")
|
||||
proc main(ftp: AsyncFtpClient) {.async.} =
|
||||
await ftp.connect()
|
||||
|
||||
@@ -23,8 +23,7 @@
|
||||
## proc cb(req: Request) {.async.} =
|
||||
## await req.respond(Http200, "Hello World")
|
||||
##
|
||||
## asyncCheck server.serve(Port(8080), cb)
|
||||
## runForever()
|
||||
## waitFor server.serve(Port(8080), cb)
|
||||
|
||||
import strtabs, asyncnet, asyncdispatch, parseutils, uri, strutils
|
||||
type
|
||||
@@ -109,22 +108,19 @@ proc sendHeaders*(req: Request, headers: StringTableRef): Future[void] =
|
||||
addHeaders(msg, headers)
|
||||
return req.client.send(msg)
|
||||
|
||||
proc respond*(req: Request, code: HttpCode,
|
||||
content: string, headers = newStringTable()) {.async.} =
|
||||
proc respond*(req: Request, code: HttpCode, content: string,
|
||||
headers: StringTableRef = nil): Future[void] =
|
||||
## Responds to the request with the specified ``HttpCode``, headers and
|
||||
## content.
|
||||
##
|
||||
## This procedure will **not** close the client socket.
|
||||
var customHeaders = headers
|
||||
customHeaders["Content-Length"] = $content.len
|
||||
var msg = "HTTP/1.1 " & $code & "\c\L"
|
||||
msg.addHeaders(customHeaders)
|
||||
await req.client.send(msg & "\c\L" & content)
|
||||
|
||||
proc newRequest(): Request =
|
||||
result.headers = newStringTable(modeCaseInsensitive)
|
||||
result.hostname = ""
|
||||
result.body = ""
|
||||
if headers != nil:
|
||||
msg.addHeaders(headers)
|
||||
msg.add("Content-Length: " & $content.len & "\c\L\c\L")
|
||||
msg.add(content)
|
||||
result = req.client.send(msg)
|
||||
|
||||
proc parseHeader(line: string): tuple[key, value: string] =
|
||||
var i = 0
|
||||
@@ -149,59 +145,65 @@ proc sendStatus(client: AsyncSocket, status: string): Future[void] =
|
||||
proc processClient(client: AsyncSocket, address: string,
|
||||
callback: proc (request: Request):
|
||||
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:
|
||||
# GET /path HTTP/1.1
|
||||
# Header: val
|
||||
# \n
|
||||
var request = newRequest()
|
||||
request.hostname = address
|
||||
request.headers.clear(modeCaseInsensitive)
|
||||
request.hostname.shallowCopy(address)
|
||||
assert client != nil
|
||||
request.client = client
|
||||
|
||||
# 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 == "":
|
||||
client.close()
|
||||
return
|
||||
let lineParts = line.split(' ')
|
||||
if lineParts.len != 3:
|
||||
await request.respond(Http400, "Invalid request. Got: " & line)
|
||||
continue
|
||||
|
||||
let reqMethod = lineParts[0]
|
||||
let path = lineParts[1]
|
||||
let protocol = lineParts[2]
|
||||
var i = 0
|
||||
for linePart in line.split(' '):
|
||||
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
|
||||
var i = 0
|
||||
while true:
|
||||
i = 0
|
||||
let headerLine = await client.recvLine()
|
||||
if headerLine == "":
|
||||
line.setLen(0)
|
||||
await client.recvLineInto(addr line)
|
||||
|
||||
if line == "":
|
||||
client.close(); return
|
||||
if headerLine == "\c\L": break
|
||||
# TODO: Compiler crash
|
||||
#let (key, value) = parseHeader(headerLine)
|
||||
let kv = parseHeader(headerLine)
|
||||
request.headers[kv.key] = kv.value
|
||||
if line == "\c\L": break
|
||||
let (key, value) = parseHeader(line)
|
||||
request.headers[key] = value
|
||||
|
||||
request.reqMethod = reqMethod
|
||||
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":
|
||||
if request.reqMethod == "post":
|
||||
# Check for Expect header
|
||||
if request.headers.hasKey("Expect"):
|
||||
if request.headers["Expect"].toLower == "100-continue":
|
||||
await client.sendStatus("100 Continue")
|
||||
else:
|
||||
await client.sendStatus("417 Expectation Failed")
|
||||
|
||||
|
||||
# Read the body
|
||||
# - Check for Content-length header
|
||||
if request.headers.hasKey("Content-Length"):
|
||||
@@ -215,11 +217,11 @@ proc processClient(client: AsyncSocket, address: string,
|
||||
await request.respond(Http400, "Bad Request. No Content-Length.")
|
||||
continue
|
||||
|
||||
case reqMethod.normalize
|
||||
case request.reqMethod
|
||||
of "get", "post", "head", "put", "delete", "trace", "options", "connect", "patch":
|
||||
await callback(request)
|
||||
else:
|
||||
await request.respond(Http400, "Invalid request method. Got: " & reqMethod)
|
||||
await request.respond(Http400, "Invalid request method. Got: " & request.reqMethod)
|
||||
|
||||
# Persistent connections
|
||||
if (request.protocol == HttpVer11 and
|
||||
@@ -247,7 +249,7 @@ proc serve*(server: AsyncHttpServer, port: Port,
|
||||
server.socket.setSockOpt(OptReuseAddr, true)
|
||||
server.socket.bindAddr(port, address)
|
||||
server.socket.listen()
|
||||
|
||||
|
||||
while true:
|
||||
# TODO: Causes compiler crash.
|
||||
#var (address, client) = await server.socket.acceptAddr()
|
||||
@@ -260,7 +262,7 @@ proc close*(server: AsyncHttpServer) =
|
||||
## Terminates the async http server instance.
|
||||
server.socket.close()
|
||||
|
||||
when isMainModule:
|
||||
when not defined(testing) and isMainModule:
|
||||
proc main =
|
||||
var server = newAsyncHttpServer()
|
||||
proc cb(req: Request) {.async.} =
|
||||
|
||||
@@ -660,7 +660,7 @@ proc len*(disp: Dispatcher): int =
|
||||
## Retrieves the amount of delegates in ``disp``.
|
||||
return disp.delegates.len
|
||||
|
||||
when isMainModule:
|
||||
when not defined(testing) and isMainModule:
|
||||
|
||||
proc testConnect(s: AsyncSocket, no: int) =
|
||||
echo("Connected! " & $no)
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
##
|
||||
## Chat server
|
||||
## ^^^^^^^^^^^
|
||||
##
|
||||
##
|
||||
## The following example demonstrates a simple chat server.
|
||||
##
|
||||
## .. code-block::nim
|
||||
@@ -182,26 +182,30 @@ proc connect*(socket: AsyncSocket, address: string, port: Port,
|
||||
sslSetConnectState(socket.sslHandle)
|
||||
sslLoop(socket, flags, sslDoHandshake(socket.sslHandle))
|
||||
|
||||
proc readInto(buf: cstring, size: int, socket: AsyncSocket,
|
||||
flags: set[SocketFlag]): Future[int] {.async.} =
|
||||
template readInto(buf: cstring, size: int, socket: AsyncSocket,
|
||||
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:
|
||||
when defined(ssl):
|
||||
# SSL mode.
|
||||
sslLoop(socket, flags,
|
||||
sslRead(socket.sslHandle, buf, size.cint))
|
||||
result = opResult
|
||||
res = opResult
|
||||
else:
|
||||
var data = await recv(socket.fd.TAsyncFD, size, flags)
|
||||
if data.len != 0:
|
||||
copyMem(buf, addr data[0], data.len)
|
||||
var recvIntoFut = recvInto(socket.fd.TAsyncFD, buf, size, flags)
|
||||
yield recvIntoFut
|
||||
# Not in SSL mode.
|
||||
result = data.len
|
||||
res = recvIntoFut.read()
|
||||
res
|
||||
|
||||
proc readIntoBuf(socket: AsyncSocket,
|
||||
flags: set[SocketFlag]): Future[int] {.async.} =
|
||||
result = await readInto(addr socket.buffer[0], BufferSize, socket, flags)
|
||||
template readIntoBuf(socket: AsyncSocket,
|
||||
flags: set[SocketFlag]): int =
|
||||
var size = readInto(addr socket.buffer[0], BufferSize, socket, flags)
|
||||
socket.currPos = 0
|
||||
socket.bufLen = result
|
||||
socket.bufLen = size
|
||||
size
|
||||
|
||||
proc recv*(socket: AsyncSocket, size: int,
|
||||
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 ``""``.
|
||||
if socket.isBuffered:
|
||||
result = newString(size)
|
||||
shallow(result)
|
||||
let originalBufPos = socket.currPos
|
||||
|
||||
if socket.bufLen == 0:
|
||||
let res = await socket.readIntoBuf(flags - {SocketFlag.Peek})
|
||||
let res = socket.readIntoBuf(flags - {SocketFlag.Peek})
|
||||
if res == 0:
|
||||
result.setLen(0)
|
||||
return
|
||||
@@ -236,7 +241,7 @@ proc recv*(socket: AsyncSocket, size: int,
|
||||
if SocketFlag.Peek in flags:
|
||||
# We don't want to get another buffer if we're peeking.
|
||||
break
|
||||
let res = await socket.readIntoBuf(flags - {SocketFlag.Peek})
|
||||
let res = socket.readIntoBuf(flags - {SocketFlag.Peek})
|
||||
if res == 0:
|
||||
break
|
||||
|
||||
@@ -251,7 +256,7 @@ proc recv*(socket: AsyncSocket, size: int,
|
||||
result.setLen(read)
|
||||
else:
|
||||
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)
|
||||
|
||||
proc send*(socket: AsyncSocket, data: string,
|
||||
@@ -302,15 +307,14 @@ proc accept*(socket: AsyncSocket,
|
||||
retFut.complete(future.read.client)
|
||||
return retFut
|
||||
|
||||
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.
|
||||
proc recvLineInto*(socket: AsyncSocket, resString: ptr string,
|
||||
flags = {SocketFlag.SafeDisconn}) {.async.} =
|
||||
## Reads a line of data from ``socket`` into ``resString``.
|
||||
##
|
||||
## 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``
|
||||
@@ -318,27 +322,32 @@ proc recvLine*(socket: AsyncSocket,
|
||||
## 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")
|
||||
##
|
||||
## **Warning**: ``recvLineInto`` on unbuffered sockets assumes that the
|
||||
## protocol uses ``\r\L`` to delimit a new line.
|
||||
##
|
||||
## **Warning**: ``recvLineInto`` currently uses a raw pointer to a string for
|
||||
## performance reasons. This will likely change soon to use FutureVars.
|
||||
assert SocketFlag.Peek notin flags ## TODO:
|
||||
result = newFuture[void]("asyncnet.recvLineInto")
|
||||
|
||||
template addNLIfEmpty(): stmt =
|
||||
if resString[].len == 0:
|
||||
resString[].add("\c\L")
|
||||
|
||||
if socket.isBuffered:
|
||||
result = ""
|
||||
if socket.bufLen == 0:
|
||||
let res = await socket.readIntoBuf(flags)
|
||||
let res = socket.readIntoBuf(flags)
|
||||
if res == 0:
|
||||
return
|
||||
|
||||
var lastR = false
|
||||
while true:
|
||||
if socket.currPos >= socket.bufLen:
|
||||
let res = await socket.readIntoBuf(flags)
|
||||
let res = socket.readIntoBuf(flags)
|
||||
if res == 0:
|
||||
result = ""
|
||||
break
|
||||
resString[].setLen(0)
|
||||
return
|
||||
|
||||
case socket.buffer[socket.currPos]
|
||||
of '\r':
|
||||
@@ -353,24 +362,53 @@ proc recvLine*(socket: AsyncSocket,
|
||||
socket.currPos.inc()
|
||||
return
|
||||
else:
|
||||
result.add socket.buffer[socket.currPos]
|
||||
resString[].add socket.buffer[socket.currPos]
|
||||
socket.currPos.inc()
|
||||
else:
|
||||
result = ""
|
||||
var c = ""
|
||||
while true:
|
||||
c = await recv(socket, 1, flags)
|
||||
let recvFut = recv(socket, 1, flags)
|
||||
c = recvFut.read()
|
||||
if c.len == 0:
|
||||
return ""
|
||||
resString[].setLen(0)
|
||||
return
|
||||
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"
|
||||
addNLIfEmpty()
|
||||
return
|
||||
elif c == "\L":
|
||||
addNLIfEmpty()
|
||||
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].} =
|
||||
## Marks ``socket`` as accepting connections.
|
||||
@@ -458,7 +496,7 @@ proc isClosed*(socket: AsyncSocket): bool =
|
||||
## Determines whether the socket has been closed.
|
||||
return socket.closed
|
||||
|
||||
when isMainModule:
|
||||
when not defined(testing) and isMainModule:
|
||||
type
|
||||
TestCases = enum
|
||||
HighClient, LowClient, LowServer
|
||||
@@ -500,11 +538,11 @@ when isMainModule:
|
||||
proc (future: Future[void]) =
|
||||
echo("Send")
|
||||
client.close()
|
||||
|
||||
|
||||
var f = accept(sock)
|
||||
f.callback = onAccept
|
||||
|
||||
|
||||
var f = accept(sock)
|
||||
f.callback = onAccept
|
||||
runForever()
|
||||
|
||||
|
||||
|
||||
@@ -16,33 +16,33 @@ import times
|
||||
## Vectors are implemented as direction vectors, ie. when transformed with a matrix
|
||||
## the translation part of matrix is ignored. The coordinate system used is
|
||||
## right handed, because its compatible with 2d coordinate system (rotation around
|
||||
## zaxis equals 2d rotation).
|
||||
## zaxis equals 2d rotation).
|
||||
## Operators `+` , `-` , `*` , `/` , `+=` , `-=` , `*=` and `/=` are implemented
|
||||
## for vectors and scalars.
|
||||
##
|
||||
##
|
||||
## Quick start example:
|
||||
##
|
||||
##
|
||||
## # 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)
|
||||
##
|
||||
##
|
||||
## # 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)
|
||||
##
|
||||
##
|
||||
##
|
||||
##
|
||||
## pt &= m # transforms pt in place
|
||||
##
|
||||
##
|
||||
## 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
|
||||
|
||||
|
||||
|
||||
type
|
||||
type
|
||||
TMatrix3d* =object
|
||||
## Implements a row major 3d matrix, which means
|
||||
## transformations are applied the order they are concatenated.
|
||||
@@ -53,12 +53,12 @@ type
|
||||
## [ tx ty tz tw ]
|
||||
ax*,ay*,az*,aw*, bx*,by*,bz*,bw*, cx*,cy*,cz*,cw*, tx*,ty*,tz*,tw*:float
|
||||
TPoint3d* = object
|
||||
## Implements a non-homegeneous 2d point stored as
|
||||
## Implements a non-homegeneous 2d point stored as
|
||||
## an `x` , `y` and `z` coordinate.
|
||||
x*,y*,z*:float
|
||||
TVector3d* = object
|
||||
## Implements a 3d **direction vector** stored as
|
||||
## an `x` , `y` and `z` coordinate. Direction vector means,
|
||||
TVector3d* = object
|
||||
## Implements a 3d **direction vector** stored as
|
||||
## an `x` , `y` and `z` coordinate. Direction vector means,
|
||||
## that when transforming a vector with a matrix, the translational
|
||||
## part of the matrix is ignored.
|
||||
x*,y*,z*:float
|
||||
@@ -67,7 +67,7 @@ type
|
||||
|
||||
# Some forward declarations
|
||||
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.
|
||||
## `bx` , `by` , `bz` is the local y 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`)
|
||||
proc point3d*(x,y,z:float):TPoint3d {.noInit,inline.}
|
||||
## 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.
|
||||
## If `v` has zero length (and thus no angle), it is left unmodified and false is
|
||||
## returned, otherwise true is returned.
|
||||
@@ -85,7 +85,7 @@ proc tryNormalize*(v:var TVector3d):bool
|
||||
|
||||
let
|
||||
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,0.0,1.0,0.0,
|
||||
0.0,0.0,0.0,1.0)
|
||||
@@ -114,20 +114,20 @@ proc safeArccos(v:float):float=
|
||||
## due to rounding issues
|
||||
return arccos(clamp(v,-1.0,1.0))
|
||||
|
||||
template makeBinOpVector(s:expr)=
|
||||
template makeBinOpVector(s:expr)=
|
||||
## 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))
|
||||
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))
|
||||
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))
|
||||
|
||||
template makeBinOpAssignVector(s:expr)=
|
||||
|
||||
template makeBinOpAssignVector(s:expr)=
|
||||
## 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)
|
||||
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)
|
||||
|
||||
|
||||
@@ -188,20 +188,20 @@ proc scale*(s:float):TMatrix3d {.noInit.} =
|
||||
|
||||
proc scale*(s:float,org:TPoint3d):TMatrix3d {.noInit.} =
|
||||
## 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)
|
||||
|
||||
proc stretch*(sx,sy,sz:float):TMatrix3d {.noInit.} =
|
||||
## Returns new a stretch matrix, which is a
|
||||
## 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)
|
||||
|
||||
|
||||
proc stretch*(sx,sy,sz:float,org:TPoint3d):TMatrix3d {.noInit.} =
|
||||
## Returns a new stretch matrix, which is a
|
||||
## scale matrix with non uniform scale in x,y and z.
|
||||
## `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)
|
||||
|
||||
|
||||
proc move*(dx,dy,dz:float):TMatrix3d {.noInit.} =
|
||||
## Returns a new translation matrix.
|
||||
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
|
||||
uwomc=normax.x*normax.z*omc
|
||||
vwomc=normax.y*normax.z*omc
|
||||
|
||||
|
||||
result.setElements(
|
||||
u2+(1.0-u2)*cs, uvomc+wsi, uwomc-vsi, 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
|
||||
# for how this is computed
|
||||
|
||||
|
||||
var normax=axis
|
||||
if not normax.tryNormalize: #simplifies matrix computation below a lot
|
||||
raise newException(DivByZeroError,"Cannot rotate around zero length axis")
|
||||
|
||||
|
||||
let
|
||||
u=normax.x
|
||||
v=normax.y
|
||||
@@ -272,7 +272,7 @@ proc rotate*(angle:float,org:TPoint3d,axis:TVector3d):TMatrix3d {.noInit.}=
|
||||
uvomc=normax.x*normax.y*omc
|
||||
uwomc=normax.x*normax.z*omc
|
||||
vwomc=normax.y*normax.z*omc
|
||||
|
||||
|
||||
result.setElements(
|
||||
u2+(v2+w2)*cs, uvomc+wsi, uwomc-vsi, 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,
|
||||
s,0,c,0,
|
||||
0,0,0,1)
|
||||
|
||||
|
||||
proc rotateZ*(angle:float):TMatrix3d {.noInit.}=
|
||||
## Creates a matrix that rotates around the z-axis with `angle` radians,
|
||||
## which is also called a 'yaw' matrix.
|
||||
@@ -317,19 +317,19 @@ proc rotateZ*(angle:float):TMatrix3d {.noInit.}=
|
||||
-s,c,0,0,
|
||||
0,0,1,0,
|
||||
0,0,0,1)
|
||||
|
||||
|
||||
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)
|
||||
## 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.
|
||||
|
||||
|
||||
#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
|
||||
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
|
||||
|
||||
|
||||
#subtract squared lengths of axes to check if uniform scaling:
|
||||
let
|
||||
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
|
||||
|
||||
|
||||
|
||||
|
||||
proc mirror*(planeperp:TVector3d):TMatrix3d {.noInit.}=
|
||||
## Creates a matrix that mirrors over the plane that has `planeperp` as normal,
|
||||
## and passes through origo. `planeperp` does not need to be normalized.
|
||||
|
||||
|
||||
# https://en.wikipedia.org/wiki/Transformation_matrix
|
||||
var n=planeperp
|
||||
if not n.tryNormalize:
|
||||
raise newException(DivByZeroError,"Cannot mirror over a plane with a zero length normal")
|
||||
|
||||
|
||||
let
|
||||
a=n.x
|
||||
b=n.y
|
||||
@@ -357,7 +357,7 @@ proc mirror*(planeperp:TVector3d):TMatrix3d {.noInit.}=
|
||||
ab=a*b
|
||||
ac=a*c
|
||||
bc=b*c
|
||||
|
||||
|
||||
result.setElements(
|
||||
1-2*a*a , -2*ab,-2*ac,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
|
||||
if not n.tryNormalize:
|
||||
raise newException(DivByZeroError,"Cannot mirror over a plane with a zero length normal")
|
||||
|
||||
|
||||
let
|
||||
a=n.x
|
||||
b=n.y
|
||||
@@ -390,7 +390,7 @@ proc mirror*(org:TPoint3d,planeperp:TVector3d):TMatrix3d {.noInit.}=
|
||||
tx=org.x
|
||||
ty=org.y
|
||||
tz=org.z
|
||||
|
||||
|
||||
result.setElements(
|
||||
1-2*aa , -2*ab,-2*ac,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=
|
||||
## 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
|
||||
let
|
||||
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
|
||||
## determinant is zero, thus not invertible, a EDivByZero
|
||||
## will be raised.
|
||||
|
||||
|
||||
# this computation comes from optimize(invert(m)) in maxima CAS
|
||||
|
||||
let
|
||||
|
||||
let
|
||||
det=m.determinant
|
||||
O2=m.cy*m.tw-m.cw*m.ty
|
||||
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=
|
||||
## Checks if all elements of `m1`and `m2` is equal within
|
||||
## a given tolerance `tol`.
|
||||
return
|
||||
return
|
||||
abs(m1.ax-m2.ax)<=tol and
|
||||
abs(m1.ay-m2.ay)<=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
|
||||
## tolerance of 1e-6.
|
||||
equals(m1,m2)
|
||||
|
||||
|
||||
proc transpose*(m:TMatrix3d):TMatrix3d {.noInit.}=
|
||||
## 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)
|
||||
|
||||
|
||||
proc getXAxis*(m:TMatrix3d):TVector3d {.noInit.}=
|
||||
## Gets the local x axis of `m`
|
||||
result.x=m.ax
|
||||
@@ -509,26 +509,26 @@ proc getZAxis*(m:TMatrix3d):TVector3d {.noInit.}=
|
||||
result.y=m.cy
|
||||
result.z=m.cz
|
||||
|
||||
|
||||
|
||||
proc `$`*(m:TMatrix3d):string=
|
||||
## String representation of `m`
|
||||
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.cx) & "," & rtos(m.cy) & "," &rtos(m.cz) & "," & rtos(m.cw) &
|
||||
"\n" & rtos(m.tx) & "," & rtos(m.ty) & "," &rtos(m.tz) & "," & rtos(m.tw)
|
||||
|
||||
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.cx) & "," & rtos(m.cy) & "," & rtos(m.cz) & "," & rtos(m.cw) &
|
||||
"\n" & rtos(m.tx) & "," & rtos(m.ty) & "," & rtos(m.tz) & "," & rtos(m.tw)
|
||||
|
||||
proc apply*(m:TMatrix3d, x,y,z:var float, translate=false)=
|
||||
## Applies transformation `m` onto `x` , `y` , `z` , optionally
|
||||
## using the translation part of the matrix.
|
||||
let
|
||||
let
|
||||
oldx=x
|
||||
oldy=y
|
||||
oldz=z
|
||||
|
||||
|
||||
x=m.cx*oldz+m.bx*oldy+m.ax*oldx
|
||||
y=m.cy*oldz+m.by*oldy+m.ay*oldx
|
||||
z=m.cz*oldz+m.bz*oldy+m.az*oldx
|
||||
|
||||
|
||||
if translate:
|
||||
x+=m.tx
|
||||
y+=m.ty
|
||||
@@ -552,13 +552,13 @@ proc `len=`*(v:var TVector3d,newlen:float) {.noInit.} =
|
||||
## an arbitrary vector of the requested length is returned.
|
||||
|
||||
let fac=newlen/v.len
|
||||
|
||||
|
||||
if newlen==0.0:
|
||||
v.x=0.0
|
||||
v.y=0.0
|
||||
v.z=0.0
|
||||
return
|
||||
|
||||
|
||||
if fac==Inf or fac==NegInf:
|
||||
#to short for float accuracy
|
||||
#do as good as possible:
|
||||
@@ -588,7 +588,7 @@ proc `&` *(v:TVector3d,m:TMatrix3d):TVector3d {.noInit.} =
|
||||
## Concatenate vector `v` with a transformation matrix.
|
||||
## Transforming a vector ignores the translational part
|
||||
## of the matrix.
|
||||
|
||||
|
||||
# | AX AY AZ AW |
|
||||
# | X Y Z 1 | * | BX BY BZ BW |
|
||||
# | CX CY CZ CW |
|
||||
@@ -605,12 +605,12 @@ proc `&=` *(v:var TVector3d,m:TMatrix3d) {.noInit.} =
|
||||
## Applies transformation `m` onto `v` in place.
|
||||
## Transforming a vector ignores the translational part
|
||||
## of the matrix.
|
||||
|
||||
|
||||
# | AX AY AZ AW |
|
||||
# | X Y Z 1 | * | BX BY BZ BW |
|
||||
# | CX CY CZ CW |
|
||||
# | 0 0 0 1 |
|
||||
|
||||
|
||||
let
|
||||
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
|
||||
@@ -620,38 +620,38 @@ proc `&=` *(v:var TVector3d,m:TMatrix3d) {.noInit.} =
|
||||
|
||||
proc transformNorm*(v:var TVector3d,m:TMatrix3d)=
|
||||
## Applies a normal direction transformation `m` onto `v` in place.
|
||||
## The resulting vector is *not* normalized. Transforming a vector ignores the
|
||||
## translational part of the matrix. If the matrix is not invertible
|
||||
## The resulting vector is *not* normalized. Transforming a vector ignores the
|
||||
## translational part of the matrix. If the matrix is not invertible
|
||||
## (determinant=0), an EDivByZero will be raised.
|
||||
|
||||
# transforming a normal is done by transforming
|
||||
# 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,
|
||||
# (possibly by hardware) as well as having a consistent API with the 2d version.
|
||||
v&=transpose(inverse(m))
|
||||
|
||||
|
||||
proc transformInv*(v:var TVector3d,m:TMatrix3d)=
|
||||
## Applies the inverse of `m` on vector `v`. Transforming a vector ignores
|
||||
## the translational part of the matrix. Transforming a vector ignores the
|
||||
## Applies the inverse of `m` on vector `v`. Transforming a vector ignores
|
||||
## the translational part of the matrix. Transforming a vector ignores the
|
||||
## translational part of the matrix.
|
||||
## If the matrix is not invertible (determinant=0), an EDivByZero
|
||||
## will be raised.
|
||||
|
||||
|
||||
# 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.
|
||||
v&=m.inverse
|
||||
|
||||
|
||||
proc transformNormInv*(vec:var TVector3d,m:TMatrix3d)=
|
||||
## Applies an inverse normal direction transformation `m` onto `v` in place.
|
||||
## This is faster than creating an inverse
|
||||
## matrix and transformNorm(...) it. Transforming a vector ignores the
|
||||
## This is faster than creating an inverse
|
||||
## matrix and transformNorm(...) it. Transforming a vector ignores the
|
||||
## translational part of the matrix.
|
||||
|
||||
|
||||
# see vector2d:s equivalent for a deeper look how/why this works
|
||||
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.
|
||||
## If `v` has zero length (and thus no angle), it is left unmodified and false is
|
||||
## returned, otherwise true is returned.
|
||||
@@ -663,26 +663,26 @@ proc tryNormalize*(v:var TVector3d):bool=
|
||||
v.x/=mag
|
||||
v.y/=mag
|
||||
v.z/=mag
|
||||
|
||||
|
||||
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.
|
||||
## If `v` has zero length, an EDivByZero will be raised.
|
||||
if not tryNormalize(v):
|
||||
raise newException(DivByZeroError,"Cannot normalize zero length vector")
|
||||
|
||||
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.
|
||||
|
||||
# see PDF document http://inside.mines.edu/~gmurray/ArbitraryAxisRotation/ArbitraryAxisRotation.pdf
|
||||
# for how this is computed
|
||||
|
||||
|
||||
var normax=axis
|
||||
if not normax.tryNormalize:
|
||||
raise newException(DivByZeroError,"Cannot rotate around zero length axis")
|
||||
|
||||
|
||||
let
|
||||
cs=cos(angle)
|
||||
si=sin(angle)
|
||||
@@ -694,11 +694,11 @@ proc rotate*(vec:var TVector3d,angle:float,axis:TVector3d)=
|
||||
y=vec.y
|
||||
z=vec.z
|
||||
uxyzomc=(u*x+v*y+w*z)*omc
|
||||
|
||||
|
||||
vec.x=u*uxyzomc+x*cs+(v*z-w*y)*si
|
||||
vec.y=v*uxyzomc+y*cs+(w*x-u*z)*si
|
||||
vec.z=w*uxyzomc+z*cs+(u*y-v*x)*si
|
||||
|
||||
|
||||
proc scale*(v:var TVector3d,s:float)=
|
||||
## Scales the vector in place with factor `s`
|
||||
v.x*=s
|
||||
@@ -713,12 +713,12 @@ proc stretch*(v:var TVector3d,sx,sy,sz:float)=
|
||||
|
||||
proc mirror*(v:var TVector3d,planeperp:TVector3d)=
|
||||
## 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.
|
||||
|
||||
|
||||
var n=planeperp
|
||||
n.normalize
|
||||
|
||||
|
||||
let
|
||||
x=v.x
|
||||
y=v.y
|
||||
@@ -729,7 +729,7 @@ proc mirror*(v:var TVector3d,planeperp:TVector3d)=
|
||||
ac=a*c
|
||||
ab=a*b
|
||||
bc=b*c
|
||||
|
||||
|
||||
v.x= -2*(ac*z+ab*y+a*a*x)+x
|
||||
v.y= -2*(bc*z+b*b*y+ab*x)+y
|
||||
v.z= -2*(c*c*z+bc*y+ac*x)+z
|
||||
@@ -740,7 +740,7 @@ proc `-` *(v:TVector3d):TVector3d=
|
||||
result.x= -v.x
|
||||
result.y= -v.y
|
||||
result.z= -v.z
|
||||
|
||||
|
||||
# declare templated binary operators
|
||||
makeBinOpVector(`+`)
|
||||
makeBinOpVector(`-`)
|
||||
@@ -752,7 +752,7 @@ makeBinOpAssignVector(`*=`)
|
||||
makeBinOpAssignVector(`/=`)
|
||||
|
||||
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.
|
||||
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=
|
||||
## 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
|
||||
|
||||
|
||||
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
|
||||
equals(v1,v2)
|
||||
|
||||
|
||||
proc angleTo*(v1,v2:TVector3d):float=
|
||||
## Returns the smallest angle between v1 and v2,
|
||||
## which is in range 0-PI
|
||||
@@ -801,7 +801,7 @@ proc arbitraryAxis*(norm:TVector3d):TMatrix3d {.noInit.}=
|
||||
ay=cross(norm,ax)
|
||||
ay.normalize()
|
||||
az=cross(ax,ay)
|
||||
|
||||
|
||||
result.setElements(
|
||||
ax.x,ax.y,ax.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.}=
|
||||
## Computes the bisector between v1 and v2 as a normalized vector.
|
||||
## 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.
|
||||
var
|
||||
vmag1=v1.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
|
||||
if vmag1==0.0:
|
||||
if vmag1==0.0:
|
||||
if vmag2==0: #both are zero length return any normalized vector
|
||||
return XAXIS
|
||||
vmag1=1.0
|
||||
if vmag2==0.0: vmag2=1.0
|
||||
|
||||
if vmag2==0.0: vmag2=1.0
|
||||
|
||||
let
|
||||
x1=v1.x/vmag1
|
||||
y1=v1.y/vmag1
|
||||
@@ -832,14 +832,14 @@ proc bisect*(v1,v2:TVector3d):TVector3d {.noInit.}=
|
||||
x2=v2.x/vmag2
|
||||
y2=v2.y/vmag2
|
||||
z2=v2.z/vmag2
|
||||
|
||||
|
||||
result.x=(x1 + x2) * 0.5
|
||||
result.y=(y1 + y2) * 0.5
|
||||
result.z=(z1 + z2) * 0.5
|
||||
|
||||
|
||||
if not result.tryNormalize():
|
||||
# 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.
|
||||
result=v1.cross(XAXIS)
|
||||
if result.sqrLen<1.0e-9:
|
||||
@@ -857,14 +857,14 @@ proc point3d*(x,y,z:float):TPoint3d=
|
||||
result.x=x
|
||||
result.y=y
|
||||
result.z=z
|
||||
|
||||
|
||||
proc sqrDist*(a,b:TPoint3d):float=
|
||||
## Computes the squared distance between `a`and `b`
|
||||
let dx=b.x-a.x
|
||||
let dy=b.y-a.y
|
||||
let dz=b.z-a.z
|
||||
result=dx*dx+dy*dy+dz*dz
|
||||
|
||||
|
||||
proc dist*(a,b:TPoint3d):float {.inline.}=
|
||||
## Computes the absolute distance between `a`and `b`
|
||||
result=sqrt(sqrDist(a,b))
|
||||
@@ -876,7 +876,7 @@ proc `$` *(p:TPoint3d):string=
|
||||
result.add(rtos(p.y))
|
||||
result.add(",")
|
||||
result.add(rtos(p.z))
|
||||
|
||||
|
||||
proc `&`*(p:TPoint3d,m:TMatrix3d):TPoint3d=
|
||||
## Concatenates a point `p` with a transform `m`,
|
||||
## 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.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
|
||||
|
||||
|
||||
proc transformInv*(p:var TPoint3d,m:TMatrix3d)=
|
||||
## Applies the inverse of transformation `m` onto `p` in place.
|
||||
## If the matrix is not invertable (determinant=0) , EDivByZero will
|
||||
## be raised.
|
||||
|
||||
|
||||
# can possibly be more optimized in the future so use this function when possible
|
||||
p&=inverse(m)
|
||||
|
||||
|
||||
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.
|
||||
result.x=p.x+v.x
|
||||
result.y=p.y+v.y
|
||||
@@ -917,7 +917,7 @@ proc `+=`*(p:var TPoint3d,v:TVector3d) {.noInit,inline.} =
|
||||
p.z+=v.z
|
||||
|
||||
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.
|
||||
result.x=p.x-v.x
|
||||
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.
|
||||
p.x-=v.x
|
||||
p.y-=v.y
|
||||
p.z-=v.z
|
||||
p.z-=v.z
|
||||
|
||||
proc equals(p1,p2:TPoint3d,tol=1.0e-6):bool {.inline.}=
|
||||
## 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
|
||||
|
||||
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
|
||||
equals(p1,p2)
|
||||
|
||||
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.
|
||||
|
||||
|
||||
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
|
||||
p.x=v.x
|
||||
p.y=v.y
|
||||
p.z=v.z
|
||||
|
||||
|
||||
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`
|
||||
|
||||
|
||||
# see PDF document http://inside.mines.edu/~gmurray/ArbitraryAxisRotation/ArbitraryAxisRotation.pdf
|
||||
# for how this is computed
|
||||
|
||||
|
||||
var normax=axis
|
||||
normax.normalize
|
||||
|
||||
|
||||
let
|
||||
cs=cos(angle)
|
||||
omc=1.0-cs
|
||||
@@ -987,17 +987,17 @@ proc rotate*(p:var TPoint3d,angle:float,org:TPoint3d,axis:TVector3d)=
|
||||
bv=b*v
|
||||
cw=c*w
|
||||
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.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
|
||||
|
||||
|
||||
proc scale*(p:var TPoint3d,fac:float) {.inline.}=
|
||||
## Scales a point in place `fac` times with world origo as origin.
|
||||
p.x*=fac
|
||||
p.y*=fac
|
||||
p.z*=fac
|
||||
|
||||
|
||||
proc scale*(p:var TPoint3d,fac:float,org:TPoint3d){.inline.}=
|
||||
## Scales the point in place `fac` times with `org` as origin.
|
||||
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
|
||||
|
||||
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.
|
||||
p.x*=facx
|
||||
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.y=(p.y - org.y) * facy + org.y
|
||||
p.z=(p.z - org.z) * facz + org.z
|
||||
|
||||
|
||||
|
||||
proc move*(p:var TPoint3d,dx,dy,dz:float){.inline.}=
|
||||
## 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.}=
|
||||
## 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
|
||||
# product of two edge vectors. Taking this time 0.5 gives the triangle area.
|
||||
return cross(b-a,c-a).len*0.5
|
||||
|
||||
@@ -525,7 +525,7 @@ proc get*[K,V](table: var PConcTable[K,V], key: var K): V =
|
||||
|
||||
|
||||
#Tests ----------------------------
|
||||
when isMainModule:
|
||||
when not defined(testing) and isMainModule:
|
||||
import locks, times, mersenne
|
||||
|
||||
const
|
||||
|
||||
@@ -286,18 +286,19 @@ proc `$`*[T](c: CritBitTree[T]): string =
|
||||
result.add("}")
|
||||
|
||||
when isMainModule:
|
||||
import sequtils
|
||||
|
||||
var r: CritBitTree[void]
|
||||
r.incl "abc"
|
||||
r.incl "xyz"
|
||||
r.incl "def"
|
||||
r.incl "definition"
|
||||
r.incl "prefix"
|
||||
|
||||
doAssert r.contains"def"
|
||||
#r.del "def"
|
||||
|
||||
for w in r.items:
|
||||
echo w
|
||||
|
||||
for w in r.itemsWithPrefix("de"):
|
||||
echo w
|
||||
r.excl "def"
|
||||
|
||||
assert toSeq(r.items) == @["abc", "definition", "prefix", "xyz"]
|
||||
|
||||
assert toSeq(r.itemsWithPrefix("de")) == @["definition"]
|
||||
|
||||
@@ -198,14 +198,21 @@ proc empty*(s: IntSet): bool {.inline, deprecated.} =
|
||||
result = s.counter == 0
|
||||
|
||||
when isMainModule:
|
||||
import sequtils, algorithm
|
||||
|
||||
var x = initIntSet()
|
||||
x.incl(1)
|
||||
x.incl(2)
|
||||
x.incl(7)
|
||||
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)
|
||||
for e in items(y): echo e
|
||||
var ys = toSeq(items(y))
|
||||
ys.sort(cmp[int])
|
||||
assert ys == @[1, 2, 7, 1056]
|
||||
|
||||
|
||||
@@ -492,9 +492,8 @@ when isMainModule:
|
||||
|
||||
block: # filter iterator test
|
||||
let numbers = @[1, 4, 5, 8, 9, 7, 4]
|
||||
for n in filter(numbers, proc (x: int): bool = x mod 2 == 0):
|
||||
echo($n)
|
||||
# echoes 4, 8, 4 in separate lines
|
||||
assert toSeq(filter(numbers, proc (x: int): bool = x mod 2 == 0)) ==
|
||||
@[4, 8, 4]
|
||||
|
||||
block: # keepIf test
|
||||
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 b.repeat(3) == @[]
|
||||
|
||||
echo "Finished doc tests"
|
||||
when not defined(testing):
|
||||
echo "Finished doc tests"
|
||||
|
||||
@@ -970,6 +970,7 @@ when isMainModule and not defined(release):
|
||||
if s <= i or mustRehash(s, i):
|
||||
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()
|
||||
|
||||
@@ -819,15 +819,18 @@ proc enlarge[A](t: var CountTable[A]) =
|
||||
swap(t.data, n)
|
||||
|
||||
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
|
||||
var h = rawGet(t, key)
|
||||
if h >= 0:
|
||||
t.data[h].val = val
|
||||
else:
|
||||
h = -1 - h
|
||||
t.data[h].key = key
|
||||
t.data[h].val = val
|
||||
if mustRehash(len(t.data), t.counter): enlarge(t)
|
||||
rawInsert(t, t.data, key, val)
|
||||
inc(t.counter)
|
||||
#h = -1 - h
|
||||
#t.data[h].key = key
|
||||
#t.data[h].val = val
|
||||
|
||||
proc initCountTable*[A](initialSize=64): CountTable[A] =
|
||||
## creates a new count table that is empty.
|
||||
@@ -984,6 +987,22 @@ proc sort*[A](t: CountTableRef[A]) =
|
||||
## `t` in the sorted order.
|
||||
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:
|
||||
type
|
||||
Person = object
|
||||
@@ -1012,3 +1031,48 @@ when isMainModule:
|
||||
s2[p2] = 45_000
|
||||
s3[p1] = 30_000
|
||||
s3[p2] = 45_000
|
||||
|
||||
var
|
||||
t1 = initCountTable[string]()
|
||||
t2 = initCountTable[string]()
|
||||
t1.inc("foo")
|
||||
t1.inc("bar", 2)
|
||||
t1.inc("baz", 3)
|
||||
t2.inc("foo", 4)
|
||||
t2.inc("bar")
|
||||
t2.inc("baz", 11)
|
||||
merge(t1, t2)
|
||||
assert(t1["foo"] == 5)
|
||||
assert(t1["bar"] == 3)
|
||||
assert(t1["baz"] == 14)
|
||||
|
||||
let
|
||||
t1r = newCountTable[string]()
|
||||
t2r = newCountTable[string]()
|
||||
t1r.inc("foo")
|
||||
t1r.inc("bar", 2)
|
||||
t1r.inc("baz", 3)
|
||||
t2r.inc("foo", 4)
|
||||
t2r.inc("bar")
|
||||
t2r.inc("baz", 11)
|
||||
merge(t1r, t2r)
|
||||
assert(t1r["foo"] == 5)
|
||||
assert(t1r["bar"] == 3)
|
||||
assert(t1r["baz"] == 14)
|
||||
|
||||
var
|
||||
t1l = initCountTable[string]()
|
||||
t2l = initCountTable[string]()
|
||||
t1l.inc("foo")
|
||||
t1l.inc("bar", 2)
|
||||
t1l.inc("baz", 3)
|
||||
t2l.inc("foo", 4)
|
||||
t2l.inc("bar")
|
||||
t2l.inc("baz", 11)
|
||||
let
|
||||
t1merging = t1l
|
||||
t2merging = t2l
|
||||
let merged = merge(t1merging, t2merging)
|
||||
assert(merged["foo"] == 5)
|
||||
assert(merged["bar"] == 3)
|
||||
assert(merged["baz"] == 14)
|
||||
|
||||
@@ -78,7 +78,7 @@ proc advice*(s: var ThreadPoolState): ThreadPoolAdvice =
|
||||
result = doNothing
|
||||
inc s.calls
|
||||
|
||||
when isMainModule:
|
||||
when not defined(testing) and isMainModule:
|
||||
proc busyLoop() =
|
||||
while true:
|
||||
discard random(80)
|
||||
|
||||
@@ -300,7 +300,7 @@ proc setMinPoolSize*(size: range[1..MaxThreadPoolSize]) =
|
||||
minPoolSize = size
|
||||
|
||||
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``.
|
||||
maxPoolSize = size
|
||||
if currentPoolSize > maxPoolSize:
|
||||
|
||||
@@ -56,6 +56,12 @@ proc setCookie*(key, value: string, expires: TimeInfo,
|
||||
when isMainModule:
|
||||
var tim = Time(int(getTime()) + 76 * (60 * 60 * 24))
|
||||
|
||||
echo(setCookie("test", "value", tim.getGMTime()))
|
||||
let cookie = setCookie("test", "value", tim.getGMTime())
|
||||
when not defined(testing):
|
||||
echo cookie
|
||||
let start = "Set-Cookie: test=value; Expires="
|
||||
assert cookie[0..start.high] == start
|
||||
|
||||
echo parseCookies("uid=1; kp=2")
|
||||
let table = parseCookies("uid=1; kp=2")
|
||||
assert table["uid"] == "1"
|
||||
assert table["kp"] == "2"
|
||||
|
||||
@@ -451,7 +451,7 @@ proc convert*(s: string, destEncoding = "UTF-8",
|
||||
finally:
|
||||
close(c)
|
||||
|
||||
when isMainModule:
|
||||
when not defined(testing) and isMainModule:
|
||||
let
|
||||
orig = "öäüß"
|
||||
cp1252 = convert(orig, "CP1252", "UTF-8")
|
||||
|
||||
@@ -198,7 +198,7 @@ proc register*(d: Dispatcher, monitor: FSMonitor,
|
||||
var deleg = toDelegate(monitor)
|
||||
d.register(deleg)
|
||||
|
||||
when isMainModule:
|
||||
when not defined(testing) and isMainModule:
|
||||
proc main =
|
||||
var disp = newDispatcher()
|
||||
var monitor = newMonitor()
|
||||
|
||||
@@ -593,7 +593,7 @@ proc register*(d: Dispatcher, ftp: AsyncFTPClient): Delegate {.discardable.} =
|
||||
ftp.disp = d
|
||||
return ftp.disp.register(ftp.csock)
|
||||
|
||||
when isMainModule:
|
||||
when not defined(testing) and isMainModule:
|
||||
proc main =
|
||||
var d = newDispatcher()
|
||||
let hev =
|
||||
@@ -629,7 +629,7 @@ when isMainModule:
|
||||
if not d.poll(): break
|
||||
main()
|
||||
|
||||
when isMainModule and false:
|
||||
when not defined(testing) and isMainModule:
|
||||
var ftp = ftpClient("example.com", user = "foo", pass = "bar")
|
||||
ftp.connect()
|
||||
echo ftp.pwd()
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
|
||||
## The ``gentabs`` module implements an efficient hash table that is a
|
||||
## key-value mapping. The keys are required to be strings, but the values
|
||||
## may be any Nim or user defined type. This module supports matching
|
||||
## may be any Nim or user defined type. This module supports matching
|
||||
## of keys in case-sensitive, case-insensitive and style-insensitive modes.
|
||||
|
||||
{.deprecated.}
|
||||
@@ -22,7 +22,7 @@ type
|
||||
modeCaseSensitive, ## case sensitive matching of keys
|
||||
modeCaseInsensitive, ## case insensitive matching of keys
|
||||
modeStyleInsensitive ## style sensitive matching of keys
|
||||
|
||||
|
||||
TGenKeyValuePair[T] = tuple[key: string, val: T]
|
||||
TGenKeyValuePairSeq[T] = seq[TGenKeyValuePair[T]]
|
||||
TGenTable*[T] = object of RootObj
|
||||
@@ -83,7 +83,7 @@ proc rawGet[T](tbl: PGenTable[T], key: string): int =
|
||||
h = nextTry(h, high(tbl.data))
|
||||
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) =
|
||||
var h: THash
|
||||
h = myhash(tbl, key) and high(data)
|
||||
@@ -96,7 +96,7 @@ proc enlarge[T](tbl: PGenTable[T]) =
|
||||
var n: TGenKeyValuePairSeq[T]
|
||||
newSeq(n, len(tbl.data) * growthFactor)
|
||||
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)
|
||||
swap(tbl.data, n)
|
||||
|
||||
@@ -141,20 +141,20 @@ when isMainModule:
|
||||
assert(not x.hasKey("NOPE")) # ...but key "NOPE" is not in the table.
|
||||
for k,v in pairs(x): # make sure the 'pairs' iterator works
|
||||
assert(x[k]==v)
|
||||
|
||||
|
||||
#
|
||||
# Verify a table of user-defined types
|
||||
#
|
||||
type
|
||||
TMyType = tuple[first, second: string] # a pair of strings
|
||||
|
||||
|
||||
var y = newGenTable[TMyType](modeCaseInsensitive) # hash table where each
|
||||
# value is TMyType tuple
|
||||
|
||||
|
||||
#var junk: TMyType = ("OK", "Here")
|
||||
|
||||
|
||||
#echo junk.first, " ", junk.second
|
||||
|
||||
|
||||
y["Hello"] = ("Hello", "World")
|
||||
y["Goodbye"] = ("Goodbye", "Everyone")
|
||||
#y["Hello"] = TMyType( ("Hello", "World") )
|
||||
@@ -163,31 +163,44 @@ when isMainModule:
|
||||
assert( not isNil(y["Hello"].first) )
|
||||
assert( y["Hello"].first == "Hello" )
|
||||
assert( y["Hello"].second == "World" )
|
||||
|
||||
|
||||
#
|
||||
# Verify table of tables
|
||||
#
|
||||
var z: PGenTable[ PGenTable[int] ] # hash table where each value is
|
||||
var z: PGenTable[ PGenTable[int] ] # hash table where each value is
|
||||
# a hash table of ints
|
||||
|
||||
|
||||
z = newGenTable[PGenTable[int]](modeCaseInsensitive)
|
||||
z["first"] = newGenTable[int](modeCaseInsensitive)
|
||||
z["first"]["one"] = 1
|
||||
z["first"]["two"] = 2
|
||||
z["first"]["three"] = 3
|
||||
|
||||
|
||||
z["second"] = newGenTable[int](modeCaseInsensitive)
|
||||
z["second"]["red"] = 10
|
||||
z["second"]["blue"] = 20
|
||||
|
||||
|
||||
assert(len(z) == 2) # length of outer table
|
||||
assert(len(z["first"]) == 3) # length of "first" table
|
||||
assert(len(z["second"]) == 2) # length of "second" table
|
||||
assert( z["first"]["one"] == 1) # retrieve from first inner table
|
||||
assert( z["second"]["red"] == 10) # retrieve from second inner table
|
||||
|
||||
for k,v in pairs(z):
|
||||
echo( "$# ($#) ->" % [k,$len(v)] )
|
||||
#for k2,v2 in pairs(v):
|
||||
# echo( " $# <-> $#" % [k2,$v2] )
|
||||
echo()
|
||||
|
||||
when false:
|
||||
# disabled: depends on hash order:
|
||||
var output = ""
|
||||
for k, v in pairs(z):
|
||||
output.add( "$# ($#) ->\L" % [k,$len(v)] )
|
||||
for k2,v2 in pairs(v):
|
||||
output.add( " $# <-> $#\L" % [k2,$v2] )
|
||||
|
||||
let expected = unindent """
|
||||
first (3) ->
|
||||
two <-> 2
|
||||
three <-> 3
|
||||
one <-> 1
|
||||
second (2) ->
|
||||
red <-> 10
|
||||
blue <-> 20
|
||||
"""
|
||||
assert output == expected
|
||||
|
||||
@@ -37,29 +37,29 @@
|
||||
## h = h !& hash(x.bar)
|
||||
## result = !$h
|
||||
|
||||
import
|
||||
import
|
||||
strutils
|
||||
|
||||
type
|
||||
THash* = int ## a hash value; hash tables using these values should
|
||||
type
|
||||
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``
|
||||
## 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
|
||||
## only needed if you need to implement a hash proc for a new datatype.
|
||||
result = h +% val
|
||||
result = result +% result shl 10
|
||||
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
|
||||
## only needed if you need to implement a hash proc for a new datatype.
|
||||
result = h +% h shl 3
|
||||
result = result xor (result shr 11)
|
||||
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`
|
||||
var h: THash = 0
|
||||
when defined(js):
|
||||
@@ -69,7 +69,7 @@ proc hashData*(data: pointer, size: int): THash =
|
||||
var p = cast[cstring](data)
|
||||
var i = 0
|
||||
var s = size
|
||||
while s > 0:
|
||||
while s > 0:
|
||||
h = h !& ord(p[i])
|
||||
inc(i)
|
||||
dec(s)
|
||||
@@ -78,7 +78,7 @@ proc hashData*(data: pointer, size: int): THash =
|
||||
when defined(js):
|
||||
var objectID = 0
|
||||
|
||||
proc hash*(x: pointer): THash {.inline.} =
|
||||
proc hash*(x: pointer): THash {.inline.} =
|
||||
## efficient hashing of pointers
|
||||
when defined(js):
|
||||
asm """
|
||||
@@ -93,7 +93,7 @@ proc hash*(x: pointer): THash {.inline.} =
|
||||
"""
|
||||
else:
|
||||
result = (cast[THash](x)) shr 3 # skip the alignment
|
||||
|
||||
|
||||
when not defined(booting):
|
||||
proc hash*[T: proc](x: T): THash {.inline.} =
|
||||
## efficient hashing of proc vars; closures are supported too.
|
||||
@@ -101,58 +101,65 @@ when not defined(booting):
|
||||
result = hash(rawProc(x)) !& hash(rawEnv(x))
|
||||
else:
|
||||
result = hash(pointer(x))
|
||||
|
||||
proc hash*(x: int): THash {.inline.} =
|
||||
|
||||
proc hash*(x: int): THash {.inline.} =
|
||||
## efficient hashing of integers
|
||||
result = x
|
||||
|
||||
proc hash*(x: int64): THash {.inline.} =
|
||||
proc hash*(x: int64): THash {.inline.} =
|
||||
## efficient hashing of integers
|
||||
result = toU32(x)
|
||||
|
||||
proc hash*(x: char): THash {.inline.} =
|
||||
proc hash*(x: char): THash {.inline.} =
|
||||
## efficient hashing of characters
|
||||
result = ord(x)
|
||||
|
||||
proc hash*(x: string): THash =
|
||||
proc hash*(x: string): THash =
|
||||
## efficient hashing of strings
|
||||
var h: THash = 0
|
||||
for i in 0..x.len-1:
|
||||
for i in 0..x.len-1:
|
||||
h = h !& ord(x[i])
|
||||
result = !$h
|
||||
|
||||
proc hashIgnoreStyle*(x: string): THash =
|
||||
|
||||
proc hashIgnoreStyle*(x: string): THash =
|
||||
## efficient hashing of strings; style is ignored
|
||||
var h: THash = 0
|
||||
for i in 0..x.len-1:
|
||||
for i in 0..x.len-1:
|
||||
var c = x[i]
|
||||
if c == '_':
|
||||
if c == '_':
|
||||
continue # skip _
|
||||
if c in {'A'..'Z'}:
|
||||
if c in {'A'..'Z'}:
|
||||
c = chr(ord(c) + (ord('a') - ord('A'))) # toLower()
|
||||
h = h !& ord(c)
|
||||
result = !$h
|
||||
|
||||
proc hashIgnoreCase*(x: string): THash =
|
||||
proc hashIgnoreCase*(x: string): THash =
|
||||
## efficient hashing of strings; case is ignored
|
||||
var h: THash = 0
|
||||
for i in 0..x.len-1:
|
||||
for i in 0..x.len-1:
|
||||
var c = x[i]
|
||||
if c in {'A'..'Z'}:
|
||||
if c in {'A'..'Z'}:
|
||||
c = chr(ord(c) + (ord('a') - ord('A'))) # toLower()
|
||||
h = h !& ord(c)
|
||||
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.} =
|
||||
var y = x + 1.0
|
||||
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 =
|
||||
for it in items(x): result = result !& hash(it)
|
||||
result = !$result
|
||||
@@ -160,3 +167,4 @@ proc hash*[A](x: openArray[A]): THash =
|
||||
proc hash*[A](x: set[A]): THash =
|
||||
for it in items(x): result = result !& hash(it)
|
||||
result = !$result
|
||||
|
||||
|
||||
@@ -483,7 +483,8 @@ macro `var`*(e: expr): expr {.immediate.} =
|
||||
result = xmlCheckedTag(e, "var", commonAttr)
|
||||
|
||||
when isMainModule:
|
||||
var nim = "Nim"
|
||||
echo h1(a(href="http://nim-lang.org", nim))
|
||||
echo form(action="test", `accept-charset` = "Content-Type")
|
||||
|
||||
let nim = "Nim"
|
||||
assert h1(a(href="http://nim-lang.org", nim)) ==
|
||||
"""<h1><a href="http://nim-lang.org">Nim</a></h1>"""
|
||||
assert form(action="test", `accept-charset` = "Content-Type") ==
|
||||
"""<form action="test" accept-charset="Content-Type"></form>"""
|
||||
|
||||
@@ -593,7 +593,7 @@ proc loadHtml*(path: string): XmlNode =
|
||||
var errors: seq[string] = @[]
|
||||
result = loadHtml(path, errors)
|
||||
|
||||
when isMainModule:
|
||||
when not defined(testing) and isMainModule:
|
||||
import os
|
||||
|
||||
var errors: seq[string] = @[]
|
||||
|
||||
@@ -819,7 +819,7 @@ proc get*(client: AsyncHttpClient, url: string): Future[Response] {.async.} =
|
||||
result = await client.request(redirectTo, httpGET)
|
||||
lastUrl = redirectTo
|
||||
|
||||
when isMainModule:
|
||||
when not defined(testing) and isMainModule:
|
||||
when true:
|
||||
# Async
|
||||
proc main() {.async.} =
|
||||
|
||||
@@ -514,7 +514,7 @@ proc close*(h: PAsyncHTTPServer) =
|
||||
## Closes the ``PAsyncHTTPServer``.
|
||||
h.asyncSocket.close()
|
||||
|
||||
when isMainModule:
|
||||
when not defined(testing) and isMainModule:
|
||||
var counter = 0
|
||||
|
||||
var s: TServer
|
||||
|
||||
@@ -808,22 +808,25 @@ proc `[]=`*(obj: JsonNode, key: string, val: JsonNode) =
|
||||
return
|
||||
obj.fields.add((key, val))
|
||||
|
||||
proc `{}`*(node: JsonNode, key: string): JsonNode =
|
||||
## Transverses the node and gets the given value. If any of the
|
||||
## names does not exist, returns nil
|
||||
proc `{}`*(node: JsonNode, keys: varargs[string]): JsonNode =
|
||||
## Traverses the node and gets the given value. If any of the
|
||||
## keys do not exist, returns nil. Also returns nil if one of the
|
||||
## intermediate data structures is not an object
|
||||
result = node
|
||||
if isNil(node): return nil
|
||||
result = result[key]
|
||||
for key in keys:
|
||||
if isNil(result) or result.kind!=JObject:
|
||||
return nil
|
||||
result=result[key]
|
||||
|
||||
proc `{}=`*(node: JsonNode, names: varargs[string], value: JsonNode) =
|
||||
## Transverses the node and tries to set the value at the given location
|
||||
## to `value` If any of the names are missing, they are added
|
||||
proc `{}=`*(node: JsonNode, keys: varargs[string], value: JsonNode) =
|
||||
## Traverses the node and tries to set the value at the given location
|
||||
## to `value` If any of the keys are missing, they are added
|
||||
var node = node
|
||||
for i in 0..(names.len-2):
|
||||
if isNil(node[names[i]]):
|
||||
node[names[i]] = newJObject()
|
||||
node = node[names[i]]
|
||||
node[names[names.len-1]] = value
|
||||
for i in 0..(keys.len-2):
|
||||
if isNil(node[keys[i]]):
|
||||
node[keys[i]] = newJObject()
|
||||
node = node[keys[i]]
|
||||
node[keys[keys.len-1]] = value
|
||||
|
||||
proc delete*(obj: JsonNode, key: string) =
|
||||
## Deletes ``obj[key]`` preserving the order of the other (key, value)-pairs.
|
||||
@@ -1150,19 +1153,22 @@ when false:
|
||||
when isMainModule:
|
||||
#var node = parse("{ \"test\": null }")
|
||||
#echo(node.existsKey("test56"))
|
||||
|
||||
var parsed = parseFile("tests/testdata/jsontest.json")
|
||||
var parsed2 = parseFile("tests/testdata/jsontest2.json")
|
||||
echo(parsed)
|
||||
echo()
|
||||
echo(pretty(parsed, 2))
|
||||
echo()
|
||||
echo(parsed["keyÄÖöoßß"])
|
||||
echo()
|
||||
echo(pretty(parsed2))
|
||||
try:
|
||||
echo(parsed["key2"][12123])
|
||||
raise newException(ValueError, "That line was expected to fail")
|
||||
except IndexError: echo()
|
||||
|
||||
when not defined(testing):
|
||||
echo(parsed)
|
||||
echo()
|
||||
echo(pretty(parsed, 2))
|
||||
echo()
|
||||
echo(parsed["keyÄÖöoßß"])
|
||||
echo()
|
||||
echo(pretty(parsed2))
|
||||
try:
|
||||
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" }"""
|
||||
# nil passthrough
|
||||
@@ -1186,9 +1192,17 @@ when isMainModule:
|
||||
except:
|
||||
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:
|
||||
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 = %*
|
||||
[
|
||||
@@ -1216,12 +1230,13 @@ when isMainModule:
|
||||
}
|
||||
]
|
||||
assert j3 == %[%{"name": %"John", "age": %30}, %{"name": %"Susan", "age": %31}]
|
||||
|
||||
discard """
|
||||
while true:
|
||||
var json = stdin.readLine()
|
||||
var node = parse(json)
|
||||
echo(node)
|
||||
echo()
|
||||
echo()
|
||||
"""
|
||||
|
||||
when not defined(testing):
|
||||
discard """
|
||||
while true:
|
||||
var json = stdin.readLine()
|
||||
var node = parse(json)
|
||||
echo(node)
|
||||
echo()
|
||||
echo()
|
||||
"""
|
||||
|
||||
@@ -278,7 +278,7 @@ proc getLogFilter*(): Level =
|
||||
|
||||
# --------------
|
||||
|
||||
when isMainModule:
|
||||
when not defined(testing) and isMainModule:
|
||||
var L = newConsoleLogger()
|
||||
var fL = newFileLogger("test.log", fmtStr = verboseFmtStr)
|
||||
var rL = newRollingFileLogger("rolling.log", fmtStr = verboseFmtStr)
|
||||
|
||||
@@ -15,8 +15,8 @@
|
||||
## type than its compiletime type:
|
||||
##
|
||||
## .. code-block:: nim
|
||||
##
|
||||
## type
|
||||
##
|
||||
## type
|
||||
## TA = object
|
||||
## TB = object of TA
|
||||
## f: int
|
||||
@@ -28,6 +28,8 @@
|
||||
## new(b)
|
||||
## a = b
|
||||
## echo($$a[]) # produces "{}", not "{f: 0}"
|
||||
##
|
||||
## **Note**: The ``to`` and ``$$`` operations are available at compile-time!
|
||||
|
||||
import streams, typeinfo, json, intsets, tables
|
||||
|
||||
@@ -38,7 +40,12 @@ proc storeAny(s: Stream, a: TAny, stored: var IntSet) =
|
||||
case a.kind
|
||||
of akNone: assert false
|
||||
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:
|
||||
if a.kind == akSequence and isNil(a): s.write("null")
|
||||
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]) =
|
||||
case a.kind
|
||||
of akNone: assert false
|
||||
of akBool:
|
||||
of akBool:
|
||||
case p.kind
|
||||
of jsonFalse: setBiggestInt(a, 0)
|
||||
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]))
|
||||
next(p)
|
||||
return
|
||||
elif p.kind == jsonInt:
|
||||
setBiggestInt(a, getInt(p))
|
||||
next(p)
|
||||
return
|
||||
raiseParseErr(p, "string of length 1 expected for a char")
|
||||
of akEnum:
|
||||
of akEnum:
|
||||
if p.kind == jsonString:
|
||||
setBiggestInt(a, getEnumOrdinal(a, p.str))
|
||||
next(p)
|
||||
@@ -122,7 +133,7 @@ proc loadAny(p: var JsonParser, a: TAny, t: var Table[BiggestInt, pointer]) =
|
||||
if p.kind == jsonArrayEnd: next(p)
|
||||
else: raiseParseErr(p, "']' end of array expected")
|
||||
of akSequence:
|
||||
case p.kind
|
||||
case p.kind
|
||||
of jsonNull:
|
||||
setPointer(a, nil)
|
||||
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")
|
||||
next(p)
|
||||
while p.kind != jsonObjectEnd and p.kind != jsonEof:
|
||||
if p.kind != jsonString:
|
||||
if p.kind != jsonString:
|
||||
raiseParseErr(p, "string expected for a field name")
|
||||
var fieldName = p.str
|
||||
next(p)
|
||||
@@ -160,7 +171,7 @@ proc loadAny(p: var JsonParser, a: TAny, t: var Table[BiggestInt, pointer]) =
|
||||
if p.kind == jsonArrayEnd: next(p)
|
||||
else: raiseParseErr(p, "']' end of array expected")
|
||||
of akPtr, akRef:
|
||||
case p.kind
|
||||
case p.kind
|
||||
of jsonNull:
|
||||
setPointer(a, nil)
|
||||
next(p)
|
||||
@@ -170,7 +181,7 @@ proc loadAny(p: var JsonParser, a: TAny, t: var Table[BiggestInt, pointer]) =
|
||||
of jsonArrayStart:
|
||||
next(p)
|
||||
if a.kind == akRef: invokeNew(a)
|
||||
else: setPointer(a, alloc0(a.baseTypeSize))
|
||||
else: setPointer(a, alloc0(a.baseTypeSize))
|
||||
if p.kind == jsonInt:
|
||||
t[p.getInt] = getPointer(a)
|
||||
next(p)
|
||||
@@ -179,8 +190,8 @@ proc loadAny(p: var JsonParser, a: TAny, t: var Table[BiggestInt, pointer]) =
|
||||
if p.kind == jsonArrayEnd: next(p)
|
||||
else: raiseParseErr(p, "']' end of ref-address pair expected")
|
||||
else: raiseParseErr(p, "int for pointer type expected")
|
||||
of akProc, akPointer, akCString:
|
||||
case p.kind
|
||||
of akProc, akPointer, akCString:
|
||||
case p.kind
|
||||
of jsonNull:
|
||||
setPointer(a, nil)
|
||||
next(p)
|
||||
@@ -189,7 +200,7 @@ proc loadAny(p: var JsonParser, a: TAny, t: var Table[BiggestInt, pointer]) =
|
||||
next(p)
|
||||
else: raiseParseErr(p, "int for pointer type expected")
|
||||
of akString:
|
||||
case p.kind
|
||||
case p.kind
|
||||
of jsonNull:
|
||||
setPointer(a, nil)
|
||||
next(p)
|
||||
@@ -197,7 +208,7 @@ proc loadAny(p: var JsonParser, a: TAny, t: var Table[BiggestInt, pointer]) =
|
||||
setString(a, p.str)
|
||||
next(p)
|
||||
else: raiseParseErr(p, "string expected")
|
||||
of akInt..akInt64, akUInt..akUInt64:
|
||||
of akInt..akInt64, akUInt..akUInt64:
|
||||
if p.kind == jsonInt:
|
||||
setBiggestInt(a, getInt(p))
|
||||
next(p)
|
||||
@@ -243,22 +254,22 @@ proc to*[T](data: string): T =
|
||||
## reads data and transforms it to a ``T``.
|
||||
var tab = initTable[BiggestInt, pointer]()
|
||||
loadAny(newStringStream(data), toAny(result), tab)
|
||||
|
||||
when isMainModule:
|
||||
|
||||
when not defined(testing) and isMainModule:
|
||||
template testit(x: expr) = echo($$to[type(x)]($$x))
|
||||
|
||||
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"]]
|
||||
testit(x)
|
||||
var test2: tuple[name: string, s: uint] = ("tuple test", 56u)
|
||||
testit(test2)
|
||||
|
||||
|
||||
type
|
||||
TE = enum
|
||||
blah, blah2
|
||||
|
||||
|
||||
TestObj = object
|
||||
test, asd: int
|
||||
case test2: TE
|
||||
@@ -266,7 +277,7 @@ when isMainModule:
|
||||
help: string
|
||||
else:
|
||||
nil
|
||||
|
||||
|
||||
PNode = ref TNode
|
||||
TNode = object
|
||||
next, prev: PNode
|
||||
@@ -294,7 +305,7 @@ when isMainModule:
|
||||
test4.a = "ref string test: A"
|
||||
test4.b = "ref string test: B"
|
||||
testit(test4)
|
||||
|
||||
|
||||
var test5 = @[(0,1),(2,3),(4,5)]
|
||||
testit(test5)
|
||||
|
||||
@@ -305,7 +316,7 @@ when isMainModule:
|
||||
echo($$test7)
|
||||
testit(test7)
|
||||
|
||||
type
|
||||
type
|
||||
TA {.inheritable.} = object
|
||||
TB = object of TA
|
||||
f: int
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user