diff --git a/.gitignore b/.gitignore
index d804fb8f54..462df4efc7 100644
--- a/.gitignore
+++ b/.gitignore
@@ -41,4 +41,3 @@ xcuserdata/
/testresults.html
/testresults.json
testament.db
-/csources/
diff --git a/.gitmodules b/.gitmodules
new file mode 100644
index 0000000000..26f35d82cd
--- /dev/null
+++ b/.gitmodules
@@ -0,0 +1,3 @@
+[submodule "csources"]
+ path = csources
+ url = ../../nim-lang/csources.git
diff --git a/build.sh b/build.sh
old mode 100644
new mode 100755
index 139c283599..91e1692412
--- a/build.sh
+++ b/build.sh
@@ -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"
diff --git a/compiler/ast.nim b/compiler/ast.nim
index b0e9577fc3..3798410e87 100644
--- a/compiler/ast.nim
+++ b/compiler/ast.nim
@@ -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) =
diff --git a/compiler/ccgexprs.nim b/compiler/ccgexprs.nim
index 11c9d2d500..05a3602d16 100644
--- a/compiler/ccgexprs.nim
+++ b/compiler/ccgexprs.nim
@@ -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)
diff --git a/compiler/ccgstmts.nim b/compiler/ccgstmts.nim
index 1277f71545..6d29b16847 100644
--- a/compiler/ccgstmts.nim
+++ b/compiler/ccgstmts.nim
@@ -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 .. 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)
diff --git a/compiler/ccgtypes.nim b/compiler/ccgtypes.nim
index 60ebf591be..3742fd2fd0 100644
--- a/compiler/ccgtypes.nim
+++ b/compiler/ccgtypes.nim
@@ -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:
diff --git a/compiler/ccgutils.nim b/compiler/ccgutils.nim
index 4e94c18675..4ba6643ecf 100644
--- a/compiler/ccgutils.nim
+++ b/compiler/ccgutils.nim
@@ -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:
diff --git a/compiler/commands.nim b/compiler/commands.nim
index 5b5f461efd..b6ebb6bcb7 100644
--- a/compiler/commands.nim
+++ b/compiler/commands.nim
@@ -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)
diff --git a/compiler/condsyms.nim b/compiler/condsyms.nim
index 7ddf44d4ac..ad7d80c850 100644
--- a/compiler/condsyms.nim
+++ b/compiler/condsyms.nim
@@ -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
diff --git a/compiler/extccomp.nim b/compiler/extccomp.nim
index 499d9ae521..75cb1ef27b 100644
--- a/compiler/extccomp.nim
+++ b/compiler/extccomp.nim
@@ -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"))
diff --git a/compiler/guards.nim b/compiler/guards.nim
index cedd2be2bd..df2c1dd75f 100644
--- a/compiler/guards.nim
+++ b/compiler/guards.nim
@@ -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
diff --git a/compiler/installer.ini b/compiler/installer.ini
index 12a8e702de..fff82cb5b1 100644
--- a/compiler/installer.ini
+++ b/compiler/installer.ini
@@ -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]
diff --git a/compiler/jsgen.nim b/compiler/jsgen.nim
index 6c667a3a71..704713243f 100644
--- a/compiler/jsgen.nim
+++ b/compiler/jsgen.nim
@@ -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.. 0 and L.buf[L.bufpos-1] in SymChars
diff --git a/compiler/lowerings.nim b/compiler/lowerings.nim
index 0b4f97ead4..b6b01d5585 100644
--- a/compiler/lowerings.nim
+++ b/compiler/lowerings.nim
@@ -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])
diff --git a/compiler/msgs.nim b/compiler/msgs.nim
index 778b839f37..041a181be7 100644
--- a/compiler/msgs.nim
+++ b/compiler/msgs.nim
@@ -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:
diff --git a/compiler/nimfix/nimfix.nim.cfg b/compiler/nimfix/nimfix.nim.cfg
index 533563a985..73219d6f81 100644
--- a/compiler/nimfix/nimfix.nim.cfg
+++ b/compiler/nimfix/nimfix.nim.cfg
@@ -5,7 +5,7 @@ hint[XDeclaredButNotUsed]:off
path:"$projectPath/.."
path:"$lib/packages/docutils"
-path:"$nim/compiler"
+path:"../../compiler"
define:useStdoutAsStdmsg
symbol:nimfix
diff --git a/compiler/nimsuggest/nimsuggest.nim.cfg b/compiler/nimsuggest/nimsuggest.nim.cfg
index 062092f16d..acca173968 100644
--- a/compiler/nimsuggest/nimsuggest.nim.cfg
+++ b/compiler/nimsuggest/nimsuggest.nim.cfg
@@ -6,7 +6,7 @@ hint[XDeclaredButNotUsed]:off
path:"$projectPath/../.."
path:"$lib/packages/docutils"
-path:"$nim/compiler"
+path:"../../compiler"
define:useStdoutAsStdmsg
define:nimsuggest
diff --git a/compiler/options.nim b/compiler/options.nim
index 709959cc48..d07342fce4 100644
--- a/compiler/options.nim
+++ b/compiler/options.nim
@@ -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.
diff --git a/compiler/parampatterns.nim b/compiler/parampatterns.nim
index 3f67005b9f..b7fe269dfc 100644
--- a/compiler/parampatterns.nim
+++ b/compiler/parampatterns.nim
@@ -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
diff --git a/compiler/parser.nim b/compiler/parser.nim
index d2831ea46e..0d2ba7cfc0 100644
--- a/compiler/parser.nim
+++ b/compiler/parser.nim
@@ -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
diff --git a/compiler/plugins.nim b/compiler/plugins.nim
new file mode 100644
index 0000000000..1c9b7b77b8
--- /dev/null
+++ b/compiler/plugins.nim
@@ -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)
diff --git a/compiler/plugins/active.nim b/compiler/plugins/active.nim
new file mode 100644
index 0000000000..e9c11c2ea7
--- /dev/null
+++ b/compiler/plugins/active.nim
@@ -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
diff --git a/compiler/plugins/locals/locals.nim b/compiler/plugins/locals/locals.nim
new file mode 100644
index 0000000000..d89149f338
--- /dev/null
+++ b/compiler/plugins/locals/locals.nim
@@ -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)
diff --git a/compiler/pragmas.nim b/compiler/pragmas.nim
index ec594069ef..c048d78e9e 100644
--- a/compiler/pragmas.nim
+++ b/compiler/pragmas.nim
@@ -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)
diff --git a/compiler/sem.nim b/compiler/sem.nim
index 7eabaf491d..346a17df1a 100644
--- a/compiler/sem.nim
+++ b/compiler/sem.nim
@@ -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
diff --git a/compiler/semdata.nim b/compiler/semdata.nim
index cf7a52ff56..345a8c0d13 100644
--- a/compiler/semdata.nim
+++ b/compiler/semdata.nim
@@ -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, @[
diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim
index c5bfbfa92a..cd6ba37535 100644
--- a/compiler/semexprs.nim
+++ b/compiler/semexprs.nim
@@ -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:
diff --git a/compiler/semfold.nim b/compiler/semfold.nim
index 52931bc2b7..da24005c2d 100644
--- a/compiler/semfold.nim
+++ b/compiler/semfold.nim
@@ -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
diff --git a/compiler/seminst.nim b/compiler/seminst.nim
index f72e2dc5be..b2aef63a80 100644
--- a/compiler/seminst.nim
+++ b/compiler/seminst.nim
@@ -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)
diff --git a/compiler/semmacrosanity.nim b/compiler/semmacrosanity.nim
index 2ef7a54e7e..bb9814a169 100644
--- a/compiler/semmacrosanity.nim
+++ b/compiler/semmacrosanity.nim
@@ -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
diff --git a/compiler/semmagic.nim b/compiler/semmagic.nim
index de7700be6d..0a7846f1de 100644
--- a/compiler/semmagic.nim
+++ b/compiler/semmagic.nim
@@ -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
diff --git a/compiler/sempass2.nim b/compiler/sempass2.nim
index 5a243afa01..adf03be641 100644
--- a/compiler/sempass2.nim
+++ b/compiler/sempass2.nim
@@ -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.. = 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)
diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim
index a8463cbed9..c355a5bf11 100644
--- a/compiler/semstmts.nim
+++ b/compiler/semstmts.nim
@@ -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)
diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim
index 4a45da2f9e..8c7bd7243f 100644
--- a/compiler/semtypes.nim
+++ b/compiler/semtypes.nim
@@ -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 .. 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)
diff --git a/compiler/types.nim b/compiler/types.nim
index 153c26a42f..e205f5722d 100644
--- a/compiler/types.nim
+++ b/compiler/types.nim
@@ -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
diff --git a/compiler/vm.nim b/compiler/vm.nim
index 3b5c8e7f3b..1c6c9a30b0 100644
--- a/compiler/vm.nim
+++ b/compiler/vm.nim
@@ -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 .. 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 =
diff --git a/compiler/vmdef.nim b/compiler/vmdef.nim
index 90b9f2517a..047009f018 100644
--- a/compiler/vmdef.nim
+++ b/compiler/vmdef.nim
@@ -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}
diff --git a/compiler/vmdeps.nim b/compiler/vmdeps.nim
index 6148ed319a..21ee4967b1 100644
--- a/compiler/vmdeps.nim
+++ b/compiler/vmdeps.nim
@@ -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)
diff --git a/compiler/vmgen.nim b/compiler/vmgen.nim
index c3013852d1..0743a45020 100644
--- a/compiler/vmgen.nim
+++ b/compiler/vmgen.nim
@@ -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
diff --git a/compiler/vmmarshal.nim b/compiler/vmmarshal.nim
new file mode 100644
index 0000000000..293d0d9494
--- /dev/null
+++ b/compiler/vmmarshal.nim
@@ -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.. 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.. 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)
diff --git a/compiler/vmops.nim b/compiler/vmops.nim
index 502ad8ecc3..1023d4783f 100644
--- a/compiler/vmops.nim
+++ b/compiler/vmops.nim
@@ -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
diff --git a/config/nim.cfg b/config/nim.cfg
index ccb9977db4..fef7df79ec 100644
--- a/config/nim.cfg
+++ b/config/nim.cfg
@@ -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
diff --git a/csources b/csources
new file mode 160000
index 0000000000..15724e2e1f
--- /dev/null
+++ b/csources
@@ -0,0 +1 @@
+Subproject commit 15724e2e1f3e7749d508dfcd995e84fea2850802
diff --git a/doc/grammar.txt b/doc/grammar.txt
index b535154950..72dc6c9744 100644
--- a/doc/grammar.txt
+++ b/doc/grammar.txt
@@ -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
diff --git a/doc/lib.txt b/doc/lib.txt
index 385e7a91a7..1c02780683 100644
--- a/doc/lib.txt
+++ b/doc/lib.txt
@@ -37,7 +37,7 @@ Core
* `unsigned `_
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 `_
@@ -45,7 +45,7 @@ Core
import it explicitly.
* `channels `_
- 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 `_
@@ -55,7 +55,7 @@ Core
Contains the AST API and documentation of Nim for writing macros.
* `typeinfo `_
- Provides (unsafe) access to Nim's run time type information.
+ Provides (unsafe) access to Nim's run time type information.
* `typetraits `_
This module defines compile-time reflection procs for working with types.
@@ -110,9 +110,9 @@ String handling
* `unicode `_
This module provides support to handle the Unicode UTF-8 encoding.
-
+
* `encodings `_
- 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 `_
@@ -159,7 +159,7 @@ Generic Operating System Services
may provide other implementations for this standard stream interface.
* `marshal `_
- Contains procs for serialization and deseralization of arbitrary Nim
+ Contains procs for serialization and deseralization of arbitrary Nim
data structures.
* `terminal `_
@@ -168,7 +168,7 @@ Generic Operating System Services
sequences and does not depend on any other module.
* `memfiles `_
- 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 `_
@@ -228,7 +228,7 @@ Internet Protocols and Support
This module implements a simple HTTP client.
* `smtp `_
- This module implement a simple SMTP client.
+ This module implement a simple SMTP client.
* `ftpclient `_
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 `_
- 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 `_
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 `_
This module implements a zip archive creator/reader/modifier.
-* `web `_
- This module contains simple high-level procedures for dealing with the
- Web like loading the contents of a Web page from an URL.
-
* `ssl `_
- 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 `_
@@ -513,25 +509,6 @@ Regular expressions
Wrapper for the TRE library.
-Graphics libraries
-------------------
-
-* `sdl `_
- Part of the wrapper for SDL.
-* `sdl_gfx `_
- Part of the wrapper for SDL.
-* `sdl_image `_
- Part of the wrapper for SDL.
-* `sdl_mixer `_
- Part of the wrapper for SDL.
-* `sdl_net `_
- Part of the wrapper for SDL.
-* `sdl_ttf `_
- Part of the wrapper for SDL.
-* `smpeg `_
- Part of the wrapper for SDL.
-
-
GUI libraries
-------------
@@ -591,7 +568,7 @@ Data Compression and Archiving
Scientific computing
--------------------
-* `libsvm `_
+* `libsvm `_
Low level wrapper for `lib svm `_.
Nimble
diff --git a/doc/manual/stmts.txt b/doc/manual/stmts.txt
index 5b12848722..5e47110e9a 100644
--- a/doc/manual/stmts.txt
+++ b/doc/manual/stmts.txt
@@ -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
diff --git a/doc/manual/syntax.txt b/doc/manual/syntax.txt
index cf44eb5881..24644bce2d 100644
--- a/doc/manual/syntax.txt
+++ b/doc/manual/syntax.txt
@@ -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
diff --git a/doc/manual/type_bound_ops.txt b/doc/manual/type_bound_ops.txt
index efa5578d43..c707979feb 100644
--- a/doc/manual/type_bound_ops.txt
+++ b/doc/manual/type_bound_ops.txt
@@ -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.
diff --git a/doc/nimc.txt b/doc/nimc.txt
index cfbccc479d..fb1873539c 100644
--- a/doc/nimc.txt
+++ b/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::iterator x;
diff --git a/doc/nimsuggest.txt b/doc/nimsuggest.txt
new file mode 100644
index 0000000000..2b52196b92
--- /dev/null
+++ b/doc/nimsuggest.txt
@@ -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 `_
+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 `_. 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``).
diff --git a/doc/tools.txt b/doc/tools.txt
index 7f28308791..b0a45c5757 100644
--- a/doc/tools.txt
+++ b/doc/tools.txt
@@ -4,6 +4,11 @@ Tools available with Nim
The standard distribution ships with the following tools:
+- | `Nimsuggest for IDE support `_
+ | 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 `_
| How to generate a nice installer for your Nim program.
diff --git a/doc/tut1.txt b/doc/tut1.txt
index cb5a0c8dd5..58ace1dbe0 100644
--- a/doc/tut1.txt
+++ b/doc/tut1.txt
@@ -16,7 +16,7 @@ Introduction
-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
`_ 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 .. 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
diff --git a/koch.nim b/koch.nim
index 508d7e0070..55019b5446 100644
--- a/koch.nim
+++ b/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)
diff --git a/lib/core/macros.nim b/lib/core/macros.nim
index 5583748e0a..35f0f61c17 100644
--- a/lib/core/macros.nim
+++ b/lib/core/macros.nim
@@ -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)
diff --git a/lib/impure/db_sqlite.nim b/lib/impure/db_sqlite.nim
index 4be692f390..8536ab6f22 100644
--- a/lib/impure/db_sqlite.nim
+++ b/lib/impure/db_sqlite.nim
@@ -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)", [])
diff --git a/lib/impure/graphics.nim b/lib/impure/graphics.nim
index dfadb46eeb..814c0ebe14 100644
--- a/lib/impure/graphics.nim
+++ b/lib/impure/graphics.nim
@@ -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)
diff --git a/lib/impure/rdstdin.nim b/lib/impure/rdstdin.nim
index 55f8c5d32e..f4d00979c5 100644
--- a/lib/impure/rdstdin.nim
+++ b/lib/impure/rdstdin.nim
@@ -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)
diff --git a/lib/impure/re.nim b/lib/impure/re.nim
index 91381bda39..279f8aadd6 100644
--- a/lib/impure/re.nim
+++ b/lib/impure/re.nim
@@ -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 `_. 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:
diff --git a/lib/impure/ssl.nim b/lib/impure/ssl.nim
index bb7cfc0d38..d318a19798 100644
--- a/lib/impure/ssl.nim
+++ b/lib/impure/ssl.nim
@@ -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)
diff --git a/lib/impure/zipfiles.nim b/lib/impure/zipfiles.nim
index c22294061d..b41ca1e4bb 100644
--- a/lib/impure/zipfiles.nim
+++ b/lib/impure/zipfiles.nim
@@ -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")
diff --git a/lib/js/dom.nim b/lib/js/dom.nim
index 94f4fa29c5..b063fa8386 100644
--- a/lib/js/dom.nim
+++ b/lib/js/dom.nim
@@ -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
diff --git a/lib/nimbase.h b/lib/nimbase.h
index e9dad0bb7a..eea618bac1 100644
--- a/lib/nimbase.h
+++ b/lib/nimbase.h
@@ -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))
diff --git a/lib/packages/docutils/rst.nim b/lib/packages/docutils/rst.nim
index a4d095e68f..2ee94ba139 100644
--- a/lib/packages/docutils/rst.nim
+++ b/lib/packages/docutils/rst.nim
@@ -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)
diff --git a/lib/packages/docutils/rstgen.nim b/lib/packages/docutils/rstgen.nim
index da05be9bfb..9e96d8a632 100644
--- a/lib/packages/docutils/rstgen.nim
+++ b/lib/packages/docutils/rstgen.nim
@@ -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 = ""
-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, "$2", "$2\\label{$1}",
+ dispA(d.target, result, "$2", "$2\\label{$1}",
[id, term])
type
@@ -656,7 +656,7 @@ proc mergeIndexes*(dir: string): string =
result.add("API symbols
\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", "\\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$3",
+ dispA(d.target, result, "\n$3",
"\\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, "$3",
+ dispA(d.target, result, "$3",
"\\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,
- "$2\n",
+ "$2\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, "
", "\\includegraphics$2{$1}",
+ dispA(d.target, result, "
", "\\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,
- """
""",
"\\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, "$1", "\\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, "$1
", "$1", [tmp])
else:
dispA(d.target, result, "$2
", "$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, "$1
\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, "$1\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,
"" &
"" &
- "" &
+ "" &
"$1" &
- "
",
- "\\begin{description}$1\\end{description}\n",
+ "",
+ "\\begin{description}$1\\end{description}\n",
[tmp])
of rnField: renderField(d, n, result)
- of rnFieldName:
+ of rnFieldName:
renderAux(d, n, "$1: | ",
"\\item[$1:]", result)
- of rnFieldBody:
+ of rnFieldBody:
renderAux(d, n, "$1 | ", " $1\n", result)
- of rnIndex:
+ of rnIndex:
renderRstToOut(d, n.sons[2], result)
- of rnOptionList:
- renderAux(d, n, "",
+ of rnOptionList:
+ renderAux(d, n, "",
"\\begin{description}\n$1\\end{description}\n", result)
- of rnOptionListItem:
+ of rnOptionListItem:
renderAux(d, n, "$1
\n", "$1", result)
- of rnOptionGroup:
+ of rnOptionGroup:
renderAux(d, n, "$1 | ", "\\item[$1]", result)
- of rnDescription:
+ of rnDescription:
renderAux(d, n, "$1 | \n", " $1\n", result)
- of rnOption, rnOptionString, rnOptionArgument:
+ of rnOption, rnOptionString, rnOptionArgument:
doAssert false, "renderRstToOut"
of rnLiteralBlock:
- renderAux(d, n, "$1
\n",
+ renderAux(d, n, "$1
\n",
"\\begin{rstpre}\n$1\n\\end{rstpre}\n", result)
- of rnQuotedLiteralBlock:
+ of rnQuotedLiteralBlock:
doAssert false, "renderRstToOut"
- of rnLineBlock:
+ of rnLineBlock:
renderAux(d, n, "$1
", "$1\n\n", result)
- of rnLineBlockItem:
+ of rnLineBlockItem:
renderAux(d, n, "$1
", "$1\\\\\n", result)
- of rnBlockQuote:
- renderAux(d, n, "$1
\n",
+ of rnBlockQuote:
+ renderAux(d, n, "$1
\n",
"\\begin{quote}$1\\end{quote}\n", result)
- of rnTable, rnGridTable:
- renderAux(d, n,
- "",
+ of rnTable, rnGridTable:
+ renderAux(d, n,
+ "",
"\\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("")
renderAux(d, n, result)
result.add("
\n")
- of rnTableDataCell:
+ of rnTableDataCell:
renderAux(d, n, "$1 | ", "$1", result)
- of rnTableHeaderCell:
+ of rnTableHeaderCell:
renderAux(d, n, "$1 | ", "\\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,
"$1",
"$1\\ref{$2}", [tmp, rstnodeToRefname(n)])
- of rnStandaloneHyperlink:
- renderAux(d, n,
- "$1",
+ of rnStandaloneHyperlink:
+ renderAux(d, n,
+ "$1",
"\\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, "$1", "\\textbf{$1}", result)
of rnTripleEmphasis:
- renderAux(d, n, "$1",
+ renderAux(d, n, "$1",
"\\textbf{emph{$1}}", result)
of rnInterpretedText:
renderAux(d, n, "$1", "\\emph{$1}", result)
of rnIdx:
renderIndexTerm(d, n, result)
- of rnInlineLiteral:
- renderAux(d, n,
- "$1",
+ of rnInlineLiteral:
+ renderAux(d, n,
+ "$1",
"\\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)) ==
+ "Hello world!"
diff --git a/lib/posix/termios.nim b/lib/posix/termios.nim
index 830e8a2076..710b2fa6ba 100644
--- a/lib/posix/termios.nim
+++ b/lib/posix/termios.nim
@@ -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: "", 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: "".} = 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: "".}
-# 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: "".}
-# 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: "".}
-# 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: "".}
-# 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: "".}
-# 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: "".}
# Set the state of FD to *TERMIOS_P.
-# Values for OPTIONAL_ACTIONS (TCSA*) are in .
+# Values for OPTIONAL_ACTIONS (TCSA*) are in .
proc tcSetAttr*(fd: cint; optional_actions: cint; termios: ptr Termios): cint {.
importc: "tcsetattr", header: "".}
-# 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: "".}
-# 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: "".}
# 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: "".}
# Flush pending data on FD.
-# Values for QUEUE_SELECTOR (TC{I,O,IO}FLUSH) are in .
+# Values for QUEUE_SELECTOR (TC{I,O,IO}FLUSH) are in .
-proc tcFlush*(fd: cint; queue_selector: cint): cint {.importc: "tcflush",
+proc tcFlush*(fd: cint; queue_selector: cint): cint {.importc: "tcflush",
header: "".}
# Suspend or restart transmission on FD.
-# Values for ACTION (TC[IO]{OFF,ON}) are in .
+# Values for ACTION (TC[IO]{OFF,ON}) are in .
-proc tcFlow*(fd: cint; action: cint): cint {.importc: "tcflow",
+proc tcFlow*(fd: cint; action: cint): cint {.importc: "tcflow",
header: "".}
-# 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: "".}
diff --git a/lib/pure/actors.nim b/lib/pure/actors.nim
index 8c61ce7df3..294c24741e 100644
--- a/lib/pure/actors.nim
+++ b/lib/pure/actors.nim
@@ -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)
diff --git a/lib/pure/algorithm.nim b/lib/pure/algorithm.nim
index 68960e2e8d..f7ccb92348 100644
--- a/lib/pure/algorithm.nim
+++ b/lib/pure/algorithm.nim
@@ -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)
diff --git a/lib/pure/asyncdispatch.nim b/lib/pure/asyncdispatch.nim
index 27f77cef21..a4d7a16324 100644
--- a/lib/pure/asyncdispatch.nim
+++ b/lib/pure/asyncdispatch.nim
@@ -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")
diff --git a/lib/pure/asyncftpclient.nim b/lib/pure/asyncftpclient.nim
index 96f54b49e9..daf69d59f5 100644
--- a/lib/pure/asyncftpclient.nim
+++ b/lib/pure/asyncftpclient.nim
@@ -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()
diff --git a/lib/pure/asynchttpserver.nim b/lib/pure/asynchttpserver.nim
index 64242234cd..279cedb5d3 100644
--- a/lib/pure/asynchttpserver.nim
+++ b/lib/pure/asynchttpserver.nim
@@ -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.} =
diff --git a/lib/pure/asyncio.nim b/lib/pure/asyncio.nim
index f58bb43024..6ae2c608bd 100644
--- a/lib/pure/asyncio.nim
+++ b/lib/pure/asyncio.nim
@@ -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)
diff --git a/lib/pure/asyncnet.nim b/lib/pure/asyncnet.nim
index e7325e0d71..aadbde8240 100644
--- a/lib/pure/asyncnet.nim
+++ b/lib/pure/asyncnet.nim
@@ -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()
-
+
diff --git a/lib/pure/basic3d.nim b/lib/pure/basic3d.nim
index 18ebed67b2..5a943dd05d 100644
--- a/lib/pure/basic3d.nim
+++ b/lib/pure/basic3d.nim
@@ -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
diff --git a/lib/pure/collections/LockFreeHash.nim b/lib/pure/collections/LockFreeHash.nim
index c3954468ac..0df97c685e 100644
--- a/lib/pure/collections/LockFreeHash.nim
+++ b/lib/pure/collections/LockFreeHash.nim
@@ -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
diff --git a/lib/pure/collections/critbits.nim b/lib/pure/collections/critbits.nim
index 3d10e39aa3..7e3f238512 100644
--- a/lib/pure/collections/critbits.nim
+++ b/lib/pure/collections/critbits.nim
@@ -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"]
diff --git a/lib/pure/collections/intsets.nim b/lib/pure/collections/intsets.nim
index ae26c5af67..25f6616a62 100644
--- a/lib/pure/collections/intsets.nim
+++ b/lib/pure/collections/intsets.nim
@@ -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]
diff --git a/lib/pure/collections/sequtils.nim b/lib/pure/collections/sequtils.nim
index 3f37d1ef08..e9cd2cb3c5 100644
--- a/lib/pure/collections/sequtils.nim
+++ b/lib/pure/collections/sequtils.nim
@@ -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"
diff --git a/lib/pure/collections/sets.nim b/lib/pure/collections/sets.nim
index bd6c2dc20e..280e0eebab 100644
--- a/lib/pure/collections/sets.nim
+++ b/lib/pure/collections/sets.nim
@@ -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()
diff --git a/lib/pure/collections/tables.nim b/lib/pure/collections/tables.nim
index 5c4ac0401b..a9357ce67d 100644
--- a/lib/pure/collections/tables.nim
+++ b/lib/pure/collections/tables.nim
@@ -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)
diff --git a/lib/pure/concurrency/cpuload.nim b/lib/pure/concurrency/cpuload.nim
index c1796089a3..7ce5e01b77 100644
--- a/lib/pure/concurrency/cpuload.nim
+++ b/lib/pure/concurrency/cpuload.nim
@@ -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)
diff --git a/lib/pure/concurrency/threadpool.nim b/lib/pure/concurrency/threadpool.nim
index 9f1e53fb8a..a431691add 100644
--- a/lib/pure/concurrency/threadpool.nim
+++ b/lib/pure/concurrency/threadpool.nim
@@ -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:
diff --git a/lib/pure/cookies.nim b/lib/pure/cookies.nim
index 6247efed24..9983c4a041 100644
--- a/lib/pure/cookies.nim
+++ b/lib/pure/cookies.nim
@@ -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"
diff --git a/lib/pure/encodings.nim b/lib/pure/encodings.nim
index 25c7ad9ef7..2a61346150 100644
--- a/lib/pure/encodings.nim
+++ b/lib/pure/encodings.nim
@@ -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")
diff --git a/lib/pure/fsmonitor.nim b/lib/pure/fsmonitor.nim
index e6919b661b..83779eb9c7 100644
--- a/lib/pure/fsmonitor.nim
+++ b/lib/pure/fsmonitor.nim
@@ -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()
diff --git a/lib/pure/ftpclient.nim b/lib/pure/ftpclient.nim
index dc387b79c7..dd141eb017 100644
--- a/lib/pure/ftpclient.nim
+++ b/lib/pure/ftpclient.nim
@@ -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()
diff --git a/lib/pure/gentabs.nim b/lib/pure/gentabs.nim
index a6128efc96..84d0a44de0 100644
--- a/lib/pure/gentabs.nim
+++ b/lib/pure/gentabs.nim
@@ -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
diff --git a/lib/pure/hashes.nim b/lib/pure/hashes.nim
index a16342d446..2ce8ac7961 100644
--- a/lib/pure/hashes.nim
+++ b/lib/pure/hashes.nim
@@ -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
+
diff --git a/lib/pure/htmlgen.nim b/lib/pure/htmlgen.nim
index d712e53f36..e6c15371e7 100644
--- a/lib/pure/htmlgen.nim
+++ b/lib/pure/htmlgen.nim
@@ -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)) ==
+ """"""
+ assert form(action="test", `accept-charset` = "Content-Type") ==
+ """"""
diff --git a/lib/pure/htmlparser.nim b/lib/pure/htmlparser.nim
index 5e4eba4e53..9719181b8c 100644
--- a/lib/pure/htmlparser.nim
+++ b/lib/pure/htmlparser.nim
@@ -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] = @[]
diff --git a/lib/pure/httpclient.nim b/lib/pure/httpclient.nim
index 4c2580da03..9c27ecdabb 100644
--- a/lib/pure/httpclient.nim
+++ b/lib/pure/httpclient.nim
@@ -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.} =
diff --git a/lib/pure/httpserver.nim b/lib/pure/httpserver.nim
index 5efdbe297b..dc76c92284 100644
--- a/lib/pure/httpserver.nim
+++ b/lib/pure/httpserver.nim
@@ -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
diff --git a/lib/pure/json.nim b/lib/pure/json.nim
index 1617402c94..5d824d6f8e 100644
--- a/lib/pure/json.nim
+++ b/lib/pure/json.nim
@@ -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()
+ """
diff --git a/lib/pure/logging.nim b/lib/pure/logging.nim
index ca674af4b3..a2ea534726 100644
--- a/lib/pure/logging.nim
+++ b/lib/pure/logging.nim
@@ -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)
diff --git a/lib/pure/marshal.nim b/lib/pure/marshal.nim
index b63c334ff4..e0092f314b 100644
--- a/lib/pure/marshal.nim
+++ b/lib/pure/marshal.nim
@@ -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
diff --git a/lib/pure/math.nim b/lib/pure/math.nim
index aa64933fb9..cb58ea39b6 100644
--- a/lib/pure/math.nim
+++ b/lib/pure/math.nim
@@ -85,7 +85,7 @@ proc fac*(n: int): int {.noSideEffect.} =
proc isPowerOfTwo*(x: int): bool {.noSideEffect.} =
## returns true, if `x` is a power of two, false otherwise.
## Zero and negative numbers are not a power of two.
- return (x != 0) and ((x and (x - 1)) == 0)
+ return (x > 0) and ((x and (x - 1)) == 0)
proc nextPowerOfTwo*(x: int): int {.noSideEffect.} =
## returns `x` rounded up to the nearest power of two.
@@ -114,18 +114,23 @@ proc sum*[T](x: openArray[T]): T {.noSideEffect.} =
## If `x` is empty, 0 is returned.
for i in items(x): result = result + i
-proc mean*(x: openArray[float]): float {.noSideEffect.} =
- ## computes the mean of the elements in `x`.
- ## If `x` is empty, NaN is returned.
- result = sum(x) / toFloat(len(x))
+template toFloat(f: float): float = f
-proc variance*(x: openArray[float]): float {.noSideEffect.} =
+proc mean*[T](x: openArray[T]): float {.noSideEffect.} =
+ ## computes the mean of the elements in `x`, which are first converted to floats.
+ ## If `x` is empty, NaN is returned.
+ ## ``toFloat(x: T): float`` must be defined.
+ for i in items(x): result = result + toFloat(i)
+ result = result / toFloat(len(x))
+
+proc variance*[T](x: openArray[T]): float {.noSideEffect.} =
## computes the variance of the elements in `x`.
## If `x` is empty, NaN is returned.
+ ## ``toFloat(x: T): float`` must be defined.
result = 0.0
var m = mean(x)
- for i in 0 .. high(x):
- var diff = x[i] - m
+ for i in items(x):
+ var diff = toFloat(i) - m
result = result + diff*diff
result = result / toFloat(len(x))
@@ -372,7 +377,9 @@ when isMainModule and not defined(JS):
randomize(seed)
for i in 0..SIZE-1:
assert buf[i] == random(high(int)), "non deterministic random seeding"
- echo "random values equal after reseeding"
+
+ when not defined(testing):
+ echo "random values equal after reseeding"
# Check for no side effect annotation
proc mySqrt(num: float): float {.noSideEffect.} =
diff --git a/lib/pure/mersenne.nim b/lib/pure/mersenne.nim
index a6a781cb8c..74112e3045 100644
--- a/lib/pure/mersenne.nim
+++ b/lib/pure/mersenne.nim
@@ -32,7 +32,7 @@ proc getNum*(m: var MersenneTwister): int =
return int(y)
# Test
-when isMainModule:
+when not defined(testing) and isMainModule:
var mt = newMersenneTwister(2525)
for i in 0..99:
diff --git a/lib/pure/mimetypes.nim b/lib/pure/mimetypes.nim
index a52ba4ebe9..642419e644 100644
--- a/lib/pure/mimetypes.nim
+++ b/lib/pure/mimetypes.nim
@@ -518,5 +518,5 @@ proc register*(mimedb: var MimeDB, ext: string, mimetype: string) =
when isMainModule:
var m = newMimetypes()
- echo m.getMimetype("mp4")
- echo m.getExt("text/html")
+ assert m.getMimetype("mp4") == "video/mp4"
+ assert m.getExt("text/html") == "html"
diff --git a/lib/pure/oids.nim b/lib/pure/oids.nim
index 0dc8e3c15f..ac90dd16b2 100644
--- a/lib/pure/oids.nim
+++ b/lib/pure/oids.nim
@@ -88,6 +88,6 @@ proc generatedTime*(oid: Oid): Time =
bigEndian32(addr(tmp), addr(dummy))
result = Time(tmp)
-when isMainModule:
+when not defined(testing) and isMainModule:
let xo = genOid()
echo xo.generatedTime
diff --git a/lib/pure/osproc.nim b/lib/pure/osproc.nim
index eb7ad64bbf..dce0673baa 100644
--- a/lib/pure/osproc.nim
+++ b/lib/pure/osproc.nim
@@ -174,7 +174,7 @@ proc terminate*(p: Process) {.rtl, extern: "nosp$1", tags: [].}
proc kill*(p: Process) {.rtl, extern: "nosp$1", tags: [].}
## Kill the process `p`. On Posix OSes the procedure sends ``SIGKILL`` to
## the process. On Windows ``kill()`` is simply an alias for ``terminate()``.
-
+
proc running*(p: Process): bool {.rtl, extern: "nosp$1", tags: [].}
## Returns true iff the process `p` is still running. Returns immediately.
@@ -666,7 +666,7 @@ elif not defined(useNimRtl):
data.workingDir = workingDir
- when declared(posix_spawn) and not defined(useFork) and
+ when declared(posix_spawn) and not defined(useFork) and
not defined(useClone) and not defined(linux):
pid = startProcessAuxSpawn(data)
else:
@@ -823,7 +823,7 @@ elif not defined(useNimRtl):
discard execvp(data.sysCommand, data.sysArgs)
else:
when defined(uClibc):
- # uClibc environment (OpenWrt included) doesn't have the full execvpe
+ # uClibc environment (OpenWrt included) doesn't have the full execvpe
discard execve(data.sysCommand, data.sysArgs, data.sysEnv)
else:
discard execvpe(data.sysCommand, data.sysArgs, data.sysEnv)
@@ -864,9 +864,9 @@ elif not defined(useNimRtl):
raiseOsError(osLastError())
proc kill(p: Process) =
- if kill(p.id, SIGKILL) != 0'i32:
+ if kill(p.id, SIGKILL) != 0'i32:
raiseOsError(osLastError())
-
+
proc waitForExit(p: Process, timeout: int = -1): int =
#if waitPid(p.id, p.exitCode, 0) == int(p.id):
# ``waitPid`` fails if the process is not running anymore. But then
@@ -883,7 +883,7 @@ elif not defined(useNimRtl):
var ret = waitpid(p.id, p.exitCode, WNOHANG)
var b = ret == int(p.id)
if b: result = -1
- if p.exitCode == -3: result = -1
+ if not WIFEXITED(p.exitCode): result = -1
else: result = p.exitCode.int shr 8
proc createStream(stream: var Stream, handle: var FileHandle,
@@ -907,7 +907,7 @@ elif not defined(useNimRtl):
createStream(p.errStream, p.errHandle, fmRead)
return p.errStream
- proc csystem(cmd: cstring): cint {.nodecl, importc: "system",
+ proc csystem(cmd: cstring): cint {.nodecl, importc: "system",
header: "".}
proc execCmd(command: string): int =
diff --git a/lib/pure/parsecsv.nim b/lib/pure/parsecsv.nim
index f4943ed895..117d75cfa7 100644
--- a/lib/pure/parsecsv.nim
+++ b/lib/pure/parsecsv.nim
@@ -166,7 +166,7 @@ proc close*(my: var CsvParser) {.inline.} =
## closes the parser `my` and its associated input stream.
lexbase.close(my)
-when isMainModule:
+when not defined(testing) and isMainModule:
import os
var s = newFileStream(paramStr(1), fmRead)
if s == nil: quit("cannot open the file" & paramStr(1))
diff --git a/lib/pure/parsesql.nim b/lib/pure/parsesql.nim
index bb4ede7791..91917b1c53 100644
--- a/lib/pure/parsesql.nim
+++ b/lib/pure/parsesql.nim
@@ -1330,7 +1330,7 @@ proc renderSQL*(n: SqlNode): string =
result = ""
ra(n, result, 0)
-when isMainModule:
+when not defined(testing) and isMainModule:
echo(renderSQL(parseSQL(newStringStream("""
CREATE TYPE happiness AS ENUM ('happy', 'very happy', 'ecstatic');
CREATE TABLE holidays (
diff --git a/lib/pure/parseutils.nim b/lib/pure/parseutils.nim
index eb649a878c..c07b713de8 100644
--- a/lib/pure/parseutils.nim
+++ b/lib/pure/parseutils.nim
@@ -323,8 +323,12 @@ iterator interpolatedFragments*(s: string): tuple[kind: InterpolatedKind,
i = j
when isMainModule:
- for k, v in interpolatedFragments("$test{} $this is ${an{ example}} "):
- echo "(", k, ", \"", v, "\")"
+ import sequtils
+ let input = "$test{} $this is ${an{ example}} "
+ let expected = @[(ikVar, "test"), (ikStr, "{} "), (ikVar, "this"),
+ (ikStr, " is "), (ikExpr, "an{ example}"), (ikStr, " ")]
+ assert toSeq(interpolatedFragments(input)) == expected
+
var value = 0
discard parseHex("0x38", value)
assert value == 56
diff --git a/lib/pure/parsexml.nim b/lib/pure/parsexml.nim
index 2663c5b2f4..eb792f0862 100644
--- a/lib/pure/parsexml.nim
+++ b/lib/pure/parsexml.nim
@@ -628,7 +628,7 @@ proc next*(my: var XmlParser) =
my.kind = xmlError
my.state = stateNormal
-when isMainModule:
+when not defined(testing) and isMainModule:
import os
var s = newFileStream(paramStr(1), fmRead)
if s == nil: quit("cannot open the file" & paramStr(1))
diff --git a/lib/pure/pegs.nimfix b/lib/pure/pegs.nimfix
deleted file mode 100644
index 15bc953511..0000000000
--- a/lib/pure/pegs.nimfix
+++ /dev/null
@@ -1,1770 +0,0 @@
-#
-#
-# Nim's Runtime Library
-# (c) Copyright 2012 Andreas Rumpf
-#
-# See the file "copying.txt", included in this
-# distribution, for details about the copyright.
-#
-
-## Simple PEG (Parsing expression grammar) matching. Uses no memorization, but
-## uses superoperators and symbol inlining to improve performance. Note:
-## Matching performance is hopefully competitive with optimized regular
-## expression engines.
-##
-## .. include:: ../doc/pegdocs.txt
-##
-
-include "system/inclrtl"
-
-const
- useUnicode = true ## change this to deactivate proper UTF-8 support
-
-import
- strutils
-
-when useUnicode:
- import unicode
-
-const
- InlineThreshold = 5 ## number of leaves; -1 to disable inlining
- MaxSubpatterns* = 10 ## defines the maximum number of subpatterns that
- ## can be captured. More subpatterns cannot be captured!
-
-type
- PegKind = enum
- pkEmpty,
- pkAny, ## any character (.)
- pkAnyRune, ## any Unicode character (_)
- pkNewLine, ## CR-LF, LF, CR
- pkLetter, ## Unicode letter
- pkLower, ## Unicode lower case letter
- pkUpper, ## Unicode upper case letter
- pkTitle, ## Unicode title character
- pkWhitespace, ## Unicode whitespace character
- pkTerminal,
- pkTerminalIgnoreCase,
- pkTerminalIgnoreStyle,
- pkChar, ## single character to match
- pkCharChoice,
- pkNonTerminal,
- pkSequence, ## a b c ... --> Internal DSL: peg(a, b, c)
- pkOrderedChoice, ## a / b / ... --> Internal DSL: a / b or /[a, b, c]
- pkGreedyRep, ## a* --> Internal DSL: *a
- ## a+ --> (a a*)
- pkGreedyRepChar, ## x* where x is a single character (superop)
- pkGreedyRepSet, ## [set]* (superop)
- pkGreedyAny, ## .* or _* (superop)
- pkOption, ## a? --> Internal DSL: ?a
- pkAndPredicate, ## &a --> Internal DSL: &a
- pkNotPredicate, ## !a --> Internal DSL: !a
- pkCapture, ## {a} --> Internal DSL: capture(a)
- pkBackRef, ## $i --> Internal DSL: backref(i)
- pkBackRefIgnoreCase,
- pkBackRefIgnoreStyle,
- pkSearch, ## @a --> Internal DSL: !*a
- pkCapturedSearch, ## {@} a --> Internal DSL: !*\a
- pkRule, ## a <- b
- pkList, ## a, b
- pkStartAnchor ## ^ --> Internal DSL: startAnchor()
- NonTerminalFlag = enum
- ntDeclared, ntUsed
- NonTerminalObj = object ## represents a non terminal symbol
- name: string ## the name of the symbol
- line: int ## line the symbol has been declared/used in
- col: int ## column the symbol has been declared/used in
- flags: set[NonTerminalFlag] ## the nonterminal's flags
- rule: TNode ## the rule that the symbol refers to
- TNode {.shallow.} = object
- case kind: PegKind
- of pkEmpty..pkWhitespace: nil
- of pkTerminal, pkTerminalIgnoreCase, pkTerminalIgnoreStyle: term: string
- of pkChar, pkGreedyRepChar: ch: char
- of pkCharChoice, pkGreedyRepSet: charChoice: ref set[char]
- of pkNonTerminal: nt: PNonTerminal
- of pkBackRef..pkBackRefIgnoreStyle: index: range[0..MaxSubpatterns]
- else: sons: seq[TNode]
- PNonTerminal* = ref NonTerminalObj
- TPeg* = TNode
-
-block:
- type
- Peg = TNode
- NonTerminal = PNonTerminal
- {.deprecated: [TPeg: Peg, PNonTerminal: NonTerminal].}
-
-proc term*(t: string): TPeg {.nosideEffect, rtl, extern: "npegs$1Str".} =
- ## constructs a PEG from a terminal string
- if t.len != 1:
- result.kind = pkTerminal
- result.term = t
- else:
- result.kind = pkChar
- result.ch = t[0]
-
-proc termIgnoreCase*(t: string): TPeg {.nosideEffect, rtl, extern: "npegs$1".} =
- ## constructs a PEG from a terminal string; ignore case for matching
- result.kind = pkTerminalIgnoreCase
- result.term = t
-
-proc termIgnoreStyle*(t: string): TPeg {.nosideEffect, rtl, extern: "npegs$1".} =
- ## constructs a PEG from a terminal string; ignore style for matching
- result.kind = pkTerminalIgnoreStyle
- result.term = t
-
-proc term*(t: char): TPeg {.nosideEffect, rtl, extern: "npegs$1Char".} =
- ## constructs a PEG from a terminal char
- assert t != '\0'
- result.kind = pkChar
- result.ch = t
-
-proc charSet*(s: set[char]): TPeg {.nosideEffect, rtl, extern: "npegs$1".} =
- ## constructs a PEG from a character set `s`
- assert '\0' notin s
- result.kind = pkCharChoice
- new(result.charChoice)
- result.charChoice[] = s
-
-proc len(a: TPeg): int {.inline.} = return a.sons.len
-proc add(d: var TPeg, s: TPeg) {.inline.} = add(d.sons, s)
-
-proc addChoice(dest: var TPeg, elem: TPeg) =
- var L = dest.len-1
- if L >= 0 and dest.sons[L].kind == pkCharChoice:
- # caution! Do not introduce false aliasing here!
- case elem.kind
- of pkCharChoice:
- dest.sons[L] = charSet(dest.sons[L].charChoice[] + elem.charChoice[])
- of pkChar:
- dest.sons[L] = charSet(dest.sons[L].charChoice[] + {elem.ch})
- else: add(dest, elem)
- else: add(dest, elem)
-
-template multipleOp(k: PegKind, localOpt: expr) =
- result.kind = k
- result.sons = @[]
- for x in items(a):
- if x.kind == k:
- for y in items(x.sons):
- localOpt(result, y)
- else:
- localOpt(result, x)
- if result.len == 1:
- result = result.sons[0]
-
-proc `/`*(a: varargs[TPeg]): TPeg {.
- nosideEffect, rtl, extern: "npegsOrderedChoice".} =
- ## constructs an ordered choice with the PEGs in `a`
- multipleOp(pkOrderedChoice, addChoice)
-
-proc addSequence(dest: var TPeg, elem: TPeg) =
- var L = dest.len-1
- if L >= 0 and dest.sons[L].kind == pkTerminal:
- # caution! Do not introduce false aliasing here!
- case elem.kind
- of pkTerminal:
- dest.sons[L] = term(dest.sons[L].term & elem.term)
- of pkChar:
- dest.sons[L] = term(dest.sons[L].term & elem.ch)
- else: add(dest, elem)
- else: add(dest, elem)
-
-proc sequence*(a: varargs[TPeg]): TPeg {.
- nosideEffect, rtl, extern: "npegs$1".} =
- ## constructs a sequence with all the PEGs from `a`
- multipleOp(pkSequence, addSequence)
-
-proc `?`*(a: TPeg): TPeg {.nosideEffect, rtl, extern: "npegsOptional".} =
- ## constructs an optional for the PEG `a`
- if a.kind in {pkOption, pkGreedyRep, pkGreedyAny, pkGreedyRepChar,
- pkGreedyRepSet}:
- # a* ? --> a*
- # a? ? --> a?
- result = a
- else:
- result.kind = pkOption
- result.sons = @[a]
-
-proc `*`*(a: TPeg): TPeg {.nosideEffect, rtl, extern: "npegsGreedyRep".} =
- ## constructs a "greedy repetition" for the PEG `a`
- case a.kind
- of pkGreedyRep, pkGreedyRepChar, pkGreedyRepSet, pkGreedyAny, pkOption:
- assert false
- # produces endless loop!
- of pkChar:
- result.kind = pkGreedyRepChar
- result.ch = a.ch
- of pkCharChoice:
- result.kind = pkGreedyRepSet
- result.charChoice = a.charChoice # copying a reference suffices!
- of pkAny, pkAnyRune:
- result.kind = pkGreedyAny
- else:
- result.kind = pkGreedyRep
- result.sons = @[a]
-
-proc `!*`*(a: TPeg): TPeg {.nosideEffect, rtl, extern: "npegsSearch".} =
- ## constructs a "search" for the PEG `a`
- result.kind = pkSearch
- result.sons = @[a]
-
-proc `!*\`*(a: TPeg): TPeg {.noSideEffect, rtl,
- extern: "npgegsCapturedSearch".} =
- ## constructs a "captured search" for the PEG `a`
- result.kind = pkCapturedSearch
- result.sons = @[a]
-
-proc `+`*(a: TPeg): TPeg {.nosideEffect, rtl, extern: "npegsGreedyPosRep".} =
- ## constructs a "greedy positive repetition" with the PEG `a`
- return sequence(a, *a)
-
-proc `&`*(a: TPeg): TPeg {.nosideEffect, rtl, extern: "npegsAndPredicate".} =
- ## constructs an "and predicate" with the PEG `a`
- result.kind = pkAndPredicate
- result.sons = @[a]
-
-proc `!`*(a: TPeg): TPeg {.nosideEffect, rtl, extern: "npegsNotPredicate".} =
- ## constructs a "not predicate" with the PEG `a`
- result.kind = pkNotPredicate
- result.sons = @[a]
-
-proc any*: TPeg {.inline.} =
- ## constructs the PEG `any character`:idx: (``.``)
- result.kind = pkAny
-
-proc anyRune*: TPeg {.inline.} =
- ## constructs the PEG `any rune`:idx: (``_``)
- result.kind = pkAnyRune
-
-proc newLine*: TPeg {.inline.} =
- ## constructs the PEG `newline`:idx: (``\n``)
- result.kind = pkNewLine
-
-proc unicodeLetter*: TPeg {.inline.} =
- ## constructs the PEG ``\letter`` which matches any Unicode letter.
- result.kind = pkLetter
-
-proc unicodeLower*: TPeg {.inline.} =
- ## constructs the PEG ``\lower`` which matches any Unicode lowercase letter.
- result.kind = pkLower
-
-proc unicodeUpper*: TPeg {.inline.} =
- ## constructs the PEG ``\upper`` which matches any Unicode uppercase letter.
- result.kind = pkUpper
-
-proc unicodeTitle*: TPeg {.inline.} =
- ## constructs the PEG ``\title`` which matches any Unicode title letter.
- result.kind = pkTitle
-
-proc unicodeWhitespace*: TPeg {.inline.} =
- ## constructs the PEG ``\white`` which matches any Unicode
- ## whitespace character.
- result.kind = pkWhitespace
-
-proc startAnchor*: TPeg {.inline.} =
- ## constructs the PEG ``^`` which matches the start of the input.
- result.kind = pkStartAnchor
-
-proc endAnchor*: TPeg {.inline.} =
- ## constructs the PEG ``$`` which matches the end of the input.
- result = !any()
-
-proc capture*(a: TPeg): TPeg {.nosideEffect, rtl, extern: "npegsCapture".} =
- ## constructs a capture with the PEG `a`
- result.kind = pkCapture
- result.sons = @[a]
-
-proc backref*(index: range[1..MaxSubpatterns]): TPeg {.
- nosideEffect, rtl, extern: "npegs$1".} =
- ## constructs a back reference of the given `index`. `index` starts counting
- ## from 1.
- result.kind = pkBackRef
- result.index = index-1
-
-proc backrefIgnoreCase*(index: range[1..MaxSubpatterns]): TPeg {.
- nosideEffect, rtl, extern: "npegs$1".} =
- ## constructs a back reference of the given `index`. `index` starts counting
- ## from 1. Ignores case for matching.
- result.kind = pkBackRefIgnoreCase
- result.index = index-1
-
-proc backrefIgnoreStyle*(index: range[1..MaxSubpatterns]): TPeg {.
- nosideEffect, rtl, extern: "npegs$1".}=
- ## constructs a back reference of the given `index`. `index` starts counting
- ## from 1. Ignores style for matching.
- result.kind = pkBackRefIgnoreStyle
- result.index = index-1
-
-proc spaceCost(n: TPeg): int =
- case n.kind
- of pkEmpty: discard
- of pkTerminal, pkTerminalIgnoreCase, pkTerminalIgnoreStyle, pkChar,
- pkGreedyRepChar, pkCharChoice, pkGreedyRepSet,
- pkAny..pkWhitespace, pkGreedyAny:
- result = 1
- of pkNonTerminal:
- # we cannot inline a rule with a non-terminal
- result = InlineThreshold+1
- else:
- for i in 0..n.len-1:
- inc(result, spaceCost(n.sons[i]))
- if result >= InlineThreshold: break
-
-proc nonterminal*(n: PNonTerminal): TPeg {.
- nosideEffect, rtl, extern: "npegs$1".} =
- ## constructs a PEG that consists of the nonterminal symbol
- assert n != nil
- if ntDeclared in n.flags and spaceCost(n.rule) < InlineThreshold:
- when false: echo "inlining symbol: ", n.name
- result = n.rule # inlining of rule enables better optimizations
- else:
- result.kind = pkNonTerminal
- result.nt = n
-
-proc newNonTerminal*(name: string, line, column: int): PNonTerminal {.
- nosideEffect, rtl, extern: "npegs$1".} =
- ## constructs a nonterminal symbol
- new(result)
- result.name = name
- result.line = line
- result.col = column
-
-template letters*: expr =
- ## expands to ``charset({'A'..'Z', 'a'..'z'})``
- charSet({'A'..'Z', 'a'..'z'})
-
-template digits*: expr =
- ## expands to ``charset({'0'..'9'})``
- charSet({'0'..'9'})
-
-template whitespace*: expr =
- ## expands to ``charset({' ', '\9'..'\13'})``
- charSet({' ', '\9'..'\13'})
-
-template identChars*: expr =
- ## expands to ``charset({'a'..'z', 'A'..'Z', '0'..'9', '_'})``
- charSet({'a'..'z', 'A'..'Z', '0'..'9', '_'})
-
-template identStartChars*: expr =
- ## expands to ``charset({'A'..'Z', 'a'..'z', '_'})``
- charSet({'a'..'z', 'A'..'Z', '_'})
-
-template ident*: expr =
- ## same as ``[a-zA-Z_][a-zA-z_0-9]*``; standard identifier
- sequence(charSet({'a'..'z', 'A'..'Z', '_'}),
- *charSet({'a'..'z', 'A'..'Z', '0'..'9', '_'}))
-
-template natural*: expr =
- ## same as ``\d+``
- +digits
-
-# ------------------------- debugging -----------------------------------------
-
-proc esc(c: char, reserved = {'\0'..'\255'}): string =
- case c
- of '\b': result = "\\b"
- of '\t': result = "\\t"
- of '\c': result = "\\c"
- of '\L': result = "\\l"
- of '\v': result = "\\v"
- of '\f': result = "\\f"
- of '\e': result = "\\e"
- of '\a': result = "\\a"
- of '\\': result = "\\\\"
- of 'a'..'z', 'A'..'Z', '0'..'9', '_': result = $c
- elif c < ' ' or c >= '\128': result = '\\' & $ord(c)
- elif c in reserved: result = '\\' & c
- else: result = $c
-
-proc singleQuoteEsc(c: char): string = return "'" & esc(c, {'\''}) & "'"
-
-proc singleQuoteEsc(str: string): string =
- result = "'"
- for c in items(str): add result, esc(c, {'\''})
- add result, '\''
-
-proc charSetEscAux(cc: set[char]): string =
- const reserved = {'^', '-', ']'}
- result = ""
- var c1 = 0
- while c1 <= 0xff:
- if chr(c1) in cc:
- var c2 = c1
- while c2 < 0xff and chr(succ(c2)) in cc: inc(c2)
- if c1 == c2:
- add result, esc(chr(c1), reserved)
- elif c2 == succ(c1):
- add result, esc(chr(c1), reserved) & esc(chr(c2), reserved)
- else:
- add result, esc(chr(c1), reserved) & '-' & esc(chr(c2), reserved)
- c1 = c2
- inc(c1)
-
-proc charSetEsc(cc: set[char]): string =
- if card(cc) >= 128+64:
- result = "[^" & charSetEscAux({'\1'..'\xFF'} - cc) & ']'
- else:
- result = '[' & charSetEscAux(cc) & ']'
-
-proc toStrAux(r: TPeg, res: var string) =
- case r.kind
- of pkEmpty: add(res, "()")
- of pkAny: add(res, '.')
- of pkAnyRune: add(res, '_')
- of pkLetter: add(res, "\\letter")
- of pkLower: add(res, "\\lower")
- of pkUpper: add(res, "\\upper")
- of pkTitle: add(res, "\\title")
- of pkWhitespace: add(res, "\\white")
-
- of pkNewLine: add(res, "\\n")
- of pkTerminal: add(res, singleQuoteEsc(r.term))
- of pkTerminalIgnoreCase:
- add(res, 'i')
- add(res, singleQuoteEsc(r.term))
- of pkTerminalIgnoreStyle:
- add(res, 'y')
- add(res, singleQuoteEsc(r.term))
- of pkChar: add(res, singleQuoteEsc(r.ch))
- of pkCharChoice: add(res, charSetEsc(r.charChoice[]))
- of pkNonTerminal: add(res, r.nt.name)
- of pkSequence:
- add(res, '(')
- toStrAux(r.sons[0], res)
- for i in 1 .. high(r.sons):
- add(res, ' ')
- toStrAux(r.sons[i], res)
- add(res, ')')
- of pkOrderedChoice:
- add(res, '(')
- toStrAux(r.sons[0], res)
- for i in 1 .. high(r.sons):
- add(res, " / ")
- toStrAux(r.sons[i], res)
- add(res, ')')
- of pkGreedyRep:
- toStrAux(r.sons[0], res)
- add(res, '*')
- of pkGreedyRepChar:
- add(res, singleQuoteEsc(r.ch))
- add(res, '*')
- of pkGreedyRepSet:
- add(res, charSetEsc(r.charChoice[]))
- add(res, '*')
- of pkGreedyAny:
- add(res, ".*")
- of pkOption:
- toStrAux(r.sons[0], res)
- add(res, '?')
- of pkAndPredicate:
- add(res, '&')
- toStrAux(r.sons[0], res)
- of pkNotPredicate:
- add(res, '!')
- toStrAux(r.sons[0], res)
- of pkSearch:
- add(res, '@')
- toStrAux(r.sons[0], res)
- of pkCapturedSearch:
- add(res, "{@}")
- toStrAux(r.sons[0], res)
- of pkCapture:
- add(res, '{')
- toStrAux(r.sons[0], res)
- add(res, '}')
- of pkBackRef:
- add(res, '$')
- add(res, $r.index)
- of pkBackRefIgnoreCase:
- add(res, "i$")
- add(res, $r.index)
- of pkBackRefIgnoreStyle:
- add(res, "y$")
- add(res, $r.index)
- of pkRule:
- toStrAux(r.sons[0], res)
- add(res, " <- ")
- toStrAux(r.sons[1], res)
- of pkList:
- for i in 0 .. high(r.sons):
- toStrAux(r.sons[i], res)
- add(res, "\n")
- of pkStartAnchor:
- add(res, '^')
-
-proc `$` *(r: TPeg): string {.nosideEffect, rtl, extern: "npegsToString".} =
- ## converts a PEG to its string representation
- result = ""
- toStrAux(r, result)
-
-# --------------------- core engine -------------------------------------------
-
-type
- Captures* = object ## contains the captured substrings.
- matches: array[0..MaxSubpatterns-1, tuple[first, last: int]]
- ml: int
- origStart: int
-
-{.deprecated: [TCaptures: Captures].}
-
-proc bounds*(c: Captures,
- i: range[0..MaxSubpatterns-1]): tuple[first, last: int] =
- ## returns the bounds ``[first..last]`` of the `i`'th capture.
- result = c.matches[i]
-
-when not useUnicode:
- type
- Rune = char
- template fastRuneAt(s, i, ch: expr) =
- ch = s[i]
- inc(i)
- template runeLenAt(s, i: expr): expr = 1
-
- proc isAlpha(a: char): bool {.inline.} = return a in {'a'..'z','A'..'Z'}
- proc isUpper(a: char): bool {.inline.} = return a in {'A'..'Z'}
- proc isLower(a: char): bool {.inline.} = return a in {'a'..'z'}
- proc isTitle(a: char): bool {.inline.} = return false
- proc isWhiteSpace(a: char): bool {.inline.} = return a in {' ', '\9'..'\13'}
-
-proc rawMatch*(s: string, p: TPeg, start: int, c: var Captures): int {.
- nosideEffect, rtl, extern: "npegs$1".} =
- ## low-level matching proc that implements the PEG interpreter. Use this
- ## for maximum efficiency (every other PEG operation ends up calling this
- ## proc).
- ## Returns -1 if it does not match, else the length of the match
- case p.kind
- of pkEmpty: result = 0 # match of length 0
- of pkAny:
- if s[start] != '\0': result = 1
- else: result = -1
- of pkAnyRune:
- if s[start] != '\0':
- result = runeLenAt(s, start)
- else:
- result = -1
- of pkLetter:
- if s[start] != '\0':
- var a: Rune
- result = start
- fastRuneAt(s, result, a)
- if isAlpha(a): dec(result, start)
- else: result = -1
- else:
- result = -1
- of pkLower:
- if s[start] != '\0':
- var a: Rune
- result = start
- fastRuneAt(s, result, a)
- if isLower(a): dec(result, start)
- else: result = -1
- else:
- result = -1
- of pkUpper:
- if s[start] != '\0':
- var a: Rune
- result = start
- fastRuneAt(s, result, a)
- if isUpper(a): dec(result, start)
- else: result = -1
- else:
- result = -1
- of pkTitle:
- if s[start] != '\0':
- var a: Rune
- result = start
- fastRuneAt(s, result, a)
- if isTitle(a): dec(result, start)
- else: result = -1
- else:
- result = -1
- of pkWhitespace:
- if s[start] != '\0':
- var a: Rune
- result = start
- fastRuneAt(s, result, a)
- if isWhiteSpace(a): dec(result, start)
- else: result = -1
- else:
- result = -1
- of pkGreedyAny:
- result = len(s) - start
- of pkNewLine:
- if s[start] == '\L': result = 1
- elif s[start] == '\C':
- if s[start+1] == '\L': result = 2
- else: result = 1
- else: result = -1
- of pkTerminal:
- result = len(p.term)
- for i in 0..result-1:
- if p.term[i] != s[start+i]:
- result = -1
- break
- of pkTerminalIgnoreCase:
- var
- i = 0
- a, b: Rune
- result = start
- while i < len(p.term):
- fastRuneAt(p.term, i, a)
- fastRuneAt(s, result, b)
- if toLower(a) != toLower(b):
- result = -1
- break
- dec(result, start)
- of pkTerminalIgnoreStyle:
- var
- i = 0
- a, b: Rune
- result = start
- while i < len(p.term):
- while true:
- fastRuneAt(p.term, i, a)
- if a != Rune('_'): break
- while true:
- fastRuneAt(s, result, b)
- if b != Rune('_'): break
- if toLower(a) != toLower(b):
- result = -1
- break
- dec(result, start)
- of pkChar:
- if p.ch == s[start]: result = 1
- else: result = -1
- of pkCharChoice:
- if contains(p.charChoice[], s[start]): result = 1
- else: result = -1
- of pkNonTerminal:
- var oldMl = c.ml
- when false: echo "enter: ", p.nt.name
- result = rawMatch(s, p.nt.rule, start, c)
- when false: echo "leave: ", p.nt.name
- if result < 0: c.ml = oldMl
- of pkSequence:
- var oldMl = c.ml
- result = 0
- for i in 0..high(p.sons):
- var x = rawMatch(s, p.sons[i], start+result, c)
- if x < 0:
- c.ml = oldMl
- result = -1
- break
- else: inc(result, x)
- of pkOrderedChoice:
- var oldMl = c.ml
- for i in 0..high(p.sons):
- result = rawMatch(s, p.sons[i], start, c)
- if result >= 0: break
- c.ml = oldMl
- of pkSearch:
- var oldMl = c.ml
- result = 0
- while start+result < s.len:
- var x = rawMatch(s, p.sons[0], start+result, c)
- if x >= 0:
- inc(result, x)
- return
- inc(result)
- result = -1
- c.ml = oldMl
- of pkCapturedSearch:
- var idx = c.ml # reserve a slot for the subpattern
- inc(c.ml)
- result = 0
- while start+result < s.len:
- var x = rawMatch(s, p.sons[0], start+result, c)
- if x >= 0:
- if idx < MaxSubpatterns:
- c.matches[idx] = (start, start+result-1)
- #else: silently ignore the capture
- inc(result, x)
- return
- inc(result)
- result = -1
- c.ml = idx
- of pkGreedyRep:
- result = 0
- while true:
- var x = rawMatch(s, p.sons[0], start+result, c)
- # if x == 0, we have an endless loop; so the correct behaviour would be
- # not to break. But endless loops can be easily introduced:
- # ``(comment / \w*)*`` is such an example. Breaking for x == 0 does the
- # expected thing in this case.
- if x <= 0: break
- inc(result, x)
- of pkGreedyRepChar:
- result = 0
- var ch = p.ch
- while ch == s[start+result]: inc(result)
- of pkGreedyRepSet:
- result = 0
- while contains(p.charChoice[], s[start+result]): inc(result)
- of pkOption:
- result = max(0, rawMatch(s, p.sons[0], start, c))
- of pkAndPredicate:
- var oldMl = c.ml
- result = rawMatch(s, p.sons[0], start, c)
- if result >= 0: result = 0 # do not consume anything
- else: c.ml = oldMl
- of pkNotPredicate:
- var oldMl = c.ml
- result = rawMatch(s, p.sons[0], start, c)
- if result < 0: result = 0
- else:
- c.ml = oldMl
- result = -1
- of pkCapture:
- var idx = c.ml # reserve a slot for the subpattern
- inc(c.ml)
- result = rawMatch(s, p.sons[0], start, c)
- if result >= 0:
- if idx < MaxSubpatterns:
- c.matches[idx] = (start, start+result-1)
- #else: silently ignore the capture
- else:
- c.ml = idx
- of pkBackRef..pkBackRefIgnoreStyle:
- if p.index >= c.ml: return -1
- var (a, b) = c.matches[p.index]
- var n: TPeg
- n.kind = succ(pkTerminal, ord(p.kind)-ord(pkBackRef))
- n.term = s.substr(a, b)
- result = rawMatch(s, n, start, c)
- of pkStartAnchor:
- if c.origStart == start: result = 0
- else: result = -1
- of pkRule, pkList: assert false
-
-template fillMatches(s, caps, c: expr) =
- for k in 0..c.ml-1:
- caps[k] = substr(s, c.matches[k][0], c.matches[k][1])
-
-proc match*(s: string, pattern: TPeg, matches: var openArray[string],
- start = 0): bool {.nosideEffect, rtl, extern: "npegs$1Capture".} =
- ## returns ``true`` if ``s[start..]`` matches the ``pattern`` and
- ## the captured substrings in the array ``matches``. If it does not
- ## match, nothing is written into ``matches`` and ``false`` is
- ## returned.
- var c: Captures
- c.origStart = start
- result = rawMatch(s, pattern, start, c) == len(s) - start
- if result: fillMatches(s, matches, c)
-
-proc match*(s: string, pattern: TPeg,
- start = 0): bool {.nosideEffect, rtl, extern: "npegs$1".} =
- ## returns ``true`` if ``s`` matches the ``pattern`` beginning from ``start``.
- var c: Captures
- c.origStart = start
- result = rawMatch(s, pattern, start, c) == len(s)-start
-
-proc matchLen*(s: string, pattern: TPeg, matches: var openArray[string],
- start = 0): int {.nosideEffect, rtl, extern: "npegs$1Capture".} =
- ## the same as ``match``, but it returns the length of the match,
- ## if there is no match, -1 is returned. Note that a match length
- ## of zero can happen. It's possible that a suffix of `s` remains
- ## that does not belong to the match.
- var c: Captures
- c.origStart = start
- result = rawMatch(s, pattern, start, c)
- if result >= 0: fillMatches(s, matches, c)
-
-proc matchLen*(s: string, pattern: TPeg,
- start = 0): int {.nosideEffect, rtl, extern: "npegs$1".} =
- ## the same as ``match``, but it returns the length of the match,
- ## if there is no match, -1 is returned. Note that a match length
- ## of zero can happen. It's possible that a suffix of `s` remains
- ## that does not belong to the match.
- var c: Captures
- c.origStart = start
- result = rawMatch(s, pattern, start, c)
-
-proc find*(s: string, pattern: TPeg, matches: var openArray[string],
- start = 0): int {.nosideEffect, rtl, extern: "npegs$1Capture".} =
- ## returns the starting position of ``pattern`` in ``s`` and the captured
- ## substrings in the array ``matches``. If it does not match, nothing
- ## is written into ``matches`` and -1 is returned.
- var c: Captures
- c.origStart = start
- for i in start .. s.len-1:
- c.ml = 0
- if rawMatch(s, pattern, i, c) >= 0:
- fillMatches(s, matches, c)
- return i
- return -1
- # could also use the pattern here: (!P .)* P
-
-proc findBounds*(s: string, pattern: TPeg, matches: var openArray[string],
- start = 0): tuple[first, last: int] {.
- nosideEffect, rtl, extern: "npegs$1Capture".} =
- ## returns the starting position and end position of ``pattern`` in ``s``
- ## and the captured
- ## substrings in the array ``matches``. If it does not match, nothing
- ## is written into ``matches`` and (-1,0) is returned.
- var c: Captures
- c.origStart = start
- for i in start .. s.len-1:
- c.ml = 0
- var L = rawMatch(s, pattern, i, c)
- if L >= 0:
- fillMatches(s, matches, c)
- return (i, i+L-1)
- return (-1, 0)
-
-proc find*(s: string, pattern: TPeg,
- start = 0): int {.nosideEffect, rtl, extern: "npegs$1".} =
- ## returns the starting position of ``pattern`` in ``s``. If it does not
- ## match, -1 is returned.
- var c: Captures
- c.origStart = start
- for i in start .. s.len-1:
- if rawMatch(s, pattern, i, c) >= 0: return i
- return -1
-
-iterator findAll*(s: string, pattern: TPeg, start = 0): string =
- ## yields all matching *substrings* of `s` that match `pattern`.
- var c: Captures
- c.origStart = start
- var i = start
- while i < s.len:
- c.ml = 0
- var L = rawMatch(s, pattern, i, c)
- if L < 0:
- inc(i, 1)
- else:
- yield substr(s, i, i+L-1)
- inc(i, L)
-
-proc findAll*(s: string, pattern: TPeg, start = 0): seq[string] {.
- nosideEffect, rtl, extern: "npegs$1".} =
- ## returns all matching *substrings* of `s` that match `pattern`.
- ## If it does not match, @[] is returned.
- accumulateResult(findAll(s, pattern, start))
-
-when not defined(nimhygiene):
- {.pragma: inject.}
-
-template `=~`*(s: string, pattern: TPeg): bool =
- ## This calls ``match`` with an implicit declared ``matches`` array that
- ## can be used in the scope of the ``=~`` call:
- ##
- ## .. code-block:: nim
- ##
- ## if line =~ peg"\s* {\w+} \s* '=' \s* {\w+}":
- ## # matches a key=value pair:
- ## echo("Key: ", matches[0])
- ## echo("Value: ", matches[1])
- ## elif line =~ peg"\s*{'#'.*}":
- ## # matches a comment
- ## # note that the implicit ``matches`` array is different from the
- ## # ``matches`` array of the first branch
- ## echo("comment: ", matches[0])
- ## else:
- ## echo("syntax error")
- ##
- bind MaxSubpatterns
- when not declaredInScope(matches):
- var matches {.inject.}: array[0..MaxSubpatterns-1, string]
- match(s, pattern, matches)
-
-# ------------------------- more string handling ------------------------------
-
-proc contains*(s: string, pattern: TPeg, start = 0): bool {.
- nosideEffect, rtl, extern: "npegs$1".} =
- ## same as ``find(s, pattern, start) >= 0``
- return find(s, pattern, start) >= 0
-
-proc contains*(s: string, pattern: TPeg, matches: var openArray[string],
- start = 0): bool {.nosideEffect, rtl, extern: "npegs$1Capture".} =
- ## same as ``find(s, pattern, matches, start) >= 0``
- return find(s, pattern, matches, start) >= 0
-
-proc startsWith*(s: string, prefix: TPeg, start = 0): bool {.
- nosideEffect, rtl, extern: "npegs$1".} =
- ## returns true if `s` starts with the pattern `prefix`
- result = matchLen(s, prefix, start) >= 0
-
-proc endsWith*(s: string, suffix: TPeg, start = 0): bool {.
- nosideEffect, rtl, extern: "npegs$1".} =
- ## returns true if `s` ends with the pattern `prefix`
- var c: Captures
- c.origStart = start
- for i in start .. s.len-1:
- if rawMatch(s, suffix, i, c) == s.len - i: return true
-
-proc replacef*(s: string, sub: TPeg, by: string): string {.
- nosideEffect, rtl, extern: "npegs$1".} =
- ## Replaces `sub` in `s` by the string `by`. Captures can be accessed in `by`
- ## with the notation ``$i`` and ``$#`` (see strutils.`%`). Examples:
- ##
- ## .. code-block:: nim
- ## "var1=key; var2=key2".replace(peg"{\ident}'='{\ident}", "$1<-$2$2")
- ##
- ## Results in:
- ##
- ## .. code-block:: nim
- ##
- ## "var1<-keykey; val2<-key2key2"
- result = ""
- var i = 0
- var caps: array[0..MaxSubpatterns-1, string]
- var c: Captures
- while i < s.len:
- c.ml = 0
- var x = rawMatch(s, sub, i, c)
- if x <= 0:
- add(result, s[i])
- inc(i)
- else:
- fillMatches(s, caps, c)
- addf(result, by, caps)
- inc(i, x)
- add(result, substr(s, i))
-
-proc replace*(s: string, sub: TPeg, by = ""): string {.
- nosideEffect, rtl, extern: "npegs$1".} =
- ## Replaces `sub` in `s` by the string `by`. Captures cannot be accessed
- ## in `by`.
- result = ""
- var i = 0
- var c: Captures
- while i < s.len:
- var x = rawMatch(s, sub, i, c)
- if x <= 0:
- add(result, s[i])
- inc(i)
- else:
- add(result, by)
- inc(i, x)
- add(result, substr(s, i))
-
-proc parallelReplace*(s: string, subs: varargs[
- tuple[pattern: TPeg, repl: string]]): string {.
- nosideEffect, rtl, extern: "npegs$1".} =
- ## Returns a modified copy of `s` with the substitutions in `subs`
- ## applied in parallel.
- result = ""
- var i = 0
- var c: Captures
- var caps: array[0..MaxSubpatterns-1, string]
- while i < s.len:
- block searchSubs:
- for j in 0..high(subs):
- c.ml = 0
- var x = rawMatch(s, subs[j][0], i, c)
- if x > 0:
- fillMatches(s, caps, c)
- addf(result, subs[j][1], caps)
- inc(i, x)
- break searchSubs
- add(result, s[i])
- inc(i)
- # copy the rest:
- add(result, substr(s, i))
-
-proc transformFile*(infile, outfile: string,
- subs: varargs[tuple[pattern: TPeg, repl: string]]) {.
- rtl, extern: "npegs$1".} =
- ## reads in the file `infile`, performs a parallel replacement (calls
- ## `parallelReplace`) and writes back to `outfile`. Raises ``EIO`` if an
- ## error occurs. This is supposed to be used for quick scripting.
- var x = readFile(infile).string
- writeFile(outfile, x.parallelReplace(subs))
-
-iterator split*(s: string, sep: TPeg): string =
- ## Splits the string `s` into substrings.
- ##
- ## Substrings are separated by the PEG `sep`.
- ## Examples:
- ##
- ## .. code-block:: nim
- ## for word in split("00232this02939is39an22example111", peg"\d+"):
- ## writeln(stdout, word)
- ##
- ## Results in:
- ##
- ## .. code-block:: nim
- ## "this"
- ## "is"
- ## "an"
- ## "example"
- ##
- var c: Captures
- var
- first = 0
- last = 0
- while last < len(s):
- c.ml = 0
- var x = rawMatch(s, sep, last, c)
- if x > 0: inc(last, x)
- first = last
- while last < len(s):
- inc(last)
- c.ml = 0
- x = rawMatch(s, sep, last, c)
- if x > 0: break
- if first < last:
- yield substr(s, first, last-1)
-
-proc split*(s: string, sep: TPeg): seq[string] {.
- nosideEffect, rtl, extern: "npegs$1".} =
- ## Splits the string `s` into substrings.
- accumulateResult(split(s, sep))
-
-# ------------------- scanner -------------------------------------------------
-
-type
- TModifier = enum
- modNone,
- modVerbatim,
- modIgnoreCase,
- modIgnoreStyle
- TTokKind = enum ## enumeration of all tokens
- tkInvalid, ## invalid token
- tkEof, ## end of file reached
- tkAny, ## .
- tkAnyRune, ## _
- tkIdentifier, ## abc
- tkStringLit, ## "abc" or 'abc'
- tkCharSet, ## [^A-Z]
- tkParLe, ## '('
- tkParRi, ## ')'
- tkCurlyLe, ## '{'
- tkCurlyRi, ## '}'
- tkCurlyAt, ## '{@}'
- tkArrow, ## '<-'
- tkBar, ## '/'
- tkStar, ## '*'
- tkPlus, ## '+'
- tkAmp, ## '&'
- tkNot, ## '!'
- tkOption, ## '?'
- tkAt, ## '@'
- tkBuiltin, ## \identifier
- tkEscaped, ## \\
- tkBackref, ## '$'
- tkDollar, ## '$'
- tkHat ## '^'
-
- TToken {.final.} = object ## a token
- kind: TTokKind ## the type of the token
- modifier: TModifier
- literal: string ## the parsed (string) literal
- charset: set[char] ## if kind == tkCharSet
- index: int ## if kind == tkBackref
-
- PegLexer {.inheritable.} = object ## the lexer object.
- bufpos: int ## the current position within the buffer
- buf: cstring ## the buffer itself
- lineNumber: int ## the current line number
- lineStart: int ## index of last line start in buffer
- colOffset: int ## column to add
- filename: string
-
-const
- tokKindToStr: array[TTokKind, string] = [
- "invalid", "[EOF]", ".", "_", "identifier", "string literal",
- "character set", "(", ")", "{", "}", "{@}",
- "<-", "/", "*", "+", "&", "!", "?",
- "@", "built-in", "escaped", "$", "$", "^"
- ]
-
-proc handleCR(L: var PegLexer, pos: int): int =
- assert(L.buf[pos] == '\c')
- inc(L.lineNumber)
- result = pos+1
- if L.buf[result] == '\L': inc(result)
- L.lineStart = result
-
-proc handleLF(L: var PegLexer, pos: int): int =
- assert(L.buf[pos] == '\L')
- inc(L.lineNumber)
- result = pos+1
- L.lineStart = result
-
-proc init(L: var PegLexer, input, filename: string, line = 1, col = 0) =
- L.buf = input
- L.bufpos = 0
- L.lineNumber = line
- L.colOffset = col
- L.lineStart = 0
- L.filename = filename
-
-proc getColumn(L: PegLexer): int {.inline.} =
- result = abs(L.bufpos - L.lineStart) + L.colOffset
-
-proc getLine(L: PegLexer): int {.inline.} =
- result = L.lineNumber
-
-proc errorStr(L: PegLexer, msg: string, line = -1, col = -1): string =
- var line = if line < 0: getLine(L) else: line
- var col = if col < 0: getColumn(L) else: col
- result = "$1($2, $3) Error: $4" % [L.filename, $line, $col, msg]
-
-proc handleHexChar(c: var PegLexer, xi: var int) =
- case c.buf[c.bufpos]
- of '0'..'9':
- xi = (xi shl 4) or (ord(c.buf[c.bufpos]) - ord('0'))
- inc(c.bufpos)
- of 'a'..'f':
- xi = (xi shl 4) or (ord(c.buf[c.bufpos]) - ord('a') + 10)
- inc(c.bufpos)
- of 'A'..'F':
- xi = (xi shl 4) or (ord(c.buf[c.bufpos]) - ord('A') + 10)
- inc(c.bufpos)
- else: discard
-
-proc getEscapedChar(c: var PegLexer, tok: var TToken) =
- inc(c.bufpos)
- case c.buf[c.bufpos]
- of 'r', 'R', 'c', 'C':
- add(tok.literal, '\c')
- inc(c.bufpos)
- of 'l', 'L':
- add(tok.literal, '\L')
- inc(c.bufpos)
- of 'f', 'F':
- add(tok.literal, '\f')
- inc(c.bufpos)
- of 'e', 'E':
- add(tok.literal, '\e')
- inc(c.bufpos)
- of 'a', 'A':
- add(tok.literal, '\a')
- inc(c.bufpos)
- of 'b', 'B':
- add(tok.literal, '\b')
- inc(c.bufpos)
- of 'v', 'V':
- add(tok.literal, '\v')
- inc(c.bufpos)
- of 't', 'T':
- add(tok.literal, '\t')
- inc(c.bufpos)
- of 'x', 'X':
- inc(c.bufpos)
- var xi = 0
- handleHexChar(c, xi)
- handleHexChar(c, xi)
- if xi == 0: tok.kind = tkInvalid
- else: add(tok.literal, chr(xi))
- of '0'..'9':
- var val = ord(c.buf[c.bufpos]) - ord('0')
- inc(c.bufpos)
- var i = 1
- while (i <= 3) and (c.buf[c.bufpos] in {'0'..'9'}):
- val = val * 10 + ord(c.buf[c.bufpos]) - ord('0')
- inc(c.bufpos)
- inc(i)
- if val > 0 and val <= 255: add(tok.literal, chr(val))
- else: tok.kind = tkInvalid
- of '\0'..'\31':
- tok.kind = tkInvalid
- elif c.buf[c.bufpos] in strutils.Letters:
- tok.kind = tkInvalid
- else:
- add(tok.literal, c.buf[c.bufpos])
- inc(c.bufpos)
-
-proc skip(c: var PegLexer) =
- var pos = c.bufpos
- var buf = c.buf
- while true:
- case buf[pos]
- of ' ', '\t':
- inc(pos)
- of '#':
- while not (buf[pos] in {'\c', '\L', '\0'}): inc(pos)
- of '\c':
- pos = handleCR(c, pos)
- buf = c.buf
- of '\L':
- pos = handleLF(c, pos)
- buf = c.buf
- else:
- break # EndOfFile also leaves the loop
- c.bufpos = pos
-
-proc getString(c: var PegLexer, tok: var TToken) =
- tok.kind = tkStringLit
- var pos = c.bufpos + 1
- var buf = c.buf
- var quote = buf[pos-1]
- while true:
- case buf[pos]
- of '\\':
- c.bufpos = pos
- getEscapedChar(c, tok)
- pos = c.bufpos
- of '\c', '\L', '\0':
- tok.kind = tkInvalid
- break
- elif buf[pos] == quote:
- inc(pos)
- break
- else:
- add(tok.literal, buf[pos])
- inc(pos)
- c.bufpos = pos
-
-proc getDollar(c: var PegLexer, tok: var TToken) =
- var pos = c.bufpos + 1
- var buf = c.buf
- if buf[pos] in {'0'..'9'}:
- tok.kind = tkBackref
- tok.index = 0
- while buf[pos] in {'0'..'9'}:
- tok.index = tok.index * 10 + ord(buf[pos]) - ord('0')
- inc(pos)
- else:
- tok.kind = tkDollar
- c.bufpos = pos
-
-proc getCharSet(c: var PegLexer, tok: var TToken) =
- tok.kind = tkCharSet
- tok.charset = {}
- var pos = c.bufpos + 1
- var buf = c.buf
- var caret = false
- if buf[pos] == '^':
- inc(pos)
- caret = true
- while true:
- var ch: char
- case buf[pos]
- of ']':
- inc(pos)
- break
- of '\\':
- c.bufpos = pos
- getEscapedChar(c, tok)
- pos = c.bufpos
- ch = tok.literal[tok.literal.len-1]
- of '\C', '\L', '\0':
- tok.kind = tkInvalid
- break
- else:
- ch = buf[pos]
- inc(pos)
- incl(tok.charset, ch)
- if buf[pos] == '-':
- if buf[pos+1] == ']':
- incl(tok.charset, '-')
- inc(pos)
- else:
- inc(pos)
- var ch2: char
- case buf[pos]
- of '\\':
- c.bufpos = pos
- getEscapedChar(c, tok)
- pos = c.bufpos
- ch2 = tok.literal[tok.literal.len-1]
- of '\C', '\L', '\0':
- tok.kind = tkInvalid
- break
- else:
- ch2 = buf[pos]
- inc(pos)
- for i in ord(ch)+1 .. ord(ch2):
- incl(tok.charset, chr(i))
- c.bufpos = pos
- if caret: tok.charset = {'\1'..'\xFF'} - tok.charset
-
-proc getSymbol(c: var PegLexer, tok: var TToken) =
- var pos = c.bufpos
- var buf = c.buf
- while true:
- add(tok.literal, buf[pos])
- inc(pos)
- if buf[pos] notin strutils.IdentChars: break
- c.bufpos = pos
- tok.kind = tkIdentifier
-
-proc getBuiltin(c: var PegLexer, tok: var TToken) =
- if c.buf[c.bufpos+1] in strutils.Letters:
- inc(c.bufpos)
- getSymbol(c, tok)
- tok.kind = tkBuiltin
- else:
- tok.kind = tkEscaped
- getEscapedChar(c, tok) # may set tok.kind to tkInvalid
-
-proc getTok(c: var PegLexer, tok: var TToken) =
- tok.kind = tkInvalid
- tok.modifier = modNone
- setLen(tok.literal, 0)
- skip(c)
- case c.buf[c.bufpos]
- of '{':
- inc(c.bufpos)
- if c.buf[c.bufpos] == '@' and c.buf[c.bufpos+1] == '}':
- tok.kind = tkCurlyAt
- inc(c.bufpos, 2)
- add(tok.literal, "{@}")
- else:
- tok.kind = tkCurlyLe
- add(tok.literal, '{')
- of '}':
- tok.kind = tkCurlyRi
- inc(c.bufpos)
- add(tok.literal, '}')
- of '[':
- getCharSet(c, tok)
- of '(':
- tok.kind = tkParLe
- inc(c.bufpos)
- add(tok.literal, '(')
- of ')':
- tok.kind = tkParRi
- inc(c.bufpos)
- add(tok.literal, ')')
- of '.':
- tok.kind = tkAny
- inc(c.bufpos)
- add(tok.literal, '.')
- of '_':
- tok.kind = tkAnyRune
- inc(c.bufpos)
- add(tok.literal, '_')
- of '\\':
- getBuiltin(c, tok)
- of '\'', '"': getString(c, tok)
- of '$': getDollar(c, tok)
- of '\0':
- tok.kind = tkEof
- tok.literal = "[EOF]"
- of 'a'..'z', 'A'..'Z', '\128'..'\255':
- getSymbol(c, tok)
- if c.buf[c.bufpos] in {'\'', '"'} or
- c.buf[c.bufpos] == '$' and c.buf[c.bufpos+1] in {'0'..'9'}:
- case tok.literal
- of "i": tok.modifier = modIgnoreCase
- of "y": tok.modifier = modIgnoreStyle
- of "v": tok.modifier = modVerbatim
- else: discard
- setLen(tok.literal, 0)
- if c.buf[c.bufpos] == '$':
- getDollar(c, tok)
- else:
- getString(c, tok)
- if tok.modifier == modNone: tok.kind = tkInvalid
- of '+':
- tok.kind = tkPlus
- inc(c.bufpos)
- add(tok.literal, '+')
- of '*':
- tok.kind = tkStar
- inc(c.bufpos)
- add(tok.literal, '+')
- of '<':
- if c.buf[c.bufpos+1] == '-':
- inc(c.bufpos, 2)
- tok.kind = tkArrow
- add(tok.literal, "<-")
- else:
- add(tok.literal, '<')
- of '/':
- tok.kind = tkBar
- inc(c.bufpos)
- add(tok.literal, '/')
- of '?':
- tok.kind = tkOption
- inc(c.bufpos)
- add(tok.literal, '?')
- of '!':
- tok.kind = tkNot
- inc(c.bufpos)
- add(tok.literal, '!')
- of '&':
- tok.kind = tkAmp
- inc(c.bufpos)
- add(tok.literal, '!')
- of '@':
- tok.kind = tkAt
- inc(c.bufpos)
- add(tok.literal, '@')
- if c.buf[c.bufpos] == '@':
- tok.kind = tkCurlyAt
- inc(c.bufpos)
- add(tok.literal, '@')
- of '^':
- tok.kind = tkHat
- inc(c.bufpos)
- add(tok.literal, '^')
- else:
- add(tok.literal, c.buf[c.bufpos])
- inc(c.bufpos)
-
-proc arrowIsNextTok(c: PegLexer): bool =
- # the only look ahead we need
- var pos = c.bufpos
- while c.buf[pos] in {'\t', ' '}: inc(pos)
- result = c.buf[pos] == '<' and c.buf[pos+1] == '-'
-
-# ----------------------------- parser ----------------------------------------
-
-type
- EInvalidPeg* = object of ValueError ## raised if an invalid
- ## PEG has been detected
- PegParser = object of PegLexer ## the PEG parser object
- tok: TToken
- nonterms: seq[PNonTerminal]
- modifier: TModifier
- captures: int
- identIsVerbatim: bool
- skip: TPeg
-
-proc pegError(p: PegParser, msg: string, line = -1, col = -1) =
- var e: ref EInvalidPeg
- new(e)
- e.msg = errorStr(p, msg, line, col)
- raise e
-
-proc getTok(p: var PegParser) =
- getTok(p, p.tok)
- if p.tok.kind == tkInvalid: pegError(p, "invalid token")
-
-proc eat(p: var PegParser, kind: TTokKind) =
- if p.tok.kind == kind: getTok(p)
- else: pegError(p, tokKindToStr[kind] & " expected")
-
-proc parseExpr(p: var PegParser): TPeg
-
-proc getNonTerminal(p: var PegParser, name: string): PNonTerminal =
- for i in 0..high(p.nonterms):
- result = p.nonterms[i]
- if cmpIgnoreStyle(result.name, name) == 0: return
- # forward reference:
- result = newNonTerminal(name, getLine(p), getColumn(p))
- add(p.nonterms, result)
-
-proc modifiedTerm(s: string, m: TModifier): TPeg =
- case m
- of modNone, modVerbatim: result = term(s)
- of modIgnoreCase: result = termIgnoreCase(s)
- of modIgnoreStyle: result = termIgnoreStyle(s)
-
-proc modifiedBackref(s: int, m: TModifier): TPeg =
- case m
- of modNone, modVerbatim: result = backref(s)
- of modIgnoreCase: result = backrefIgnoreCase(s)
- of modIgnoreStyle: result = backrefIgnoreStyle(s)
-
-proc builtin(p: var PegParser): TPeg =
- # do not use "y", "skip" or "i" as these would be ambiguous
- case p.tok.literal
- of "n": result = newLine()
- of "d": result = charSet({'0'..'9'})
- of "D": result = charSet({'\1'..'\xff'} - {'0'..'9'})
- of "s": result = charSet({' ', '\9'..'\13'})
- of "S": result = charSet({'\1'..'\xff'} - {' ', '\9'..'\13'})
- of "w": result = charSet({'a'..'z', 'A'..'Z', '_', '0'..'9'})
- of "W": result = charSet({'\1'..'\xff'} - {'a'..'z','A'..'Z','_','0'..'9'})
- of "a": result = charSet({'a'..'z', 'A'..'Z'})
- of "A": result = charSet({'\1'..'\xff'} - {'a'..'z', 'A'..'Z'})
- of "ident": result = pegs.ident
- of "letter": result = unicodeLetter()
- of "upper": result = unicodeUpper()
- of "lower": result = unicodeLower()
- of "title": result = unicodeTitle()
- of "white": result = unicodeWhitespace()
- else: pegError(p, "unknown built-in: " & p.tok.literal)
-
-proc token(terminal: TPeg, p: PegParser): TPeg =
- if p.skip.kind == pkEmpty: result = terminal
- else: result = sequence(p.skip, terminal)
-
-proc primary(p: var PegParser): TPeg =
- case p.tok.kind
- of tkAmp:
- getTok(p)
- return &primary(p)
- of tkNot:
- getTok(p)
- return !primary(p)
- of tkAt:
- getTok(p)
- return !*primary(p)
- of tkCurlyAt:
- getTok(p)
- return !*\primary(p).token(p)
- else: discard
- case p.tok.kind
- of tkIdentifier:
- if p.identIsVerbatim:
- var m = p.tok.modifier
- if m == modNone: m = p.modifier
- result = modifiedTerm(p.tok.literal, m).token(p)
- getTok(p)
- elif not arrowIsNextTok(p):
- var nt = getNonTerminal(p, p.tok.literal)
- incl(nt.flags, ntUsed)
- result = nonterminal(nt).token(p)
- getTok(p)
- else:
- pegError(p, "expression expected, but found: " & p.tok.literal)
- of tkStringLit:
- var m = p.tok.modifier
- if m == modNone: m = p.modifier
- result = modifiedTerm(p.tok.literal, m).token(p)
- getTok(p)
- of tkCharSet:
- if '\0' in p.tok.charset:
- pegError(p, "binary zero ('\\0') not allowed in character class")
- result = charSet(p.tok.charset).token(p)
- getTok(p)
- of tkParLe:
- getTok(p)
- result = parseExpr(p)
- eat(p, tkParRi)
- of tkCurlyLe:
- getTok(p)
- result = capture(parseExpr(p)).token(p)
- eat(p, tkCurlyRi)
- inc(p.captures)
- of tkAny:
- result = any().token(p)
- getTok(p)
- of tkAnyRune:
- result = anyRune().token(p)
- getTok(p)
- of tkBuiltin:
- result = builtin(p).token(p)
- getTok(p)
- of tkEscaped:
- result = term(p.tok.literal[0]).token(p)
- getTok(p)
- of tkDollar:
- result = endAnchor()
- getTok(p)
- of tkHat:
- result = startAnchor()
- getTok(p)
- of tkBackref:
- var m = p.tok.modifier
- if m == modNone: m = p.modifier
- result = modifiedBackref(p.tok.index, m).token(p)
- if p.tok.index < 0 or p.tok.index > p.captures:
- pegError(p, "invalid back reference index: " & $p.tok.index)
- getTok(p)
- else:
- pegError(p, "expression expected, but found: " & p.tok.literal)
- getTok(p) # we must consume a token here to prevent endless loops!
- while true:
- case p.tok.kind
- of tkOption:
- result = ?result
- getTok(p)
- of tkStar:
- result = *result
- getTok(p)
- of tkPlus:
- result = +result
- getTok(p)
- else: break
-
-proc seqExpr(p: var PegParser): TPeg =
- result = primary(p)
- while true:
- case p.tok.kind
- of tkAmp, tkNot, tkAt, tkStringLit, tkCharSet, tkParLe, tkCurlyLe,
- tkAny, tkAnyRune, tkBuiltin, tkEscaped, tkDollar, tkBackref,
- tkHat, tkCurlyAt:
- result = sequence(result, primary(p))
- of tkIdentifier:
- if not arrowIsNextTok(p):
- result = sequence(result, primary(p))
- else: break
- else: break
-
-proc parseExpr(p: var PegParser): TPeg =
- result = seqExpr(p)
- while p.tok.kind == tkBar:
- getTok(p)
- result = result / seqExpr(p)
-
-proc parseRule(p: var PegParser): PNonTerminal =
- if p.tok.kind == tkIdentifier and arrowIsNextTok(p):
- result = getNonTerminal(p, p.tok.literal)
- if ntDeclared in result.flags:
- pegError(p, "attempt to redefine: " & result.name)
- result.line = getLine(p)
- result.col = getColumn(p)
- getTok(p)
- eat(p, tkArrow)
- result.rule = parseExpr(p)
- incl(result.flags, ntDeclared) # NOW inlining may be attempted
- else:
- pegError(p, "rule expected, but found: " & p.tok.literal)
-
-proc rawParse(p: var PegParser): TPeg =
- ## parses a rule or a PEG expression
- while p.tok.kind == tkBuiltin:
- case p.tok.literal
- of "i":
- p.modifier = modIgnoreCase
- getTok(p)
- of "y":
- p.modifier = modIgnoreStyle
- getTok(p)
- of "skip":
- getTok(p)
- p.skip = ?primary(p)
- else: break
- if p.tok.kind == tkIdentifier and arrowIsNextTok(p):
- result = parseRule(p).rule
- while p.tok.kind != tkEof:
- discard parseRule(p)
- else:
- p.identIsVerbatim = true
- result = parseExpr(p)
- if p.tok.kind != tkEof:
- pegError(p, "EOF expected, but found: " & p.tok.literal)
- for i in 0..high(p.nonterms):
- var nt = p.nonterms[i]
- if ntDeclared notin nt.flags:
- pegError(p, "undeclared identifier: " & nt.name, nt.line, nt.col)
- elif ntUsed notin nt.flags and i > 0:
- pegError(p, "unused rule: " & nt.name, nt.line, nt.col)
-
-proc parsePeg*(pattern: string, filename = "pattern", line = 1, col = 0): TPeg =
- ## constructs a Peg object from `pattern`. `filename`, `line`, `col` are
- ## used for error messages, but they only provide start offsets. `parsePeg`
- ## keeps track of line and column numbers within `pattern`.
- var p: PegParser
- init(PegLexer(p), pattern, filename, line, col)
- p.tok.kind = tkInvalid
- p.tok.modifier = modNone
- p.tok.literal = ""
- p.tok.charset = {}
- p.nonterms = @[]
- p.identIsVerbatim = false
- getTok(p)
- result = rawParse(p)
-
-proc peg*(pattern: string): TPeg =
- ## constructs a Peg object from the `pattern`. The short name has been
- ## chosen to encourage its use as a raw string modifier::
- ##
- ## peg"{\ident} \s* '=' \s* {.*}"
- result = parsePeg(pattern, "pattern")
-
-proc escapePeg*(s: string): string =
- ## escapes `s` so that it is matched verbatim when used as a peg.
- result = ""
- var inQuote = false
- for c in items(s):
- case c
- of '\0'..'\31', '\'', '"', '\\':
- if inQuote:
- result.add('\'')
- inQuote = false
- result.add("\\x")
- result.add(toHex(ord(c), 2))
- else:
- if not inQuote:
- result.add('\'')
- inQuote = true
- result.add(c)
- if inQuote: result.add('\'')
-
-when isMainModule:
- assert escapePeg("abc''def'") == r"'abc'\x27\x27'def'\x27"
- assert match("(a b c)", peg"'(' @ ')'")
- assert match("W_HI_Le", peg"\y 'while'")
- assert(not match("W_HI_L", peg"\y 'while'"))
- assert(not match("W_HI_Le", peg"\y v'while'"))
- assert match("W_HI_Le", peg"y'while'")
-
- assert($ +digits == $peg"\d+")
- assert "0158787".match(peg"\d+")
- assert "ABC 0232".match(peg"\w+\s+\d+")
- assert "ABC".match(peg"\d+ / \w+")
-
- for word in split("00232this02939is39an22example111", peg"\d+"):
- writeln(stdout, word)
-
- assert matchLen("key", ident) == 3
-
- var pattern = sequence(ident, *whitespace, term('='), *whitespace, ident)
- assert matchLen("key1= cal9", pattern) == 11
-
- var ws = newNonTerminal("ws", 1, 1)
- ws.rule = *whitespace
-
- var expr = newNonTerminal("expr", 1, 1)
- expr.rule = sequence(capture(ident), *sequence(
- nonterminal(ws), term('+'), nonterminal(ws), nonterminal(expr)))
-
- var c: Captures
- var s = "a+b + c +d+e+f"
- assert rawMatch(s, expr.rule, 0, c) == len(s)
- var a = ""
- for i in 0..c.ml-1:
- a.add(substr(s, c.matches[i][0], c.matches[i][1]))
- assert a == "abcdef"
- #echo expr.rule
-
- #const filename = "lib/devel/peg/grammar.txt"
- #var grammar = parsePeg(newFileStream(filename, fmRead), filename)
- #echo "a <- [abc]*?".match(grammar)
- assert find("_____abc_______", term("abc"), 2) == 5
- assert match("_______ana", peg"A <- 'ana' / . A")
- assert match("abcs%%%", peg"A <- ..A / .A / '%'")
-
- var matches: array[0..MaxSubpatterns-1, string]
- if "abc" =~ peg"{'a'}'bc' 'xyz' / {\ident}":
- assert matches[0] == "abc"
- else:
- assert false
-
- var g2 = peg"""S <- A B / C D
- A <- 'a'+
- B <- 'b'+
- C <- 'c'+
- D <- 'd'+
- """
- assert($g2 == "((A B) / (C D))")
- assert match("cccccdddddd", g2)
- assert("var1=key; var2=key2".replacef(peg"{\ident}'='{\ident}", "$1<-$2$2") ==
- "var1<-keykey; var2<-key2key2")
- assert("var1=key; var2=key2".replace(peg"{\ident}'='{\ident}", "$1<-$2$2") ==
- "$1<-$2$2; $1<-$2$2")
- assert "var1=key; var2=key2".endsWith(peg"{\ident}'='{\ident}")
-
- if "aaaaaa" =~ peg"'aa' !. / ({'a'})+":
- assert matches[0] == "a"
- else:
- assert false
-
- if match("abcdefg", peg"c {d} ef {g}", matches, 2):
- assert matches[0] == "d"
- assert matches[1] == "g"
- else:
- assert false
-
- for x in findAll("abcdef", peg".", 3):
- echo x
-
- for x in findAll("abcdef", peg"^{.}", 3):
- assert x == "d"
-
- if "f(a, b)" =~ peg"{[0-9]+} / ({\ident} '(' {@} ')')":
- assert matches[0] == "f"
- assert matches[1] == "a, b"
- else:
- assert false
-
- assert match("eine übersicht und außerdem", peg"(\letter \white*)+")
- # ß is not a lower cased letter?!
- assert match("eine übersicht und auerdem", peg"(\lower \white*)+")
- assert match("EINE ÜBERSICHT UND AUSSERDEM", peg"(\upper \white*)+")
- assert(not match("456678", peg"(\letter)+"))
-
- assert("var1 = key; var2 = key2".replacef(
- peg"\skip(\s*) {\ident}'='{\ident}", "$1<-$2$2") ==
- "var1<-keykey;var2<-key2key2")
-
- assert match("prefix/start", peg"^start$", 7)
-
diff --git a/lib/pure/redis.nim b/lib/pure/redis.nim
index 9177ddee59..aa2e0f9bdd 100644
--- a/lib/pure/redis.nim
+++ b/lib/pure/redis.nim
@@ -1080,7 +1080,7 @@ proc assertListsIdentical(listA, listB: seq[string]) =
assert(item == listB[i])
i = i + 1
-when isMainModule:
+when not defined(testing) and isMainModule:
when false:
var r = open()
diff --git a/lib/pure/romans.nim b/lib/pure/romans.nim
index 79fb755261..0c182843a1 100644
--- a/lib/pure/romans.nim
+++ b/lib/pure/romans.nim
@@ -44,16 +44,13 @@ proc decimalToRoman*(number: range[1..3_999]): string =
("XC", 90), ("L", 50), ("XL", 40), ("X", 10), ("IX", 9),
("V", 5), ("IV", 4), ("I", 1)]
result = ""
- var decVal = number
+ var decVal: int = number
for key, val in items(romanComposites):
while decVal >= val:
dec(decVal, val)
result.add(key)
when isMainModule:
- import math
- randomize()
- for i in 1 .. 100:
- var rnd = 1 + random(3990)
- assert rnd == rnd.decimalToRoman.romanToDecimal
+ for i in 1 .. 3_999:
+ assert i == i.decimalToRoman.romanToDecimal
diff --git a/lib/pure/selectors.nim b/lib/pure/selectors.nim
index b6bc9dd3a4..6901ecf588 100644
--- a/lib/pure/selectors.nim
+++ b/lib/pure/selectors.nim
@@ -296,7 +296,7 @@ proc contains*(s: Selector, key: SelectorKey): bool =
TReadyInfo: ReadyInfo, PSelector: Selector].}
-when isMainModule and not defined(nimdoc):
+when not defined(testing) and isMainModule and not defined(nimdoc):
# Select()
import sockets
type
diff --git a/lib/pure/smtp.nim b/lib/pure/smtp.nim
index 81198f9e1f..c1bc259a54 100644
--- a/lib/pure/smtp.nim
+++ b/lib/pure/smtp.nim
@@ -253,7 +253,7 @@ proc close*(smtp: AsyncSmtp) {.async.} =
await smtp.sock.send("QUIT\c\L")
smtp.sock.close()
-when isMainModule:
+when not defined(testing) and isMainModule:
#var msg = createMessage("Test subject!",
# "Hello, my name is dom96.\n What\'s yours?", @["dominik@localhost"])
#echo(msg)
diff --git a/lib/pure/strtabs.nim b/lib/pure/strtabs.nim
index 727d5a386d..7fdd994f21 100644
--- a/lib/pure/strtabs.nim
+++ b/lib/pure/strtabs.nim
@@ -168,6 +168,12 @@ proc newStringTable*(mode: StringTableMode): StringTableRef {.
result.counter = 0
newSeq(result.data, startSize)
+proc clear*(s: StringTableRef, mode: StringTableMode) =
+ ## resets a string table to be empty again.
+ s.mode = mode
+ s.counter = 0
+ s.data.setLen(startSize)
+
proc newStringTable*(keyValuePairs: varargs[string],
mode: StringTableMode): StringTableRef {.
rtl, extern: "nst$1WithPairs".} =
@@ -227,7 +233,7 @@ proc `$`*(t: StringTableRef): string {.rtl, extern: "nstDollar".} =
result = "{:}"
else:
result = "{"
- for key, val in pairs(t):
+ for key, val in pairs(t):
if result.len > 1: result.add(", ")
result.add(key)
result.add(": ")
diff --git a/lib/pure/strutils.nim b/lib/pure/strutils.nim
index 2678f4fc85..eb4be719a3 100644
--- a/lib/pure/strutils.nim
+++ b/lib/pure/strutils.nim
@@ -169,14 +169,12 @@ proc cmpIgnoreStyle*(a, b: string): int {.noSideEffect,
{.pop.}
-proc strip*(s: string, leading = true, trailing = true): string {.noSideEffect,
- rtl, extern: "nsuStrip".} =
- ## Strips whitespace from `s` and returns the resulting string.
+proc strip*(s: string, leading = true, trailing = true, chars: set[char] = Whitespace): string
+ {.noSideEffect, rtl, extern: "nsuStrip".} =
+ ## Strips `chars` from `s` and returns the resulting string.
##
- ## If `leading` is true, leading whitespace is stripped.
- ## If `trailing` is true, trailing whitespace is stripped.
- const
- chars: set[char] = Whitespace
+ ## If `leading` is true, leading `chars` are stripped.
+ ## If `trailing` is true, trailing `chars` are stripped.
var
first = 0
last = len(s)-1
@@ -1402,14 +1400,21 @@ when isMainModule:
doAssert align("a", 0) == "a"
doAssert align("1232", 6) == " 1232"
doAssert align("1232", 6, '#') == "##1232"
- echo wordWrap(""" this is a long text -- muchlongerthan10chars and here
- it goes""", 10, false)
+
+ let
+ inp = """ this is a long text -- muchlongerthan10chars and here
+ it goes"""
+ outp = " this is a\nlong text\n--\nmuchlongerthan10chars\nand here\nit goes"
+ doAssert wordWrap(inp, 10, false) == outp
+
doAssert formatBiggestFloat(0.00000000001, ffDecimal, 11) == "0.00000000001"
- doAssert formatBiggestFloat(0.00000000001, ffScientific, 1) == "1.0e-11"
+ doAssert formatBiggestFloat(0.00000000001, ffScientific, 1) in
+ ["1.0e-11", "1.0e-011"]
doAssert "$# $3 $# $#" % ["a", "b", "c"] == "a c b c"
- echo formatSize(1'i64 shl 31 + 300'i64) # == "4,GB"
- echo formatSize(1'i64 shl 31)
+ when not defined(testing):
+ echo formatSize(1'i64 shl 31 + 300'i64) # == "4,GB"
+ echo formatSize(1'i64 shl 31)
doAssert "$animal eats $food." % ["animal", "The cat", "food", "fish"] ==
"The cat eats fish."
@@ -1426,3 +1431,11 @@ when isMainModule:
doAssert count("foofoofoo", "foofoo", overlapping = true) == 2
doAssert count("foofoofoo", 'f') == 3
doAssert count("foofoofoobar", {'f','b'}) == 4
+
+ doAssert strip(" foofoofoo ") == "foofoofoo"
+ doAssert strip("sfoofoofoos", chars = {'s'}) == "foofoofoo"
+ doAssert strip("barfoofoofoobar", chars = {'b', 'a', 'r'}) == "foofoofoo"
+ doAssert strip("stripme but don't strip this stripme",
+ chars = {'s', 't', 'r', 'i', 'p', 'm', 'e'}) == " but don't strip this "
+ doAssert strip("sfoofoofoos", leading = false, chars = {'s'}) == "sfoofoofoo"
+ doAssert strip("sfoofoofoos", trailing = false, chars = {'s'}) == "foofoofoos"
diff --git a/lib/pure/subexes.nim b/lib/pure/subexes.nim
index d701b85b1d..d213c99e6d 100644
--- a/lib/pure/subexes.nim
+++ b/lib/pure/subexes.nim
@@ -386,8 +386,13 @@ when isMainModule:
longishA,
longish)"""
- echo "type TMyEnum* = enum\n $', '2i'\n '{..}" % ["fieldA",
- "fieldB", "FiledClkad", "fieldD", "fieldE", "longishFieldName"]
+ assert "type TMyEnum* = enum\n $', '2i'\n '{..}" % ["fieldA",
+ "fieldB", "FiledClkad", "fieldD", "fieldE", "longishFieldName"] ==
+ strutils.unindent """
+ type TMyEnum* = enum
+ fieldA, fieldB,
+ FiledClkad, fieldD,
+ fieldE, longishFieldName"""
doAssert subex"$1($', '{2..})" % ["f", "a", "b", "c"] == "f(a, b, c)"
@@ -395,7 +400,12 @@ when isMainModule:
doAssert subex"$['''|'|''''|']']#" % "0" == "'|"
- echo subex("type\n TEnum = enum\n $', '40c'\n '{..}") % [
- "fieldNameA", "fieldNameB", "fieldNameC", "fieldNameD"]
+ assert subex("type\n TEnum = enum\n $', '40c'\n '{..}") % [
+ "fieldNameA", "fieldNameB", "fieldNameC", "fieldNameD"] ==
+ strutils.unindent """
+ type
+ TEnum = enum
+ fieldNameA, fieldNameB, fieldNameC,
+ fieldNameD"""
diff --git a/lib/pure/terminal.nim b/lib/pure/terminal.nim
index df637dcb67..29f700db5c 100644
--- a/lib/pure/terminal.nim
+++ b/lib/pure/terminal.nim
@@ -51,12 +51,13 @@ else:
proc setRaw(fd: FileHandle, time: cint = TCSAFLUSH) =
var mode: Termios
discard fd.tcgetattr(addr mode)
- mode.iflag = mode.iflag and not Tcflag(BRKINT or ICRNL or INPCK or ISTRIP or IXON)
- mode.oflag = mode.oflag and not Tcflag(OPOST)
- mode.cflag = (mode.cflag and not Tcflag(CSIZE or PARENB)) or CS8
- mode.lflag = mode.lflag and not Tcflag(ECHO or ICANON or IEXTEN or ISIG)
- mode.cc[VMIN] = 1.cuchar
- mode.cc[VTIME] = 0.cuchar
+ mode.c_iflag = mode.c_iflag and not Tcflag(BRKINT or ICRNL or INPCK or
+ ISTRIP or IXON)
+ mode.c_oflag = mode.c_oflag and not Tcflag(OPOST)
+ mode.c_cflag = (mode.c_cflag and not Tcflag(CSIZE or PARENB)) or CS8
+ mode.c_lflag = mode.c_lflag and not Tcflag(ECHO or ICANON or IEXTEN or ISIG)
+ mode.c_cc[VMIN] = 1.cuchar
+ mode.c_cc[VTIME] = 0.cuchar
discard fd.tcsetattr(time, addr mode)
proc setCursorPos*(x, y: int) =
@@ -375,7 +376,7 @@ when not defined(windows):
result = stdin.readChar()
discard fd.tcsetattr(TCSADRAIN, addr oldMode)
-when isMainModule:
+when not defined(testing) and isMainModule:
system.addQuitProc(resetAttributes)
write(stdout, "never mind")
eraseLine()
diff --git a/lib/pure/times.nim b/lib/pure/times.nim
index 5cc9b4993d..1b9fa4599e 100644
--- a/lib/pure/times.nim
+++ b/lib/pure/times.nim
@@ -119,7 +119,7 @@ type
## in the range 0 to 23.
monthday*: range[1..31] ## The day of the month, in the range 1 to 31.
month*: Month ## The current month.
- year*: range[-10_000..10_000] ## The current year.
+ year*: int ## The current year.
weekday*: WeekDay ## The current day of the week.
yearday*: range[0..365] ## The number of days since January 1,
## in the range 0 to 365.
@@ -379,7 +379,7 @@ when not defined(JS):
result.hour = t.hour
result.monthday = t.monthday
result.month = ord(t.month)
- result.year = t.year - 1900
+ result.year = cint(t.year - 1900)
result.weekday = weekDays[t.weekday]
result.yearday = t.yearday
result.isdst = if t.isDST: 1 else: 0
@@ -764,7 +764,7 @@ proc parseToken(info: var TimeInfo; token, value: string; j: var int) =
info.monthday = value[j..j+1].parseInt()
j += 2
of "ddd":
- case value[j..j+2].toLower():
+ case value[j..j+2].toLower()
of "sun":
info.weekday = dSun
of "mon":
@@ -1036,8 +1036,6 @@ when isMainModule:
# Tue 19 Jan 03:14:07 GMT 2038
var t = getGMTime(fromSeconds(2147483647))
- echo t.format("ddd dd MMM hh:mm:ss ZZZ yyyy")
- echo t.format("ddd ddMMMhhmmssZZZyyyy")
assert t.format("ddd dd MMM hh:mm:ss ZZZ yyyy") == "Tue 19 Jan 03:14:07 UTC 2038"
assert t.format("ddd ddMMMhh:mm:ssZZZyyyy") == "Tue 19Jan03:14:07UTC2038"
@@ -1070,46 +1068,48 @@ when isMainModule:
var f = "dddd at hh:mmtt on MMM d, yyyy"
assert($s.parse(f) == "Tue Dec 15 09:04:00 2015")
# ANSIC = "Mon Jan _2 15:04:05 2006"
- s = "Mon Jan 2 15:04:05 2006"
- f = "ddd MMM d HH:mm:ss yyyy"
- assert($s.parse(f) == "Mon Jan 2 15:04:05 2006")
+ s = "Thu Jan 12 15:04:05 2006"
+ f = "ddd MMM dd HH:mm:ss yyyy"
+ assert($s.parse(f) == "Thu Jan 12 15:04:05 2006")
# UnixDate = "Mon Jan _2 15:04:05 MST 2006"
- s = "Mon Jan 2 15:04:05 MST 2006"
- f = "ddd MMM d HH:mm:ss ZZZ yyyy"
- assert($s.parse(f) == "Mon Jan 2 15:04:05 2006")
+ s = "Thu Jan 12 15:04:05 MST 2006"
+ f = "ddd MMM dd HH:mm:ss ZZZ yyyy"
+ assert($s.parse(f) == "Thu Jan 12 15:04:05 2006")
# RubyDate = "Mon Jan 02 15:04:05 -0700 2006"
- s = "Mon Jan 02 15:04:05 -07:00 2006"
+ s = "Thu Jan 12 15:04:05 -07:00 2006"
f = "ddd MMM dd HH:mm:ss zzz yyyy"
- assert($s.parse(f) == "Mon Jan 2 15:04:05 2006")
+ assert($s.parse(f) == "Thu Jan 12 15:04:05 2006")
# RFC822 = "02 Jan 06 15:04 MST"
- s = "02 Jan 06 15:04 MST"
+ s = "12 Jan 16 15:04 MST"
f = "dd MMM yy HH:mm ZZZ"
- assert($s.parse(f) == "Mon Jan 2 15:04:00 2006")
+ assert($s.parse(f) == "Tue Jan 12 15:04:00 2016")
# RFC822Z = "02 Jan 06 15:04 -0700" # RFC822 with numeric zone
- s = "02 Jan 06 15:04 -07:00"
+ s = "12 Jan 16 15:04 -07:00"
f = "dd MMM yy HH:mm zzz"
- assert($s.parse(f) == "Mon Jan 2 15:04:00 2006")
+ assert($s.parse(f) == "Tue Jan 12 15:04:00 2016")
# RFC850 = "Monday, 02-Jan-06 15:04:05 MST"
- s = "Monday, 02-Jan-06 15:04:05 MST"
+ s = "Monday, 12-Jan-06 15:04:05 MST"
f = "dddd, dd-MMM-yy HH:mm:ss ZZZ"
- assert($s.parse(f) == "Mon Jan 2 15:04:05 2006")
+ assert($s.parse(f) == "Thu Jan 12 15:04:05 2006")
# RFC1123 = "Mon, 02 Jan 2006 15:04:05 MST"
- s = "Mon, 02 Jan 2006 15:04:05 MST"
+ s = "Thu, 12 Jan 2006 15:04:05 MST"
f = "ddd, dd MMM yyyy HH:mm:ss ZZZ"
- assert($s.parse(f) == "Mon Jan 2 15:04:05 2006")
+ assert($s.parse(f) == "Thu Jan 12 15:04:05 2006")
# RFC1123Z = "Mon, 02 Jan 2006 15:04:05 -0700" # RFC1123 with numeric zone
- s = "Mon, 02 Jan 2006 15:04:05 -07:00"
+ s = "Thu, 12 Jan 2006 15:04:05 -07:00"
f = "ddd, dd MMM yyyy HH:mm:ss zzz"
- assert($s.parse(f) == "Mon Jan 2 15:04:05 2006")
+ assert($s.parse(f) == "Thu Jan 12 15:04:05 2006")
# RFC3339 = "2006-01-02T15:04:05Z07:00"
- s = "2006-01-02T15:04:05Z-07:00"
+ s = "2006-01-12T15:04:05Z-07:00"
f = "yyyy-MM-ddTHH:mm:ssZzzz"
- assert($s.parse(f) == "Mon Jan 2 15:04:05 2006")
+ assert($s.parse(f) == "Thu Jan 12 15:04:05 2006")
# RFC3339Nano = "2006-01-02T15:04:05.999999999Z07:00"
- s = "2006-01-02T15:04:05.999999999Z-07:00"
+ s = "2006-01-12T15:04:05.999999999Z-07:00"
f = "yyyy-MM-ddTHH:mm:ss.999999999Zzzz"
- assert($s.parse(f) == "Mon Jan 2 15:04:05 2006")
+ assert($s.parse(f) == "Thu Jan 12 15:04:05 2006")
# Kitchen = "3:04PM"
s = "3:04PM"
f = "h:mmtt"
- echo "Kitchen: " & $s.parse(f)
+ assert "15:04:00" in $s.parse(f)
+ when not defined(testing):
+ echo "Kitchen: " & $s.parse(f)
diff --git a/lib/pure/unicode.nim b/lib/pure/unicode.nim
index 4a9f4631d0..5fd3c2418e 100644
--- a/lib/pure/unicode.nim
+++ b/lib/pure/unicode.nim
@@ -105,6 +105,31 @@ template fastRuneAt*(s: string, i: int, result: expr, doInc = true) =
result = Rune(ord(s[i]))
when doInc: inc(i)
+proc validateUtf8*(s: string): int =
+ ## returns the position of the invalid byte in ``s`` if the string ``s`` does
+ ## not hold valid UTF-8 data. Otherwise -1 is returned.
+ var i = 0
+ let L = s.len
+ while i < L:
+ if ord(s[i]) <=% 127:
+ inc(i)
+ elif ord(s[i]) shr 5 == 0b110:
+ if i+1 < L and ord(s[i+1]) shr 6 == 0b10: inc(i, 2)
+ else: return i
+ elif ord(s[i]) shr 4 == 0b1110:
+ if i+2 < L and ord(s[i+1]) shr 6 == 0b10 and ord(s[i+2]) shr 6 == 0b10:
+ inc i, 3
+ else: return i
+ elif ord(s[i]) shr 3 == 0b11110:
+ if i+3 < L and ord(s[i+1]) shr 6 == 0b10 and
+ ord(s[i+2]) shr 6 == 0b10 and
+ ord(s[i+3]) shr 6 == 0b10:
+ inc i, 4
+ else: return i
+ else:
+ return i
+ return -1
+
proc runeAt*(s: string, i: Natural): Rune =
## returns the unicode character in `s` at byte index `i`
fastRuneAt(s, i, result, false)
diff --git a/lib/pure/unidecode/unidecode.nim b/lib/pure/unidecode/unidecode.nim
index 798eef5d0c..a83b9be0f9 100644
--- a/lib/pure/unidecode/unidecode.nim
+++ b/lib/pure/unidecode/unidecode.nim
@@ -70,5 +70,5 @@ proc unidecode*(s: string): string =
when isMainModule:
loadUnidecodeTable("lib/pure/unidecode/unidecode.dat")
- echo unidecode("Äußerst")
+ assert unidecode("Äußerst") == "Ausserst"
diff --git a/lib/pure/uri.nim b/lib/pure/uri.nim
index b0afb75f99..1890a9bf4b 100644
--- a/lib/pure/uri.nim
+++ b/lib/pure/uri.nim
@@ -53,10 +53,10 @@ proc parseAuthority(authority: string, result: var Uri) =
while true:
case authority[i]
of '@':
- result.password = result.port
- result.port = ""
- result.username = result.hostname
- result.hostname = ""
+ swap result.password, result.port
+ result.port.setLen(0)
+ swap result.username, result.hostname
+ result.hostname.setLen(0)
inPort = false
of ':':
inPort = true
@@ -75,7 +75,7 @@ proc parsePath(uri: string, i: var int, result: var Uri) =
# The 'mailto' scheme's PATH actually contains the hostname/username
if result.scheme.toLower == "mailto":
parseAuthority(result.path, result)
- result.path = ""
+ result.path.setLen(0)
if uri[i] == '?':
i.inc # Skip '?'
@@ -85,13 +85,21 @@ proc parsePath(uri: string, i: var int, result: var Uri) =
i.inc # Skip '#'
i.inc parseUntil(uri, result.anchor, {}, i)
-proc initUri(): Uri =
+proc initUri*(): Uri =
+ ## Initializes a URI.
result = Uri(scheme: "", username: "", password: "", hostname: "", port: "",
path: "", query: "", anchor: "")
-proc parseUri*(uri: string): Uri =
- ## Parses a URI.
- result = initUri()
+proc resetUri(uri: var Uri) =
+ for f in uri.fields:
+ when f is string:
+ f.setLen(0)
+ else:
+ f = false
+
+proc parseUri*(uri: string, result: var Uri) =
+ ## Parses a URI. The `result` variable will be cleared before.
+ resetUri(result)
var i = 0
@@ -105,7 +113,7 @@ proc parseUri*(uri: string): Uri =
if uri[i] != ':':
# Assume this is a reference URI (relative URI)
i = 0
- result.scheme = ""
+ result.scheme.setLen(0)
parsePath(uri, i, result)
return
i.inc # Skip ':'
@@ -124,6 +132,11 @@ proc parseUri*(uri: string): Uri =
# Path
parsePath(uri, i, result)
+proc parseUri*(uri: string): Uri =
+ ## Parses a URI and returns it.
+ result = initUri()
+ parseUri(uri, result)
+
proc removeDotSegments(path: string): string =
var collection: seq[string] = @[]
let endsWithSlash = path[path.len-1] == '/'
diff --git a/lib/pure/xmldomparser.nim b/lib/pure/xmldomparser.nim
index 7f34d72a82..0503624357 100644
--- a/lib/pure/xmldomparser.nim
+++ b/lib/pure/xmldomparser.nim
@@ -155,7 +155,7 @@ proc loadXMLFile*(path: string): PDocument =
return loadXMLStream(s)
-when isMainModule:
+when not defined(testing) and isMainModule:
var xml = loadXMLFile("nim/xmldom/test.xml")
#echo(xml.getElementsByTagName("m:test2")[0].namespaceURI)
#echo(xml.getElementsByTagName("bla:test")[0].namespaceURI)
diff --git a/lib/pure/xmlparser.nim b/lib/pure/xmlparser.nim
index 755bfcdbc4..840cae7341 100644
--- a/lib/pure/xmlparser.nim
+++ b/lib/pure/xmlparser.nim
@@ -143,7 +143,7 @@ proc loadXml*(path: string): XmlNode =
result = loadXml(path, errors)
if errors.len > 0: raiseInvalidXml(errors)
-when isMainModule:
+when not defined(testing) and isMainModule:
import os
var errors: seq[string] = @[]
diff --git a/lib/pure/xmltree.nim b/lib/pure/xmltree.nim
index 0bf5b52a46..7526a989a1 100644
--- a/lib/pure/xmltree.nim
+++ b/lib/pure/xmltree.nim
@@ -353,5 +353,6 @@ proc findAll*(n: XmlNode, tag: string): seq[XmlNode] =
findAll(n, tag, result)
when isMainModule:
- assert """Nim rules.""" ==
+ let link = "http://nim-lang.org"
+ assert """Nim rules.""" ==
$(<>a(href="http://nim-lang.org", newText("Nim rules.")))
diff --git a/lib/system.nim b/lib/system.nim
index 83f0717177..ca4c814110 100644
--- a/lib/system.nim
+++ b/lib/system.nim
@@ -663,7 +663,7 @@ proc `+` *(x: int): int {.magic: "UnaryPlusI", noSideEffect.}
proc `+` *(x: int8): int8 {.magic: "UnaryPlusI", noSideEffect.}
proc `+` *(x: int16): int16 {.magic: "UnaryPlusI", noSideEffect.}
proc `+` *(x: int32): int32 {.magic: "UnaryPlusI", noSideEffect.}
-proc `+` *(x: int64): int64 {.magic: "UnaryPlusI64", noSideEffect.}
+proc `+` *(x: int64): int64 {.magic: "UnaryPlusI", noSideEffect.}
## Unary `+` operator for an integer. Has no effect.
proc `-` *(x: int): int {.magic: "UnaryMinusI", noSideEffect.}
@@ -1532,10 +1532,10 @@ const
NimMajor*: int = 0
## is the major number of Nim's version.
- NimMinor*: int = 10
+ NimMinor*: int = 11
## is the minor number of Nim's version.
- NimPatch*: int = 3
+ NimPatch*: int = 2
## is the patch number of Nim's version.
NimVersion*: string = $NimMajor & "." & $NimMinor & "." & $NimPatch
@@ -1638,7 +1638,7 @@ proc min*(x, y: int16): int16 {.magic: "MinI", noSideEffect.} =
if x <= y: x else: y
proc min*(x, y: int32): int32 {.magic: "MinI", noSideEffect.} =
if x <= y: x else: y
-proc min*(x, y: int64): int64 {.magic: "MinI64", noSideEffect.} =
+proc min*(x, y: int64): int64 {.magic: "MinI", noSideEffect.} =
## The minimum value of two integers.
if x <= y: x else: y
@@ -1656,7 +1656,7 @@ proc max*(x, y: int16): int16 {.magic: "MaxI", noSideEffect.} =
if y <= x: x else: y
proc max*(x, y: int32): int32 {.magic: "MaxI", noSideEffect.} =
if y <= x: x else: y
-proc max*(x, y: int64): int64 {.magic: "MaxI64", noSideEffect.} =
+proc max*(x, y: int64): int64 {.magic: "MaxI", noSideEffect.} =
## The maximum value of two integers.
if y <= x: x else: y
@@ -1744,6 +1744,12 @@ iterator items*(E: typedesc[enum]): E =
for v in low(E)..high(E):
yield v
+iterator items*[T](s: Slice[T]): T =
+ ## iterates over the slice `s`, yielding each value between `s.a` and `s.b`
+ ## (inclusively).
+ for x in s.a..s.b:
+ yield x
+
iterator pairs*[T](a: openArray[T]): tuple[key: int, val: T] {.inline.} =
## iterates over each item of `a`. Yields ``(index, a[index])`` pairs.
var i = 0
@@ -3201,7 +3207,7 @@ when hostOS != "standalone":
if x == nil: x = y
else: x.add(y)
-proc locals*(): RootObj {.magic: "Locals", noSideEffect.} =
+proc locals*(): RootObj {.magic: "Plugin", noSideEffect.} =
## generates a tuple constructor expression listing all the local variables
## in the current scope. This is quite fast as it does not rely
## on any debug or runtime information. Note that in constrast to what
@@ -3250,4 +3256,20 @@ proc `^`*(x: int): int {.noSideEffect, magic: "Roof".} =
## overloaded ``[]`` or ``[]=`` accessors.
discard
+template `..^`*(a, b: expr): expr =
+ ## a shortcut for '.. ^' to avoid the common gotcha that a space between
+ ## '..' and '^' is required.
+ a .. ^b
+
+template `..<`*(a, b: expr): expr =
+ ## a shortcut for '.. <' to avoid the common gotcha that a space between
+ ## '..' and '<' is required.
+ a .. ",
+ TDl_info {.importc: "Dl_info", header: "",
final, pure.} = object
dli_fname: cstring
dli_fbase: pointer
@@ -98,7 +98,7 @@ when defined(nativeStacktrace) and nativeStackTraceSupported:
tempDlInfo: TDl_info
# This is allowed to be expensive since it only happens during crashes
# (but this way you don't need manual stack tracing)
- var size = backtrace(cast[ptr pointer](addr(tempAddresses)),
+ var size = backtrace(cast[ptr pointer](addr(tempAddresses)),
len(tempAddresses))
var enabled = false
for i in 0..size-1:
@@ -123,7 +123,7 @@ when defined(nativeStacktrace) and nativeStackTraceSupported:
when not hasThreadSupport:
var
tempFrames: array [0..127, PFrame] # should not be alloc'd on stack
-
+
proc auxWriteStackTrace(f: PFrame, s: var string) =
when hasThreadSupport:
var
@@ -160,7 +160,7 @@ proc auxWriteStackTrace(f: PFrame, s: var string) =
inc(i)
b = b.prev
for j in countdown(i-1, 0):
- if tempFrames[j] == nil:
+ if tempFrames[j] == nil:
add(s, "(")
add(s, $skipped)
add(s, " calls omitted) ...")
@@ -214,41 +214,49 @@ proc raiseExceptionAux(e: ref Exception) =
if not localRaiseHook(e): return
if globalRaiseHook != nil:
if not globalRaiseHook(e): return
- if excHandler != nil:
- if not excHandler.hasRaiseAction or excHandler.raiseAction(e):
- pushCurrentException(e)
- c_longjmp(excHandler.context, 1)
- elif e[] of OutOfMemError:
- showErrorMessage(e.name)
- quitOrDebug()
- else:
- when hasSomeStackTrace:
- var buf = newStringOfCap(2000)
- if isNil(e.trace): rawWriteStackTrace(buf)
- else: add(buf, e.trace)
- add(buf, "Error: unhandled exception: ")
- if not isNil(e.msg): add(buf, e.msg)
- add(buf, " [")
- add(buf, $e.name)
- add(buf, "]\n")
- showErrorMessage(buf)
+ when defined(cpp):
+ if e[] of OutOfMemError:
+ showErrorMessage(e.name)
+ quitOrDebug()
else:
- # ugly, but avoids heap allocations :-)
- template xadd(buf, s, slen: expr) =
- if L + slen < high(buf):
- copyMem(addr(buf[L]), cstring(s), slen)
- inc L, slen
- template add(buf, s: expr) =
- xadd(buf, s, s.len)
- var buf: array [0..2000, char]
- var L = 0
- add(buf, "Error: unhandled exception: ")
- if not isNil(e.msg): add(buf, e.msg)
- add(buf, " [")
- xadd(buf, e.name, c_strlen(e.name))
- add(buf, "]\n")
- showErrorMessage(buf)
- quitOrDebug()
+ pushCurrentException(e)
+ {.emit: "throw NimException(`e`, `e`->name);".}
+ else:
+ if excHandler != nil:
+ if not excHandler.hasRaiseAction or excHandler.raiseAction(e):
+ pushCurrentException(e)
+ c_longjmp(excHandler.context, 1)
+ elif e[] of OutOfMemError:
+ showErrorMessage(e.name)
+ quitOrDebug()
+ else:
+ when hasSomeStackTrace:
+ var buf = newStringOfCap(2000)
+ if isNil(e.trace): rawWriteStackTrace(buf)
+ else: add(buf, e.trace)
+ add(buf, "Error: unhandled exception: ")
+ if not isNil(e.msg): add(buf, e.msg)
+ add(buf, " [")
+ add(buf, $e.name)
+ add(buf, "]\n")
+ showErrorMessage(buf)
+ else:
+ # ugly, but avoids heap allocations :-)
+ template xadd(buf, s, slen: expr) =
+ if L + slen < high(buf):
+ copyMem(addr(buf[L]), cstring(s), slen)
+ inc L, slen
+ template add(buf, s: expr) =
+ xadd(buf, s, s.len)
+ var buf: array [0..2000, char]
+ var L = 0
+ add(buf, "Error: unhandled exception: ")
+ if not isNil(e.msg): add(buf, e.msg)
+ add(buf, " [")
+ xadd(buf, e.name, c_strlen(e.name))
+ add(buf, "]\n")
+ showErrorMessage(buf)
+ quitOrDebug()
proc raiseException(e: ref Exception, ename: cstring) {.compilerRtl.} =
e.name = ename
@@ -309,7 +317,7 @@ when not defined(noSignalHandler):
proc signalHandler(sig: cint) {.exportc: "signalHandler", noconv.} =
template processSignal(s, action: expr) {.immediate, dirty.} =
if s == SIGINT: action("SIGINT: Interrupted by Ctrl-C.\n")
- elif s == SIGSEGV:
+ elif s == SIGSEGV:
action("SIGSEGV: Illegal storage access. (Attempt to read from nil?)\n")
elif s == SIGABRT:
when defined(endb):
diff --git a/lib/windows/winlean.nim b/lib/windows/winlean.nim
index 584f7cf482..57b79c7ded 100644
--- a/lib/windows/winlean.nim
+++ b/lib/windows/winlean.nim
@@ -10,6 +10,8 @@
## This module implements a small wrapper for some needed Win API procedures,
## so that the Nim compiler does not depend on the huge Windows module.
+{.deadCodeElim:on.}
+
const
useWinUnicode* = not defined(useWinAnsi)
@@ -29,7 +31,7 @@ type
nLength*: int32
lpSecurityDescriptor*: pointer
bInheritHandle*: WINBOOL
-
+
TSTARTUPINFO* {.final, pure.} = object
cb*: int32
lpReserved*: cstring
@@ -59,7 +61,7 @@ type
TFILETIME* {.final, pure.} = object ## CANNOT BE int64 BECAUSE OF ALIGNMENT
dwLowDateTime*: DWORD
dwHighDateTime*: DWORD
-
+
TBY_HANDLE_FILE_INFORMATION* {.final, pure.} = object
dwFileAttributes*: DWORD
ftCreationTime*: TFILETIME
@@ -94,26 +96,26 @@ const
STD_ERROR_HANDLE* = -12'i32
DETACHED_PROCESS* = 8'i32
-
+
SW_SHOWNORMAL* = 1'i32
INVALID_HANDLE_VALUE* = THandle(-1)
-
+
CREATE_UNICODE_ENVIRONMENT* = 1024'i32
proc closeHandle*(hObject: THandle): WINBOOL {.stdcall, dynlib: "kernel32",
importc: "CloseHandle".}
-
+
proc readFile*(hFile: THandle, Buffer: pointer, nNumberOfBytesToRead: int32,
lpNumberOfBytesRead: ptr int32, lpOverlapped: pointer): WINBOOL{.
stdcall, dynlib: "kernel32", importc: "ReadFile".}
-
+
proc writeFile*(hFile: THandle, Buffer: pointer, nNumberOfBytesToWrite: int32,
- lpNumberOfBytesWritten: ptr int32,
+ lpNumberOfBytesWritten: ptr int32,
lpOverlapped: pointer): WINBOOL{.
stdcall, dynlib: "kernel32", importc: "WriteFile".}
proc createPipe*(hReadPipe, hWritePipe: var THandle,
- lpPipeAttributes: var TSECURITY_ATTRIBUTES,
+ lpPipeAttributes: var TSECURITY_ATTRIBUTES,
nSize: int32): WINBOOL{.
stdcall, dynlib: "kernel32", importc: "CreatePipe".}
@@ -159,7 +161,7 @@ proc setStdHandle*(nStdHandle: int32, hHandle: THandle): WINBOOL {.stdcall,
proc flushFileBuffers*(hFile: THandle): WINBOOL {.stdcall, dynlib: "kernel32",
importc: "FlushFileBuffers".}
-proc getLastError*(): int32 {.importc: "GetLastError",
+proc getLastError*(): int32 {.importc: "GetLastError",
stdcall, dynlib: "kernel32".}
when useWinUnicode:
@@ -179,7 +181,7 @@ proc localFree*(p: pointer) {.
importc: "LocalFree", stdcall, dynlib: "kernel32".}
when useWinUnicode:
- proc getCurrentDirectoryW*(nBufferLength: int32,
+ proc getCurrentDirectoryW*(nBufferLength: int32,
lpBuffer: WideCString): int32 {.
importc: "GetCurrentDirectoryW", dynlib: "kernel32", stdcall.}
proc setCurrentDirectoryW*(lpPathName: WideCString): int32 {.
@@ -191,8 +193,8 @@ when useWinUnicode:
proc setEnvironmentVariableW*(lpName, lpValue: WideCString): int32 {.
stdcall, dynlib: "kernel32", importc: "SetEnvironmentVariableW".}
- proc getModuleFileNameW*(handle: THandle, buf: WideCString,
- size: int32): int32 {.importc: "GetModuleFileNameW",
+ proc getModuleFileNameW*(handle: THandle, buf: WideCString,
+ size: int32): int32 {.importc: "GetModuleFileNameW",
dynlib: "kernel32", stdcall.}
else:
proc getCurrentDirectoryA*(nBufferLength: int32, lpBuffer: cstring): int32 {.
@@ -269,14 +271,14 @@ proc findClose*(hFindFile: THandle) {.stdcall, dynlib: "kernel32",
when useWinUnicode:
proc getFullPathNameW*(lpFileName: WideCString, nBufferLength: int32,
- lpBuffer: WideCString,
+ lpBuffer: WideCString,
lpFilePart: var WideCString): int32 {.
- stdcall, dynlib: "kernel32",
+ stdcall, dynlib: "kernel32",
importc: "GetFullPathNameW".}
proc getFileAttributesW*(lpFileName: WideCString): int32 {.
- stdcall, dynlib: "kernel32",
+ stdcall, dynlib: "kernel32",
importc: "GetFileAttributesW".}
- proc setFileAttributesW*(lpFileName: WideCString,
+ proc setFileAttributesW*(lpFileName: WideCString,
dwFileAttributes: int32): WINBOOL {.
stdcall, dynlib: "kernel32", importc: "SetFileAttributesW".}
@@ -299,12 +301,12 @@ when useWinUnicode:
else:
proc getFullPathNameA*(lpFileName: cstring, nBufferLength: int32,
lpBuffer: cstring, lpFilePart: var cstring): int32 {.
- stdcall, dynlib: "kernel32",
+ stdcall, dynlib: "kernel32",
importc: "GetFullPathNameA".}
proc getFileAttributesA*(lpFileName: cstring): int32 {.
- stdcall, dynlib: "kernel32",
+ stdcall, dynlib: "kernel32",
importc: "GetFileAttributesA".}
- proc setFileAttributesA*(lpFileName: cstring,
+ proc setFileAttributesA*(lpFileName: cstring,
dwFileAttributes: int32): WINBOOL {.
stdcall, dynlib: "kernel32", importc: "SetFileAttributesA".}
@@ -324,10 +326,10 @@ else:
proc getCommandLineA*(): cstring {.
importc: "GetCommandLineA", stdcall, dynlib: "kernel32".}
-proc rdFileTime*(f: TFILETIME): int64 =
+proc rdFileTime*(f: TFILETIME): int64 =
result = ze64(f.dwLowDateTime) or (ze64(f.dwHighDateTime) shl 32)
-proc rdFileSize*(f: TWIN32_FIND_DATA): int64 =
+proc rdFileSize*(f: TWIN32_FIND_DATA): int64 =
result = ze64(f.nFileSizeLow) or (ze64(f.nFileSizeHigh) shl 32)
proc getSystemTimeAsFileTime*(lpSystemTimeAsFileTime: var TFILETIME) {.
@@ -347,7 +349,7 @@ else:
lpParameters, lpDirectory: cstring,
nShowCmd: int32): THandle{.
stdcall, dynlib: "shell32.dll", importc: "ShellExecuteA".}
-
+
proc getFileInformationByHandle*(hFile: THandle,
lpFileInformation: ptr TBY_HANDLE_FILE_INFORMATION): WINBOOL{.
stdcall, dynlib: "kernel32", importc: "GetFileInformationByHandle".}
@@ -357,12 +359,12 @@ const
WSASYS_STATUS_LEN* = 128
FD_SETSIZE* = 64
MSG_PEEK* = 2
-
+
INADDR_ANY* = 0
INADDR_LOOPBACK* = 0x7F000001
INADDR_BROADCAST* = -1
INADDR_NONE* = -1
-
+
ws2dll = "Ws2_32.dll"
WSAEWOULDBLOCK* = 10035
@@ -376,31 +378,31 @@ type
{.deprecated: [TSocketHandle: SocketHandle].}
type
- WSAData* {.importc: "WSADATA", header: "winsock2.h".} = object
+ WSAData* {.importc: "WSADATA", header: "winsock2.h".} = object
wVersion, wHighVersion: int16
szDescription: array[0..WSADESCRIPTION_LEN, char]
szSystemStatus: array[0..WSASYS_STATUS_LEN, char]
iMaxSockets, iMaxUdpDg: int16
lpVendorInfo: cstring
-
- SockAddr* {.importc: "SOCKADDR", header: "winsock2.h".} = object
+
+ SockAddr* {.importc: "SOCKADDR", header: "winsock2.h".} = object
sa_family*: int16 # unsigned
sa_data: array[0..13, char]
InAddr* {.importc: "IN_ADDR", header: "winsock2.h".} = object
s_addr*: int32 # IP address
-
- Sockaddr_in* {.importc: "SOCKADDR_IN",
+
+ Sockaddr_in* {.importc: "SOCKADDR_IN",
header: "winsock2.h".} = object
sin_family*: int16
sin_port*: int16 # unsigned
sin_addr*: InAddr
sin_zero*: array[0..7, char]
- In6_addr* {.importc: "IN6_ADDR", header: "winsock2.h".} = object
+ In6_addr* {.importc: "IN6_ADDR", header: "winsock2.h".} = object
bytes*: array[0..15, char]
- Sockaddr_in6* {.importc: "SOCKADDR_IN6",
+ Sockaddr_in6* {.importc: "SOCKADDR_IN6",
header: "winsock2.h".} = object
sin6_family*: int16
sin6_port*: int16 # unsigned
@@ -430,23 +432,23 @@ type
h_addrtype*: int16
h_length*: int16
h_addr_list*: cstringArray
-
+
TFdSet* = object
fd_count*: cint # unsigned
fd_array*: array[0..FD_SETSIZE-1, SocketHandle]
-
+
Timeval* = object
tv_sec*, tv_usec*: int32
-
+
AddrInfo* = object
- ai_flags*: cint ## Input flags.
- ai_family*: cint ## Address family of socket.
- ai_socktype*: cint ## Socket type.
- ai_protocol*: cint ## Protocol of socket.
- ai_addrlen*: int ## Length of socket address.
+ ai_flags*: cint ## Input flags.
+ ai_family*: cint ## Address family of socket.
+ ai_socktype*: cint ## Socket type.
+ ai_protocol*: cint ## Protocol of socket.
+ ai_addrlen*: int ## Length of socket address.
ai_canonname*: cstring ## Canonical name of service location.
- ai_addr*: ptr SockAddr ## Socket address of socket.
- ai_next*: ptr AddrInfo ## Pointer to next in list.
+ ai_addr*: ptr SockAddr ## Socket address of socket.
+ ai_next*: ptr AddrInfo ## Pointer to next in list.
SockLen* = cuint
@@ -501,7 +503,7 @@ proc bindSocket*(s: SocketHandle, name: ptr SockAddr, namelen: SockLen): cint {.
stdcall, importc: "bind", dynlib: ws2dll.}
proc connect*(s: SocketHandle, name: ptr SockAddr, namelen: SockLen): cint {.
stdcall, importc: "connect", dynlib: ws2dll.}
-proc getsockname*(s: SocketHandle, name: ptr SockAddr,
+proc getsockname*(s: SocketHandle, name: ptr SockAddr,
namelen: ptr SockLen): cint {.
stdcall, importc: "getsockname", dynlib: ws2dll.}
proc getsockopt*(s: SocketHandle, level, optname: cint, optval: pointer,
@@ -515,7 +517,7 @@ proc listen*(s: SocketHandle, backlog: cint): cint {.
stdcall, importc: "listen", dynlib: ws2dll.}
proc recv*(s: SocketHandle, buf: pointer, len, flags: cint): cint {.
stdcall, importc: "recv", dynlib: ws2dll.}
-proc recvfrom*(s: SocketHandle, buf: cstring, len, flags: cint,
+proc recvfrom*(s: SocketHandle, buf: cstring, len, flags: cint,
fromm: ptr SockAddr, fromlen: ptr SockLen): cint {.
stdcall, importc: "recvfrom", dynlib: ws2dll.}
proc select*(nfds: cint, readfds, writefds, exceptfds: ptr TFdSet,
@@ -529,22 +531,22 @@ proc sendto*(s: SocketHandle, buf: pointer, len, flags: cint,
proc shutdown*(s: SocketHandle, how: cint): cint {.
stdcall, importc: "shutdown", dynlib: ws2dll.}
-
+
proc getnameinfo*(a1: ptr SockAddr, a2: SockLen,
a3: cstring, a4: SockLen, a5: cstring,
a6: SockLen, a7: cint): cint {.
stdcall, importc: "getnameinfo", dynlib: ws2dll.}
-
+
proc inet_addr*(cp: cstring): int32 {.
- stdcall, importc: "inet_addr", dynlib: ws2dll.}
+ stdcall, importc: "inet_addr", dynlib: ws2dll.}
proc WSAFDIsSet(s: SocketHandle, set: var TFdSet): bool {.
stdcall, importc: "__WSAFDIsSet", dynlib: ws2dll, noSideEffect.}
-proc FD_ISSET*(socket: SocketHandle, set: var TFdSet): cint =
+proc FD_ISSET*(socket: SocketHandle, set: var TFdSet): cint =
result = if WSAFDIsSet(socket, set): 1'i32 else: 0'i32
-proc FD_SET*(socket: SocketHandle, s: var TFdSet) =
+proc FD_SET*(socket: SocketHandle, s: var TFdSet) =
if s.fd_count < FD_SETSIZE:
s.fd_array[int(s.fd_count)] = socket
inc(s.fd_count)
@@ -575,8 +577,8 @@ type
proc waitForMultipleObjects*(nCount: DWORD, lpHandles: PWOHandleArray,
bWaitAll: WINBOOL, dwMilliseconds: DWORD): DWORD{.
stdcall, dynlib: "kernel32", importc: "WaitForMultipleObjects".}
-
-
+
+
# for memfiles.nim:
const
@@ -586,7 +588,7 @@ const
FILE_SHARE_READ* = 1'i32
FILE_SHARE_DELETE* = 4'i32
FILE_SHARE_WRITE* = 2'i32
-
+
CREATE_ALWAYS* = 2'i32
CREATE_NEW* = 1'i32
OPEN_EXISTING* = 3'i32
@@ -628,7 +630,7 @@ proc setEndOfFile*(hFile: THandle): WINBOOL {.stdcall, dynlib: "kernel32",
importc: "SetEndOfFile".}
proc setFilePointer*(hFile: THandle, lDistanceToMove: LONG,
- lpDistanceToMoveHigh: ptr LONG,
+ lpDistanceToMoveHigh: ptr LONG,
dwMoveMethod: DWORD): DWORD {.
stdcall, dynlib: "kernel32", importc: "SetFilePointer".}
@@ -637,14 +639,14 @@ proc getFileSize*(hFile: THandle, lpFileSizeHigh: ptr DWORD): DWORD{.stdcall,
proc mapViewOfFileEx*(hFileMappingObject: THandle, dwDesiredAccess: DWORD,
dwFileOffsetHigh, dwFileOffsetLow: DWORD,
- dwNumberOfBytesToMap: DWORD,
+ dwNumberOfBytesToMap: DWORD,
lpBaseAddress: pointer): pointer{.
stdcall, dynlib: "kernel32", importc: "MapViewOfFileEx".}
proc createFileMappingW*(hFile: THandle,
lpFileMappingAttributes: pointer,
flProtect, dwMaximumSizeHigh: DWORD,
- dwMaximumSizeLow: DWORD,
+ dwMaximumSizeLow: DWORD,
lpName: pointer): THandle {.
stdcall, dynlib: "kernel32", importc: "CreateFileMappingW".}
@@ -702,7 +704,7 @@ proc getOverlappedResult*(hFile: THandle, lpOverlapped: TOVERLAPPED,
lpNumberOfBytesTransferred: var DWORD, bWait: WINBOOL): WINBOOL{.
stdcall, dynlib: "kernel32", importc: "GetOverlappedResult".}
-const
+const
IOC_OUT* = 0x40000000
IOC_IN* = 0x80000000
IOC_WS2* = 0x08000000
@@ -725,7 +727,7 @@ var
proc WSAIoctl*(s: SocketHandle, dwIoControlCode: DWORD, lpvInBuffer: pointer,
cbInBuffer: DWORD, lpvOutBuffer: pointer, cbOutBuffer: DWORD,
lpcbBytesReturned: PDWORD, lpOverlapped: POVERLAPPED,
- lpCompletionRoutine: POVERLAPPED_COMPLETION_ROUTINE): cint
+ lpCompletionRoutine: POVERLAPPED_COMPLETION_ROUTINE): cint
{.stdcall, importc: "WSAIoctl", dynlib: "Ws2_32.dll".}
type
@@ -746,7 +748,7 @@ proc WSASend*(s: SocketHandle, buf: ptr TWSABuf, bufCount: DWORD,
proc get_osfhandle*(fd:FileHandle): THandle {.
importc: "_get_osfhandle", header:"".}
-proc getSystemTimes*(lpIdleTime, lpKernelTime,
+proc getSystemTimes*(lpIdleTime, lpKernelTime,
lpUserTime: var TFILETIME): WINBOOL {.stdcall,
dynlib: "kernel32", importc: "GetSystemTimes".}
diff --git a/lib/wrappers/claro.nim b/lib/wrappers/claro.nim
index d36b9f1783..0fb0882bf5 100644
--- a/lib/wrappers/claro.nim
+++ b/lib/wrappers/claro.nim
@@ -2710,7 +2710,7 @@ proc workspace_window_set_icon*(w: ptr TWorkspaceWindow, icon: ptr TImage){.
claro_base_init()
claro_graphics_init()
-when isMainModule:
+when not defined(testing) and isMainModule:
var w = newWindow(nil, newBounds(100, 100, 230, 230), 0)
window_set_title(w, "Hello, World!")
diff --git a/lib/wrappers/pcre.nim b/lib/wrappers/pcre.nim
index afa8f447ab..67436f0265 100644
--- a/lib/wrappers/pcre.nim
+++ b/lib/wrappers/pcre.nim
@@ -1,158 +1,190 @@
#************************************************
-# Perl-Compatible Regular Expressions *
-#***********************************************
+# Perl-Compatible Regular Expressions *
+#************************************************
# This is the public header file for the PCRE library, to be #included by
-#applications that call the PCRE functions.
+# applications that call the PCRE functions.
#
-# Copyright (c) 1997-2010 University of Cambridge
+# Copyright (c) 1997-2014 University of Cambridge
#
#-----------------------------------------------------------------------------
-#Redistribution and use in source and binary forms, with or without
-#modification, are permitted provided that the following conditions are met:
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
#
-# Redistributions of source code must retain the above copyright notice,
+# * Redistributions of source code must retain the above copyright notice,
# this list of conditions and the following disclaimer.
#
-# Redistributions in binary form must reproduce the above copyright
+# * Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
#
-# Neither the name of the University of Cambridge nor the names of its
+# * Neither the name of the University of Cambridge nor the names of its
# contributors may be used to endorse or promote products derived from
# this software without specific prior written permission.
#
-#THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-#AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-#IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-#ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
-#LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
-#CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
-#SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-#INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
-#CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
-#ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
-#POSSIBILITY OF SUCH DAMAGE.
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
#-----------------------------------------------------------------------------
-#
-{.deadcodeElim: on.}
+{.deadCodeElim: on.}
-when not defined(pcreDll):
- when hostOS == "windows":
- const pcreDll = "pcre.dll"
- elif hostOS == "macosx":
- const pcreDll = "libpcre(.3|.1|).dylib"
- else:
- const pcreDll = "libpcre.so(.3|.1|)"
- {.pragma: pcreImport, dynlib: pcreDll.}
-else:
- {.pragma: pcreImport, header: "".}
+# The current PCRE version information.
-# The current PCRE version information.
-
-const
- MAJOR* = 8
- MINOR* = 31
- PRERELEASE* = true
- DATE* = "2012-07-06"
+const
+ PCRE_MAJOR* = 8
+ PCRE_MINOR* = 36
+ PCRE_PRERELEASE* = true
+ PCRE_DATE* = "2014-09-26"
# When an application links to a PCRE DLL in Windows, the symbols that are
# imported have to be identified as such. When building PCRE, the appropriate
# export setting is defined in pcre_internal.h, which includes this file. So we
-# don't change existing definitions of PCRE_EXP_DECL and PCRECPP_EXP_DECL.
+# don't change existing definitions of PCRE_EXP_DECL and PCRECPP_EXP_DECL.
-# Have to include stdlib.h in order to ensure that size_t is defined;
-# it is needed here for malloc.
+# By default, we use the standard "extern" declarations.
-# Allow for C++ users
+# Allow for C++ users
-# Options. Some are compile-time only, some are run-time only, and some are
-# both, so we keep them all distinct.
-
-const
- CASELESS* = 0x00000001
- MULTILINE* = 0x00000002
- DOTALL* = 0x00000004
- EXTENDED* = 0x00000008
- ANCHORED* = 0x00000010
- DOLLAR_ENDONLY* = 0x00000020
- EXTRA* = 0x00000040
- NOTBOL* = 0x00000080
- NOTEOL* = 0x00000100
- UNGREEDY* = 0x00000200
- NOTEMPTY* = 0x00000400
- UTF8* = 0x00000800
- NO_AUTO_CAPTURE* = 0x00001000
- NO_UTF8_CHECK* = 0x00002000
- AUTO_CALLOUT* = 0x00004000
- PARTIAL_SOFT* = 0x00008000
- PARTIAL* = 0x00008000 # Backwards compatible synonym
- DFA_SHORTEST* = 0x00010000
- DFA_RESTART* = 0x00020000
- FIRSTLINE* = 0x00040000
- DUPNAMES* = 0x00080000
- NEWLINE_CR* = 0x00100000
- NEWLINE_LF* = 0x00200000
- NEWLINE_CRLF* = 0x00300000
- NEWLINE_ANY* = 0x00400000
- NEWLINE_ANYCRLF* = 0x00500000
- BSR_ANYCRLF* = 0x00800000
- BSR_UNICODE* = 0x01000000
- JAVASCRIPT_COMPAT* = 0x02000000
- NO_START_OPTIMIZE* = 0x04000000
- NO_START_OPTIMISE* = 0x04000000
- PARTIAL_HARD* = 0x08000000
- NOTEMPTY_ATSTART* = 0x10000000
- UCP* = 0x20000000
-
-# Exec-time and get/set-time error codes
-
-const
- ERROR_NOMATCH* = (- 1)
- ERROR_NULL* = (- 2)
- ERROR_BADOPTION* = (- 3)
- ERROR_BADMAGIC* = (- 4)
- ERROR_UNKNOWN_OPCODE* = (- 5)
- ERROR_UNKNOWN_NODE* = (- 5) # For backward compatibility
- ERROR_NOMEMORY* = (- 6)
- ERROR_NOSUBSTRING* = (- 7)
- ERROR_MATCHLIMIT* = (- 8)
- ERROR_CALLOUT* = (- 9) # Never used by PCRE itself
- ERROR_BADUTF8* = (- 10)
- ERROR_BADUTF8_OFFSET* = (- 11)
- ERROR_PARTIAL* = (- 12)
- ERROR_BADPARTIAL* = (- 13)
- ERROR_INTERNAL* = (- 14)
- ERROR_BADCOUNT* = (- 15)
- ERROR_DFA_UITEM* = (- 16)
- ERROR_DFA_UCOND* = (- 17)
- ERROR_DFA_UMLIMIT* = (- 18)
- ERROR_DFA_WSSIZE* = (- 19)
- ERROR_DFA_RECURSE* = (- 20)
- ERROR_RECURSIONLIMIT* = (- 21)
- ERROR_NULLWSLIMIT* = (- 22) # No longer actually used
- ERROR_BADNEWLINE* = (- 23)
- ERROR_BADOFFSET* = (- 24)
- ERROR_SHORTUTF8* = (- 25)
- ERROR_RECURSELOOP* = (- 26)
- ERROR_JIT_STACKLIMIT* = (- 27)
- ERROR_BADMODE* = (- 28)
- ERROR_BADENDIANNESS* = (- 29)
- ERROR_DFA_BADRESTART* = (- 30)
-
-# Specific error codes for UTF-8 validity checks
+# Public options. Some are compile-time only, some are run-time only, and some
+# are both. Most of the compile-time options are saved with the compiled regex
+# so that they can be inspected during studying (and therefore JIT compiling).
+# Note that pcre_study() has its own set of options. Originally, all the options
+# defined here used distinct bits. However, almost all the bits in a 32-bit word
+# are now used, so in order to conserve them, option bits that were previously
+# only recognized at matching time (i.e. by pcre_exec() or pcre_dfa_exec()) may
+# also be used for compile-time options that affect only compiling and are not
+# relevant for studying or JIT compiling.
+#
+# Some options for pcre_compile() change its behaviour but do not affect the
+# behaviour of the execution functions. Other options are passed through to the
+# execution functions and affect their behaviour, with or without affecting the
+# behaviour of pcre_compile().
+#
+# Options that can be passed to pcre_compile() are tagged Cx below, with these
+# variants:
+#
+# C1 Affects compile only
+# C2 Does not affect compile; affects exec, dfa_exec
+# C3 Affects compile, exec, dfa_exec
+# C4 Affects compile, exec, dfa_exec, study
+# C5 Affects compile, exec, study
+#
+# Options that can be set for pcre_exec() and/or pcre_dfa_exec() are flagged
+# with E and D, respectively. They take precedence over C3, C4, and C5 settings
+# passed from pcre_compile(). Those that are compatible with JIT execution are
+# flagged with J.
const
- UTF8_ERR0* = 0
- UTF8_ERR1* = 1
- UTF8_ERR2* = 2
- UTF8_ERR3* = 3
- UTF8_ERR4* = 4
- UTF8_ERR5* = 5
- UTF8_ERR6* = 6
- UTF8_ERR7* = 7
- UTF8_ERR8* = 8
- UTF8_ERR9* = 9
+ CASELESS* = 0x00000001 # C1
+ MULTILINE* = 0x00000002 # C1
+ DOTALL* = 0x00000004 # C1
+ EXTENDED* = 0x00000008 # C1
+ ANCHORED* = 0x00000010 # C4 E D
+ DOLLAR_ENDONLY* = 0x00000020 # C2
+ EXTRA* = 0x00000040 # C1
+ NOTBOL* = 0x00000080 # E D J
+ NOTEOL* = 0x00000100 # E D J
+ UNGREEDY* = 0x00000200 # C1
+ NOTEMPTY* = 0x00000400 # E D J
+ UTF8* = 0x00000800 # C4 )
+ UTF16* = 0x00000800 # C4 ) Synonyms
+ UTF32* = 0x00000800 # C4 )
+ NO_AUTO_CAPTURE* = 0x00001000 # C1
+ NO_UTF8_CHECK* = 0x00002000 # C1 E D J )
+ NO_UTF16_CHECK* = 0x00002000 # C1 E D J ) Synonyms
+ NO_UTF32_CHECK* = 0x00002000 # C1 E D J )
+ AUTO_CALLOUT* = 0x00004000 # C1
+ PARTIAL_SOFT* = 0x00008000 # E D J ) Synonyms
+ PARTIAL* = 0x00008000 # E D J )
+
+# This pair use the same bit.
+const
+ NEVER_UTF* = 0x00010000 # C1 ) Overlaid
+ DFA_SHORTEST* = 0x00010000 # D ) Overlaid
+
+# This pair use the same bit.
+const
+ NO_AUTO_POSSESS* = 0x00020000 # C1 ) Overlaid
+ DFA_RESTART* = 0x00020000 # D ) Overlaid
+
+const
+ FIRSTLINE* = 0x00040000 # C3
+ DUPNAMES* = 0x00080000 # C1
+ NEWLINE_CR* = 0x00100000 # C3 E D
+ NEWLINE_LF* = 0x00200000 # C3 E D
+ NEWLINE_CRLF* = 0x00300000 # C3 E D
+ NEWLINE_ANY* = 0x00400000 # C3 E D
+ NEWLINE_ANYCRLF* = 0x00500000 # C3 E D
+ BSR_ANYCRLF* = 0x00800000 # C3 E D
+ BSR_UNICODE* = 0x01000000 # C3 E D
+ JAVASCRIPT_COMPAT* = 0x02000000 # C5
+ NO_START_OPTIMIZE* = 0x04000000 # C2 E D ) Synonyms
+ NO_START_OPTIMISE* = 0x04000000 # C2 E D )
+ PARTIAL_HARD* = 0x08000000 # E D J
+ NOTEMPTY_ATSTART* = 0x10000000 # E D J
+ UCP* = 0x20000000 # C3
+
+## Exec-time and get/set-time error codes
+const
+ ERROR_NOMATCH* = -1
+ ERROR_NULL* = -2
+ ERROR_BADOPTION* = -3
+ ERROR_BADMAGIC* = -4
+ ERROR_UNKNOWN_OPCODE* = -5
+ ERROR_UNKNOWN_NODE* = -5 ## For backward compatibility
+ ERROR_NOMEMORY* = -6
+ ERROR_NOSUBSTRING* = -7
+ ERROR_MATCHLIMIT* = -8
+ ERROR_CALLOUT* = -9 ## Never used by PCRE itself
+ ERROR_BADUTF8* = -10 ## Same for 8/16/32
+ ERROR_BADUTF16* = -10 ## Same for 8/16/32
+ ERROR_BADUTF32* = -10 ## Same for 8/16/32
+ ERROR_BADUTF8_OFFSET* = -11 ## Same for 8/16
+ ERROR_BADUTF16_OFFSET* = -11 ## Same for 8/16
+ ERROR_PARTIAL* = -12
+ ERROR_BADPARTIAL* = -13
+ ERROR_INTERNAL* = -14
+ ERROR_BADCOUNT* = -15
+ ERROR_DFA_UITEM* = -16
+ ERROR_DFA_UCOND* = -17
+ ERROR_DFA_UMLIMIT* = -18
+ ERROR_DFA_WSSIZE* = -19
+ ERROR_DFA_RECURSE* = -20
+ ERROR_RECURSIONLIMIT* = -21
+ ERROR_NULLWSLIMIT* = -22 ## No longer actually used
+ ERROR_BADNEWLINE* = -23
+ ERROR_BADOFFSET* = -24
+ ERROR_SHORTUTF8* = -25
+ ERROR_SHORTUTF16* = -25 ## Same for 8/16
+ ERROR_RECURSELOOP* = -26
+ ERROR_JIT_STACKLIMIT* = -27
+ ERROR_BADMODE* = -28
+ ERROR_BADENDIANNESS* = -29
+ ERROR_DFA_BADRESTART* = -30
+ ERROR_JIT_BADOPTION* = -31
+ ERROR_BADLENGTH* = -32
+ ERROR_UNSET* = -33
+
+## Specific error codes for UTF-8 validity checks
+const
+ UTF8_ERR0* = 0
+ UTF8_ERR1* = 1
+ UTF8_ERR2* = 2
+ UTF8_ERR3* = 3
+ UTF8_ERR4* = 4
+ UTF8_ERR5* = 5
+ UTF8_ERR6* = 6
+ UTF8_ERR7* = 7
+ UTF8_ERR8* = 8
+ UTF8_ERR9* = 9
UTF8_ERR10* = 10
UTF8_ERR11* = 11
UTF8_ERR12* = 12
@@ -165,193 +197,305 @@ const
UTF8_ERR19* = 19
UTF8_ERR20* = 20
UTF8_ERR21* = 21
+ UTF8_ERR22* = 22 # Unused (was non-character)
-# Request types for pcre_fullinfo()
-
-const
- INFO_OPTIONS* = 0
- INFO_SIZE* = 1
- INFO_CAPTURECOUNT* = 2
- INFO_BACKREFMAX* = 3
- INFO_FIRSTBYTE* = 4
- INFO_FIRSTCHAR* = 4 # For backwards compatibility
- INFO_FIRSTTABLE* = 5
- INFO_LASTLITERAL* = 6
- INFO_NAMEENTRYSIZE* = 7
- INFO_NAMECOUNT* = 8
- INFO_NAMETABLE* = 9
- INFO_STUDYSIZE* = 10
- INFO_DEFAULT_TABLES* = 11
- INFO_OKPARTIAL* = 12
- INFO_JCHANGED* = 13
- INFO_HASCRORLF* = 14
- INFO_MINLENGTH* = 15
- INFO_JIT* = 16
- INFO_JITSIZE* = 17
- INFO_MAXLOOKBEHIND* = 18
-
-# Request types for pcre_config(). Do not re-arrange, in order to remain
-# compatible.
-
-const
- CONFIG_UTF8* = 0
- CONFIG_NEWLINE* = 1
- CONFIG_LINK_SIZE* = 2
- CONFIG_POSIX_MALLOC_THRESHOLD* = 3
- CONFIG_MATCH_LIMIT* = 4
- CONFIG_STACKRECURSE* = 5
- CONFIG_UNICODE_PROPERTIES* = 6
- CONFIG_MATCH_LIMIT_RECURSION* = 7
- CONFIG_BSR* = 8
- CONFIG_JIT* = 9
- CONFIG_JITTARGET* = 11
-
-# Request types for pcre_study(). Do not re-arrange, in order to remain
-# compatible.
-
+## Specific error codes for UTF-16 validity checks
const
- STUDY_JIT_COMPILE* = 0x00000001
- STUDY_JIT_PARTIAL_SOFT_COMPILE* = 0x00000002
- STUDY_JIT_PARTIAL_HARD_COMPILE* = 0x00000004
+ UTF16_ERR0* = 0
+ UTF16_ERR1* = 1
+ UTF16_ERR2* = 2
+ UTF16_ERR3* = 3
+ UTF16_ERR4* = 4 # Unused (was non-character)
-# Bit flags for the pcre_extra structure. Do not re-arrange or redefine
-# these bits, just add new ones on the end, in order to remain compatible.
+## Specific error codes for UTF-32 validity checks
+const
+ UTF32_ERR0* = 0
+ UTF32_ERR1* = 1
+ UTF32_ERR2* = 2 # Unused (was non-character)
+ UTF32_ERR3* = 3
-const
- EXTRA_STUDY_DATA* = 0x00000001
- EXTRA_MATCH_LIMIT* = 0x00000002
- EXTRA_CALLOUT_DATA* = 0x00000004
- EXTRA_TABLES* = 0x00000008
- EXTRA_MATCH_LIMIT_RECURSION* = 0x00000010
- EXTRA_MARK* = 0x00000020
- EXTRA_EXECUTABLE_JIT* = 0x00000040
+## Request types for pcre_fullinfo()
+const
+ INFO_OPTIONS* = 0
+ INFO_SIZE* = 1
+ INFO_CAPTURECOUNT* = 2
+ INFO_BACKREFMAX* = 3
+ INFO_FIRSTBYTE* = 4
+ INFO_FIRSTCHAR* = 4 ## For backwards compatibility
+ INFO_FIRSTTABLE* = 5
+ INFO_LASTLITERAL* = 6
+ INFO_NAMEENTRYSIZE* = 7
+ INFO_NAMECOUNT* = 8
+ INFO_NAMETABLE* = 9
+ INFO_STUDYSIZE* = 10
+ INFO_DEFAULT_TABLES* = 11
+ INFO_OKPARTIAL* = 12
+ INFO_JCHANGED* = 13
+ INFO_HASCRORLF* = 14
+ INFO_MINLENGTH* = 15
+ INFO_JIT* = 16
+ INFO_JITSIZE* = 17
+ INFO_MAXLOOKBEHIND* = 18
+ INFO_FIRSTCHARACTER* = 19
+ INFO_FIRSTCHARACTERFLAGS* = 20
+ INFO_REQUIREDCHAR* = 21
+ INFO_REQUIREDCHARFLAGS* = 22
+ INFO_MATCHLIMIT* = 23
+ INFO_RECURSIONLIMIT* = 24
+ INFO_MATCH_EMPTY* = 25
-# Types
+## Request types for pcre_config(). Do not re-arrange, in order to remain
+## compatible.
+const
+ CONFIG_UTF8* = 0
+ CONFIG_NEWLINE* = 1
+ CONFIG_LINK_SIZE* = 2
+ CONFIG_POSIX_MALLOC_THRESHOLD* = 3
+ CONFIG_MATCH_LIMIT* = 4
+ CONFIG_STACKRECURSE* = 5
+ CONFIG_UNICODE_PROPERTIES* = 6
+ CONFIG_MATCH_LIMIT_RECURSION* = 7
+ CONFIG_BSR* = 8
+ CONFIG_JIT* = 9
+ CONFIG_UTF16* = 10
+ CONFIG_JITTARGET* = 11
+ CONFIG_UTF32* = 12
+ CONFIG_PARENS_LIMIT* = 13
-type
- TPcre*{.pure, final.} = object
- PPcre* = ptr TPcre
- Tjit_stack*{.pure, final.} = object
- Pjit_stack* = ptr Tjit_stack
+## Request types for pcre_study(). Do not re-arrange, in order to remain
+## compatible.
+const
+ STUDY_JIT_COMPILE* = 0x0001
+ STUDY_JIT_PARTIAL_SOFT_COMPILE* = 0x0002
+ STUDY_JIT_PARTIAL_HARD_COMPILE* = 0x0004
+ STUDY_EXTRA_NEEDED* = 0x0008
-# When PCRE is compiled as a C++ library, the subject pointer type can be
-# replaced with a custom type. For conventional use, the public interface is a
-# const char *.
-
-# The structure for passing additional data to pcre_exec(). This is defined in
-# such as way as to be extensible. Always add new fields at the end, in order to
-# remain compatible.
-
-type
- TExtra*{.pure, final.} = object
- flags*: int ## Bits for which fields are set
- study_data*: pointer ## Opaque data from pcre_study()
- match_limit*: int ## Maximum number of calls to match()
- callout_data*: pointer ## Data passed back in callouts
- tables*: cstring ## Pointer to character tables
- match_limit_recursion*: int ## Max recursive calls to match()
- mark*: ptr cstring ## For passing back a mark pointer
- executable_jit*: pointer ## Contains a pointer to a compiled jit code
-
-
-# The structure for passing out data via the pcre_callout_function. We use a
-# structure so that new fields can be added on the end in future versions,
-# without changing the API of the function, thereby allowing old clients to work
-# without modification.
-
-type
- TCalloutBlock*{.pure, final.} = object
- version*: cint ## Identifies version of block
- callout_number*: cint ## Number compiled into pattern
- offset_vector*: ptr cint ## The offset vector
- subject*: cstring ## The subject being matched
- subject_length*: cint ## The length of the subject
- start_match*: cint ## Offset to start of this match attempt
- current_position*: cint ## Where we currently are in the subject
- capture_top*: cint ## Max current capture
- capture_last*: cint ## Most recently closed capture
- callout_data*: pointer ## Data passed in with the call
- pattern_position*: cint ## Offset to next item in the pattern
- next_item_length*: cint ## Length of next item in the pattern
- mark*: cstring ## Pointer to current mark or NULL
-
-# Indirection for store get and free functions. These can be set to
-#alternative malloc/free functions if required. Special ones are used in the
-#non-recursive case for "frames". There is also an optional callout function
-#that is triggered by the (?) regex item. For Virtual Pascal, these definitions
-#have to take another form.
-
-# User defined callback which provides a stack just before the match starts.
+## Bit flags for the pcre[16|32]_extra structure. Do not re-arrange or redefine
+## these bits, just add new ones on the end, in order to remain compatible.
+const
+ EXTRA_STUDY_DATA* = 0x0001
+ EXTRA_MATCH_LIMIT* = 0x0002
+ EXTRA_CALLOUT_DATA* = 0x0004
+ EXTRA_TABLES* = 0x0008
+ EXTRA_MATCH_LIMIT_RECURSION* = 0x0010
+ EXTRA_MARK* = 0x0020
+ EXTRA_EXECUTABLE_JIT* = 0x0040
+## Types
type
- TJitCallback* = proc(p: pointer): ptr Tjit_stack{.cdecl.}
+ Pcre* = object
+ Pcre16* = object
+ Pcre32* = object
+ JitStack* = object
+ JitStack16* = object
+ JitStack32* = object
-# Exported PCRE functions
-proc compile*(a2: cstring, a3: cint, a4: ptr cstring, a5: ptr cint,
- a6: ptr char): ptr TPcre{.cdecl, importc: "pcre_compile",
- pcreImport.}
-proc compile2*(a2: cstring, a3: cint, a4: ptr cint, a5: ptr cstring,
- a6: ptr cint, a7: ptr char): ptr TPcre{.cdecl,
- importc: "pcre_compile2", pcreImport.}
-proc config*(a2: cint, a3: pointer): cint{.cdecl, importc: "pcre_config",
- pcreImport.}
-proc copy_named_substring*(a2: ptr TPcre, a3: cstring, a4: ptr cint, a5: cint,
- a6: cstring, a7: cstring, a8: cint): cint{.cdecl,
- importc: "pcre_copy_named_substring", pcreImport.}
-proc copy_substring*(a2: cstring, a3: ptr cint, a4: cint, a5: cint,
- a6: cstring,
- a7: cint): cint{.cdecl, importc: "pcre_copy_substring",
- pcreImport.}
-proc dfa_exec*(a2: ptr TPcre, a3: ptr TExtra, a4: cstring, a5: cint,
- a6: cint, a7: cint, a8: ptr cint, a9: cint, a10: ptr cint,
- a11: cint): cint{.cdecl, importc: "pcre_dfa_exec",
- pcreImport.}
-proc exec*(a2: ptr TPcre, a3: ptr TExtra, a4: cstring, a5: cint, a6: cint,
- a7: cint, a8: ptr cint, a9: cint): cint {.
- cdecl, importc: "pcre_exec", pcreImport.}
-proc free_substring*(a2: cstring){.cdecl, importc: "pcre_free_substring",
- pcreImport.}
-proc free_substring_list*(a2: cstringArray){.cdecl,
- importc: "pcre_free_substring_list", pcreImport.}
-proc fullinfo*(a2: ptr TPcre, a3: ptr TExtra, a4: cint, a5: pointer): cint{.
- cdecl, importc: "pcre_fullinfo", pcreImport.}
-proc get_named_substring*(a2: ptr TPcre, a3: cstring, a4: ptr cint, a5: cint,
- a6: cstring, a7: cstringArray): cint{.cdecl,
- importc: "pcre_get_named_substring", pcreImport.}
-proc get_stringnumber*(a2: ptr TPcre, a3: cstring): cint{.cdecl,
- importc: "pcre_get_stringnumber", pcreImport.}
-proc get_stringtable_entries*(a2: ptr TPcre, a3: cstring, a4: cstringArray,
- a5: cstringArray): cint{.cdecl,
- importc: "pcre_get_stringtable_entries", pcreImport.}
-proc get_substring*(a2: cstring, a3: ptr cint, a4: cint, a5: cint,
- a6: cstringArray): cint{.cdecl,
- importc: "pcre_get_substring", pcreImport.}
-proc get_substring_list*(a2: cstring, a3: ptr cint, a4: cint,
- a5: ptr cstringArray): cint{.cdecl,
- importc: "pcre_get_substring_list", pcreImport.}
-proc maketables*(): ptr char{.cdecl, importc: "pcre_maketables",
- pcreImport.}
-proc refcount*(a2: ptr TPcre, a3: cint): cint{.cdecl, importc: "pcre_refcount",
- pcreImport.}
-proc study*(a2: ptr TPcre, a3: cint, a4: var cstring): ptr TExtra{.cdecl,
- importc: "pcre_study", pcreImport.}
-proc version*(): cstring{.cdecl, importc: "pcre_version", pcreImport.}
+## The structure for passing additional data to pcre_exec(). This is defined in
+## such as way as to be extensible. Always add new fields at the end, in order
+## to remain compatible.
+type
+ ExtraData* = object
+ flags*: clong ## Bits for which fields are set
+ study_data*: pointer ## Opaque data from pcre_study()
+ match_limit*: clong ## Maximum number of calls to match()
+ callout_data*: pointer ## Data passed back in callouts
+ tables*: pointer ## Pointer to character tables
+ match_limit_recursion*: clong ## Max recursive calls to match()
+ mark*: pointer ## For passing back a mark pointer
+ executable_jit*: pointer ## Contains a pointer to a compiled jit code
+
+## The structure for passing out data via the pcre_callout_function. We use a
+## structure so that new fields can be added on the end in future versions,
+## without changing the API of the function, thereby allowing old clients to
+## work without modification.
+type
+ CalloutBlock* = object
+ version* : cint ## Identifies version of block
+ # ------------------------ Version 0 -------------------------------
+ callout_number* : cint ## Number compiled into pattern
+ offset_vector* : ptr cint ## The offset vector
+ subject* : cstring ## The subject being matched
+ subject_length* : cint ## The length of the subject
+ start_match* : cint ## Offset to start of this match attempt
+ current_position*: cint ## Where we currently are in the subject
+ capture_top* : cint ## Max current capture
+ capture_last* : cint ## Most recently closed capture
+ callout_data* : pointer ## Data passed in with the call
+ # ------------------- Added for Version 1 --------------------------
+ pattern_position*: cint ## Offset to next item in the pattern
+ next_item_length*: cint ## Length of next item in the pattern
+ # ------------------- Added for Version 2 --------------------------
+ mark* : pointer ## Pointer to current mark or NULL
+ # ------------------------------------------------------------------
+
+
+## User defined callback which provides a stack just before the match starts.
+type
+ JitCallback* = proc (a: pointer): ptr JitStack {.cdecl.}
+
+
+when not defined(usePcreHeader):
+ when hostOS == "windows":
+ const pcreDll = "pcre.dll"
+ elif hostOS == "macosx":
+ const pcreDll = "libpcre(.3|.1|).dylib"
+ else:
+ const pcreDll = "libpcre.so(.3|.1|)"
+ {.push dynlib: pcreDll.}
+else:
+ {.push header: "".}
+
+{.push cdecl, importc: "pcre_$1".}
+
+# Exported PCRE functions
+
+proc compile*(pattern: cstring,
+ options: cint,
+ errptr: ptr cstring,
+ erroffset: ptr cint,
+ tableptr: pointer): ptr Pcre
+
+proc compile2*(pattern: cstring,
+ options: cint,
+ errorcodeptr: ptr cint,
+ errptr: ptr cstring,
+ erroffset: ptr cint,
+ tableptr: pointer): ptr Pcre
+
+proc config*(what: cint,
+ where: pointer): cint
+
+proc copy_named_substring*(code: ptr Pcre,
+ subject: cstring,
+ ovector: ptr cint,
+ stringcount: cint,
+ stringname: cstring,
+ buffer: cstring,
+ buffersize: cint): cint
+
+proc copy_substring*(subject: cstring,
+ ovector: ptr cint,
+ stringcount: cint,
+ stringnumber: cint,
+ buffer: cstring,
+ buffersize: cint): cint
+
+proc dfa_exec*(code: ptr Pcre,
+ extra: ptr ExtraData,
+ subject: cstring,
+ length: cint,
+ startoffset: cint,
+ options: cint,
+ ovector: ptr cint,
+ ovecsize: cint,
+ workspace: ptr cint,
+ wscount: cint): cint
+
+proc exec*(code: ptr Pcre,
+ extra: ptr ExtraData,
+ subject: cstring,
+ length: cint,
+ startoffset: cint,
+ options: cint,
+ ovector: ptr cint,
+ ovecsize: cint): cint
+
+proc jit_exec*(code: ptr Pcre,
+ extra: ptr ExtraData,
+ subject: cstring,
+ length: cint,
+ startoffset: cint,
+ options: cint,
+ ovector: ptr cint,
+ ovecsize: cint,
+ jstack: ptr JitStack): cint
+
+proc free_substring*(stringptr: cstring)
+
+proc free_substring_list*(stringptr: cstringArray)
+
+proc fullinfo*(code: ptr Pcre,
+ extra: ptr ExtraData,
+ what: cint,
+ where: pointer): cint
+
+proc get_named_substring*(code: ptr Pcre,
+ subject: cstring,
+ ovector: ptr cint,
+ stringcount: cint,
+ stringname: cstring,
+ stringptr: cstringArray): cint
+
+proc get_stringnumber*(code: ptr Pcre,
+ name: cstring): cint
+
+proc get_stringtable_entries*(code: ptr Pcre,
+ name: cstring,
+ first: cstringArray,
+ last: cstringArray): cint
+
+proc get_substring*(subject: cstring,
+ ovector: ptr cint,
+ stringcount: cint,
+ stringnumber: cint,
+ stringptr: cstringArray): cint
+
+proc get_substring_list*(subject: cstring,
+ ovector: ptr cint,
+ stringcount: cint,
+ listptr: ptr cstringArray): cint
+
+proc maketables*(): pointer
+
+proc refcount*(code: ptr Pcre,
+ adjust: cint): cint
+
+proc study*(code: ptr Pcre,
+ options: cint,
+ errptr: ptr cstring): ptr ExtraData
+
+proc free_study*(extra: ptr ExtraData)
+
+proc version*(): cstring
# Utility functions for byte order swaps.
-proc pattern_to_host_byte_order*(a2: ptr TPcre, a3: ptr TExtra,
- a4: ptr char): cint{.cdecl, importc: "pcre_pattern_to_host_byte_order",
- pcreImport.}
+proc pattern_to_host_byte_order*(code: ptr Pcre,
+ extra: ptr ExtraData,
+ tables: pointer): cint
# JIT compiler related functions.
-proc jit_stack_alloc*(a2: cint, a3: cint): ptr Tjit_stack{.cdecl,
- importc: "pcre_jit_stack_alloc", pcreImport.}
-proc jit_stack_free*(a2: ptr Tjit_stack){.cdecl, importc: "pcre_jit_stack_free",
- pcreImport.}
-proc assign_jit_stack*(a2: ptr TExtra, a3: TJitCallback, a4: pointer){.cdecl,
- importc: "pcre_assign_jit_stack", pcreImport.}
+proc jit_stack_alloc*(startsize: cint,
+ maxsize: cint): ptr JitStack
-var
- pcre_free*: proc (p: ptr TPcre) {.cdecl.}
+proc jit_stack_free*(stack: ptr JitStack)
+
+proc assign_jit_stack*(extra: ptr ExtraData,
+ callback: JitCallback,
+ data: pointer)
+
+proc jit_free_unused_memory*()
+
+
+# There was an odd function with `var cstring` instead of `ptr`
+proc study*(code: ptr Pcre,
+ options: cint,
+ errptr: var cstring): ptr ExtraData {.deprecated.}
+
+{.pop.}
+{.pop.}
+
+
+{.deprecated: [MAJOR: PCRE_MAJOR, MINOR: PCRE_MINOR,
+ PRERELEASE: PCRE_PRERELEASE, DATE: PCRE_DATE].}
+
+{.deprecated: [TPcre: Pcre, TJitStack: JitStack].}
+type
+ PPcre* {.deprecated.} = ptr Pcre
+ PJitStack* {.deprecated.} = ptr JitStack
+
+{.deprecated: [TExtra: ExtraData].}
+{.deprecated: [TCalloutBlock: CalloutBlock].}
+{.deprecated: [TJitCallback: JitCallback].}
diff --git a/tests/bind/tnicerrorforsymchoice.nim b/tests/bind/tnicerrorforsymchoice.nim
index bf6d92927f..5145fdcff6 100644
--- a/tests/bind/tnicerrorforsymchoice.nim
+++ b/tests/bind/tnicerrorforsymchoice.nim
@@ -1,17 +1,17 @@
discard """
line: 18
- errormsg: "type mismatch: got (proc (TScgi) | proc (AsyncSocket, StringTableRef, string)"
+ errormsg: "type mismatch: got (proc (s: TScgi) | proc (client: AsyncSocket, headers: StringTableRef, input: string){.gcsafe, locks: 0.}"
"""
#bug #442
import scgi, sockets, asyncio, strtabs
proc handleSCGIRequest[TScgi: ScgiState | AsyncScgiState](s: TScgi) =
discard
-proc handleSCGIRequest(client: AsyncSocket, headers: StringTableRef,
+proc handleSCGIRequest(client: AsyncSocket, headers: StringTableRef,
input: string) =
discard
-proc test(handle: proc (client: AsyncSocket, headers: StringTableRef,
+proc test(handle: proc (client: AsyncSocket, headers: StringTableRef,
input: string), b: int) =
discard
diff --git a/tests/ccgbugs/tpartialcs.nim b/tests/ccgbugs/tpartialcs.nim
new file mode 100644
index 0000000000..12ff65c376
--- /dev/null
+++ b/tests/ccgbugs/tpartialcs.nim
@@ -0,0 +1,20 @@
+
+# bug #2551
+
+type Tup = tuple
+ A, a: int
+
+type Obj = object
+ A, a: int
+
+var x: Tup # This works.
+var y: Obj # This doesn't.
+
+# bug #2212
+
+proc f() =
+ let
+ p = 1.0
+ P = 0.25 + 0.5
+
+f()
diff --git a/tests/closure/tinvalidclosure.nim b/tests/closure/tinvalidclosure.nim
index 18968a6c6b..c9136a7368 100644
--- a/tests/closure/tinvalidclosure.nim
+++ b/tests/closure/tinvalidclosure.nim
@@ -1,6 +1,6 @@
discard """
line: 12
- errormsg: "type mismatch: got (proc (int){.closure, gcsafe, locks: 0.})"
+ errormsg: "type mismatch: got (proc (x: int){.closure, gcsafe, locks: 0.})"
"""
proc ugh[T](x: T) {.closure.} =
diff --git a/tests/collections/tcounttable.nim b/tests/collections/tcounttable.nim
new file mode 100644
index 0000000000..ebbb1c8e51
--- /dev/null
+++ b/tests/collections/tcounttable.nim
@@ -0,0 +1,19 @@
+discard """
+ output: "And we get here"
+"""
+
+# bug #2625
+
+const s_len = 32
+
+import tables
+var substr_counts: CountTable[string] = initCountTable[string]()
+var my_string = "Hello, this is sadly broken for strings over 64 characters. Note that it *does* appear to work for short strings."
+for i in 0..(my_string.len - s_len):
+ let s = my_string[i..i+s_len-1]
+ substr_counts[s] = 1
+ # substr_counts[s] = substr_counts[s] + 1 # Also breaks, + 2 as well, etc.
+ # substr_counts.inc(s) # This works
+ #echo "Iteration ", i
+
+echo "And we get here"
diff --git a/tests/collections/tsets.nim b/tests/collections/tsets.nim
index 656c5b3f2d..a5bbe8dbd6 100644
--- a/tests/collections/tsets.nim
+++ b/tests/collections/tsets.nim
@@ -1,17 +1,37 @@
-discard """
- output: '''true
-true'''
-"""
-
import sets
-var
- a = initSet[int]()
- b = initSet[int]()
- c = initSet[string]()
-for i in 0..5: a.incl(i)
-for i in 1..6: b.incl(i)
-for i in 0..5: c.incl($i)
+block setEquality:
+ var
+ a = initSet[int]()
+ b = initSet[int]()
+ c = initSet[string]()
+
+ for i in 0..5: a.incl(i)
+ for i in 1..6: b.incl(i)
+ for i in 0..5: c.incl($i)
+
+ doAssert map(a, proc(x: int): int = x + 1) == b
+ doAssert map(a, proc(x: int): string = $x) == c
+
+
+block setsContainingTuples:
+ var set = initSet[tuple[i: int, i64: int64, f: float]]()
+ set.incl( (i: 123, i64: 123'i64, f: 3.14) )
+ doAssert set.contains( (i: 123, i64: 123'i64, f: 3.14) )
+ doAssert( not set.contains( (i: 456, i64: 789'i64, f: 2.78) ) )
+
+
+block setWithTuplesWithSeqs:
+ var s = initSet[tuple[s: seq[int]]]()
+ s.incl( (s: @[1, 2, 3]) )
+ doAssert s.contains( (s: @[1, 2, 3]) )
+ doAssert( not s.contains((s: @[4, 5, 6])) )
+
+
+block setWithSequences:
+ var s = initSet[seq[int]]()
+ s.incl( @[1, 2, 3] )
+ doAssert s.contains(@[1, 2, 3])
+ doAssert( not s.contains(@[4, 5, 6]) )
+
-echo map(a, proc(x: int): int = x + 1) == b
-echo map(a, proc(x: int): string = $x) == c
diff --git a/tests/cpp/tcppraise.nim b/tests/cpp/tcppraise.nim
new file mode 100644
index 0000000000..a9ea8e6ce3
--- /dev/null
+++ b/tests/cpp/tcppraise.nim
@@ -0,0 +1,17 @@
+discard """
+ cmd: "nim cpp $file"
+ output: '''foo
+bar
+Need odd and >= 3 digits##
+baz'''
+"""
+
+# bug #1888
+echo "foo"
+try:
+ echo "bar"
+ raise newException(ValueError, "Need odd and >= 3 digits")
+# echo "baz"
+except ValueError:
+ echo getCurrentExceptionMsg(), "##"
+echo "baz"
diff --git a/tests/cpp/get_subsystem.nim b/tests/cpp/tget_subsystem.nim
similarity index 75%
rename from tests/cpp/get_subsystem.nim
rename to tests/cpp/tget_subsystem.nim
index 38593b03aa..4619147390 100644
--- a/tests/cpp/get_subsystem.nim
+++ b/tests/cpp/tget_subsystem.nim
@@ -16,7 +16,8 @@ struct SystemManager {
""".}
type Input {.importcpp: "System::Input".} = object
-proc getSubsystem*[T](): ptr T {.importcpp: "SystemManager::getSubsystem<'*0>()".}
+proc getSubsystem*[T](): ptr T {.
+ importcpp: "SystemManager::getSubsystem<'*0>()", nodecl.}
let input: ptr Input = getSubsystem[Input]()
diff --git a/tests/cpp/vector_iterator.nim b/tests/cpp/tvector_iterator.nim
similarity index 100%
rename from tests/cpp/vector_iterator.nim
rename to tests/cpp/tvector_iterator.nim
diff --git a/tests/cpp/tvectorseq.nim b/tests/cpp/tvectorseq.nim
new file mode 100644
index 0000000000..6eb5dc9e42
--- /dev/null
+++ b/tests/cpp/tvectorseq.nim
@@ -0,0 +1,38 @@
+discard """
+ output: '''(x: 1.0)
+(x: 0.0)'''
+ cmd: "nim cpp $file"
+ disabled: "true"
+"""
+
+# This cannot work yet because we omit type information for importcpp'ed types.
+# Fixing this is not hard, but also requires fixing Urhonimo.
+
+# bug #2536
+
+{.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}
+
+# hack around another codegen issue: Generics are attached to where they came
+# from:
+proc `$!`(v: seq[Vector3]): string = "(x: " & $v[0].x & ")"
+
+proc vec3List*(): seq[Vector3] =
+ let s = @[constructVector3(cfloat(1))]
+ echo($!s)
+ result = s
+ echo($!result)
+
+let f = vec3List()
+#echo($!f)
diff --git a/tests/distinct/tdistinct_consts.nim b/tests/distinct/tdistinct_consts.nim
new file mode 100644
index 0000000000..4f6ced2d2f
--- /dev/null
+++ b/tests/distinct/tdistinct_consts.nim
@@ -0,0 +1,20 @@
+
+# bug #2641
+
+type MyChar = distinct char
+const c:MyChar = MyChar('a')
+
+type MyBool = distinct bool
+const b:MyBool = MyBool(true)
+
+type MyBoolSet = distinct set[bool]
+const bs:MyBoolSet = MyBoolSet({true})
+
+type MyCharSet= distinct set[char]
+const cs:MyCharSet = MyCharSet({'a'})
+
+type MyBoolSeq = distinct seq[bool]
+const bseq:MyBoolSeq = MyBoolSeq(@[true, false])
+
+type MyBoolArr = distinct array[3, bool]
+const barr:MyBoolArr = MyBoolArr([true, false, true])
diff --git a/tests/exception/texceptionbreak.nim b/tests/exception/texceptionbreak.nim
index 76e986787f..00dd8ed9fc 100644
--- a/tests/exception/texceptionbreak.nim
+++ b/tests/exception/texceptionbreak.nim
@@ -5,20 +5,20 @@ discard """
# First variety
try:
- raise newException(EOS, "Problem")
-except EOS:
+ raise newException(OSError, "Problem")
+except OSError:
for y in [1, 2, 3]:
discard
try:
discard
- except EOS:
+ except OSError:
discard
echo "1"
# Second Variety
try:
- raise newException(EOS, "Problem")
-except EOS:
+ raise newException(OSError, "Problem")
+except OSError:
for y in [1, 2, 3]:
discard
for y in [1, 2, 3]:
@@ -28,8 +28,8 @@ echo "2"
# Third Variety
try:
- raise newException(EOS, "Problem")
-except EOS:
+ raise newException(OSError, "Problem")
+except OSError:
block:
break
@@ -38,8 +38,8 @@ echo "3"
# Fourth Variety
block:
try:
- raise newException(EOS, "Problem")
- except EOS:
+ raise newException(OSError, "Problem")
+ except OSError:
break
-echo "4"
\ No newline at end of file
+echo "4"
diff --git a/tests/exception/texceptions.nim b/tests/exception/texceptions.nim
index 69b2d0f6a4..bdf3385997 100644
--- a/tests/exception/texceptions.nim
+++ b/tests/exception/texceptions.nim
@@ -35,9 +35,9 @@ echo ""
proc reraise_in_except =
try:
echo "BEFORE"
- raise newException(EIO, "")
+ raise newException(IOError, "")
- except EIO:
+ except IOError:
echo "EXCEPT"
raise
@@ -52,7 +52,7 @@ echo ""
proc return_in_except =
try:
echo "BEFORE"
- raise newException(EIO, "")
+ raise newException(IOError, "")
except:
echo "EXCEPT"
diff --git a/tests/exception/texcsub.nim b/tests/exception/texcsub.nim
index 3dba357f9d..02125d2c03 100644
--- a/tests/exception/texcsub.nim
+++ b/tests/exception/texcsub.nim
@@ -5,12 +5,12 @@ discard """
# Test inheritance for exception matching:
try:
- raise newException(EOS, "dummy message")
-except E_Base:
+ raise newException(OSError, "dummy message")
+except Exception:
echo "caught!"
-except:
+except:
echo "wtf!?"
-
+
#OUT caught!
diff --git a/tests/exception/tfinally4.nim b/tests/exception/tfinally4.nim
index 05c57c4f5c..3aa707ff6e 100644
--- a/tests/exception/tfinally4.nim
+++ b/tests/exception/tfinally4.nim
@@ -8,19 +8,19 @@ discard """
var raiseEx = true
var returnA = true
var returnB = false
-
-proc main: int =
+
+proc main: int =
try: #A
try: #B
if raiseEx:
- raise newException(EOS, "")
+ raise newException(OSError, "")
return 3
finally: #B
echo "B1"
if returnB:
return 2
echo "B2"
- except EOS: #A
+ except OSError: #A
echo "catch"
finally: #A
echo "A1"
diff --git a/tests/exception/tnestedreturn.nim b/tests/exception/tnestedreturn.nim
index 591638f0ef..1480764f1b 100644
--- a/tests/exception/tnestedreturn.nim
+++ b/tests/exception/tnestedreturn.nim
@@ -7,7 +7,7 @@ discard """
proc test1() =
- finally: echo "A"
+ defer: echo "A"
try:
raise newException(OSError, "Problem")
@@ -19,7 +19,7 @@ test1()
proc test2() =
- finally: echo "B"
+ defer: echo "B"
try:
return
diff --git a/tests/exception/tonraise.nim b/tests/exception/tonraise.nim
index 1a555dd94e..a155f0b8e3 100644
--- a/tests/exception/tonraise.nim
+++ b/tests/exception/tonraise.nim
@@ -4,8 +4,8 @@ success'''
"""
type
- ESomething = object of E_Base
- ESomeOtherErr = object of E_Base
+ ESomething = object of Exception
+ ESomeOtherErr = object of Exception
proc genErrors(s: string) =
if s == "error!":
@@ -17,14 +17,14 @@ proc foo() =
var i = 0
try:
inc i
- onRaise(proc (e: ref E_Base): bool =
+ onRaise(proc (e: ref Exception): bool =
echo "i: ", i)
genErrors("errssor!")
except ESomething:
echo("ESomething happened")
except:
echo("Some other error happened")
-
+
# test that raise handler is gone:
try:
genErrors("error!")
diff --git a/tests/exception/treraise.nim b/tests/exception/treraise.nim
index cbd0b5f8a1..b2a11d34f2 100644
--- a/tests/exception/treraise.nim
+++ b/tests/exception/treraise.nim
@@ -4,8 +4,8 @@ discard """
exitcode: "1"
"""
type
- ESomething = object of E_Base
- ESomeOtherErr = object of E_Base
+ ESomething = object of Exception
+ ESomeOtherErr = object of Exception
proc genErrors(s: string) =
if s == "error!":
diff --git a/tests/js/tstringitems.nim b/tests/js/tstringitems.nim
new file mode 100644
index 0000000000..f4ea02fec9
--- /dev/null
+++ b/tests/js/tstringitems.nim
@@ -0,0 +1,24 @@
+discard """
+ output: '''Hello
+Hello'''
+"""
+
+# bug #2581
+
+const someVars = [ "Hello" ]
+var someVars2 = [ "Hello" ]
+
+proc getSomeVar: string =
+ for i in someVars:
+ if i == "Hello":
+ result = i
+ break
+
+proc getSomeVar2: string =
+ for i in someVars2:
+ if i == "Hello":
+ result = i
+ break
+
+echo getSomeVar()
+echo getSomeVar2()
diff --git a/tests/js/tunittests.nim b/tests/js/tunittests.nim
index af38cd9b90..8a264a5e02 100644
--- a/tests/js/tunittests.nim
+++ b/tests/js/tunittests.nim
@@ -1,3 +1,10 @@
+discard """
+ disabled: "true"
+"""
+
+# Unittest uses lambdalifting at compile-time which we disable for the JS
+# codegen! So this cannot and will not work for quite some time.
+
import unittest
suite "Bacon":
diff --git a/tests/macros/tlexerex.nim b/tests/macros/tlexerex.nim
new file mode 100644
index 0000000000..d348a4bcc5
--- /dev/null
+++ b/tests/macros/tlexerex.nim
@@ -0,0 +1,16 @@
+
+import macros
+
+macro match*(s: cstring|string; pos: int; sections: untyped): untyped =
+ for sec in sections.children:
+ expectKind sec, nnkOfBranch
+ expectLen sec, 2
+ result = newStmtList()
+
+when isMainModule:
+ var input = "the input"
+ var pos = 0
+ match input, pos:
+ of r"[a-zA-Z_]\w+": echo "an identifier"
+ of r"\d+": echo "an integer"
+ of r".": echo "something else"
diff --git a/tests/macros/treturnsempty.nim b/tests/macros/treturnsempty.nim
new file mode 100644
index 0000000000..7af26a7472
--- /dev/null
+++ b/tests/macros/treturnsempty.nim
@@ -0,0 +1,12 @@
+discard """
+ errormsg: "type mismatch"
+ line: 11
+"""
+# bug #2372
+macro foo(dummy: int): stmt =
+ discard
+
+proc takeStr(s: string) = echo s
+
+takeStr foo(12)
+
diff --git a/tests/macros/typesapi2.nim b/tests/macros/typesapi2.nim
index 016295ba4f..2e59d2154e 100644
--- a/tests/macros/typesapi2.nim
+++ b/tests/macros/typesapi2.nim
@@ -1,4 +1,4 @@
-# tests to see if a symbol returned from macros.getType() can
+# tests to see if a symbol returned from macros.getType() can
# be used as a type
import macros
@@ -20,7 +20,7 @@ static: assert iii is TestFN
proc foo11 : testTypesym(void) =
echo "HI!"
-static: assert foo11 is proc():void
+static: assert foo11 is (proc():void {.nimcall.})
var sss: testTypesym(seq[int])
static: assert sss is seq[int]
diff --git a/tests/misc/tunsigned64mod.nim b/tests/misc/tunsigned64mod.nim
index 9ae0d535a9..3007405a2e 100644
--- a/tests/misc/tunsigned64mod.nim
+++ b/tests/misc/tunsigned64mod.nim
@@ -10,3 +10,17 @@ let t1 = v1 mod 2 # works
let t2 = 7'u64 mod 2'u64 # works
let t3 = v2 mod 2'u64 # Error: invalid type: 'range 0..1(uint64)
let t4 = (v2 mod 2'u64).uint64 # works
+
+# bug #2550
+
+var x: uint # doesn't work
+echo x mod 2 == 0
+
+var y: uint64 # doesn't work
+echo y mod 2 == 0
+
+var z: uint32 # works
+echo z mod 2 == 0
+
+var a: int # works
+echo a mod 2 == 0
diff --git a/tests/overload/tspec.nim b/tests/overload/tspec.nim
index 685df503a1..f2002a390e 100644
--- a/tests/overload/tspec.nim
+++ b/tests/overload/tspec.nim
@@ -11,7 +11,10 @@ ref T
123
2
1
-@[123, 2, 1]'''
+@[123, 2, 1]
+Called!
+merge with var
+merge no var'''
"""
# Things that's even in the spec now!
@@ -79,3 +82,37 @@ proc takeV[T](a: varargs[T]) =
takeV([123, 2, 1]) # takeV's T is "int", not "array of int"
echo(@[123, 2, 1])
+
+# bug #2600
+
+type
+ FutureBase* = ref object of RootObj ## Untyped future.
+
+ Future*[T] = ref object of FutureBase ## Typed future.
+ value: T ## Stored value
+
+ FutureVar*[T] = distinct Future[T]
+
+proc newFuture*[T](): Future[T] =
+ new(result)
+
+proc newFutureVar*[T](): FutureVar[T] =
+ result = FutureVar[T](newFuture[T]())
+
+proc mget*[T](future: FutureVar[T]): var T =
+ Future[T](future).value
+
+proc reset*[T](future: FutureVar[T]) =
+ echo "Called!"
+
+proc merge[T](x: Future[T]) = echo "merge no var"
+proc merge[T](x: var Future[T]) = echo "merge with var"
+
+when true:
+ var foo = newFutureVar[string]()
+ foo.mget() = ""
+ foo.mget.add("Foobar")
+ foo.reset()
+ var bar = newFuture[int]()
+ bar.merge # merge with var
+ merge(newFuture[int]()) # merge no var
diff --git a/tests/parallel/twrong_refcounts.nim b/tests/parallel/twrong_refcounts.nim
new file mode 100644
index 0000000000..db32a96d80
--- /dev/null
+++ b/tests/parallel/twrong_refcounts.nim
@@ -0,0 +1,53 @@
+discard """
+ output: "Success"
+"""
+
+import math, threadPool
+
+# ---
+
+type
+ Person = object
+ age: int
+ friend: ref Person
+
+var
+ people: seq[ref Person] = @[]
+
+proc newPerson(age:int): ref Person =
+ result.new()
+ result.age = age
+
+proc greet(p:Person) =
+ #echo p.age, ", ", p.friend.age
+ p.friend.age += 1
+
+# ---
+
+proc setup =
+ for i in 0 .. <20:
+ people.add newPerson(i + 1)
+ for i in 0 .. <20:
+ people[i].friend = people[random(20)]
+
+proc update =
+ var countA: array[20, int]
+ var countB: array[20, int]
+
+ for i, p in people:
+ countA[i] = getRefCount(p)
+ parallel:
+ for i in 0 .. people.high:
+ spawn greet(people[i][])
+ for i, p in people:
+ countB[i] = getRefCount(p)
+
+ for i in 0 .. <20:
+ doAssert countA[i] == countB[i]
+ echo "Success"
+
+# ---
+
+when isMainModule:
+ setup()
+ update()
diff --git a/tests/parser/tinvcolonlocation1.nim b/tests/parser/tinvcolonlocation1.nim
new file mode 100644
index 0000000000..cacde48bd7
--- /dev/null
+++ b/tests/parser/tinvcolonlocation1.nim
@@ -0,0 +1,12 @@
+discard """
+ file: "tinvcolonlocation1.nim"
+ line: 8
+ column: 3
+ errormsg: "':' expected"
+"""
+try #<- missing ':'
+ echo "try"
+except:
+ echo "except"
+finally:
+ echo "finally"
diff --git a/tests/parser/tinvcolonlocation2.nim b/tests/parser/tinvcolonlocation2.nim
new file mode 100644
index 0000000000..2b6a92b9d8
--- /dev/null
+++ b/tests/parser/tinvcolonlocation2.nim
@@ -0,0 +1,15 @@
+discard """
+ file: "tinvcolonlocation2.nim"
+ line: 11
+ column: 1
+ errormsg: "':' expected"
+"""
+try:
+ echo "try"
+except #<- missing ':'
+ echo "except"
+finally:
+#<-- error will be here above, at the beginning of finally,
+# since compiler tries to consome echo and part of except
+# expression
+ echo "finally"
diff --git a/tests/parser/tinvcolonlocation3.nim b/tests/parser/tinvcolonlocation3.nim
new file mode 100644
index 0000000000..2b30b1dbe9
--- /dev/null
+++ b/tests/parser/tinvcolonlocation3.nim
@@ -0,0 +1,12 @@
+discard """
+ file: "tinvcolonlocation3.nim"
+ line: 12
+ column: 3
+ errormsg: "':' expected"
+"""
+try:
+ echo "try"
+except:
+ echo "except"
+finally #<- missing ':'
+ echo "finally"
diff --git a/tests/parser/tstrongspaces.nim b/tests/parser/tstrongspaces.nim
index 568abda4ca..e70b91988c 100644
--- a/tests/parser/tstrongspaces.nim
+++ b/tests/parser/tstrongspaces.nim
@@ -15,6 +15,10 @@ true
tester args
all
all args
+19
+-3
+false
+-2
'''
"""
@@ -67,3 +71,13 @@ const
echo tester & " " & args|"all"
echo "all" | tester & " " & args
echo "all"|tester & " " & args
+
+# Test arrow like operators. See also tests/macros/tclosuremacro.nim
+proc `+->`(a, b: int): int = a + b*4
+template `===>`(a, b: int): expr = a - b shr 1
+
+echo 3 +-> 2 + 2 and 4
+var arrowed = 3+->2 + 2 and 4 # arrowed = 4
+echo arrowed ===> 15
+echo (2 * 3+->2) == (2*3 +-> 2)
+echo arrowed ===> 2 + 3+->2
diff --git a/tests/parser/ttupleunpack.nim b/tests/parser/ttupleunpack.nim
new file mode 100644
index 0000000000..aaa06f9f41
--- /dev/null
+++ b/tests/parser/ttupleunpack.nim
@@ -0,0 +1,35 @@
+discard """
+ file: "ttupleunpack.nim"
+ output: ""
+ exitcode: 0
+"""
+
+proc returnsTuple(): (int, int, int) = (4, 2, 3)
+
+proc main2 =
+ let (x, _, z) = returnsTuple()
+
+proc main() =
+
+ proc foo(): tuple[x, y, z: int] =
+ return (4, 2, 3)
+
+ var (x, _, y) = foo()
+ doAssert x == 4
+ doAssert y == 3
+
+ var (a, _, _) = foo()
+ doAssert a == 4
+
+ var (aa, _, _) = foo()
+ doAssert aa == 4
+
+ iterator bar(): tuple[x, y, z: int] =
+ yield (1,2,3)
+
+ for x, y, _ in bar():
+ doAssert x == 1
+ doAssert y == 2
+
+main()
+main2()
diff --git a/tests/sets/tsets.nim b/tests/sets/tsets.nim
index e370209ed0..6461753296 100644
--- a/tests/sets/tsets.nim
+++ b/tests/sets/tsets.nim
@@ -1,6 +1,7 @@
discard """
file: "tsets.nim"
- output: "Ha ein F ist in s!"
+ output: '''Ha ein F ist in s!
+false'''
"""
# Test the handling of sets
@@ -15,30 +16,30 @@ type
TAZ = range['a'..'z']
TAZset = set[TAZ]
- TTokType* = enum
+ TTokType* = enum
tkInvalid, tkEof,
tkSymbol,
- tkAddr, tkAnd, tkAs, tkAsm, tkBlock, tkBreak, tkCase, tkCast, tkConst,
- tkContinue, tkConverter, tkDiscard, tkDiv, tkElif, tkElse, tkEnd, tkEnum,
- tkExcept, tkException, tkFinally, tkFor, tkFrom, tkGeneric, tkIf, tkImplies,
- tkImport, tkIn, tkInclude, tkIs, tkIsnot, tkIterator, tkLambda, tkMacro,
- tkMethod, tkMod, tkNil, tkNot, tkNotin, tkObject, tkOf, tkOr, tkOut, tkProc,
- tkPtr, tkRaise, tkRecord, tkRef, tkReturn, tkShl, tkShr, tkTemplate, tkTry,
+ tkAddr, tkAnd, tkAs, tkAsm, tkBlock, tkBreak, tkCase, tkCast, tkConst,
+ tkContinue, tkConverter, tkDiscard, tkDiv, tkElif, tkElse, tkEnd, tkEnum,
+ tkExcept, tkException, tkFinally, tkFor, tkFrom, tkGeneric, tkIf, tkImplies,
+ tkImport, tkIn, tkInclude, tkIs, tkIsnot, tkIterator, tkLambda, tkMacro,
+ tkMethod, tkMod, tkNil, tkNot, tkNotin, tkObject, tkOf, tkOr, tkOut, tkProc,
+ tkPtr, tkRaise, tkRecord, tkRef, tkReturn, tkShl, tkShr, tkTemplate, tkTry,
tkType, tkVar, tkWhen, tkWhere, tkWhile, tkWith, tkWithout, tkXor, tkYield,
- tkIntLit, tkInt8Lit, tkInt16Lit, tkInt32Lit, tkInt64Lit, tkFloatLit,
- tkFloat32Lit, tkFloat64Lit, tkStrLit, tkRStrLit, tkTripleStrLit, tkCharLit,
- tkRCharLit, tkParLe, tkParRi, tkBracketLe, tkBracketRi, tkCurlyLe,
- tkCurlyRi, tkBracketDotLe, tkBracketDotRi,
- tkCurlyDotLe, tkCurlyDotRi,
+ tkIntLit, tkInt8Lit, tkInt16Lit, tkInt32Lit, tkInt64Lit, tkFloatLit,
+ tkFloat32Lit, tkFloat64Lit, tkStrLit, tkRStrLit, tkTripleStrLit, tkCharLit,
+ tkRCharLit, tkParLe, tkParRi, tkBracketLe, tkBracketRi, tkCurlyLe,
+ tkCurlyRi, tkBracketDotLe, tkBracketDotRi,
+ tkCurlyDotLe, tkCurlyDotRi,
tkParDotLe, tkParDotRi,
- tkComma, tkSemiColon, tkColon, tkEquals, tkDot, tkDotDot, tkHat, tkOpr,
+ tkComma, tkSemiColon, tkColon, tkEquals, tkDot, tkDotDot, tkHat, tkOpr,
tkComment, tkAccent, tkInd, tkSad, tkDed,
tkSpaces, tkInfixOpr, tkPrefixOpr, tkPostfixOpr
TTokTypeRange = range[tkSymbol..tkDed]
TTokTypes* = set[TTokTypeRange]
const
- toktypes: TTokTypes = {TTokTypeRange(tkSymbol)..pred(tkIntLit),
+ toktypes: TTokTypes = {TTokTypeRange(tkSymbol)..pred(tkIntLit),
tkStrLit..tkTripleStrLit}
var
@@ -62,3 +63,142 @@ for x in low(TTokTypeRange) .. high(TTokTypeRange):
#OUT Ha ein F ist in s!
+type
+ TMsgKind* = enum
+ errUnknown, errIllFormedAstX, errInternal, errCannotOpenFile, errGenerated,
+ errXCompilerDoesNotSupportCpp, errStringLiteralExpected,
+ errIntLiteralExpected, errInvalidCharacterConstant,
+ errClosingTripleQuoteExpected, errClosingQuoteExpected,
+ errTabulatorsAreNotAllowed, errInvalidToken, errLineTooLong,
+ errInvalidNumber, errNumberOutOfRange, errNnotAllowedInCharacter,
+ errClosingBracketExpected, errMissingFinalQuote, errIdentifierExpected,
+ errNewlineExpected,
+ errInvalidModuleName,
+ errOperatorExpected, errTokenExpected, errStringAfterIncludeExpected,
+ errRecursiveDependencyX, errOnOrOffExpected, errNoneSpeedOrSizeExpected,
+ errInvalidPragma, errUnknownPragma, errInvalidDirectiveX,
+ errAtPopWithoutPush, errEmptyAsm, errInvalidIndentation,
+ errExceptionExpected, errExceptionAlreadyHandled,
+ errYieldNotAllowedHere, errYieldNotAllowedInTryStmt,
+ errInvalidNumberOfYieldExpr, errCannotReturnExpr, errAttemptToRedefine,
+ errStmtInvalidAfterReturn, errStmtExpected, errInvalidLabel,
+ errInvalidCmdLineOption, errCmdLineArgExpected, errCmdLineNoArgExpected,
+ errInvalidVarSubstitution, errUnknownVar, errUnknownCcompiler,
+ errOnOrOffExpectedButXFound, errNoneBoehmRefcExpectedButXFound,
+ errNoneSpeedOrSizeExpectedButXFound, errGuiConsoleOrLibExpectedButXFound,
+ errUnknownOS, errUnknownCPU, errGenOutExpectedButXFound,
+ errArgsNeedRunOption, errInvalidMultipleAsgn, errColonOrEqualsExpected,
+ errExprExpected, errUndeclaredIdentifier, errUseQualifier, errTypeExpected,
+ errSystemNeeds, errExecutionOfProgramFailed, errNotOverloadable,
+ errInvalidArgForX, errStmtHasNoEffect, errXExpectsTypeOrValue,
+ errXExpectsArrayType, errIteratorCannotBeInstantiated, errExprXAmbiguous,
+ errConstantDivisionByZero, errOrdinalTypeExpected,
+ errOrdinalOrFloatTypeExpected, errOverOrUnderflow,
+ errCannotEvalXBecauseIncompletelyDefined, errChrExpectsRange0_255,
+ errDynlibRequiresExportc, errUndeclaredFieldX, errNilAccess,
+ errIndexOutOfBounds, errIndexTypesDoNotMatch, errBracketsInvalidForType,
+ errValueOutOfSetBounds, errFieldInitTwice, errFieldNotInit,
+ errExprXCannotBeCalled, errExprHasNoType, errExprXHasNoType,
+ errCastNotInSafeMode, errExprCannotBeCastedToX, errCommaOrParRiExpected,
+ errCurlyLeOrParLeExpected, errSectionExpected, errRangeExpected,
+ errMagicOnlyInSystem, errPowerOfTwoExpected,
+ errStringMayNotBeEmpty, errCallConvExpected, errProcOnlyOneCallConv,
+ errSymbolMustBeImported, errExprMustBeBool, errConstExprExpected,
+ errDuplicateCaseLabel, errRangeIsEmpty, errSelectorMustBeOfCertainTypes,
+ errSelectorMustBeOrdinal, errOrdXMustNotBeNegative, errLenXinvalid,
+ errWrongNumberOfVariables, errExprCannotBeRaised, errBreakOnlyInLoop,
+ errTypeXhasUnknownSize, errConstNeedsConstExpr, errConstNeedsValue,
+ errResultCannotBeOpenArray, errSizeTooBig, errSetTooBig,
+ errBaseTypeMustBeOrdinal, errInheritanceOnlyWithNonFinalObjects,
+ errInheritanceOnlyWithEnums, errIllegalRecursionInTypeX,
+ errCannotInstantiateX, errExprHasNoAddress, errXStackEscape,
+ errVarForOutParamNeeded,
+ errPureTypeMismatch, errTypeMismatch, errButExpected, errButExpectedX,
+ errAmbiguousCallXYZ, errWrongNumberOfArguments,
+ errXCannotBePassedToProcVar,
+ errXCannotBeInParamDecl, errPragmaOnlyInHeaderOfProc, errImplOfXNotAllowed,
+ errImplOfXexpected, errNoSymbolToBorrowFromFound, errDiscardValueX,
+ errInvalidDiscard, errIllegalConvFromXtoY, errCannotBindXTwice,
+ errInvalidOrderInArrayConstructor,
+ errInvalidOrderInEnumX, errEnumXHasHoles, errExceptExpected, errInvalidTry,
+ errOptionExpected, errXisNoLabel, errNotAllCasesCovered,
+ errUnknownSubstitionVar, errComplexStmtRequiresInd, errXisNotCallable,
+ errNoPragmasAllowedForX, errNoGenericParamsAllowedForX,
+ errInvalidParamKindX, errDefaultArgumentInvalid, errNamedParamHasToBeIdent,
+ errNoReturnTypeForX, errConvNeedsOneArg, errInvalidPragmaX,
+ errXNotAllowedHere, errInvalidControlFlowX,
+ errXisNoType, errCircumNeedsPointer, errInvalidExpression,
+ errInvalidExpressionX, errEnumHasNoValueX, errNamedExprExpected,
+ errNamedExprNotAllowed, errXExpectsOneTypeParam,
+ errArrayExpectsTwoTypeParams, errInvalidVisibilityX, errInitHereNotAllowed,
+ errXCannotBeAssignedTo, errIteratorNotAllowed, errXNeedsReturnType,
+ errNoReturnTypeDeclared,
+ errInvalidCommandX, errXOnlyAtModuleScope,
+ errXNeedsParamObjectType,
+ errTemplateInstantiationTooNested, errInstantiationFrom,
+ errInvalidIndexValueForTuple, errCommandExpectsFilename,
+ errMainModuleMustBeSpecified,
+ errXExpected,
+ errTIsNotAConcreteType,
+ errInvalidSectionStart, errGridTableNotImplemented, errGeneralParseError,
+ errNewSectionExpected, errWhitespaceExpected, errXisNoValidIndexFile,
+ errCannotRenderX, errVarVarTypeNotAllowed, errInstantiateXExplicitly,
+ errOnlyACallOpCanBeDelegator, errUsingNoSymbol,
+ errMacroBodyDependsOnGenericTypes,
+ errDestructorNotGenericEnough,
+ errInlineIteratorsAsProcParams,
+ errXExpectsTwoArguments,
+ errXExpectsObjectTypes, errXcanNeverBeOfThisSubtype, errTooManyIterations,
+ errCannotInterpretNodeX, errFieldXNotFound, errInvalidConversionFromTypeX,
+ errAssertionFailed, errCannotGenerateCodeForX, errXRequiresOneArgument,
+ errUnhandledExceptionX, errCyclicTree, errXisNoMacroOrTemplate,
+ errXhasSideEffects, errIteratorExpected, errLetNeedsInit,
+ errThreadvarCannotInit, errWrongSymbolX, errIllegalCaptureX,
+ errXCannotBeClosure, errXMustBeCompileTime,
+ errCannotInferTypeOfTheLiteral,
+ errCannotInferReturnType,
+ errGenericLambdaNotAllowed,
+ errCompilerDoesntSupportTarget,
+ errUser,
+ warnCannotOpenFile,
+ warnOctalEscape, warnXIsNeverRead, warnXmightNotBeenInit,
+ warnDeprecated, warnConfigDeprecated,
+ warnSmallLshouldNotBeUsed, warnUnknownMagic, warnRedefinitionOfLabel,
+ warnUnknownSubstitutionX, warnLanguageXNotSupported,
+ warnFieldXNotSupported, warnCommentXIgnored,
+ warnNilStatement, warnTypelessParam,
+ warnDifferentHeaps, warnWriteToForeignHeap, warnUnsafeCode,
+ warnEachIdentIsTuple, warnShadowIdent,
+ warnProveInit, warnProveField, warnProveIndex, warnGcUnsafe, warnGcUnsafe2,
+ warnUninit, warnGcMem, warnDestructor, warnLockLevel, warnResultShadowed,
+ warnUser,
+ hintSuccess, hintSuccessX,
+ hintLineTooLong, hintXDeclaredButNotUsed, hintConvToBaseNotNeeded,
+ hintConvFromXtoItselfNotNeeded, hintExprAlwaysX, hintQuitCalled,
+ hintProcessing, hintCodeBegin, hintCodeEnd, hintConf, hintPath,
+ hintConditionAlwaysTrue, hintName, hintPattern,
+ hintUser
+
+const
+ fatalMin* = errUnknown
+ fatalMax* = errInternal
+ errMin* = errUnknown
+ errMax* = errUser
+ warnMin* = warnCannotOpenFile
+ warnMax* = pred(hintSuccess)
+ hintMin* = hintSuccess
+ hintMax* = high(TMsgKind)
+
+type
+ TNoteKind* = range[warnMin..hintMax] # "notes" are warnings or hints
+ TNoteKinds* = set[TNoteKind]
+
+var
+ gNotes*: TNoteKinds = {low(TNoteKind)..high(TNoteKind)} -
+ {warnShadowIdent, warnUninit,
+ warnProveField, warnProveIndex, warnGcUnsafe}
+
+
+#import compiler.msgs
+
+echo warnUninit in gNotes
diff --git a/tests/stdlib/tdialogs.nim b/tests/stdlib/tdialogs.nim
deleted file mode 100644
index f0203d3197..0000000000
--- a/tests/stdlib/tdialogs.nim
+++ /dev/null
@@ -1,17 +0,0 @@
-# Test the dialogs module
-
-import dialogs, gtk2
-
-gtk2.nimrod_init()
-
-var x = chooseFilesToOpen(nil)
-for a in items(x):
- writeln(stdout, a)
-
-info(nil, "start with an info box")
-warning(nil, "now a warning ...")
-error(nil, "... and an error!")
-
-writeln(stdout, chooseFileToOpen(nil))
-writeln(stdout, chooseFileToSave(nil))
-writeln(stdout, chooseDir(nil))
diff --git a/tests/template/tdefault_nil.nim b/tests/template/tdefault_nil.nim
new file mode 100644
index 0000000000..891166306d
--- /dev/null
+++ b/tests/template/tdefault_nil.nim
@@ -0,0 +1,14 @@
+
+# bug #2629
+import sequtils, os
+
+template glob_rst(basedir: string = nil): expr =
+ if baseDir.isNil:
+ to_seq(walk_files("*.rst"))
+ else:
+ to_seq(walk_files(basedir/"*.rst"))
+
+let
+ rst_files = concat(glob_rst(), glob_rst("docs"))
+
+when isMainModule: echo rst_files
diff --git a/tests/template/tstmt_semchecked_twice.nim b/tests/template/tstmt_semchecked_twice.nim
new file mode 100644
index 0000000000..05c16c3c97
--- /dev/null
+++ b/tests/template/tstmt_semchecked_twice.nim
@@ -0,0 +1,30 @@
+
+# bug #2585
+
+type
+ RenderPass = object
+ state: ref int
+
+ RenderData* = object
+ fb: int
+ walls: seq[RenderPass]
+
+ Mat2 = int
+ Vector2[T] = T
+ Pixels=int
+
+template use*(fb: int, st: stmt) : stmt =
+ echo "a ", $fb
+ st
+ echo "a ", $fb
+
+proc render(rdat: var RenderData; passes: var openarray[RenderPass]; proj: Mat2;
+ indexType = 1) =
+ for i in 0 .. (0, 10, 2):
+ write(W, 'c')
+ else:
+ block:
+ var x: W
+ write(x, char)
+
+ FmtAlign = enum ## Format alignment
+ faDefault ## default for given format type
+ faLeft ## left aligned
+ faRight ## right aligned
+ faCenter ## centered
+ faPadding ## right aligned, fill characters after sign (numbers only)
+
+ FmtSign = enum ## Format sign
+ fsMinus ## only unary minus, no reservered sign space for positive numbers
+ fsPlus ## unary minus and unary plus
+ fsSpace ## unary minus and reserved space for positive numbers
+
+ FmtType = enum ## Format type
+ ftDefault ## default format for given parameter type
+ ftStr ## string
+ ftChar ## character
+ ftDec ## decimal integer
+ ftBin ## binary integer
+ ftOct ## octal integer
+ ftHex ## hexadecimal integer
+ ftFix ## real number in fixed point notation
+ ftSci ## real number in scientific notation
+ ftGen ## real number in generic form (either fixed point or scientific)
+ ftPercent ## real number multiplied by 100 and % added
+
+ Format = tuple ## Formatting information.
+ typ: FmtType ## format type
+ precision: int ## floating point precision
+ width: int ## minimal width
+ fill: string ## the fill character, UTF8
+ align: FmtAlign ## aligment
+ sign: FmtSign ## sign notation
+ baseprefix: bool ## whether binary, octal, hex should be prefixed by 0b, 0x, 0o
+ upcase: bool ## upper case letters in hex or exponential formats
+ comma: bool ##
+ arysep: string ## separator for array elements
+
+ PartKind = enum pkStr, pkFmt
+
+ Part = object
+ ## Information of a part of the target string.
+ case kind: PartKind ## type of the part
+ of pkStr:
+ str: string ## literal string
+ of pkFmt:
+ arg: int ## position argument
+ fmt: string ## format string
+ field: string ## field of argument to be accessed
+ index: int ## array index of argument to be accessed
+ nested: bool ## true if the argument contains nested formats
+
+const
+ DefaultPrec = 6 ## Default precision for floating point numbers.
+ DefaultFmt: Format = (ftDefault, -1, -1, nil, faDefault, fsMinus, false, false, false, nil)
+ ## Default format corresponding to the empty format string, i.e.
+ ## `x.format("") == x.format(DefaultFmt)`.
+ round_nums = [0.5, 0.05, 0.005, 0.0005, 0.00005, 0.000005, 0.0000005, 0.00000005]
+ ## Rounding offset for floating point numbers up to precision 8.
+
+proc write(s: var string; c: char) =
+ s.add(c)
+
+proc has(c: Captures; i: range[0..pegs.MaxSubpatterns-1]): bool {.nosideeffect, inline.} =
+ ## Tests whether `c` contains a non-empty capture `i`.
+ let b = c.bounds(i)
+ result = b.first <= b.last
+
+proc get(str: string; c: Captures; i: range[0..MaxSubpatterns-1]; def: char): char {.nosideeffect, inline.} =
+ ## If capture `i` is non-empty return that portion of `str` casted
+ ## to `char`, otherwise return `def`.
+ result = if c.has(i): str[c.bounds(i).first] else: def
+
+proc get(str: string; c: Captures; i: range[0..MaxSubpatterns-1]; def: string; begoff: int = 0): string {.nosideeffect, inline.} =
+ ## If capture `i` is non-empty return that portion of `str` as
+ ## string, otherwise return `def`.
+ let b = c.bounds(i)
+ result = if c.has(i): str.substr(b.first + begoff, b.last) else: def
+
+proc get(str: string; c: Captures; i: range[0..MaxSubpatterns-1]; def: int; begoff: int = 0): int {.nosideeffect, inline.} =
+ ## If capture `i` is non-empty return that portion of `str`
+ ## converted to int, otherwise return `def`.
+ if c.has(i):
+ discard str.parseInt(result, c.bounds(i).first + begoff)
+ else:
+ result = def
+
+proc parse(fmt: string): Format {.nosideeffect.} =
+ # Converts the format string `fmt` into a `Format` structure.
+ let p =
+ sequence(capture(?sequence(anyRune(), &charSet({'<', '>', '=', '^'}))),
+ capture(?charSet({'<', '>', '=', '^'})),
+ capture(?charSet({'-', '+', ' '})),
+ capture(?charSet({'#'})),
+ capture(?(+digits())),
+ capture(?charSet({','})),
+ capture(?sequence(charSet({'.'}), +digits())),
+ capture(?charSet({'b', 'c', 'd', 'e', 'E', 'f', 'F', 'g', 'G', 'n', 'o', 's', 'x', 'X', '%'})),
+ capture(?sequence(charSet({'a'}), *pegs.any())))
+ # let p=peg"{(_&[<>=^])?}{[<>=^]?}{[-+ ]?}{[#]?}{[0-9]+?}{[,]?}{([.][0-9]+)?}{[bcdeEfFgGnosxX%]?}{(a.*)?}"
+
+ var caps: Captures
+ if fmt.rawmatch(p, 0, caps) < 0:
+ raise newException(FormatError, "Invalid format string")
+
+ result.fill = fmt.get(caps, 0, nil)
+
+ case fmt.get(caps, 1, 0.char)
+ of '<': result.align = faLeft
+ of '>': result.align = faRight
+ of '^': result.align = faCenter
+ of '=': result.align = faPadding
+ else: result.align = faDefault
+
+ case fmt.get(caps, 2, '-')
+ of '-': result.sign = fsMinus
+ of '+': result.sign = fsPlus
+ of ' ': result.sign = fsSpace
+ else: result.sign = fsMinus
+
+ result.baseprefix = caps.has(3)
+
+ result.width = fmt.get(caps, 4, -1)
+
+ if caps.has(4) and fmt[caps.bounds(4).first] == '0':
+ if result.fill != nil:
+ raise newException(FormatError, "Leading 0 in with not allowed with explicit fill character")
+ if result.align != faDefault:
+ raise newException(FormatError, "Leading 0 in with not allowed with explicit alignment")
+ result.fill = "0"
+ result.align = faPadding
+
+ result.comma = caps.has(5)
+
+ result.precision = fmt.get(caps, 6, -1, 1)
+
+ case fmt.get(caps, 7, 0.char)
+ of 's': result.typ = ftStr
+ of 'c': result.typ = ftChar
+ of 'd', 'n': result.typ = ftDec
+ of 'b': result.typ = ftBin
+ of 'o': result.typ = ftOct
+ of 'x': result.typ = ftHex
+ of 'X': result.typ = ftHex; result.upcase = true
+ of 'f', 'F': result.typ = ftFix
+ of 'e': result.typ = ftSci
+ of 'E': result.typ = ftSci; result.upcase = true
+ of 'g': result.typ = ftGen
+ of 'G': result.typ = ftGen; result.upcase = true
+ of '%': result.typ = ftPercent
+ else: result.typ = ftDefault
+
+ result.arysep = fmt.get(caps, 8, nil, 1)
+
+proc getalign(fmt: Format; defalign: FmtAlign; slen: int) : tuple[left, right:int] {.nosideeffect.} =
+ ## Returns the number of left and right padding characters for a
+ ## given format alignment and width of the object to be printed.
+ ##
+ ## `fmt`
+ ## the format data
+ ## `default`
+ ## if `fmt.align == faDefault`, then this alignment is used
+ ## `slen`
+ ## the width of the object to be printed.
+ ##
+ ## The returned values `(left, right)` will be as minimal as possible
+ ## so that `left + slen + right >= fmt.width`.
+ result.left = 0
+ result.right = 0
+ if (fmt.width >= 0) and (slen < fmt.width):
+ let alg = if fmt.align == faDefault: defalign else: fmt.align
+ case alg:
+ of faLeft: result.right = fmt.width - slen
+ of faRight, faPadding: result.left = fmt.width - slen
+ of faCenter:
+ result.left = (fmt.width - slen) div 2
+ result.right = fmt.width - slen - result.left
+ else: discard
+
+proc writefill(o: var Writer; fmt: Format; n: int; signum: int = 0) =
+ ## Write characters for filling. This function also writes the sign
+ ## of a numeric format and handles the padding alignment
+ ## accordingly.
+ ##
+ ## `o`
+ ## output object
+ ## `add`
+ ## output function
+ ## `fmt`
+ ## format to be used (important for padding aligment)
+ ## `n`
+ ## the number of filling characters to be written
+ ## `signum`
+ ## the sign of the number to be written, < 0 negative, > 0 positive, = 0 zero
+ if fmt.align == faPadding and signum != 0:
+ if signum < 0: write(o, '-')
+ elif fmt.sign == fsPlus: write(o, '+')
+ elif fmt.sign == fsSpace: write(o, ' ')
+
+ if fmt.fill == nil:
+ for i in 1..n: write(o, ' ')
+ else:
+ for i in 1..n:
+ for c in fmt.fill:
+ write(o, c)
+
+ if fmt.align != faPadding and signum != 0:
+ if signum < 0: write(o, '-')
+ elif fmt.sign == fsPlus: write(o, '+')
+ elif fmt.sign == fsSpace: write(o, ' ')
+
+proc writeformat(o: var Writer; s: string; fmt: Format) =
+ ## Write string `s` according to format `fmt` using output object
+ ## `o` and output function `add`.
+ if fmt.typ notin {ftDefault, ftStr}:
+ raise newException(FormatError, "String variable must have 's' format type")
+
+ # compute alignment
+ let len = if fmt.precision < 0: runelen(s) else: min(runelen(s), fmt.precision)
+ var alg = getalign(fmt, faLeft, len)
+ writefill(o, fmt, alg.left)
+ var pos = 0
+ for i in 0..len-1:
+ let rlen = runeLenAt(s, pos)
+ for j in pos..pos+rlen-1: write(o, s[j])
+ pos += rlen
+ writefill(o, fmt, alg.right)
+
+proc writeformat(o: var Writer; c: char; fmt: Format) =
+ ## Write character `c` according to format `fmt` using output object
+ ## `o` and output function `add`.
+ if not (fmt.typ in {ftChar, ftDefault}):
+ raise newException(FormatError, "Character variable must have 'c' format type")
+
+ # compute alignment
+ var alg = getalign(fmt, faLeft, 1)
+ writefill(o, fmt, alg.left)
+ write(o, c)
+ writefill(o, fmt, alg.right)
+
+proc writeformat(o: var Writer; c: Rune; fmt: Format) =
+ ## Write rune `c` according to format `fmt` using output object
+ ## `o` and output function `add`.
+ if not (fmt.typ in {ftChar, ftDefault}):
+ raise newException(FormatError, "Character variable must have 'c' format type")
+
+ # compute alignment
+ var alg = getalign(fmt, faLeft, 1)
+ writefill(o, fmt, alg.left)
+ let s = c.toUTF8
+ for c in s: write(o, c)
+ writefill(o, fmt, alg.right)
+
+proc abs(x: SomeUnsignedInt): SomeUnsignedInt {.inline.} = x
+ ## Return the absolute value of the unsigned int `x`.
+
+proc writeformat(o: var Writer; i: SomeInteger; fmt: Format) =
+ ## Write integer `i` according to format `fmt` using output object
+ ## `o` and output function `add`.
+ var fmt = fmt
+ if fmt.typ == ftDefault:
+ fmt.typ = ftDec
+ if not (fmt.typ in {ftBin, ftOct, ftHex, ftDec}):
+ raise newException(FormatError, "Integer variable must of one of the following types: b,o,x,X,d,n")
+
+ var base: type(i)
+ var len = 0
+ case fmt.typ:
+ of ftDec:
+ base = 10
+ of ftBin:
+ base = 2
+ if fmt.baseprefix: len += 2
+ of ftOct:
+ base = 8
+ if fmt.baseprefix: len += 2
+ of ftHex:
+ base = 16
+ if fmt.baseprefix: len += 2
+ else: assert(false)
+
+ if fmt.sign != fsMinus or i < 0: len.inc
+
+ var x: type(i) = abs(i)
+ var irev: type(i) = 0
+ var ilen = 0
+ while x > 0.SomeInteger:
+ len.inc
+ ilen.inc
+ irev = irev * base + x mod base
+ x = x div base
+ if ilen == 0:
+ ilen.inc
+ len.inc
+
+ var alg = getalign(fmt, faRight, len)
+ writefill(o, fmt, alg.left, if i >= 0.SomeInteger: 1 else: -1)
+ if fmt.baseprefix:
+ case fmt.typ
+ of ftBin:
+ write(o, '0')
+ write(o, 'b')
+ of ftOct:
+ write(o, '0')
+ write(o, 'o')
+ of ftHex:
+ write(o, '0')
+ write(o, 'x')
+ else:
+ raise newException(FormatError, "# only allowed with b, o, x or X")
+ while ilen > 0:
+ ilen.dec
+ let c = irev mod base
+ irev = irev div base
+ if c < 10:
+ write(o, ('0'.int + c.int).char)
+ elif fmt.upcase:
+ write(o, ('A'.int + c.int - 10).char)
+ else:
+ write(o, ('a'.int + c.int - 10).char)
+ writefill(o, fmt, alg.right)
+
+proc writeformat(o: var Writer; p: pointer; fmt: Format) =
+ ## Write pointer `i` according to format `fmt` using output object
+ ## `o` and output function `add`.
+ ##
+ ## Pointers are casted to unsigned int and formated as hexadecimal
+ ## with prefix unless specified otherwise.
+ var f = fmt
+ if f.typ == 0.char:
+ f.typ = 'x'
+ f.baseprefix = true
+ writeformat(o, add, cast[uint](p), f)
+
+proc writeformat(o: var Writer; x: SomeReal; fmt: Format) =
+ ## Write real number `x` according to format `fmt` using output
+ ## object `o` and output function `add`.
+ var fmt = fmt
+ # handle default format
+ if fmt.typ == ftDefault:
+ fmt.typ = ftGen
+ if fmt.precision < 0: fmt.precision = DefaultPrec
+ if not (fmt.typ in {ftFix, ftSci, ftGen, ftPercent}):
+ raise newException(FormatError, "Integer variable must of one of the following types: f,F,e,E,g,G,%")
+
+ let positive = x >= 0 and classify(x) != fcNegZero
+ var len = 0
+
+ if fmt.sign != fsMinus or not positive: len.inc
+
+ var prec = if fmt.precision < 0: DefaultPrec else: fmt.precision
+ var y = abs(x)
+ var exp = 0
+ var numstr, frstr: array[0..31, char]
+ var numlen, frbeg, frlen = 0
+
+ if fmt.typ == ftPercent: y *= 100
+
+ case classify(x):
+ of fcNan:
+ numstr[0..2] = ['n', 'a', 'n']
+ numlen = 3
+ of fcInf, fcNegInf:
+ numstr[0..2] = ['f', 'n', 'i']
+ numlen = 3
+ of fcZero, fcNegZero:
+ numstr[0] = '0'
+ numlen = 1
+ else: # a usual fractional number
+ if not (fmt.typ in {ftFix, ftPercent}): # not fixed point
+ exp = int(floor(log10(y)))
+ if fmt.typ == ftGen:
+ if prec == 0: prec = 1
+ if -4 <= exp and exp < prec:
+ prec = prec-1-exp
+ exp = 0
+ else:
+ prec = prec - 1
+ len += 4 # exponent
+ else:
+ len += 4 # exponent
+ # shift y so that 1 <= abs(y) < 2
+ if exp > 0: y /= pow(10.SomeReal, abs(exp).SomeReal)
+ elif exp < 0: y *= pow(10.SomeReal, abs(exp).SomeReal)
+ elif fmt.typ == ftPercent:
+ len += 1 # percent sign
+
+ # handle rounding by adding +0.5 * LSB
+ if prec < len(round_nums): y += round_nums[prec]
+
+ # split into integer and fractional part
+ var mult = 1'i64
+ for i in 1..prec: mult *= 10
+ var num = y.int64
+ var fr = ((y - num.SomeReal) * mult.SomeReal).int64
+ # build integer part string
+ while num != 0:
+ numstr[numlen] = ('0'.int + (num mod 10)).char
+ numlen.inc
+ num = num div 10
+ if numlen == 0:
+ numstr[0] = '0'
+ numlen.inc
+ # build fractional part string
+ while fr != 0:
+ frstr[frlen] = ('0'.int + (fr mod 10)).char
+ frlen.inc
+ fr = fr div 10
+ while frlen < prec:
+ frstr[frlen] = '0'
+ frlen.inc
+ # possible remove trailing 0
+ if fmt.typ == ftGen:
+ while frbeg < frlen and frstr[frbeg] == '0': frbeg.inc
+ # update length of string
+ len += numlen;
+ if frbeg < frlen:
+ len += 1 + frlen - frbeg # decimal point and fractional string
+
+ let alg = getalign(fmt, faRight, len)
+ writefill(o, fmt, alg.left, if positive: 1 else: -1)
+ for i in (numlen-1).countdown(0): write(o, numstr[i])
+ if frbeg < frlen:
+ write(o, '.')
+ for i in (frlen-1).countdown(frbeg): write(o, frstr[i])
+ if fmt.typ == ftSci or (fmt.typ == ftGen and exp != 0):
+ write(o, if fmt.upcase: 'E' else: 'e')
+ if exp >= 0:
+ write(o, '+')
+ else:
+ write(o, '-')
+ exp = -exp
+ if exp < 10:
+ write(o, '0')
+ write(o, ('0'.int + exp).char)
+ else:
+ var i=0
+ while exp > 0:
+ numstr[i] = ('0'.int + exp mod 10).char
+ i+=1
+ exp = exp div 10
+ while i>0:
+ i-=1
+ write(o, numstr[i])
+ if fmt.typ == ftPercent: write(o, '%')
+ writefill(o, fmt, alg.right)
+
+proc writeformat(o: var Writer; b: bool; fmt: Format) =
+ ## Write boolean value `b` according to format `fmt` using output
+ ## object `o`. A boolean may be formatted numerically or as string.
+ ## In the former case true is written as 1 and false as 0, in the
+ ## latter the strings "true" and "false" are shown, respectively.
+ ## The default is string format.
+ if fmt.typ in {ftStr, ftDefault}:
+ writeformat(o,
+ if b: "true"
+ else: "false",
+ fmt)
+ elif fmt.typ in {ftBin, ftOct, ftHex, ftDec}:
+ writeformat(o,
+ if b: 1
+ else: 0,
+ fmt)
+ else:
+ raise newException(FormatError, "Boolean values must of one of the following types: s,b,o,x,X,d,n")
+
+proc writeformat(o: var Writer; ary: openarray[any]; fmt: Format) =
+ ## Write array `ary` according to format `fmt` using output object
+ ## `o` and output function `add`.
+ if ary.len == 0: return
+
+ var sep: string
+ var nxtfmt = fmt
+ if fmt.arysep == nil:
+ sep = "\t"
+ elif fmt.arysep.len == 0:
+ sep = ""
+ else:
+ let sepch = fmt.arysep[0]
+ let nxt = 1 + skipUntil(fmt.arysep, sepch, 1)
+ if nxt >= 1:
+ nxtfmt.arysep = fmt.arysep.substr(nxt)
+ sep = fmt.arysep.substr(1, nxt-1)
+ else:
+ nxtfmt.arysep = ""
+ sep = fmt.arysep.substr(1)
+ writeformat(o, ary[0], nxtfmt)
+ for i in 1..ary.len-1:
+ for c in sep: write(o, c)
+ writeformat(o, ary[i], nxtfmt)
+
+proc addformat[T](o: var Writer; x: T; fmt: Format = DefaultFmt) {.inline.} =
+ ## Write `x` formatted with `fmt` to `o`.
+ writeformat(o, x, fmt)
+
+proc addformat[T](o: var Writer; x: T; fmt: string) {.inline.} =
+ ## The same as `addformat(o, x, parse(fmt))`.
+ addformat(o, x, fmt.parse)
+
+proc addformat(s: var string; x: string) {.inline.} =
+ ## Write `x` to `s`. This is a fast specialized version for
+ ## appending unformatted strings.
+ add(s, x)
+
+proc addformat(f: File; x: string) {.inline.} =
+ ## Write `x` to `f`. This is a fast specialized version for
+ ## writing unformatted strings to a file.
+ write(f, x)
+
+proc addformat[T](f: File; x: T; fmt: Format = DefaultFmt) {.inline.} =
+ ## Write `x` to file `f` using format `fmt`.
+ var g = f
+ writeformat(g, x, fmt)
+
+proc addformat[T](f: File; x: T; fmt: string) {.inline.} =
+ ## Write `x` to file `f` using format string `fmt`. This is the same
+ ## as `addformat(f, x, parse(fmt))`
+ addformat(f, x, parse(fmt))
+
+proc addformat(s: Stream; x: string) {.inline.} =
+ ## Write `x` to `s`. This is a fast specialized version for
+ ## writing unformatted strings to a stream.
+ write(s, x)
+
+proc addformat[T](s: Stream; x: T; fmt: Format = DefaultFmt) {.inline.} =
+ ## Write `x` to stream `s` using format `fmt`.
+ var g = s
+ writeformat(g, x, fmt)
+
+proc addformat[T](s: Stream; x: T; fmt: string) {.inline.} =
+ ## Write `x` to stream `s` using format string `fmt`. This is the same
+ ## as `addformat(s, x, parse(fmt))`
+ addformat(s, x, parse(fmt))
+
+proc format[T](x: T; fmt: Format): string =
+ ## Return `x` formatted as a string according to format `fmt`.
+ result = ""
+ addformat(result, x, fmt)
+
+proc format[T](x: T; fmt: string): string =
+ ## Return `x` formatted as a string according to format string `fmt`.
+ result = format(x, fmt.parse)
+
+proc format[T](x: T): string {.inline.} =
+ ## Return `x` formatted as a string according to the default format.
+ ## The default format corresponds to an empty format string.
+ var fmt {.global.} : Format = DefaultFmt
+ result = format(x, fmt)
+
+proc unquoted(s: string): string {.compileTime.} =
+ ## Return `s` {{ and }} by single { and }, respectively.
+ result = ""
+ var pos = 0
+ while pos < s.len:
+ let nxt = pos + skipUntil(s, {'{', '}'})
+ result.add(s.substr(pos, nxt))
+ pos = nxt + 2
+
+proc splitfmt(s: string): seq[Part] {.compiletime, nosideeffect.} =
+ ## Split format string `s` into a sequence of "parts".
+ ##
+
+ ## Each part is either a literal string or a format specification. A
+ ## format specification is a substring of the form
+ ## "{[arg][:format]}" where `arg` is either empty or a number
+ ## refering to the arg-th argument and an additional field or array
+ ## index. The format string is a string accepted by `parse`.
+ let subpeg = sequence(capture(digits()),
+ capture(?sequence(charSet({'.'}), *pegs.identStartChars(), *identChars())),
+ capture(?sequence(charSet({'['}), +digits(), charSet({']'}))),
+ capture(?sequence(charSet({':'}), *pegs.any())))
+ result = @[]
+ var pos = 0
+ while true:
+ let oppos = pos + skipUntil(s, {'{', '}'}, pos)
+ # reached the end
+ if oppos >= s.len:
+ if pos < s.len:
+ result.add(Part(kind: pkStr, str: s.substr(pos).unquoted))
+ return
+ # skip double
+ if oppos + 1 < s.len and s[oppos] == s[oppos+1]:
+ result.add(Part(kind: pkStr, str: s.substr(pos, oppos)))
+ pos = oppos + 2
+ continue
+ if s[oppos] == '}':
+ error("Single '}' encountered in format string")
+ if oppos > pos:
+ result.add(Part(kind: pkStr, str: s.substr(pos, oppos-1).unquoted))
+ # find matching closing }
+ var lvl = 1
+ var nested = false
+ pos = oppos
+ while lvl > 0:
+ pos.inc
+ pos = pos + skipUntil(s, {'{', '}'}, pos)
+ if pos >= s.len:
+ error("Single '{' encountered in format string")
+ if s[pos] == '{':
+ lvl.inc
+ if lvl == 2:
+ nested = true
+ if lvl > 2:
+ error("Too many nested format levels")
+ else:
+ lvl.dec
+ let clpos = pos
+ var fmtpart = Part(kind: pkFmt, arg: -1, fmt: s.substr(oppos+1, clpos-1), field: nil, index: int.high, nested: nested)
+ if fmtpart.fmt.len > 0:
+ var m: array[0..3, string]
+ if not fmtpart.fmt.match(subpeg, m):
+ error("invalid format string")
+
+ if m[1] != nil and m[1].len > 0:
+ fmtpart.field = m[1].substr(1)
+ if m[2] != nil and m[2].len > 0:
+ discard parseInt(m[2].substr(1, m[2].len-2), fmtpart.index)
+
+ if m[0].len > 0: discard parseInt(m[0], fmtpart.arg)
+ if m[3] == nil or m[3].len == 0:
+ fmtpart.fmt = ""
+ elif m[3][0] == ':':
+ fmtpart.fmt = m[3].substr(1)
+ else:
+ fmtpart.fmt = m[3]
+ result.add(fmtpart)
+ pos = clpos + 1
+
+proc literal(s: string): NimNode {.compiletime, nosideeffect.} =
+ ## Return the nim literal of string `s`. This handles the case if
+ ## `s` is nil.
+ result = if s == nil: newNilLit() else: newLit(s)
+
+proc literal(b: bool): NimNode {.compiletime, nosideeffect.} =
+ ## Return the nim literal of boolean `b`. This is either `true`
+ ## or `false` symbol.
+ result = if b: "true".ident else: "false".ident
+
+proc literal[T](x: T): NimNode {.compiletime, nosideeffect.} =
+ ## Return the nim literal of value `x`.
+ when type(x) is enum:
+ result = ($x).ident
+ else:
+ result = newLit(x)
+
+proc generatefmt(fmtstr: string;
+ args: var openarray[tuple[arg:NimNode, cnt:int]];
+ arg: var int;): seq[tuple[val, fmt:NimNode]] {.compiletime.} =
+ ## fmtstr
+ ## the format string
+ ## args
+ ## array of expressions for the arguments
+ ## arg
+ ## the number of the next argument for automatic parsing
+ ##
+ ## If arg is < 0 then the functions assumes that explicit numbering
+ ## must be used, otherwise automatic numbering is used starting at
+ ## `arg`. The value of arg is updated according to the number of
+ ## arguments being used. If arg == 0 then automatic and manual
+ ## numbering is not decided (because no explicit manual numbering is
+ ## fixed und no automatically numbered argument has been used so
+ ## far).
+ ##
+ ## The function returns a list of pairs `(val, fmt)` where `val` is
+ ## an expression to be formatted and `fmt` is the format string (or
+ ## Format). Therefore, the resulting string can be generated by
+ ## concatenating expressions `val.format(fmt)`. If `fmt` is `nil`
+ ## then `val` is a (literal) string expression.
+ try:
+ result = @[]
+ for part in splitfmt(fmtstr):
+ case part.kind
+ of pkStr: result.add((newLit(part.str), nil))
+ of pkFmt:
+ # first compute the argument expression
+ # start with the correct index
+ var argexpr : NimNode
+ if part.arg >= 0:
+ if arg > 0:
+ error("Cannot switch from automatic field numbering to manual field specification")
+ if part.arg >= args.len:
+ error("Invalid explicit argument index: " & $part.arg)
+ argexpr = args[part.arg].arg
+ args[part.arg].cnt = args[part.arg].cnt + 1
+ arg = -1
+ else:
+ if arg < 0:
+ error("Cannot switch from manual field specification to automatic field numbering")
+ if arg >= args.len:
+ error("Too few arguments for format string")
+ argexpr = args[arg].arg
+ args[arg].cnt = args[arg].cnt + 1
+ arg.inc
+ # possible field access
+ if part.field != nil and part.field.len > 0:
+ argexpr = newDotExpr(argexpr, part.field.ident)
+ # possible array access
+ if part.index < int.high:
+ argexpr = newNimNode(nnkBracketExpr).add(argexpr, newLit(part.index))
+ # now the expression for the format data
+ var fmtexpr: NimNode
+ if part.nested:
+ # nested format string. Compute the format string by
+ # concatenating the parts of the substring.
+ for e in generatefmt(part.fmt, args, arg):
+ var newexpr = if part.fmt == nil: e.val else: newCall(bindsym"format", e.val, e.fmt)
+ if fmtexpr != nil and fmtexpr.kind != nnkNilLit:
+ fmtexpr = infix(fmtexpr, "&", newexpr)
+ else:
+ fmtexpr = newexpr
+ else:
+ # literal format string, precompute the format data
+ fmtexpr = newNimNode(nnkPar)
+ for field, val in part.fmt.parse.fieldPairs:
+ fmtexpr.add(newNimNode(nnkExprColonExpr).add(field.ident, literal(val)))
+ # add argument
+ result.add((argexpr, fmtexpr))
+ finally:
+ discard
+
+proc addfmtfmt(fmtstr: string; args: NimNode; retvar: NimNode): NimNode {.compileTime.} =
+ var argexprs = newseq[tuple[arg:NimNode; cnt:int]](args.len)
+ result = newNimNode(nnkStmtListExpr)
+ # generate let bindings for arguments
+ for i in 0..args.len-1:
+ let argsym = gensym(nskLet, "arg" & $i)
+ result.add(newLetStmt(argsym, args[i]))
+ argexprs[i].arg = argsym
+ # add result values
+ var arg = 0
+ for e in generatefmt(fmtstr, argexprs, arg):
+ if e.fmt == nil or e.fmt.kind == nnkNilLit:
+ result.add(newCall(bindsym"addformat", retvar, e.val))
+ else:
+ result.add(newCall(bindsym"addformat", retvar, e.val, e.fmt))
+ for i, arg in argexprs:
+ if arg.cnt == 0:
+ warning("Argument " & $(i+1) & " `" & args[i].repr & "` is not used in format string")
+
+macro addfmt(s: var string, fmtstr: string{lit}, args: varargs[expr]): expr =
+ ## The same as `s.add(fmtstr.fmt(args...))` but faster.
+ result = addfmtfmt($fmtstr, args, s)
+
+var s: string = ""
+s.addfmt("a:{}", 42)
diff --git a/tests/tuples/tuple_with_seq.nim b/tests/tuples/tuple_with_seq.nim
new file mode 100644
index 0000000000..39edb500f9
--- /dev/null
+++ b/tests/tuples/tuple_with_seq.nim
@@ -0,0 +1,46 @@
+discard """
+ output: '''it's nil
+@[1, 2, 3]'''
+"""
+
+template foo(s: string = nil) =
+ if isNil(s):
+ echo "it's nil"
+ else:
+ echo s
+
+foo
+
+
+# bug #2632
+
+proc takeTup(x: tuple[s: string;x: seq[int]]) =
+ discard
+
+takeTup(("foo", @[]))
+
+
+#proc foobar(): () =
+
+proc f(xs: seq[int]) =
+ discard
+
+proc g(t: tuple[n:int, xs:seq[int]]) =
+ discard
+
+when isMainModule:
+ f(@[]) # OK
+ g((1,@[1])) # OK
+ g((0,@[])) # NG
+
+
+# bug #2630
+type T = tuple[a: seq[int], b: int]
+
+var t: T = (@[1,2,3], 7)
+
+proc test(s: seq[int]): T =
+ echo s
+ (s, 7)
+
+t = test(t.a)
diff --git a/tests/types/temptyseqs.nim b/tests/types/temptyseqs.nim
index 2b07ba679a..834f63729f 100644
--- a/tests/types/temptyseqs.nim
+++ b/tests/types/temptyseqs.nim
@@ -1,5 +1,13 @@
discard """
- output: "1"
+ output: '''1
+foo
+bar
+baz
+foo
+bar
+baz
+yes
+no'''
"""
# bug #1708
@@ -24,3 +32,59 @@ when true:
const foo2: seq[string] = @[]
echo foo[0][0][0]
+
+proc takeEmpty(x: openArray[string] = []) = discard
+takeEmpty()
+takeEmpty([])
+
+proc takeEmpty2(x: openArray[string] = @[]) = discard
+takeEmpty2()
+takeEmpty2([])
+takeEmpty2(@[])
+
+#takeEmpty2([nil])
+
+#rawMessage(errExecutionOfProgramFailed, [])
+
+# bug #2470
+const
+ stuff: seq[string] = @[]
+
+for str in stuff:
+ echo "str=", str
+
+# bug #1354
+proc foo4[T](more: seq[T] = @[]) =
+ var more2 = more
+
+foo4[int]()
+
+proc maino: int =
+ var wd: cstring = nil
+ inc result
+
+discard maino()
+
+proc varargso(a: varargs[string]) =
+ for x in a:
+ echo x
+
+varargso(["foo", "bar", "baz"])
+varargso("foo", "bar", "baz")
+
+
+type
+ Flago = enum
+ tfNeedsInit, tfNotNil
+
+var s: set[Flago] = {tfNeedsInit}
+
+if {tfNeedsInit, tfNotNil} * s != {}:
+ echo "yes"
+else:
+ echo "no"
+
+if {tfNeedsInit, tfNotNil} * s <= {tfNotNil}:
+ echo "yes"
+else:
+ echo "no"
diff --git a/tests/types/tisopr.nim b/tests/types/tisopr.nim
index 8b7fe4e46c..b9acfa5fbe 100644
--- a/tests/types/tisopr.nim
+++ b/tests/types/tisopr.nim
@@ -1,5 +1,11 @@
discard """
- output: '''true true false yes'''
+ output: '''true true false yes
+false
+false
+false
+true
+true
+no'''
"""
proc IsVoid[T](): string =
@@ -28,7 +34,7 @@ no s.items is iterator: float
yes s.items is iterator: TNumber
no s.items is iterator: object
-type
+type
Iter[T] = iterator: T
yes s.items is Iter[TNumber]
@@ -51,3 +57,34 @@ yes Foo[4, int] is Bar[int]
no Foo[4, int] is Baz[4]
yes Foo[4, float] is Baz[4]
+
+# bug #2505
+
+echo(8'i8 is int32)
+
+# bug #1853
+type SeqOrSet[E] = seq[E] or set[E]
+type SeqOfInt = seq[int]
+type SeqOrSetOfInt = SeqOrSet[int]
+
+# This prints "false", which seems less correct that (1) printing "true" or (2)
+# raising a compiler error.
+echo seq is SeqOrSet
+
+# This prints "false", as expected.
+echo seq is SeqOrSetOfInt
+
+# This prints "true", as expected.
+echo SeqOfInt is SeqOrSet
+
+# This causes an internal error (filename: compiler/semtypes.nim, line: 685).
+echo SeqOfInt is SeqOrSetOfInt
+
+# bug #2522
+proc test[T](x: T) =
+ when T is typedesc:
+ echo "yes"
+ else:
+ echo "no"
+
+test(7)
diff --git a/todo.txt b/todo.txt
index a61f932a93..4972015a73 100644
--- a/todo.txt
+++ b/todo.txt
@@ -1,7 +1,21 @@
-version 0.10.4
+version 0.11.2
==============
-- make 'nil' work for 'add' and 'len'
+- The remaining bugs of the lambda lifting pass that is responsible to enable
+ closures and closure iterators need to be fixed.
+- ``concept`` needs to be refined, a nice name for the feature is not enough.
+- Destructors need to be refined.
+- make '--implicitStatic:on' the default; then we can also clean up the
+ 'static[T]' mess in the compiler!
+
+- Finish the implementation of the 'parallel' statement.
+- Deprecate ``immediate`` for templates and macros
+- special case varargs[untyped] and varargs[typed]
+- make 'nil' work for 'add':
+ - resizeString
+ - incrSeq
+ - addChar
+
version 1.0
===========
@@ -35,8 +49,6 @@ Low priority:
Misc
----
-- make '--implicitStatic:on' the default; then we can also clean up the
- 'static[T]' mess in the compiler!
- make tuple unpacking work in a non-var/let context
- built-in 'getImpl'
- prevent 'alloc(TypeWithGCedMemory)'
@@ -50,6 +62,7 @@ Bugs
- scopes are still broken for generic instantiation!
- blocks can "export" an identifier but the CCG generates {} for them ...
- ConcreteTypes in a 'case' means we don't check for duplicated case branches
+- typedesc matches a generic type T!
version 0.9.x
diff --git a/tools/niminst/niminst.nim b/tools/niminst/niminst.nim
index e50b251d37..f0ae45484f 100644
--- a/tools/niminst/niminst.nim
+++ b/tools/niminst/niminst.nim
@@ -35,6 +35,7 @@ type
actionNsis, # action: create NSIS installer
actionScripts # action: create install and deinstall scripts
actionZip, # action: create zip file
+ actionTargz, # action: create targz file
actionDeb # action: prepare deb package
FileCategory = enum
@@ -171,6 +172,7 @@ proc parseCmdLine(c: var ConfigData) =
of "csource": incl(c.actions, actionCSource)
of "scripts": incl(c.actions, actionScripts)
of "zip": incl(c.actions, actionZip)
+ of "targz": incl(c.actions, actionTargz)
of "inno": incl(c.actions, actionInno)
of "nsis": incl(c.actions, actionNsis)
of "deb": incl(c.actions, actionDeb)
@@ -181,10 +183,10 @@ proc parseCmdLine(c: var ConfigData) =
break
of cmdLongoption, cmdShortOption:
case normalize(key.string)
- of "help", "h":
+ of "help", "h":
stdout.write(Usage)
quit(0)
- of "version", "v":
+ of "version", "v":
stdout.write(Version & "\n")
quit(0)
of "o", "output": c.outdir = val
@@ -240,7 +242,7 @@ proc incl(s: var seq[string], x: string): int =
for i in 0.. div.active { visibility:visible; opacity:1; transition-delay:0s; }
#slideshow > div.init { transition-delay:0s; }
- #slideshow-nav { z-index:3; position:absolute; top:110px;; right:-12px; }
- #slideshow-nav > div { margin:5px 0; width:23px; height:23px; background:url("images/slideshow-nav.png") no-repeat; }
+ #slideshow-nav { z-index:3; position:absolute; top:341px; left:18px; }
+ #slideshow-nav > div { display:inline-block; margin:5px 0; width:23px; height:23px; background:url("images/slideshow-nav.png") no-repeat; }
#slideshow-nav > div:hover { background-image:url("images/slideshow-nav_active.png"); opacity:0.5; }
#slideshow-nav > div.active { background-image:url("images/slideshow-nav_active.png"); opacity:1; }
diff --git a/web/documentation.txt b/web/documentation.txt
index dbb737cd9c..67f8b4070b 100644
--- a/web/documentation.txt
+++ b/web/documentation.txt
@@ -8,13 +8,13 @@ Nim's Documentation
.. container:: libraries
- - | `Standard Library `_
+ - | `Standard Library `_
| This document describes Nim's standard library.
- - | `Language Manual `_
+ - | `Language Manual `_
| The Nim manual is a draft that will evolve into a proper specification.
- - | `Compiler User Guide `_
+ - | `Compiler User Guide `_
| The user guide lists command line arguments, special features of the
compiler, etc.
@@ -26,11 +26,11 @@ Nim's Documentation
.. container:: tools
- - | `Source Code Filters `_
+ - | `Source Code Filters `_
| The Nim compiler supports source code filters as a simple yet powerful
builtin templating system.
- - | `Tools Documentation `_
+ - | `Tools Documentation `_
| Description of some tools that come with the standard distribution.
@@ -41,16 +41,17 @@ Nim's Documentation
.. container:: internals
- - | `Garbage Collector `_
+ - | `Garbage Collector `_
| Additional documentation about Nim's GC and how to operate it in a
realtime setting.
- - | `Internal Documentation `_
- | The internal documentation describes how the compiler is implemented. Read
- this if you want to hack the compiler.
+ - | `Internal Documentation `_
+ | The internal documentation describes how the compiler is implemented.
+ Read this if you want to hack the compiler.
Search Options
--------------
-`Documentation Index `_ - The generated index. **Index + (Ctrl+F) == Joy**
+`Documentation Index `_ - The generated
+index. **Index + (Ctrl+F) == Joy**
diff --git a/web/download.txt b/web/download.txt
index 3d47467f23..6acc80b53f 100644
--- a/web/download.txt
+++ b/web/download.txt
@@ -13,8 +13,8 @@ Binaries
--------
Unfortunately for now we only provide builds for Windows.
-* 32 bit: `nim-0.10.2_x32.exe `_
-* 64 bit: `nim-0.10.2_x64.exe `_
+* 32 bit: `nim-0.11.2_x32.exe `_
+* 64 bit: `nim-0.11.2_x64.exe `_
Installation based on generated C code
@@ -24,8 +24,12 @@ This installation method is the preferred way for Linux, Mac OS X, and other Uni
like systems. Binary packages may be provided later.
-Download `nim-0.10.2.zip `_, extract it and follow
-these instructions:
+Download one of these:
+
+* `nim-0.11.2.zip (28 MB) `_
+* `nim-0.11.2.tar.xz (2.6MB) `_
+
+Extract the file and follow these instructions:
* sh build.sh
* Add ``$your_install_dir/bin`` to your PATH.
diff --git a/web/learn.txt b/web/learn.txt
index 7a9600e570..bf0cc43efc 100644
--- a/web/learn.txt
+++ b/web/learn.txt
@@ -8,10 +8,10 @@ Learning Nim
.. container:: tutorials
- - | `Tutorial (part I) `_
+ - | `Tutorial (part I) `_
| Learn the basics of Nim's types, variables, procedures, control flow, etc...
- - | `Tutorial (part II) `_
+ - | `Tutorial (part II) `_
| Learn Nim's more advanced features such as OOP, generics, macros, etc...
@@ -52,5 +52,5 @@ Learning Nim
Documentation
-------------
-More examples of Nim code can be found in the `Nim Language Documentation `_.
+More examples of Nim code can be found in the `Nim Language Documentation `_.
diff --git a/web/news.txt b/web/news.txt
index af44f91a1f..9719fb8d7b 100644
--- a/web/news.txt
+++ b/web/news.txt
@@ -3,333 +3,423 @@ News
====
..
- 2015-03-01 Version 0.10.4 released
+ 2015-05-05 Version 0.11.2 released
==================================
-
Changes affecting backwards compatibility
-----------------------------------------
- - Parameter names are finally properly ``gensym``'ed. This can break
- templates though that used to rely on the fact that they are not.
- (Bug #1915.) This means this doesn't compile anymore:
-
- .. code-block:: nim
-
- template doIt(body: stmt) {.immediate.} =
- # this used to inject the 'str' parameter:
- proc res(str: string) =
- body
-
- doIt:
- echo str # Error: undeclared identifier: 'str'
-
- Declare the ``doIt`` template as ``immediate, dirty`` to get the old
- behaviour.
- - Tuple field names are not ignored anymore, this caused too many problems
- in practice so now the behaviour as it was for version 0.9.6: If field
- names exist for the tuple type, they are checked.
- - ``logging.level`` and ``logging.handlers`` are no longer exported.
- ``addHandler``, ``getHandlers``, ``setLogFilter`` and ``getLogFilter``
- should be used instead.
- - ``nim idetools`` has been replaced by a separate tool `nimsuggest`_.
- - *arrow like* operators are not right associative anymore.
- - Typeless parameters are now only allowed in templates and macros. The old
- way turned out to be too error-prone.
- - The 'addr' and 'type' operators are now parsed as unary function
- application. This means ``type(x).name`` is now parsed as ``(type(x)).name``
- and not as ``type((x).name)``. Note that this also affects the AST
- structure; for immediate macro parameters ``nkCall('addr', 'x')`` is
- produced instead of ``nkAddr('x')``.
- - ``concept`` is now a keyword and is used instead of ``generic``.
- - The ``inc``, ``dec``, ``+=``, ``-=`` builtins now produce OverflowError
- exceptions. This means code like the following:
-
- .. code-block:: nim
- var x = low(T)
- while x <= high(T):
- echo x
- inc x
-
- Needs to be replaced by something like this:
-
- .. code-block:: nim
- var x = low(T).int
- while x <= high(T).int:
- echo x.T
- inc x
-
- - **Negative indexing for slicing does not work anymore!** Instead
- of ``a[0.. -1]`` you can
- use ``a[0.. ^1]``. This also works with accessing a single
- element ``a[^1]``. Note that we cannot detect this reliably as it is
- determined at **runtime** whether negative indexing is used!
- ``a[0.. -1]`` now produces the empty string/sequence.
- - The compiler now warns about code like ``foo +=1`` which uses inconsistent
- spacing around binary operators. Later versions of the language will parse
- these as unary operators instead so that ``echo $foo`` finally can do what
- people expect it to do.
- - ``system.untyped`` and ``system.typed`` have been introduced as aliases
- for ``expr`` and ``stmt``. The new names capture the semantics much better
- and most likely ``expr`` and ``stmt`` will be deprecated in favor of the
- new names.
- - The ``split`` method in module ``re`` has changed. It now handles the case
- of matches having a length of 0, and empty strings being yielded from the
- iterator. A notable change might be that a pattern being matched at the
- beginning and end of a string, will result in an empty string being produced
- at the start and the end of the iterator.
Language Additions
------------------
- - For empty ``case object`` branches ``discard`` can finally be used instead
- of ``nil``.
- - Automatic dereferencing is now done for the first argument of a routine
- call if overloading resolution produces no match otherwise. This feature
- has to be enabled with the `experimental`_ pragma.
- - Objects that do not use inheritance nor ``case`` can be put into ``const``
- sections. This means that finally this is possible and produces rather
- nice code:
-
- .. code-block:: nim
- import tables
-
- const
- foo = {"ah": "finally", "this": "is", "possible.": "nice!"}.toTable()
-
-
- - Ordinary parameters can follow after a varargs parameter. This means the
- following is finally accepted by the compiler:
-
- .. code-block:: nim
- template takesBlock(a, b: int, x: varargs[expr]; blck: stmt) =
- blck
- echo a, b
-
- takesBlock 1, 2, "some", 0.90, "random stuff":
- echo "yay"
-
- - Overloading by 'var T' is now finally possible:
-
- .. code-block:: nim
- proc varOrConst(x: var int) = echo "var"
- proc varOrConst(x: int) = echo "const"
-
- var x: int
- varOrConst(x) # "var"
- varOrConst(45) # "const"
-
- - Array and seq indexing can now use the builtin ``^`` operator to access
- things from backwards: ``a[^1]`` is like Python's ``a[-1]``.
- - A first version of the specification and implementation of the overloading
- of the assignment operator has arrived!
-
-
- Library additions
- -----------------
-
- - ``reversed`` proc added to the ``unicode`` module.
- - Added multipart param to httpclient's ``post`` and ``postContent`` together
- with a ``newMultipartData`` proc.
- - Added `%*` operator for JSON.
- - The compiler is now available as Nimble package for c2nim.
-
Bugfixes
--------
- - Fixed internal compiler error when using ``char()`` in an echo call
- (`#1788 `_).
- - Fixed Windows cross-compilation on Linux.
- - Overload resolution now works for types distinguished only by a
- ``static[int]`` param
- (`#1056 `_).
- - Other fixes relating to generic types and static params.
- - Fixed some compiler crashes with unnamed tuples
- (`#1774 `_).
- - Fixed ``channels.tryRecv`` blocking
- (`#1816 `_).
- - Fixed generic instantiation errors with ``typedesc``
- (`#419 `_).
- - Fixed generic regression where the compiler no longer detected constant
- expressions properly (`#544 `_).
- - Fixed internal error with generic proc using ``static[T]`` in a specific
- way (`#1049 `_).
- - More fixes relating to generics
- (`#1820 `_,
- `#1050 `_,
- `#1859 `_,
- `#1858 `_).
- - Fixed httpclient to properly encode queries.
- - Many fixes to the ``uri`` module.
- - Async sockets are now closed on error.
- - Fixes to httpclient's handling of multipart data.
- - Fixed GC segfaults with asynchronous sockets
- (`#1796 `_).
- - Added more versions to openssl's DLL version list
- (`076f993 `_).
- - Fixed shallow copy in iterators being broken
- (`#1803 `_).
- - ``nil`` can now be inserted into tables with the ``db_sqlite`` module
- (`#1866 `_).
- - Fixed "Incorrect assembler generated"
- (`#1907 `_)
- - Fixed "Expression templates that define macros are unusable in some contexts"
- (`#1903 `_)
- - Fixed "a second level generic subclass causes the compiler to crash"
- (`#1919 `_)
- - Fixed "nim 0.10.2 generates invalid AsyncHttpClient C code for MSVC "
- (`#1901 `_)
- - Fixed "1 shl n produces wrong C code"
- (`#1928 `_)
- - Fixed "Internal error on tuple yield"
- (`#1838 `_)
- - Fixed "ICE with template"
- (`#1915 `_)
- - Fixed "include the tool directory in the installer as it is required by koch"
- (`#1947 `_)
- - Fixed "Can't compile if file location contains spaces on Windows"
- (`#1955 `_)
- - Fixed "List comprehension macro only supports infix checks as guards"
- (`#1920 `_)
- - Fixed "wrong field names of compatible tuples in generic types"
- (`#1910 `_)
- - Fixed "Macros within templates no longer work as expected"
- (`#1944 `_)
- - Fixed "Compiling for Standalone AVR broken in 0.10.2"
- (`#1964 `_)
- - Fixed "Compiling for Standalone AVR broken in 0.10.2"
- (`#1964 `_)
- - Fixed "Code generation for mitems with tuple elements"
- (`#1833 `_)
- - Fixed "httpclient.HttpMethod should not be an enum"
- (`#1962 `_)
- - Fixed "terminal / eraseScreen() throws an OverflowError"
- (`#1906 `_)
- - Fixed "setControlCHook(nil) disables registered quit procs"
- (`#1546 `_)
- - Fixed "Unexpected idetools behaviour"
- (`#325 `_)
- - Fixed "Unused lifted lambda does not compile"
- (`#1642 `_)
- - Fixed "'low' and 'high' don't work with cstring asguments"
- (`#2030 `_)
- - Fixed "Converting to int does not round in JS backend"
- (`#1959 `_)
- - Fixed "Internal error genRecordField 2 when adding region to pointer."
- (`#2039 `_)
- - Fixed "Macros fail to compile when compiled with --os:standalone"
- (`#2041 `_)
- - Fixed "Reading from {.compileTime.} variables can cause code generation to fail"
- (`#2022 `_)
- - Fixed "Passing overloaded symbols to templates fails inside generic procedures"
- (`#1988 `_)
- - Fixed "Compiling iterator with object assignment in release mode causes "var not init""
- (`#2023 `_)
- - Fixed "calling a large number of macros doing some computation fails"
- (`#1989 `_)
- - Fixed "Can't get Koch to install nim under Windows"
- (`#2061 `_)
- - Fixed "Template with two stmt parameters segfaults compiler"
- (`#2057 `_)
- - Fixed "`noSideEffect` not affected by `echo`"
- (`#2011 `_)
- - Fixed "Compiling with the cpp backend ignores --passc"
- (`#1601 `_)
- - Fixed "Put untyped procedure parameters behind the experimental pragma"
- (`#1956 `_)
- - Fixed "generic regression"
- (`#2073 `_)
- - Fixed "generic regression"
- (`#2073 `_)
- - Fixed "Regression in template lookup with generics"
- (`#2004 `_)
- - Fixed "GC's growObj is wrong for edge cases"
- (`#2070 `_)
- - Fixed "Compiler internal error when creating an array out of a typeclass"
- (`#1131 `_)
- - Fixed "GC's growObj is wrong for edge cases"
- (`#2070 `_)
- - Fixed "Invalid Objective-C code generated when calling class method"
- (`#2068 `_)
- - Fixed "walkDirRec Error"
- (`#2116 `_)
- - Fixed "Typo in code causes compiler SIGSEGV in evalAtCompileTime"
- (`#2113 `_)
- - Fixed "Regression on exportc"
- (`#2118 `_)
- - Fixed "Error message"
- (`#2102 `_)
- - Fixed "hint[path] = off not working in nim.cfg"
- (`#2103 `_)
- - Fixed "compiler crashes when getting a tuple from a sequence of generic tuples"
- (`#2121 `_)
- - Fixed "nim check hangs with when"
- (`#2123 `_)
- - Fixed "static[T] param in nested type resolve/caching issue"
- (`#2125 `_)
- - Fixed "repr should display ``\0``"
- (`#2124 `_)
- - Fixed "'nim check' never ends in case of recursive dependency "
- (`#2051 `_)
- - Fixed "From macros: Error: unhandled exception: sons is not accessible"
- (`#2167 `_)
- - Fixed "`fieldPairs` doesn't work inside templates"
- (`#1902 `_)
- - Fixed "fields iterator misbehavior on break statement"
- (`#2134 `_)
- - Fixed "Fix for compiler not building anymore since #c3244ef1ff"
- (`#2193 `_)
- - Fixed "JSON parser fails in cpp output mode"
- (`#2199 `_)
- - Fixed "macros.getType mishandles void return"
- (`#2211 `_)
- - Fixed "Regression involving templates instantiated within generics"
- (`#2215 `_)
- - Fixed ""Error: invalid type" for 'not nil' on generic type."
- (`#2216 `_)
- - Fixed "--threads:on breaks async"
- (`#2074 `_)
- - Fixed "Type mismatch not always caught, can generate bad code for C backend."
- (`#2169 `_)
- - Fixed "Failed C compilation when storing proc to own type in object"
- (`#2233 `_)
- - Fixed "Unknown line/column number in constant declaration type conversion error"
- (`#2252 `_)
- - Fixed "Adding {.compile.} fails if nimcache already exists."
- (`#2247 `_)
- - Fixed "Two different type names generated for a single type (C backend)"
- (`#2250 `_)
- - Fixed "Ambigous call when it should not be"
- (`#2229 `_)
- - Fixed "Make sure we can load root urls"
- (`#2227 `_)
- - Fixed "Failure to slice a string with an int subrange type"
- (`#794 `_)
- - Fixed "documentation error"
- (`#2205 `_)
- - Fixed "Code growth when using `const`"
- (`#1940 `_)
- - Fixed "Instances of generic types confuse overload resolution"
- (`#2220 `_)
- - Fixed "Compiler error when initializing sdl2's EventType"
- (`#2316 `_)
- - Fixed "Parallel disjoint checking can't handle `<`, `items`, or arrays"
- (`#2287 `_)
- - Fixed "Strings aren't copied in parallel loop"
- (`#2286 `_)
- - Fixed "JavaScript compiler crash with tables"
- (`#2298 `_)
- - Fixed "Range checker too restrictive"
- (`#1845 `_)
- - Fixed "Failure to slice a string with an int subrange type"
- (`#794 `_)
- - Fixed "Remind user when compiling in debug mode"
- (`#1868 `_)
- - Fixed "Compiler user guide has jumbled options/commands."
- (`#1819 `_)
- - Fixed "using `method`: 1 in a objects constructor fails when compiling"
- (`#1791 `_)
+
+2015-05-04 Version 0.11.2 released
+==================================
+
+This is just a bugfix release that fixes the most pressing regressions we
+introduced with version 0.11.0. The way types are computed was
+changed significantly causing all sort of problems. Sorry for the
+inconvenience; we grew overconfident our large test suite would prevent these
+things.
+
+
+2015-04-30 Version 0.11.0 released
+==================================
+
+With this release we are one step closer to reaching version 1.0 and by
+extension the persistence of the Nim specification. As mentioned in the
+previous release notes, starting with version 1.0, we will not be introducing
+any more breaking changes to Nim.
+
+The *language* itself is very close to 1.0, the primary area that requires
+more work is the standard library.
+
+Take a look at the `download `_ page for binaries (Windows-only)
+and 0.11.0 snapshots of the source code. The Windows installer now also
+includes `Aporia `_,
+`Nimble `_ and other useful tools to get
+you started with Nim.
+
+What's left to be done
+~~~~~~~~~~~~~~~~~~~~~~
+
+The 1.0 release is expected by the end of this year. Rumors say it will be in
+summer 2015. What's left:
+
+* Bug fixes, bug fixes, bug fixes, in particular:
+ - The remaining bugs of the lambda lifting pass that is responsible to enable
+ closures and closure iterators need to be fixed.
+ - ``concept`` needs to be refined, a nice name for the feature is not enough.
+ - Destructors need to be refined.
+ - ``static[T]`` needs to be fixed.
+ - Finish the implementation of the 'parallel' statement.
+* ``immediate`` templates and macros will be deprecated as these will soon be
+ completely unnecessary, instead the ``typed`` or ``untyped`` metatypes can
+ be used.
+* More of the standard library should be moved to Nimble packages and what's
+ left should use the features we have for concurrency and parallelism.
+
+
+
+Changes affecting backwards compatibility
+-----------------------------------------
+
+- Parameter names are finally properly ``gensym``'ed. This can break
+ templates though that used to rely on the fact that they are not.
+ (Bug #1915.) This means this doesn't compile anymore:
+
+.. code-block:: nim
+
+ template doIt(body: stmt) {.immediate.} =
+ # this used to inject the 'str' parameter:
+ proc res(str: string) =
+ body
+
+ doIt:
+ echo str # Error: undeclared identifier: 'str'
+..
+
+ This used to inject the ``str`` parameter into the scope of the body.
+ Declare the ``doIt`` template as ``immediate, dirty`` to get the old
+ behaviour.
+- Tuple field names are not ignored anymore, this caused too many problems
+ in practice so now the behaviour is as it was for version 0.9.6: If field
+ names exist for the tuple type, they are checked.
+- ``logging.level`` and ``logging.handlers`` are no longer exported.
+ ``addHandler``, ``getHandlers``, ``setLogFilter`` and ``getLogFilter``
+ should be used instead.
+- ``nim idetools`` has been replaced by a separate
+ tool `nimsuggest <0.11.0/nimsuggest.html>`_.
+- *arrow like* operators are not right associative anymore and are required
+ to end with either ``->``, ``~>`` or
+ ``=>``, not just ``>``. Examples of operators still considered arrow like:
+ ``->``, ``==>``, ``+=>``. On the other hand, the following operators are now
+ considered regular operators again: ``|>``, ``-+>``, etc.
+- Typeless parameters are now only allowed in templates and macros. The old
+ way turned out to be too error-prone.
+- The 'addr' and 'type' operators are now parsed as unary function
+ application. This means ``type(x).name`` is now parsed as ``(type(x)).name``
+ and not as ``type((x).name)``. Note that this also affects the AST
+ structure; for immediate macro parameters ``nkCall('addr', 'x')`` is
+ produced instead of ``nkAddr('x')``.
+- ``concept`` is now a keyword and is used instead of ``generic``.
+- The ``inc``, ``dec``, ``+=``, ``-=`` builtins now produce OverflowError
+ exceptions. This means code like the following:
+
+.. code-block:: nim
+ var x = low(T)
+ while x <= high(T):
+ echo x
+ inc x
+
+Needs to be replaced by something like this:
+
+.. code-block:: nim
+ var x = low(T).int
+ while x <= high(T).int:
+ echo x.T
+ inc x
+
+- **Negative indexing for slicing does not work anymore!** Instead
+ of ``a[0.. -1]`` you can
+ use ``a[0.. ^1]``. This also works with accessing a single
+ element ``a[^1]``. Note that we cannot detect this reliably as it is
+ determined at **runtime** whether negative indexing is used!
+ ``a[0.. -1]`` now produces the empty string/sequence.
+- The compiler now warns about code like ``foo +=1`` which uses inconsistent
+ spacing around binary operators. Later versions of the language will parse
+ these as unary operators instead so that ``echo $foo`` finally can do what
+ people expect it to do.
+- ``system.untyped`` and ``system.typed`` have been introduced as aliases
+ for ``expr`` and ``stmt``. The new names capture the semantics much better
+ and most likely ``expr`` and ``stmt`` will be deprecated in favor of the
+ new names.
+- The ``split`` method in module ``re`` has changed. It now handles the case
+ of matches having a length of 0, and empty strings being yielded from the
+ iterator. A notable change might be that a pattern being matched at the
+ beginning and end of a string, will result in an empty string being produced
+ at the start and the end of the iterator.
+- The compiler and nimsuggest now count columns starting with 1, not 0 for
+ consistency with the rest of the world.
+
+
+Language Additions
+------------------
+
+- For empty ``case object`` branches ``discard`` can finally be used instead
+ of ``nil``.
+- Automatic dereferencing is now done for the first argument of a routine
+ call if overloading resolution produces no match otherwise. This feature
+ has to be enabled with
+ the `experimental <0.11.0/manual.html#pragmas-experimental-pragma>`_ pragma.
+- Objects that do not use inheritance nor ``case`` can be put into ``const``
+ sections. This means that finally this is possible and produces rather
+ nice code:
+
+.. code-block:: nim
+ import tables
+
+ const
+ foo = {"ah": "finally", "this": "is", "possible.": "nice!"}.toTable()
+
+
+- Ordinary parameters can follow after a varargs parameter. This means the
+ following is finally accepted by the compiler:
+
+.. code-block:: nim
+ template takesBlock(a, b: int, x: varargs[expr]; blck: stmt) =
+ blck
+ echo a, b
+
+ takesBlock 1, 2, "some", 0.90, "random stuff":
+ echo "yay"
+
+- Overloading by 'var T' is now finally possible:
+
+.. code-block:: nim
+ proc varOrConst(x: var int) = echo "var"
+ proc varOrConst(x: int) = echo "const"
+
+ var x: int
+ varOrConst(x) # "var"
+ varOrConst(45) # "const"
+
+- Array and seq indexing can now use the builtin ``^`` operator to access
+ things from backwards: ``a[^1]`` is like Python's ``a[-1]``.
+- A first version of the specification and implementation of the overloading
+ of the assignment operator has arrived!
+- ``system.len`` for strings and sequences now returns 0 for nil.
+
+- A single underscore can now be used to discard values when unpacking tuples:
+
+.. code-block:: nim
+ let (path, _, _) = os.splitFile("path/file.ext")
+
+
+- ``marshal.$$`` and ``marshal.to`` can be executed at compile-time.
+- Interoperability with C++ improved tremendously; C++'s templates and
+ operators can be wrapped directly. See
+ `this <0.11.0/nimc.html#additional-features-importcpp-pragma>`_
+ for more information.
+- ``macros.getType`` can be used to query an AST's type at compile-time. This
+ enables more powerful macros, for instance *currying* can now be done with
+ a macro.
+
+
+Library additions
+-----------------
+
+- ``reversed`` proc added to the ``unicode`` module.
+- Added multipart param to httpclient's ``post`` and ``postContent`` together
+ with a ``newMultipartData`` proc.
+- Added `%*` operator for JSON.
+- The compiler is now available as Nimble package for c2nim.
+- Added ``..^`` and ``..<`` templates to system so that the rather annoying
+ space between ``.. <`` and ``.. ^`` is not necessary anymore.
+- Added ``system.xlen`` for strings and sequences to get back the old ``len``
+ operation that doesn't check for ``nil`` for efficiency.
+
+
+Bugfixes
+--------
+
+- Fixed internal compiler error when using ``char()`` in an echo call
+ (`#1788 `_).
+- Fixed Windows cross-compilation on Linux.
+- Overload resolution now works for types distinguished only by a
+ ``static[int]`` param
+ (`#1056 `_).
+- Other fixes relating to generic types and static params.
+- Fixed some compiler crashes with unnamed tuples
+ (`#1774 `_).
+- Fixed ``channels.tryRecv`` blocking
+ (`#1816 `_).
+- Fixed generic instantiation errors with ``typedesc``
+ (`#419 `_).
+- Fixed generic regression where the compiler no longer detected constant
+ expressions properly (`#544 `_).
+- Fixed internal error with generic proc using ``static[T]`` in a specific
+ way (`#1049 `_).
+- More fixes relating to generics (`#1820 `_,
+ `#1050 `_,
+ `#1859 `_,
+ `#1858 `_).
+- Fixed httpclient to properly encode queries.
+- Many fixes to the ``uri`` module.
+- Async sockets are now closed on error.
+- Fixes to httpclient's handling of multipart data.
+- Fixed GC segfaults with asynchronous sockets
+ (`#1796 `_).
+- Added more versions to openssl's DLL version list
+ (`076f993 `_).
+- Fixed shallow copy in iterators being broken
+ (`#1803 `_).
+- ``nil`` can now be inserted into tables with the ``db_sqlite`` module
+ (`#1866 `_).
+- Fixed "Incorrect assembler generated"
+ (`#1907 `_)
+- Fixed "Expression templates that define macros are unusable in some contexts"
+ (`#1903 `_)
+- Fixed "a second level generic subclass causes the compiler to crash"
+ (`#1919 `_)
+- Fixed "nim 0.10.2 generates invalid AsyncHttpClient C code for MSVC "
+ (`#1901 `_)
+- Fixed "1 shl n produces wrong C code"
+ (`#1928 `_)
+- Fixed "Internal error on tuple yield"
+ (`#1838 `_)
+- Fixed "ICE with template"
+ (`#1915 `_)
+- Fixed "include the tool directory in the installer as it is required by koch"
+ (`#1947 `_)
+- Fixed "Can't compile if file location contains spaces on Windows"
+ (`#1955 `_)
+- Fixed "List comprehension macro only supports infix checks as guards"
+ (`#1920 `_)
+- Fixed "wrong field names of compatible tuples in generic types"
+ (`#1910 `_)
+- Fixed "Macros within templates no longer work as expected"
+ (`#1944 `_)
+- Fixed "Compiling for Standalone AVR broken in 0.10.2"
+ (`#1964 `_)
+- Fixed "Compiling for Standalone AVR broken in 0.10.2"
+ (`#1964 `_)
+- Fixed "Code generation for mitems with tuple elements"
+ (`#1833 `_)
+- Fixed "httpclient.HttpMethod should not be an enum"
+ (`#1962 `_)
+- Fixed "terminal / eraseScreen() throws an OverflowError"
+ (`#1906 `_)
+- Fixed "setControlCHook(nil) disables registered quit procs"
+ (`#1546 `_)
+- Fixed "Unexpected idetools behaviour"
+ (`#325 `_)
+- Fixed "Unused lifted lambda does not compile"
+ (`#1642 `_)
+- Fixed "'low' and 'high' don't work with cstring asguments"
+ (`#2030 `_)
+- Fixed "Converting to int does not round in JS backend"
+ (`#1959 `_)
+- Fixed "Internal error genRecordField 2 when adding region to pointer."
+ (`#2039 `_)
+- Fixed "Macros fail to compile when compiled with --os:standalone"
+ (`#2041 `_)
+- Fixed "Reading from {.compileTime.} variables can cause code generation to fail"
+ (`#2022 `_)
+- Fixed "Passing overloaded symbols to templates fails inside generic procedures"
+ (`#1988 `_)
+- Fixed "Compiling iterator with object assignment in release mode causes "var not init""
+ (`#2023 `_)
+- Fixed "calling a large number of macros doing some computation fails"
+ (`#1989 `_)
+- Fixed "Can't get Koch to install nim under Windows"
+ (`#2061 `_)
+- Fixed "Template with two stmt parameters segfaults compiler"
+ (`#2057 `_)
+- Fixed "`noSideEffect` not affected by `echo`"
+ (`#2011 `_)
+- Fixed "Compiling with the cpp backend ignores --passc"
+ (`#1601 `_)
+- Fixed "Put untyped procedure parameters behind the experimental pragma"
+ (`#1956 `_)
+- Fixed "generic regression"
+ (`#2073 `_)
+- Fixed "generic regression"
+ (`#2073 `_)
+- Fixed "Regression in template lookup with generics"
+ (`#2004 `_)
+- Fixed "GC's growObj is wrong for edge cases"
+ (`#2070 `_)
+- Fixed "Compiler internal error when creating an array out of a typeclass"
+ (`#1131 `_)
+- Fixed "GC's growObj is wrong for edge cases"
+ (`#2070 `_)
+- Fixed "Invalid Objective-C code generated when calling class method"
+ (`#2068 `_)
+- Fixed "walkDirRec Error"
+ (`#2116 `_)
+- Fixed "Typo in code causes compiler SIGSEGV in evalAtCompileTime"
+ (`#2113 `_)
+- Fixed "Regression on exportc"
+ (`#2118 `_)
+- Fixed "Error message"
+ (`#2102 `_)
+- Fixed "hint[path] = off not working in nim.cfg"
+ (`#2103 `_)
+- Fixed "compiler crashes when getting a tuple from a sequence of generic tuples"
+ (`#2121 `_)
+- Fixed "nim check hangs with when"
+ (`#2123 `_)
+- Fixed "static[T] param in nested type resolve/caching issue"
+ (`#2125 `_)
+- Fixed "repr should display ``\0``"
+ (`#2124 `_)
+- Fixed "'nim check' never ends in case of recursive dependency "
+ (`#2051 `_)
+- Fixed "From macros: Error: unhandled exception: sons is not accessible"
+ (`#2167 `_)
+- Fixed "`fieldPairs` doesn't work inside templates"
+ (`#1902 `_)
+- Fixed "fields iterator misbehavior on break statement"
+ (`#2134 `_)
+- Fixed "Fix for compiler not building anymore since #c3244ef1ff"
+ (`#2193 `_)
+- Fixed "JSON parser fails in cpp output mode"
+ (`#2199 `_)
+- Fixed "macros.getType mishandles void return"
+ (`#2211 `_)
+- Fixed "Regression involving templates instantiated within generics"
+ (`#2215 `_)
+- Fixed ""Error: invalid type" for 'not nil' on generic type."
+ (`#2216 `_)
+- Fixed "--threads:on breaks async"
+ (`#2074 `_)
+- Fixed "Type mismatch not always caught, can generate bad code for C backend."
+ (`#2169 `_)
+- Fixed "Failed C compilation when storing proc to own type in object"
+ (`#2233 `_)
+- Fixed "Unknown line/column number in constant declaration type conversion error"
+ (`#2252 `_)
+- Fixed "Adding {.compile.} fails if nimcache already exists."
+ (`#2247 `_)
+- Fixed "Two different type names generated for a single type (C backend)"
+ (`#2250 `_)
+- Fixed "Ambigous call when it should not be"
+ (`#2229 `_)
+- Fixed "Make sure we can load root urls"
+ (`#2227 `_)
+- Fixed "Failure to slice a string with an int subrange type"
+ (`#794 `_)
+- Fixed "documentation error"
+ (`#2205 `_)
+- Fixed "Code growth when using `const`"
+ (`#1940 `_)
+- Fixed "Instances of generic types confuse overload resolution"
+ (`#2220 `_)
+- Fixed "Compiler error when initializing sdl2's EventType"
+ (`#2316 `_)
+- Fixed "Parallel disjoint checking can't handle `<`, `items`, or arrays"
+ (`#2287 `_)
+- Fixed "Strings aren't copied in parallel loop"
+ (`#2286 `_)
+- Fixed "JavaScript compiler crash with tables"
+ (`#2298 `_)
+- Fixed "Range checker too restrictive"
+ (`#1845 `_)
+- Fixed "Failure to slice a string with an int subrange type"
+ (`#794 `_)
+- Fixed "Remind user when compiling in debug mode"
+ (`#1868 `_)
+- Fixed "Compiler user guide has jumbled options/commands."
+ (`#1819 `_)
+- Fixed "using `method`: 1 in a objects constructor fails when compiling"
+ (`#1791 `_)
+
2014-12-29 Version 0.10.2 released
==================================
diff --git a/web/question.txt b/web/question.txt
index 0733a2455f..c4d9979224 100644
--- a/web/question.txt
+++ b/web/question.txt
@@ -16,11 +16,11 @@ General FAQ
language that tries to give the programmer ultimate power without compromises
on runtime efficiency.
This means it focuses on compile-time mechanisms in all their
- various forms. Beneath a nice infix/indentation based syntax with a
- powerful (AST based, hygienic) macro system lies a semantic model that supports
- a soft realtime GC on thread local heaps. Asynchronous message passing is used
- between threads, so no "stop the world" mechanism is necessary. An unsafe
- shared memory heap is also provided for the increased efficiency that results
+ various forms. Beneath a nice infix/indentation based syntax with a
+ powerful (AST based, hygienic) macro system lies a semantic model that supports
+ a soft realtime GC on thread local heaps. Asynchronous message passing is used
+ between threads, so no "stop the world" mechanism is necessary. An unsafe
+ shared memory heap is also provided for the increased efficiency that results
from that model.
@@ -29,8 +29,8 @@ General FAQ
Why yet another programming language?
-------------------------------------
- Nim is one of the very few *programmable* statically typed languages, and
- one of the even fewer that produces native binaries that require no
+ Nim is one of the very few *programmable* statically typed languages, and
+ one of the even fewer that produces native binaries that require no
runtime or interpreter.
@@ -48,12 +48,12 @@ General FAQ
What is Nim's take on concurrency?
----------------------------------
- Nim primarily focusses on thread local (and garbage collected) heaps and
- message passing between threads. Each thread has its own GC, so no
+ Nim primarily focusses on thread local (and garbage collected) heaps and
+ message passing between threads. Each thread has its own GC, so no
"stop the world" mechanism is necessary. An unsafe shared memory heap is also
provided.
- Future versions will additionally include a GC "per thread group"
+ Future versions will additionally include a GC "per thread group"
and Nim's type system will be enhanced to accurately model this shared
memory heap.
@@ -74,7 +74,7 @@ General FAQ
------------------
The compiler is in development and some important features are still missing.
- However, the compiler is quite stable already: It is able to compile itself
+ However, the compiler is quite stable already: It is able to compile itself
and a substantial body of other code. Until version 1.0.0 is released,
minor incompatibilities with older versions of the compiler will be introduced.
@@ -83,9 +83,9 @@ General FAQ
How fast is Nim?
----------------
- Benchmarks show it to be comparable to C. Some language features (methods,
+ Benchmarks show it to be comparable to C. Some language features (methods,
closures, message passing) are not yet as optimized as they could and will be.
- The only overhead Nim has over C is the GC which has been tuned
+ The only overhead Nim has over C is the GC which has been tuned
for years but still needs some work.
@@ -96,7 +96,7 @@ General FAQ
A JVM backend is almost impossible. The JVM is not expressive enough. It has
never been designed as a general purpose VM anyway. A CLR backend is possible
- but would require much work.
+ but would require much work.
.. container:: standout
@@ -112,6 +112,7 @@ General FAQ
- jEdit: https://github.com/exhu/nimrod-misc/tree/master/jedit
- TextMate: Available in bundle installer (`Repository `_)
- Sublime Text: Available via Package Control (`Repository