Merge branch 'master' of https://github.com/Araq/Nim into patch-global-log-level
This commit is contained in:
commit
863dd62bc4
186 changed files with 5652 additions and 4330 deletions
36
appveyor.yml
36
appveyor.yml
|
|
@ -1,36 +0,0 @@
|
||||||
clone_depth: 5
|
|
||||||
|
|
||||||
artifacts:
|
|
||||||
- path: bin\nim.exe
|
|
||||||
|
|
||||||
platform:
|
|
||||||
- x64
|
|
||||||
|
|
||||||
before_build:
|
|
||||||
- git log -1
|
|
||||||
- C:\msys64\usr\bin\bash -lc "pacman --noconfirm -S zlib-devel"
|
|
||||||
- appveyor DownloadFile http://nim-lang.org/download/dlls.zip
|
|
||||||
- 7z e dlls.zip -odlls
|
|
||||||
- del dlls\libcurl.dll
|
|
||||||
- appveyor DownloadFile http://flatassembler.net/fasmw17139.zip
|
|
||||||
- 7z e fasmw17139.zip -obin fasm.exe
|
|
||||||
|
|
||||||
build_script:
|
|
||||||
- SET PATH=C:\msys64\mingw64\bin;dlls;bin;%PATH%
|
|
||||||
- gcc -v
|
|
||||||
- git clone -q --depth 1 https://github.com/nim-lang/csources
|
|
||||||
- cd csources
|
|
||||||
- build64.bat
|
|
||||||
- cd ..
|
|
||||||
- nim c koch
|
|
||||||
- koch boot
|
|
||||||
- koch boot -d:release
|
|
||||||
|
|
||||||
before_test:
|
|
||||||
- nim e install_nimble.nims
|
|
||||||
- nimble update
|
|
||||||
- nimble install zip
|
|
||||||
|
|
||||||
test_script:
|
|
||||||
- nim c --taintMode:on tests/testament/tester
|
|
||||||
- tests\testament\tester --pedantic all
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
[Package]
|
[Package]
|
||||||
name = "compiler"
|
name = "compiler"
|
||||||
version = "0.12.0"
|
version = "0.13.0"
|
||||||
author = "Andreas Rumpf"
|
author = "Andreas Rumpf"
|
||||||
description = "Compiler package providing the compiler sources as a library."
|
description = "Compiler package providing the compiler sources as a library."
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
|
|
|
||||||
|
|
@ -500,8 +500,7 @@ type
|
||||||
skResult, # special 'result' variable
|
skResult, # special 'result' variable
|
||||||
skProc, # a proc
|
skProc, # a proc
|
||||||
skMethod, # a method
|
skMethod, # a method
|
||||||
skIterator, # an inline iterator
|
skIterator, # an iterator
|
||||||
skClosureIterator, # a resumable closure iterator
|
|
||||||
skConverter, # a type converter
|
skConverter, # a type converter
|
||||||
skMacro, # a macro
|
skMacro, # a macro
|
||||||
skTemplate, # a template; currently also misused for user-defined
|
skTemplate, # a template; currently also misused for user-defined
|
||||||
|
|
@ -518,7 +517,7 @@ type
|
||||||
TSymKinds* = set[TSymKind]
|
TSymKinds* = set[TSymKind]
|
||||||
|
|
||||||
const
|
const
|
||||||
routineKinds* = {skProc, skMethod, skIterator, skClosureIterator,
|
routineKinds* = {skProc, skMethod, skIterator,
|
||||||
skConverter, skMacro, skTemplate}
|
skConverter, skMacro, skTemplate}
|
||||||
tfIncompleteStruct* = tfVarargs
|
tfIncompleteStruct* = tfVarargs
|
||||||
tfUncheckedArray* = tfVarargs
|
tfUncheckedArray* = tfVarargs
|
||||||
|
|
@ -905,7 +904,7 @@ type
|
||||||
# the poor naming choices in the standard library.
|
# the poor naming choices in the standard library.
|
||||||
|
|
||||||
const
|
const
|
||||||
OverloadableSyms* = {skProc, skMethod, skIterator, skClosureIterator,
|
OverloadableSyms* = {skProc, skMethod, skIterator,
|
||||||
skConverter, skModule, skTemplate, skMacro}
|
skConverter, skModule, skTemplate, skMacro}
|
||||||
|
|
||||||
GenericTypes*: TTypeKinds = {tyGenericInvocation, tyGenericBody,
|
GenericTypes*: TTypeKinds = {tyGenericInvocation, tyGenericBody,
|
||||||
|
|
@ -929,11 +928,11 @@ const
|
||||||
NilableTypes*: TTypeKinds = {tyPointer, tyCString, tyRef, tyPtr, tySequence,
|
NilableTypes*: TTypeKinds = {tyPointer, tyCString, tyRef, tyPtr, tySequence,
|
||||||
tyProc, tyString, tyError}
|
tyProc, tyString, tyError}
|
||||||
ExportableSymKinds* = {skVar, skConst, skProc, skMethod, skType,
|
ExportableSymKinds* = {skVar, skConst, skProc, skMethod, skType,
|
||||||
skIterator, skClosureIterator,
|
skIterator,
|
||||||
skMacro, skTemplate, skConverter, skEnumField, skLet, skStub, skAlias}
|
skMacro, skTemplate, skConverter, skEnumField, skLet, skStub, skAlias}
|
||||||
PersistentNodeFlags*: TNodeFlags = {nfBase2, nfBase8, nfBase16,
|
PersistentNodeFlags*: TNodeFlags = {nfBase2, nfBase8, nfBase16,
|
||||||
nfDotSetter, nfDotField,
|
nfDotSetter, nfDotField,
|
||||||
nfIsRef, nfIsCursor}
|
nfIsRef, nfIsCursor, nfLL}
|
||||||
namePos* = 0
|
namePos* = 0
|
||||||
patternPos* = 1 # empty except for term rewriting macros
|
patternPos* = 1 # empty except for term rewriting macros
|
||||||
genericParamsPos* = 2
|
genericParamsPos* = 2
|
||||||
|
|
@ -958,11 +957,9 @@ const
|
||||||
nkStrKinds* = {nkStrLit..nkTripleStrLit}
|
nkStrKinds* = {nkStrLit..nkTripleStrLit}
|
||||||
|
|
||||||
skLocalVars* = {skVar, skLet, skForVar, skParam, skResult}
|
skLocalVars* = {skVar, skLet, skForVar, skParam, skResult}
|
||||||
skProcKinds* = {skProc, skTemplate, skMacro, skIterator, skClosureIterator,
|
skProcKinds* = {skProc, skTemplate, skMacro, skIterator,
|
||||||
skMethod, skConverter}
|
skMethod, skConverter}
|
||||||
|
|
||||||
skIterators* = {skIterator, skClosureIterator}
|
|
||||||
|
|
||||||
var ggDebug* {.deprecated.}: bool ## convenience switch for trying out things
|
var ggDebug* {.deprecated.}: bool ## convenience switch for trying out things
|
||||||
|
|
||||||
proc isCallExpr*(n: PNode): bool =
|
proc isCallExpr*(n: PNode): bool =
|
||||||
|
|
@ -1013,6 +1010,10 @@ proc newNode*(kind: TNodeKind): PNode =
|
||||||
writeStackTrace()
|
writeStackTrace()
|
||||||
inc gNodeId
|
inc gNodeId
|
||||||
|
|
||||||
|
proc newTree*(kind: TNodeKind; children: varargs[PNode]): PNode =
|
||||||
|
result = newNode(kind)
|
||||||
|
result.sons = @children
|
||||||
|
|
||||||
proc newIntNode*(kind: TNodeKind, intVal: BiggestInt): PNode =
|
proc newIntNode*(kind: TNodeKind, intVal: BiggestInt): PNode =
|
||||||
result = newNode(kind)
|
result = newNode(kind)
|
||||||
result.intVal = intVal
|
result.intVal = intVal
|
||||||
|
|
@ -1554,12 +1555,13 @@ proc isGenericRoutine*(s: PSym): bool =
|
||||||
else: discard
|
else: discard
|
||||||
|
|
||||||
proc skipGenericOwner*(s: PSym): PSym =
|
proc skipGenericOwner*(s: PSym): PSym =
|
||||||
internalAssert s.kind in skProcKinds
|
|
||||||
## Generic instantiations are owned by their originating generic
|
## Generic instantiations are owned by their originating generic
|
||||||
## symbol. This proc skips such owners and goes straight to the owner
|
## symbol. This proc skips such owners and goes straight to the owner
|
||||||
## of the generic itself (the module or the enclosing proc).
|
## of the generic itself (the module or the enclosing proc).
|
||||||
result = if sfFromGeneric in s.flags: s.owner.owner
|
result = if s.kind in skProcKinds and sfFromGeneric in s.flags:
|
||||||
else: s.owner
|
s.owner.owner
|
||||||
|
else:
|
||||||
|
s.owner
|
||||||
|
|
||||||
proc originatingModule*(s: PSym): PSym =
|
proc originatingModule*(s: PSym): PSym =
|
||||||
result = s.owner
|
result = s.owner
|
||||||
|
|
|
||||||
|
|
@ -118,6 +118,14 @@ proc openArrayLoc(p: BProc, n: PNode): Rope =
|
||||||
result = "$1->data, $1->$2" % [a.rdLoc, lenField(p)]
|
result = "$1->data, $1->$2" % [a.rdLoc, lenField(p)]
|
||||||
of tyArray, tyArrayConstr:
|
of tyArray, tyArrayConstr:
|
||||||
result = "$1, $2" % [rdLoc(a), rope(lengthOrd(a.t))]
|
result = "$1, $2" % [rdLoc(a), rope(lengthOrd(a.t))]
|
||||||
|
of tyPtr, tyRef:
|
||||||
|
case lastSon(a.t).kind
|
||||||
|
of tyString, tySequence:
|
||||||
|
result = "(*$1)->data, (*$1)->$2" % [a.rdLoc, lenField(p)]
|
||||||
|
of tyArray, tyArrayConstr:
|
||||||
|
result = "$1, $2" % [rdLoc(a), rope(lengthOrd(lastSon(a.t)))]
|
||||||
|
else:
|
||||||
|
internalError("openArrayLoc: " & typeToString(a.t))
|
||||||
else: internalError("openArrayLoc: " & typeToString(a.t))
|
else: internalError("openArrayLoc: " & typeToString(a.t))
|
||||||
|
|
||||||
proc genArgStringToCString(p: BProc, n: PNode): Rope {.inline.} =
|
proc genArgStringToCString(p: BProc, n: PNode): Rope {.inline.} =
|
||||||
|
|
@ -515,7 +523,7 @@ proc genNamedParamCall(p: BProc, ri: PNode, d: var TLoc) =
|
||||||
line(p, cpsStmts, pl)
|
line(p, cpsStmts, pl)
|
||||||
|
|
||||||
proc genCall(p: BProc, e: PNode, d: var TLoc) =
|
proc genCall(p: BProc, e: PNode, d: var TLoc) =
|
||||||
if e.sons[0].typ.callConv == ccClosure:
|
if e.sons[0].typ.skipTypes({tyGenericInst}).callConv == ccClosure:
|
||||||
genClosureCall(p, nil, e, d)
|
genClosureCall(p, nil, e, d)
|
||||||
elif e.sons[0].kind == nkSym and sfInfixCall in e.sons[0].sym.flags:
|
elif e.sons[0].kind == nkSym and sfInfixCall in e.sons[0].sym.flags:
|
||||||
genInfixCall(p, nil, e, d)
|
genInfixCall(p, nil, e, d)
|
||||||
|
|
@ -528,7 +536,7 @@ proc genCall(p: BProc, e: PNode, d: var TLoc) =
|
||||||
if d.s == onStack and containsGarbageCollectedRef(d.t): keepAlive(p, d)
|
if d.s == onStack and containsGarbageCollectedRef(d.t): keepAlive(p, d)
|
||||||
|
|
||||||
proc genAsgnCall(p: BProc, le, ri: PNode, d: var TLoc) =
|
proc genAsgnCall(p: BProc, le, ri: PNode, d: var TLoc) =
|
||||||
if ri.sons[0].typ.callConv == ccClosure:
|
if ri.sons[0].typ.skipTypes({tyGenericInst}).callConv == ccClosure:
|
||||||
genClosureCall(p, le, ri, d)
|
genClosureCall(p, le, ri, d)
|
||||||
elif ri.sons[0].kind == nkSym and sfInfixCall in ri.sons[0].sym.flags:
|
elif ri.sons[0].kind == nkSym and sfInfixCall in ri.sons[0].sym.flags:
|
||||||
genInfixCall(p, le, ri, d)
|
genInfixCall(p, le, ri, d)
|
||||||
|
|
|
||||||
|
|
@ -959,6 +959,7 @@ proc genEcho(p: BProc, n: PNode) =
|
||||||
addf(args, ", $1? ($1)->data:\"nil\"", [rdLoc(a)])
|
addf(args, ", $1? ($1)->data:\"nil\"", [rdLoc(a)])
|
||||||
linefmt(p, cpsStmts, "printf($1$2);$n",
|
linefmt(p, cpsStmts, "printf($1$2);$n",
|
||||||
makeCString(repeat("%s", n.len) & tnl), args)
|
makeCString(repeat("%s", n.len) & tnl), args)
|
||||||
|
linefmt(p, cpsStmts, "fflush(stdout);$n")
|
||||||
|
|
||||||
proc gcUsage(n: PNode) =
|
proc gcUsage(n: PNode) =
|
||||||
if gSelectedGC == gcNone: message(n.info, warnGcMem, n.renderTree)
|
if gSelectedGC == gcNone: message(n.info, warnGcMem, n.renderTree)
|
||||||
|
|
@ -1415,11 +1416,11 @@ proc binaryExprIn(p: BProc, e: PNode, a, b, d: var TLoc, frmt: string) =
|
||||||
|
|
||||||
proc genInExprAux(p: BProc, e: PNode, a, b, d: var TLoc) =
|
proc genInExprAux(p: BProc, e: PNode, a, b, d: var TLoc) =
|
||||||
case int(getSize(skipTypes(e.sons[1].typ, abstractVar)))
|
case int(getSize(skipTypes(e.sons[1].typ, abstractVar)))
|
||||||
of 1: binaryExprIn(p, e, a, b, d, "(($1 &(1<<(($2)&7)))!=0)")
|
of 1: binaryExprIn(p, e, a, b, d, "(($1 &(1U<<((NU)($2)&7U)))!=0)")
|
||||||
of 2: binaryExprIn(p, e, a, b, d, "(($1 &(1<<(($2)&15)))!=0)")
|
of 2: binaryExprIn(p, e, a, b, d, "(($1 &(1U<<((NU)($2)&15U)))!=0)")
|
||||||
of 4: binaryExprIn(p, e, a, b, d, "(($1 &(1<<(($2)&31)))!=0)")
|
of 4: binaryExprIn(p, e, a, b, d, "(($1 &(1U<<((NU)($2)&31U)))!=0)")
|
||||||
of 8: binaryExprIn(p, e, a, b, d, "(($1 &(IL64(1)<<(($2)&IL64(63))))!=0)")
|
of 8: binaryExprIn(p, e, a, b, d, "(($1 &((NU64)1<<((NU)($2)&63U)))!=0)")
|
||||||
else: binaryExprIn(p, e, a, b, d, "(($1[$2/8] &(1<<($2%8)))!=0)")
|
else: binaryExprIn(p, e, a, b, d, "(($1[(NU)($2)>>3] &(1U<<((NU)($2)&7U)))!=0)")
|
||||||
|
|
||||||
proc binaryStmtInExcl(p: BProc, e: PNode, d: var TLoc, frmt: string) =
|
proc binaryStmtInExcl(p: BProc, e: PNode, d: var TLoc, frmt: string) =
|
||||||
var a, b: TLoc
|
var a, b: TLoc
|
||||||
|
|
@ -1500,8 +1501,8 @@ proc genSetOp(p: BProc, e: PNode, d: var TLoc, op: TMagic) =
|
||||||
else: internalError(e.info, "genSetOp()")
|
else: internalError(e.info, "genSetOp()")
|
||||||
else:
|
else:
|
||||||
case op
|
case op
|
||||||
of mIncl: binaryStmtInExcl(p, e, d, "$1[$2/8] |=(1<<($2%8));$n")
|
of mIncl: binaryStmtInExcl(p, e, d, "$1[(NU)($2)>>3] |=(1U<<($2&7U));$n")
|
||||||
of mExcl: binaryStmtInExcl(p, e, d, "$1[$2/8] &= ~(1<<($2%8));$n")
|
of mExcl: binaryStmtInExcl(p, e, d, "$1[(NU)($2)>>3] &= ~(1U<<($2&7U));$n")
|
||||||
of mCard: unaryExprChar(p, e, d, "#cardSet($1, " & $size & ')')
|
of mCard: unaryExprChar(p, e, d, "#cardSet($1, " & $size & ')')
|
||||||
of mLtSet, mLeSet:
|
of mLtSet, mLeSet:
|
||||||
getTemp(p, getSysType(tyInt), i) # our counter
|
getTemp(p, getSysType(tyInt), i) # our counter
|
||||||
|
|
@ -1733,8 +1734,6 @@ proc genMagicExpr(p: BProc, e: PNode, d: var TLoc, op: TMagic) =
|
||||||
of mEcho: genEcho(p, e[1].skipConv)
|
of mEcho: genEcho(p, e[1].skipConv)
|
||||||
of mArrToSeq: genArrToSeq(p, e, d)
|
of mArrToSeq: genArrToSeq(p, e, d)
|
||||||
of mNLen..mNError, mSlurp..mQuoteAst:
|
of mNLen..mNError, mSlurp..mQuoteAst:
|
||||||
echo "from here ", p.prc.name.s, " ", p.prc.info
|
|
||||||
writestacktrace()
|
|
||||||
localError(e.info, errXMustBeCompileTime, e.sons[0].sym.name.s)
|
localError(e.info, errXMustBeCompileTime, e.sons[0].sym.name.s)
|
||||||
of mSpawn:
|
of mSpawn:
|
||||||
let n = lowerings.wrapProcForSpawn(p.module.module, e, e.typ, nil, nil)
|
let n = lowerings.wrapProcForSpawn(p.module.module, e, e.typ, nil, nil)
|
||||||
|
|
@ -1788,11 +1787,11 @@ proc genSetConstr(p: BProc, e: PNode, d: var TLoc) =
|
||||||
initLocExpr(p, e.sons[i].sons[0], a)
|
initLocExpr(p, e.sons[i].sons[0], a)
|
||||||
initLocExpr(p, e.sons[i].sons[1], b)
|
initLocExpr(p, e.sons[i].sons[1], b)
|
||||||
lineF(p, cpsStmts, "for ($1 = $3; $1 <= $4; $1++) $n" &
|
lineF(p, cpsStmts, "for ($1 = $3; $1 <= $4; $1++) $n" &
|
||||||
"$2[$1/8] |=(1<<($1%8));$n", [rdLoc(idx), rdLoc(d),
|
"$2[(NU)($1)>>3] |=(1U<<((NU)($1)&7U));$n", [rdLoc(idx), rdLoc(d),
|
||||||
rdSetElemLoc(a, e.typ), rdSetElemLoc(b, e.typ)])
|
rdSetElemLoc(a, e.typ), rdSetElemLoc(b, e.typ)])
|
||||||
else:
|
else:
|
||||||
initLocExpr(p, e.sons[i], a)
|
initLocExpr(p, e.sons[i], a)
|
||||||
lineF(p, cpsStmts, "$1[$2/8] |=(1<<($2%8));$n",
|
lineF(p, cpsStmts, "$1[(NU)($2)>>3] |=(1U<<((NU)($2)&7U));$n",
|
||||||
[rdLoc(d), rdSetElemLoc(a, e.typ)])
|
[rdLoc(d), rdSetElemLoc(a, e.typ)])
|
||||||
else:
|
else:
|
||||||
# small set
|
# small set
|
||||||
|
|
@ -1839,15 +1838,17 @@ proc genClosure(p: BProc, n: PNode, d: var TLoc) =
|
||||||
assert n.kind == nkClosure
|
assert n.kind == nkClosure
|
||||||
|
|
||||||
if isConstClosure(n):
|
if isConstClosure(n):
|
||||||
inc(p.labels)
|
inc(p.module.labels)
|
||||||
var tmp = "LOC" & rope(p.labels)
|
var tmp = "CNSTCLOSURE" & rope(p.module.labels)
|
||||||
addf(p.module.s[cfsData], "NIM_CONST $1 $2 = $3;$n",
|
addf(p.module.s[cfsData], "static NIM_CONST $1 $2 = $3;$n",
|
||||||
[getTypeDesc(p.module, n.typ), tmp, genConstExpr(p, n)])
|
[getTypeDesc(p.module, n.typ), tmp, genConstExpr(p, n)])
|
||||||
putIntoDest(p, d, n.typ, tmp, OnStatic)
|
putIntoDest(p, d, n.typ, tmp, OnStatic)
|
||||||
else:
|
else:
|
||||||
var tmp, a, b: TLoc
|
var tmp, a, b: TLoc
|
||||||
initLocExpr(p, n.sons[0], a)
|
initLocExpr(p, n.sons[0], a)
|
||||||
initLocExpr(p, n.sons[1], b)
|
initLocExpr(p, n.sons[1], b)
|
||||||
|
if n.sons[0].skipConv.kind == nkClosure:
|
||||||
|
internalError(n.info, "closure to closure created")
|
||||||
getTemp(p, n.typ, tmp)
|
getTemp(p, n.typ, tmp)
|
||||||
linefmt(p, cpsStmts, "$1.ClPrc = $2; $1.ClEnv = $3;$n",
|
linefmt(p, cpsStmts, "$1.ClPrc = $2; $1.ClEnv = $3;$n",
|
||||||
tmp.rdLoc, a.rdLoc, b.rdLoc)
|
tmp.rdLoc, a.rdLoc, b.rdLoc)
|
||||||
|
|
@ -1966,7 +1967,9 @@ proc expr(p: BProc, n: PNode, d: var TLoc) =
|
||||||
else:
|
else:
|
||||||
genProc(p.module, sym)
|
genProc(p.module, sym)
|
||||||
putLocIntoDest(p, d, sym.loc)
|
putLocIntoDest(p, d, sym.loc)
|
||||||
of skProc, skConverter, skIterators:
|
of skProc, skConverter, skIterator:
|
||||||
|
#if sym.kind == skIterator:
|
||||||
|
# echo renderTree(sym.getBody, {renderIds})
|
||||||
if sfCompileTime in sym.flags:
|
if sfCompileTime in sym.flags:
|
||||||
localError(n.info, "request to generate code for .compileTime proc: " &
|
localError(n.info, "request to generate code for .compileTime proc: " &
|
||||||
sym.name.s)
|
sym.name.s)
|
||||||
|
|
@ -1988,6 +1991,7 @@ proc expr(p: BProc, n: PNode, d: var TLoc) =
|
||||||
if sfGlobal in sym.flags: genVarPrototype(p.module, sym)
|
if sfGlobal in sym.flags: genVarPrototype(p.module, sym)
|
||||||
if sym.loc.r == nil or sym.loc.t == nil:
|
if sym.loc.r == nil or sym.loc.t == nil:
|
||||||
#echo "FAILED FOR PRCO ", p.prc.name.s
|
#echo "FAILED FOR PRCO ", p.prc.name.s
|
||||||
|
#echo renderTree(p.prc.ast, {renderIds})
|
||||||
internalError n.info, "expr: var not init " & sym.name.s & "_" & $sym.id
|
internalError n.info, "expr: var not init " & sym.name.s & "_" & $sym.id
|
||||||
if sfThread in sym.flags:
|
if sfThread in sym.flags:
|
||||||
accessThreadLocalVar(p, sym)
|
accessThreadLocalVar(p, sym)
|
||||||
|
|
@ -2005,9 +2009,9 @@ proc expr(p: BProc, n: PNode, d: var TLoc) =
|
||||||
putLocIntoDest(p, d, sym.loc)
|
putLocIntoDest(p, d, sym.loc)
|
||||||
of skParam:
|
of skParam:
|
||||||
if sym.loc.r == nil or sym.loc.t == nil:
|
if sym.loc.r == nil or sym.loc.t == nil:
|
||||||
#echo "FAILED FOR PRCO ", p.prc.name.s
|
# echo "FAILED FOR PRCO ", p.prc.name.s
|
||||||
#debug p.prc.typ.n
|
# debug p.prc.typ.n
|
||||||
#echo renderTree(p.prc.ast, {renderIds})
|
# echo renderTree(p.prc.ast, {renderIds})
|
||||||
internalError(n.info, "expr: param not init " & sym.name.s & "_" & $sym.id)
|
internalError(n.info, "expr: param not init " & sym.name.s & "_" & $sym.id)
|
||||||
putLocIntoDest(p, d, sym.loc)
|
putLocIntoDest(p, d, sym.loc)
|
||||||
else: internalError(n.info, "expr(" & $sym.kind & "); unknown symbol")
|
else: internalError(n.info, "expr(" & $sym.kind & "); unknown symbol")
|
||||||
|
|
|
||||||
|
|
@ -16,13 +16,13 @@ const
|
||||||
# above X strings a hash-switch for strings is generated
|
# above X strings a hash-switch for strings is generated
|
||||||
|
|
||||||
proc registerGcRoot(p: BProc, v: PSym) =
|
proc registerGcRoot(p: BProc, v: PSym) =
|
||||||
if gSelectedGC in {gcMarkAndSweep, gcGenerational} and
|
if gSelectedGC in {gcMarkAndSweep, gcGenerational, gcV2} and
|
||||||
containsGarbageCollectedRef(v.loc.t):
|
containsGarbageCollectedRef(v.loc.t):
|
||||||
# we register a specialized marked proc here; this has the advantage
|
# we register a specialized marked proc here; this has the advantage
|
||||||
# that it works out of the box for thread local storage then :-)
|
# that it works out of the box for thread local storage then :-)
|
||||||
let prc = genTraverseProcForGlobal(p.module, v)
|
let prc = genTraverseProcForGlobal(p.module, v)
|
||||||
linefmt(p.module.initProc, cpsStmts,
|
appcg(p.module, p.module.initProc.procSec(cpsStmts),
|
||||||
"#nimRegisterGlobalMarker($1);$n", prc)
|
"#nimRegisterGlobalMarker($1);$n", [prc])
|
||||||
|
|
||||||
proc isAssignedImmediately(n: PNode): bool {.inline.} =
|
proc isAssignedImmediately(n: PNode): bool {.inline.} =
|
||||||
if n.kind == nkEmpty: return false
|
if n.kind == nkEmpty: return false
|
||||||
|
|
@ -955,7 +955,7 @@ proc genAsmOrEmitStmt(p: BProc, t: PNode, isAsmStmt=false): Rope =
|
||||||
res.add(t.sons[i].strVal)
|
res.add(t.sons[i].strVal)
|
||||||
of nkSym:
|
of nkSym:
|
||||||
var sym = t.sons[i].sym
|
var sym = t.sons[i].sym
|
||||||
if sym.kind in {skProc, skIterator, skClosureIterator, skMethod}:
|
if sym.kind in {skProc, skIterator, skMethod}:
|
||||||
var a: TLoc
|
var a: TLoc
|
||||||
initLocExpr(p, t.sons[i], a)
|
initLocExpr(p, t.sons[i], a)
|
||||||
res.add($rdLoc(a))
|
res.add($rdLoc(a))
|
||||||
|
|
|
||||||
|
|
@ -594,7 +594,7 @@ proc cgsym(m: BModule, name: string): Rope =
|
||||||
var sym = magicsys.getCompilerProc(name)
|
var sym = magicsys.getCompilerProc(name)
|
||||||
if sym != nil:
|
if sym != nil:
|
||||||
case sym.kind
|
case sym.kind
|
||||||
of skProc, skMethod, skConverter, skIterators: genProc(m, sym)
|
of skProc, skMethod, skConverter, skIterator: genProc(m, sym)
|
||||||
of skVar, skResult, skLet: genVarPrototype(m, sym)
|
of skVar, skResult, skLet: genVarPrototype(m, sym)
|
||||||
of skType: discard getTypeDesc(m, sym.typ)
|
of skType: discard getTypeDesc(m, sym.typ)
|
||||||
else: internalError("cgsym: " & name & ": " & $sym.kind)
|
else: internalError("cgsym: " & name & ": " & $sym.kind)
|
||||||
|
|
|
||||||
|
|
@ -18,8 +18,10 @@ proc genConv(n: PNode, d: PType, downcast: bool): PNode =
|
||||||
var source = skipTypes(n.typ, abstractPtrs)
|
var source = skipTypes(n.typ, abstractPtrs)
|
||||||
if (source.kind == tyObject) and (dest.kind == tyObject):
|
if (source.kind == tyObject) and (dest.kind == tyObject):
|
||||||
var diff = inheritanceDiff(dest, source)
|
var diff = inheritanceDiff(dest, source)
|
||||||
if diff == high(int): internalError(n.info, "cgmeth.genConv")
|
if diff == high(int):
|
||||||
if diff < 0:
|
# no subtype relation, nothing to do
|
||||||
|
result = n
|
||||||
|
elif diff < 0:
|
||||||
result = newNodeIT(nkObjUpConv, n.info, d)
|
result = newNodeIT(nkObjUpConv, n.info, d)
|
||||||
addSon(result, n)
|
addSon(result, n)
|
||||||
if downcast: internalError(n.info, "cgmeth.genConv: no upcast allowed")
|
if downcast: internalError(n.info, "cgmeth.genConv: no upcast allowed")
|
||||||
|
|
|
||||||
|
|
@ -149,10 +149,10 @@ proc ropeFormatNamedVars(frmt: FormatStr, varnames: openArray[string],
|
||||||
proc genComment(d: PDoc, n: PNode): string =
|
proc genComment(d: PDoc, n: PNode): string =
|
||||||
result = ""
|
result = ""
|
||||||
var dummyHasToc: bool
|
var dummyHasToc: bool
|
||||||
if n.comment != nil and startsWith(n.comment, "##"):
|
if n.comment != nil:
|
||||||
renderRstToOut(d[], parseRst(n.comment, toFilename(n.info),
|
renderRstToOut(d[], parseRst(n.comment, toFilename(n.info),
|
||||||
toLinenumber(n.info), toColumn(n.info),
|
toLinenumber(n.info), toColumn(n.info),
|
||||||
dummyHasToc, d.options + {roSkipPounds}), result)
|
dummyHasToc, d.options), result)
|
||||||
|
|
||||||
proc genRecComment(d: PDoc, n: PNode): Rope =
|
proc genRecComment(d: PDoc, n: PNode): Rope =
|
||||||
if n == nil: return nil
|
if n == nil: return nil
|
||||||
|
|
@ -537,7 +537,7 @@ proc generateJson(d: PDoc, n: PNode, jArray: JsonNode = nil): JsonNode =
|
||||||
proc genSection(d: PDoc, kind: TSymKind) =
|
proc genSection(d: PDoc, kind: TSymKind) =
|
||||||
const sectionNames: array[skModule..skTemplate, string] = [
|
const sectionNames: array[skModule..skTemplate, string] = [
|
||||||
"Imports", "Types", "Vars", "Lets", "Consts", "Vars", "Procs", "Methods",
|
"Imports", "Types", "Vars", "Lets", "Consts", "Vars", "Procs", "Methods",
|
||||||
"Iterators", "Iterators", "Converters", "Macros", "Templates"
|
"Iterators", "Converters", "Macros", "Templates"
|
||||||
]
|
]
|
||||||
if d.section[kind] == nil: return
|
if d.section[kind] == nil: return
|
||||||
var title = sectionNames[kind].rope
|
var title = sectionNames[kind].rope
|
||||||
|
|
|
||||||
|
|
@ -38,7 +38,8 @@ proc evalTemplateAux(templ, actual: PNode, c: var TemplCtx, result: PNode) =
|
||||||
if s.owner.id == c.owner.id:
|
if s.owner.id == c.owner.id:
|
||||||
if s.kind == skParam and sfGenSym notin s.flags:
|
if s.kind == skParam and sfGenSym notin s.flags:
|
||||||
handleParam actual.sons[s.position]
|
handleParam actual.sons[s.position]
|
||||||
elif s.kind == skGenericParam:
|
elif s.kind == skGenericParam or
|
||||||
|
s.kind == skType and s.typ != nil and s.typ.kind == tyGenericParam:
|
||||||
handleParam actual.sons[s.owner.typ.len + s.position - 1]
|
handleParam actual.sons[s.owner.typ.len + s.position - 1]
|
||||||
else:
|
else:
|
||||||
internalAssert sfGenSym in s.flags
|
internalAssert sfGenSym in s.flags
|
||||||
|
|
|
||||||
|
|
@ -62,7 +62,7 @@ Files: "icons/koch_icon.o"
|
||||||
|
|
||||||
Files: "compiler/readme.txt"
|
Files: "compiler/readme.txt"
|
||||||
Files: "compiler/installer.ini"
|
Files: "compiler/installer.ini"
|
||||||
Files: "compiler/nim.nim.cfg"
|
Files: "compiler/*.cfg"
|
||||||
Files: "compiler/*.nim"
|
Files: "compiler/*.nim"
|
||||||
Files: "doc/*.txt"
|
Files: "doc/*.txt"
|
||||||
Files: "doc/manual/*.txt"
|
Files: "doc/manual/*.txt"
|
||||||
|
|
@ -84,6 +84,8 @@ Files: "tools/niminst/*.nsh"
|
||||||
Files: "web/website.ini"
|
Files: "web/website.ini"
|
||||||
Files: "web/*.nim"
|
Files: "web/*.nim"
|
||||||
Files: "web/*.txt"
|
Files: "web/*.txt"
|
||||||
|
Files: "bin/nimblepkg/*.nim"
|
||||||
|
Files: "bin/nimblepkg/*.cfg"
|
||||||
|
|
||||||
[Lib]
|
[Lib]
|
||||||
Files: "lib/nimbase.h"
|
Files: "lib/nimbase.h"
|
||||||
|
|
@ -107,9 +109,6 @@ Files: "lib/wrappers/readline/*.nim"
|
||||||
Files: "lib/wrappers/linenoise/*.nim"
|
Files: "lib/wrappers/linenoise/*.nim"
|
||||||
Files: "lib/wrappers/linenoise/*.c"
|
Files: "lib/wrappers/linenoise/*.c"
|
||||||
Files: "lib/wrappers/linenoise/*.h"
|
Files: "lib/wrappers/linenoise/*.h"
|
||||||
Files: "lib/wrappers/sdl/*.nim"
|
|
||||||
Files: "lib/wrappers/zip/*.nim"
|
|
||||||
Files: "lib/wrappers/zip/libzip_all.c"
|
|
||||||
|
|
||||||
Files: "lib/windows/*.nim"
|
Files: "lib/windows/*.nim"
|
||||||
Files: "lib/posix/*.nim"
|
Files: "lib/posix/*.nim"
|
||||||
|
|
|
||||||
|
|
@ -931,7 +931,7 @@ proc isIndirect(v: PSym): bool =
|
||||||
result = {sfAddrTaken, sfGlobal} * v.flags != {} and
|
result = {sfAddrTaken, sfGlobal} * v.flags != {} and
|
||||||
#(mapType(v.typ) != etyObject) and
|
#(mapType(v.typ) != etyObject) and
|
||||||
{sfImportc, sfVolatile, sfExportc} * v.flags == {} and
|
{sfImportc, sfVolatile, sfExportc} * v.flags == {} and
|
||||||
v.kind notin {skProc, skConverter, skMethod, skIterator, skClosureIterator,
|
v.kind notin {skProc, skConverter, skMethod, skIterator,
|
||||||
skConst, skTemp, skLet}
|
skConst, skTemp, skLet}
|
||||||
|
|
||||||
proc genAddr(p: PProc, n: PNode, r: var TCompRes) =
|
proc genAddr(p: PProc, n: PNode, r: var TCompRes) =
|
||||||
|
|
@ -1636,7 +1636,10 @@ proc gen(p: PProc, n: PNode, r: var TCompRes) =
|
||||||
of nkSym:
|
of nkSym:
|
||||||
genSym(p, n, r)
|
genSym(p, n, r)
|
||||||
of nkCharLit..nkInt64Lit:
|
of nkCharLit..nkInt64Lit:
|
||||||
r.res = rope(n.intVal)
|
if n.typ.kind == tyBool:
|
||||||
|
r.res = if n.intVal == 0: rope"false" else: rope"true"
|
||||||
|
else:
|
||||||
|
r.res = rope(n.intVal)
|
||||||
r.kind = resExpr
|
r.kind = resExpr
|
||||||
of nkNilLit:
|
of nkNilLit:
|
||||||
if isEmptyType(n.typ):
|
if isEmptyType(n.typ):
|
||||||
|
|
|
||||||
|
|
@ -116,7 +116,7 @@ proc genEnumInfo(p: PProc, typ: PType, name: Rope) =
|
||||||
[name, genTypeInfo(p, typ.sons[0])])
|
[name, genTypeInfo(p, typ.sons[0])])
|
||||||
|
|
||||||
proc genTypeInfo(p: PProc, typ: PType): Rope =
|
proc genTypeInfo(p: PProc, typ: PType): Rope =
|
||||||
let t = typ.skipTypes({tyGenericInst})
|
let t = typ.skipTypes({tyGenericInst, tyDistinct})
|
||||||
result = "NTI$1" % [rope(t.id)]
|
result = "NTI$1" % [rope(t.id)]
|
||||||
if containsOrIncl(p.g.typeInfoGenerated, t.id): return
|
if containsOrIncl(p.g.typeInfoGenerated, t.id): return
|
||||||
case t.kind
|
case t.kind
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load diff
|
|
@ -662,6 +662,7 @@ proc getString(L: var TLexer, tok: var TToken, rawMode: bool) =
|
||||||
L.lineNumber = line
|
L.lineNumber = line
|
||||||
lexMessagePos(L, errClosingTripleQuoteExpected, L.lineStart)
|
lexMessagePos(L, errClosingTripleQuoteExpected, L.lineStart)
|
||||||
L.lineNumber = line2
|
L.lineNumber = line2
|
||||||
|
L.bufpos = pos
|
||||||
break
|
break
|
||||||
else:
|
else:
|
||||||
add(tok.literal, buf[pos])
|
add(tok.literal, buf[pos])
|
||||||
|
|
@ -768,24 +769,88 @@ proc getOperator(L: var TLexer, tok: var TToken) =
|
||||||
if buf[pos] in {CR, LF, nimlexbase.EndOfFile}:
|
if buf[pos] in {CR, LF, nimlexbase.EndOfFile}:
|
||||||
tok.strongSpaceB = -1
|
tok.strongSpaceB = -1
|
||||||
|
|
||||||
|
proc skipMultiLineComment(L: var TLexer; tok: var TToken; start: int;
|
||||||
|
isDoc: bool) =
|
||||||
|
var pos = start
|
||||||
|
var buf = L.buf
|
||||||
|
var toStrip = 0
|
||||||
|
# detect the amount of indentation:
|
||||||
|
if isDoc:
|
||||||
|
toStrip = getColNumber(L, pos)
|
||||||
|
while buf[pos] == ' ': inc pos
|
||||||
|
if buf[pos] in {CR, LF}:
|
||||||
|
pos = handleCRLF(L, pos)
|
||||||
|
buf = L.buf
|
||||||
|
toStrip = 0
|
||||||
|
while buf[pos] == ' ':
|
||||||
|
inc pos
|
||||||
|
inc toStrip
|
||||||
|
var nesting = 0
|
||||||
|
while true:
|
||||||
|
case buf[pos]
|
||||||
|
of '#':
|
||||||
|
if isDoc:
|
||||||
|
if buf[pos+1] == '#' and buf[pos+2] == '[':
|
||||||
|
inc nesting
|
||||||
|
tok.literal.add '#'
|
||||||
|
elif buf[pos+1] == '[':
|
||||||
|
inc nesting
|
||||||
|
inc pos
|
||||||
|
of ']':
|
||||||
|
if isDoc:
|
||||||
|
if buf[pos+1] == '#' and buf[pos+2] == '#':
|
||||||
|
if nesting == 0:
|
||||||
|
inc(pos, 3)
|
||||||
|
break
|
||||||
|
dec nesting
|
||||||
|
tok.literal.add ']'
|
||||||
|
elif buf[pos+1] == '#':
|
||||||
|
if nesting == 0:
|
||||||
|
inc(pos, 2)
|
||||||
|
break
|
||||||
|
dec nesting
|
||||||
|
inc pos
|
||||||
|
of '\t':
|
||||||
|
lexMessagePos(L, errTabulatorsAreNotAllowed, pos)
|
||||||
|
inc(pos)
|
||||||
|
if isDoc: tok.literal.add '\t'
|
||||||
|
of CR, LF:
|
||||||
|
pos = handleCRLF(L, pos)
|
||||||
|
buf = L.buf
|
||||||
|
# strip leading whitespace:
|
||||||
|
if isDoc:
|
||||||
|
tok.literal.add "\n"
|
||||||
|
inc tok.iNumber
|
||||||
|
var c = toStrip
|
||||||
|
while buf[pos] == ' ' and c > 0:
|
||||||
|
inc pos
|
||||||
|
dec c
|
||||||
|
of nimlexbase.EndOfFile:
|
||||||
|
lexMessagePos(L, errGenerated, pos, "end of multiline comment expected")
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
if isDoc: tok.literal.add buf[pos]
|
||||||
|
inc(pos)
|
||||||
|
L.bufpos = pos
|
||||||
|
|
||||||
proc scanComment(L: var TLexer, tok: var TToken) =
|
proc scanComment(L: var TLexer, tok: var TToken) =
|
||||||
var pos = L.bufpos
|
var pos = L.bufpos
|
||||||
var buf = L.buf
|
var buf = L.buf
|
||||||
when not defined(nimfix):
|
|
||||||
assert buf[pos+1] == '#'
|
|
||||||
if buf[pos+2] == '[':
|
|
||||||
if buf[pos+3] == ']':
|
|
||||||
# ##[] is the (rather complex) "cursor token" for idetools
|
|
||||||
tok.tokType = tkComment
|
|
||||||
tok.literal = "[]"
|
|
||||||
inc(L.bufpos, 4)
|
|
||||||
return
|
|
||||||
else:
|
|
||||||
lexMessagePos(L, warnDeprecated, pos, "use '## [' instead; '##['")
|
|
||||||
|
|
||||||
tok.tokType = tkComment
|
tok.tokType = tkComment
|
||||||
# iNumber contains the number of '\n' in the token
|
# iNumber contains the number of '\n' in the token
|
||||||
tok.iNumber = 0
|
tok.iNumber = 0
|
||||||
|
when not defined(nimfix):
|
||||||
|
assert buf[pos+1] == '#'
|
||||||
|
if buf[pos+2] == '[':
|
||||||
|
skipMultiLineComment(L, tok, pos+3, true)
|
||||||
|
return
|
||||||
|
inc(pos, 2)
|
||||||
|
|
||||||
|
var toStrip = 0
|
||||||
|
while buf[pos] == ' ':
|
||||||
|
inc pos
|
||||||
|
inc toStrip
|
||||||
|
|
||||||
when defined(nimfix):
|
when defined(nimfix):
|
||||||
var col = getColNumber(L, pos)
|
var col = getColNumber(L, pos)
|
||||||
while true:
|
while true:
|
||||||
|
|
@ -819,6 +884,12 @@ proc scanComment(L: var TLexer, tok: var TToken) =
|
||||||
if doContinue():
|
if doContinue():
|
||||||
tok.literal.add "\n"
|
tok.literal.add "\n"
|
||||||
when defined(nimfix): col = indent
|
when defined(nimfix): col = indent
|
||||||
|
else:
|
||||||
|
inc(pos, 2)
|
||||||
|
var c = toStrip
|
||||||
|
while buf[pos] == ' ' and c > 0:
|
||||||
|
inc pos
|
||||||
|
dec c
|
||||||
inc tok.iNumber
|
inc tok.iNumber
|
||||||
else:
|
else:
|
||||||
if buf[pos] > ' ':
|
if buf[pos] > ' ':
|
||||||
|
|
@ -842,9 +913,16 @@ proc skip(L: var TLexer, tok: var TToken) =
|
||||||
pos = handleCRLF(L, pos)
|
pos = handleCRLF(L, pos)
|
||||||
buf = L.buf
|
buf = L.buf
|
||||||
var indent = 0
|
var indent = 0
|
||||||
while buf[pos] == ' ':
|
while true:
|
||||||
inc(pos)
|
if buf[pos] == ' ':
|
||||||
inc(indent)
|
inc(pos)
|
||||||
|
inc(indent)
|
||||||
|
elif buf[pos] == '#' and buf[pos+1] == '[':
|
||||||
|
skipMultiLineComment(L, tok, pos+2, false)
|
||||||
|
pos = L.bufpos
|
||||||
|
buf = L.buf
|
||||||
|
else:
|
||||||
|
break
|
||||||
tok.strongSpaceA = 0
|
tok.strongSpaceA = 0
|
||||||
when defined(nimfix):
|
when defined(nimfix):
|
||||||
template doBreak(): expr = buf[pos] > ' '
|
template doBreak(): expr = buf[pos] > ' '
|
||||||
|
|
@ -862,8 +940,11 @@ proc skip(L: var TLexer, tok: var TToken) =
|
||||||
# do not skip documentation comment:
|
# do not skip documentation comment:
|
||||||
if buf[pos+1] == '#': break
|
if buf[pos+1] == '#': break
|
||||||
if buf[pos+1] == '[':
|
if buf[pos+1] == '[':
|
||||||
lexMessagePos(L, warnDeprecated, pos, "use '# [' instead; '#['")
|
skipMultiLineComment(L, tok, pos+2, false)
|
||||||
while buf[pos] notin {CR, LF, nimlexbase.EndOfFile}: inc(pos)
|
pos = L.bufpos
|
||||||
|
buf = L.buf
|
||||||
|
else:
|
||||||
|
while buf[pos] notin {CR, LF, nimlexbase.EndOfFile}: inc(pos)
|
||||||
else:
|
else:
|
||||||
break # EndOfFile also leaves the loop
|
break # EndOfFile also leaves the loop
|
||||||
L.bufpos = pos
|
L.bufpos = pos
|
||||||
|
|
|
||||||
|
|
@ -133,7 +133,7 @@ type
|
||||||
|
|
||||||
proc getSymRepr*(s: PSym): string =
|
proc getSymRepr*(s: PSym): string =
|
||||||
case s.kind
|
case s.kind
|
||||||
of skProc, skMethod, skConverter, skIterators: result = getProcHeader(s)
|
of skProc, skMethod, skConverter, skIterator: result = getProcHeader(s)
|
||||||
else: result = s.name.s
|
else: result = s.name.s
|
||||||
|
|
||||||
proc ensureNoMissingOrUnusedSymbols(scope: PScope) =
|
proc ensureNoMissingOrUnusedSymbols(scope: PScope) =
|
||||||
|
|
|
||||||
|
|
@ -165,9 +165,10 @@ proc indirectAccess*(a: PNode, b: string, info: TLineInfo): PNode =
|
||||||
deref.typ = a.typ.skipTypes(abstractInst).sons[0]
|
deref.typ = a.typ.skipTypes(abstractInst).sons[0]
|
||||||
var t = deref.typ.skipTypes(abstractInst)
|
var t = deref.typ.skipTypes(abstractInst)
|
||||||
var field: PSym
|
var field: PSym
|
||||||
|
let bb = getIdent(b)
|
||||||
while true:
|
while true:
|
||||||
assert t.kind == tyObject
|
assert t.kind == tyObject
|
||||||
field = getSymFromList(t.n, getIdent(b))
|
field = getSymFromList(t.n, bb)
|
||||||
if field != nil: break
|
if field != nil: break
|
||||||
t = t.sons[0]
|
t = t.sons[0]
|
||||||
if t == nil: break
|
if t == nil: break
|
||||||
|
|
@ -585,7 +586,7 @@ proc wrapProcForSpawn*(owner: PSym; spawnExpr: PNode; retType: PType;
|
||||||
objType.addField(field)
|
objType.addField(field)
|
||||||
result.add newFastAsgnStmt(newDotExpr(scratchObj, field), n[0])
|
result.add newFastAsgnStmt(newDotExpr(scratchObj, field), n[0])
|
||||||
fn = indirectAccess(castExpr, field, n.info)
|
fn = indirectAccess(castExpr, field, n.info)
|
||||||
elif fn.kind == nkSym and fn.sym.kind in {skClosureIterator, skIterator}:
|
elif fn.kind == nkSym and fn.sym.kind == skIterator:
|
||||||
localError(n.info, "iterator in spawn environment is not allowed")
|
localError(n.info, "iterator in spawn environment is not allowed")
|
||||||
elif fn.typ.callConv == ccClosure:
|
elif fn.typ.callConv == ccClosure:
|
||||||
localError(n.info, "closure in spawn environment is not allowed")
|
localError(n.info, "closure in spawn environment is not allowed")
|
||||||
|
|
|
||||||
|
|
@ -106,13 +106,17 @@ proc toTreeSet(s: TBitSet, settype: PType, info: TLineInfo): PNode =
|
||||||
inc(b)
|
inc(b)
|
||||||
if (b >= len(s) * ElemSize) or not bitSetIn(s, b): break
|
if (b >= len(s) * ElemSize) or not bitSetIn(s, b): break
|
||||||
dec(b)
|
dec(b)
|
||||||
|
let aa = newIntTypeNode(nkIntLit, a + first, elemType)
|
||||||
|
aa.info = info
|
||||||
if a == b:
|
if a == b:
|
||||||
addSon(result, newIntTypeNode(nkIntLit, a + first, elemType))
|
addSon(result, aa)
|
||||||
else:
|
else:
|
||||||
n = newNodeI(nkRange, info)
|
n = newNodeI(nkRange, info)
|
||||||
n.typ = elemType
|
n.typ = elemType
|
||||||
addSon(n, newIntTypeNode(nkIntLit, a + first, elemType))
|
addSon(n, aa)
|
||||||
addSon(n, newIntTypeNode(nkIntLit, b + first, elemType))
|
let bb = newIntTypeNode(nkIntLit, b + first, elemType)
|
||||||
|
bb.info = info
|
||||||
|
addSon(n, bb)
|
||||||
addSon(result, n)
|
addSon(result, n)
|
||||||
e = b
|
e = b
|
||||||
inc(e)
|
inc(e)
|
||||||
|
|
|
||||||
|
|
@ -112,12 +112,7 @@ proc rawSkipComment(p: var TParser, node: PNode) =
|
||||||
if p.tok.tokType == tkComment:
|
if p.tok.tokType == tkComment:
|
||||||
if node != nil:
|
if node != nil:
|
||||||
if node.comment == nil: node.comment = ""
|
if node.comment == nil: node.comment = ""
|
||||||
if p.tok.literal == "[]":
|
add(node.comment, p.tok.literal)
|
||||||
node.flags.incl nfIsCursor
|
|
||||||
#echo "parser: "
|
|
||||||
#debug node
|
|
||||||
else:
|
|
||||||
add(node.comment, p.tok.literal)
|
|
||||||
else:
|
else:
|
||||||
parMessage(p, errInternal, "skipComment")
|
parMessage(p, errInternal, "skipComment")
|
||||||
getTok(p)
|
getTok(p)
|
||||||
|
|
@ -250,12 +245,14 @@ proc isUnary(p: TParser): bool =
|
||||||
if p.tok.tokType in {tkOpr, tkDotDot} and
|
if p.tok.tokType in {tkOpr, tkDotDot} and
|
||||||
p.tok.strongSpaceB == 0 and
|
p.tok.strongSpaceB == 0 and
|
||||||
p.tok.strongSpaceA > 0:
|
p.tok.strongSpaceA > 0:
|
||||||
# XXX change this after 0.10.4 is out
|
|
||||||
if p.strongSpaces:
|
|
||||||
result = true
|
result = true
|
||||||
else:
|
# versions prior to 0.13.0 used to do this:
|
||||||
parMessage(p, warnDeprecated,
|
when false:
|
||||||
"will be parsed as unary operator; inconsistent spacing")
|
if p.strongSpaces:
|
||||||
|
result = true
|
||||||
|
else:
|
||||||
|
parMessage(p, warnDeprecated,
|
||||||
|
"will be parsed as unary operator; inconsistent spacing")
|
||||||
|
|
||||||
proc checkBinary(p: TParser) {.inline.} =
|
proc checkBinary(p: TParser) {.inline.} =
|
||||||
## Check if the current parser token is a binary operator.
|
## Check if the current parser token is a binary operator.
|
||||||
|
|
@ -991,7 +988,7 @@ proc isExprStart(p: TParser): bool =
|
||||||
of tkSymbol, tkAccent, tkOpr, tkNot, tkNil, tkCast, tkIf,
|
of tkSymbol, tkAccent, tkOpr, tkNot, tkNil, tkCast, tkIf,
|
||||||
tkProc, tkIterator, tkBind, tkAddr,
|
tkProc, tkIterator, tkBind, tkAddr,
|
||||||
tkParLe, tkBracketLe, tkCurlyLe, tkIntLit..tkCharLit, tkVar, tkRef, tkPtr,
|
tkParLe, tkBracketLe, tkCurlyLe, tkIntLit..tkCharLit, tkVar, tkRef, tkPtr,
|
||||||
tkTuple, tkObject, tkType, tkWhen, tkCase:
|
tkTuple, tkObject, tkType, tkWhen, tkCase, tkOut:
|
||||||
result = true
|
result = true
|
||||||
else: result = false
|
else: result = false
|
||||||
|
|
||||||
|
|
@ -1038,7 +1035,7 @@ proc parseObject(p: var TParser): PNode
|
||||||
proc parseTypeClass(p: var TParser): PNode
|
proc parseTypeClass(p: var TParser): PNode
|
||||||
|
|
||||||
proc primary(p: var TParser, mode: TPrimaryMode): PNode =
|
proc primary(p: var TParser, mode: TPrimaryMode): PNode =
|
||||||
#| typeKeyw = 'var' | 'ref' | 'ptr' | 'shared' | 'tuple'
|
#| typeKeyw = 'var' | 'out' | 'ref' | 'ptr' | 'shared' | 'tuple'
|
||||||
#| | 'proc' | 'iterator' | 'distinct' | 'object' | 'enum'
|
#| | 'proc' | 'iterator' | 'distinct' | 'object' | 'enum'
|
||||||
#| primary = typeKeyw typeDescK
|
#| primary = typeKeyw typeDescK
|
||||||
#| / prefixOperator* identOrLiteral primarySuffix*
|
#| / prefixOperator* identOrLiteral primarySuffix*
|
||||||
|
|
@ -1112,6 +1109,7 @@ proc primary(p: var TParser, mode: TPrimaryMode): PNode =
|
||||||
optInd(p, result)
|
optInd(p, result)
|
||||||
addSon(result, primary(p, pmNormal))
|
addSon(result, primary(p, pmNormal))
|
||||||
of tkVar: result = parseTypeDescKAux(p, nkVarTy, mode)
|
of tkVar: result = parseTypeDescKAux(p, nkVarTy, mode)
|
||||||
|
of tkOut: result = parseTypeDescKAux(p, nkVarTy, mode)
|
||||||
of tkRef: result = parseTypeDescKAux(p, nkRefTy, mode)
|
of tkRef: result = parseTypeDescKAux(p, nkRefTy, mode)
|
||||||
of tkPtr: result = parseTypeDescKAux(p, nkPtrTy, mode)
|
of tkPtr: result = parseTypeDescKAux(p, nkPtrTy, mode)
|
||||||
of tkDistinct: result = parseTypeDescKAux(p, nkDistinctTy, mode)
|
of tkDistinct: result = parseTypeDescKAux(p, nkDistinctTy, mode)
|
||||||
|
|
@ -1763,7 +1761,7 @@ proc parseObject(p: var TParser): PNode =
|
||||||
addSon(result, parseObjectPart(p))
|
addSon(result, parseObjectPart(p))
|
||||||
|
|
||||||
proc parseTypeClassParam(p: var TParser): PNode =
|
proc parseTypeClassParam(p: var TParser): PNode =
|
||||||
if p.tok.tokType == tkVar:
|
if p.tok.tokType in {tkOut, tkVar}:
|
||||||
result = newNodeP(nkVarTy, p)
|
result = newNodeP(nkVarTy, p)
|
||||||
getTok(p)
|
getTok(p)
|
||||||
result.addSon(p.parseSymbol)
|
result.addSon(p.parseSymbol)
|
||||||
|
|
@ -1771,7 +1769,7 @@ proc parseTypeClassParam(p: var TParser): PNode =
|
||||||
result = p.parseSymbol
|
result = p.parseSymbol
|
||||||
|
|
||||||
proc parseTypeClass(p: var TParser): PNode =
|
proc parseTypeClass(p: var TParser): PNode =
|
||||||
#| typeClassParam = ('var')? symbol
|
#| typeClassParam = ('var' | 'out')? symbol
|
||||||
#| typeClass = typeClassParam ^* ',' (pragma)? ('of' typeDesc ^* ',')?
|
#| typeClass = typeClassParam ^* ',' (pragma)? ('of' typeDesc ^* ',')?
|
||||||
#| &IND{>} stmt
|
#| &IND{>} stmt
|
||||||
result = newNodeP(nkTypeClassTy, p)
|
result = newNodeP(nkTypeClassTy, p)
|
||||||
|
|
|
||||||
|
|
@ -10,4 +10,4 @@
|
||||||
## Include file that imports all plugins that are active.
|
## Include file that imports all plugins that are active.
|
||||||
|
|
||||||
import
|
import
|
||||||
locals.locals
|
locals.locals, itersgen
|
||||||
|
|
|
||||||
51
compiler/plugins/itersgen.nim
Normal file
51
compiler/plugins/itersgen.nim
Normal file
|
|
@ -0,0 +1,51 @@
|
||||||
|
#
|
||||||
|
#
|
||||||
|
# The Nim Compiler
|
||||||
|
# (c) Copyright 2015 Andreas Rumpf
|
||||||
|
#
|
||||||
|
# See the file "copying.txt", included in this
|
||||||
|
# distribution, for details about the copyright.
|
||||||
|
#
|
||||||
|
|
||||||
|
## Plugin to transform an inline iterator into a data structure.
|
||||||
|
|
||||||
|
import compiler/pluginsupport, compiler/ast, compiler/astalgo,
|
||||||
|
compiler/magicsys, compiler/lookups, compiler/semdata,
|
||||||
|
compiler/lambdalifting, compiler/rodread, compiler/msgs
|
||||||
|
|
||||||
|
|
||||||
|
proc iterToProcImpl(c: PContext, n: PNode): PNode =
|
||||||
|
result = newNodeI(nkStmtList, n.info)
|
||||||
|
let iter = n[1]
|
||||||
|
if iter.kind != nkSym or iter.sym.kind != skIterator:
|
||||||
|
localError(iter.info, "first argument needs to be an iterator")
|
||||||
|
return
|
||||||
|
if n[2].typ.isNil:
|
||||||
|
localError(n[2].info, "second argument needs to be a type")
|
||||||
|
return
|
||||||
|
if n[3].kind != nkIdent:
|
||||||
|
localError(n[3].info, "third argument needs to be an identifier")
|
||||||
|
return
|
||||||
|
|
||||||
|
let t = n[2].typ.skipTypes({tyTypeDesc, tyGenericInst})
|
||||||
|
if t.kind notin {tyRef, tyPtr} or t.lastSon.kind != tyObject:
|
||||||
|
localError(n[2].info,
|
||||||
|
"type must be a non-generic ref|ptr to object with state field")
|
||||||
|
return
|
||||||
|
let body = liftIterToProc(iter.sym, iter.sym.getBody, t)
|
||||||
|
|
||||||
|
let prc = newSym(skProc, n[3].ident, iter.sym.owner, iter.sym.info)
|
||||||
|
prc.typ = copyType(iter.sym.typ, prc, false)
|
||||||
|
excl prc.typ.flags, tfCapturesEnv
|
||||||
|
prc.typ.n.add newSymNode(getEnvParam(iter.sym))
|
||||||
|
prc.typ.rawAddSon t
|
||||||
|
let orig = iter.sym.ast
|
||||||
|
prc.ast = newProcNode(nkProcDef, n.info,
|
||||||
|
name = newSymNode(prc),
|
||||||
|
params = orig[paramsPos],
|
||||||
|
pragmas = orig[pragmasPos],
|
||||||
|
body = body)
|
||||||
|
prc.ast.add iter.sym.ast.sons[resultPos]
|
||||||
|
addInterfaceDecl(c, prc)
|
||||||
|
|
||||||
|
registerPlugin("stdlib", "system", "iterToProc", iterToProcImpl)
|
||||||
|
|
@ -9,8 +9,8 @@
|
||||||
|
|
||||||
## The builtin 'system.locals' implemented as a plugin.
|
## The builtin 'system.locals' implemented as a plugin.
|
||||||
|
|
||||||
import compiler/plugins, compiler/ast, compiler/astalgo, compiler/magicsys,
|
import compiler/pluginsupport, compiler/ast, compiler/astalgo,
|
||||||
compiler/lookups, compiler/semdata, compiler/lowerings
|
compiler/magicsys, compiler/lookups, compiler/semdata, compiler/lowerings
|
||||||
|
|
||||||
proc semLocals(c: PContext, n: PNode): PNode =
|
proc semLocals(c: PContext, n: PNode): PNode =
|
||||||
var counter = 0
|
var counter = 0
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@
|
||||||
# distribution, for details about the copyright.
|
# distribution, for details about the copyright.
|
||||||
#
|
#
|
||||||
|
|
||||||
## Plugin support for the Nim compiler. Right now there are no plugins and they
|
## Plugin support for the Nim compiler. Right now they
|
||||||
## need to be build with the compiler, no DLL support.
|
## need to be build with the compiler, no DLL support.
|
||||||
|
|
||||||
import ast, semdata, idents
|
import ast, semdata, idents
|
||||||
|
|
@ -20,13 +20,16 @@ type
|
||||||
next: Plugin
|
next: Plugin
|
||||||
|
|
||||||
proc pluginMatches(p: Plugin; s: PSym): bool =
|
proc pluginMatches(p: Plugin; s: PSym): bool =
|
||||||
if s.name.id != p.fn.id: return false
|
if s.name.id != p.fn.id:
|
||||||
let module = s.owner
|
return false
|
||||||
|
let module = s.skipGenericOwner
|
||||||
if module == nil or module.kind != skModule or
|
if module == nil or module.kind != skModule or
|
||||||
module.name.id != p.module.id: return false
|
module.name.id != p.module.id:
|
||||||
|
return false
|
||||||
let package = module.owner
|
let package = module.owner
|
||||||
if package == nil or package.kind != skPackage or
|
if package == nil or package.kind != skPackage or
|
||||||
package.name.id != p.package.id: return false
|
package.name.id != p.package.id:
|
||||||
|
return false
|
||||||
return true
|
return true
|
||||||
|
|
||||||
var head: Plugin
|
var head: Plugin
|
||||||
|
|
@ -167,33 +167,24 @@ proc makeNimString(s: string): string =
|
||||||
proc putComment(g: var TSrcGen, s: string) =
|
proc putComment(g: var TSrcGen, s: string) =
|
||||||
if s.isNil: return
|
if s.isNil: return
|
||||||
var i = 0
|
var i = 0
|
||||||
var comIndent = 1
|
|
||||||
var isCode = (len(s) >= 2) and (s[1] != ' ')
|
var isCode = (len(s) >= 2) and (s[1] != ' ')
|
||||||
var ind = g.lineLen
|
var ind = g.lineLen
|
||||||
var com = ""
|
var com = "## "
|
||||||
while true:
|
while true:
|
||||||
case s[i]
|
case s[i]
|
||||||
of '\0':
|
of '\0':
|
||||||
break
|
break
|
||||||
of '\x0D':
|
of '\x0D':
|
||||||
put(g, tkComment, com)
|
put(g, tkComment, com)
|
||||||
com = ""
|
com = "## "
|
||||||
inc(i)
|
inc(i)
|
||||||
if s[i] == '\x0A': inc(i)
|
if s[i] == '\x0A': inc(i)
|
||||||
optNL(g, ind)
|
optNL(g, ind)
|
||||||
of '\x0A':
|
of '\x0A':
|
||||||
put(g, tkComment, com)
|
put(g, tkComment, com)
|
||||||
com = ""
|
com = "## "
|
||||||
inc(i)
|
inc(i)
|
||||||
optNL(g, ind)
|
optNL(g, ind)
|
||||||
of '#':
|
|
||||||
add(com, s[i])
|
|
||||||
inc(i)
|
|
||||||
comIndent = 0
|
|
||||||
while s[i] == ' ':
|
|
||||||
add(com, s[i])
|
|
||||||
inc(i)
|
|
||||||
inc(comIndent)
|
|
||||||
of ' ', '\x09':
|
of ' ', '\x09':
|
||||||
add(com, s[i])
|
add(com, s[i])
|
||||||
inc(i)
|
inc(i)
|
||||||
|
|
@ -206,7 +197,7 @@ proc putComment(g: var TSrcGen, s: string) =
|
||||||
if not isCode and (g.lineLen + (j - i) > MaxLineLen):
|
if not isCode and (g.lineLen + (j - i) > MaxLineLen):
|
||||||
put(g, tkComment, com)
|
put(g, tkComment, com)
|
||||||
optNL(g, ind)
|
optNL(g, ind)
|
||||||
com = '#' & spaces(comIndent)
|
com = "## "
|
||||||
while s[i] > ' ':
|
while s[i] > ' ':
|
||||||
add(com, s[i])
|
add(com, s[i])
|
||||||
inc(i)
|
inc(i)
|
||||||
|
|
@ -283,7 +274,7 @@ proc shouldRenderComment(g: var TSrcGen, n: PNode): bool =
|
||||||
result = false
|
result = false
|
||||||
if n.comment != nil:
|
if n.comment != nil:
|
||||||
result = (renderNoComments notin g.flags) or
|
result = (renderNoComments notin g.flags) or
|
||||||
(renderDocComments in g.flags) and startsWith(n.comment, "##")
|
(renderDocComments in g.flags)
|
||||||
|
|
||||||
proc gcom(g: var TSrcGen, n: PNode) =
|
proc gcom(g: var TSrcGen, n: PNode) =
|
||||||
assert(n != nil)
|
assert(n != nil)
|
||||||
|
|
@ -1330,6 +1321,8 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) =
|
||||||
initContext c
|
initContext c
|
||||||
putWithSpace g, tkSymbol, if n.kind == nkState: "state" else: "goto"
|
putWithSpace g, tkSymbol, if n.kind == nkState: "state" else: "goto"
|
||||||
gsons(g, n, c)
|
gsons(g, n, c)
|
||||||
|
of nkBreakState:
|
||||||
|
put(g, tkTuple, "breakstate")
|
||||||
of nkTypeClassTy:
|
of nkTypeClassTy:
|
||||||
gTypeClassTy(g, n)
|
gTypeClassTy(g, n)
|
||||||
else:
|
else:
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,7 @@ import
|
||||||
procfind, lookups, rodread, pragmas, passes, semdata, semtypinst, sigmatch,
|
procfind, lookups, rodread, pragmas, passes, semdata, semtypinst, sigmatch,
|
||||||
intsets, transf, vmdef, vm, idgen, aliases, cgmeth, lambdalifting,
|
intsets, transf, vmdef, vm, idgen, aliases, cgmeth, lambdalifting,
|
||||||
evaltempl, patterns, parampatterns, sempass2, nimfix.pretty, semmacrosanity,
|
evaltempl, patterns, parampatterns, sempass2, nimfix.pretty, semmacrosanity,
|
||||||
semparallel, lowerings, plugins, plugins.active
|
semparallel, lowerings, pluginsupport, plugins.active
|
||||||
|
|
||||||
when defined(nimfix):
|
when defined(nimfix):
|
||||||
import nimfix.prettybase
|
import nimfix.prettybase
|
||||||
|
|
@ -186,6 +186,8 @@ proc newSymG*(kind: TSymKind, n: PNode, c: PContext): PSym =
|
||||||
result.owner = getCurrOwner()
|
result.owner = getCurrOwner()
|
||||||
else:
|
else:
|
||||||
result = newSym(kind, considerQuotedIdent(n), getCurrOwner(), n.info)
|
result = newSym(kind, considerQuotedIdent(n), getCurrOwner(), n.info)
|
||||||
|
#if kind in {skForVar, skLet, skVar} and result.owner.kind == skModule:
|
||||||
|
# incl(result.flags, sfGlobal)
|
||||||
|
|
||||||
proc semIdentVis(c: PContext, kind: TSymKind, n: PNode,
|
proc semIdentVis(c: PContext, kind: TSymKind, n: PNode,
|
||||||
allowed: TSymFlags): PSym
|
allowed: TSymFlags): PSym
|
||||||
|
|
@ -202,7 +204,7 @@ proc typeAllowedCheck(info: TLineInfo; typ: PType; kind: TSymKind) =
|
||||||
"' in this context: '" & typeToString(typ) & "'")
|
"' in this context: '" & typeToString(typ) & "'")
|
||||||
|
|
||||||
proc paramsTypeCheck(c: PContext, typ: PType) {.inline.} =
|
proc paramsTypeCheck(c: PContext, typ: PType) {.inline.} =
|
||||||
typeAllowedCheck(typ.n.info, typ, skConst)
|
typeAllowedCheck(typ.n.info, typ, skProc)
|
||||||
|
|
||||||
proc expectMacroOrTemplateCall(c: PContext, n: PNode): PSym
|
proc expectMacroOrTemplateCall(c: PContext, n: PNode): PSym
|
||||||
proc semDirectOp(c: PContext, n: PNode, flags: TExprFlags): PNode
|
proc semDirectOp(c: PContext, n: PNode, flags: TExprFlags): PNode
|
||||||
|
|
@ -485,4 +487,3 @@ proc myClose(context: PPassContext, n: PNode): PNode =
|
||||||
popProcCon(c)
|
popProcCon(c)
|
||||||
|
|
||||||
const semPass* = makePass(myOpen, myOpenCached, myProcess, myClose)
|
const semPass* = makePass(myOpen, myOpenCached, myProcess, myClose)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -75,7 +75,7 @@ proc pickBestCandidate(c: PContext, headSymbol: PNode,
|
||||||
errors.add(err)
|
errors.add(err)
|
||||||
if z.state == csMatch:
|
if z.state == csMatch:
|
||||||
# little hack so that iterators are preferred over everything else:
|
# little hack so that iterators are preferred over everything else:
|
||||||
if sym.kind in skIterators: inc(z.exactMatches, 200)
|
if sym.kind == skIterator: inc(z.exactMatches, 200)
|
||||||
case best.state
|
case best.state
|
||||||
of csEmpty, csNoMatch: best = z
|
of csEmpty, csNoMatch: best = z
|
||||||
of csMatch:
|
of csMatch:
|
||||||
|
|
@ -395,7 +395,7 @@ proc explicitGenericInstantiation(c: PContext, n: PNode, s: PSym): PNode =
|
||||||
for i in countup(0, len(a)-1):
|
for i in countup(0, len(a)-1):
|
||||||
var candidate = a.sons[i].sym
|
var candidate = a.sons[i].sym
|
||||||
if candidate.kind in {skProc, skMethod, skConverter,
|
if candidate.kind in {skProc, skMethod, skConverter,
|
||||||
skIterator, skClosureIterator}:
|
skIterator}:
|
||||||
# it suffices that the candidate has the proper number of generic
|
# it suffices that the candidate has the proper number of generic
|
||||||
# type parameters:
|
# type parameters:
|
||||||
if safeLen(candidate.ast.sons[genericParamsPos]) == n.len-1:
|
if safeLen(candidate.ast.sons[genericParamsPos]) == n.len-1:
|
||||||
|
|
|
||||||
|
|
@ -315,7 +315,7 @@ proc makeRangeType*(c: PContext; first, last: BiggestInt;
|
||||||
addSonSkipIntLit(result, intType) # basetype of range
|
addSonSkipIntLit(result, intType) # basetype of range
|
||||||
|
|
||||||
proc markIndirect*(c: PContext, s: PSym) {.inline.} =
|
proc markIndirect*(c: PContext, s: PSym) {.inline.} =
|
||||||
if s.kind in {skProc, skConverter, skMethod, skIterator, skClosureIterator}:
|
if s.kind in {skProc, skConverter, skMethod, skIterator}:
|
||||||
incl(s.flags, sfAddrTaken)
|
incl(s.flags, sfAddrTaken)
|
||||||
# XXX add to 'c' for global analysis
|
# XXX add to 'c' for global analysis
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -385,7 +385,8 @@ proc isOpImpl(c: PContext, n: PNode): PNode =
|
||||||
result = newIntNode(nkIntLit, ord(t.kind == tyProc and
|
result = newIntNode(nkIntLit, ord(t.kind == tyProc and
|
||||||
t.callConv == ccClosure and
|
t.callConv == ccClosure and
|
||||||
tfIterator notin t.flags))
|
tfIterator notin t.flags))
|
||||||
else: discard
|
else:
|
||||||
|
result = newIntNode(nkIntLit, 0)
|
||||||
else:
|
else:
|
||||||
var t2 = n[2].typ.skipTypes({tyTypeDesc})
|
var t2 = n[2].typ.skipTypes({tyTypeDesc})
|
||||||
maybeLiftType(t2, c, n.info)
|
maybeLiftType(t2, c, n.info)
|
||||||
|
|
@ -752,11 +753,11 @@ proc semOverloadedCallAnalyseEffects(c: PContext, n: PNode, nOrig: PNode,
|
||||||
flags: TExprFlags): PNode =
|
flags: TExprFlags): PNode =
|
||||||
if flags*{efInTypeof, efWantIterator} != {}:
|
if flags*{efInTypeof, efWantIterator} != {}:
|
||||||
# consider: 'for x in pReturningArray()' --> we don't want the restriction
|
# consider: 'for x in pReturningArray()' --> we don't want the restriction
|
||||||
# to 'skIterators' anymore; skIterators are preferred in sigmatch already
|
# to 'skIterator' anymore; skIterator is preferred in sigmatch already
|
||||||
# for typeof support.
|
# for typeof support.
|
||||||
# for ``type(countup(1,3))``, see ``tests/ttoseq``.
|
# for ``type(countup(1,3))``, see ``tests/ttoseq``.
|
||||||
result = semOverloadedCall(c, n, nOrig,
|
result = semOverloadedCall(c, n, nOrig,
|
||||||
{skProc, skMethod, skConverter, skMacro, skTemplate}+skIterators)
|
{skProc, skMethod, skConverter, skMacro, skTemplate, skIterator})
|
||||||
else:
|
else:
|
||||||
result = semOverloadedCall(c, n, nOrig,
|
result = semOverloadedCall(c, n, nOrig,
|
||||||
{skProc, skMethod, skConverter, skMacro, skTemplate})
|
{skProc, skMethod, skConverter, skMacro, skTemplate})
|
||||||
|
|
@ -769,7 +770,7 @@ proc semOverloadedCallAnalyseEffects(c: PContext, n: PNode, nOrig: PNode,
|
||||||
case callee.kind
|
case callee.kind
|
||||||
of skMacro, skTemplate: discard
|
of skMacro, skTemplate: discard
|
||||||
else:
|
else:
|
||||||
if callee.kind in skIterators and callee.id == c.p.owner.id:
|
if callee.kind == skIterator and callee.id == c.p.owner.id:
|
||||||
localError(n.info, errRecursiveDependencyX, callee.name.s)
|
localError(n.info, errRecursiveDependencyX, callee.name.s)
|
||||||
# error correction, prevents endless for loop elimination in transf.
|
# error correction, prevents endless for loop elimination in transf.
|
||||||
# See bug #2051:
|
# See bug #2051:
|
||||||
|
|
@ -1200,7 +1201,7 @@ proc semSubscript(c: PContext, n: PNode, flags: TExprFlags): PNode =
|
||||||
let s = if n.sons[0].kind == nkSym: n.sons[0].sym
|
let s = if n.sons[0].kind == nkSym: n.sons[0].sym
|
||||||
elif n[0].kind in nkSymChoices: n.sons[0][0].sym
|
elif n[0].kind in nkSymChoices: n.sons[0][0].sym
|
||||||
else: nil
|
else: nil
|
||||||
if s != nil and s.kind in {skProc, skMethod, skConverter}+skIterators:
|
if s != nil and s.kind in {skProc, skMethod, skConverter, skIterator}:
|
||||||
# type parameters: partial generic specialization
|
# type parameters: partial generic specialization
|
||||||
n.sons[0] = semSymGenericInstantiation(c, n.sons[0], s)
|
n.sons[0] = semSymGenericInstantiation(c, n.sons[0], s)
|
||||||
result = explicitGenericInstantiation(c, n, s)
|
result = explicitGenericInstantiation(c, n, s)
|
||||||
|
|
@ -1348,8 +1349,8 @@ proc semAsgn(c: PContext, n: PNode; mode=asgnNormal): PNode =
|
||||||
proc semReturn(c: PContext, n: PNode): PNode =
|
proc semReturn(c: PContext, n: PNode): PNode =
|
||||||
result = n
|
result = n
|
||||||
checkSonsLen(n, 1)
|
checkSonsLen(n, 1)
|
||||||
if c.p.owner.kind in {skConverter, skMethod, skProc, skMacro,
|
if c.p.owner.kind in {skConverter, skMethod, skProc, skMacro} or (
|
||||||
skClosureIterator}:
|
c.p.owner.kind == skIterator and c.p.owner.typ.callConv == ccClosure):
|
||||||
if n.sons[0].kind != nkEmpty:
|
if n.sons[0].kind != nkEmpty:
|
||||||
# transform ``return expr`` to ``result = expr; return``
|
# transform ``return expr`` to ``result = expr; return``
|
||||||
if c.p.resultSym != nil:
|
if c.p.resultSym != nil:
|
||||||
|
|
@ -1425,7 +1426,7 @@ proc semYieldVarResult(c: PContext, n: PNode, restype: PType) =
|
||||||
proc semYield(c: PContext, n: PNode): PNode =
|
proc semYield(c: PContext, n: PNode): PNode =
|
||||||
result = n
|
result = n
|
||||||
checkSonsLen(n, 1)
|
checkSonsLen(n, 1)
|
||||||
if c.p.owner == nil or c.p.owner.kind notin skIterators:
|
if c.p.owner == nil or c.p.owner.kind != skIterator:
|
||||||
localError(n.info, errYieldNotAllowedHere)
|
localError(n.info, errYieldNotAllowedHere)
|
||||||
elif c.p.inTryStmt > 0 and c.p.owner.typ.callConv != ccInline:
|
elif c.p.inTryStmt > 0 and c.p.owner.typ.callConv != ccInline:
|
||||||
localError(n.info, errYieldNotAllowedInTryStmt)
|
localError(n.info, errYieldNotAllowedInTryStmt)
|
||||||
|
|
@ -1434,20 +1435,15 @@ proc semYield(c: PContext, n: PNode): PNode =
|
||||||
var iterType = c.p.owner.typ
|
var iterType = c.p.owner.typ
|
||||||
let restype = iterType.sons[0]
|
let restype = iterType.sons[0]
|
||||||
if restype != nil:
|
if restype != nil:
|
||||||
let adjustedRes = if restype.kind == tyIter: restype.base
|
if restype.kind != tyExpr:
|
||||||
else: restype
|
n.sons[0] = fitNode(c, restype, n.sons[0])
|
||||||
if adjustedRes.kind != tyExpr:
|
|
||||||
n.sons[0] = fitNode(c, adjustedRes, n.sons[0])
|
|
||||||
if n.sons[0].typ == nil: internalError(n.info, "semYield")
|
if n.sons[0].typ == nil: internalError(n.info, "semYield")
|
||||||
|
|
||||||
if resultTypeIsInferrable(adjustedRes):
|
if resultTypeIsInferrable(restype):
|
||||||
let inferred = n.sons[0].typ
|
let inferred = n.sons[0].typ
|
||||||
if restype.kind == tyIter:
|
iterType.sons[0] = inferred
|
||||||
restype.sons[0] = inferred
|
|
||||||
else:
|
|
||||||
iterType.sons[0] = inferred
|
|
||||||
|
|
||||||
semYieldVarResult(c, n, adjustedRes)
|
semYieldVarResult(c, n, restype)
|
||||||
else:
|
else:
|
||||||
localError(n.info, errCannotReturnExpr)
|
localError(n.info, errCannotReturnExpr)
|
||||||
elif c.p.owner.typ.sons[0] != nil:
|
elif c.p.owner.typ.sons[0] != nil:
|
||||||
|
|
@ -1780,7 +1776,24 @@ proc semMagic(c: PContext, n: PNode, s: PSym, flags: TExprFlags): PNode =
|
||||||
result = setMs(n, s)
|
result = setMs(n, s)
|
||||||
result.sons[1] = semExpr(c, n.sons[1])
|
result.sons[1] = semExpr(c, n.sons[1])
|
||||||
result.typ = n[1].typ
|
result.typ = n[1].typ
|
||||||
else: result = semDirectOp(c, n, flags)
|
of mPlugin:
|
||||||
|
# semDirectOp with conditional 'afterCallActions':
|
||||||
|
let nOrig = n.copyTree
|
||||||
|
#semLazyOpAux(c, n)
|
||||||
|
result = semOverloadedCallAnalyseEffects(c, n, nOrig, flags)
|
||||||
|
if result == nil:
|
||||||
|
result = errorNode(c, n)
|
||||||
|
else:
|
||||||
|
let callee = result.sons[0].sym
|
||||||
|
if callee.magic == mNone:
|
||||||
|
semFinishOperands(c, result)
|
||||||
|
activate(c, result)
|
||||||
|
fixAbstractType(c, result)
|
||||||
|
analyseIfAddressTakenInCall(c, result)
|
||||||
|
if callee.magic != mNone:
|
||||||
|
result = magicsAfterOverloadResolution(c, result, flags)
|
||||||
|
else:
|
||||||
|
result = semDirectOp(c, n, flags)
|
||||||
|
|
||||||
proc semWhen(c: PContext, n: PNode, semCheck = true): PNode =
|
proc semWhen(c: PContext, n: PNode, semCheck = true): PNode =
|
||||||
# If semCheck is set to false, ``when`` will return the verbatim AST of
|
# If semCheck is set to false, ``when`` will return the verbatim AST of
|
||||||
|
|
@ -1804,6 +1817,7 @@ proc semWhen(c: PContext, n: PNode, semCheck = true): PNode =
|
||||||
whenNimvm = lookUp(c, exprNode).magic == mNimvm
|
whenNimvm = lookUp(c, exprNode).magic == mNimvm
|
||||||
elif exprNode.kind == nkSym:
|
elif exprNode.kind == nkSym:
|
||||||
whenNimvm = exprNode.sym.magic == mNimvm
|
whenNimvm = exprNode.sym.magic == mNimvm
|
||||||
|
if whenNimvm: n.flags.incl nfLL
|
||||||
|
|
||||||
for i in countup(0, sonsLen(n) - 1):
|
for i in countup(0, sonsLen(n) - 1):
|
||||||
var it = n.sons[i]
|
var it = n.sons[i]
|
||||||
|
|
@ -2111,7 +2125,7 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
|
||||||
var s = lookUp(c, n)
|
var s = lookUp(c, n)
|
||||||
if c.inTypeClass == 0: semCaptureSym(s, c.p.owner)
|
if c.inTypeClass == 0: semCaptureSym(s, c.p.owner)
|
||||||
result = semSym(c, n, s, flags)
|
result = semSym(c, n, s, flags)
|
||||||
if s.kind in {skProc, skMethod, skConverter}+skIterators:
|
if s.kind in {skProc, skMethod, skConverter, skIterator}:
|
||||||
#performProcvarCheck(c, n, s)
|
#performProcvarCheck(c, n, s)
|
||||||
result = symChoice(c, n, s, scClosed)
|
result = symChoice(c, n, s, scClosed)
|
||||||
if result.kind == nkSym:
|
if result.kind == nkSym:
|
||||||
|
|
@ -2167,7 +2181,7 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
|
||||||
message(n.info, warnDeprecated, "bind")
|
message(n.info, warnDeprecated, "bind")
|
||||||
result = semExpr(c, n.sons[0], flags)
|
result = semExpr(c, n.sons[0], flags)
|
||||||
of nkTypeOfExpr, nkTupleTy, nkTupleClassTy, nkRefTy..nkEnumTy, nkStaticTy:
|
of nkTypeOfExpr, nkTupleTy, nkTupleClassTy, nkRefTy..nkEnumTy, nkStaticTy:
|
||||||
var typ = semTypeNode(c, n, nil).skipTypes({tyTypeDesc, tyIter})
|
var typ = semTypeNode(c, n, nil).skipTypes({tyTypeDesc})
|
||||||
result.typ = makeTypeDesc(c, typ)
|
result.typ = makeTypeDesc(c, typ)
|
||||||
#result = symNodeFromType(c, typ, n.info)
|
#result = symNodeFromType(c, typ, n.info)
|
||||||
of nkCall, nkInfix, nkPrefix, nkPostfix, nkCommand, nkCallStrLit:
|
of nkCall, nkInfix, nkPrefix, nkPostfix, nkCommand, nkCallStrLit:
|
||||||
|
|
@ -2199,7 +2213,7 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
|
||||||
localError(n.info, errUseQualifier, s.name.s)
|
localError(n.info, errUseQualifier, s.name.s)
|
||||||
elif s.magic == mNone: result = semDirectOp(c, n, flags)
|
elif s.magic == mNone: result = semDirectOp(c, n, flags)
|
||||||
else: result = semMagic(c, n, s, flags)
|
else: result = semMagic(c, n, s, flags)
|
||||||
of skProc, skMethod, skConverter, skIterators:
|
of skProc, skMethod, skConverter, skIterator:
|
||||||
if s.magic == mNone: result = semDirectOp(c, n, flags)
|
if s.magic == mNone: result = semDirectOp(c, n, flags)
|
||||||
else: result = semMagic(c, n, s, flags)
|
else: result = semMagic(c, n, s, flags)
|
||||||
else:
|
else:
|
||||||
|
|
@ -2240,7 +2254,7 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
|
||||||
var tupexp = semTuplePositionsConstr(c, n, flags)
|
var tupexp = semTuplePositionsConstr(c, n, flags)
|
||||||
if isTupleType(tupexp):
|
if isTupleType(tupexp):
|
||||||
# reinterpret as type
|
# reinterpret as type
|
||||||
var typ = semTypeNode(c, n, nil).skipTypes({tyTypeDesc, tyIter})
|
var typ = semTypeNode(c, n, nil).skipTypes({tyTypeDesc})
|
||||||
result.typ = makeTypeDesc(c, typ)
|
result.typ = makeTypeDesc(c, typ)
|
||||||
else:
|
else:
|
||||||
result = tupexp
|
result = tupexp
|
||||||
|
|
|
||||||
|
|
@ -58,7 +58,7 @@ proc semGenericStmtSymbol(c: PContext, n: PNode, s: PSym,
|
||||||
of skUnknown:
|
of skUnknown:
|
||||||
# Introduced in this pass! Leave it as an identifier.
|
# Introduced in this pass! Leave it as an identifier.
|
||||||
result = n
|
result = n
|
||||||
of skProc, skMethod, skIterators, skConverter, skModule:
|
of skProc, skMethod, skIterator, skConverter, skModule:
|
||||||
result = symChoice(c, n, s, scOpen)
|
result = symChoice(c, n, s, scOpen)
|
||||||
of skTemplate:
|
of skTemplate:
|
||||||
if macroToExpand(s):
|
if macroToExpand(s):
|
||||||
|
|
@ -226,7 +226,7 @@ proc semGenericStmt(c: PContext, n: PNode,
|
||||||
of skUnknown, skParam:
|
of skUnknown, skParam:
|
||||||
# Leave it as an identifier.
|
# Leave it as an identifier.
|
||||||
discard
|
discard
|
||||||
of skProc, skMethod, skIterators, skConverter, skModule:
|
of skProc, skMethod, skIterator, skConverter, skModule:
|
||||||
result.sons[0] = symChoice(c, fn, s, scOption)
|
result.sons[0] = symChoice(c, fn, s, scOption)
|
||||||
# do not check of 's.magic==mRoof' here because it might be some
|
# do not check of 's.magic==mRoof' here because it might be some
|
||||||
# other '^' but after overload resolution the proper one:
|
# other '^' but after overload resolution the proper one:
|
||||||
|
|
|
||||||
|
|
@ -207,7 +207,7 @@ proc magicsAfterOverloadResolution(c: PContext, n: PNode,
|
||||||
result = n.sons[1]
|
result = n.sons[1]
|
||||||
else:
|
else:
|
||||||
result = newNodeIT(nkCall, n.info, getSysType(tyInt))
|
result = newNodeIT(nkCall, n.info, getSysType(tyInt))
|
||||||
result.add newSymNode(createMagic("-", mSubI), n.info)
|
result.add newSymNode(getSysMagic("-", mSubI), n.info)
|
||||||
result.add lenExprB
|
result.add lenExprB
|
||||||
result.add n.sons[1]
|
result.add n.sons[1]
|
||||||
of mPlugin:
|
of mPlugin:
|
||||||
|
|
|
||||||
|
|
@ -504,7 +504,8 @@ proc notNilCheck(tracked: PEffects, n: PNode, paramType: PType) =
|
||||||
if n.kind == nkAddr:
|
if n.kind == nkAddr:
|
||||||
# addr(x[]) can't be proven, but addr(x) can:
|
# addr(x[]) can't be proven, but addr(x) can:
|
||||||
if not containsNode(n, {nkDerefExpr, nkHiddenDeref}): return
|
if not containsNode(n, {nkDerefExpr, nkHiddenDeref}): return
|
||||||
elif (n.kind == nkSym and n.sym.kind in routineKinds) or n.kind in procDefs:
|
elif (n.kind == nkSym and n.sym.kind in routineKinds) or
|
||||||
|
n.kind in procDefs+{nkObjConstr}:
|
||||||
# 'p' is not nil obviously:
|
# 'p' is not nil obviously:
|
||||||
return
|
return
|
||||||
case impliesNotNil(tracked.guards, n)
|
case impliesNotNil(tracked.guards, n)
|
||||||
|
|
@ -704,7 +705,15 @@ proc track(tracked: PEffects, n: PNode) =
|
||||||
for i in 1 .. <len(n): trackOperand(tracked, n.sons[i], paramType(op, i))
|
for i in 1 .. <len(n): trackOperand(tracked, n.sons[i], paramType(op, i))
|
||||||
if a.kind == nkSym and a.sym.magic in {mNew, mNewFinalize, mNewSeq}:
|
if a.kind == nkSym and a.sym.magic in {mNew, mNewFinalize, mNewSeq}:
|
||||||
# may not look like an assignment, but it is:
|
# may not look like an assignment, but it is:
|
||||||
initVarViaNew(tracked, n.sons[1])
|
let arg = n.sons[1]
|
||||||
|
initVarViaNew(tracked, arg)
|
||||||
|
if {tfNeedsInit} * arg.typ.lastSon.flags != {}:
|
||||||
|
if a.sym.magic == mNewSeq and n[2].kind in {nkCharLit..nkUInt64Lit} and
|
||||||
|
n[2].intVal == 0:
|
||||||
|
# var s: seq[notnil]; newSeq(s, 0) is a special case!
|
||||||
|
discard
|
||||||
|
else:
|
||||||
|
message(arg.info, warnProveInit, $arg)
|
||||||
for i in 0 .. <safeLen(n):
|
for i in 0 .. <safeLen(n):
|
||||||
track(tracked, n.sons[i])
|
track(tracked, n.sons[i])
|
||||||
of nkDotExpr:
|
of nkDotExpr:
|
||||||
|
|
@ -875,7 +884,8 @@ proc trackProc*(s: PSym, body: PNode) =
|
||||||
var t: TEffects
|
var t: TEffects
|
||||||
initEffects(effects, s, t)
|
initEffects(effects, s, t)
|
||||||
track(t, body)
|
track(t, body)
|
||||||
if not isEmptyType(s.typ.sons[0]) and tfNeedsInit in s.typ.sons[0].flags and
|
if not isEmptyType(s.typ.sons[0]) and
|
||||||
|
{tfNeedsInit, tfNotNil} * s.typ.sons[0].flags != {} and
|
||||||
s.kind in {skProc, skConverter, skMethod}:
|
s.kind in {skProc, skConverter, skMethod}:
|
||||||
var res = s.ast.sons[resultPos].sym # get result symbol
|
var res = s.ast.sons[resultPos].sym # get result symbol
|
||||||
if res.id notin t.init:
|
if res.id notin t.init:
|
||||||
|
|
|
||||||
|
|
@ -84,7 +84,7 @@ proc performProcvarCheck(c: PContext, n: PNode, s: PSym) =
|
||||||
proc semProcvarCheck(c: PContext, n: PNode) =
|
proc semProcvarCheck(c: PContext, n: PNode) =
|
||||||
let n = n.skipConv
|
let n = n.skipConv
|
||||||
if n.kind == nkSym and n.sym.kind in {skProc, skMethod, skConverter,
|
if n.kind == nkSym and n.sym.kind in {skProc, skMethod, skConverter,
|
||||||
skIterator, skClosureIterator}:
|
skIterator}:
|
||||||
performProcvarCheck(c, n, n.sym)
|
performProcvarCheck(c, n, n.sym)
|
||||||
|
|
||||||
proc semProc(c: PContext, n: PNode): PNode
|
proc semProc(c: PContext, n: PNode): PNode
|
||||||
|
|
@ -326,11 +326,14 @@ proc semIdentDef(c: PContext, n: PNode, kind: TSymKind): PSym =
|
||||||
incl(result.flags, sfGlobal)
|
incl(result.flags, sfGlobal)
|
||||||
else:
|
else:
|
||||||
result = semIdentWithPragma(c, kind, n, {})
|
result = semIdentWithPragma(c, kind, n, {})
|
||||||
|
if result.owner.kind == skModule:
|
||||||
|
incl(result.flags, sfGlobal)
|
||||||
suggestSym(n.info, result)
|
suggestSym(n.info, result)
|
||||||
styleCheckDef(result)
|
styleCheckDef(result)
|
||||||
|
|
||||||
proc checkNilable(v: PSym) =
|
proc checkNilable(v: PSym) =
|
||||||
if sfGlobal in v.flags and {tfNotNil, tfNeedsInit} * v.typ.flags != {}:
|
if {sfGlobal, sfImportC} * v.flags == {sfGlobal} and
|
||||||
|
{tfNotNil, tfNeedsInit} * v.typ.flags != {}:
|
||||||
if v.ast.isNil:
|
if v.ast.isNil:
|
||||||
message(v.info, warnProveInit, v.name.s)
|
message(v.info, warnProveInit, v.name.s)
|
||||||
elif tfNotNil in v.typ.flags and tfNotNil notin v.ast.typ.flags:
|
elif tfNotNil in v.typ.flags and tfNotNil notin v.ast.typ.flags:
|
||||||
|
|
@ -539,7 +542,7 @@ proc symForVar(c: PContext, n: PNode): PSym =
|
||||||
proc semForVars(c: PContext, n: PNode): PNode =
|
proc semForVars(c: PContext, n: PNode): PNode =
|
||||||
result = n
|
result = n
|
||||||
var length = sonsLen(n)
|
var length = sonsLen(n)
|
||||||
let iterBase = n.sons[length-2].typ.skipTypes({tyIter})
|
let iterBase = n.sons[length-2].typ
|
||||||
var iter = skipTypes(iterBase, {tyGenericInst})
|
var iter = skipTypes(iterBase, {tyGenericInst})
|
||||||
# length == 3 means that there is one for loop variable
|
# length == 3 means that there is one for loop variable
|
||||||
# and thus no tuple unpacking:
|
# and thus no tuple unpacking:
|
||||||
|
|
@ -593,12 +596,11 @@ proc semFor(c: PContext, n: PNode): PNode =
|
||||||
result.kind = nkParForStmt
|
result.kind = nkParForStmt
|
||||||
else:
|
else:
|
||||||
result = semForFields(c, n, call.sons[0].sym.magic)
|
result = semForFields(c, n, call.sons[0].sym.magic)
|
||||||
elif (isCallExpr and call.sons[0].typ.callConv == ccClosure) or
|
elif isCallExpr and call.sons[0].typ.callConv == ccClosure:
|
||||||
call.typ.kind == tyIter:
|
|
||||||
# first class iterator:
|
# first class iterator:
|
||||||
result = semForVars(c, n)
|
result = semForVars(c, n)
|
||||||
elif not isCallExpr or call.sons[0].kind != nkSym or
|
elif not isCallExpr or call.sons[0].kind != nkSym or
|
||||||
call.sons[0].sym.kind notin skIterators:
|
call.sons[0].sym.kind != skIterator:
|
||||||
if length == 3:
|
if length == 3:
|
||||||
n.sons[length-2] = implicitIterator(c, "items", n.sons[length-2])
|
n.sons[length-2] = implicitIterator(c, "items", n.sons[length-2])
|
||||||
elif length == 4:
|
elif length == 4:
|
||||||
|
|
@ -958,15 +960,17 @@ proc semInferredLambda(c: PContext, pt: TIdTable, n: PNode): PNode =
|
||||||
var n = n
|
var n = n
|
||||||
|
|
||||||
let original = n.sons[namePos].sym
|
let original = n.sons[namePos].sym
|
||||||
let s = copySym(original, false)
|
let s = original #copySym(original, false)
|
||||||
incl(s.flags, sfFromGeneric)
|
#incl(s.flags, sfFromGeneric)
|
||||||
|
#s.owner = original
|
||||||
|
|
||||||
n = replaceTypesInBody(c, pt, n, original)
|
n = replaceTypesInBody(c, pt, n, original)
|
||||||
result = n
|
result = n
|
||||||
s.ast = result
|
s.ast = result
|
||||||
n.sons[namePos].sym = s
|
n.sons[namePos].sym = s
|
||||||
n.sons[genericParamsPos] = emptyNode
|
n.sons[genericParamsPos] = emptyNode
|
||||||
let params = n.typ.n
|
# for LL we need to avoid wrong aliasing
|
||||||
|
let params = copyTree n.typ.n
|
||||||
n.sons[paramsPos] = params
|
n.sons[paramsPos] = params
|
||||||
s.typ = n.typ
|
s.typ = n.typ
|
||||||
for i in 1..<params.len:
|
for i in 1..<params.len:
|
||||||
|
|
@ -974,6 +978,7 @@ proc semInferredLambda(c: PContext, pt: TIdTable, n: PNode): PNode =
|
||||||
tyFromExpr, tyFieldAccessor}+tyTypeClasses:
|
tyFromExpr, tyFieldAccessor}+tyTypeClasses:
|
||||||
localError(params[i].info, "cannot infer type of parameter: " &
|
localError(params[i].info, "cannot infer type of parameter: " &
|
||||||
params[i].sym.name.s)
|
params[i].sym.name.s)
|
||||||
|
#params[i].sym.owner = s
|
||||||
openScope(c)
|
openScope(c)
|
||||||
pushOwner(s)
|
pushOwner(s)
|
||||||
addParams(c, params, skProc)
|
addParams(c, params, skProc)
|
||||||
|
|
@ -1006,7 +1011,8 @@ proc activate(c: PContext, n: PNode) =
|
||||||
discard
|
discard
|
||||||
|
|
||||||
proc maybeAddResult(c: PContext, s: PSym, n: PNode) =
|
proc maybeAddResult(c: PContext, s: PSym, n: PNode) =
|
||||||
if s.typ.sons[0] != nil and s.kind != skIterator:
|
if s.typ.sons[0] != nil and not
|
||||||
|
(s.kind == skIterator and s.typ.callConv != ccClosure):
|
||||||
addResult(c, s.typ.sons[0], n.info, s.kind)
|
addResult(c, s.typ.sons[0], n.info, s.kind)
|
||||||
addResultNode(c, n)
|
addResultNode(c, n)
|
||||||
|
|
||||||
|
|
@ -1143,13 +1149,15 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind,
|
||||||
if tfTriggersCompileTime in s.typ.flags: incl(s.flags, sfCompileTime)
|
if tfTriggersCompileTime in s.typ.flags: incl(s.flags, sfCompileTime)
|
||||||
if n.sons[patternPos].kind != nkEmpty:
|
if n.sons[patternPos].kind != nkEmpty:
|
||||||
n.sons[patternPos] = semPattern(c, n.sons[patternPos])
|
n.sons[patternPos] = semPattern(c, n.sons[patternPos])
|
||||||
if s.kind in skIterators:
|
if s.kind == skIterator:
|
||||||
s.typ.flags.incl(tfIterator)
|
s.typ.flags.incl(tfIterator)
|
||||||
|
|
||||||
var proto = searchForProc(c, oldScope, s)
|
var proto = searchForProc(c, oldScope, s)
|
||||||
if proto == nil:
|
if proto == nil:
|
||||||
if s.kind == skClosureIterator: s.typ.callConv = ccClosure
|
if s.kind == skIterator and s.typ.callConv == ccClosure:
|
||||||
else: s.typ.callConv = lastOptionEntry(c).defaultCC
|
discard
|
||||||
|
else:
|
||||||
|
s.typ.callConv = lastOptionEntry(c).defaultCC
|
||||||
# add it here, so that recursive procs are possible:
|
# add it here, so that recursive procs are possible:
|
||||||
if sfGenSym in s.flags: discard
|
if sfGenSym in s.flags: discard
|
||||||
elif kind in OverloadableSyms:
|
elif kind in OverloadableSyms:
|
||||||
|
|
@ -1209,7 +1217,7 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind,
|
||||||
n.sons[bodyPos] = transformBody(c.module, semBody, s)
|
n.sons[bodyPos] = transformBody(c.module, semBody, s)
|
||||||
popProcCon(c)
|
popProcCon(c)
|
||||||
else:
|
else:
|
||||||
if s.typ.sons[0] != nil and kind notin skIterators:
|
if s.typ.sons[0] != nil and kind != skIterator:
|
||||||
addDecl(c, newSym(skUnknown, getIdent"result", nil, n.info))
|
addDecl(c, newSym(skUnknown, getIdent"result", nil, n.info))
|
||||||
openScope(c)
|
openScope(c)
|
||||||
n.sons[bodyPos] = semGenericStmt(c, n.sons[bodyPos])
|
n.sons[bodyPos] = semGenericStmt(c, n.sons[bodyPos])
|
||||||
|
|
@ -1230,9 +1238,9 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind,
|
||||||
if n.sons[patternPos].kind != nkEmpty:
|
if n.sons[patternPos].kind != nkEmpty:
|
||||||
c.patterns.add(s)
|
c.patterns.add(s)
|
||||||
if isAnon: result.typ = s.typ
|
if isAnon: result.typ = s.typ
|
||||||
if isTopLevel(c) and s.kind != skClosureIterator and
|
if isTopLevel(c) and s.kind != skIterator and
|
||||||
s.typ.callConv == ccClosure:
|
s.typ.callConv == ccClosure:
|
||||||
message(s.info, warnDeprecated, "top level '.closure' calling convention")
|
localError(s.info, "'.closure' calling convention for top level routines is invalid")
|
||||||
|
|
||||||
proc determineType(c: PContext, s: PSym) =
|
proc determineType(c: PContext, s: PSym) =
|
||||||
if s.typ != nil: return
|
if s.typ != nil: return
|
||||||
|
|
@ -1240,15 +1248,12 @@ proc determineType(c: PContext, s: PSym) =
|
||||||
discard semProcAux(c, s.ast, s.kind, {}, stepDetermineType)
|
discard semProcAux(c, s.ast, s.kind, {}, stepDetermineType)
|
||||||
|
|
||||||
proc semIterator(c: PContext, n: PNode): PNode =
|
proc semIterator(c: PContext, n: PNode): PNode =
|
||||||
let kind = if hasPragma(n[pragmasPos], wClosure) or
|
|
||||||
n[namePos].kind == nkEmpty: skClosureIterator
|
|
||||||
else: skIterator
|
|
||||||
# gensym'ed iterator?
|
# gensym'ed iterator?
|
||||||
if n[namePos].kind == nkSym:
|
if n[namePos].kind == nkSym:
|
||||||
# gensym'ed iterators might need to become closure iterators:
|
# gensym'ed iterators might need to become closure iterators:
|
||||||
n[namePos].sym.owner = getCurrOwner()
|
n[namePos].sym.owner = getCurrOwner()
|
||||||
n[namePos].sym.kind = kind
|
n[namePos].sym.kind = skIterator
|
||||||
result = semProcAux(c, n, kind, iteratorPragmas)
|
result = semProcAux(c, n, skIterator, iteratorPragmas)
|
||||||
var s = result.sons[namePos].sym
|
var s = result.sons[namePos].sym
|
||||||
var t = s.typ
|
var t = s.typ
|
||||||
if t.sons[0] == nil and s.typ.callConv != ccClosure:
|
if t.sons[0] == nil and s.typ.callConv != ccClosure:
|
||||||
|
|
|
||||||
|
|
@ -228,10 +228,7 @@ proc semTemplSymbol(c: PContext, n: PNode, s: PSym): PNode =
|
||||||
of skParam:
|
of skParam:
|
||||||
result = n
|
result = n
|
||||||
of skType:
|
of skType:
|
||||||
if (s.typ != nil) and (s.typ.kind != tyGenericParam):
|
result = newSymNodeTypeDesc(s, n.info)
|
||||||
result = newSymNodeTypeDesc(s, n.info)
|
|
||||||
else:
|
|
||||||
result = n
|
|
||||||
else:
|
else:
|
||||||
result = newSymNode(s, n.info)
|
result = newSymNode(s, n.info)
|
||||||
|
|
||||||
|
|
@ -456,9 +453,7 @@ proc semTemplBody(c: var TemplCtx, n: PNode): PNode =
|
||||||
of nkMethodDef:
|
of nkMethodDef:
|
||||||
result = semRoutineInTemplBody(c, n, skMethod)
|
result = semRoutineInTemplBody(c, n, skMethod)
|
||||||
of nkIteratorDef:
|
of nkIteratorDef:
|
||||||
let kind = if hasPragma(n[pragmasPos], wClosure): skClosureIterator
|
result = semRoutineInTemplBody(c, n, skIterator)
|
||||||
else: skIterator
|
|
||||||
result = semRoutineInTemplBody(c, n, kind)
|
|
||||||
of nkTemplateDef:
|
of nkTemplateDef:
|
||||||
result = semRoutineInTemplBody(c, n, skTemplate)
|
result = semRoutineInTemplBody(c, n, skTemplate)
|
||||||
of nkMacroDef:
|
of nkMacroDef:
|
||||||
|
|
|
||||||
|
|
@ -135,13 +135,19 @@ proc semAnyRef(c: PContext; n: PNode; kind: TTypeKind; prev: PType): PType =
|
||||||
checkMinSonsLen(n, 1)
|
checkMinSonsLen(n, 1)
|
||||||
var base = semTypeNode(c, n.lastSon, nil)
|
var base = semTypeNode(c, n.lastSon, nil)
|
||||||
result = newOrPrevType(kind, prev, c)
|
result = newOrPrevType(kind, prev, c)
|
||||||
|
var isNilable = false
|
||||||
# check every except the last is an object:
|
# check every except the last is an object:
|
||||||
for i in isCall .. n.len-2:
|
for i in isCall .. n.len-2:
|
||||||
let region = semTypeNode(c, n[i], nil)
|
let ni = n[i]
|
||||||
if region.skipTypes({tyGenericInst}).kind notin {tyError, tyObject}:
|
if ni.kind == nkNilLit:
|
||||||
message n[i].info, errGenerated, "region needs to be an object type"
|
isNilable = true
|
||||||
addSonSkipIntLit(result, region)
|
else:
|
||||||
|
let region = semTypeNode(c, ni, nil)
|
||||||
|
if region.skipTypes({tyGenericInst}).kind notin {tyError, tyObject}:
|
||||||
|
message n[i].info, errGenerated, "region needs to be an object type"
|
||||||
|
addSonSkipIntLit(result, region)
|
||||||
addSonSkipIntLit(result, base)
|
addSonSkipIntLit(result, base)
|
||||||
|
#if not isNilable: result.flags.incl tfNotNil
|
||||||
|
|
||||||
proc semVarType(c: PContext, n: PNode, prev: PType): PType =
|
proc semVarType(c: PContext, n: PNode, prev: PType): PType =
|
||||||
if sonsLen(n) == 1:
|
if sonsLen(n) == 1:
|
||||||
|
|
@ -826,15 +832,6 @@ proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode,
|
||||||
result = newTypeWithSons(c, tyCompositeTypeClass, @[paramType, result])
|
result = newTypeWithSons(c, tyCompositeTypeClass, @[paramType, result])
|
||||||
result = addImplicitGeneric(result)
|
result = addImplicitGeneric(result)
|
||||||
|
|
||||||
of tyIter:
|
|
||||||
if paramType.callConv == ccInline:
|
|
||||||
if procKind notin {skTemplate, skMacro, skIterator}:
|
|
||||||
localError(info, errInlineIteratorsAsProcParams)
|
|
||||||
if paramType.len == 1:
|
|
||||||
let lifted = liftingWalk(paramType.base)
|
|
||||||
if lifted != nil: paramType.sons[0] = lifted
|
|
||||||
result = addImplicitGeneric(paramType)
|
|
||||||
|
|
||||||
of tyGenericInst:
|
of tyGenericInst:
|
||||||
if paramType.lastSon.kind == tyUserTypeClass:
|
if paramType.lastSon.kind == tyUserTypeClass:
|
||||||
var cp = copyType(paramType, getCurrOwner(), false)
|
var cp = copyType(paramType, getCurrOwner(), false)
|
||||||
|
|
@ -865,11 +862,6 @@ proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode,
|
||||||
of tyUserTypeClass, tyBuiltInTypeClass, tyAnd, tyOr, tyNot:
|
of tyUserTypeClass, tyBuiltInTypeClass, tyAnd, tyOr, tyNot:
|
||||||
result = addImplicitGeneric(copyType(paramType, getCurrOwner(), true))
|
result = addImplicitGeneric(copyType(paramType, getCurrOwner(), true))
|
||||||
|
|
||||||
of tyExpr:
|
|
||||||
if procKind notin {skMacro, skTemplate}:
|
|
||||||
result = addImplicitGeneric(newTypeS(tyAnything, c))
|
|
||||||
#result = addImplicitGenericImpl(newTypeS(tyGenericParam, c), nil)
|
|
||||||
|
|
||||||
of tyGenericParam:
|
of tyGenericParam:
|
||||||
markUsed(info, paramType.sym)
|
markUsed(info, paramType.sym)
|
||||||
styleCheckUse(info, paramType.sym)
|
styleCheckUse(info, paramType.sym)
|
||||||
|
|
@ -968,10 +960,6 @@ proc semProcTypeNode(c: PContext, n, genericParams: PNode,
|
||||||
var r: PType
|
var r: PType
|
||||||
if n.sons[0].kind != nkEmpty:
|
if n.sons[0].kind != nkEmpty:
|
||||||
r = semTypeNode(c, n.sons[0], nil)
|
r = semTypeNode(c, n.sons[0], nil)
|
||||||
elif kind == skIterator:
|
|
||||||
# XXX This is special magic we should likely get rid of
|
|
||||||
r = newTypeS(tyExpr, c)
|
|
||||||
message(n.info, warnDeprecated, "implicit return type for 'iterator'")
|
|
||||||
|
|
||||||
if r != nil:
|
if r != nil:
|
||||||
# turn explicit 'void' return type into 'nil' because the rest of the
|
# turn explicit 'void' return type into 'nil' because the rest of the
|
||||||
|
|
@ -996,7 +984,8 @@ proc semProcTypeNode(c: PContext, n, genericParams: PNode,
|
||||||
# see tchainediterators
|
# see tchainediterators
|
||||||
# in cases like iterator foo(it: iterator): type(it)
|
# in cases like iterator foo(it: iterator): type(it)
|
||||||
# we don't need to change the return type to iter[T]
|
# we don't need to change the return type to iter[T]
|
||||||
if not r.isInlineIterator: r = newTypeWithSons(c, tyIter, @[r])
|
result.flags.incl tfIterator
|
||||||
|
# XXX Would be nice if we could get rid of this
|
||||||
result.sons[0] = r
|
result.sons[0] = r
|
||||||
result.n.typ = r
|
result.n.typ = r
|
||||||
|
|
||||||
|
|
@ -1151,7 +1140,7 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType =
|
||||||
# for ``type(countup(1,3))``, see ``tests/ttoseq``.
|
# for ``type(countup(1,3))``, see ``tests/ttoseq``.
|
||||||
checkSonsLen(n, 1)
|
checkSonsLen(n, 1)
|
||||||
let typExpr = semExprWithType(c, n.sons[0], {efInTypeof})
|
let typExpr = semExprWithType(c, n.sons[0], {efInTypeof})
|
||||||
result = typExpr.typ.skipTypes({tyIter})
|
result = typExpr.typ
|
||||||
of nkPar:
|
of nkPar:
|
||||||
if sonsLen(n) == 1: result = semTypeNode(c, n.sons[0], prev)
|
if sonsLen(n) == 1: result = semTypeNode(c, n.sons[0], prev)
|
||||||
else:
|
else:
|
||||||
|
|
@ -1169,6 +1158,14 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType =
|
||||||
result = semTypeNode(c, b, prev)
|
result = semTypeNode(c, b, prev)
|
||||||
elif ident != nil and ident.id == ord(wDotDot):
|
elif ident != nil and ident.id == ord(wDotDot):
|
||||||
result = semRangeAux(c, n, prev)
|
result = semRangeAux(c, n, prev)
|
||||||
|
elif n[0].kind == nkNilLit and n.len == 2:
|
||||||
|
result = semTypeNode(c, n.sons[1], prev)
|
||||||
|
if result.skipTypes({tyGenericInst}).kind in NilableTypes+GenericTypes:
|
||||||
|
if tfNotNil in result.flags:
|
||||||
|
result = freshType(result, prev)
|
||||||
|
result.flags.excl(tfNotNil)
|
||||||
|
else:
|
||||||
|
localError(n.info, errGenerated, "invalid type")
|
||||||
elif n[0].kind notin nkIdentKinds:
|
elif n[0].kind notin nkIdentKinds:
|
||||||
result = semTypeExpr(c, n)
|
result = semTypeExpr(c, n)
|
||||||
else:
|
else:
|
||||||
|
|
@ -1209,7 +1206,7 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType =
|
||||||
elif op.id == ord(wType):
|
elif op.id == ord(wType):
|
||||||
checkSonsLen(n, 2)
|
checkSonsLen(n, 2)
|
||||||
let typExpr = semExprWithType(c, n.sons[1], {efInTypeof})
|
let typExpr = semExprWithType(c, n.sons[1], {efInTypeof})
|
||||||
result = typExpr.typ.skipTypes({tyIter})
|
result = typExpr.typ
|
||||||
else:
|
else:
|
||||||
result = semTypeExpr(c, n)
|
result = semTypeExpr(c, n)
|
||||||
of nkWhenStmt:
|
of nkWhenStmt:
|
||||||
|
|
@ -1290,14 +1287,16 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType =
|
||||||
result.flags.incl tfHasStatic
|
result.flags.incl tfHasStatic
|
||||||
of nkIteratorTy:
|
of nkIteratorTy:
|
||||||
if n.sonsLen == 0:
|
if n.sonsLen == 0:
|
||||||
result = newConstraint(c, tyIter)
|
result = newTypeS(tyBuiltInTypeClass, c)
|
||||||
|
let child = newTypeS(tyProc, c)
|
||||||
|
child.flags.incl tfIterator
|
||||||
|
result.addSonSkipIntLit(child)
|
||||||
else:
|
else:
|
||||||
result = semProcTypeWithScope(c, n, prev, skClosureIterator)
|
result = semProcTypeWithScope(c, n, prev, skIterator)
|
||||||
|
result.flags.incl(tfIterator)
|
||||||
if n.lastSon.kind == nkPragma and hasPragma(n.lastSon, wInline):
|
if n.lastSon.kind == nkPragma and hasPragma(n.lastSon, wInline):
|
||||||
result.kind = tyIter
|
|
||||||
result.callConv = ccInline
|
result.callConv = ccInline
|
||||||
else:
|
else:
|
||||||
result.flags.incl(tfIterator)
|
|
||||||
result.callConv = ccClosure
|
result.callConv = ccClosure
|
||||||
of nkProcTy:
|
of nkProcTy:
|
||||||
if n.sonsLen == 0:
|
if n.sonsLen == 0:
|
||||||
|
|
|
||||||
|
|
@ -77,7 +77,7 @@ proc cacheTypeInst*(inst: PType) =
|
||||||
# update the refcount
|
# update the refcount
|
||||||
let gt = inst.sons[0]
|
let gt = inst.sons[0]
|
||||||
let t = if gt.kind == tyGenericBody: gt.lastSon else: gt
|
let t = if gt.kind == tyGenericBody: gt.lastSon else: gt
|
||||||
if t.kind in {tyStatic, tyGenericParam, tyIter} + tyTypeClasses:
|
if t.kind in {tyStatic, tyGenericParam} + tyTypeClasses:
|
||||||
return
|
return
|
||||||
gt.sym.typeInstCache.safeAdd(inst)
|
gt.sym.typeInstCache.safeAdd(inst)
|
||||||
|
|
||||||
|
|
@ -390,7 +390,7 @@ proc replaceTypeVarsTAux(cl: var TReplTypeVars, t: PType): PType =
|
||||||
result = t
|
result = t
|
||||||
if t == nil: return
|
if t == nil: return
|
||||||
|
|
||||||
if t.kind in {tyStatic, tyGenericParam, tyIter} + tyTypeClasses:
|
if t.kind in {tyStatic, tyGenericParam} + tyTypeClasses:
|
||||||
let lookup = PType(idTableGet(cl.typeMap, t))
|
let lookup = PType(idTableGet(cl.typeMap, t))
|
||||||
if lookup != nil: return lookup
|
if lookup != nil: return lookup
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -167,12 +167,12 @@ proc sumGeneric(t: PType): int =
|
||||||
t = t.lastSon
|
t = t.lastSon
|
||||||
if t.kind == tyEmpty: break
|
if t.kind == tyEmpty: break
|
||||||
inc result
|
inc result
|
||||||
of tyGenericInvocation, tyTuple:
|
of tyGenericInvocation, tyTuple, tyProc:
|
||||||
result += ord(t.kind == tyGenericInvocation)
|
result += ord(t.kind == tyGenericInvocation)
|
||||||
for i in 0 .. <t.len: result += t.sons[i].sumGeneric
|
for i in 0 .. <t.len: result += t.sons[i].sumGeneric
|
||||||
break
|
break
|
||||||
of tyGenericParam, tyExpr, tyStatic, tyStmt: break
|
of tyGenericParam, tyExpr, tyStatic, tyStmt: break
|
||||||
of tyBool, tyChar, tyEnum, tyObject, tyProc, tyPointer,
|
of tyBool, tyChar, tyEnum, tyObject, tyPointer,
|
||||||
tyString, tyCString, tyInt..tyInt64, tyFloat..tyFloat128,
|
tyString, tyCString, tyInt..tyInt64, tyFloat..tyFloat128,
|
||||||
tyUInt..tyUInt64:
|
tyUInt..tyUInt64:
|
||||||
return isvar
|
return isvar
|
||||||
|
|
@ -1256,10 +1256,6 @@ proc localConvMatch(c: PContext, m: var TCandidate, f, a: PType,
|
||||||
result.typ = getInstantiatedType(c, arg, m, base(f))
|
result.typ = getInstantiatedType(c, arg, m, base(f))
|
||||||
m.baseTypeMatch = true
|
m.baseTypeMatch = true
|
||||||
|
|
||||||
proc isInlineIterator*(t: PType): bool =
|
|
||||||
result = t.kind == tyIter or
|
|
||||||
(t.kind == tyBuiltInTypeClass and t.base.kind == tyIter)
|
|
||||||
|
|
||||||
proc incMatches(m: var TCandidate; r: TTypeRelation; convMatch = 1) =
|
proc incMatches(m: var TCandidate; r: TTypeRelation; convMatch = 1) =
|
||||||
case r
|
case r
|
||||||
of isConvertible, isIntConv: inc(m.convMatches, convMatch)
|
of isConvertible, isIntConv: inc(m.convMatches, convMatch)
|
||||||
|
|
@ -1323,13 +1319,6 @@ proc paramTypesMatchAux(m: var TCandidate, f, argType: PType,
|
||||||
else:
|
else:
|
||||||
return argSemantized # argOrig
|
return argSemantized # argOrig
|
||||||
|
|
||||||
if r != isNone and f.isInlineIterator:
|
|
||||||
var inlined = newTypeS(tyStatic, c)
|
|
||||||
inlined.sons = @[argType]
|
|
||||||
inlined.n = argSemantized
|
|
||||||
put(m.bindings, f, inlined)
|
|
||||||
return argSemantized
|
|
||||||
|
|
||||||
# If r == isBothMetaConvertible then we rerun typeRel.
|
# If r == isBothMetaConvertible then we rerun typeRel.
|
||||||
# bothMetaCounter is for safety to avoid any infinite loop,
|
# bothMetaCounter is for safety to avoid any infinite loop,
|
||||||
# I don't have any example when it is needed.
|
# I don't have any example when it is needed.
|
||||||
|
|
@ -1453,7 +1442,7 @@ proc paramTypesMatch*(m: var TCandidate, f, a: PType,
|
||||||
z.calleeSym = m.calleeSym
|
z.calleeSym = m.calleeSym
|
||||||
var best = -1
|
var best = -1
|
||||||
for i in countup(0, sonsLen(arg) - 1):
|
for i in countup(0, sonsLen(arg) - 1):
|
||||||
if arg.sons[i].sym.kind in {skProc, skMethod, skConverter}+skIterators:
|
if arg.sons[i].sym.kind in {skProc, skMethod, skConverter, skIterator}:
|
||||||
copyCandidate(z, m)
|
copyCandidate(z, m)
|
||||||
z.callee = arg.sons[i].typ
|
z.callee = arg.sons[i].typ
|
||||||
z.calleeSym = arg.sons[i].sym
|
z.calleeSym = arg.sons[i].sym
|
||||||
|
|
@ -1646,6 +1635,7 @@ proc matchesAux(c: PContext, n, nOrig: PNode,
|
||||||
if arg != nil and m.baseTypeMatch and container != nil:
|
if arg != nil and m.baseTypeMatch and container != nil:
|
||||||
addSon(container, arg)
|
addSon(container, arg)
|
||||||
incrIndexType(container.typ)
|
incrIndexType(container.typ)
|
||||||
|
checkConstraint(n.sons[a])
|
||||||
else:
|
else:
|
||||||
m.state = csNoMatch
|
m.state = csNoMatch
|
||||||
return
|
return
|
||||||
|
|
@ -1686,7 +1676,7 @@ proc matchesAux(c: PContext, n, nOrig: PNode,
|
||||||
setSon(m.call, formal.position + 1, arg)
|
setSon(m.call, formal.position + 1, arg)
|
||||||
inc(f)
|
inc(f)
|
||||||
container = nil
|
container = nil
|
||||||
checkConstraint(n.sons[a])
|
checkConstraint(n.sons[a])
|
||||||
inc(a)
|
inc(a)
|
||||||
|
|
||||||
proc semFinishOperands*(c: PContext, n: PNode) =
|
proc semFinishOperands*(c: PContext, n: PNode) =
|
||||||
|
|
|
||||||
|
|
@ -45,7 +45,7 @@ type
|
||||||
inlining: int # > 0 if we are in inlining context (copy vars)
|
inlining: int # > 0 if we are in inlining context (copy vars)
|
||||||
nestedProcs: int # > 0 if we are in a nested proc
|
nestedProcs: int # > 0 if we are in a nested proc
|
||||||
contSyms, breakSyms: seq[PSym] # to transform 'continue' and 'break'
|
contSyms, breakSyms: seq[PSym] # to transform 'continue' and 'break'
|
||||||
deferDetected: bool
|
deferDetected, tooEarly: bool
|
||||||
PTransf = ref TTransfContext
|
PTransf = ref TTransfContext
|
||||||
|
|
||||||
proc newTransNode(a: PNode): PTransNode {.inline.} =
|
proc newTransNode(a: PNode): PTransNode {.inline.} =
|
||||||
|
|
@ -93,10 +93,15 @@ proc getCurrOwner(c: PTransf): PSym =
|
||||||
if c.transCon != nil: result = c.transCon.owner
|
if c.transCon != nil: result = c.transCon.owner
|
||||||
else: result = c.module
|
else: result = c.module
|
||||||
|
|
||||||
proc newTemp(c: PTransf, typ: PType, info: TLineInfo): PSym =
|
proc newTemp(c: PTransf, typ: PType, info: TLineInfo): PNode =
|
||||||
result = newSym(skTemp, getIdent(genPrefix), getCurrOwner(c), info)
|
let r = newSym(skTemp, getIdent(genPrefix), getCurrOwner(c), info)
|
||||||
result.typ = skipTypes(typ, {tyGenericInst})
|
r.typ = skipTypes(typ, {tyGenericInst})
|
||||||
incl(result.flags, sfFromGeneric)
|
incl(r.flags, sfFromGeneric)
|
||||||
|
let owner = getCurrOwner(c)
|
||||||
|
if owner.isIterator and not c.tooEarly:
|
||||||
|
result = freshVarForClosureIter(r, owner)
|
||||||
|
else:
|
||||||
|
result = newSymNode(r)
|
||||||
|
|
||||||
proc transform(c: PTransf, n: PNode): PTransNode
|
proc transform(c: PTransf, n: PNode): PTransNode
|
||||||
|
|
||||||
|
|
@ -111,13 +116,22 @@ proc newAsgnStmt(c: PTransf, le: PNode, ri: PTransNode): PTransNode =
|
||||||
result[1] = ri
|
result[1] = ri
|
||||||
|
|
||||||
proc transformSymAux(c: PTransf, n: PNode): PNode =
|
proc transformSymAux(c: PTransf, n: PNode): PNode =
|
||||||
#if n.sym.kind == skClosureIterator:
|
let s = n.sym
|
||||||
# return liftIterSym(n)
|
if s.typ != nil and s.typ.callConv == ccClosure:
|
||||||
|
if s.kind == skIterator:
|
||||||
|
if c.tooEarly: return n
|
||||||
|
else: return liftIterSym(n, getCurrOwner(c))
|
||||||
|
elif s.kind in {skProc, skConverter, skMethod} and not c.tooEarly:
|
||||||
|
# top level .closure procs are still somewhat supported for 'Nake':
|
||||||
|
return makeClosure(s, nil, n.info)
|
||||||
|
#elif n.sym.kind in {skVar, skLet} and n.sym.typ.callConv == ccClosure:
|
||||||
|
# echo n.info, " come heer for ", c.tooEarly
|
||||||
|
# if not c.tooEarly:
|
||||||
var b: PNode
|
var b: PNode
|
||||||
var tc = c.transCon
|
var tc = c.transCon
|
||||||
if sfBorrow in n.sym.flags and n.sym.kind in routineKinds:
|
if sfBorrow in s.flags and s.kind in routineKinds:
|
||||||
# simply exchange the symbol:
|
# simply exchange the symbol:
|
||||||
b = n.sym.getBody
|
b = s.getBody
|
||||||
if b.kind != nkSym: internalError(n.info, "wrong AST for borrowed symbol")
|
if b.kind != nkSym: internalError(n.info, "wrong AST for borrowed symbol")
|
||||||
b = newSymNode(b.sym)
|
b = newSymNode(b.sym)
|
||||||
b.info = n.info
|
b.info = n.info
|
||||||
|
|
@ -132,6 +146,16 @@ proc transformSymAux(c: PTransf, n: PNode): PNode =
|
||||||
proc transformSym(c: PTransf, n: PNode): PTransNode =
|
proc transformSym(c: PTransf, n: PNode): PTransNode =
|
||||||
result = PTransNode(transformSymAux(c, n))
|
result = PTransNode(transformSymAux(c, n))
|
||||||
|
|
||||||
|
proc freshVar(c: PTransf; v: PSym): PNode =
|
||||||
|
let owner = getCurrOwner(c)
|
||||||
|
if owner.isIterator and not c.tooEarly:
|
||||||
|
result = freshVarForClosureIter(v, owner)
|
||||||
|
else:
|
||||||
|
var newVar = copySym(v)
|
||||||
|
incl(newVar.flags, sfFromGeneric)
|
||||||
|
newVar.owner = owner
|
||||||
|
result = newSymNode(newVar)
|
||||||
|
|
||||||
proc transformVarSection(c: PTransf, v: PNode): PTransNode =
|
proc transformVarSection(c: PTransf, v: PNode): PTransNode =
|
||||||
result = newTransNode(v)
|
result = newTransNode(v)
|
||||||
for i in countup(0, sonsLen(v)-1):
|
for i in countup(0, sonsLen(v)-1):
|
||||||
|
|
@ -141,35 +165,30 @@ proc transformVarSection(c: PTransf, v: PNode): PTransNode =
|
||||||
elif it.kind == nkIdentDefs:
|
elif it.kind == nkIdentDefs:
|
||||||
if it.sons[0].kind == nkSym:
|
if it.sons[0].kind == nkSym:
|
||||||
internalAssert(it.len == 3)
|
internalAssert(it.len == 3)
|
||||||
var newVar = copySym(it.sons[0].sym)
|
let x = freshVar(c, it.sons[0].sym)
|
||||||
incl(newVar.flags, sfFromGeneric)
|
idNodeTablePut(c.transCon.mapping, it.sons[0].sym, x)
|
||||||
# fixes a strange bug for rodgen:
|
|
||||||
#include(it.sons[0].sym.flags, sfFromGeneric);
|
|
||||||
newVar.owner = getCurrOwner(c)
|
|
||||||
idNodeTablePut(c.transCon.mapping, it.sons[0].sym, newSymNode(newVar))
|
|
||||||
var defs = newTransNode(nkIdentDefs, it.info, 3)
|
var defs = newTransNode(nkIdentDefs, it.info, 3)
|
||||||
if importantComments():
|
if importantComments():
|
||||||
# keep documentation information:
|
# keep documentation information:
|
||||||
PNode(defs).comment = it.comment
|
PNode(defs).comment = it.comment
|
||||||
defs[0] = newSymNode(newVar).PTransNode
|
defs[0] = x.PTransNode
|
||||||
defs[1] = it.sons[1].PTransNode
|
defs[1] = it.sons[1].PTransNode
|
||||||
defs[2] = transform(c, it.sons[2])
|
defs[2] = transform(c, it.sons[2])
|
||||||
newVar.ast = defs[2].PNode
|
if x.kind == nkSym: x.sym.ast = defs[2].PNode
|
||||||
result[i] = defs
|
result[i] = defs
|
||||||
else:
|
else:
|
||||||
# has been transformed into 'param.x' for closure iterators, so keep it:
|
# has been transformed into 'param.x' for closure iterators, so just
|
||||||
result[i] = PTransNode(it)
|
# transform it:
|
||||||
|
result[i] = transform(c, it)
|
||||||
else:
|
else:
|
||||||
if it.kind != nkVarTuple:
|
if it.kind != nkVarTuple:
|
||||||
internalError(it.info, "transformVarSection: not nkVarTuple")
|
internalError(it.info, "transformVarSection: not nkVarTuple")
|
||||||
var L = sonsLen(it)
|
var L = sonsLen(it)
|
||||||
var defs = newTransNode(it.kind, it.info, L)
|
var defs = newTransNode(it.kind, it.info, L)
|
||||||
for j in countup(0, L-3):
|
for j in countup(0, L-3):
|
||||||
var newVar = copySym(it.sons[j].sym)
|
let x = freshVar(c, it.sons[j].sym)
|
||||||
incl(newVar.flags, sfFromGeneric)
|
idNodeTablePut(c.transCon.mapping, it.sons[j].sym, x)
|
||||||
newVar.owner = getCurrOwner(c)
|
defs[j] = x.PTransNode
|
||||||
idNodeTablePut(c.transCon.mapping, it.sons[j].sym, newSymNode(newVar))
|
|
||||||
defs[j] = newSymNode(newVar).PTransNode
|
|
||||||
assert(it.sons[L-2].kind == nkEmpty)
|
assert(it.sons[L-2].kind == nkEmpty)
|
||||||
defs[L-2] = ast.emptyNode.PTransNode
|
defs[L-2] = ast.emptyNode.PTransNode
|
||||||
defs[L-1] = transform(c, it.sons[L-1])
|
defs[L-1] = transform(c, it.sons[L-1])
|
||||||
|
|
@ -294,10 +313,18 @@ proc introduceNewLocalVars(c: PTransf, n: PNode): PTransNode =
|
||||||
result = PTransNode(n)
|
result = PTransNode(n)
|
||||||
of nkVarSection, nkLetSection:
|
of nkVarSection, nkLetSection:
|
||||||
result = transformVarSection(c, n)
|
result = transformVarSection(c, n)
|
||||||
|
of nkClosure:
|
||||||
|
# it can happen that for-loop-inlining produced a fresh
|
||||||
|
# set of variables, including some computed environment
|
||||||
|
# (bug #2604). We need to patch this environment here too:
|
||||||
|
let a = n[1]
|
||||||
|
if a.kind == nkSym:
|
||||||
|
n.sons[1] = transformSymAux(c, a)
|
||||||
|
return PTransNode(n)
|
||||||
else:
|
else:
|
||||||
result = newTransNode(n)
|
result = newTransNode(n)
|
||||||
for i in countup(0, sonsLen(n)-1):
|
for i in countup(0, sonsLen(n)-1):
|
||||||
result[i] = introduceNewLocalVars(c, n.sons[i])
|
result[i] = introduceNewLocalVars(c, n.sons[i])
|
||||||
|
|
||||||
proc transformYield(c: PTransf, n: PNode): PTransNode =
|
proc transformYield(c: PTransf, n: PNode): PTransNode =
|
||||||
result = newTransNode(nkStmtList, n.info, 0)
|
result = newTransNode(nkStmtList, n.info, 0)
|
||||||
|
|
@ -348,6 +375,22 @@ proc transformAddrDeref(c: PTransf, n: PNode, a, b: TNodeKind): PTransNode =
|
||||||
# addr ( deref ( x )) --> x
|
# addr ( deref ( x )) --> x
|
||||||
result = PTransNode(n.sons[0].sons[0])
|
result = PTransNode(n.sons[0].sons[0])
|
||||||
|
|
||||||
|
proc generateThunk(prc: PNode, dest: PType): PNode =
|
||||||
|
## Converts 'prc' into '(thunk, nil)' so that it's compatible with
|
||||||
|
## a closure.
|
||||||
|
|
||||||
|
# we cannot generate a proper thunk here for GC-safety reasons
|
||||||
|
# (see internal documentation):
|
||||||
|
if gCmd == cmdCompileToJS: return prc
|
||||||
|
result = newNodeIT(nkClosure, prc.info, dest)
|
||||||
|
var conv = newNodeIT(nkHiddenSubConv, prc.info, dest)
|
||||||
|
conv.add(emptyNode)
|
||||||
|
conv.add(prc)
|
||||||
|
if prc.kind == nkClosure:
|
||||||
|
internalError(prc.info, "closure to closure created")
|
||||||
|
result.add(conv)
|
||||||
|
result.add(newNodeIT(nkNilLit, prc.info, getSysType(tyNil)))
|
||||||
|
|
||||||
proc transformConv(c: PTransf, n: PNode): PTransNode =
|
proc transformConv(c: PTransf, n: PNode): PTransNode =
|
||||||
# numeric types need range checks:
|
# numeric types need range checks:
|
||||||
var dest = skipTypes(n.typ, abstractVarRange)
|
var dest = skipTypes(n.typ, abstractVarRange)
|
||||||
|
|
@ -428,6 +471,10 @@ proc transformConv(c: PTransf, n: PNode): PTransNode =
|
||||||
of tyGenericParam, tyOrdinal:
|
of tyGenericParam, tyOrdinal:
|
||||||
result = transform(c, n.sons[1])
|
result = transform(c, n.sons[1])
|
||||||
# happens sometimes for generated assignments, etc.
|
# happens sometimes for generated assignments, etc.
|
||||||
|
of tyProc:
|
||||||
|
result = transformSons(c, n)
|
||||||
|
if dest.callConv == ccClosure and source.callConv == ccDefault:
|
||||||
|
result = generateThunk(result[1].PNode, dest).PTransNode
|
||||||
else:
|
else:
|
||||||
result = transformSons(c, n)
|
result = transformSons(c, n)
|
||||||
|
|
||||||
|
|
@ -478,11 +525,14 @@ proc transformFor(c: PTransf, n: PNode): PTransNode =
|
||||||
result[1] = newNode(nkEmpty).PTransNode
|
result[1] = newNode(nkEmpty).PTransNode
|
||||||
return result
|
return result
|
||||||
c.breakSyms.add(labl)
|
c.breakSyms.add(labl)
|
||||||
if call.typ.kind != tyIter and
|
if call.kind notin nkCallKinds or call.sons[0].kind != nkSym or
|
||||||
(call.kind notin nkCallKinds or call.sons[0].kind != nkSym or
|
call.sons[0].typ.callConv == ccClosure:
|
||||||
call.sons[0].sym.kind != skIterator):
|
|
||||||
n.sons[length-1] = transformLoopBody(c, n.sons[length-1]).PNode
|
n.sons[length-1] = transformLoopBody(c, n.sons[length-1]).PNode
|
||||||
result[1] = lambdalifting.liftForLoop(n).PTransNode
|
if not c.tooEarly:
|
||||||
|
n.sons[length-2] = transform(c, n.sons[length-2]).PNode
|
||||||
|
result[1] = lambdalifting.liftForLoop(n, getCurrOwner(c)).PTransNode
|
||||||
|
else:
|
||||||
|
result[1] = newNode(nkEmpty).PTransNode
|
||||||
discard c.breakSyms.pop
|
discard c.breakSyms.pop
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
|
@ -512,16 +562,15 @@ proc transformFor(c: PTransf, n: PNode): PTransNode =
|
||||||
for i in countup(1, sonsLen(call) - 1):
|
for i in countup(1, sonsLen(call) - 1):
|
||||||
var arg = transform(c, call.sons[i]).PNode
|
var arg = transform(c, call.sons[i]).PNode
|
||||||
var formal = skipTypes(iter.typ, abstractInst).n.sons[i].sym
|
var formal = skipTypes(iter.typ, abstractInst).n.sons[i].sym
|
||||||
if arg.typ.kind == tyIter: continue
|
|
||||||
case putArgInto(arg, formal.typ)
|
case putArgInto(arg, formal.typ)
|
||||||
of paDirectMapping:
|
of paDirectMapping:
|
||||||
idNodeTablePut(newC.mapping, formal, arg)
|
idNodeTablePut(newC.mapping, formal, arg)
|
||||||
of paFastAsgn:
|
of paFastAsgn:
|
||||||
# generate a temporary and produce an assignment statement:
|
# generate a temporary and produce an assignment statement:
|
||||||
var temp = newTemp(c, formal.typ, formal.info)
|
var temp = newTemp(c, formal.typ, formal.info)
|
||||||
addVar(v, newSymNode(temp))
|
addVar(v, temp)
|
||||||
add(stmtList, newAsgnStmt(c, newSymNode(temp), arg.PTransNode))
|
add(stmtList, newAsgnStmt(c, temp, arg.PTransNode))
|
||||||
idNodeTablePut(newC.mapping, formal, newSymNode(temp))
|
idNodeTablePut(newC.mapping, formal, temp)
|
||||||
of paVarAsgn:
|
of paVarAsgn:
|
||||||
assert(skipTypes(formal.typ, abstractInst).kind == tyVar)
|
assert(skipTypes(formal.typ, abstractInst).kind == tyVar)
|
||||||
idNodeTablePut(newC.mapping, formal, arg)
|
idNodeTablePut(newC.mapping, formal, arg)
|
||||||
|
|
@ -702,18 +751,13 @@ proc transform(c: PTransf, n: PNode): PTransNode =
|
||||||
result = PTransNode(n)
|
result = PTransNode(n)
|
||||||
of nkBracketExpr: result = transformArrayAccess(c, n)
|
of nkBracketExpr: result = transformArrayAccess(c, n)
|
||||||
of procDefs:
|
of procDefs:
|
||||||
when false:
|
var s = n.sons[namePos].sym
|
||||||
if n.sons[genericParamsPos].kind == nkEmpty:
|
if n.typ != nil and s.typ.callConv == ccClosure:
|
||||||
var s = n.sons[namePos].sym
|
result = transformSym(c, n.sons[namePos])
|
||||||
n.sons[bodyPos] = PNode(transform(c, s.getBody))
|
# use the same node as before if still a symbol:
|
||||||
if s.ast.sons[bodyPos] != n.sons[bodyPos]:
|
if result.PNode.kind == nkSym: result = PTransNode(n)
|
||||||
# somehow this can happen ... :-/
|
else:
|
||||||
s.ast.sons[bodyPos] = n.sons[bodyPos]
|
result = PTransNode(n)
|
||||||
#n.sons[bodyPos] = liftLambdas(s, n)
|
|
||||||
#if n.kind == nkMethodDef: methodDef(s, false)
|
|
||||||
#if n.kind == nkIteratorDef and n.typ != nil:
|
|
||||||
# return liftIterSym(n.sons[namePos]).PTransNode
|
|
||||||
result = PTransNode(n)
|
|
||||||
of nkMacroDef:
|
of nkMacroDef:
|
||||||
# XXX no proper closure support yet:
|
# XXX no proper closure support yet:
|
||||||
when false:
|
when false:
|
||||||
|
|
@ -750,7 +794,7 @@ proc transform(c: PTransf, n: PNode): PTransNode =
|
||||||
result = newTransNode(nkCommentStmt, n.info, 0)
|
result = newTransNode(nkCommentStmt, n.info, 0)
|
||||||
tryStmt.addSon(deferPart)
|
tryStmt.addSon(deferPart)
|
||||||
# disable the original 'defer' statement:
|
# disable the original 'defer' statement:
|
||||||
n.kind = nkCommentStmt
|
n.kind = nkEmpty
|
||||||
of nkContinueStmt:
|
of nkContinueStmt:
|
||||||
result = PTransNode(newNodeI(nkBreakStmt, n.info))
|
result = PTransNode(newNodeI(nkBreakStmt, n.info))
|
||||||
var labl = c.contSyms[c.contSyms.high]
|
var labl = c.contSyms[c.contSyms.high]
|
||||||
|
|
@ -796,7 +840,14 @@ proc transform(c: PTransf, n: PNode): PTransNode =
|
||||||
# XXX comment handling really sucks:
|
# XXX comment handling really sucks:
|
||||||
if importantComments():
|
if importantComments():
|
||||||
PNode(result).comment = n.comment
|
PNode(result).comment = n.comment
|
||||||
of nkClosure: return PTransNode(n)
|
of nkClosure:
|
||||||
|
# it can happen that for-loop-inlining produced a fresh
|
||||||
|
# set of variables, including some computed environment
|
||||||
|
# (bug #2604). We need to patch this environment here too:
|
||||||
|
let a = n[1]
|
||||||
|
if a.kind == nkSym:
|
||||||
|
n.sons[1] = transformSymAux(c, a)
|
||||||
|
return PTransNode(n)
|
||||||
else:
|
else:
|
||||||
result = transformSons(c, n)
|
result = transformSons(c, n)
|
||||||
when false:
|
when false:
|
||||||
|
|
@ -868,11 +919,11 @@ proc transformBody*(module: PSym, n: PNode, prc: PSym): PNode =
|
||||||
result = n
|
result = n
|
||||||
else:
|
else:
|
||||||
var c = openTransf(module, "")
|
var c = openTransf(module, "")
|
||||||
result = processTransf(c, n, prc)
|
result = liftLambdas(prc, n, c.tooEarly)
|
||||||
|
#result = n
|
||||||
|
result = processTransf(c, result, prc)
|
||||||
liftDefer(c, result)
|
liftDefer(c, result)
|
||||||
result = liftLambdas(prc, result)
|
#result = liftLambdas(prc, result)
|
||||||
#if prc.kind == skClosureIterator:
|
|
||||||
# result = lambdalifting.liftIterator(prc, result)
|
|
||||||
incl(result.flags, nfTransf)
|
incl(result.flags, nfTransf)
|
||||||
when useEffectSystem: trackProc(prc, result)
|
when useEffectSystem: trackProc(prc, result)
|
||||||
#if prc.name.s == "testbody":
|
#if prc.name.s == "testbody":
|
||||||
|
|
@ -885,9 +936,11 @@ proc transformStmt*(module: PSym, n: PNode): PNode =
|
||||||
var c = openTransf(module, "")
|
var c = openTransf(module, "")
|
||||||
result = processTransf(c, n, module)
|
result = processTransf(c, n, module)
|
||||||
liftDefer(c, result)
|
liftDefer(c, result)
|
||||||
result = liftLambdasForTopLevel(module, result)
|
#result = liftLambdasForTopLevel(module, result)
|
||||||
incl(result.flags, nfTransf)
|
incl(result.flags, nfTransf)
|
||||||
when useEffectSystem: trackTopLevelStmt(module, result)
|
when useEffectSystem: trackTopLevelStmt(module, result)
|
||||||
|
#if n.info ?? "temp.nim":
|
||||||
|
# echo renderTree(result, {renderIds})
|
||||||
|
|
||||||
proc transformExpr*(module: PSym, n: PNode): PNode =
|
proc transformExpr*(module: PSym, n: PNode): PNode =
|
||||||
if nfTransf in n.flags:
|
if nfTransf in n.flags:
|
||||||
|
|
|
||||||
|
|
@ -1061,7 +1061,8 @@ proc typeAllowedNode(marker: var IntSet, n: PNode, kind: TSymKind,
|
||||||
else:
|
else:
|
||||||
for i in countup(0, sonsLen(n) - 1):
|
for i in countup(0, sonsLen(n) - 1):
|
||||||
let it = n.sons[i]
|
let it = n.sons[i]
|
||||||
if it.kind == nkRecCase and kind == skConst: return n.typ
|
if it.kind == nkRecCase and kind in {skProc, skConst}:
|
||||||
|
return n.typ
|
||||||
result = typeAllowedNode(marker, it, kind, flags)
|
result = typeAllowedNode(marker, it, kind, flags)
|
||||||
if result != nil: break
|
if result != nil: break
|
||||||
|
|
||||||
|
|
@ -1076,7 +1077,7 @@ proc matchType*(a: PType, pattern: openArray[tuple[k:TTypeKind, i:int]],
|
||||||
|
|
||||||
proc typeAllowedAux(marker: var IntSet, typ: PType, kind: TSymKind,
|
proc typeAllowedAux(marker: var IntSet, typ: PType, kind: TSymKind,
|
||||||
flags: TTypeAllowedFlags = {}): PType =
|
flags: TTypeAllowedFlags = {}): PType =
|
||||||
assert(kind in {skVar, skLet, skConst, skParam, skResult})
|
assert(kind in {skVar, skLet, skConst, skProc, skParam, skResult})
|
||||||
# if we have already checked the type, return true, because we stop the
|
# if we have already checked the type, return true, because we stop the
|
||||||
# evaluation if something is wrong:
|
# evaluation if something is wrong:
|
||||||
result = nil
|
result = nil
|
||||||
|
|
@ -1085,7 +1086,7 @@ proc typeAllowedAux(marker: var IntSet, typ: PType, kind: TSymKind,
|
||||||
var t = skipTypes(typ, abstractInst-{tyTypeDesc})
|
var t = skipTypes(typ, abstractInst-{tyTypeDesc})
|
||||||
case t.kind
|
case t.kind
|
||||||
of tyVar:
|
of tyVar:
|
||||||
if kind == skConst: return t
|
if kind in {skProc, skConst}: return t
|
||||||
var t2 = skipTypes(t.sons[0], abstractInst-{tyTypeDesc})
|
var t2 = skipTypes(t.sons[0], abstractInst-{tyTypeDesc})
|
||||||
case t2.kind
|
case t2.kind
|
||||||
of tyVar:
|
of tyVar:
|
||||||
|
|
@ -1097,6 +1098,7 @@ proc typeAllowedAux(marker: var IntSet, typ: PType, kind: TSymKind,
|
||||||
if kind notin {skParam, skResult}: result = t
|
if kind notin {skParam, skResult}: result = t
|
||||||
else: result = typeAllowedAux(marker, t2, kind, flags)
|
else: result = typeAllowedAux(marker, t2, kind, flags)
|
||||||
of tyProc:
|
of tyProc:
|
||||||
|
if kind == skConst and t.callConv == ccClosure: return t
|
||||||
for i in countup(1, sonsLen(t) - 1):
|
for i in countup(1, sonsLen(t) - 1):
|
||||||
result = typeAllowedAux(marker, t.sons[i], skParam, flags)
|
result = typeAllowedAux(marker, t.sons[i], skParam, flags)
|
||||||
if result != nil: break
|
if result != nil: break
|
||||||
|
|
@ -1144,7 +1146,8 @@ proc typeAllowedAux(marker: var IntSet, typ: PType, kind: TSymKind,
|
||||||
result = typeAllowedAux(marker, t.sons[i], kind, flags)
|
result = typeAllowedAux(marker, t.sons[i], kind, flags)
|
||||||
if result != nil: break
|
if result != nil: break
|
||||||
of tyObject, tyTuple:
|
of tyObject, tyTuple:
|
||||||
if kind == skConst and t.kind == tyObject and t.sons[0] != nil: return t
|
if kind in {skProc, skConst} and
|
||||||
|
t.kind == tyObject and t.sons[0] != nil: return t
|
||||||
let flags = flags+{taField}
|
let flags = flags+{taField}
|
||||||
for i in countup(0, sonsLen(t) - 1):
|
for i in countup(0, sonsLen(t) - 1):
|
||||||
result = typeAllowedAux(marker, t.sons[i], kind, flags)
|
result = typeAllowedAux(marker, t.sons[i], kind, flags)
|
||||||
|
|
|
||||||
|
|
@ -255,9 +255,12 @@ proc cleanUpOnException(c: PCtx; tos: PStackFrame):
|
||||||
nextExceptOrFinally = pc2 + c.code[pc2].regBx - wordExcess
|
nextExceptOrFinally = pc2 + c.code[pc2].regBx - wordExcess
|
||||||
inc pc2
|
inc pc2
|
||||||
while c.code[pc2].opcode == opcExcept:
|
while c.code[pc2].opcode == opcExcept:
|
||||||
let exceptType = c.types[c.code[pc2].regBx-wordExcess].skipTypes(
|
let excIndex = c.code[pc2].regBx-wordExcess
|
||||||
|
let exceptType = if excIndex > 0: c.types[excIndex].skipTypes(
|
||||||
abstractPtrs)
|
abstractPtrs)
|
||||||
if inheritanceDiff(exceptType, raisedType) <= 0:
|
else: nil
|
||||||
|
#echo typeToString(exceptType), " ", typeToString(raisedType)
|
||||||
|
if exceptType.isNil or inheritanceDiff(exceptType, raisedType) <= 0:
|
||||||
# mark exception as handled but keep it in B for
|
# mark exception as handled but keep it in B for
|
||||||
# the getCurrentException() builtin:
|
# the getCurrentException() builtin:
|
||||||
c.currentExceptionB = c.currentExceptionA
|
c.currentExceptionB = c.currentExceptionA
|
||||||
|
|
@ -356,7 +359,14 @@ proc opConv*(dest: var TFullReg, src: TFullReg, desttyp, srctyp: PType): bool =
|
||||||
of tyFloat..tyFloat64:
|
of tyFloat..tyFloat64:
|
||||||
dest.intVal = int(src.floatVal)
|
dest.intVal = int(src.floatVal)
|
||||||
else:
|
else:
|
||||||
dest.intVal = src.intVal and ((1 shl (desttyp.size*8))-1)
|
let srcDist = (sizeof(src.intVal) - srctyp.size) * 8
|
||||||
|
let destDist = (sizeof(dest.intVal) - desttyp.size) * 8
|
||||||
|
when system.cpuEndian == bigEndian:
|
||||||
|
dest.intVal = (src.intVal shr srcDist) shl srcDist
|
||||||
|
dest.intVal = (dest.intVal shr destDist) shl destDist
|
||||||
|
else:
|
||||||
|
dest.intVal = (src.intVal shl srcDist) shr srcDist
|
||||||
|
dest.intVal = (dest.intVal shl destDist) shr destDist
|
||||||
of tyFloat..tyFloat64:
|
of tyFloat..tyFloat64:
|
||||||
if dest.kind != rkFloat:
|
if dest.kind != rkFloat:
|
||||||
myreset(dest); dest.kind = rkFloat
|
myreset(dest); dest.kind = rkFloat
|
||||||
|
|
@ -608,7 +618,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
|
||||||
addSon(regs[ra].node, r.copyTree)
|
addSon(regs[ra].node, r.copyTree)
|
||||||
of opcExcl:
|
of opcExcl:
|
||||||
decodeB(rkNode)
|
decodeB(rkNode)
|
||||||
var b = newNodeIT(nkCurly, regs[rb].node.info, regs[rb].node.typ)
|
var b = newNodeIT(nkCurly, regs[ra].node.info, regs[ra].node.typ)
|
||||||
addSon(b, regs[rb].regToNode)
|
addSon(b, regs[rb].regToNode)
|
||||||
var r = diffSets(regs[ra].node, b)
|
var r = diffSets(regs[ra].node, b)
|
||||||
discardSons(regs[ra].node)
|
discardSons(regs[ra].node)
|
||||||
|
|
@ -1190,6 +1200,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
|
||||||
createStr regs[ra]
|
createStr regs[ra]
|
||||||
let a = regs[rb].node
|
let a = regs[rb].node
|
||||||
if a.kind in {nkStrLit..nkTripleStrLit}: regs[ra].node.strVal = a.strVal
|
if a.kind in {nkStrLit..nkTripleStrLit}: regs[ra].node.strVal = a.strVal
|
||||||
|
elif a.kind == nkCommentStmt: regs[ra].node.strVal = a.comment
|
||||||
else: stackTrace(c, tos, pc, errFieldXNotFound, "strVal")
|
else: stackTrace(c, tos, pc, errFieldXNotFound, "strVal")
|
||||||
of opcSlurp:
|
of opcSlurp:
|
||||||
decodeB(rkNode)
|
decodeB(rkNode)
|
||||||
|
|
|
||||||
|
|
@ -70,7 +70,7 @@ proc atomicTypeX(name: string; t: PType; info: TLineInfo): PNode =
|
||||||
proc mapTypeToAst(t: PType, info: TLineInfo; allowRecursion=false): PNode
|
proc mapTypeToAst(t: PType, info: TLineInfo; allowRecursion=false): PNode
|
||||||
|
|
||||||
proc mapTypeToBracket(name: string; t: PType; info: TLineInfo): PNode =
|
proc mapTypeToBracket(name: string; t: PType; info: TLineInfo): PNode =
|
||||||
result = newNodeIT(nkBracketExpr, info, t)
|
result = newNodeIT(nkBracketExpr, if t.n.isNil: info else: t.n.info, t)
|
||||||
result.add atomicTypeX(name, t, info)
|
result.add atomicTypeX(name, t, info)
|
||||||
for i in 0 .. < t.len:
|
for i in 0 .. < t.len:
|
||||||
if t.sons[i] == nil:
|
if t.sons[i] == nil:
|
||||||
|
|
@ -92,19 +92,19 @@ proc mapTypeToAst(t: PType, info: TLineInfo; allowRecursion=false): PNode =
|
||||||
of tyStmt: result = atomicType("stmt")
|
of tyStmt: result = atomicType("stmt")
|
||||||
of tyEmpty: result = atomicType"void"
|
of tyEmpty: result = atomicType"void"
|
||||||
of tyArrayConstr, tyArray:
|
of tyArrayConstr, tyArray:
|
||||||
result = newNodeIT(nkBracketExpr, info, t)
|
result = newNodeIT(nkBracketExpr, if t.n.isNil: info else: t.n.info, t)
|
||||||
result.add atomicType("array")
|
result.add atomicType("array")
|
||||||
result.add mapTypeToAst(t.sons[0], info)
|
result.add mapTypeToAst(t.sons[0], info)
|
||||||
result.add mapTypeToAst(t.sons[1], info)
|
result.add mapTypeToAst(t.sons[1], info)
|
||||||
of tyTypeDesc:
|
of tyTypeDesc:
|
||||||
if t.base != nil:
|
if t.base != nil:
|
||||||
result = newNodeIT(nkBracketExpr, info, t)
|
result = newNodeIT(nkBracketExpr, if t.n.isNil: info else: t.n.info, t)
|
||||||
result.add atomicType("typeDesc")
|
result.add atomicType("typeDesc")
|
||||||
result.add mapTypeToAst(t.base, info)
|
result.add mapTypeToAst(t.base, info)
|
||||||
else:
|
else:
|
||||||
result = atomicType"typeDesc"
|
result = atomicType"typeDesc"
|
||||||
of tyGenericInvocation:
|
of tyGenericInvocation:
|
||||||
result = newNodeIT(nkBracketExpr, info, t)
|
result = newNodeIT(nkBracketExpr, if t.n.isNil: info else: t.n.info, t)
|
||||||
for i in 0 .. < t.len:
|
for i in 0 .. < t.len:
|
||||||
result.add mapTypeToAst(t.sons[i], info)
|
result.add mapTypeToAst(t.sons[i], info)
|
||||||
of tyGenericInst, tyGenericBody, tyOrdinal, tyUserTypeClassInst:
|
of tyGenericInst, tyGenericBody, tyOrdinal, tyUserTypeClassInst:
|
||||||
|
|
@ -117,7 +117,7 @@ proc mapTypeToAst(t: PType, info: TLineInfo; allowRecursion=false): PNode =
|
||||||
of tyGenericParam, tyForward: result = atomicType(t.sym.name.s)
|
of tyGenericParam, tyForward: result = atomicType(t.sym.name.s)
|
||||||
of tyObject:
|
of tyObject:
|
||||||
if allowRecursion:
|
if allowRecursion:
|
||||||
result = newNodeIT(nkObjectTy, info, t)
|
result = newNodeIT(nkObjectTy, if t.n.isNil: info else: t.n.info, t)
|
||||||
if t.sons[0] == nil:
|
if t.sons[0] == nil:
|
||||||
result.add ast.emptyNode
|
result.add ast.emptyNode
|
||||||
else:
|
else:
|
||||||
|
|
@ -126,7 +126,7 @@ proc mapTypeToAst(t: PType, info: TLineInfo; allowRecursion=false): PNode =
|
||||||
else:
|
else:
|
||||||
result = atomicType(t.sym.name.s)
|
result = atomicType(t.sym.name.s)
|
||||||
of tyEnum:
|
of tyEnum:
|
||||||
result = newNodeIT(nkEnumTy, info, t)
|
result = newNodeIT(nkEnumTy, if t.n.isNil: info else: t.n.info, t)
|
||||||
result.add copyTree(t.n)
|
result.add copyTree(t.n)
|
||||||
of tyTuple: result = mapTypeToBracket("tuple", t, info)
|
of tyTuple: result = mapTypeToBracket("tuple", t, info)
|
||||||
of tySet: result = mapTypeToBracket("set", t, info)
|
of tySet: result = mapTypeToBracket("set", t, info)
|
||||||
|
|
@ -137,7 +137,7 @@ proc mapTypeToAst(t: PType, info: TLineInfo; allowRecursion=false): PNode =
|
||||||
of tyProc: result = mapTypeToBracket("proc", t, info)
|
of tyProc: result = mapTypeToBracket("proc", t, info)
|
||||||
of tyOpenArray: result = mapTypeToBracket("openArray", t, info)
|
of tyOpenArray: result = mapTypeToBracket("openArray", t, info)
|
||||||
of tyRange:
|
of tyRange:
|
||||||
result = newNodeIT(nkBracketExpr, info, t)
|
result = newNodeIT(nkBracketExpr, if t.n.isNil: info else: t.n.info, t)
|
||||||
result.add atomicType("range")
|
result.add atomicType("range")
|
||||||
result.add t.n.sons[0].copyTree
|
result.add t.n.sons[0].copyTree
|
||||||
result.add t.n.sons[1].copyTree
|
result.add t.n.sons[1].copyTree
|
||||||
|
|
@ -174,7 +174,7 @@ proc mapTypeToAst(t: PType, info: TLineInfo; allowRecursion=false): PNode =
|
||||||
of tyNot: result = mapTypeToBracket("not", t, info)
|
of tyNot: result = mapTypeToBracket("not", t, info)
|
||||||
of tyAnything: result = atomicType"anything"
|
of tyAnything: result = atomicType"anything"
|
||||||
of tyStatic, tyFromExpr, tyFieldAccessor:
|
of tyStatic, tyFromExpr, tyFieldAccessor:
|
||||||
result = newNodeIT(nkBracketExpr, info, t)
|
result = newNodeIT(nkBracketExpr, if t.n.isNil: info else: t.n.info, t)
|
||||||
result.add atomicType("static")
|
result.add atomicType("static")
|
||||||
if t.n != nil:
|
if t.n != nil:
|
||||||
result.add t.n.copyTree
|
result.add t.n.copyTree
|
||||||
|
|
|
||||||
|
|
@ -1209,7 +1209,7 @@ proc checkCanEval(c: PCtx; n: PNode) =
|
||||||
not s.isOwnedBy(c.prc.sym) and s.owner != c.module and c.mode != emRepl:
|
not s.isOwnedBy(c.prc.sym) and s.owner != c.module and c.mode != emRepl:
|
||||||
cannotEval(n)
|
cannotEval(n)
|
||||||
elif s.kind in {skProc, skConverter, skMethod,
|
elif s.kind in {skProc, skConverter, skMethod,
|
||||||
skIterator, skClosureIterator} and sfForward in s.flags:
|
skIterator} and sfForward in s.flags:
|
||||||
cannotEval(n)
|
cannotEval(n)
|
||||||
|
|
||||||
proc isTemp(c: PCtx; dest: TDest): bool =
|
proc isTemp(c: PCtx; dest: TDest): bool =
|
||||||
|
|
@ -1604,7 +1604,8 @@ proc matches(s: PSym; x: string): bool =
|
||||||
var s = s
|
var s = s
|
||||||
var L = y.len-1
|
var L = y.len-1
|
||||||
while L >= 0:
|
while L >= 0:
|
||||||
if s == nil or y[L].cmpIgnoreStyle(s.name.s) != 0: return false
|
if s == nil or (y[L].cmpIgnoreStyle(s.name.s) != 0 and y[L] != "*"):
|
||||||
|
return false
|
||||||
s = s.owner
|
s = s.owner
|
||||||
dec L
|
dec L
|
||||||
result = true
|
result = true
|
||||||
|
|
@ -1613,7 +1614,8 @@ proc matches(s: PSym; y: varargs[string]): bool =
|
||||||
var s = s
|
var s = s
|
||||||
var L = y.len-1
|
var L = y.len-1
|
||||||
while L >= 0:
|
while L >= 0:
|
||||||
if s == nil or y[L].cmpIgnoreStyle(s.name.s) != 0: return false
|
if s == nil or (y[L].cmpIgnoreStyle(s.name.s) != 0 and y[L] != "*"):
|
||||||
|
return false
|
||||||
s = if sfFromGeneric in s.flags: s.owner.owner else: s.owner
|
s = if sfFromGeneric in s.flags: s.owner.owner else: s.owner
|
||||||
dec L
|
dec L
|
||||||
result = true
|
result = true
|
||||||
|
|
@ -1636,7 +1638,7 @@ proc gen(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags = {}) =
|
||||||
case s.kind
|
case s.kind
|
||||||
of skVar, skForVar, skTemp, skLet, skParam, skResult:
|
of skVar, skForVar, skTemp, skLet, skParam, skResult:
|
||||||
genRdVar(c, n, dest, flags)
|
genRdVar(c, n, dest, flags)
|
||||||
of skProc, skConverter, skMacro, skTemplate, skMethod, skIterators:
|
of skProc, skConverter, skMacro, skTemplate, skMethod, skIterator:
|
||||||
# 'skTemplate' is only allowed for 'getAst' support:
|
# 'skTemplate' is only allowed for 'getAst' support:
|
||||||
if procIsCallback(c, s): discard
|
if procIsCallback(c, s): discard
|
||||||
elif sfImportc in s.flags: c.importcSym(n.info, s)
|
elif sfImportc in s.flags: c.importcSym(n.info, s)
|
||||||
|
|
|
||||||
|
|
@ -55,9 +55,16 @@ template getX(k, field) {.immediate, dirty.} =
|
||||||
result = s[i+a.rb+1].field
|
result = s[i+a.rb+1].field
|
||||||
|
|
||||||
proc getInt*(a: VmArgs; i: Natural): BiggestInt = getX(rkInt, intVal)
|
proc getInt*(a: VmArgs; i: Natural): BiggestInt = getX(rkInt, intVal)
|
||||||
|
proc getBool*(a: VmArgs; i: Natural): bool = getInt(a, i) != 0
|
||||||
proc getFloat*(a: VmArgs; i: Natural): BiggestFloat = getX(rkFloat, floatVal)
|
proc getFloat*(a: VmArgs; i: Natural): BiggestFloat = getX(rkFloat, floatVal)
|
||||||
proc getString*(a: VmArgs; i: Natural): string =
|
proc getString*(a: VmArgs; i: Natural): string =
|
||||||
doAssert i < a.rc-1
|
doAssert i < a.rc-1
|
||||||
let s = cast[seq[TFullReg]](a.slots)
|
let s = cast[seq[TFullReg]](a.slots)
|
||||||
doAssert s[i+a.rb+1].kind == rkNode
|
doAssert s[i+a.rb+1].kind == rkNode
|
||||||
result = s[i+a.rb+1].node.strVal
|
result = s[i+a.rb+1].node.strVal
|
||||||
|
|
||||||
|
proc getNode*(a: VmArgs; i: Natural): PNode =
|
||||||
|
doAssert i < a.rc-1
|
||||||
|
let s = cast[seq[TFullReg]](a.slots)
|
||||||
|
doAssert s[i+a.rb+1].kind == rkNode
|
||||||
|
result = s[i+a.rb+1].node
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,7 @@ from math import sqrt, ln, log10, log2, exp, round, arccos, arcsin,
|
||||||
arctan, arctan2, cos, cosh, hypot, sinh, sin, tan, tanh, pow, trunc,
|
arctan, arctan2, cos, cosh, hypot, sinh, sin, tan, tanh, pow, trunc,
|
||||||
floor, ceil, fmod
|
floor, ceil, fmod
|
||||||
|
|
||||||
from os import getEnv, existsEnv, dirExists, fileExists
|
from os import getEnv, existsEnv, dirExists, fileExists, walkDir
|
||||||
|
|
||||||
template mathop(op) {.immediate, dirty.} =
|
template mathop(op) {.immediate, dirty.} =
|
||||||
registerCallback(c, "stdlib.math." & astToStr(op), `op Wrapper`)
|
registerCallback(c, "stdlib.math." & astToStr(op), `op Wrapper`)
|
||||||
|
|
@ -48,6 +48,12 @@ proc getCurrentExceptionMsgWrapper(a: VmArgs) {.nimcall.} =
|
||||||
setResult(a, if a.currentException.isNil: ""
|
setResult(a, if a.currentException.isNil: ""
|
||||||
else: a.currentException.sons[3].skipColon.strVal)
|
else: a.currentException.sons[3].skipColon.strVal)
|
||||||
|
|
||||||
|
proc staticWalkDirImpl(path: string, relative: bool): PNode =
|
||||||
|
result = newNode(nkBracket)
|
||||||
|
for k, f in walkDir(path, relative):
|
||||||
|
result.add newTree(nkPar, newIntNode(nkIntLit, k.ord),
|
||||||
|
newStrNode(nkStrLit, f))
|
||||||
|
|
||||||
proc registerAdditionalOps*(c: PCtx) =
|
proc registerAdditionalOps*(c: PCtx) =
|
||||||
wrap1f(sqrt)
|
wrap1f(sqrt)
|
||||||
wrap1f(ln)
|
wrap1f(ln)
|
||||||
|
|
@ -78,3 +84,5 @@ proc registerAdditionalOps*(c: PCtx) =
|
||||||
wrap1s(fileExists)
|
wrap1s(fileExists)
|
||||||
wrap2svoid(writeFile)
|
wrap2svoid(writeFile)
|
||||||
systemop getCurrentExceptionMsg
|
systemop getCurrentExceptionMsg
|
||||||
|
registerCallback c, "stdlib.*.staticWalkDir", proc (a: VmArgs) {.nimcall.} =
|
||||||
|
setResult(a, staticWalkDirImpl(getString(a, 0), getBool(a, 1)))
|
||||||
|
|
|
||||||
|
|
@ -102,8 +102,8 @@ doc.file = """<?xml version="1.0" encoding="utf-8" ?>
|
||||||
<link rel="shortcut icon" href="data:image/x-icon;base64,AAABAAEAEBAAAAEAIABoBAAAFgAAACgAAAAQAAAAIAAAAAEAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AAAAAAUAAAAF////AP///wD///8A////AP///wD///8A////AP///wD///8A////AAAAAAIAAABbAAAAlQAAAKIAAACbAAAAmwAAAKIAAACVAAAAWwAAAAL///8A////AP///wD///8A////AAAAABQAAADAAAAAYwAAAA3///8A////AP///wD///8AAAAADQAAAGMAAADAAAAAFP///wD///8A////AP///wAAAACdAAAAOv///wD///8A////AP///wD///8A////AP///wD///8AAAAAOgAAAJ3///8A////AP///wAAAAAnAAAAcP///wAAAAAoAAAASv///wD///8A////AP///wAAAABKAAAAKP///wAAAABwAAAAJ////wD///8AAAAAgQAAABwAAACIAAAAkAAAAJMAAACtAAAAFQAAABUAAACtAAAAkwAAAJAAAACIAAAAHAAAAIH///8A////AAAAAKQAAACrAAAAaP///wD///8AAAAARQAAANIAAADSAAAARf///wD///8AAAAAaAAAAKsAAACk////AAAAADMAAACcAAAAnQAAABj///8A////AP///wAAAAAYAAAAGP///wD///8A////AAAAABgAAACdAAAAnAAAADMAAAB1AAAAwwAAAP8AAADpAAAAsQAAAE4AAAAb////AP///wAAAAAbAAAATgAAALEAAADpAAAA/wAAAMMAAAB1AAAAtwAAAOkAAAD/AAAA/wAAAP8AAADvAAAA3gAAAN4AAADeAAAA3gAAAO8AAAD/AAAA/wAAAP8AAADpAAAAtwAAAGUAAAA/AAAA3wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAADfAAAAPwAAAGX///8A////AAAAAEgAAADtAAAAvwAAAL0AAADGAAAA7wAAAO8AAADGAAAAvQAAAL8AAADtAAAASP///wD///8A////AP///wD///8AAAAAO////wD///8A////AAAAAIcAAACH////AP///wD///8AAAAAO////wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A//8AAP//AAD4HwAA7/cAAN/7AAD//wAAoYUAAJ55AACf+QAAh+EAAAAAAADAAwAA4AcAAP5/AAD//wAA//8AAA=="/>
|
<link rel="shortcut icon" href="data:image/x-icon;base64,AAABAAEAEBAAAAEAIABoBAAAFgAAACgAAAAQAAAAIAAAAAEAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AAAAAAUAAAAF////AP///wD///8A////AP///wD///8A////AP///wD///8A////AAAAAAIAAABbAAAAlQAAAKIAAACbAAAAmwAAAKIAAACVAAAAWwAAAAL///8A////AP///wD///8A////AAAAABQAAADAAAAAYwAAAA3///8A////AP///wD///8AAAAADQAAAGMAAADAAAAAFP///wD///8A////AP///wAAAACdAAAAOv///wD///8A////AP///wD///8A////AP///wD///8AAAAAOgAAAJ3///8A////AP///wAAAAAnAAAAcP///wAAAAAoAAAASv///wD///8A////AP///wAAAABKAAAAKP///wAAAABwAAAAJ////wD///8AAAAAgQAAABwAAACIAAAAkAAAAJMAAACtAAAAFQAAABUAAACtAAAAkwAAAJAAAACIAAAAHAAAAIH///8A////AAAAAKQAAACrAAAAaP///wD///8AAAAARQAAANIAAADSAAAARf///wD///8AAAAAaAAAAKsAAACk////AAAAADMAAACcAAAAnQAAABj///8A////AP///wAAAAAYAAAAGP///wD///8A////AAAAABgAAACdAAAAnAAAADMAAAB1AAAAwwAAAP8AAADpAAAAsQAAAE4AAAAb////AP///wAAAAAbAAAATgAAALEAAADpAAAA/wAAAMMAAAB1AAAAtwAAAOkAAAD/AAAA/wAAAP8AAADvAAAA3gAAAN4AAADeAAAA3gAAAO8AAAD/AAAA/wAAAP8AAADpAAAAtwAAAGUAAAA/AAAA3wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAADfAAAAPwAAAGX///8A////AAAAAEgAAADtAAAAvwAAAL0AAADGAAAA7wAAAO8AAADGAAAAvQAAAL8AAADtAAAASP///wD///8A////AP///wD///8AAAAAO////wD///8A////AAAAAIcAAACH////AP///wD///8AAAAAO////wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A//8AAP//AAD4HwAA7/cAAN/7AAD//wAAoYUAAJ55AACf+QAAh+EAAAAAAADAAwAA4AcAAP5/AAD//wAA//8AAA=="/>
|
||||||
|
|
||||||
<!-- Google fonts -->
|
<!-- Google fonts -->
|
||||||
<link href='http://fonts.googleapis.com/css?family=Raleway:400,600,900' rel='stylesheet' type='text/css'>
|
<link href='http://fonts.googleapis.com/css?family=Raleway:400,600,900' rel='stylesheet' type='text/css'/>
|
||||||
<link href='http://fonts.googleapis.com/css?family=Source+Code+Pro:400,500,600' rel='stylesheet' type='text/css'>
|
<link href='http://fonts.googleapis.com/css?family=Source+Code+Pro:400,500,600' rel='stylesheet' type='text/css'/>
|
||||||
|
|
||||||
<!-- CSS -->
|
<!-- CSS -->
|
||||||
<title>$title</title>
|
<title>$title</title>
|
||||||
|
|
@ -1246,7 +1246,7 @@ dt pre > span.Operator ~ span.Identifier, dt pre > span.Operator ~ span.Operator
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="twelve-columns footer">
|
<div class="twelve-columns footer">
|
||||||
<span class="nim-sprite"></span>
|
<span class="nim-sprite"></span>
|
||||||
<br>
|
<br/>
|
||||||
<small>Made with Nim. Generated: $date $time UTC</small>
|
<small>Made with Nim. Generated: $date $time UTC</small>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -924,9 +924,11 @@ AST:
|
||||||
|
|
||||||
.. code-block:: nim
|
.. code-block:: nim
|
||||||
nnkLetSection(
|
nnkLetSection(
|
||||||
nnkIdentDefs(!"v"),
|
nnkIdentDefs(
|
||||||
nnkEmpty(), # for the type
|
nnkIdent(!"a"),
|
||||||
nnkIntLit(3)
|
nnkEmpty(), # or nnkIdent(...) for the type
|
||||||
|
nnkIntLit(3),
|
||||||
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
Const section
|
Const section
|
||||||
|
|
|
||||||
|
|
@ -35,10 +35,13 @@ castExpr = 'cast' '[' optInd typeDesc optPar ']' '(' optInd expr optPar ')'
|
||||||
parKeyw = 'discard' | 'include' | 'if' | 'while' | 'case' | 'try'
|
parKeyw = 'discard' | 'include' | 'if' | 'while' | 'case' | 'try'
|
||||||
| 'finally' | 'except' | 'for' | 'block' | 'const' | 'let'
|
| 'finally' | 'except' | 'for' | 'block' | 'const' | 'let'
|
||||||
| 'when' | 'var' | 'mixin'
|
| 'when' | 'var' | 'mixin'
|
||||||
par = '(' optInd (&parKeyw complexOrSimpleStmt ^+ ';'
|
par = '(' optInd
|
||||||
| simpleExpr ('=' expr (';' complexOrSimpleStmt ^+ ';' )? )?
|
( &parKeyw complexOrSimpleStmt ^+ ';'
|
||||||
| (':' expr)? (',' (exprColonEqExpr comma?)*)? )?
|
| ';' complexOrSimpleStmt ^+ ';'
|
||||||
optPar ')'
|
| pragmaStmt
|
||||||
|
| simpleExpr ( ('=' expr (';' complexOrSimpleStmt ^+ ';' )? )
|
||||||
|
| (':' expr (',' exprColonEqExpr ^+ ',' )? ) ) )
|
||||||
|
optPar ')'
|
||||||
literal = | INT_LIT | INT8_LIT | INT16_LIT | INT32_LIT | INT64_LIT
|
literal = | INT_LIT | INT8_LIT | INT16_LIT | INT32_LIT | INT64_LIT
|
||||||
| UINT_LIT | UINT8_LIT | UINT16_LIT | UINT32_LIT | UINT64_LIT
|
| UINT_LIT | UINT8_LIT | UINT16_LIT | UINT32_LIT | UINT64_LIT
|
||||||
| FLOAT_LIT | FLOAT32_LIT | FLOAT64_LIT
|
| FLOAT_LIT | FLOAT32_LIT | FLOAT64_LIT
|
||||||
|
|
@ -86,7 +89,7 @@ expr = (ifExpr
|
||||||
| caseExpr
|
| caseExpr
|
||||||
| tryExpr)
|
| tryExpr)
|
||||||
/ simpleExpr
|
/ simpleExpr
|
||||||
typeKeyw = 'var' | 'ref' | 'ptr' | 'shared' | 'tuple'
|
typeKeyw = 'var' | 'out' | 'ref' | 'ptr' | 'shared' | 'tuple'
|
||||||
| 'proc' | 'iterator' | 'distinct' | 'object' | 'enum'
|
| 'proc' | 'iterator' | 'distinct' | 'object' | 'enum'
|
||||||
primary = typeKeyw typeDescK
|
primary = typeKeyw typeDescK
|
||||||
/ prefixOperator* identOrLiteral primarySuffix*
|
/ prefixOperator* identOrLiteral primarySuffix*
|
||||||
|
|
@ -165,7 +168,7 @@ objectCase = 'case' identWithPragma ':' typeDesc ':'? COMMENT?
|
||||||
objectPart = IND{>} objectPart^+IND{=} DED
|
objectPart = IND{>} objectPart^+IND{=} DED
|
||||||
/ objectWhen / objectCase / 'nil' / 'discard' / declColonEquals
|
/ objectWhen / objectCase / 'nil' / 'discard' / declColonEquals
|
||||||
object = 'object' pragma? ('of' typeDesc)? COMMENT? objectPart
|
object = 'object' pragma? ('of' typeDesc)? COMMENT? objectPart
|
||||||
typeClassParam = ('var')? symbol
|
typeClassParam = ('var' | 'out')? symbol
|
||||||
typeClass = typeClassParam ^* ',' (pragma)? ('of' typeDesc ^* ',')?
|
typeClass = typeClassParam ^* ',' (pragma)? ('of' typeDesc ^* ',')?
|
||||||
&IND{>} stmt
|
&IND{>} stmt
|
||||||
typeDef = identWithPragma genericParamList? '=' optInd typeDefAux
|
typeDef = identWithPragma genericParamList? '=' optInd typeDefAux
|
||||||
|
|
|
||||||
30
doc/lib.txt
30
doc/lib.txt
|
|
@ -84,7 +84,7 @@ Collections and algorithms
|
||||||
* `sequtils <sequtils.html>`_
|
* `sequtils <sequtils.html>`_
|
||||||
This module implements operations for the built-in seq type
|
This module implements operations for the built-in seq type
|
||||||
which were inspired by functional programming languages.
|
which were inspired by functional programming languages.
|
||||||
|
|
||||||
|
|
||||||
String handling
|
String handling
|
||||||
---------------
|
---------------
|
||||||
|
|
@ -165,6 +165,8 @@ Generic Operating System Services
|
||||||
This module implements the ability to monitor a directory/file for changes
|
This module implements the ability to monitor a directory/file for changes
|
||||||
using Posix's inotify API.
|
using Posix's inotify API.
|
||||||
|
|
||||||
|
**Warning:** This module will likely be moved out to a Nimble package soon.
|
||||||
|
|
||||||
* `asyncfile <asyncfile.html>`_
|
* `asyncfile <asyncfile.html>`_
|
||||||
This module implements asynchronous file reading and writing using
|
This module implements asynchronous file reading and writing using
|
||||||
``asyncdispatch``.
|
``asyncdispatch``.
|
||||||
|
|
@ -191,6 +193,11 @@ Math libraries
|
||||||
* `basic3d <basic3d.html>`_
|
* `basic3d <basic3d.html>`_
|
||||||
Basic 3d support with vectors, points, matrices and some basic utilities.
|
Basic 3d support with vectors, points, matrices and some basic utilities.
|
||||||
|
|
||||||
|
* `mersenne <mersenne.html>`_
|
||||||
|
Mersenne twister random number generator.
|
||||||
|
|
||||||
|
* `stats <stats.html>`_
|
||||||
|
Statistical analysis
|
||||||
|
|
||||||
Internet Protocols and Support
|
Internet Protocols and Support
|
||||||
------------------------------
|
------------------------------
|
||||||
|
|
@ -209,7 +216,8 @@ Internet Protocols and Support
|
||||||
This module implements a simple HTTP server.
|
This module implements a simple HTTP server.
|
||||||
|
|
||||||
* `httpclient <httpclient.html>`_
|
* `httpclient <httpclient.html>`_
|
||||||
This module implements a simple HTTP client.
|
This module implements a simple HTTP client which supports both synchronous
|
||||||
|
and asynchronous retrieval of web pages.
|
||||||
|
|
||||||
* `smtp <smtp.html>`_
|
* `smtp <smtp.html>`_
|
||||||
This module implement a simple SMTP client.
|
This module implement a simple SMTP client.
|
||||||
|
|
@ -226,19 +234,17 @@ Internet Protocols and Support
|
||||||
* `asyncdispatch <asyncdispatch.html>`_
|
* `asyncdispatch <asyncdispatch.html>`_
|
||||||
This module implements an asynchronous dispatcher for IO operations.
|
This module implements an asynchronous dispatcher for IO operations.
|
||||||
|
|
||||||
**Note:** This module is still largely experimental.
|
|
||||||
|
|
||||||
* `asyncnet <asyncnet.html>`_
|
* `asyncnet <asyncnet.html>`_
|
||||||
This module implements asynchronous sockets based on the ``asyncdispatch``
|
This module implements asynchronous sockets based on the ``asyncdispatch``
|
||||||
module.
|
module.
|
||||||
|
|
||||||
**Note:** This module is still largely experimental.
|
|
||||||
|
|
||||||
* `asynchttpserver <asynchttpserver.html>`_
|
* `asynchttpserver <asynchttpserver.html>`_
|
||||||
This module implements an asynchronous HTTP server using the ``asyncnet``
|
This module implements an asynchronous HTTP server using the ``asyncnet``
|
||||||
module.
|
module.
|
||||||
|
|
||||||
**Note:** This module is still largely experimental.
|
* `asyncftpclient <asyncftpclient.html>`_
|
||||||
|
This module implements an asynchronous FTP client using the ``asyncnet``
|
||||||
|
module.
|
||||||
|
|
||||||
* `net <net.html>`_
|
* `net <net.html>`_
|
||||||
This module implements a high-level sockets API. It will replace the
|
This module implements a high-level sockets API. It will replace the
|
||||||
|
|
@ -346,6 +352,8 @@ Cryptography and Hashing
|
||||||
* `base64 <base64.html>`_
|
* `base64 <base64.html>`_
|
||||||
This module implements a base64 encoder and decoder.
|
This module implements a base64 encoder and decoder.
|
||||||
|
|
||||||
|
* `securehash <securehash.html>`_
|
||||||
|
This module implements a sha1 encoder and decoder.
|
||||||
|
|
||||||
Multimedia support
|
Multimedia support
|
||||||
------------------
|
------------------
|
||||||
|
|
@ -374,10 +382,18 @@ Miscellaneous
|
||||||
* `logging <logging.html>`_
|
* `logging <logging.html>`_
|
||||||
This module implements a simple logger.
|
This module implements a simple logger.
|
||||||
|
|
||||||
|
* `options <options.html>`_
|
||||||
|
Types which encapsulate an optional value.
|
||||||
|
|
||||||
* `future <future.html>`_
|
* `future <future.html>`_
|
||||||
This module implements new experimental features. Currently the syntax
|
This module implements new experimental features. Currently the syntax
|
||||||
sugar for anonymous procedures.
|
sugar for anonymous procedures.
|
||||||
|
|
||||||
|
* `coro <coro.html>`_
|
||||||
|
This module implements experimental coroutines in Nim.
|
||||||
|
|
||||||
|
* `unittest <unittest.html>`_
|
||||||
|
Implements a Unit testing DSL.
|
||||||
|
|
||||||
Modules for JS backend
|
Modules for JS backend
|
||||||
---------------------------
|
---------------------------
|
||||||
|
|
|
||||||
|
|
@ -213,7 +213,7 @@ Concepts are written in the following form:
|
||||||
|
|
||||||
Container[T] = concept c
|
Container[T] = concept c
|
||||||
c.len is Ordinal
|
c.len is Ordinal
|
||||||
items(c) is iterator
|
items(c) is T
|
||||||
for value in c:
|
for value in c:
|
||||||
type(value) is T
|
type(value) is T
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -69,6 +69,34 @@ Documentation comments are tokens; they are only allowed at certain places in
|
||||||
the input file as they belong to the syntax tree!
|
the input file as they belong to the syntax tree!
|
||||||
|
|
||||||
|
|
||||||
|
Multiline comments
|
||||||
|
------------------
|
||||||
|
|
||||||
|
Starting with version 0.13.0 of the language Nim supports multiline comments.
|
||||||
|
They look like:
|
||||||
|
|
||||||
|
.. code-block:: nim
|
||||||
|
#[Comment here.
|
||||||
|
Multiple lines
|
||||||
|
are not a problem.]#
|
||||||
|
|
||||||
|
Multiline comments support nesting:
|
||||||
|
|
||||||
|
.. code-block:: nim
|
||||||
|
#[ #[ Multiline comment in already
|
||||||
|
commented out code. ]#
|
||||||
|
proc p[T](x: T) = discard
|
||||||
|
]#
|
||||||
|
|
||||||
|
Multiline documentation comments look like and support nesting too:
|
||||||
|
|
||||||
|
.. code-block:: nim
|
||||||
|
proc foo =
|
||||||
|
##[Long documentation comment
|
||||||
|
here.
|
||||||
|
]##
|
||||||
|
|
||||||
|
|
||||||
Identifiers & Keywords
|
Identifiers & Keywords
|
||||||
----------------------
|
----------------------
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -152,9 +152,11 @@ In module related statements, if any part of the module name /
|
||||||
path begins with a number, you may have to quote it in double quotes.
|
path begins with a number, you may have to quote it in double quotes.
|
||||||
In the following example, it would be seen as a literal number '3.0' of type
|
In the following example, it would be seen as a literal number '3.0' of type
|
||||||
'float64' if not quoted, if uncertain - quote it:
|
'float64' if not quoted, if uncertain - quote it:
|
||||||
|
|
||||||
.. code-block:: nim
|
.. code-block:: nim
|
||||||
import "gfx/3d/somemodule"
|
import "gfx/3d/somemodule"
|
||||||
|
|
||||||
|
|
||||||
Scope rules
|
Scope rules
|
||||||
-----------
|
-----------
|
||||||
Identifiers are valid from the point of their declaration until the end of
|
Identifiers are valid from the point of their declaration until the end of
|
||||||
|
|
|
||||||
|
|
@ -236,8 +236,6 @@ executable code.
|
||||||
Do notation
|
Do notation
|
||||||
-----------
|
-----------
|
||||||
|
|
||||||
**Note:** The future of the ``do`` notation is uncertain.
|
|
||||||
|
|
||||||
As a special more convenient notation, proc expressions involved in procedure
|
As a special more convenient notation, proc expressions involved in procedure
|
||||||
calls can use the ``do`` keyword:
|
calls can use the ``do`` keyword:
|
||||||
|
|
||||||
|
|
@ -251,10 +249,12 @@ calls can use the ``do`` keyword:
|
||||||
``do`` is written after the parentheses enclosing the regular proc params.
|
``do`` is written after the parentheses enclosing the regular proc params.
|
||||||
The proc expression represented by the do block is appended to them.
|
The proc expression represented by the do block is appended to them.
|
||||||
|
|
||||||
More than one ``do`` block can appear in a single call:
|
``do`` with parentheses is an anonymous ``proc``; however a ``do`` without
|
||||||
|
parentheses is just a block of code. The ``do`` notation can be used to
|
||||||
|
pass multiple blocks to a macro:
|
||||||
|
|
||||||
.. code-block:: nim
|
.. code-block:: nim
|
||||||
proc performWithUndo(task: proc(), undo: proc()) = ...
|
macro performWithUndo(task, undo: untyped) = ...
|
||||||
|
|
||||||
performWithUndo do:
|
performWithUndo do:
|
||||||
# multiple-line block of code
|
# multiple-line block of code
|
||||||
|
|
|
||||||
|
|
@ -64,6 +64,14 @@ Precedence level Operators First charact
|
||||||
================ =============================================== ================== ===============
|
================ =============================================== ================== ===============
|
||||||
|
|
||||||
|
|
||||||
|
Whether an operator is used a prefix operator is also affected by preceeding whitespace (this parsing change was introduced with version 0.13.0):
|
||||||
|
|
||||||
|
.. code-block:: nim
|
||||||
|
echo $foo
|
||||||
|
# is parsed as
|
||||||
|
echo($foo)
|
||||||
|
|
||||||
|
|
||||||
Strong spaces
|
Strong spaces
|
||||||
-------------
|
-------------
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@ The syntax to *invoke* a template is the same as calling a procedure.
|
||||||
Example:
|
Example:
|
||||||
|
|
||||||
.. code-block:: nim
|
.. code-block:: nim
|
||||||
template `!=` (a, b: expr): expr =
|
template `!=` (a, b: untyped): untyped =
|
||||||
# this definition exists in the System module
|
# this definition exists in the System module
|
||||||
not (a == b)
|
not (a == b)
|
||||||
|
|
||||||
|
|
@ -23,50 +23,56 @@ templates:
|
||||||
| ``a in b`` is transformed into ``contains(b, a)``.
|
| ``a in b`` is transformed into ``contains(b, a)``.
|
||||||
| ``notin`` and ``isnot`` have the obvious meanings.
|
| ``notin`` and ``isnot`` have the obvious meanings.
|
||||||
|
|
||||||
The "types" of templates can be the symbols ``expr`` (stands for *expression*),
|
The "types" of templates can be the symbols ``untyped``,
|
||||||
``stmt`` (stands for *statement*) or ``typedesc`` (stands for *type
|
``typed`` or ``typedesc`` (stands for *type
|
||||||
description*). These are "meta types", they can only be used in certain
|
description*). These are "meta types", they can only be used in certain
|
||||||
contexts. Real types can be used too; this implies that expressions are
|
contexts. Real types can be used too; this implies that ``typed`` expressions
|
||||||
expected.
|
are expected.
|
||||||
|
|
||||||
|
|
||||||
Ordinary vs immediate templates
|
Typed vs untyped parameters
|
||||||
-------------------------------
|
---------------------------
|
||||||
|
|
||||||
There are two different kinds of templates: immediate templates and
|
An ``untyped`` parameter means that symbol lookups and type resolution is not
|
||||||
ordinary templates. Ordinary templates take part in overloading resolution. As
|
performed before the expression is passed to the template. This means that for
|
||||||
such their arguments need to be type checked before the template is invoked.
|
example *undeclared* identifiers can be passed to the template:
|
||||||
So ordinary templates cannot receive undeclared identifiers:
|
|
||||||
|
|
||||||
.. code-block:: nim
|
.. code-block:: nim
|
||||||
|
|
||||||
template declareInt(x: expr) =
|
template declareInt(x: untyped) =
|
||||||
var x: int
|
|
||||||
|
|
||||||
declareInt(x) # error: unknown identifier: 'x'
|
|
||||||
|
|
||||||
An ``immediate`` template does not participate in overload resolution and so
|
|
||||||
its arguments are not checked for semantics before invocation. So they can
|
|
||||||
receive undeclared identifiers:
|
|
||||||
|
|
||||||
.. code-block:: nim
|
|
||||||
|
|
||||||
template declareInt(x: expr) {.immediate.} =
|
|
||||||
var x: int
|
var x: int
|
||||||
|
|
||||||
declareInt(x) # valid
|
declareInt(x) # valid
|
||||||
|
x = 3
|
||||||
|
|
||||||
|
|
||||||
|
.. code-block:: nim
|
||||||
|
|
||||||
|
template declareInt(x: typed) =
|
||||||
|
var x: int
|
||||||
|
|
||||||
|
declareInt(x) # invalid, because x has not been declared and so has no type
|
||||||
|
|
||||||
|
A template where every parameter is ``untyped`` is called an `immediate`:idx:
|
||||||
|
template. For historical reasons templates can be explicitly annotated with
|
||||||
|
an ``immediate`` pragma and then these templates do not take part in
|
||||||
|
overloading resolution and the parameters' types are *ignored* by the
|
||||||
|
compiler. Explicit immediate templates are about to be deprecated in later
|
||||||
|
versions of the compiler.
|
||||||
|
|
||||||
|
**Note**: For historical reasons ``stmt`` is an alias for ``typed`` and
|
||||||
|
``expr`` an alias for ``untyped``, but new code should use the newer,
|
||||||
|
clearer names.
|
||||||
|
|
||||||
|
|
||||||
Passing a code block to a template
|
Passing a code block to a template
|
||||||
----------------------------------
|
----------------------------------
|
||||||
|
|
||||||
If there is a ``stmt`` parameter it should be the last in the template
|
You can pass a block of statements as a last parameter to a template via a
|
||||||
declaration, because statements are passed to a template via a
|
|
||||||
special ``:`` syntax:
|
special ``:`` syntax:
|
||||||
|
|
||||||
.. code-block:: nim
|
.. code-block:: nim
|
||||||
|
template withFile(f, fn, mode, actions: untyped): untyped =
|
||||||
template withFile(f, fn, mode: expr, actions: stmt): stmt {.immediate.} =
|
|
||||||
var f: File
|
var f: File
|
||||||
if open(f, fn, mode):
|
if open(f, fn, mode):
|
||||||
try:
|
try:
|
||||||
|
|
@ -84,6 +90,64 @@ In the example the two ``writeLine`` statements are bound to the ``actions``
|
||||||
parameter.
|
parameter.
|
||||||
|
|
||||||
|
|
||||||
|
Usually to pass a block of code to a template the parameter that accepts
|
||||||
|
the block needs to be of type ``untyped``. Because symbol lookups are then
|
||||||
|
delayed until template instantiation time:
|
||||||
|
|
||||||
|
.. code-block:: nim
|
||||||
|
template t(body: typed) =
|
||||||
|
block:
|
||||||
|
body
|
||||||
|
|
||||||
|
t:
|
||||||
|
var i = 1
|
||||||
|
echo i
|
||||||
|
|
||||||
|
t:
|
||||||
|
var i = 2 # fails with 'attempt to redeclare i'
|
||||||
|
echo i
|
||||||
|
|
||||||
|
The above code fails with the mysterious error message that ``i`` has already
|
||||||
|
been declared. The reason for this is that the ``var i = ...`` bodies need to
|
||||||
|
be type-checked before they are passed to the ``body`` parameter and type
|
||||||
|
checking in Nim implies symbol lookups. For the symbol lookups to succeed
|
||||||
|
``i`` needs to be added to the current (i.e. outer) scope. After type checking
|
||||||
|
these additions to the symbol table are not rolled back (for better or worse).
|
||||||
|
The same code works with ``untyped`` as the passed body is not required to be
|
||||||
|
type-checked:
|
||||||
|
|
||||||
|
.. code-block:: nim
|
||||||
|
template t(body: untyped) =
|
||||||
|
block:
|
||||||
|
body
|
||||||
|
|
||||||
|
t:
|
||||||
|
var i = 1
|
||||||
|
echo i
|
||||||
|
|
||||||
|
t:
|
||||||
|
var i = 2 # compiles
|
||||||
|
echo i
|
||||||
|
|
||||||
|
|
||||||
|
Varargs of untyped
|
||||||
|
------------------
|
||||||
|
|
||||||
|
In addition to the ``untyped`` meta-type that prevents type checking there is
|
||||||
|
also ``varargs[untyped]`` so that not even the number of parameters is fixed:
|
||||||
|
|
||||||
|
.. code-block:: nim
|
||||||
|
template hideIdentifiers(x: varargs[untyped]) = discard
|
||||||
|
|
||||||
|
hideIdentifiers(undeclared1, undeclared2)
|
||||||
|
|
||||||
|
However, since a template cannot iterate over varargs, this feature is
|
||||||
|
generally much more useful for macros.
|
||||||
|
|
||||||
|
**Note**: For historical reasons ``varargs[expr]`` is not equivalent
|
||||||
|
to ``varargs[untyped]``.
|
||||||
|
|
||||||
|
|
||||||
Symbol binding in templates
|
Symbol binding in templates
|
||||||
---------------------------
|
---------------------------
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ Tools available with Nim
|
||||||
|
|
||||||
The standard distribution ships with the following tools:
|
The standard distribution ships with the following tools:
|
||||||
|
|
||||||
- | `Documentation generator <docs/docgen.html>`_
|
- | `Documentation generator <docgen.html>`_
|
||||||
| The builtin document generator ``nim doc2`` generates HTML documentation
|
| The builtin document generator ``nim doc2`` generates HTML documentation
|
||||||
from ``.nim`` source files.
|
from ``.nim`` source files.
|
||||||
|
|
||||||
|
|
|
||||||
23
doc/tut1.txt
23
doc/tut1.txt
|
|
@ -758,19 +758,18 @@ However, this cannot be done for mutually recursive procedures:
|
||||||
# forward declaration:
|
# forward declaration:
|
||||||
proc even(n: int): bool
|
proc even(n: int): bool
|
||||||
|
|
||||||
proc even(n: int): bool
|
.. code-block:: nim
|
||||||
|
proc odd(n: int): bool =
|
||||||
|
assert(n >= 0) # makes sure we don't run into negative recursion
|
||||||
|
if n == 0: false
|
||||||
|
else:
|
||||||
|
n == 1 or even(n-1)
|
||||||
|
|
||||||
proc odd(n: int): bool =
|
proc even(n: int): bool =
|
||||||
assert(n >= 0) # makes sure we don't run into negative recursion
|
assert(n >= 0) # makes sure we don't run into negative recursion
|
||||||
if n == 0: false
|
if n == 1: false
|
||||||
else:
|
else:
|
||||||
n == 1 or even(n-1)
|
n == 0 or odd(n-1)
|
||||||
|
|
||||||
proc even(n: int): bool =
|
|
||||||
assert(n >= 0) # makes sure we don't run into negative recursion
|
|
||||||
if n == 1: false
|
|
||||||
else:
|
|
||||||
n == 0 or odd(n-1)
|
|
||||||
|
|
||||||
Here ``odd`` depends on ``even`` and vice versa. Thus ``even`` needs to be
|
Here ``odd`` depends on ``even`` and vice versa. Thus ``even`` needs to be
|
||||||
introduced to the compiler before it is completely defined. The syntax for
|
introduced to the compiler before it is completely defined. The syntax for
|
||||||
|
|
|
||||||
22
doc/tut2.txt
22
doc/tut2.txt
|
|
@ -112,7 +112,7 @@ Example:
|
||||||
Sym = object # a symbol
|
Sym = object # a symbol
|
||||||
name: string # the symbol's name
|
name: string # the symbol's name
|
||||||
line: int # the line the symbol was declared in
|
line: int # the line the symbol was declared in
|
||||||
code: PNode # the symbol's abstract syntax tree
|
code: Node # the symbol's abstract syntax tree
|
||||||
|
|
||||||
|
|
||||||
Type conversions
|
Type conversions
|
||||||
|
|
@ -162,11 +162,11 @@ An example:
|
||||||
of nkFloat: floatVal: float
|
of nkFloat: floatVal: float
|
||||||
of nkString: strVal: string
|
of nkString: strVal: string
|
||||||
of nkAdd, nkSub:
|
of nkAdd, nkSub:
|
||||||
leftOp, rightOp: PNode
|
leftOp, rightOp: Node
|
||||||
of nkIf:
|
of nkIf:
|
||||||
condition, thenPart, elsePart: PNode
|
condition, thenPart, elsePart: Node
|
||||||
|
|
||||||
var n = PNode(kind: nkFloat, floatVal: 1.0)
|
var n = Node(kind: nkFloat, floatVal: 1.0)
|
||||||
# the following statement raises an `FieldError` exception, because
|
# the following statement raises an `FieldError` exception, because
|
||||||
# n.kind's value does not fit:
|
# n.kind's value does not fit:
|
||||||
n.strVal = ""
|
n.strVal = ""
|
||||||
|
|
@ -990,3 +990,17 @@ generated by `treeRepr <macros.html#treeRepr>`_. If at the end of the this
|
||||||
example you add ``echo treeRepr(result)`` you should get the same output as
|
example you add ``echo treeRepr(result)`` you should get the same output as
|
||||||
using the ``dumpTree`` macro, but of course you can call that at any point of
|
using the ``dumpTree`` macro, but of course you can call that at any point of
|
||||||
the macro where you might be having troubles.
|
the macro where you might be having troubles.
|
||||||
|
|
||||||
|
|
||||||
|
Compilation to JavaScript
|
||||||
|
=========================
|
||||||
|
|
||||||
|
Nim code can be compiled to JavaScript. However in order to write
|
||||||
|
JavaScript-compatible code you should remember the following:
|
||||||
|
- ``addr`` and ``ptr`` have slightly different semantic meaning in JavaScript.
|
||||||
|
It is recommended to avoid those if you're not sure how they are translated
|
||||||
|
to JavaScript.
|
||||||
|
- ``cast[T](x)`` in JavaScript is translated to ``(x)``.
|
||||||
|
- ``cstring`` in JavaScript means JavaScript string. It is a good practice to
|
||||||
|
use ``cstring`` only when it is semantically appropriate. E.g. don't use
|
||||||
|
``cstring`` as a binary data buffer.
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@
|
||||||
# the standard deviation of its columns.
|
# the standard deviation of its columns.
|
||||||
# The CSV file can have a header which is then used for the output.
|
# The CSV file can have a header which is then used for the output.
|
||||||
|
|
||||||
import os, streams, parsecsv, strutils, math
|
import os, streams, parsecsv, strutils, math, stats
|
||||||
|
|
||||||
if paramCount() < 1:
|
if paramCount() < 1:
|
||||||
quit("Usage: statcsv filename[.csv]")
|
quit("Usage: statcsv filename[.csv]")
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,6 @@
|
||||||
|
|
||||||
|
import ospaths
|
||||||
|
|
||||||
mode = ScriptMode.Verbose
|
mode = ScriptMode.Verbose
|
||||||
|
|
||||||
var id = 0
|
var id = 0
|
||||||
|
|
@ -10,4 +12,8 @@ exec "git clone https://github.com/nim-lang/nimble.git nimble" & $id
|
||||||
withDir "nimble" & $id & "/src":
|
withDir "nimble" & $id & "/src":
|
||||||
exec "nim c nimble"
|
exec "nim c nimble"
|
||||||
|
|
||||||
|
mkDir "bin/nimblepkg"
|
||||||
|
for file in listFiles("nimble" & $id & "/src/nimblepkg/"):
|
||||||
|
cpFile file, "bin/nimblepkg/" & file.extractFilename
|
||||||
|
|
||||||
mvFile "nimble" & $id & "/src/nimble".toExe, "bin/nimble".toExe
|
mvFile "nimble" & $id & "/src/nimble".toExe, "bin/nimble".toExe
|
||||||
|
|
|
||||||
|
|
@ -97,7 +97,7 @@ type
|
||||||
nskUnknown, nskConditional, nskDynLib, nskParam,
|
nskUnknown, nskConditional, nskDynLib, nskParam,
|
||||||
nskGenericParam, nskTemp, nskModule, nskType, nskVar, nskLet,
|
nskGenericParam, nskTemp, nskModule, nskType, nskVar, nskLet,
|
||||||
nskConst, nskResult,
|
nskConst, nskResult,
|
||||||
nskProc, nskMethod, nskIterator, nskClosureIterator,
|
nskProc, nskMethod, nskIterator,
|
||||||
nskConverter, nskMacro, nskTemplate, nskField,
|
nskConverter, nskMacro, nskTemplate, nskField,
|
||||||
nskEnumField, nskForVar, nskLabel,
|
nskEnumField, nskForVar, nskLabel,
|
||||||
nskStub
|
nskStub
|
||||||
|
|
@ -416,8 +416,7 @@ proc newLit*(i: BiggestInt): NimNode {.compileTime.} =
|
||||||
|
|
||||||
proc newLit*(b: bool): NimNode {.compileTime.} =
|
proc newLit*(b: bool): NimNode {.compileTime.} =
|
||||||
## produces a new boolean literal node.
|
## produces a new boolean literal node.
|
||||||
result = newNimNode(nnkIntLit)
|
result = if b: bindSym"true" else: bindSym"false"
|
||||||
result.intVal = ord(b)
|
|
||||||
|
|
||||||
proc newLit*(f: BiggestFloat): NimNode {.compileTime.} =
|
proc newLit*(f: BiggestFloat): NimNode {.compileTime.} =
|
||||||
## produces a new float literal node.
|
## produces a new float literal node.
|
||||||
|
|
|
||||||
|
|
@ -61,7 +61,10 @@ type
|
||||||
## wrapped value and **must not** live longer than
|
## wrapped value and **must not** live longer than
|
||||||
## its wrapped value.
|
## its wrapped value.
|
||||||
value: pointer
|
value: pointer
|
||||||
rawType: PNimType
|
when defined(js):
|
||||||
|
rawType: PNimType
|
||||||
|
else:
|
||||||
|
rawTypePtr: pointer
|
||||||
|
|
||||||
ppointer = ptr pointer
|
ppointer = ptr pointer
|
||||||
pbyteArray = ptr array[0.. 0xffff, int8]
|
pbyteArray = ptr array[0.. 0xffff, int8]
|
||||||
|
|
@ -71,6 +74,14 @@ type
|
||||||
when defined(gogc):
|
when defined(gogc):
|
||||||
elemSize: int
|
elemSize: int
|
||||||
PGenSeq = ptr TGenericSeq
|
PGenSeq = ptr TGenericSeq
|
||||||
|
|
||||||
|
when not defined(js):
|
||||||
|
template rawType(x: Any): PNimType =
|
||||||
|
cast[PNimType](x.rawTypePtr)
|
||||||
|
|
||||||
|
template `rawType=`(x: var Any, p: PNimType) =
|
||||||
|
x.rawTypePtr = cast[pointer](p)
|
||||||
|
|
||||||
{.deprecated: [TAny: Any, TAnyKind: AnyKind].}
|
{.deprecated: [TAny: Any, TAnyKind: AnyKind].}
|
||||||
|
|
||||||
when defined(gogc):
|
when defined(gogc):
|
||||||
|
|
@ -108,7 +119,7 @@ proc selectBranch(aa: pointer, n: ptr TNimNode): ptr TNimNode =
|
||||||
else:
|
else:
|
||||||
result = n.sons[n.len]
|
result = n.sons[n.len]
|
||||||
|
|
||||||
proc newAny(value: pointer, rawType: PNimType): Any =
|
proc newAny(value: pointer, rawType: PNimType): Any {.inline.} =
|
||||||
result.value = value
|
result.value = value
|
||||||
result.rawType = rawType
|
result.rawType = rawType
|
||||||
|
|
||||||
|
|
@ -126,8 +137,7 @@ proc toAny*[T](x: var T): Any {.inline.} =
|
||||||
## constructs a ``Any`` object from `x`. This captures `x`'s address, so
|
## constructs a ``Any`` object from `x`. This captures `x`'s address, so
|
||||||
## `x` can be modified with its ``Any`` wrapper! The client needs to ensure
|
## `x` can be modified with its ``Any`` wrapper! The client needs to ensure
|
||||||
## that the wrapper **does not** live longer than `x`!
|
## that the wrapper **does not** live longer than `x`!
|
||||||
result.value = addr(x)
|
newAny(addr(x), cast[PNimType](getTypeInfo(x)))
|
||||||
result.rawType = cast[PNimType](getTypeInfo(x))
|
|
||||||
|
|
||||||
proc kind*(x: Any): AnyKind {.inline.} =
|
proc kind*(x: Any): AnyKind {.inline.} =
|
||||||
## get the type kind
|
## get the type kind
|
||||||
|
|
@ -345,7 +355,7 @@ proc `[]`*(x: Any, fieldName: string): Any =
|
||||||
result.value = x.value +!! n.offset
|
result.value = x.value +!! n.offset
|
||||||
result.rawType = n.typ
|
result.rawType = n.typ
|
||||||
elif x.rawType.kind == tyObject and x.rawType.base != nil:
|
elif x.rawType.kind == tyObject and x.rawType.base != nil:
|
||||||
return `[]`(Any(value: x.value, rawType: x.rawType.base), fieldName)
|
return `[]`(newAny(x.value, x.rawType.base), fieldName)
|
||||||
else:
|
else:
|
||||||
raise newException(ValueError, "invalid field name: " & fieldName)
|
raise newException(ValueError, "invalid field name: " & fieldName)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
#
|
#
|
||||||
#
|
#
|
||||||
# Nim's Runtime Library
|
# Nim's Runtime Library
|
||||||
# (c) Copyright 2012 Andreas Rumpf
|
# (c) Copyright 2015 Andreas Rumpf
|
||||||
#
|
#
|
||||||
# See the file "copying.txt", included in this
|
# See the file "copying.txt", included in this
|
||||||
# distribution, for details about the copyright.
|
# distribution, for details about the copyright.
|
||||||
|
|
@ -10,7 +10,49 @@
|
||||||
## A higher level `mySQL`:idx: database wrapper. The same interface is
|
## A higher level `mySQL`:idx: database wrapper. The same interface is
|
||||||
## implemented for other databases too.
|
## implemented for other databases too.
|
||||||
##
|
##
|
||||||
## Example:
|
## See also: `db_odbc <db_odbc.html>`_, `db_sqlite <db_sqlite.html>`_,
|
||||||
|
## `db_postgres <db_postgres.html>`_.
|
||||||
|
##
|
||||||
|
## Parameter substitution
|
||||||
|
## ----------------------
|
||||||
|
##
|
||||||
|
## All ``db_*`` modules support the same form of parameter substitution.
|
||||||
|
## That is, using the ``?`` (question mark) to signify the place where a
|
||||||
|
## value should be placed. For example:
|
||||||
|
##
|
||||||
|
## .. code-block:: Nim
|
||||||
|
## sql"INSERT INTO myTable (colA, colB, colC) VALUES (?, ?, ?)"
|
||||||
|
##
|
||||||
|
##
|
||||||
|
## Examples
|
||||||
|
## --------
|
||||||
|
##
|
||||||
|
## Opening a connection to a database
|
||||||
|
## ==================================
|
||||||
|
##
|
||||||
|
## .. code-block:: Nim
|
||||||
|
## import db_mysql
|
||||||
|
## let db = open("localhost", "user", "password", "dbname")
|
||||||
|
## db.close()
|
||||||
|
##
|
||||||
|
## Creating a table
|
||||||
|
## ================
|
||||||
|
##
|
||||||
|
## .. code-block:: Nim
|
||||||
|
## db.exec(sql"DROP TABLE IF EXISTS myTable")
|
||||||
|
## db.exec(sql("""CREATE TABLE myTable (
|
||||||
|
## id integer,
|
||||||
|
## name varchar(50) not null)"""))
|
||||||
|
##
|
||||||
|
## Inserting data
|
||||||
|
## ==============
|
||||||
|
##
|
||||||
|
## .. code-block:: Nim
|
||||||
|
## db.exec(sql"INSERT INTO myTable (id, name) VALUES (0, ?)",
|
||||||
|
## "Dominik")
|
||||||
|
##
|
||||||
|
## Larger example
|
||||||
|
## ==============
|
||||||
##
|
##
|
||||||
## .. code-block:: Nim
|
## .. code-block:: Nim
|
||||||
##
|
##
|
||||||
|
|
@ -43,45 +85,26 @@
|
||||||
|
|
||||||
import strutils, mysql
|
import strutils, mysql
|
||||||
|
|
||||||
|
import db_common
|
||||||
|
export db_common
|
||||||
|
|
||||||
type
|
type
|
||||||
DbConn* = PMySQL ## encapsulates a database connection
|
DbConn* = PMySQL ## encapsulates a database connection
|
||||||
Row* = seq[string] ## a row of a dataset. NULL database values will be
|
Row* = seq[string] ## a row of a dataset. NULL database values will be
|
||||||
## transformed always to the empty string.
|
## converted to nil.
|
||||||
InstantRow* = tuple[row: cstringArray, len: int] ## a handle that can be
|
InstantRow* = object ## a handle that can be used to get a row's
|
||||||
## used to get a row's
|
## column text on demand
|
||||||
## column text on demand
|
row: cstringArray
|
||||||
EDb* = object of IOError ## exception that is raised if a database error occurs
|
len: int
|
||||||
|
{.deprecated: [TRow: Row, TDbConn: DbConn].}
|
||||||
|
|
||||||
SqlQuery* = distinct string ## an SQL query string
|
proc dbError*(db: DbConn) {.noreturn.} =
|
||||||
|
## raises a DbError exception.
|
||||||
FDb* = object of IOEffect ## effect that denotes a database operation
|
var e: ref DbError
|
||||||
FReadDb* = object of FDb ## effect that denotes a read operation
|
|
||||||
FWriteDb* = object of FDb ## effect that denotes a write operation
|
|
||||||
{.deprecated: [TRow: Row, TSqlQuery: SqlQuery, TDbConn: DbConn].}
|
|
||||||
|
|
||||||
proc sql*(query: string): SqlQuery {.noSideEffect, inline.} =
|
|
||||||
## constructs a SqlQuery from the string `query`. This is supposed to be
|
|
||||||
## used as a raw-string-literal modifier:
|
|
||||||
## ``sql"update user set counter = counter + 1"``
|
|
||||||
##
|
|
||||||
## If assertions are turned off, it does nothing. If assertions are turned
|
|
||||||
## on, later versions will check the string for valid syntax.
|
|
||||||
result = SqlQuery(query)
|
|
||||||
|
|
||||||
proc dbError(db: DbConn) {.noreturn.} =
|
|
||||||
## raises an EDb exception.
|
|
||||||
var e: ref EDb
|
|
||||||
new(e)
|
new(e)
|
||||||
e.msg = $mysql.error(db)
|
e.msg = $mysql.error(db)
|
||||||
raise e
|
raise e
|
||||||
|
|
||||||
proc dbError*(msg: string) {.noreturn.} =
|
|
||||||
## raises an EDb exception with message `msg`.
|
|
||||||
var e: ref EDb
|
|
||||||
new(e)
|
|
||||||
e.msg = msg
|
|
||||||
raise e
|
|
||||||
|
|
||||||
when false:
|
when false:
|
||||||
proc dbQueryOpt*(db: DbConn, query: string, args: varargs[string, `$`]) =
|
proc dbQueryOpt*(db: DbConn, query: string, args: varargs[string, `$`]) =
|
||||||
var stmt = mysql_stmt_init(db)
|
var stmt = mysql_stmt_init(db)
|
||||||
|
|
@ -114,7 +137,7 @@ proc dbFormat(formatstr: SqlQuery, args: varargs[string]): string =
|
||||||
add(result, c)
|
add(result, c)
|
||||||
|
|
||||||
proc tryExec*(db: DbConn, query: SqlQuery, args: varargs[string, `$`]): bool {.
|
proc tryExec*(db: DbConn, query: SqlQuery, args: varargs[string, `$`]): bool {.
|
||||||
tags: [FReadDB, FWriteDb].} =
|
tags: [ReadDbEffect, WriteDbEffect].} =
|
||||||
## tries to execute the query and returns true if successful, false otherwise.
|
## tries to execute the query and returns true if successful, false otherwise.
|
||||||
var q = dbFormat(query, args)
|
var q = dbFormat(query, args)
|
||||||
return mysql.realQuery(db, q, q.len) == 0'i32
|
return mysql.realQuery(db, q, q.len) == 0'i32
|
||||||
|
|
@ -124,7 +147,7 @@ proc rawExec(db: DbConn, query: SqlQuery, args: varargs[string, `$`]) =
|
||||||
if mysql.realQuery(db, q, q.len) != 0'i32: dbError(db)
|
if mysql.realQuery(db, q, q.len) != 0'i32: dbError(db)
|
||||||
|
|
||||||
proc exec*(db: DbConn, query: SqlQuery, args: varargs[string, `$`]) {.
|
proc exec*(db: DbConn, query: SqlQuery, args: varargs[string, `$`]) {.
|
||||||
tags: [FReadDB, FWriteDb].} =
|
tags: [ReadDbEffect, WriteDbEffect].} =
|
||||||
## executes the query and raises EDB if not successful.
|
## executes the query and raises EDB if not successful.
|
||||||
var q = dbFormat(query, args)
|
var q = dbFormat(query, args)
|
||||||
if mysql.realQuery(db, q, q.len) != 0'i32: dbError(db)
|
if mysql.realQuery(db, q, q.len) != 0'i32: dbError(db)
|
||||||
|
|
@ -139,7 +162,7 @@ proc properFreeResult(sqlres: mysql.PRES, row: cstringArray) =
|
||||||
mysql.freeResult(sqlres)
|
mysql.freeResult(sqlres)
|
||||||
|
|
||||||
iterator fastRows*(db: DbConn, query: SqlQuery,
|
iterator fastRows*(db: DbConn, query: SqlQuery,
|
||||||
args: varargs[string, `$`]): Row {.tags: [FReadDB].} =
|
args: varargs[string, `$`]): Row {.tags: [ReadDbEffect].} =
|
||||||
## executes the query and iterates over the result dataset.
|
## executes the query and iterates over the result dataset.
|
||||||
##
|
##
|
||||||
## This is very fast, but potentially dangerous. Use this iterator only
|
## This is very fast, but potentially dangerous. Use this iterator only
|
||||||
|
|
@ -167,9 +190,9 @@ iterator fastRows*(db: DbConn, query: SqlQuery,
|
||||||
|
|
||||||
iterator instantRows*(db: DbConn, query: SqlQuery,
|
iterator instantRows*(db: DbConn, query: SqlQuery,
|
||||||
args: varargs[string, `$`]): InstantRow
|
args: varargs[string, `$`]): InstantRow
|
||||||
{.tags: [FReadDb].} =
|
{.tags: [ReadDbEffect].} =
|
||||||
## same as fastRows but returns a handle that can be used to get column text
|
## Same as fastRows but returns a handle that can be used to get column text
|
||||||
## on demand using []. Returned handle is valid only within the interator body.
|
## on demand using []. Returned handle is valid only within the iterator body.
|
||||||
rawExec(db, query, args)
|
rawExec(db, query, args)
|
||||||
var sqlres = mysql.useResult(db)
|
var sqlres = mysql.useResult(db)
|
||||||
if sqlres != nil:
|
if sqlres != nil:
|
||||||
|
|
@ -178,20 +201,102 @@ iterator instantRows*(db: DbConn, query: SqlQuery,
|
||||||
while true:
|
while true:
|
||||||
row = mysql.fetchRow(sqlres)
|
row = mysql.fetchRow(sqlres)
|
||||||
if row == nil: break
|
if row == nil: break
|
||||||
yield (row: row, len: L)
|
yield InstantRow(row: row, len: L)
|
||||||
properFreeResult(sqlres, row)
|
properFreeResult(sqlres, row)
|
||||||
|
|
||||||
|
proc setTypeName(t: var DbType; f: PFIELD) =
|
||||||
|
shallowCopy(t.name, $f.name)
|
||||||
|
t.maxReprLen = Natural(f.max_length)
|
||||||
|
if (NOT_NULL_FLAG and f.flags) != 0: t.notNull = true
|
||||||
|
case f.ftype
|
||||||
|
of TYPE_DECIMAL:
|
||||||
|
t.kind = dbDecimal
|
||||||
|
of TYPE_TINY:
|
||||||
|
t.kind = dbInt
|
||||||
|
t.size = 1
|
||||||
|
of TYPE_SHORT:
|
||||||
|
t.kind = dbInt
|
||||||
|
t.size = 2
|
||||||
|
of TYPE_LONG:
|
||||||
|
t.kind = dbInt
|
||||||
|
t.size = 4
|
||||||
|
of TYPE_FLOAT:
|
||||||
|
t.kind = dbFloat
|
||||||
|
t.size = 4
|
||||||
|
of TYPE_DOUBLE:
|
||||||
|
t.kind = dbFloat
|
||||||
|
t.size = 8
|
||||||
|
of TYPE_NULL:
|
||||||
|
t.kind = dbNull
|
||||||
|
of TYPE_TIMESTAMP:
|
||||||
|
t.kind = dbTimestamp
|
||||||
|
of TYPE_LONGLONG:
|
||||||
|
t.kind = dbInt
|
||||||
|
t.size = 8
|
||||||
|
of TYPE_INT24:
|
||||||
|
t.kind = dbInt
|
||||||
|
t.size = 3
|
||||||
|
of TYPE_DATE:
|
||||||
|
t.kind = dbDate
|
||||||
|
of TYPE_TIME:
|
||||||
|
t.kind = dbTime
|
||||||
|
of TYPE_DATETIME:
|
||||||
|
t.kind = dbDatetime
|
||||||
|
of TYPE_YEAR:
|
||||||
|
t.kind = dbDate
|
||||||
|
of TYPE_NEWDATE:
|
||||||
|
t.kind = dbDate
|
||||||
|
of TYPE_VARCHAR, TYPE_VAR_STRING, TYPE_STRING:
|
||||||
|
t.kind = dbVarchar
|
||||||
|
of TYPE_BIT:
|
||||||
|
t.kind = dbBit
|
||||||
|
of TYPE_NEWDECIMAL:
|
||||||
|
t.kind = dbDecimal
|
||||||
|
of TYPE_ENUM: t.kind = dbEnum
|
||||||
|
of TYPE_SET: t.kind = dbSet
|
||||||
|
of TYPE_TINY_BLOB, TYPE_MEDIUM_BLOB, TYPE_LONG_BLOB,
|
||||||
|
TYPE_BLOB: t.kind = dbBlob
|
||||||
|
of TYPE_GEOMETRY:
|
||||||
|
t.kind = dbGeometry
|
||||||
|
|
||||||
|
proc setColumnInfo(columns: var DbColumns; res: PRES; L: int) =
|
||||||
|
setLen(columns, L)
|
||||||
|
for i in 0..<L:
|
||||||
|
let fp = mysql.fetch_field_direct(res, cint(i))
|
||||||
|
setTypeName(columns[i].typ, fp)
|
||||||
|
columns[i].name = $fp.name
|
||||||
|
columns[i].tableName = $fp.table
|
||||||
|
columns[i].primaryKey = (fp.flags and PRI_KEY_FLAG) != 0
|
||||||
|
#columns[i].foreignKey = there is no such thing in mysql
|
||||||
|
|
||||||
|
iterator instantRows*(db: DbConn; columns: var DbColumns; query: SqlQuery;
|
||||||
|
args: varargs[string, `$`]): InstantRow =
|
||||||
|
## Same as fastRows but returns a handle that can be used to get column text
|
||||||
|
## on demand using []. Returned handle is valid only within the iterator body.
|
||||||
|
rawExec(db, query, args)
|
||||||
|
var sqlres = mysql.useResult(db)
|
||||||
|
if sqlres != nil:
|
||||||
|
let L = int(mysql.numFields(sqlres))
|
||||||
|
setColumnInfo(columns, sqlres, L)
|
||||||
|
var row: cstringArray
|
||||||
|
while true:
|
||||||
|
row = mysql.fetchRow(sqlres)
|
||||||
|
if row == nil: break
|
||||||
|
yield InstantRow(row: row, len: L)
|
||||||
|
properFreeResult(sqlres, row)
|
||||||
|
|
||||||
|
|
||||||
proc `[]`*(row: InstantRow, col: int): string {.inline.} =
|
proc `[]`*(row: InstantRow, col: int): string {.inline.} =
|
||||||
## returns text for given column of the row
|
## Returns text for given column of the row.
|
||||||
$row.row[col]
|
$row.row[col]
|
||||||
|
|
||||||
proc len*(row: InstantRow): int {.inline.} =
|
proc len*(row: InstantRow): int {.inline.} =
|
||||||
## returns number of columns in the row
|
## Returns number of columns in the row.
|
||||||
row.len
|
row.len
|
||||||
|
|
||||||
proc getRow*(db: DbConn, query: SqlQuery,
|
proc getRow*(db: DbConn, query: SqlQuery,
|
||||||
args: varargs[string, `$`]): Row {.tags: [FReadDB].} =
|
args: varargs[string, `$`]): Row {.tags: [ReadDbEffect].} =
|
||||||
## retrieves a single row. If the query doesn't return any rows, this proc
|
## Retrieves a single row. If the query doesn't return any rows, this proc
|
||||||
## will return a Row with empty strings for each column.
|
## will return a Row with empty strings for each column.
|
||||||
rawExec(db, query, args)
|
rawExec(db, query, args)
|
||||||
var sqlres = mysql.useResult(db)
|
var sqlres = mysql.useResult(db)
|
||||||
|
|
@ -209,7 +314,7 @@ proc getRow*(db: DbConn, query: SqlQuery,
|
||||||
properFreeResult(sqlres, row)
|
properFreeResult(sqlres, row)
|
||||||
|
|
||||||
proc getAllRows*(db: DbConn, query: SqlQuery,
|
proc getAllRows*(db: DbConn, query: SqlQuery,
|
||||||
args: varargs[string, `$`]): seq[Row] {.tags: [FReadDB].} =
|
args: varargs[string, `$`]): seq[Row] {.tags: [ReadDbEffect].} =
|
||||||
## executes the query and returns the whole result dataset.
|
## executes the query and returns the whole result dataset.
|
||||||
result = @[]
|
result = @[]
|
||||||
rawExec(db, query, args)
|
rawExec(db, query, args)
|
||||||
|
|
@ -232,19 +337,19 @@ proc getAllRows*(db: DbConn, query: SqlQuery,
|
||||||
mysql.freeResult(sqlres)
|
mysql.freeResult(sqlres)
|
||||||
|
|
||||||
iterator rows*(db: DbConn, query: SqlQuery,
|
iterator rows*(db: DbConn, query: SqlQuery,
|
||||||
args: varargs[string, `$`]): Row {.tags: [FReadDB].} =
|
args: varargs[string, `$`]): Row {.tags: [ReadDbEffect].} =
|
||||||
## same as `fastRows`, but slower and safe.
|
## same as `fastRows`, but slower and safe.
|
||||||
for r in items(getAllRows(db, query, args)): yield r
|
for r in items(getAllRows(db, query, args)): yield r
|
||||||
|
|
||||||
proc getValue*(db: DbConn, query: SqlQuery,
|
proc getValue*(db: DbConn, query: SqlQuery,
|
||||||
args: varargs[string, `$`]): string {.tags: [FReadDB].} =
|
args: varargs[string, `$`]): string {.tags: [ReadDbEffect].} =
|
||||||
## executes the query and returns the first column of the first row of the
|
## executes the query and returns the first column of the first row of the
|
||||||
## result dataset. Returns "" if the dataset contains no rows or the database
|
## result dataset. Returns "" if the dataset contains no rows or the database
|
||||||
## value is NULL.
|
## value is NULL.
|
||||||
result = getRow(db, query, args)[0]
|
result = getRow(db, query, args)[0]
|
||||||
|
|
||||||
proc tryInsertId*(db: DbConn, query: SqlQuery,
|
proc tryInsertId*(db: DbConn, query: SqlQuery,
|
||||||
args: varargs[string, `$`]): int64 {.tags: [FWriteDb].} =
|
args: varargs[string, `$`]): int64 {.tags: [WriteDbEffect].} =
|
||||||
## executes the query (typically "INSERT") and returns the
|
## executes the query (typically "INSERT") and returns the
|
||||||
## generated ID for the row or -1 in case of an error.
|
## generated ID for the row or -1 in case of an error.
|
||||||
var q = dbFormat(query, args)
|
var q = dbFormat(query, args)
|
||||||
|
|
@ -254,7 +359,7 @@ proc tryInsertId*(db: DbConn, query: SqlQuery,
|
||||||
result = mysql.insertId(db)
|
result = mysql.insertId(db)
|
||||||
|
|
||||||
proc insertId*(db: DbConn, query: SqlQuery,
|
proc insertId*(db: DbConn, query: SqlQuery,
|
||||||
args: varargs[string, `$`]): int64 {.tags: [FWriteDb].} =
|
args: varargs[string, `$`]): int64 {.tags: [WriteDbEffect].} =
|
||||||
## executes the query (typically "INSERT") and returns the
|
## executes the query (typically "INSERT") and returns the
|
||||||
## generated ID for the row.
|
## generated ID for the row.
|
||||||
result = tryInsertID(db, query, args)
|
result = tryInsertID(db, query, args)
|
||||||
|
|
@ -262,18 +367,18 @@ proc insertId*(db: DbConn, query: SqlQuery,
|
||||||
|
|
||||||
proc execAffectedRows*(db: DbConn, query: SqlQuery,
|
proc execAffectedRows*(db: DbConn, query: SqlQuery,
|
||||||
args: varargs[string, `$`]): int64 {.
|
args: varargs[string, `$`]): int64 {.
|
||||||
tags: [FReadDB, FWriteDb].} =
|
tags: [ReadDbEffect, WriteDbEffect].} =
|
||||||
## runs the query (typically "UPDATE") and returns the
|
## runs the query (typically "UPDATE") and returns the
|
||||||
## number of affected rows
|
## number of affected rows
|
||||||
rawExec(db, query, args)
|
rawExec(db, query, args)
|
||||||
result = mysql.affectedRows(db)
|
result = mysql.affectedRows(db)
|
||||||
|
|
||||||
proc close*(db: DbConn) {.tags: [FDb].} =
|
proc close*(db: DbConn) {.tags: [DbEffect].} =
|
||||||
## closes the database connection.
|
## closes the database connection.
|
||||||
if db != nil: mysql.close(db)
|
if db != nil: mysql.close(db)
|
||||||
|
|
||||||
proc open*(connection, user, password, database: string): DbConn {.
|
proc open*(connection, user, password, database: string): DbConn {.
|
||||||
tags: [FDb].} =
|
tags: [DbEffect].} =
|
||||||
## opens a database connection. Raises `EDb` if the connection could not
|
## opens a database connection. Raises `EDb` if the connection could not
|
||||||
## be established.
|
## be established.
|
||||||
result = mysql.init(nil)
|
result = mysql.init(nil)
|
||||||
|
|
@ -291,7 +396,7 @@ proc open*(connection, user, password, database: string): DbConn {.
|
||||||
dbError(errmsg)
|
dbError(errmsg)
|
||||||
|
|
||||||
proc setEncoding*(connection: DbConn, encoding: string): bool {.
|
proc setEncoding*(connection: DbConn, encoding: string): bool {.
|
||||||
tags: [FDb].} =
|
tags: [DbEffect].} =
|
||||||
## sets the encoding of a database connection, returns true for
|
## sets the encoding of a database connection, returns true for
|
||||||
## success, false for failure.
|
## success, false for failure.
|
||||||
result = mysql.set_character_set(connection, encoding) == 0
|
result = mysql.set_character_set(connection, encoding) == 0
|
||||||
|
|
|
||||||
505
lib/impure/db_odbc.nim
Normal file
505
lib/impure/db_odbc.nim
Normal file
|
|
@ -0,0 +1,505 @@
|
||||||
|
#
|
||||||
|
#
|
||||||
|
# Nim's Runtime Library
|
||||||
|
# (c) Copyright 2015 Nim Contributors
|
||||||
|
#
|
||||||
|
# See the file "copying.txt", included in this
|
||||||
|
# distribution, for details about the copyright.
|
||||||
|
#
|
||||||
|
|
||||||
|
## A higher level `ODBC` database wrapper.
|
||||||
|
##
|
||||||
|
## This is the same interface that is implemented for other databases.
|
||||||
|
##
|
||||||
|
## This has NOT yet been (extensively) tested against ODBC drivers for
|
||||||
|
## Teradata, Oracle, Sybase, MSSqlvSvr, et. al. databases.
|
||||||
|
##
|
||||||
|
## Currently all queries are ANSI calls, not Unicode.
|
||||||
|
##
|
||||||
|
## See also: `db_postgres <db_postgres.html>`_, `db_sqlite <db_sqlite.html>`_,
|
||||||
|
## `db_mysql <db_mysql.html>`_.
|
||||||
|
##
|
||||||
|
## Parameter substitution
|
||||||
|
## ----------------------
|
||||||
|
##
|
||||||
|
## All ``db_*`` modules support the same form of parameter substitution.
|
||||||
|
## That is, using the ``?`` (question mark) to signify the place where a
|
||||||
|
## value should be placed. For example:
|
||||||
|
##
|
||||||
|
## .. code-block:: Nim
|
||||||
|
## sql"INSERT INTO myTable (colA, colB, colC) VALUES (?, ?, ?)"
|
||||||
|
##
|
||||||
|
##
|
||||||
|
## Examples
|
||||||
|
## --------
|
||||||
|
##
|
||||||
|
## Opening a connection to a database
|
||||||
|
## ==================================
|
||||||
|
##
|
||||||
|
## .. code-block:: Nim
|
||||||
|
## import db_odbc
|
||||||
|
## let db = open("localhost", "user", "password", "dbname")
|
||||||
|
## db.close()
|
||||||
|
##
|
||||||
|
## Creating a table
|
||||||
|
## ================
|
||||||
|
##
|
||||||
|
## .. code-block:: Nim
|
||||||
|
## db.exec(sql"DROP TABLE IF EXISTS myTable")
|
||||||
|
## db.exec(sql("""CREATE TABLE myTable (
|
||||||
|
## id integer,
|
||||||
|
## name varchar(50) not null)"""))
|
||||||
|
##
|
||||||
|
## Inserting data
|
||||||
|
## ==============
|
||||||
|
##
|
||||||
|
## .. code-block:: Nim
|
||||||
|
## db.exec(sql"INSERT INTO myTable (id, name) VALUES (0, ?)",
|
||||||
|
## "Andreas")
|
||||||
|
##
|
||||||
|
## Large example
|
||||||
|
## =============
|
||||||
|
##
|
||||||
|
## .. code-block:: Nim
|
||||||
|
##
|
||||||
|
## import db_odbc, math
|
||||||
|
##
|
||||||
|
## let theDb = open("localhost", "nim", "nim", "test")
|
||||||
|
##
|
||||||
|
## theDb.exec(sql"Drop table if exists myTestTbl")
|
||||||
|
## theDb.exec(sql("create table myTestTbl (" &
|
||||||
|
## " Id INT(11) NOT NULL AUTO_INCREMENT PRIMARY KEY, " &
|
||||||
|
## " Name VARCHAR(50) NOT NULL, " &
|
||||||
|
## " i INT(11), " &
|
||||||
|
## " f DECIMAL(18,10))"))
|
||||||
|
##
|
||||||
|
## theDb.exec(sql"START TRANSACTION")
|
||||||
|
## for i in 1..1000:
|
||||||
|
## theDb.exec(sql"INSERT INTO myTestTbl (name,i,f) VALUES (?,?,?)",
|
||||||
|
## "Item#" & $i, i, sqrt(i.float))
|
||||||
|
## theDb.exec(sql"COMMIT")
|
||||||
|
##
|
||||||
|
## for x in theDb.fastRows(sql"select * from myTestTbl"):
|
||||||
|
## echo x
|
||||||
|
##
|
||||||
|
## let id = theDb.tryInsertId(sql"INSERT INTO myTestTbl (name,i,f) VALUES (?,?,?)",
|
||||||
|
## "Item#1001", 1001, sqrt(1001.0))
|
||||||
|
## echo "Inserted item: ", theDb.getValue(sql"SELECT name FROM myTestTbl WHERE id=?", id)
|
||||||
|
##
|
||||||
|
## theDb.close()
|
||||||
|
|
||||||
|
|
||||||
|
import strutils, odbcsql
|
||||||
|
|
||||||
|
import db_common
|
||||||
|
export db_common
|
||||||
|
|
||||||
|
type
|
||||||
|
OdbcConnTyp = tuple[hDb: SqlHDBC, env: SqlHEnv, stmt: SqlHStmt]
|
||||||
|
DbConn* = OdbcConnTyp ## encapsulates a database connection
|
||||||
|
Row* = seq[string] ## a row of a dataset. NULL database values will be
|
||||||
|
## converted to nil.
|
||||||
|
InstantRow* = tuple[row: seq[string], len: int] ## a handle that can be
|
||||||
|
## used to get a row's
|
||||||
|
## column text on demand
|
||||||
|
|
||||||
|
{.deprecated: [TRow: Row, TSqlQuery: SqlQuery, TDbConn: DbConn].}
|
||||||
|
|
||||||
|
var
|
||||||
|
buf: array[0..4096, char]
|
||||||
|
|
||||||
|
proc properFreeResult(hType: int, sqlres: var SqlHandle) {.
|
||||||
|
tags: [WriteDbEffect], raises: [].} =
|
||||||
|
try:
|
||||||
|
discard SQLFreeHandle(hType.TSqlSmallInt, sqlres)
|
||||||
|
sqlres = nil
|
||||||
|
except: discard
|
||||||
|
|
||||||
|
proc getErrInfo(db: var DbConn): tuple[res: int, ss, ne, msg: string] {.
|
||||||
|
tags: [ReadDbEffect], raises: [].} =
|
||||||
|
## Returns ODBC error information
|
||||||
|
var
|
||||||
|
sqlState: array[0..512, char]
|
||||||
|
nativeErr: array[0..512, char]
|
||||||
|
errMsg: array[0..512, char]
|
||||||
|
retSz: TSqlSmallInt = 0
|
||||||
|
res: TSqlSmallInt = 0
|
||||||
|
try:
|
||||||
|
sqlState[0] = '\0'
|
||||||
|
nativeErr[0] = '\0'
|
||||||
|
errMsg[0] = '\0'
|
||||||
|
res = SQLErr(db.env, db.hDb, db.stmt,
|
||||||
|
cast[PSQLCHAR](sqlState.addr),
|
||||||
|
cast[PSQLCHAR](nativeErr.addr),
|
||||||
|
cast[PSQLCHAR](errMsg.addr),
|
||||||
|
511.TSqlSmallInt, retSz.addr.PSQLSMALLINT)
|
||||||
|
except:
|
||||||
|
discard
|
||||||
|
return (res.int, $sqlState, $nativeErr, $errMsg)
|
||||||
|
|
||||||
|
proc dbError*(db: var DbConn) {.
|
||||||
|
tags: [ReadDbEffect, WriteDbEffect], raises: [DbError] .} =
|
||||||
|
## Raises an `[DbError]` exception with ODBC error information
|
||||||
|
var
|
||||||
|
e: ref DbError
|
||||||
|
ss, ne, msg: string = ""
|
||||||
|
isAnError = false
|
||||||
|
res: int = 0
|
||||||
|
prevSs = ""
|
||||||
|
while true:
|
||||||
|
prevSs = ss
|
||||||
|
(res, ss, ne, msg) = db.getErrInfo()
|
||||||
|
if prevSs == ss:
|
||||||
|
break
|
||||||
|
# sqlState of 00000 is not an error
|
||||||
|
elif ss == "00000":
|
||||||
|
break
|
||||||
|
elif ss == "01000":
|
||||||
|
echo "\nWarning: ", ss, " ", msg
|
||||||
|
continue
|
||||||
|
else:
|
||||||
|
isAnError = true
|
||||||
|
echo "\nError: ", ss, " ", msg
|
||||||
|
if isAnError:
|
||||||
|
new(e)
|
||||||
|
e.msg = "ODBC Error"
|
||||||
|
if db.stmt != nil:
|
||||||
|
properFreeResult(SQL_HANDLE_STMT, db.stmt)
|
||||||
|
properFreeResult(SQL_HANDLE_DBC, db.hDb)
|
||||||
|
properFreeResult(SQL_HANDLE_ENV, db.env)
|
||||||
|
raise e
|
||||||
|
|
||||||
|
proc SqlCheck(db: var DbConn, resVal: TSqlSmallInt) {.raises: [DbError]} =
|
||||||
|
## Wrapper that checks if ``resVal`` is not SQL_SUCCESS and if so, raises [EDb]
|
||||||
|
if resVal != SQL_SUCCESS: dbError(db)
|
||||||
|
|
||||||
|
proc SqlGetDBMS(db: var DbConn): string {.
|
||||||
|
tags: [ReadDbEffect, WriteDbEffect], raises: [] .} =
|
||||||
|
## Returns the ODBC SQL_DBMS_NAME string
|
||||||
|
const
|
||||||
|
SQL_DBMS_NAME = 17.SqlUSmallInt
|
||||||
|
var
|
||||||
|
sz: TSqlSmallInt = 0
|
||||||
|
buf[0] = '\0'
|
||||||
|
try:
|
||||||
|
db.SqlCheck(SQLGetInfo(db.hDb, SQL_DBMS_NAME, cast[SqlPointer](buf.addr),
|
||||||
|
4095.TSqlSmallInt, sz.addr))
|
||||||
|
except: discard
|
||||||
|
return $buf.cstring
|
||||||
|
|
||||||
|
proc dbQuote*(s: string): string {.noSideEffect.} =
|
||||||
|
## DB quotes the string.
|
||||||
|
result = "'"
|
||||||
|
for c in items(s):
|
||||||
|
if c == '\'': add(result, "''")
|
||||||
|
else: add(result, c)
|
||||||
|
add(result, '\'')
|
||||||
|
|
||||||
|
proc dbFormat(formatstr: SqlQuery, args: varargs[string]): string {.
|
||||||
|
noSideEffect.} =
|
||||||
|
## Replace any ``?`` placeholders with `args`,
|
||||||
|
## and quotes the arguments
|
||||||
|
result = ""
|
||||||
|
var a = 0
|
||||||
|
for c in items(string(formatstr)):
|
||||||
|
if c == '?':
|
||||||
|
if args[a] == nil:
|
||||||
|
add(result, "NULL")
|
||||||
|
else:
|
||||||
|
add(result, dbQuote(args[a]))
|
||||||
|
inc(a)
|
||||||
|
else:
|
||||||
|
add(result, c)
|
||||||
|
|
||||||
|
proc prepareFetch(db: var DbConn, query: SqlQuery,
|
||||||
|
args: varargs[string, `$`]) {.
|
||||||
|
tags: [ReadDbEffect, WriteDbEffect], raises: [DbError].} =
|
||||||
|
# Prepare a statement, execute it and fetch the data to the driver
|
||||||
|
# ready for retrieval of the data
|
||||||
|
# Used internally by iterators and retrieval procs
|
||||||
|
# requires calling
|
||||||
|
# properFreeResult(SQL_HANDLE_STMT, db.stmt)
|
||||||
|
# when finished
|
||||||
|
db.SqlCheck(SQLAllocHandle(SQL_HANDLE_STMT, db.hDb, db.stmt))
|
||||||
|
var q = dbFormat(query, args)
|
||||||
|
db.SqlCheck(SQLPrepare(db.stmt, q.PSQLCHAR, q.len.TSqlSmallInt))
|
||||||
|
db.SqlCheck(SQLExecute(db.stmt))
|
||||||
|
db.SqlCheck(SQLFetch(db.stmt))
|
||||||
|
|
||||||
|
proc prepareFetchDirect(db: var DbConn, query: SqlQuery,
|
||||||
|
args: varargs[string, `$`]) {.
|
||||||
|
tags: [ReadDbEffect, WriteDbEffect], raises: [DbError].} =
|
||||||
|
# Prepare a statement, execute it and fetch the data to the driver
|
||||||
|
# ready for retrieval of the data
|
||||||
|
# Used internally by iterators and retrieval procs
|
||||||
|
# requires calling
|
||||||
|
# properFreeResult(SQL_HANDLE_STMT, db.stmt)
|
||||||
|
# when finished
|
||||||
|
db.SqlCheck(SQLAllocHandle(SQL_HANDLE_STMT, db.hDb, db.stmt))
|
||||||
|
var q = dbFormat(query, args)
|
||||||
|
db.SqlCheck(SQLExecDirect(db.stmt, q.PSQLCHAR, q.len.TSqlSmallInt))
|
||||||
|
db.SqlCheck(SQLFetch(db.stmt))
|
||||||
|
|
||||||
|
proc tryExec*(db: var DbConn, query: SqlQuery, args: varargs[string, `$`]): bool {.
|
||||||
|
tags: [ReadDbEffect, WriteDbEffect], raises: [].} =
|
||||||
|
## Tries to execute the query and returns true if successful, false otherwise.
|
||||||
|
var
|
||||||
|
res:TSqlSmallInt = -1
|
||||||
|
try:
|
||||||
|
db.prepareFetchDirect(query, args)
|
||||||
|
var
|
||||||
|
rCnt = -1
|
||||||
|
res = SQLRowCount(db.stmt, rCnt)
|
||||||
|
if res != SQL_SUCCESS: dbError(db)
|
||||||
|
properFreeResult(SQL_HANDLE_STMT, db.stmt)
|
||||||
|
except: discard
|
||||||
|
return res == SQL_SUCCESS
|
||||||
|
|
||||||
|
proc rawExec(db: var DbConn, query: SqlQuery, args: varargs[string, `$`]) {.
|
||||||
|
tags: [ReadDbEffect, WriteDbEffect], raises: [DbError].} =
|
||||||
|
db.prepareFetchDirect(query, args)
|
||||||
|
|
||||||
|
proc exec*(db: var DbConn, query: SqlQuery, args: varargs[string, `$`]) {.
|
||||||
|
tags: [ReadDbEffect, WriteDbEffect], raises: [DbError].} =
|
||||||
|
## Executes the query and raises EDB if not successful.
|
||||||
|
db.prepareFetchDirect(query, args)
|
||||||
|
properFreeResult(SQL_HANDLE_STMT, db.stmt)
|
||||||
|
|
||||||
|
proc newRow(L: int): Row {.noSideEFfect.} =
|
||||||
|
newSeq(result, L)
|
||||||
|
for i in 0..L-1: result[i] = ""
|
||||||
|
|
||||||
|
iterator fastRows*(db: var DbConn, query: SqlQuery,
|
||||||
|
args: varargs[string, `$`]): Row {.
|
||||||
|
tags: [ReadDbEffect, WriteDbEffect], raises: [DbError].} =
|
||||||
|
## Executes the query and iterates over the result dataset.
|
||||||
|
##
|
||||||
|
## This is very fast, but potentially dangerous. Use this iterator only
|
||||||
|
## if you require **ALL** the rows.
|
||||||
|
##
|
||||||
|
## Breaking the fastRows() iterator during a loop may cause a driver error
|
||||||
|
## for subsequenct queries
|
||||||
|
##
|
||||||
|
## Rows are retrieved from the server at each iteration.
|
||||||
|
var
|
||||||
|
rowRes: Row
|
||||||
|
sz: TSqlSmallInt = 0
|
||||||
|
cCnt: TSqlSmallInt = 0.TSqlSmallInt
|
||||||
|
rCnt = -1
|
||||||
|
|
||||||
|
db.prepareFetch(query, args)
|
||||||
|
db.SqlCheck(SQLNumResultCols(db.stmt, cCnt))
|
||||||
|
db.SqlCheck(SQLRowCount(db.stmt, rCnt))
|
||||||
|
rowRes = newRow(cCnt)
|
||||||
|
for rNr in 1..rCnt:
|
||||||
|
for colId in 1..cCnt:
|
||||||
|
buf[0] = '\0'
|
||||||
|
db.SqlCheck(SQLGetData(db.stmt, colId.SqlUSmallInt, SQL_C_CHAR,
|
||||||
|
cast[cstring](buf.addr), 4095.TSqlSmallInt, sz.addr))
|
||||||
|
rowRes[colId-1] = $buf.cstring
|
||||||
|
db.SqlCheck(SQLFetchScroll(db.stmt, SQL_FETCH_NEXT, 1))
|
||||||
|
yield rowRes
|
||||||
|
properFreeResult(SQL_HANDLE_STMT, db.stmt)
|
||||||
|
|
||||||
|
iterator instantRows*(db: var DbConn, query: SqlQuery,
|
||||||
|
args: varargs[string, `$`]): InstantRow
|
||||||
|
{.tags: [ReadDbEffect, WriteDbEffect].} =
|
||||||
|
## Same as fastRows but returns a handle that can be used to get column text
|
||||||
|
## on demand using []. Returned handle is valid only within the interator body.
|
||||||
|
var
|
||||||
|
rowRes: Row
|
||||||
|
sz: TSqlSmallInt = 0
|
||||||
|
cCnt: TSqlSmallInt = 0.TSqlSmallInt
|
||||||
|
rCnt = -1
|
||||||
|
db.prepareFetch(query, args)
|
||||||
|
db.SqlCheck(SQLNumResultCols(db.stmt, cCnt))
|
||||||
|
db.SqlCheck(SQLRowCount(db.stmt, rCnt))
|
||||||
|
rowRes = newRow(cCnt)
|
||||||
|
for rNr in 1..rCnt:
|
||||||
|
for colId in 1..cCnt:
|
||||||
|
buf[0] = '\0'
|
||||||
|
db.SqlCheck(SQLGetData(db.stmt, colId.SqlUSmallInt, SQL_C_CHAR,
|
||||||
|
cast[cstring](buf.addr), 4095.TSqlSmallInt, sz.addr))
|
||||||
|
rowRes[colId-1] = $buf.cstring
|
||||||
|
db.SqlCheck(SQLFetchScroll(db.stmt, SQL_FETCH_NEXT, 1))
|
||||||
|
yield (row: rowRes, len: cCnt.int)
|
||||||
|
properFreeResult(SQL_HANDLE_STMT, db.stmt)
|
||||||
|
|
||||||
|
proc `[]`*(row: InstantRow, col: int): string {.inline.} =
|
||||||
|
## Returns text for given column of the row
|
||||||
|
row.row[col]
|
||||||
|
|
||||||
|
proc len*(row: InstantRow): int {.inline.} =
|
||||||
|
## Returns number of columns in the row
|
||||||
|
row.len
|
||||||
|
|
||||||
|
proc getRow*(db: var DbConn, query: SqlQuery,
|
||||||
|
args: varargs[string, `$`]): Row {.
|
||||||
|
tags: [ReadDbEffect, WriteDbEffect], raises: [DbError].} =
|
||||||
|
## Retrieves a single row. If the query doesn't return any rows, this proc
|
||||||
|
## will return a Row with empty strings for each column.
|
||||||
|
var
|
||||||
|
sz: TSqlSmallInt = 0.TSqlSmallInt
|
||||||
|
cCnt: TSqlSmallInt = 0.TSqlSmallInt
|
||||||
|
rCnt = -1
|
||||||
|
result = @[]
|
||||||
|
db.prepareFetch(query, args)
|
||||||
|
db.SqlCheck(SQLNumResultCols(db.stmt, cCnt))
|
||||||
|
|
||||||
|
db.SqlCheck(SQLRowCount(db.stmt, rCnt))
|
||||||
|
for colId in 1..cCnt:
|
||||||
|
db.SqlCheck(SQLGetData(db.stmt, colId.SqlUSmallInt, SQL_C_CHAR,
|
||||||
|
cast[cstring](buf.addr), 4095.TSqlSmallInt, sz.addr))
|
||||||
|
result.add($buf.cstring)
|
||||||
|
db.SqlCheck(SQLFetchScroll(db.stmt, SQL_FETCH_NEXT, 1))
|
||||||
|
properFreeResult(SQL_HANDLE_STMT, db.stmt)
|
||||||
|
|
||||||
|
proc getAllRows*(db: var DbConn, query: SqlQuery,
|
||||||
|
args: varargs[string, `$`]): seq[Row] {.
|
||||||
|
tags: [ReadDbEffect, WriteDbEffect], raises: [DbError].} =
|
||||||
|
## Executes the query and returns the whole result dataset.
|
||||||
|
var
|
||||||
|
rowRes: Row
|
||||||
|
sz: TSqlSmallInt = 0
|
||||||
|
cCnt: TSqlSmallInt = 0.TSqlSmallInt
|
||||||
|
rCnt = -1
|
||||||
|
db.prepareFetch(query, args)
|
||||||
|
db.SqlCheck(SQLNumResultCols(db.stmt, cCnt))
|
||||||
|
db.SqlCheck(SQLRowCount(db.stmt, rCnt))
|
||||||
|
result = @[]
|
||||||
|
for rNr in 1..rCnt:
|
||||||
|
rowRes = @[]
|
||||||
|
buf[0] = '\0'
|
||||||
|
for colId in 1..cCnt:
|
||||||
|
db.SqlCheck(SQLGetData(db.stmt, colId.SqlUSmallInt, SQL_C_CHAR,
|
||||||
|
cast[SqlPointer](buf.addr), 4095.TSqlSmallInt, sz.addr))
|
||||||
|
rowRes.add($buf.cstring)
|
||||||
|
db.SqlCheck(SQLFetchScroll(db.stmt, SQL_FETCH_NEXT, 1))
|
||||||
|
result.add(rowRes)
|
||||||
|
properFreeResult(SQL_HANDLE_STMT, db.stmt)
|
||||||
|
|
||||||
|
iterator rows*(db: var DbConn, query: SqlQuery,
|
||||||
|
args: varargs[string, `$`]): Row {.
|
||||||
|
tags: [ReadDbEffect, WriteDbEffect], raises: [DbError].} =
|
||||||
|
## Same as `fastRows`, but slower and safe.
|
||||||
|
##
|
||||||
|
## This retrieves ALL rows into memory before
|
||||||
|
## iterating through the rows.
|
||||||
|
## Large dataset queries will impact on memory usage.
|
||||||
|
for r in items(getAllRows(db, query, args)): yield r
|
||||||
|
|
||||||
|
proc getValue*(db: var DbConn, query: SqlQuery,
|
||||||
|
args: varargs[string, `$`]): string {.
|
||||||
|
tags: [ReadDbEffect, WriteDbEffect], raises: [].} =
|
||||||
|
## Executes the query and returns the first column of the first row of the
|
||||||
|
## result dataset. Returns "" if the dataset contains no rows or the database
|
||||||
|
## value is NULL.
|
||||||
|
result = ""
|
||||||
|
try:
|
||||||
|
result = getRow(db, query, args)[0]
|
||||||
|
except: discard
|
||||||
|
|
||||||
|
proc tryInsertId*(db: var DbConn, query: SqlQuery,
|
||||||
|
args: varargs[string, `$`]): int64 {.
|
||||||
|
tags: [ReadDbEffect, WriteDbEffect], raises: [].} =
|
||||||
|
## Executes the query (typically "INSERT") and returns the
|
||||||
|
## generated ID for the row or -1 in case of an error.
|
||||||
|
if not tryExec(db, query, args):
|
||||||
|
result = -1'i64
|
||||||
|
else:
|
||||||
|
echo "DBMS: ",SqlGetDBMS(db).toLower()
|
||||||
|
result = -1'i64
|
||||||
|
try:
|
||||||
|
case SqlGetDBMS(db).toLower():
|
||||||
|
of "postgresql":
|
||||||
|
result = getValue(db, sql"SELECT LASTVAL();", []).parseInt
|
||||||
|
of "mysql":
|
||||||
|
result = getValue(db, sql"SELECT LAST_INSERT_ID();", []).parseInt
|
||||||
|
of "sqlite":
|
||||||
|
result = getValue(db, sql"SELECT LAST_INSERT_ROWID();", []).parseInt
|
||||||
|
of "microsoft sql server":
|
||||||
|
result = getValue(db, sql"SELECT SCOPE_IDENTITY();", []).parseInt
|
||||||
|
of "oracle":
|
||||||
|
result = getValue(db, sql"SELECT id.currval FROM DUAL;", []).parseInt
|
||||||
|
else: result = -1'i64
|
||||||
|
except: discard
|
||||||
|
|
||||||
|
proc insertId*(db: var DbConn, query: SqlQuery,
|
||||||
|
args: varargs[string, `$`]): int64 {.
|
||||||
|
tags: [ReadDbEffect, WriteDbEffect], raises: [DbError].} =
|
||||||
|
## Executes the query (typically "INSERT") and returns the
|
||||||
|
## generated ID for the row.
|
||||||
|
result = tryInsertID(db, query, args)
|
||||||
|
if result < 0: dbError(db)
|
||||||
|
|
||||||
|
proc execAffectedRows*(db: var DbConn, query: SqlQuery,
|
||||||
|
args: varargs[string, `$`]): int64 {.
|
||||||
|
tags: [ReadDbEffect, WriteDbEffect], raises: [DbError].} =
|
||||||
|
## Runs the query (typically "UPDATE") and returns the
|
||||||
|
## number of affected rows
|
||||||
|
result = -1
|
||||||
|
var res = SQLAllocHandle(SQL_HANDLE_STMT, db.hDb, db.stmt.SqlHandle)
|
||||||
|
if res != SQL_SUCCESS: dbError(db)
|
||||||
|
var q = dbFormat(query, args)
|
||||||
|
res = SQLPrepare(db.stmt, q.PSQLCHAR, q.len.TSqlSmallInt)
|
||||||
|
if res != SQL_SUCCESS: dbError(db)
|
||||||
|
rawExec(db, query, args)
|
||||||
|
var rCnt = -1
|
||||||
|
result = SQLRowCount(db.hDb, rCnt)
|
||||||
|
if res != SQL_SUCCESS: dbError(db)
|
||||||
|
properFreeResult(SQL_HANDLE_STMT, db.stmt)
|
||||||
|
result = rCnt
|
||||||
|
|
||||||
|
proc close*(db: var DbConn) {.
|
||||||
|
tags: [WriteDbEffect], raises: [].} =
|
||||||
|
## Closes the database connection.
|
||||||
|
if db.hDb != nil:
|
||||||
|
try:
|
||||||
|
var res = SQLDisconnect(db.hDb)
|
||||||
|
if db.stmt != nil:
|
||||||
|
res = SQLFreeHandle(SQL_HANDLE_STMT, db.stmt)
|
||||||
|
res = SQLFreeHandle(SQL_HANDLE_DBC, db.hDb)
|
||||||
|
res = SQLFreeHandle(SQL_HANDLE_ENV, db.env)
|
||||||
|
db = (hDb: nil, env: nil, stmt: nil)
|
||||||
|
except:
|
||||||
|
discard
|
||||||
|
|
||||||
|
proc open*(connection, user, password, database: string): DbConn {.
|
||||||
|
tags: [ReadDbEffect, WriteDbEffect], raises: [DbError].} =
|
||||||
|
## Opens a database connection.
|
||||||
|
##
|
||||||
|
## Raises `EDb` if the connection could not be established.
|
||||||
|
##
|
||||||
|
## Currently the database parameter is ignored,
|
||||||
|
## but included to match ``open()`` in the other db_xxxxx library modules.
|
||||||
|
var
|
||||||
|
val: TSqlInteger = SQL_OV_ODBC3
|
||||||
|
resLen = 0
|
||||||
|
result = (hDb: nil, env: nil, stmt: nil)
|
||||||
|
# allocate environment handle
|
||||||
|
var res = SQLAllocHandle(SQL_HANDLE_ENV, result.env, result.env)
|
||||||
|
if res != SQL_SUCCESS: dbError("Error: unable to initialise ODBC environment.")
|
||||||
|
res = SQLSetEnvAttr(result.env,
|
||||||
|
SQL_ATTR_ODBC_VERSION.TSqlInteger,
|
||||||
|
val, resLen.TSqlInteger)
|
||||||
|
if res != SQL_SUCCESS: dbError("Error: unable to set ODBC driver version.")
|
||||||
|
# allocate hDb handle
|
||||||
|
res = SQLAllocHandle(SQL_HANDLE_DBC, result.env, result.hDb)
|
||||||
|
if res != SQL_SUCCESS: dbError("Error: unable to allocate connection handle.")
|
||||||
|
|
||||||
|
# Connect: connection = dsn str,
|
||||||
|
res = SQLConnect(result.hDb,
|
||||||
|
connection.PSQLCHAR , connection.len.TSqlSmallInt,
|
||||||
|
user.PSQLCHAR, user.len.TSqlSmallInt,
|
||||||
|
password.PSQLCHAR, password.len.TSqlSmallInt)
|
||||||
|
if res != SQL_SUCCESS:
|
||||||
|
result.dbError()
|
||||||
|
|
||||||
|
proc setEncoding*(connection: DbConn, encoding: string): bool {.
|
||||||
|
tags: [ReadDbEffect, WriteDbEffect], raises: [DbError].} =
|
||||||
|
## Currently not implemented for ODBC.
|
||||||
|
##
|
||||||
|
## Sets the encoding of a database connection, returns true for
|
||||||
|
## success, false for failure.
|
||||||
|
#result = set_character_set(connection, encoding) == 0
|
||||||
|
dbError("setEncoding() is currently not implemented by the db_odbc module")
|
||||||
|
|
@ -10,6 +10,9 @@
|
||||||
## A higher level `PostgreSQL`:idx: database wrapper. This interface
|
## A higher level `PostgreSQL`:idx: database wrapper. This interface
|
||||||
## is implemented for other databases also.
|
## is implemented for other databases also.
|
||||||
##
|
##
|
||||||
|
## See also: `db_odbc <db_odbc.html>`_, `db_sqlite <db_sqlite.html>`_,
|
||||||
|
## `db_mysql <db_mysql.html>`_.
|
||||||
|
##
|
||||||
## Parameter substitution
|
## Parameter substitution
|
||||||
## ----------------------
|
## ----------------------
|
||||||
##
|
##
|
||||||
|
|
@ -27,7 +30,7 @@
|
||||||
##
|
##
|
||||||
## 2. ``SqlPrepared`` using ``$1, $2, $3, ...``
|
## 2. ``SqlPrepared`` using ``$1, $2, $3, ...``
|
||||||
##
|
##
|
||||||
## .. code-block:: Nim
|
## .. code-block:: Nim
|
||||||
## prepare(db, "myExampleInsert",
|
## prepare(db, "myExampleInsert",
|
||||||
## sql"""INSERT INTO myTable
|
## sql"""INSERT INTO myTable
|
||||||
## (colA, colB, colC)
|
## (colA, colB, colC)
|
||||||
|
|
@ -62,47 +65,28 @@
|
||||||
## "Dominik")
|
## "Dominik")
|
||||||
import strutils, postgres
|
import strutils, postgres
|
||||||
|
|
||||||
|
import db_common
|
||||||
|
export db_common
|
||||||
|
|
||||||
type
|
type
|
||||||
DbConn* = PPGconn ## encapsulates a database connection
|
DbConn* = PPGconn ## encapsulates a database connection
|
||||||
Row* = seq[string] ## a row of a dataset. NULL database values will be
|
Row* = seq[string] ## a row of a dataset. NULL database values will be
|
||||||
## transformed always to the empty string.
|
## converted to nil.
|
||||||
InstantRow* = tuple[res: PPGresult, line: int32] ## a handle that can be
|
InstantRow* = tuple[res: PPGresult, line: int32] ## a handle that can be
|
||||||
## used to get a row's
|
## used to get a row's
|
||||||
## column text on demand
|
## column text on demand
|
||||||
EDb* = object of IOError ## exception that is raised if a database error occurs
|
|
||||||
|
|
||||||
SqlQuery* = distinct string ## an SQL query string
|
|
||||||
SqlPrepared* = distinct string ## a identifier for the prepared queries
|
SqlPrepared* = distinct string ## a identifier for the prepared queries
|
||||||
|
|
||||||
FDb* = object of IOEffect ## effect that denotes a database operation
|
{.deprecated: [TRow: Row, TDbConn: DbConn,
|
||||||
FReadDb* = object of FDb ## effect that denotes a read operation
|
|
||||||
FWriteDb* = object of FDb ## effect that denotes a write operation
|
|
||||||
{.deprecated: [TRow: Row, TSqlQuery: SqlQuery, TDbConn: DbConn,
|
|
||||||
TSqlPrepared: SqlPrepared].}
|
TSqlPrepared: SqlPrepared].}
|
||||||
|
|
||||||
proc sql*(query: string): SqlQuery {.noSideEffect, inline.} =
|
|
||||||
## constructs a SqlQuery from the string `query`. This is supposed to be
|
|
||||||
## used as a raw-string-literal modifier:
|
|
||||||
## ``sql"update user set counter = counter + 1"``
|
|
||||||
##
|
|
||||||
## If assertions are turned off, it does nothing. If assertions are turned
|
|
||||||
## on, later versions will check the string for valid syntax.
|
|
||||||
result = SqlQuery(query)
|
|
||||||
|
|
||||||
proc dbError*(db: DbConn) {.noreturn.} =
|
proc dbError*(db: DbConn) {.noreturn.} =
|
||||||
## raises an EDb exception.
|
## raises a DbError exception.
|
||||||
var e: ref EDb
|
var e: ref DbError
|
||||||
new(e)
|
new(e)
|
||||||
e.msg = $pqErrorMessage(db)
|
e.msg = $pqErrorMessage(db)
|
||||||
raise e
|
raise e
|
||||||
|
|
||||||
proc dbError*(msg: string) {.noreturn.} =
|
|
||||||
## raises an EDb exception with message `msg`.
|
|
||||||
var e: ref EDb
|
|
||||||
new(e)
|
|
||||||
e.msg = msg
|
|
||||||
raise e
|
|
||||||
|
|
||||||
proc dbQuote*(s: string): string =
|
proc dbQuote*(s: string): string =
|
||||||
## DB quotes the string.
|
## DB quotes the string.
|
||||||
result = "'"
|
result = "'"
|
||||||
|
|
@ -127,7 +111,7 @@ proc dbFormat(formatstr: SqlQuery, args: varargs[string]): string =
|
||||||
add(result, c)
|
add(result, c)
|
||||||
|
|
||||||
proc tryExec*(db: DbConn, query: SqlQuery,
|
proc tryExec*(db: DbConn, query: SqlQuery,
|
||||||
args: varargs[string, `$`]): bool {.tags: [FReadDB, FWriteDb].} =
|
args: varargs[string, `$`]): bool {.tags: [ReadDbEffect, WriteDbEffect].} =
|
||||||
## tries to execute the query and returns true if successful, false otherwise.
|
## tries to execute the query and returns true if successful, false otherwise.
|
||||||
var res = pqexecParams(db, dbFormat(query, args), 0, nil, nil,
|
var res = pqexecParams(db, dbFormat(query, args), 0, nil, nil,
|
||||||
nil, nil, 0)
|
nil, nil, 0)
|
||||||
|
|
@ -135,7 +119,8 @@ proc tryExec*(db: DbConn, query: SqlQuery,
|
||||||
pqclear(res)
|
pqclear(res)
|
||||||
|
|
||||||
proc tryExec*(db: DbConn, stmtName: SqlPrepared,
|
proc tryExec*(db: DbConn, stmtName: SqlPrepared,
|
||||||
args: varargs[string, `$`]): bool {.tags: [FReadDB, FWriteDb].} =
|
args: varargs[string, `$`]): bool {.tags: [
|
||||||
|
ReadDbEffect, WriteDbEffect].} =
|
||||||
## tries to execute the query and returns true if successful, false otherwise.
|
## tries to execute the query and returns true if successful, false otherwise.
|
||||||
var arr = allocCStringArray(args)
|
var arr = allocCStringArray(args)
|
||||||
var res = pqexecPrepared(db, stmtName.string, int32(args.len), arr,
|
var res = pqexecPrepared(db, stmtName.string, int32(args.len), arr,
|
||||||
|
|
@ -145,7 +130,7 @@ proc tryExec*(db: DbConn, stmtName: SqlPrepared,
|
||||||
pqclear(res)
|
pqclear(res)
|
||||||
|
|
||||||
proc exec*(db: DbConn, query: SqlQuery, args: varargs[string, `$`]) {.
|
proc exec*(db: DbConn, query: SqlQuery, args: varargs[string, `$`]) {.
|
||||||
tags: [FReadDB, FWriteDb].} =
|
tags: [ReadDbEffect, WriteDbEffect].} =
|
||||||
## executes the query and raises EDB if not successful.
|
## executes the query and raises EDB if not successful.
|
||||||
var res = pqexecParams(db, dbFormat(query, args), 0, nil, nil,
|
var res = pqexecParams(db, dbFormat(query, args), 0, nil, nil,
|
||||||
nil, nil, 0)
|
nil, nil, 0)
|
||||||
|
|
@ -153,7 +138,7 @@ proc exec*(db: DbConn, query: SqlQuery, args: varargs[string, `$`]) {.
|
||||||
pqclear(res)
|
pqclear(res)
|
||||||
|
|
||||||
proc exec*(db: DbConn, stmtName: SqlPrepared,
|
proc exec*(db: DbConn, stmtName: SqlPrepared,
|
||||||
args: varargs[string]) {.tags: [FReadDB, FWriteDb].} =
|
args: varargs[string]) {.tags: [ReadDbEffect, WriteDbEffect].} =
|
||||||
var arr = allocCStringArray(args)
|
var arr = allocCStringArray(args)
|
||||||
var res = pqexecPrepared(db, stmtName.string, int32(args.len), arr,
|
var res = pqexecPrepared(db, stmtName.string, int32(args.len), arr,
|
||||||
nil, nil, 0)
|
nil, nil, 0)
|
||||||
|
|
@ -167,11 +152,7 @@ proc newRow(L: int): Row =
|
||||||
|
|
||||||
proc setupQuery(db: DbConn, query: SqlQuery,
|
proc setupQuery(db: DbConn, query: SqlQuery,
|
||||||
args: varargs[string]): PPGresult =
|
args: varargs[string]): PPGresult =
|
||||||
# s is a dummy unique id str for each setupQuery query
|
result = pqexec(db, dbFormat(query, args))
|
||||||
let s = "setupQuery_Query_" & string(query)
|
|
||||||
var res = pqprepare(db, s, dbFormat(query, args), 0, nil)
|
|
||||||
result = pqexecPrepared(db, s, 0, nil,
|
|
||||||
nil, nil, 0)
|
|
||||||
if pqResultStatus(result) != PGRES_TUPLES_OK: dbError(db)
|
if pqResultStatus(result) != PGRES_TUPLES_OK: dbError(db)
|
||||||
|
|
||||||
proc setupQuery(db: DbConn, stmtName: SqlPrepared,
|
proc setupQuery(db: DbConn, stmtName: SqlPrepared,
|
||||||
|
|
@ -184,8 +165,10 @@ proc setupQuery(db: DbConn, stmtName: SqlPrepared,
|
||||||
|
|
||||||
proc prepare*(db: DbConn; stmtName: string, query: SqlQuery;
|
proc prepare*(db: DbConn; stmtName: string, query: SqlQuery;
|
||||||
nParams: int): SqlPrepared =
|
nParams: int): SqlPrepared =
|
||||||
|
## Creates a new ``SqlPrepared`` statement. Parameter substitution is done
|
||||||
|
## via ``$1``, ``$2``, ``$3``, etc.
|
||||||
if nParams > 0 and not string(query).contains("$1"):
|
if nParams > 0 and not string(query).contains("$1"):
|
||||||
dbError("""parameter substitution expects "$1" """)
|
dbError("parameter substitution expects \"$1\"")
|
||||||
var res = pqprepare(db, stmtName, query.string, int32(nParams), nil)
|
var res = pqprepare(db, stmtName, query.string, int32(nParams), nil)
|
||||||
if pqResultStatus(res) != PGRES_COMMAND_OK: dbError(db)
|
if pqResultStatus(res) != PGRES_COMMAND_OK: dbError(db)
|
||||||
return SqlPrepared(stmtName)
|
return SqlPrepared(stmtName)
|
||||||
|
|
@ -200,7 +183,7 @@ proc setRow(res: PPGresult, r: var Row, line, cols: int32) =
|
||||||
add(r[col], x)
|
add(r[col], x)
|
||||||
|
|
||||||
iterator fastRows*(db: DbConn, query: SqlQuery,
|
iterator fastRows*(db: DbConn, query: SqlQuery,
|
||||||
args: varargs[string, `$`]): Row {.tags: [FReadDB].} =
|
args: varargs[string, `$`]): Row {.tags: [ReadDbEffect].} =
|
||||||
## executes the query and iterates over the result dataset. This is very
|
## executes the query and iterates over the result dataset. This is very
|
||||||
## fast, but potenially dangerous: If the for-loop-body executes another
|
## fast, but potenially dangerous: If the for-loop-body executes another
|
||||||
## query, the results can be undefined. For Postgres it is safe though.
|
## query, the results can be undefined. For Postgres it is safe though.
|
||||||
|
|
@ -213,7 +196,7 @@ iterator fastRows*(db: DbConn, query: SqlQuery,
|
||||||
pqclear(res)
|
pqclear(res)
|
||||||
|
|
||||||
iterator fastRows*(db: DbConn, stmtName: SqlPrepared,
|
iterator fastRows*(db: DbConn, stmtName: SqlPrepared,
|
||||||
args: varargs[string, `$`]): Row {.tags: [FReadDB].} =
|
args: varargs[string, `$`]): Row {.tags: [ReadDbEffect].} =
|
||||||
## executes the prepared query and iterates over the result dataset.
|
## executes the prepared query and iterates over the result dataset.
|
||||||
var res = setupQuery(db, stmtName, args)
|
var res = setupQuery(db, stmtName, args)
|
||||||
var L = pqNfields(res)
|
var L = pqNfields(res)
|
||||||
|
|
@ -225,9 +208,9 @@ iterator fastRows*(db: DbConn, stmtName: SqlPrepared,
|
||||||
|
|
||||||
iterator instantRows*(db: DbConn, query: SqlQuery,
|
iterator instantRows*(db: DbConn, query: SqlQuery,
|
||||||
args: varargs[string, `$`]): InstantRow
|
args: varargs[string, `$`]): InstantRow
|
||||||
{.tags: [FReadDb].} =
|
{.tags: [ReadDbEffect].} =
|
||||||
## same as fastRows but returns a handle that can be used to get column text
|
## same as fastRows but returns a handle that can be used to get column text
|
||||||
## on demand using []. Returned handle is valid only within interator body.
|
## on demand using []. Returned handle is valid only within iterator body.
|
||||||
var res = setupQuery(db, query, args)
|
var res = setupQuery(db, query, args)
|
||||||
for i in 0..pqNtuples(res)-1:
|
for i in 0..pqNtuples(res)-1:
|
||||||
yield (res: res, line: i)
|
yield (res: res, line: i)
|
||||||
|
|
@ -235,9 +218,9 @@ iterator instantRows*(db: DbConn, query: SqlQuery,
|
||||||
|
|
||||||
iterator instantRows*(db: DbConn, stmtName: SqlPrepared,
|
iterator instantRows*(db: DbConn, stmtName: SqlPrepared,
|
||||||
args: varargs[string, `$`]): InstantRow
|
args: varargs[string, `$`]): InstantRow
|
||||||
{.tags: [FReadDb].} =
|
{.tags: [ReadDbEffect].} =
|
||||||
## same as fastRows but returns a handle that can be used to get column text
|
## same as fastRows but returns a handle that can be used to get column text
|
||||||
## on demand using []. Returned handle is valid only within interator body.
|
## on demand using []. Returned handle is valid only within iterator body.
|
||||||
var res = setupQuery(db, stmtName, args)
|
var res = setupQuery(db, stmtName, args)
|
||||||
for i in 0..pqNtuples(res)-1:
|
for i in 0..pqNtuples(res)-1:
|
||||||
yield (res: res, line: i)
|
yield (res: res, line: i)
|
||||||
|
|
@ -252,7 +235,7 @@ proc len*(row: InstantRow): int32 {.inline.} =
|
||||||
pqNfields(row.res)
|
pqNfields(row.res)
|
||||||
|
|
||||||
proc getRow*(db: DbConn, query: SqlQuery,
|
proc getRow*(db: DbConn, query: SqlQuery,
|
||||||
args: varargs[string, `$`]): Row {.tags: [FReadDB].} =
|
args: varargs[string, `$`]): Row {.tags: [ReadDbEffect].} =
|
||||||
## retrieves a single row. If the query doesn't return any rows, this proc
|
## retrieves a single row. If the query doesn't return any rows, this proc
|
||||||
## will return a Row with empty strings for each column.
|
## will return a Row with empty strings for each column.
|
||||||
var res = setupQuery(db, query, args)
|
var res = setupQuery(db, query, args)
|
||||||
|
|
@ -262,7 +245,7 @@ proc getRow*(db: DbConn, query: SqlQuery,
|
||||||
pqclear(res)
|
pqclear(res)
|
||||||
|
|
||||||
proc getRow*(db: DbConn, stmtName: SqlPrepared,
|
proc getRow*(db: DbConn, stmtName: SqlPrepared,
|
||||||
args: varargs[string, `$`]): Row {.tags: [FReadDB].} =
|
args: varargs[string, `$`]): Row {.tags: [ReadDbEffect].} =
|
||||||
var res = setupQuery(db, stmtName, args)
|
var res = setupQuery(db, stmtName, args)
|
||||||
var L = pqNfields(res)
|
var L = pqNfields(res)
|
||||||
result = newRow(L)
|
result = newRow(L)
|
||||||
|
|
@ -270,39 +253,52 @@ proc getRow*(db: DbConn, stmtName: SqlPrepared,
|
||||||
pqClear(res)
|
pqClear(res)
|
||||||
|
|
||||||
proc getAllRows*(db: DbConn, query: SqlQuery,
|
proc getAllRows*(db: DbConn, query: SqlQuery,
|
||||||
args: varargs[string, `$`]): seq[Row] {.tags: [FReadDB].} =
|
args: varargs[string, `$`]): seq[Row] {.
|
||||||
|
tags: [ReadDbEffect].} =
|
||||||
## executes the query and returns the whole result dataset.
|
## executes the query and returns the whole result dataset.
|
||||||
result = @[]
|
result = @[]
|
||||||
for r in fastRows(db, query, args):
|
for r in fastRows(db, query, args):
|
||||||
result.add(r)
|
result.add(r)
|
||||||
|
|
||||||
proc getAllRows*(db: DbConn, stmtName: SqlPrepared,
|
proc getAllRows*(db: DbConn, stmtName: SqlPrepared,
|
||||||
args: varargs[string, `$`]): seq[Row] {.tags: [FReadDB].} =
|
args: varargs[string, `$`]): seq[Row] {.tags:
|
||||||
|
[ReadDbEffect].} =
|
||||||
## executes the prepared query and returns the whole result dataset.
|
## executes the prepared query and returns the whole result dataset.
|
||||||
result = @[]
|
result = @[]
|
||||||
for r in fastRows(db, stmtName, args):
|
for r in fastRows(db, stmtName, args):
|
||||||
result.add(r)
|
result.add(r)
|
||||||
|
|
||||||
iterator rows*(db: DbConn, query: SqlQuery,
|
iterator rows*(db: DbConn, query: SqlQuery,
|
||||||
args: varargs[string, `$`]): Row {.tags: [FReadDB].} =
|
args: varargs[string, `$`]): Row {.tags: [ReadDbEffect].} =
|
||||||
## same as `fastRows`, but slower and safe.
|
## same as `fastRows`, but slower and safe.
|
||||||
for r in items(getAllRows(db, query, args)): yield r
|
for r in items(getAllRows(db, query, args)): yield r
|
||||||
|
|
||||||
iterator rows*(db: DbConn, stmtName: SqlPrepared,
|
iterator rows*(db: DbConn, stmtName: SqlPrepared,
|
||||||
args: varargs[string, `$`]): Row {.tags: [FReadDB].} =
|
args: varargs[string, `$`]): Row {.tags: [ReadDbEffect].} =
|
||||||
## same as `fastRows`, but slower and safe.
|
## same as `fastRows`, but slower and safe.
|
||||||
for r in items(getAllRows(db, stmtName, args)): yield r
|
for r in items(getAllRows(db, stmtName, args)): yield r
|
||||||
|
|
||||||
proc getValue*(db: DbConn, query: SqlQuery,
|
proc getValue*(db: DbConn, query: SqlQuery,
|
||||||
args: varargs[string, `$`]): string {.tags: [FReadDB].} =
|
args: varargs[string, `$`]): string {.
|
||||||
|
tags: [ReadDbEffect].} =
|
||||||
## executes the query and returns the first column of the first row of the
|
## executes the query and returns the first column of the first row of the
|
||||||
## result dataset. Returns "" if the dataset contains no rows or the database
|
## result dataset. Returns "" if the dataset contains no rows or the database
|
||||||
## value is NULL.
|
## value is NULL.
|
||||||
var x = pqgetvalue(setupQuery(db, query, args), 0, 0)
|
var x = pqgetvalue(setupQuery(db, query, args), 0, 0)
|
||||||
result = if isNil(x): "" else: $x
|
result = if isNil(x): "" else: $x
|
||||||
|
|
||||||
|
proc getValue*(db: DbConn, stmtName: SqlPrepared,
|
||||||
|
args: varargs[string, `$`]): string {.
|
||||||
|
tags: [ReadDbEffect].} =
|
||||||
|
## executes the query and returns the first column of the first row of the
|
||||||
|
## result dataset. Returns "" if the dataset contains no rows or the database
|
||||||
|
## value is NULL.
|
||||||
|
var x = pqgetvalue(setupQuery(db, stmtName, args), 0, 0)
|
||||||
|
result = if isNil(x): "" else: $x
|
||||||
|
|
||||||
proc tryInsertID*(db: DbConn, query: SqlQuery,
|
proc tryInsertID*(db: DbConn, query: SqlQuery,
|
||||||
args: varargs[string, `$`]): int64 {.tags: [FWriteDb].}=
|
args: varargs[string, `$`]): int64 {.
|
||||||
|
tags: [WriteDbEffect].}=
|
||||||
## executes the query (typically "INSERT") and returns the
|
## executes the query (typically "INSERT") and returns the
|
||||||
## generated ID for the row or -1 in case of an error. For Postgre this adds
|
## generated ID for the row or -1 in case of an error. For Postgre this adds
|
||||||
## ``RETURNING id`` to the query, so it only works if your primary key is
|
## ``RETURNING id`` to the query, so it only works if your primary key is
|
||||||
|
|
@ -315,7 +311,8 @@ proc tryInsertID*(db: DbConn, query: SqlQuery,
|
||||||
result = -1
|
result = -1
|
||||||
|
|
||||||
proc insertID*(db: DbConn, query: SqlQuery,
|
proc insertID*(db: DbConn, query: SqlQuery,
|
||||||
args: varargs[string, `$`]): int64 {.tags: [FWriteDb].} =
|
args: varargs[string, `$`]): int64 {.
|
||||||
|
tags: [WriteDbEffect].} =
|
||||||
## executes the query (typically "INSERT") and returns the
|
## executes the query (typically "INSERT") and returns the
|
||||||
## generated ID for the row. For Postgre this adds
|
## generated ID for the row. For Postgre this adds
|
||||||
## ``RETURNING id`` to the query, so it only works if your primary key is
|
## ``RETURNING id`` to the query, so it only works if your primary key is
|
||||||
|
|
@ -325,7 +322,7 @@ proc insertID*(db: DbConn, query: SqlQuery,
|
||||||
|
|
||||||
proc execAffectedRows*(db: DbConn, query: SqlQuery,
|
proc execAffectedRows*(db: DbConn, query: SqlQuery,
|
||||||
args: varargs[string, `$`]): int64 {.tags: [
|
args: varargs[string, `$`]): int64 {.tags: [
|
||||||
FReadDB, FWriteDb].} =
|
ReadDbEffect, WriteDbEffect].} =
|
||||||
## executes the query (typically "UPDATE") and returns the
|
## executes the query (typically "UPDATE") and returns the
|
||||||
## number of affected rows.
|
## number of affected rows.
|
||||||
var q = dbFormat(query, args)
|
var q = dbFormat(query, args)
|
||||||
|
|
@ -336,7 +333,7 @@ proc execAffectedRows*(db: DbConn, query: SqlQuery,
|
||||||
|
|
||||||
proc execAffectedRows*(db: DbConn, stmtName: SqlPrepared,
|
proc execAffectedRows*(db: DbConn, stmtName: SqlPrepared,
|
||||||
args: varargs[string, `$`]): int64 {.tags: [
|
args: varargs[string, `$`]): int64 {.tags: [
|
||||||
FReadDB, FWriteDb].} =
|
ReadDbEffect, WriteDbEffect].} =
|
||||||
## executes the query (typically "UPDATE") and returns the
|
## executes the query (typically "UPDATE") and returns the
|
||||||
## number of affected rows.
|
## number of affected rows.
|
||||||
var arr = allocCStringArray(args)
|
var arr = allocCStringArray(args)
|
||||||
|
|
@ -347,12 +344,12 @@ proc execAffectedRows*(db: DbConn, stmtName: SqlPrepared,
|
||||||
result = parseBiggestInt($pqcmdTuples(res))
|
result = parseBiggestInt($pqcmdTuples(res))
|
||||||
pqclear(res)
|
pqclear(res)
|
||||||
|
|
||||||
proc close*(db: DbConn) {.tags: [FDb].} =
|
proc close*(db: DbConn) {.tags: [DbEffect].} =
|
||||||
## closes the database connection.
|
## closes the database connection.
|
||||||
if db != nil: pqfinish(db)
|
if db != nil: pqfinish(db)
|
||||||
|
|
||||||
proc open*(connection, user, password, database: string): DbConn {.
|
proc open*(connection, user, password, database: string): DbConn {.
|
||||||
tags: [FDb].} =
|
tags: [DbEffect].} =
|
||||||
## opens a database connection. Raises `EDb` if the connection could not
|
## opens a database connection. Raises `EDb` if the connection could not
|
||||||
## be established.
|
## be established.
|
||||||
##
|
##
|
||||||
|
|
@ -374,10 +371,10 @@ proc open*(connection, user, password, database: string): DbConn {.
|
||||||
if pqStatus(result) != CONNECTION_OK: dbError(result) # result = nil
|
if pqStatus(result) != CONNECTION_OK: dbError(result) # result = nil
|
||||||
|
|
||||||
proc setEncoding*(connection: DbConn, encoding: string): bool {.
|
proc setEncoding*(connection: DbConn, encoding: string): bool {.
|
||||||
tags: [FDb].} =
|
tags: [DbEffect].} =
|
||||||
## sets the encoding of a database connection, returns true for
|
## sets the encoding of a database connection, returns true for
|
||||||
## success, false for failure.
|
## success, false for failure.
|
||||||
return pqsetClientEncoding(connection, encoding) == 0
|
return pqsetClientEncoding(connection, encoding) == 0
|
||||||
|
|
||||||
|
|
||||||
# Tests are in ../../tests/untestable/tpostgres.
|
# Tests are in ../../tests/untestable/tpostgres.
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
#
|
#
|
||||||
#
|
#
|
||||||
# Nim's Runtime Library
|
# Nim's Runtime Library
|
||||||
# (c) Copyright 2012 Andreas Rumpf
|
# (c) Copyright 2015 Andreas Rumpf
|
||||||
#
|
#
|
||||||
# See the file "copying.txt", included in this
|
# See the file "copying.txt", included in this
|
||||||
# distribution, for details about the copyright.
|
# distribution, for details about the copyright.
|
||||||
|
|
@ -10,7 +10,48 @@
|
||||||
## A higher level `SQLite`:idx: database wrapper. This interface
|
## A higher level `SQLite`:idx: database wrapper. This interface
|
||||||
## is implemented for other databases too.
|
## is implemented for other databases too.
|
||||||
##
|
##
|
||||||
## Example:
|
## See also: `db_odbc <db_odbc.html>`_, `db_postgres <db_postgres.html>`_,
|
||||||
|
## `db_mysql <db_mysql.html>`_.
|
||||||
|
##
|
||||||
|
## Parameter substitution
|
||||||
|
## ----------------------
|
||||||
|
##
|
||||||
|
## All ``db_*`` modules support the same form of parameter substitution.
|
||||||
|
## That is, using the ``?`` (question mark) to signify the place where a
|
||||||
|
## value should be placed. For example:
|
||||||
|
##
|
||||||
|
## .. code-block:: Nim
|
||||||
|
## sql"INSERT INTO myTable (colA, colB, colC) VALUES (?, ?, ?)"
|
||||||
|
##
|
||||||
|
## Examples
|
||||||
|
## --------
|
||||||
|
##
|
||||||
|
## Opening a connection to a database
|
||||||
|
## ==================================
|
||||||
|
##
|
||||||
|
## .. code-block:: Nim
|
||||||
|
## import db_sqlite
|
||||||
|
## let db = open("localhost", "user", "password", "dbname")
|
||||||
|
## db.close()
|
||||||
|
##
|
||||||
|
## Creating a table
|
||||||
|
## ================
|
||||||
|
##
|
||||||
|
## .. code-block:: Nim
|
||||||
|
## db.exec(sql"DROP TABLE IF EXISTS myTable")
|
||||||
|
## db.exec(sql("""CREATE TABLE myTable (
|
||||||
|
## id integer,
|
||||||
|
## name varchar(50) not null)"""))
|
||||||
|
##
|
||||||
|
## Inserting data
|
||||||
|
## ==============
|
||||||
|
##
|
||||||
|
## .. code-block:: Nim
|
||||||
|
## db.exec(sql"INSERT INTO myTable (id, name) VALUES (0, ?)",
|
||||||
|
## "Jack")
|
||||||
|
##
|
||||||
|
## Larger example
|
||||||
|
## ==============
|
||||||
##
|
##
|
||||||
## .. code-block:: nim
|
## .. code-block:: nim
|
||||||
##
|
##
|
||||||
|
|
@ -40,47 +81,30 @@
|
||||||
##
|
##
|
||||||
## theDb.close()
|
## theDb.close()
|
||||||
|
|
||||||
|
{.deadCodeElim:on.}
|
||||||
|
|
||||||
import strutils, sqlite3
|
import strutils, sqlite3
|
||||||
|
|
||||||
|
import db_common
|
||||||
|
export db_common
|
||||||
|
|
||||||
type
|
type
|
||||||
DbConn* = PSqlite3 ## encapsulates a database connection
|
DbConn* = PSqlite3 ## encapsulates a database connection
|
||||||
Row* = seq[string] ## a row of a dataset. NULL database values will be
|
Row* = seq[string] ## a row of a dataset. NULL database values will be
|
||||||
## transformed always to the empty string.
|
## converted to nil.
|
||||||
InstantRow* = Pstmt ## a handle that can be used to get a row's column
|
InstantRow* = Pstmt ## a handle that can be used to get a row's column
|
||||||
## text on demand
|
## text on demand
|
||||||
EDb* = object of IOError ## exception that is raised if a database error occurs
|
{.deprecated: [TRow: Row, TDbConn: DbConn].}
|
||||||
|
|
||||||
SqlQuery* = distinct string ## an SQL query string
|
proc dbError*(db: DbConn) {.noreturn.} =
|
||||||
|
## raises a DbError exception.
|
||||||
FDb* = object of IOEffect ## effect that denotes a database operation
|
var e: ref DbError
|
||||||
FReadDb* = object of FDb ## effect that denotes a read operation
|
|
||||||
FWriteDb* = object of FDb ## effect that denotes a write operation
|
|
||||||
{.deprecated: [TRow: Row, TSqlQuery: SqlQuery, TDbConn: DbConn].}
|
|
||||||
|
|
||||||
proc sql*(query: string): SqlQuery {.noSideEffect, inline.} =
|
|
||||||
## constructs a SqlQuery from the string `query`. This is supposed to be
|
|
||||||
## used as a raw-string-literal modifier:
|
|
||||||
## ``sql"update user set counter = counter + 1"``
|
|
||||||
##
|
|
||||||
## If assertions are turned off, it does nothing. If assertions are turned
|
|
||||||
## on, later versions will check the string for valid syntax.
|
|
||||||
result = SqlQuery(query)
|
|
||||||
|
|
||||||
proc dbError(db: DbConn) {.noreturn.} =
|
|
||||||
## raises an EDb exception.
|
|
||||||
var e: ref EDb
|
|
||||||
new(e)
|
new(e)
|
||||||
e.msg = $sqlite3.errmsg(db)
|
e.msg = $sqlite3.errmsg(db)
|
||||||
raise e
|
raise e
|
||||||
|
|
||||||
proc dbError*(msg: string) {.noreturn.} =
|
proc dbQuote*(s: string): string =
|
||||||
## raises an EDb exception with message `msg`.
|
## DB quotes the string.
|
||||||
var e: ref EDb
|
|
||||||
new(e)
|
|
||||||
e.msg = msg
|
|
||||||
raise e
|
|
||||||
|
|
||||||
proc dbQuote(s: string): string =
|
|
||||||
if s.isNil: return "NULL"
|
if s.isNil: return "NULL"
|
||||||
result = "'"
|
result = "'"
|
||||||
for c in items(s):
|
for c in items(s):
|
||||||
|
|
@ -99,7 +123,8 @@ proc dbFormat(formatstr: SqlQuery, args: varargs[string]): string =
|
||||||
add(result, c)
|
add(result, c)
|
||||||
|
|
||||||
proc tryExec*(db: DbConn, query: SqlQuery,
|
proc tryExec*(db: DbConn, query: SqlQuery,
|
||||||
args: varargs[string, `$`]): bool {.tags: [FReadDb, FWriteDb].} =
|
args: varargs[string, `$`]): bool {.
|
||||||
|
tags: [ReadDbEffect, WriteDbEffect].} =
|
||||||
## tries to execute the query and returns true if successful, false otherwise.
|
## tries to execute the query and returns true if successful, false otherwise.
|
||||||
var q = dbFormat(query, args)
|
var q = dbFormat(query, args)
|
||||||
var stmt: sqlite3.Pstmt
|
var stmt: sqlite3.Pstmt
|
||||||
|
|
@ -108,8 +133,8 @@ proc tryExec*(db: DbConn, query: SqlQuery,
|
||||||
result = finalize(stmt) == SQLITE_OK
|
result = finalize(stmt) == SQLITE_OK
|
||||||
|
|
||||||
proc exec*(db: DbConn, query: SqlQuery, args: varargs[string, `$`]) {.
|
proc exec*(db: DbConn, query: SqlQuery, args: varargs[string, `$`]) {.
|
||||||
tags: [FReadDb, FWriteDb].} =
|
tags: [ReadDbEffect, WriteDbEffect].} =
|
||||||
## executes the query and raises EDB if not successful.
|
## executes the query and raises DbError if not successful.
|
||||||
if not tryExec(db, query, args): dbError(db)
|
if not tryExec(db, query, args): dbError(db)
|
||||||
|
|
||||||
proc newRow(L: int): Row =
|
proc newRow(L: int): Row =
|
||||||
|
|
@ -129,14 +154,14 @@ proc setRow(stmt: Pstmt, r: var Row, cols: cint) =
|
||||||
if not isNil(x): add(r[col], x)
|
if not isNil(x): add(r[col], x)
|
||||||
|
|
||||||
iterator fastRows*(db: DbConn, query: SqlQuery,
|
iterator fastRows*(db: DbConn, query: SqlQuery,
|
||||||
args: varargs[string, `$`]): Row {.tags: [FReadDb].} =
|
args: varargs[string, `$`]): Row {.tags: [ReadDbEffect].} =
|
||||||
## Executes the query and iterates over the result dataset.
|
## Executes the query and iterates over the result dataset.
|
||||||
##
|
##
|
||||||
## This is very fast, but potentially dangerous. Use this iterator only
|
## This is very fast, but potentially dangerous. Use this iterator only
|
||||||
## if you require **ALL** the rows.
|
## if you require **ALL** the rows.
|
||||||
##
|
##
|
||||||
## Breaking the fastRows() iterator during a loop will cause the next
|
## Breaking the fastRows() iterator during a loop will cause the next
|
||||||
## database query to raise an [EDb] exception ``unable to close due to ...``.
|
## database query to raise a DbError exception ``unable to close due to ...``.
|
||||||
var stmt = setupQuery(db, query, args)
|
var stmt = setupQuery(db, query, args)
|
||||||
var L = (column_count(stmt))
|
var L = (column_count(stmt))
|
||||||
var result = newRow(L)
|
var result = newRow(L)
|
||||||
|
|
@ -147,14 +172,47 @@ iterator fastRows*(db: DbConn, query: SqlQuery,
|
||||||
|
|
||||||
iterator instantRows*(db: DbConn, query: SqlQuery,
|
iterator instantRows*(db: DbConn, query: SqlQuery,
|
||||||
args: varargs[string, `$`]): InstantRow
|
args: varargs[string, `$`]): InstantRow
|
||||||
{.tags: [FReadDb].} =
|
{.tags: [ReadDbEffect].} =
|
||||||
## same as fastRows but returns a handle that can be used to get column text
|
## same as fastRows but returns a handle that can be used to get column text
|
||||||
## on demand using []. Returned handle is valid only within the interator body.
|
## on demand using []. Returned handle is valid only within the iterator body.
|
||||||
var stmt = setupQuery(db, query, args)
|
var stmt = setupQuery(db, query, args)
|
||||||
while step(stmt) == SQLITE_ROW:
|
while step(stmt) == SQLITE_ROW:
|
||||||
yield stmt
|
yield stmt
|
||||||
if finalize(stmt) != SQLITE_OK: dbError(db)
|
if finalize(stmt) != SQLITE_OK: dbError(db)
|
||||||
|
|
||||||
|
proc toTypeKind(t: var DbType; x: int32) =
|
||||||
|
case x
|
||||||
|
of SQLITE_INTEGER:
|
||||||
|
t.kind = dbInt
|
||||||
|
t.size = 8
|
||||||
|
of SQLITE_FLOAT:
|
||||||
|
t.kind = dbFloat
|
||||||
|
t.size = 8
|
||||||
|
of SQLITE_BLOB: t.kind = dbBlob
|
||||||
|
of SQLITE_NULL: t.kind = dbNull
|
||||||
|
of SQLITE_TEXT: t.kind = dbVarchar
|
||||||
|
else: t.kind = dbUnknown
|
||||||
|
|
||||||
|
proc setColumns(columns: var DbColumns; x: PStmt) =
|
||||||
|
let L = column_count(x)
|
||||||
|
setLen(columns, L)
|
||||||
|
for i in 0'i32 ..< L:
|
||||||
|
columns[i].name = $column_name(x, i)
|
||||||
|
columns[i].typ.name = $column_decltype(x, i)
|
||||||
|
toTypeKind(columns[i].typ, column_type(x, i))
|
||||||
|
columns[i].tableName = $column_table_name(x, i)
|
||||||
|
|
||||||
|
iterator instantRows*(db: DbConn; columns: var DbColumns; query: SqlQuery,
|
||||||
|
args: varargs[string, `$`]): InstantRow
|
||||||
|
{.tags: [ReadDbEffect].} =
|
||||||
|
## same as fastRows but returns a handle that can be used to get column text
|
||||||
|
## on demand using []. Returned handle is valid only within the iterator body.
|
||||||
|
var stmt = setupQuery(db, query, args)
|
||||||
|
setColumns(columns, stmt)
|
||||||
|
while step(stmt) == SQLITE_ROW:
|
||||||
|
yield stmt
|
||||||
|
if finalize(stmt) != SQLITE_OK: dbError(db)
|
||||||
|
|
||||||
proc `[]`*(row: InstantRow, col: int32): string {.inline.} =
|
proc `[]`*(row: InstantRow, col: int32): string {.inline.} =
|
||||||
## returns text for given column of the row
|
## returns text for given column of the row
|
||||||
$column_text(row, col)
|
$column_text(row, col)
|
||||||
|
|
@ -164,7 +222,7 @@ proc len*(row: InstantRow): int32 {.inline.} =
|
||||||
column_count(row)
|
column_count(row)
|
||||||
|
|
||||||
proc getRow*(db: DbConn, query: SqlQuery,
|
proc getRow*(db: DbConn, query: SqlQuery,
|
||||||
args: varargs[string, `$`]): Row {.tags: [FReadDb].} =
|
args: varargs[string, `$`]): Row {.tags: [ReadDbEffect].} =
|
||||||
## retrieves a single row. If the query doesn't return any rows, this proc
|
## retrieves a single row. If the query doesn't return any rows, this proc
|
||||||
## will return a Row with empty strings for each column.
|
## will return a Row with empty strings for each column.
|
||||||
var stmt = setupQuery(db, query, args)
|
var stmt = setupQuery(db, query, args)
|
||||||
|
|
@ -175,19 +233,19 @@ proc getRow*(db: DbConn, query: SqlQuery,
|
||||||
if finalize(stmt) != SQLITE_OK: dbError(db)
|
if finalize(stmt) != SQLITE_OK: dbError(db)
|
||||||
|
|
||||||
proc getAllRows*(db: DbConn, query: SqlQuery,
|
proc getAllRows*(db: DbConn, query: SqlQuery,
|
||||||
args: varargs[string, `$`]): seq[Row] {.tags: [FReadDb].} =
|
args: varargs[string, `$`]): seq[Row] {.tags: [ReadDbEffect].} =
|
||||||
## executes the query and returns the whole result dataset.
|
## executes the query and returns the whole result dataset.
|
||||||
result = @[]
|
result = @[]
|
||||||
for r in fastRows(db, query, args):
|
for r in fastRows(db, query, args):
|
||||||
result.add(r)
|
result.add(r)
|
||||||
|
|
||||||
iterator rows*(db: DbConn, query: SqlQuery,
|
iterator rows*(db: DbConn, query: SqlQuery,
|
||||||
args: varargs[string, `$`]): Row {.tags: [FReadDb].} =
|
args: varargs[string, `$`]): Row {.tags: [ReadDbEffect].} =
|
||||||
## same as `FastRows`, but slower and safe.
|
## same as `FastRows`, but slower and safe.
|
||||||
for r in fastRows(db, query, args): yield r
|
for r in fastRows(db, query, args): yield r
|
||||||
|
|
||||||
proc getValue*(db: DbConn, query: SqlQuery,
|
proc getValue*(db: DbConn, query: SqlQuery,
|
||||||
args: varargs[string, `$`]): string {.tags: [FReadDb].} =
|
args: varargs[string, `$`]): string {.tags: [ReadDbEffect].} =
|
||||||
## executes the query and returns the first column of the first row of the
|
## executes the query and returns the first column of the first row of the
|
||||||
## result dataset. Returns "" if the dataset contains no rows or the database
|
## result dataset. Returns "" if the dataset contains no rows or the database
|
||||||
## value is NULL.
|
## value is NULL.
|
||||||
|
|
@ -205,7 +263,7 @@ proc getValue*(db: DbConn, query: SqlQuery,
|
||||||
|
|
||||||
proc tryInsertID*(db: DbConn, query: SqlQuery,
|
proc tryInsertID*(db: DbConn, query: SqlQuery,
|
||||||
args: varargs[string, `$`]): int64
|
args: varargs[string, `$`]): int64
|
||||||
{.tags: [FWriteDb], raises: [].} =
|
{.tags: [WriteDbEffect], raises: [].} =
|
||||||
## executes the query (typically "INSERT") and returns the
|
## executes the query (typically "INSERT") and returns the
|
||||||
## generated ID for the row or -1 in case of an error.
|
## generated ID for the row or -1 in case of an error.
|
||||||
var q = dbFormat(query, args)
|
var q = dbFormat(query, args)
|
||||||
|
|
@ -218,7 +276,7 @@ proc tryInsertID*(db: DbConn, query: SqlQuery,
|
||||||
result = -1
|
result = -1
|
||||||
|
|
||||||
proc insertID*(db: DbConn, query: SqlQuery,
|
proc insertID*(db: DbConn, query: SqlQuery,
|
||||||
args: varargs[string, `$`]): int64 {.tags: [FWriteDb].} =
|
args: varargs[string, `$`]): int64 {.tags: [WriteDbEffect].} =
|
||||||
## executes the query (typically "INSERT") and returns the
|
## executes the query (typically "INSERT") and returns the
|
||||||
## generated ID for the row. For Postgre this adds
|
## generated ID for the row. For Postgre this adds
|
||||||
## ``RETURNING id`` to the query, so it only works if your primary key is
|
## ``RETURNING id`` to the query, so it only works if your primary key is
|
||||||
|
|
@ -228,18 +286,18 @@ proc insertID*(db: DbConn, query: SqlQuery,
|
||||||
|
|
||||||
proc execAffectedRows*(db: DbConn, query: SqlQuery,
|
proc execAffectedRows*(db: DbConn, query: SqlQuery,
|
||||||
args: varargs[string, `$`]): int64 {.
|
args: varargs[string, `$`]): int64 {.
|
||||||
tags: [FReadDb, FWriteDb].} =
|
tags: [ReadDbEffect, WriteDbEffect].} =
|
||||||
## executes the query (typically "UPDATE") and returns the
|
## executes the query (typically "UPDATE") and returns the
|
||||||
## number of affected rows.
|
## number of affected rows.
|
||||||
exec(db, query, args)
|
exec(db, query, args)
|
||||||
result = changes(db)
|
result = changes(db)
|
||||||
|
|
||||||
proc close*(db: DbConn) {.tags: [FDb].} =
|
proc close*(db: DbConn) {.tags: [DbEffect].} =
|
||||||
## closes the database connection.
|
## closes the database connection.
|
||||||
if sqlite3.close(db) != SQLITE_OK: dbError(db)
|
if sqlite3.close(db) != SQLITE_OK: dbError(db)
|
||||||
|
|
||||||
proc open*(connection, user, password, database: string): DbConn {.
|
proc open*(connection, user, password, database: string): DbConn {.
|
||||||
tags: [FDb].} =
|
tags: [DbEffect].} =
|
||||||
## opens a database connection. Raises `EDb` if the connection could not
|
## opens a database connection. Raises `EDb` if the connection could not
|
||||||
## be established. Only the ``connection`` parameter is used for ``sqlite``.
|
## be established. Only the ``connection`` parameter is used for ``sqlite``.
|
||||||
var db: DbConn
|
var db: DbConn
|
||||||
|
|
@ -249,7 +307,7 @@ proc open*(connection, user, password, database: string): DbConn {.
|
||||||
dbError(db)
|
dbError(db)
|
||||||
|
|
||||||
proc setEncoding*(connection: DbConn, encoding: string): bool {.
|
proc setEncoding*(connection: DbConn, encoding: string): bool {.
|
||||||
tags: [FDb].} =
|
tags: [DbEffect].} =
|
||||||
## sets the encoding of a database connection, returns true for
|
## sets the encoding of a database connection, returns true for
|
||||||
## success, false for failure.
|
## success, false for failure.
|
||||||
##
|
##
|
||||||
|
|
|
||||||
306
lib/js/dom.nim
306
lib/js/dom.nim
|
|
@ -14,36 +14,35 @@ when not defined(js) and not defined(Nimdoc):
|
||||||
{.error: "This module only works on the JavaScript platform".}
|
{.error: "This module only works on the JavaScript platform".}
|
||||||
|
|
||||||
type
|
type
|
||||||
TEventHandlers* {.importc.} = object of RootObj
|
EventTarget* = ref EventTargetObj
|
||||||
onabort*: proc (event: ref TEvent) {.nimcall.}
|
EventTargetObj {.importc.} = object of RootObj
|
||||||
onblur*: proc (event: ref TEvent) {.nimcall.}
|
onabort*: proc (event: Event) {.nimcall.}
|
||||||
onchange*: proc (event: ref TEvent) {.nimcall.}
|
onblur*: proc (event: Event) {.nimcall.}
|
||||||
onclick*: proc (event: ref TEvent) {.nimcall.}
|
onchange*: proc (event: Event) {.nimcall.}
|
||||||
ondblclick*: proc (event: ref TEvent) {.nimcall.}
|
onclick*: proc (event: Event) {.nimcall.}
|
||||||
onerror*: proc (event: ref TEvent) {.nimcall.}
|
ondblclick*: proc (event: Event) {.nimcall.}
|
||||||
onfocus*: proc (event: ref TEvent) {.nimcall.}
|
onerror*: proc (event: Event) {.nimcall.}
|
||||||
onkeydown*: proc (event: ref TEvent) {.nimcall.}
|
onfocus*: proc (event: Event) {.nimcall.}
|
||||||
onkeypress*: proc (event: ref TEvent) {.nimcall.}
|
onkeydown*: proc (event: Event) {.nimcall.}
|
||||||
onkeyup*: proc (event: ref TEvent) {.nimcall.}
|
onkeypress*: proc (event: Event) {.nimcall.}
|
||||||
onload*: proc (event: ref TEvent) {.nimcall.}
|
onkeyup*: proc (event: Event) {.nimcall.}
|
||||||
onmousedown*: proc (event: ref TEvent) {.nimcall.}
|
onload*: proc (event: Event) {.nimcall.}
|
||||||
onmousemove*: proc (event: ref TEvent) {.nimcall.}
|
onmousedown*: proc (event: Event) {.nimcall.}
|
||||||
onmouseout*: proc (event: ref TEvent) {.nimcall.}
|
onmousemove*: proc (event: Event) {.nimcall.}
|
||||||
onmouseover*: proc (event: ref TEvent) {.nimcall.}
|
onmouseout*: proc (event: Event) {.nimcall.}
|
||||||
onmouseup*: proc (event: ref TEvent) {.nimcall.}
|
onmouseover*: proc (event: Event) {.nimcall.}
|
||||||
onreset*: proc (event: ref TEvent) {.nimcall.}
|
onmouseup*: proc (event: Event) {.nimcall.}
|
||||||
onselect*: proc (event: ref TEvent) {.nimcall.}
|
onreset*: proc (event: Event) {.nimcall.}
|
||||||
onsubmit*: proc (event: ref TEvent) {.nimcall.}
|
onselect*: proc (event: Event) {.nimcall.}
|
||||||
onunload*: proc (event: ref TEvent) {.nimcall.}
|
onsubmit*: proc (event: Event) {.nimcall.}
|
||||||
|
onunload*: proc (event: Event) {.nimcall.}
|
||||||
addEventListener*: proc(ev: cstring, cb: proc(ev: ref TEvent), useCapture: bool = false) {.nimcall.}
|
|
||||||
|
|
||||||
Window* = ref WindowObj
|
Window* = ref WindowObj
|
||||||
WindowObj {.importc.} = object of TEventHandlers
|
WindowObj {.importc.} = object of EventTargetObj
|
||||||
document*: Document
|
document*: Document
|
||||||
event*: ref TEvent
|
event*: Event
|
||||||
history*: ref THistory
|
history*: History
|
||||||
location*: ref TLocation
|
location*: Location
|
||||||
closed*: bool
|
closed*: bool
|
||||||
defaultStatus*: cstring
|
defaultStatus*: cstring
|
||||||
innerHeight*, innerWidth*: int
|
innerHeight*, innerWidth*: int
|
||||||
|
|
@ -57,50 +56,15 @@ type
|
||||||
statusbar*: ref TStatusBar
|
statusbar*: ref TStatusBar
|
||||||
status*: cstring
|
status*: cstring
|
||||||
toolbar*: ref TToolBar
|
toolbar*: ref TToolBar
|
||||||
|
|
||||||
alert*: proc (msg: cstring) {.nimcall.}
|
|
||||||
back*: proc () {.nimcall.}
|
|
||||||
blur*: proc () {.nimcall.}
|
|
||||||
captureEvents*: proc (eventMask: int) {.nimcall.}
|
|
||||||
clearInterval*: proc (interval: ref TInterval) {.nimcall.}
|
|
||||||
clearTimeout*: proc (timeout: ref TTimeOut) {.nimcall.}
|
|
||||||
close*: proc () {.nimcall.}
|
|
||||||
confirm*: proc (msg: cstring): bool {.nimcall.}
|
|
||||||
disableExternalCapture*: proc () {.nimcall.}
|
|
||||||
enableExternalCapture*: proc () {.nimcall.}
|
|
||||||
find*: proc (text: cstring, caseSensitive = false,
|
|
||||||
backwards = false) {.nimcall.}
|
|
||||||
focus*: proc () {.nimcall.}
|
|
||||||
forward*: proc () {.nimcall.}
|
|
||||||
handleEvent*: proc (e: ref TEvent) {.nimcall.}
|
|
||||||
home*: proc () {.nimcall.}
|
|
||||||
moveBy*: proc (x, y: int) {.nimcall.}
|
|
||||||
moveTo*: proc (x, y: int) {.nimcall.}
|
|
||||||
open*: proc (uri, windowname: cstring,
|
|
||||||
properties: cstring = nil): Window {.nimcall.}
|
|
||||||
print*: proc () {.nimcall.}
|
|
||||||
prompt*: proc (text, default: cstring): cstring {.nimcall.}
|
|
||||||
releaseEvents*: proc (eventMask: int) {.nimcall.}
|
|
||||||
resizeBy*: proc (x, y: int) {.nimcall.}
|
|
||||||
resizeTo*: proc (x, y: int) {.nimcall.}
|
|
||||||
routeEvent*: proc (event: ref TEvent) {.nimcall.}
|
|
||||||
scrollBy*: proc (x, y: int) {.nimcall.}
|
|
||||||
scrollTo*: proc (x, y: int) {.nimcall.}
|
|
||||||
setInterval*: proc (code: cstring, pause: int): ref TInterval {.nimcall.}
|
|
||||||
setTimeout*: proc (code: cstring, pause: int): ref TTimeOut {.nimcall.}
|
|
||||||
stop*: proc () {.nimcall.}
|
|
||||||
frames*: seq[TFrame]
|
frames*: seq[TFrame]
|
||||||
|
|
||||||
Frame* = ref FrameObj
|
Frame* = ref FrameObj
|
||||||
FrameObj {.importc.} = object of WindowObj
|
FrameObj {.importc.} = object of WindowObj
|
||||||
|
|
||||||
ClassList* {.importc.} = object of RootObj
|
ClassList* = ref ClassListObj
|
||||||
add*: proc (class: cstring) {.nimcall.}
|
ClassListObj {.importc.} = object of RootObj
|
||||||
remove*: proc (class: cstring) {.nimcall.}
|
|
||||||
contains*: proc (class: cstring):bool {.nimcall.}
|
|
||||||
toggle*: proc (class: cstring) {.nimcall.}
|
|
||||||
|
|
||||||
TNodeType* = enum
|
NodeType* = enum
|
||||||
ElementNode = 1,
|
ElementNode = 1,
|
||||||
AttributeNode,
|
AttributeNode,
|
||||||
TextNode,
|
TextNode,
|
||||||
|
|
@ -115,7 +79,7 @@ type
|
||||||
NotationNode
|
NotationNode
|
||||||
|
|
||||||
Node* = ref NodeObj
|
Node* = ref NodeObj
|
||||||
NodeObj {.importc.} = object of TEventHandlers
|
NodeObj {.importc.} = object of EventTargetObj
|
||||||
attributes*: seq[Node]
|
attributes*: seq[Node]
|
||||||
childNodes*: seq[Node]
|
childNodes*: seq[Node]
|
||||||
children*: seq[Node]
|
children*: seq[Node]
|
||||||
|
|
@ -124,29 +88,12 @@ type
|
||||||
lastChild*: Node
|
lastChild*: Node
|
||||||
nextSibling*: Node
|
nextSibling*: Node
|
||||||
nodeName*: cstring
|
nodeName*: cstring
|
||||||
nodeType*: TNodeType
|
nodeType*: NodeType
|
||||||
nodeValue*: cstring
|
nodeValue*: cstring
|
||||||
parentNode*: Node
|
parentNode*: Node
|
||||||
previousSibling*: Node
|
previousSibling*: Node
|
||||||
appendChild*: proc (child: Node) {.nimcall.}
|
|
||||||
appendData*: proc (data: cstring) {.nimcall.}
|
|
||||||
cloneNode*: proc (copyContent: bool): Node {.nimcall.}
|
|
||||||
deleteData*: proc (start, len: int) {.nimcall.}
|
|
||||||
getAttribute*: proc (attr: cstring): cstring {.nimcall.}
|
|
||||||
getAttributeNode*: proc (attr: cstring): Node {.nimcall.}
|
|
||||||
hasChildNodes*: proc (): bool {.nimcall.}
|
|
||||||
innerHTML*: cstring
|
innerHTML*: cstring
|
||||||
insertBefore*: proc (newNode, before: Node) {.nimcall.}
|
style*: Style
|
||||||
insertData*: proc (position: int, data: cstring) {.nimcall.}
|
|
||||||
removeAttribute*: proc (attr: cstring) {.nimcall.}
|
|
||||||
removeAttributeNode*: proc (attr: Node) {.nimcall.}
|
|
||||||
removeChild*: proc (child: Node) {.nimcall.}
|
|
||||||
replaceChild*: proc (newNode, oldNode: Node) {.nimcall.}
|
|
||||||
replaceData*: proc (start, len: int, text: cstring) {.nimcall.}
|
|
||||||
scrollIntoView*: proc () {.nimcall.}
|
|
||||||
setAttribute*: proc (name, value: cstring) {.nimcall.}
|
|
||||||
setAttributeNode*: proc (attr: Node) {.nimcall.}
|
|
||||||
style*: ref TStyle
|
|
||||||
|
|
||||||
Document* = ref DocumentObj
|
Document* = ref DocumentObj
|
||||||
DocumentObj {.importc.} = object of NodeObj
|
DocumentObj {.importc.} = object of NodeObj
|
||||||
|
|
@ -164,31 +111,16 @@ type
|
||||||
title*: cstring
|
title*: cstring
|
||||||
URL*: cstring
|
URL*: cstring
|
||||||
vlinkColor*: cstring
|
vlinkColor*: cstring
|
||||||
captureEvents*: proc (eventMask: int) {.nimcall.}
|
|
||||||
createAttribute*: proc (identifier: cstring): Node {.nimcall.}
|
|
||||||
createElement*: proc (identifier: cstring): Element {.nimcall.}
|
|
||||||
createTextNode*: proc (identifier: cstring): Node {.nimcall.}
|
|
||||||
getElementById*: proc (id: cstring): Element {.nimcall.}
|
|
||||||
getElementsByName*: proc (name: cstring): seq[Element] {.nimcall.}
|
|
||||||
getElementsByTagName*: proc (name: cstring): seq[Element] {.nimcall.}
|
|
||||||
getElementsByClassName*: proc (name: cstring): seq[Element] {.nimcall.}
|
|
||||||
getSelection*: proc (): cstring {.nimcall.}
|
|
||||||
handleEvent*: proc (event: ref TEvent) {.nimcall.}
|
|
||||||
open*: proc () {.nimcall.}
|
|
||||||
releaseEvents*: proc (eventMask: int) {.nimcall.}
|
|
||||||
routeEvent*: proc (event: ref TEvent) {.nimcall.}
|
|
||||||
write*: proc (text: cstring) {.nimcall.}
|
|
||||||
writeln*: proc (text: cstring) {.nimcall.}
|
|
||||||
anchors*: seq[AnchorElement]
|
anchors*: seq[AnchorElement]
|
||||||
forms*: seq[FormElement]
|
forms*: seq[FormElement]
|
||||||
images*: seq[ImageElement]
|
images*: seq[ImageElement]
|
||||||
applets*: seq[ref TApplet]
|
applets*: seq[Element]
|
||||||
embeds*: seq[EmbedElement]
|
embeds*: seq[EmbedElement]
|
||||||
links*: seq[LinkElement]
|
links*: seq[LinkElement]
|
||||||
|
|
||||||
Element* = ref ElementObj
|
Element* = ref ElementObj
|
||||||
ElementObj {.importc.} = object of NodeObj
|
ElementObj {.importc.} = object of NodeObj
|
||||||
classList*: ref Classlist
|
classList*: Classlist
|
||||||
checked*: bool
|
checked*: bool
|
||||||
defaultChecked*: bool
|
defaultChecked*: bool
|
||||||
defaultValue*: cstring
|
defaultValue*: cstring
|
||||||
|
|
@ -196,14 +128,7 @@ type
|
||||||
form*: FormElement
|
form*: FormElement
|
||||||
name*: cstring
|
name*: cstring
|
||||||
readOnly*: bool
|
readOnly*: bool
|
||||||
blur*: proc () {.nimcall.}
|
|
||||||
click*: proc () {.nimcall.}
|
|
||||||
focus*: proc () {.nimcall.}
|
|
||||||
handleEvent*: proc (event: ref TEvent) {.nimcall.}
|
|
||||||
select*: proc () {.nimcall.}
|
|
||||||
options*: seq[OptionElement]
|
options*: seq[OptionElement]
|
||||||
getElementsByTagName*: proc (name: cstring): seq[Element] {.nimcall.}
|
|
||||||
getElementsByClassName*: proc (name: cstring): seq[Element] {.nimcall.}
|
|
||||||
|
|
||||||
LinkElement* = ref LinkObj
|
LinkElement* = ref LinkObj
|
||||||
LinkObj {.importc.} = object of ElementObj
|
LinkObj {.importc.} = object of ElementObj
|
||||||
|
|
@ -220,16 +145,12 @@ type
|
||||||
width*: int
|
width*: int
|
||||||
`type`*: cstring
|
`type`*: cstring
|
||||||
vspace*: int
|
vspace*: int
|
||||||
play*: proc () {.nimcall.}
|
|
||||||
stop*: proc () {.nimcall.}
|
|
||||||
|
|
||||||
AnchorElement* = ref AnchorObj
|
AnchorElement* = ref AnchorObj
|
||||||
AnchorObj {.importc.} = object of ElementObj
|
AnchorObj {.importc.} = object of ElementObj
|
||||||
text*: cstring
|
text*: cstring
|
||||||
x*, y*: int
|
x*, y*: int
|
||||||
|
|
||||||
TApplet* {.importc.} = object of RootObj
|
|
||||||
|
|
||||||
OptionElement* = ref OptionObj
|
OptionElement* = ref OptionObj
|
||||||
OptionObj {.importc.} = object of ElementObj
|
OptionObj {.importc.} = object of ElementObj
|
||||||
defaultSelected*: bool
|
defaultSelected*: bool
|
||||||
|
|
@ -244,8 +165,6 @@ type
|
||||||
encoding*: cstring
|
encoding*: cstring
|
||||||
`method`*: cstring
|
`method`*: cstring
|
||||||
target*: cstring
|
target*: cstring
|
||||||
reset*: proc () {.nimcall.}
|
|
||||||
submit*: proc () {.nimcall.}
|
|
||||||
elements*: seq[Element]
|
elements*: seq[Element]
|
||||||
|
|
||||||
ImageElement* = ref ImageObj
|
ImageElement* = ref ImageObj
|
||||||
|
|
@ -259,8 +178,8 @@ type
|
||||||
vspace*: int
|
vspace*: int
|
||||||
width*: int
|
width*: int
|
||||||
|
|
||||||
|
Style = ref StyleObj
|
||||||
TStyle* {.importc.} = object of RootObj
|
StyleObj {.importc.} = object of RootObj
|
||||||
background*: cstring
|
background*: cstring
|
||||||
backgroundAttachment*: cstring
|
backgroundAttachment*: cstring
|
||||||
backgroundColor*: cstring
|
backgroundColor*: cstring
|
||||||
|
|
@ -350,11 +269,9 @@ type
|
||||||
width*: cstring
|
width*: cstring
|
||||||
wordSpacing*: cstring
|
wordSpacing*: cstring
|
||||||
zIndex*: int
|
zIndex*: int
|
||||||
getAttribute*: proc (attr: cstring, caseSensitive=false): cstring {.nimcall.}
|
|
||||||
removeAttribute*: proc (attr: cstring, caseSensitive=false) {.nimcall.}
|
|
||||||
setAttribute*: proc (attr, value: cstring, caseSensitive=false) {.nimcall.}
|
|
||||||
|
|
||||||
TEvent* {.importc.} = object of RootObj
|
Event* = ref EventObj
|
||||||
|
EventObj {.importc.} = object of RootObj
|
||||||
target*: Node
|
target*: Node
|
||||||
altKey*, ctrlKey*, shiftKey*: bool
|
altKey*, ctrlKey*, shiftKey*: bool
|
||||||
button*: int
|
button*: int
|
||||||
|
|
@ -393,7 +310,8 @@ type
|
||||||
SUBMIT*: int
|
SUBMIT*: int
|
||||||
UNLOAD*: int
|
UNLOAD*: int
|
||||||
|
|
||||||
TLocation* {.importc.} = object of RootObj
|
Location* = ref LocationObj
|
||||||
|
LocationObj {.importc.} = object of RootObj
|
||||||
hash*: cstring
|
hash*: cstring
|
||||||
host*: cstring
|
host*: cstring
|
||||||
hostname*: cstring
|
hostname*: cstring
|
||||||
|
|
@ -402,16 +320,13 @@ type
|
||||||
port*: cstring
|
port*: cstring
|
||||||
protocol*: cstring
|
protocol*: cstring
|
||||||
search*: cstring
|
search*: cstring
|
||||||
reload*: proc () {.nimcall.}
|
|
||||||
replace*: proc (s: cstring) {.nimcall.}
|
|
||||||
|
|
||||||
THistory* {.importc.} = object of RootObj
|
History* = ref HistoryObj
|
||||||
|
HistoryObj {.importc.} = object of RootObj
|
||||||
length*: int
|
length*: int
|
||||||
back*: proc () {.nimcall.}
|
|
||||||
forward*: proc () {.nimcall.}
|
|
||||||
go*: proc (pagesToJump: int) {.nimcall.}
|
|
||||||
|
|
||||||
TNavigator* {.importc.} = object of RootObj
|
Navigator* = ref NavigatorObj
|
||||||
|
NavigatorObj {.importc.} = object of RootObj
|
||||||
appCodeName*: cstring
|
appCodeName*: cstring
|
||||||
appName*: cstring
|
appName*: cstring
|
||||||
appVersion*: cstring
|
appVersion*: cstring
|
||||||
|
|
@ -419,7 +334,6 @@ type
|
||||||
language*: cstring
|
language*: cstring
|
||||||
platform*: cstring
|
platform*: cstring
|
||||||
userAgent*: cstring
|
userAgent*: cstring
|
||||||
javaEnabled*: proc (): bool {.nimcall.}
|
|
||||||
mimeTypes*: seq[ref TMimeType]
|
mimeTypes*: seq[ref TMimeType]
|
||||||
|
|
||||||
TPlugin* {.importc.} = object of RootObj
|
TPlugin* {.importc.} = object of RootObj
|
||||||
|
|
@ -441,7 +355,8 @@ type
|
||||||
TToolBar* = TLocationBar
|
TToolBar* = TLocationBar
|
||||||
TStatusBar* = TLocationBar
|
TStatusBar* = TLocationBar
|
||||||
|
|
||||||
TScreen* {.importc.} = object of RootObj
|
Screen = ref ScreenObj
|
||||||
|
ScreenObj {.importc.} = object of RootObj
|
||||||
availHeight*: int
|
availHeight*: int
|
||||||
availWidth*: int
|
availWidth*: int
|
||||||
colorDepth*: int
|
colorDepth*: int
|
||||||
|
|
@ -452,11 +367,127 @@ type
|
||||||
TTimeOut* {.importc.} = object of RootObj
|
TTimeOut* {.importc.} = object of RootObj
|
||||||
TInterval* {.importc.} = object of RootObj
|
TInterval* {.importc.} = object of RootObj
|
||||||
|
|
||||||
|
{.push importcpp.}
|
||||||
|
|
||||||
|
# EventTarget "methods"
|
||||||
|
proc addEventListener*(et: EventTarget, ev: cstring, cb: proc(ev: Event), useCapture: bool = false)
|
||||||
|
|
||||||
|
# Window "methods"
|
||||||
|
proc alert*(w: Window, msg: cstring)
|
||||||
|
proc back*(w: Window)
|
||||||
|
proc blur*(w: Window)
|
||||||
|
proc captureEvents*(w: Window, eventMask: int) {.deprecated.}
|
||||||
|
proc clearInterval*(w: Window, interval: ref TInterval)
|
||||||
|
proc clearTimeout*(w: Window, timeout: ref TTimeOut)
|
||||||
|
proc close*(w: Window)
|
||||||
|
proc confirm*(w: Window, msg: cstring): bool
|
||||||
|
proc disableExternalCapture*(w: Window)
|
||||||
|
proc enableExternalCapture*(w: Window)
|
||||||
|
proc find*(w: Window, text: cstring, caseSensitive = false,
|
||||||
|
backwards = false)
|
||||||
|
proc focus*(w: Window)
|
||||||
|
proc forward*(w: Window)
|
||||||
|
proc handleEvent*(w: Window, e: Event)
|
||||||
|
proc home*(w: Window)
|
||||||
|
proc moveBy*(w: Window, x, y: int)
|
||||||
|
proc moveTo*(w: Window, x, y: int)
|
||||||
|
proc open*(w: Window, uri, windowname: cstring,
|
||||||
|
properties: cstring = nil): Window
|
||||||
|
proc print*(w: Window)
|
||||||
|
proc prompt*(w: Window, text, default: cstring): cstring
|
||||||
|
proc releaseEvents*(w: Window, eventMask: int) {.deprecated.}
|
||||||
|
proc resizeBy*(w: Window, x, y: int)
|
||||||
|
proc resizeTo*(w: Window, x, y: int)
|
||||||
|
proc routeEvent*(w: Window, event: Event)
|
||||||
|
proc scrollBy*(w: Window, x, y: int)
|
||||||
|
proc scrollTo*(w: Window, x, y: int)
|
||||||
|
proc setInterval*(w: Window, code: cstring, pause: int): ref TInterval
|
||||||
|
proc setTimeout*(w: Window, code: cstring, pause: int): ref TTimeOut
|
||||||
|
proc stop*(w: Window)
|
||||||
|
|
||||||
|
# Node "methods"
|
||||||
|
proc appendChild*(n, child: Node)
|
||||||
|
proc appendData*(n: Node, data: cstring)
|
||||||
|
proc cloneNode*(n: Node, copyContent: bool): Node
|
||||||
|
proc deleteData*(n: Node, start, len: int)
|
||||||
|
proc getAttribute*(n: Node, attr: cstring): cstring
|
||||||
|
proc getAttributeNode*(n: Node, attr: cstring): Node
|
||||||
|
proc hasChildNodes*(n: Node): bool
|
||||||
|
proc insertBefore*(n, newNode, before: Node)
|
||||||
|
proc insertData*(n: Node, position: int, data: cstring)
|
||||||
|
proc removeAttribute*(n: Node, attr: cstring)
|
||||||
|
proc removeAttributeNode*(n, attr: Node)
|
||||||
|
proc removeChild*(n, child: Node)
|
||||||
|
proc replaceChild*(n, newNode, oldNode: Node)
|
||||||
|
proc replaceData*(n: Node, start, len: int, text: cstring)
|
||||||
|
proc scrollIntoView*(n: Node)
|
||||||
|
proc setAttribute*(n: Node, name, value: cstring)
|
||||||
|
proc setAttributeNode*(n: Node, attr: Node)
|
||||||
|
|
||||||
|
# Document "methods"
|
||||||
|
proc captureEvents*(d: Document, eventMask: int) {.deprecated.}
|
||||||
|
proc createAttribute*(d: Document, identifier: cstring): Node
|
||||||
|
proc createElement*(d: Document, identifier: cstring): Element
|
||||||
|
proc createTextNode*(d: Document, identifier: cstring): Node
|
||||||
|
proc getElementById*(d: Document, id: cstring): Element
|
||||||
|
proc getElementsByName*(d: Document, name: cstring): seq[Element]
|
||||||
|
proc getElementsByTagName*(d: Document, name: cstring): seq[Element]
|
||||||
|
proc getElementsByClassName*(d: Document, name: cstring): seq[Element]
|
||||||
|
proc getSelection*(d: Document): cstring
|
||||||
|
proc handleEvent*(d: Document, event: Event)
|
||||||
|
proc open*(d: Document)
|
||||||
|
proc releaseEvents*(d: Document, eventMask: int) {.deprecated.}
|
||||||
|
proc routeEvent*(d: Document, event: Event)
|
||||||
|
proc write*(d: Document, text: cstring)
|
||||||
|
proc writeln*(d: Document, text: cstring)
|
||||||
|
|
||||||
|
# Element "methods"
|
||||||
|
proc blur*(e: Element)
|
||||||
|
proc click*(e: Element)
|
||||||
|
proc focus*(e: Element)
|
||||||
|
proc handleEvent*(e: Element, event: Event)
|
||||||
|
proc select*(e: Element)
|
||||||
|
proc getElementsByTagName*(e: Element, name: cstring): seq[Element]
|
||||||
|
proc getElementsByClassName*(e: Element, name: cstring): seq[Element]
|
||||||
|
|
||||||
|
# FormElement "methods"
|
||||||
|
proc reset*(f: FormElement)
|
||||||
|
proc submit*(f: FormElement)
|
||||||
|
|
||||||
|
# EmbedElement "methods"
|
||||||
|
proc play*(e: EmbedElement)
|
||||||
|
proc stop*(e: EmbedElement)
|
||||||
|
|
||||||
|
# Location "methods"
|
||||||
|
proc reload*(loc: Location)
|
||||||
|
proc replace*(loc: Location, s: cstring)
|
||||||
|
|
||||||
|
# History "methods"
|
||||||
|
proc back*(h: History)
|
||||||
|
proc forward*(h: History)
|
||||||
|
proc go*(h: History, pagesToJump: int)
|
||||||
|
|
||||||
|
# Navigator "methods"
|
||||||
|
proc javaEnabled*(h: Navigator): bool
|
||||||
|
|
||||||
|
# ClassList "methods"
|
||||||
|
proc add*(c: ClassList, class: cstring)
|
||||||
|
proc remove*(c: ClassList, class: cstring)
|
||||||
|
proc contains*(c: ClassList, class: cstring):bool
|
||||||
|
proc toggle*(c: ClassList, class: cstring)
|
||||||
|
|
||||||
|
# Style "methods"
|
||||||
|
proc getAttribute*(s: Style, attr: cstring, caseSensitive=false): cstring
|
||||||
|
proc removeAttribute*(s: Style, attr: cstring, caseSensitive=false)
|
||||||
|
proc setAttribute*(s: Style, attr, value: cstring, caseSensitive=false)
|
||||||
|
|
||||||
|
{.pop.}
|
||||||
|
|
||||||
var
|
var
|
||||||
window* {.importc, nodecl.}: Window
|
window* {.importc, nodecl.}: Window
|
||||||
document* {.importc, nodecl.}: Document
|
document* {.importc, nodecl.}: Document
|
||||||
navigator* {.importc, nodecl.}: ref TNavigator
|
navigator* {.importc, nodecl.}: Navigator
|
||||||
screen* {.importc, nodecl.}: ref TScreen
|
screen* {.importc, nodecl.}: Screen
|
||||||
|
|
||||||
proc decodeURI*(uri: cstring): cstring {.importc, nodecl.}
|
proc decodeURI*(uri: cstring): cstring {.importc, nodecl.}
|
||||||
proc encodeURI*(uri: cstring): cstring {.importc, nodecl.}
|
proc encodeURI*(uri: cstring): cstring {.importc, nodecl.}
|
||||||
|
|
@ -474,6 +505,7 @@ proc parseInt*(s: cstring, radix: int):int {.importc, nodecl.}
|
||||||
|
|
||||||
|
|
||||||
type
|
type
|
||||||
|
TEventHandlers* {.deprecated.} = EventTargetObj
|
||||||
TWindow* {.deprecated.} = WindowObj
|
TWindow* {.deprecated.} = WindowObj
|
||||||
TFrame* {.deprecated.} = FrameObj
|
TFrame* {.deprecated.} = FrameObj
|
||||||
TNode* {.deprecated.} = NodeObj
|
TNode* {.deprecated.} = NodeObj
|
||||||
|
|
@ -485,3 +517,11 @@ type
|
||||||
TOption* {.deprecated.} = OptionObj
|
TOption* {.deprecated.} = OptionObj
|
||||||
TForm* {.deprecated.} = FormObj
|
TForm* {.deprecated.} = FormObj
|
||||||
TImage* {.deprecated.} = ImageObj
|
TImage* {.deprecated.} = ImageObj
|
||||||
|
TNodeType* {.deprecated.} = NodeType
|
||||||
|
TEvent* {.deprecated.} = EventObj
|
||||||
|
TLocation* {.deprecated.} = LocationObj
|
||||||
|
THistory* {.deprecated.} = HistoryObj
|
||||||
|
TNavigator* {.deprecated.} = NavigatorObj
|
||||||
|
TStyle* {.deprecated.} = StyleObj
|
||||||
|
TScreen* {.deprecated.} = ScreenObj
|
||||||
|
TApplet* {.importc, deprecated.} = object of RootObj
|
||||||
|
|
|
||||||
|
|
@ -418,10 +418,6 @@ typedef int assert_numbits[sizeof(NI) == sizeof(void*) && NIM_INTBITS == sizeof(
|
||||||
# define NIM_EXTERNC
|
# define NIM_EXTERNC
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* we have to tinker with TNimType as it's both part of system.nim and
|
|
||||||
typeinfo.nim but system.nim doesn't export it cleanly... */
|
|
||||||
typedef struct TNimType TNimType;
|
|
||||||
|
|
||||||
/* ---------------- platform specific includes ----------------------- */
|
/* ---------------- platform specific includes ----------------------- */
|
||||||
|
|
||||||
/* VxWorks related includes */
|
/* VxWorks related includes */
|
||||||
|
|
|
||||||
|
|
@ -173,7 +173,41 @@ proc nimNextToken(g: var GeneralTokenizer) =
|
||||||
while g.buf[pos] in {' ', '\x09'..'\x0D'}: inc(pos)
|
while g.buf[pos] in {' ', '\x09'..'\x0D'}: inc(pos)
|
||||||
of '#':
|
of '#':
|
||||||
g.kind = gtComment
|
g.kind = gtComment
|
||||||
while not (g.buf[pos] in {'\0', '\x0A', '\x0D'}): inc(pos)
|
inc(pos)
|
||||||
|
var isDoc = false
|
||||||
|
if g.buf[pos] == '#':
|
||||||
|
inc(pos)
|
||||||
|
isDoc = true
|
||||||
|
if g.buf[pos] == '[':
|
||||||
|
g.kind = gtLongComment
|
||||||
|
var nesting = 0
|
||||||
|
while true:
|
||||||
|
case g.buf[pos]
|
||||||
|
of '\0': break
|
||||||
|
of '#':
|
||||||
|
if isDoc:
|
||||||
|
if g.buf[pos+1] == '#' and g.buf[pos+2] == '[':
|
||||||
|
inc nesting
|
||||||
|
elif g.buf[pos+1] == '[':
|
||||||
|
inc nesting
|
||||||
|
inc pos
|
||||||
|
of ']':
|
||||||
|
if isDoc:
|
||||||
|
if g.buf[pos+1] == '#' and g.buf[pos+2] == '#':
|
||||||
|
if nesting == 0:
|
||||||
|
inc(pos, 3)
|
||||||
|
break
|
||||||
|
dec nesting
|
||||||
|
elif g.buf[pos+1] == '#':
|
||||||
|
if nesting == 0:
|
||||||
|
inc(pos, 2)
|
||||||
|
break
|
||||||
|
dec nesting
|
||||||
|
inc pos
|
||||||
|
else:
|
||||||
|
inc pos
|
||||||
|
else:
|
||||||
|
while g.buf[pos] notin {'\0', '\x0A', '\x0D'}: inc(pos)
|
||||||
of 'a'..'z', 'A'..'Z', '_', '\x80'..'\xFF':
|
of 'a'..'z', 'A'..'Z', '_', '\x80'..'\xFF':
|
||||||
var id = ""
|
var id = ""
|
||||||
while g.buf[pos] in SymChars + {'_'}:
|
while g.buf[pos] in SymChars + {'_'}:
|
||||||
|
|
|
||||||
|
|
@ -534,7 +534,7 @@ proc generateDocumentationJumps(docs: IndexedDocs): string =
|
||||||
for title in titles:
|
for title in titles:
|
||||||
chunks.add("<a href=\"" & title.link & "\">" & title.keyword & "</a>")
|
chunks.add("<a href=\"" & title.link & "\">" & title.keyword & "</a>")
|
||||||
|
|
||||||
result.add(chunks.join(", ") & ".<br>")
|
result.add(chunks.join(", ") & ".<br/>")
|
||||||
|
|
||||||
proc generateModuleJumps(modules: seq[string]): string =
|
proc generateModuleJumps(modules: seq[string]): string =
|
||||||
## Returns a plain list of hyperlinks to the list of modules.
|
## Returns a plain list of hyperlinks to the list of modules.
|
||||||
|
|
@ -544,7 +544,7 @@ proc generateModuleJumps(modules: seq[string]): string =
|
||||||
for name in modules:
|
for name in modules:
|
||||||
chunks.add("<a href=\"" & name & ".html\">" & name & "</a>")
|
chunks.add("<a href=\"" & name & ".html\">" & name & "</a>")
|
||||||
|
|
||||||
result.add(chunks.join(", ") & ".<br>")
|
result.add(chunks.join(", ") & ".<br/>")
|
||||||
|
|
||||||
proc readIndexDir(dir: string):
|
proc readIndexDir(dir: string):
|
||||||
tuple[modules: seq[string], symbols: seq[IndexEntry], docs: IndexedDocs] =
|
tuple[modules: seq[string], symbols: seq[IndexEntry], docs: IndexedDocs] =
|
||||||
|
|
|
||||||
|
|
@ -2165,6 +2165,10 @@ proc pwrite*(a1: cint, a2: pointer, a3: int, a4: Off): int {.
|
||||||
importc, header: "<unistd.h>".}
|
importc, header: "<unistd.h>".}
|
||||||
proc read*(a1: cint, a2: pointer, a3: int): int {.importc, header: "<unistd.h>".}
|
proc read*(a1: cint, a2: pointer, a3: int): int {.importc, header: "<unistd.h>".}
|
||||||
proc readlink*(a1, a2: cstring, a3: int): int {.importc, header: "<unistd.h>".}
|
proc readlink*(a1, a2: cstring, a3: int): int {.importc, header: "<unistd.h>".}
|
||||||
|
proc ioctl*(f: FileHandle, device: uint): int {.importc: "ioctl",
|
||||||
|
header: "<sys/ioctl.h>", varargs, tags: [WriteIOEffect].}
|
||||||
|
## A system call for device-specific input/output operations and other
|
||||||
|
## operations which cannot be expressed by regular system calls
|
||||||
|
|
||||||
proc rmdir*(a1: cstring): cint {.importc, header: "<unistd.h>".}
|
proc rmdir*(a1: cstring): cint {.importc, header: "<unistd.h>".}
|
||||||
proc setegid*(a1: Gid): cint {.importc, header: "<unistd.h>".}
|
proc setegid*(a1: Gid): cint {.importc, header: "<unistd.h>".}
|
||||||
|
|
|
||||||
|
|
@ -288,7 +288,7 @@ proc defaultOnProgressChanged*(total, progress: BiggestInt,
|
||||||
result.complete()
|
result.complete()
|
||||||
|
|
||||||
proc retrFile*(ftp: AsyncFtpClient, file, dest: string,
|
proc retrFile*(ftp: AsyncFtpClient, file, dest: string,
|
||||||
onProgressChanged = defaultOnProgressChanged) {.async.} =
|
onProgressChanged: ProgressChangedProc = defaultOnProgressChanged) {.async.} =
|
||||||
## Downloads ``file`` and saves it to ``dest``.
|
## Downloads ``file`` and saves it to ``dest``.
|
||||||
## The ``EvRetr`` event is passed to the specified ``handleEvent`` function
|
## The ``EvRetr`` event is passed to the specified ``handleEvent`` function
|
||||||
## when the download is finished. The event's ``filename`` field will be equal
|
## when the download is finished. The event's ``filename`` field will be equal
|
||||||
|
|
@ -339,7 +339,7 @@ proc doUpload(ftp: AsyncFtpClient, file: File,
|
||||||
await countdownFut or sendFut
|
await countdownFut or sendFut
|
||||||
|
|
||||||
proc store*(ftp: AsyncFtpClient, file, dest: string,
|
proc store*(ftp: AsyncFtpClient, file, dest: string,
|
||||||
onProgressChanged = defaultOnProgressChanged) {.async.} =
|
onProgressChanged: ProgressChangedProc = defaultOnProgressChanged) {.async.} =
|
||||||
## Uploads ``file`` to ``dest`` on the remote FTP server. Usage of this
|
## Uploads ``file`` to ``dest`` on the remote FTP server. Usage of this
|
||||||
## function asynchronously is recommended to view the progress of
|
## function asynchronously is recommended to view the progress of
|
||||||
## the download.
|
## the download.
|
||||||
|
|
|
||||||
|
|
@ -232,7 +232,7 @@ iterator mpairs*[T](c: var CritBitTree[T]): tuple[key: string, val: var T] =
|
||||||
## yields all (key, value)-pairs of `c`. The yielded values can be modified.
|
## yields all (key, value)-pairs of `c`. The yielded values can be modified.
|
||||||
for x in leaves(c.root): yield (x.key, x.val)
|
for x in leaves(c.root): yield (x.key, x.val)
|
||||||
|
|
||||||
proc allprefixedAux[T](c: CritBitTree[T], key: string): Node[T] =
|
proc allprefixedAux[T](c: CritBitTree[T], key: string; longestMatch: bool): Node[T] =
|
||||||
var p = c.root
|
var p = c.root
|
||||||
var top = p
|
var top = p
|
||||||
if p != nil:
|
if p != nil:
|
||||||
|
|
@ -242,43 +242,51 @@ proc allprefixedAux[T](c: CritBitTree[T], key: string): Node[T] =
|
||||||
let dir = (1 + (ch.ord or p.otherBits.ord)) shr 8
|
let dir = (1 + (ch.ord or p.otherBits.ord)) shr 8
|
||||||
p = p.child[dir]
|
p = p.child[dir]
|
||||||
if q.byte < key.len: top = p
|
if q.byte < key.len: top = p
|
||||||
for i in 0 .. <key.len:
|
if not longestMatch:
|
||||||
if p.key[i] != key[i]: return
|
for i in 0 .. <key.len:
|
||||||
|
if p.key[i] != key[i]: return
|
||||||
result = top
|
result = top
|
||||||
|
|
||||||
iterator itemsWithPrefix*[T](c: CritBitTree[T], prefix: string): string =
|
iterator itemsWithPrefix*[T](c: CritBitTree[T], prefix: string;
|
||||||
## yields all keys starting with `prefix`.
|
longestMatch=false): string =
|
||||||
let top = allprefixedAux(c, prefix)
|
## yields all keys starting with `prefix`. If `longestMatch` is true,
|
||||||
|
## the longest match is returned, it doesn't have to be a complete match then.
|
||||||
|
let top = allprefixedAux(c, prefix, longestMatch)
|
||||||
for x in leaves(top): yield x.key
|
for x in leaves(top): yield x.key
|
||||||
|
|
||||||
iterator keysWithPrefix*[T](c: CritBitTree[T], prefix: string): string =
|
iterator keysWithPrefix*[T](c: CritBitTree[T], prefix: string;
|
||||||
|
longestMatch=false): string =
|
||||||
## yields all keys starting with `prefix`.
|
## yields all keys starting with `prefix`.
|
||||||
let top = allprefixedAux(c, prefix)
|
let top = allprefixedAux(c, prefix, longestMatch)
|
||||||
for x in leaves(top): yield x.key
|
for x in leaves(top): yield x.key
|
||||||
|
|
||||||
iterator valuesWithPrefix*[T](c: CritBitTree[T], prefix: string): T =
|
iterator valuesWithPrefix*[T](c: CritBitTree[T], prefix: string;
|
||||||
|
longestMatch=false): T =
|
||||||
## yields all values of `c` starting with `prefix` of the
|
## yields all values of `c` starting with `prefix` of the
|
||||||
## corresponding keys.
|
## corresponding keys.
|
||||||
let top = allprefixedAux(c, prefix)
|
let top = allprefixedAux(c, prefix, longestMatch)
|
||||||
for x in leaves(top): yield x.val
|
for x in leaves(top): yield x.val
|
||||||
|
|
||||||
iterator mvaluesWithPrefix*[T](c: var CritBitTree[T], prefix: string): var T =
|
iterator mvaluesWithPrefix*[T](c: var CritBitTree[T], prefix: string;
|
||||||
|
longestMatch=false): var T =
|
||||||
## yields all values of `c` starting with `prefix` of the
|
## yields all values of `c` starting with `prefix` of the
|
||||||
## corresponding keys. The values can be modified.
|
## corresponding keys. The values can be modified.
|
||||||
let top = allprefixedAux(c, prefix)
|
let top = allprefixedAux(c, prefix, longestMatch)
|
||||||
for x in leaves(top): yield x.val
|
for x in leaves(top): yield x.val
|
||||||
|
|
||||||
iterator pairsWithPrefix*[T](c: CritBitTree[T],
|
iterator pairsWithPrefix*[T](c: CritBitTree[T],
|
||||||
prefix: string): tuple[key: string, val: T] =
|
prefix: string;
|
||||||
|
longestMatch=false): tuple[key: string, val: T] =
|
||||||
## yields all (key, value)-pairs of `c` starting with `prefix`.
|
## yields all (key, value)-pairs of `c` starting with `prefix`.
|
||||||
let top = allprefixedAux(c, prefix)
|
let top = allprefixedAux(c, prefix, longestMatch)
|
||||||
for x in leaves(top): yield (x.key, x.val)
|
for x in leaves(top): yield (x.key, x.val)
|
||||||
|
|
||||||
iterator mpairsWithPrefix*[T](c: var CritBitTree[T],
|
iterator mpairsWithPrefix*[T](c: var CritBitTree[T],
|
||||||
prefix: string): tuple[key: string, val: var T] =
|
prefix: string;
|
||||||
|
longestMatch=false): tuple[key: string, val: var T] =
|
||||||
## yields all (key, value)-pairs of `c` starting with `prefix`.
|
## yields all (key, value)-pairs of `c` starting with `prefix`.
|
||||||
## The yielded values can be modified.
|
## The yielded values can be modified.
|
||||||
let top = allprefixedAux(c, prefix)
|
let top = allprefixedAux(c, prefix, longestMatch)
|
||||||
for x in leaves(top): yield (x.key, x.val)
|
for x in leaves(top): yield (x.key, x.val)
|
||||||
|
|
||||||
proc `$`*[T](c: CritBitTree[T]): string =
|
proc `$`*[T](c: CritBitTree[T]): string =
|
||||||
|
|
|
||||||
|
|
@ -10,12 +10,9 @@
|
||||||
## :Author: Alexander Mitchell-Robinson (Amrykid)
|
## :Author: Alexander Mitchell-Robinson (Amrykid)
|
||||||
##
|
##
|
||||||
## This module implements operations for the built-in `seq`:idx: type which
|
## This module implements operations for the built-in `seq`:idx: type which
|
||||||
## were inspired by functional programming languages. If you are looking for
|
## were inspired by functional programming languages.
|
||||||
## the typical `map` function which applies a function to every element in a
|
|
||||||
## sequence, it already exists in the `system <system.html>`_ module in both
|
|
||||||
## mutable and immutable styles.
|
|
||||||
##
|
##
|
||||||
## Also, for functional style programming you may want to pass `anonymous procs
|
## For functional style programming you may want to pass `anonymous procs
|
||||||
## <manual.html#anonymous-procs>`_ to procs like ``filter`` to reduce typing.
|
## <manual.html#anonymous-procs>`_ to procs like ``filter`` to reduce typing.
|
||||||
## Anonymous procs can use `the special do notation <manual.html#do-notation>`_
|
## Anonymous procs can use `the special do notation <manual.html#do-notation>`_
|
||||||
## which is more convenient in certain situations.
|
## which is more convenient in certain situations.
|
||||||
|
|
@ -471,7 +468,7 @@ template toSeq*(iter: expr): expr {.immediate.} =
|
||||||
## if x mod 2 == 1:
|
## if x mod 2 == 1:
|
||||||
## result = true)
|
## result = true)
|
||||||
## assert odd_numbers == @[1, 3, 5, 7, 9]
|
## assert odd_numbers == @[1, 3, 5, 7, 9]
|
||||||
|
|
||||||
when compiles(iter.len):
|
when compiles(iter.len):
|
||||||
var i = 0
|
var i = 0
|
||||||
var result = newSeq[type(iter)](iter.len)
|
var result = newSeq[type(iter)](iter.len)
|
||||||
|
|
|
||||||
|
|
@ -887,7 +887,7 @@ proc mget*[A](t: CountTableRef[A], key: A): var int {.deprecated.} =
|
||||||
result = t[][key]
|
result = t[][key]
|
||||||
|
|
||||||
proc getOrDefault*[A](t: CountTableRef[A], key: A): int =
|
proc getOrDefault*[A](t: CountTableRef[A], key: A): int =
|
||||||
getOrDefaultImpl(t, key)
|
result = t[].getOrDefault(key)
|
||||||
|
|
||||||
proc hasKey*[A](t: CountTableRef[A], key: A): bool =
|
proc hasKey*[A](t: CountTableRef[A], key: A): bool =
|
||||||
## returns true iff `key` is in the table `t`.
|
## returns true iff `key` is in the table `t`.
|
||||||
|
|
@ -1028,3 +1028,15 @@ when isMainModule:
|
||||||
assert(merged["foo"] == 5)
|
assert(merged["foo"] == 5)
|
||||||
assert(merged["bar"] == 3)
|
assert(merged["bar"] == 3)
|
||||||
assert(merged["baz"] == 14)
|
assert(merged["baz"] == 14)
|
||||||
|
|
||||||
|
block:
|
||||||
|
const testKey = "TESTKEY"
|
||||||
|
let t: CountTableRef[string] = newCountTable[string]()
|
||||||
|
|
||||||
|
# Before, does not compile with error message:
|
||||||
|
#test_counttable.nim(7, 43) template/generic instantiation from here
|
||||||
|
#lib/pure/collections/tables.nim(117, 21) template/generic instantiation from here
|
||||||
|
#lib/pure/collections/tableimpl.nim(32, 27) Error: undeclared field: 'hcode
|
||||||
|
doAssert 0 == t.getOrDefault(testKey)
|
||||||
|
t.inc(testKey,3)
|
||||||
|
doAssert 3 == t.getOrDefault(testKey)
|
||||||
|
|
|
||||||
103
lib/pure/db_common.nim
Normal file
103
lib/pure/db_common.nim
Normal file
|
|
@ -0,0 +1,103 @@
|
||||||
|
#
|
||||||
|
#
|
||||||
|
# Nim's Runtime Library
|
||||||
|
# (c) Copyright 2015 Andreas Rumpf
|
||||||
|
#
|
||||||
|
# See the file "copying.txt", included in this
|
||||||
|
# distribution, for details about the copyright.
|
||||||
|
#
|
||||||
|
|
||||||
|
## Common datatypes and definitions for all ``db_*.nim`` (
|
||||||
|
## `db_mysql <db_mysql.html>`_, `db_postgres <db_postgres.html>`_,
|
||||||
|
## and `db_sqlite <db_sqlite.html>`_) modules.
|
||||||
|
|
||||||
|
type
|
||||||
|
DbError* = object of IOError ## exception that is raised if a database error occurs
|
||||||
|
|
||||||
|
SqlQuery* = distinct string ## an SQL query string
|
||||||
|
|
||||||
|
|
||||||
|
DbEffect* = object of IOEffect ## effect that denotes a database operation
|
||||||
|
ReadDbEffect* = object of DbEffect ## effect that denotes a read operation
|
||||||
|
WriteDbEffect* = object of DbEffect ## effect that denotes a write operation
|
||||||
|
|
||||||
|
DbTypeKind* = enum ## a superset of datatypes that might be supported.
|
||||||
|
dbUnknown, ## unknown datatype
|
||||||
|
dbSerial, ## datatype used for primary auto-increment keys
|
||||||
|
dbNull, ## datatype used for the NULL value
|
||||||
|
dbBit, ## bit datatype
|
||||||
|
dbBool, ## boolean datatype
|
||||||
|
dbBlob, ## blob datatype
|
||||||
|
dbFixedChar, ## string of fixed length
|
||||||
|
dbVarchar, ## string datatype
|
||||||
|
dbJson, ## JSON datatype
|
||||||
|
dbXml, ## XML datatype
|
||||||
|
dbInt, ## some integer type
|
||||||
|
dbUInt, ## some unsigned integer type
|
||||||
|
dbDecimal, ## decimal numbers (fixed-point number)
|
||||||
|
dbFloat, ## some floating point type
|
||||||
|
dbDate, ## a year-month-day description
|
||||||
|
dbTime, ## HH:MM:SS information
|
||||||
|
dbDatetime, ## year-month-day and HH:MM:SS information,
|
||||||
|
## plus optional time or timezone information
|
||||||
|
dbTimestamp, ## Timestamp values are stored as the number of seconds
|
||||||
|
## since the epoch ('1970-01-01 00:00:00' UTC).
|
||||||
|
dbTimeInterval, ## an interval [a,b] of times
|
||||||
|
dbEnum, ## some enum
|
||||||
|
dbSet, ## set of enum values
|
||||||
|
dbArray, ## an array of values
|
||||||
|
dbComposite, ## composite type (record, struct, etc)
|
||||||
|
dbUrl, ## a URL
|
||||||
|
dbUuid, ## a UUID
|
||||||
|
dbInet, ## an IP address
|
||||||
|
dbMacAddress, ## a MAC address
|
||||||
|
dbGeometry, ## some geometric type
|
||||||
|
dbPoint, ## Point on a plane (x,y)
|
||||||
|
dbLine, ## Infinite line ((x1,y1),(x2,y2))
|
||||||
|
dbLseg, ## Finite line segment ((x1,y1),(x2,y2))
|
||||||
|
dbBox, ## Rectangular box ((x1,y1),(x2,y2))
|
||||||
|
dbPath, ## Closed or open path (similar to polygon) ((x1,y1),...)
|
||||||
|
dbPolygon, ## Polygon (similar to closed path) ((x1,y1),...)
|
||||||
|
dbCircle, ## Circle <(x,y),r> (center point and radius)
|
||||||
|
dbUser1, ## user definable datatype 1 (for unknown extensions)
|
||||||
|
dbUser2, ## user definable datatype 2 (for unknown extensions)
|
||||||
|
dbUser3, ## user definable datatype 3 (for unknown extensions)
|
||||||
|
dbUser4, ## user definable datatype 4 (for unknown extensions)
|
||||||
|
dbUser5 ## user definable datatype 5 (for unknown extensions)
|
||||||
|
|
||||||
|
DbType* = object ## describes a database type
|
||||||
|
kind*: DbTypeKind ## the kind of the described type
|
||||||
|
notNull*: bool ## does the type contain NULL?
|
||||||
|
name*: string ## the name of the type
|
||||||
|
size*: Natural ## the size of the datatype; 0 if of variable size
|
||||||
|
maxReprLen*: Natural ## maximal length required for the representation
|
||||||
|
precision*, scale*: Natural ## precision and scale of the number
|
||||||
|
min*, max*: BiggestInt ## the minimum and maximum of allowed values
|
||||||
|
validValues*: seq[string] ## valid values of an enum or a set
|
||||||
|
|
||||||
|
DbColumn* = object ## information about a database column
|
||||||
|
name*: string ## name of the column
|
||||||
|
tableName*: string ## name of the table the column belongs to (optional)
|
||||||
|
typ*: DbType ## type of the column
|
||||||
|
primaryKey*: bool ## is this a primary key?
|
||||||
|
foreignKey*: bool ## is this a foreign key?
|
||||||
|
DbColumns* = seq[DbColumn]
|
||||||
|
|
||||||
|
{.deprecated: [EDb: DbError, TSqlQuery: SqlQuery, FDb: DbEffect,
|
||||||
|
FReadDb: ReadDbEffect, FWriteDb: WriteDbEffect].}
|
||||||
|
|
||||||
|
template sql*(query: string): SqlQuery =
|
||||||
|
## constructs a SqlQuery from the string `query`. This is supposed to be
|
||||||
|
## used as a raw-string-literal modifier:
|
||||||
|
## ``sql"update user set counter = counter + 1"``
|
||||||
|
##
|
||||||
|
## If assertions are turned off, it does nothing. If assertions are turned
|
||||||
|
## on, later versions will check the string for valid syntax.
|
||||||
|
SqlQuery(query)
|
||||||
|
|
||||||
|
proc dbError*(msg: string) {.noreturn, noinline.} =
|
||||||
|
## raises an DbError exception with message `msg`.
|
||||||
|
var e: ref DbError
|
||||||
|
new(e)
|
||||||
|
e.msg = msg
|
||||||
|
raise e
|
||||||
|
|
@ -57,7 +57,7 @@ proc addHandler*(handler: var EventHandler, fn: proc(e: EventArgs) {.closure.})
|
||||||
|
|
||||||
proc removeHandler*(handler: var EventHandler, fn: proc(e: EventArgs) {.closure.}) =
|
proc removeHandler*(handler: var EventHandler, fn: proc(e: EventArgs) {.closure.}) =
|
||||||
## Removes the callback from the specified event handler.
|
## Removes the callback from the specified event handler.
|
||||||
for i in countup(0, len(handler.handlers) -1):
|
for i in countup(0, len(handler.handlers)-1):
|
||||||
if fn == handler.handlers[i]:
|
if fn == handler.handlers[i]:
|
||||||
handler.handlers.del(i)
|
handler.handlers.del(i)
|
||||||
break
|
break
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,9 @@
|
||||||
## This module allows you to monitor files or directories for changes using
|
## This module allows you to monitor files or directories for changes using
|
||||||
## asyncio.
|
## asyncio.
|
||||||
##
|
##
|
||||||
|
## **Warning**: This module will likely disappear soon and be moved into a
|
||||||
|
## new Nimble package.
|
||||||
|
##
|
||||||
## Windows support is not yet implemented.
|
## Windows support is not yet implemented.
|
||||||
##
|
##
|
||||||
## **Note:** This module uses ``inotify`` on Linux (Other Unixes are not yet
|
## **Note:** This module uses ``inotify`` on Linux (Other Unixes are not yet
|
||||||
|
|
@ -34,8 +37,8 @@ type
|
||||||
MonitorEventType* = enum ## Monitor event type
|
MonitorEventType* = enum ## Monitor event type
|
||||||
MonitorAccess, ## File was accessed.
|
MonitorAccess, ## File was accessed.
|
||||||
MonitorAttrib, ## Metadata changed.
|
MonitorAttrib, ## Metadata changed.
|
||||||
MonitorCloseWrite, ## Writtable file was closed.
|
MonitorCloseWrite, ## Writable file was closed.
|
||||||
MonitorCloseNoWrite, ## Unwrittable file closed.
|
MonitorCloseNoWrite, ## Non-writable file closed.
|
||||||
MonitorCreate, ## Subfile was created.
|
MonitorCreate, ## Subfile was created.
|
||||||
MonitorDelete, ## Subfile was deleted.
|
MonitorDelete, ## Subfile was deleted.
|
||||||
MonitorDeleteSelf, ## Watched file/directory was itself deleted.
|
MonitorDeleteSelf, ## Watched file/directory was itself deleted.
|
||||||
|
|
@ -78,21 +81,21 @@ proc add*(monitor: FSMonitor, target: string,
|
||||||
## watched paths of ``monitor``.
|
## watched paths of ``monitor``.
|
||||||
## You can specify the events to report using the ``filters`` parameter.
|
## You can specify the events to report using the ``filters`` parameter.
|
||||||
|
|
||||||
var INFilter = -1
|
var INFilter = 0
|
||||||
for f in filters:
|
for f in filters:
|
||||||
case f
|
case f
|
||||||
of MonitorAccess: INFilter = INFilter and IN_ACCESS
|
of MonitorAccess: INFilter = INFilter or IN_ACCESS
|
||||||
of MonitorAttrib: INFilter = INFilter and IN_ATTRIB
|
of MonitorAttrib: INFilter = INFilter or IN_ATTRIB
|
||||||
of MonitorCloseWrite: INFilter = INFilter and IN_CLOSE_WRITE
|
of MonitorCloseWrite: INFilter = INFilter or IN_CLOSE_WRITE
|
||||||
of MonitorCloseNoWrite: INFilter = INFilter and IN_CLOSE_NO_WRITE
|
of MonitorCloseNoWrite: INFilter = INFilter or IN_CLOSE_NO_WRITE
|
||||||
of MonitorCreate: INFilter = INFilter and IN_CREATE
|
of MonitorCreate: INFilter = INFilter or IN_CREATE
|
||||||
of MonitorDelete: INFilter = INFilter and IN_DELETE
|
of MonitorDelete: INFilter = INFilter or IN_DELETE
|
||||||
of MonitorDeleteSelf: INFilter = INFilter and IN_DELETE_SELF
|
of MonitorDeleteSelf: INFilter = INFilter or IN_DELETE_SELF
|
||||||
of MonitorModify: INFilter = INFilter and IN_MODIFY
|
of MonitorModify: INFilter = INFilter or IN_MODIFY
|
||||||
of MonitorMoveSelf: INFilter = INFilter and IN_MOVE_SELF
|
of MonitorMoveSelf: INFilter = INFilter or IN_MOVE_SELF
|
||||||
of MonitorMoved: INFilter = INFilter and IN_MOVED_FROM and IN_MOVED_TO
|
of MonitorMoved: INFilter = INFilter or IN_MOVED_FROM or IN_MOVED_TO
|
||||||
of MonitorOpen: INFilter = INFilter and IN_OPEN
|
of MonitorOpen: INFilter = INFilter or IN_OPEN
|
||||||
of MonitorAll: INFilter = INFilter and IN_ALL_EVENTS
|
of MonitorAll: INFilter = INFilter or IN_ALL_EVENTS
|
||||||
|
|
||||||
result = inotifyAddWatch(monitor.fd, target, INFilter.uint32)
|
result = inotifyAddWatch(monitor.fd, target, INFilter.uint32)
|
||||||
if result < 0:
|
if result < 0:
|
||||||
|
|
@ -200,9 +203,18 @@ proc register*(d: Dispatcher, monitor: FSMonitor,
|
||||||
|
|
||||||
when not defined(testing) and isMainModule:
|
when not defined(testing) and isMainModule:
|
||||||
proc main =
|
proc main =
|
||||||
var disp = newDispatcher()
|
var
|
||||||
var monitor = newMonitor()
|
disp = newDispatcher()
|
||||||
echo monitor.add("/home/dom/inotifytests/")
|
monitor = newMonitor()
|
||||||
|
n = 0
|
||||||
|
n = monitor.add("/tmp")
|
||||||
|
assert n == 1
|
||||||
|
n = monitor.add("/tmp", {MonitorAll})
|
||||||
|
assert n == 1
|
||||||
|
n = monitor.add("/tmp", {MonitorCloseWrite, MonitorCloseNoWrite})
|
||||||
|
assert n == 1
|
||||||
|
n = monitor.add("/tmp", {MonitorMoved, MonitorOpen, MonitorAccess})
|
||||||
|
assert n == 1
|
||||||
disp.register(monitor,
|
disp.register(monitor,
|
||||||
proc (m: FSMonitor, ev: MonitorEvent) =
|
proc (m: FSMonitor, ev: MonitorEvent) =
|
||||||
echo("Got event: ", ev.kind)
|
echo("Got event: ", ev.kind)
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,8 @@
|
||||||
## key-value mapping. The keys are required to be strings, but the values
|
## key-value mapping. The keys are required to be strings, but the values
|
||||||
## may be any Nim or user defined type. This module supports matching
|
## may be any Nim or user defined type. This module supports matching
|
||||||
## of keys in case-sensitive, case-insensitive and style-insensitive modes.
|
## of keys in case-sensitive, case-insensitive and style-insensitive modes.
|
||||||
|
##
|
||||||
|
## **Warning:** This module is deprecated, new code shouldn't use it!
|
||||||
|
|
||||||
{.deprecated.}
|
{.deprecated.}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -110,7 +110,7 @@ type
|
||||||
EInvalidProtocol: ProtocolError, EHttpRequestErr: HttpRequestError
|
EInvalidProtocol: ProtocolError, EHttpRequestErr: HttpRequestError
|
||||||
].}
|
].}
|
||||||
|
|
||||||
const defUserAgent* = "Nim httpclient/0.1"
|
const defUserAgent* = "Nim httpclient/" & NimVersion
|
||||||
|
|
||||||
proc httpError(msg: string) =
|
proc httpError(msg: string) =
|
||||||
var e: ref ProtocolError
|
var e: ref ProtocolError
|
||||||
|
|
@ -389,6 +389,7 @@ proc request*(url: string, httpMethod: string, extraHeaders = "",
|
||||||
## | An optional timeout can be specified in milliseconds, if reading from the
|
## | An optional timeout can be specified in milliseconds, if reading from the
|
||||||
## server takes longer than specified an ETimeout exception will be raised.
|
## server takes longer than specified an ETimeout exception will be raised.
|
||||||
var r = if proxy == nil: parseUri(url) else: proxy.url
|
var r = if proxy == nil: parseUri(url) else: proxy.url
|
||||||
|
var hostUrl = if proxy == nil: r else: parseUri(url)
|
||||||
var headers = substr(httpMethod, len("http"))
|
var headers = substr(httpMethod, len("http"))
|
||||||
# TODO: Use generateHeaders further down once it supports proxies.
|
# TODO: Use generateHeaders further down once it supports proxies.
|
||||||
if proxy == nil:
|
if proxy == nil:
|
||||||
|
|
@ -402,10 +403,10 @@ proc request*(url: string, httpMethod: string, extraHeaders = "",
|
||||||
|
|
||||||
headers.add(" HTTP/1.1\c\L")
|
headers.add(" HTTP/1.1\c\L")
|
||||||
|
|
||||||
if r.port == "":
|
if hostUrl.port == "":
|
||||||
add(headers, "Host: " & r.hostname & "\c\L")
|
add(headers, "Host: " & hostUrl.hostname & "\c\L")
|
||||||
else:
|
else:
|
||||||
add(headers, "Host: " & r.hostname & ":" & r.port & "\c\L")
|
add(headers, "Host: " & hostUrl.hostname & ":" & hostUrl.port & "\c\L")
|
||||||
|
|
||||||
if userAgent != "":
|
if userAgent != "":
|
||||||
add(headers, "User-Agent: " & userAgent & "\c\L")
|
add(headers, "User-Agent: " & userAgent & "\c\L")
|
||||||
|
|
@ -414,7 +415,6 @@ proc request*(url: string, httpMethod: string, extraHeaders = "",
|
||||||
add(headers, "Proxy-Authorization: basic " & auth & "\c\L")
|
add(headers, "Proxy-Authorization: basic " & auth & "\c\L")
|
||||||
add(headers, extraHeaders)
|
add(headers, extraHeaders)
|
||||||
add(headers, "\c\L")
|
add(headers, "\c\L")
|
||||||
|
|
||||||
var s = newSocket()
|
var s = newSocket()
|
||||||
if s == nil: raiseOSError(osLastError())
|
if s == nil: raiseOSError(osLastError())
|
||||||
var port = net.Port(80)
|
var port = net.Port(80)
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,9 @@
|
||||||
|
|
||||||
## This module implements a simple HTTP-Server.
|
## This module implements a simple HTTP-Server.
|
||||||
##
|
##
|
||||||
|
## **Warning**: This module will soon be deprecated in favour of
|
||||||
|
## the ``asyncdispatch`` module, you should use it instead.
|
||||||
|
##
|
||||||
## Example:
|
## Example:
|
||||||
##
|
##
|
||||||
## .. code-block:: nim
|
## .. code-block:: nim
|
||||||
|
|
|
||||||
|
|
@ -28,7 +28,10 @@ type
|
||||||
BaseLexer* = object of RootObj ## the base lexer. Inherit your lexer from
|
BaseLexer* = object of RootObj ## the base lexer. Inherit your lexer from
|
||||||
## this object.
|
## this object.
|
||||||
bufpos*: int ## the current position within the buffer
|
bufpos*: int ## the current position within the buffer
|
||||||
buf*: cstring ## the buffer itself
|
when defined(js): ## the buffer itself
|
||||||
|
buf*: string
|
||||||
|
else:
|
||||||
|
buf*: cstring
|
||||||
bufLen*: int ## length of buffer in characters
|
bufLen*: int ## length of buffer in characters
|
||||||
input: Stream ## the input stream
|
input: Stream ## the input stream
|
||||||
lineNumber*: int ## the current line number
|
lineNumber*: int ## the current line number
|
||||||
|
|
@ -43,7 +46,8 @@ const
|
||||||
|
|
||||||
proc close*(L: var BaseLexer) =
|
proc close*(L: var BaseLexer) =
|
||||||
## closes the base lexer. This closes `L`'s associated stream too.
|
## closes the base lexer. This closes `L`'s associated stream too.
|
||||||
dealloc(L.buf)
|
when not defined(js):
|
||||||
|
dealloc(L.buf)
|
||||||
close(L.input)
|
close(L.input)
|
||||||
|
|
||||||
proc fillBuffer(L: var BaseLexer) =
|
proc fillBuffer(L: var BaseLexer) =
|
||||||
|
|
@ -58,8 +62,11 @@ proc fillBuffer(L: var BaseLexer) =
|
||||||
toCopy = L.bufLen - L.sentinel - 1
|
toCopy = L.bufLen - L.sentinel - 1
|
||||||
assert(toCopy >= 0)
|
assert(toCopy >= 0)
|
||||||
if toCopy > 0:
|
if toCopy > 0:
|
||||||
moveMem(L.buf, addr(L.buf[L.sentinel + 1]), toCopy * chrSize)
|
when defined(js):
|
||||||
# "moveMem" handles overlapping regions
|
for i in 0 ..< toCopy: L.buf[i] = L.buf[L.sentinel + 1 + i]
|
||||||
|
else:
|
||||||
|
# "moveMem" handles overlapping regions
|
||||||
|
moveMem(L.buf, addr L.buf[L.sentinel + 1], toCopy * chrSize)
|
||||||
charsRead = readData(L.input, addr(L.buf[toCopy]),
|
charsRead = readData(L.input, addr(L.buf[toCopy]),
|
||||||
(L.sentinel + 1) * chrSize) div chrSize
|
(L.sentinel + 1) * chrSize) div chrSize
|
||||||
s = toCopy + charsRead
|
s = toCopy + charsRead
|
||||||
|
|
@ -81,7 +88,10 @@ proc fillBuffer(L: var BaseLexer) =
|
||||||
# double the buffer's size and try again:
|
# double the buffer's size and try again:
|
||||||
oldBufLen = L.bufLen
|
oldBufLen = L.bufLen
|
||||||
L.bufLen = L.bufLen * 2
|
L.bufLen = L.bufLen * 2
|
||||||
L.buf = cast[cstring](realloc(L.buf, L.bufLen * chrSize))
|
when defined(js):
|
||||||
|
L.buf.setLen(L.bufLen)
|
||||||
|
else:
|
||||||
|
L.buf = cast[cstring](realloc(L.buf, L.bufLen * chrSize))
|
||||||
assert(L.bufLen - oldBufLen == oldBufLen)
|
assert(L.bufLen - oldBufLen == oldBufLen)
|
||||||
charsRead = readData(L.input, addr(L.buf[oldBufLen]),
|
charsRead = readData(L.input, addr(L.buf[oldBufLen]),
|
||||||
oldBufLen * chrSize) div chrSize
|
oldBufLen * chrSize) div chrSize
|
||||||
|
|
@ -139,7 +149,10 @@ proc open*(L: var BaseLexer, input: Stream, bufLen: int = 8192;
|
||||||
L.bufpos = 0
|
L.bufpos = 0
|
||||||
L.bufLen = bufLen
|
L.bufLen = bufLen
|
||||||
L.refillChars = refillChars
|
L.refillChars = refillChars
|
||||||
L.buf = cast[cstring](alloc(bufLen * chrSize))
|
when defined(js):
|
||||||
|
L.buf = newString(bufLen)
|
||||||
|
else:
|
||||||
|
L.buf = cast[cstring](alloc(bufLen * chrSize))
|
||||||
L.sentinel = bufLen - 1
|
L.sentinel = bufLen - 1
|
||||||
L.lineStart = 0
|
L.lineStart = 0
|
||||||
L.lineNumber = 1 # lines start at 1
|
L.lineNumber = 1 # lines start at 1
|
||||||
|
|
|
||||||
|
|
@ -118,26 +118,6 @@ proc sum*[T](x: openArray[T]): T {.noSideEffect.} =
|
||||||
## If `x` is empty, 0 is returned.
|
## If `x` is empty, 0 is returned.
|
||||||
for i in items(x): result = result + i
|
for i in items(x): result = result + i
|
||||||
|
|
||||||
template toFloat(f: float): float = f
|
|
||||||
|
|
||||||
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 items(x):
|
|
||||||
var diff = toFloat(i) - m
|
|
||||||
result = result + diff*diff
|
|
||||||
result = result / toFloat(len(x))
|
|
||||||
|
|
||||||
proc random*(max: int): int {.benign.}
|
proc random*(max: int): int {.benign.}
|
||||||
## Returns a random number in the range 0..max-1. The sequence of
|
## Returns a random number in the range 0..max-1. The sequence of
|
||||||
## random number is always the same, unless `randomize` is called
|
## random number is always the same, unless `randomize` is called
|
||||||
|
|
@ -376,48 +356,6 @@ proc random*[T](a: openArray[T]): T =
|
||||||
## returns a random element from the openarray `a`.
|
## returns a random element from the openarray `a`.
|
||||||
result = a[random(a.low..a.len)]
|
result = a[random(a.low..a.len)]
|
||||||
|
|
||||||
type
|
|
||||||
RunningStat* = object ## an accumulator for statistical data
|
|
||||||
n*: int ## number of pushed data
|
|
||||||
sum*, min*, max*, mean*: float ## self-explaining
|
|
||||||
oldM, oldS, newS: float
|
|
||||||
|
|
||||||
{.deprecated: [TFloatClass: FloatClass, TRunningStat: RunningStat].}
|
|
||||||
|
|
||||||
proc push*(s: var RunningStat, x: float) =
|
|
||||||
## pushes a value `x` for processing
|
|
||||||
inc(s.n)
|
|
||||||
# See Knuth TAOCP vol 2, 3rd edition, page 232
|
|
||||||
if s.n == 1:
|
|
||||||
s.min = x
|
|
||||||
s.max = x
|
|
||||||
s.oldM = x
|
|
||||||
s.mean = x
|
|
||||||
s.oldS = 0.0
|
|
||||||
else:
|
|
||||||
if s.min > x: s.min = x
|
|
||||||
if s.max < x: s.max = x
|
|
||||||
s.mean = s.oldM + (x - s.oldM)/toFloat(s.n)
|
|
||||||
s.newS = s.oldS + (x - s.oldM)*(x - s.mean)
|
|
||||||
|
|
||||||
# set up for next iteration:
|
|
||||||
s.oldM = s.mean
|
|
||||||
s.oldS = s.newS
|
|
||||||
s.sum = s.sum + x
|
|
||||||
|
|
||||||
proc push*(s: var RunningStat, x: int) =
|
|
||||||
## pushes a value `x` for processing. `x` is simply converted to ``float``
|
|
||||||
## and the other push operation is called.
|
|
||||||
push(s, toFloat(x))
|
|
||||||
|
|
||||||
proc variance*(s: RunningStat): float =
|
|
||||||
## computes the current variance of `s`
|
|
||||||
if s.n > 1: result = s.newS / (toFloat(s.n - 1))
|
|
||||||
|
|
||||||
proc standardDeviation*(s: RunningStat): float =
|
|
||||||
## computes the current standard deviation of `s`
|
|
||||||
result = sqrt(variance(s))
|
|
||||||
|
|
||||||
{.pop.}
|
{.pop.}
|
||||||
{.pop.}
|
{.pop.}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -203,9 +203,12 @@ proc getAddrInfo*(address: string, port: Port, domain: Domain = AF_INET,
|
||||||
hints.ai_family = toInt(domain)
|
hints.ai_family = toInt(domain)
|
||||||
hints.ai_socktype = toInt(sockType)
|
hints.ai_socktype = toInt(sockType)
|
||||||
hints.ai_protocol = toInt(protocol)
|
hints.ai_protocol = toInt(protocol)
|
||||||
|
# OpenBSD doesn't support AI_V4MAPPED and doesn't define the macro AI_V4MAPPED.
|
||||||
|
# FreeBSD doesn't support AI_V4MAPPED but defines the macro.
|
||||||
# https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=198092
|
# https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=198092
|
||||||
when not defined(freebsd):
|
when not defined(freebsd) and not defined(openbsd) and not defined(netbsd):
|
||||||
hints.ai_flags = AI_V4MAPPED
|
if domain == AF_INET6:
|
||||||
|
hints.ai_flags = AI_V4MAPPED
|
||||||
var gaiResult = getaddrinfo(address, $port, addr(hints), result)
|
var gaiResult = getaddrinfo(address, $port, addr(hints), result)
|
||||||
if gaiResult != 0'i32:
|
if gaiResult != 0'i32:
|
||||||
when useWinVersion:
|
when useWinVersion:
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
#
|
#
|
||||||
#
|
#
|
||||||
# Nim's Runtime Library
|
# Nim's Runtime Library
|
||||||
# (c) Copyright 2012 Andreas Rumpf
|
# (c) Copyright 2015 Andreas Rumpf
|
||||||
#
|
#
|
||||||
# See the file "copying.txt", included in this
|
# See the file "copying.txt", included in this
|
||||||
# distribution, for details about the copyright.
|
# distribution, for details about the copyright.
|
||||||
|
|
@ -117,24 +117,38 @@ when defined(memProfiler):
|
||||||
var
|
var
|
||||||
gTicker {.threadvar.}: int
|
gTicker {.threadvar.}: int
|
||||||
|
|
||||||
proc hook(st: StackTrace, size: int) {.nimcall.} =
|
proc requestedHook(): bool {.nimcall.} =
|
||||||
if gTicker == 0:
|
if gTicker == 0:
|
||||||
gTicker = -1
|
|
||||||
when defined(ignoreAllocationSize):
|
|
||||||
hookAux(st, 1)
|
|
||||||
else:
|
|
||||||
hookAux(st, size)
|
|
||||||
gTicker = SamplingInterval
|
gTicker = SamplingInterval
|
||||||
|
result = true
|
||||||
dec gTicker
|
dec gTicker
|
||||||
|
|
||||||
|
proc hook(st: StackTrace, size: int) {.nimcall.} =
|
||||||
|
when defined(ignoreAllocationSize):
|
||||||
|
hookAux(st, 1)
|
||||||
|
else:
|
||||||
|
hookAux(st, size)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
var
|
var
|
||||||
t0 {.threadvar.}: Ticks
|
t0 {.threadvar.}: Ticks
|
||||||
|
gTicker: int # we use an additional counter to
|
||||||
|
# avoid calling 'getTicks' too frequently
|
||||||
|
|
||||||
|
proc requestedHook(): bool {.nimcall.} =
|
||||||
|
if interval == 0: result = true
|
||||||
|
elif gTicker == 0:
|
||||||
|
gTicker = 500
|
||||||
|
if getTicks() - t0 > interval:
|
||||||
|
result = true
|
||||||
|
else:
|
||||||
|
dec gTicker
|
||||||
|
|
||||||
proc hook(st: StackTrace) {.nimcall.} =
|
proc hook(st: StackTrace) {.nimcall.} =
|
||||||
|
#echo "profiling! ", interval
|
||||||
if interval == 0:
|
if interval == 0:
|
||||||
hookAux(st, 1)
|
hookAux(st, 1)
|
||||||
elif int64(t0) == 0 or getTicks() - t0 > interval:
|
else:
|
||||||
hookAux(st, 1)
|
hookAux(st, 1)
|
||||||
t0 = getTicks()
|
t0 = getTicks()
|
||||||
|
|
||||||
|
|
@ -145,9 +159,10 @@ proc cmpEntries(a, b: ptr ProfileEntry): int =
|
||||||
result = b.getTotal - a.getTotal
|
result = b.getTotal - a.getTotal
|
||||||
|
|
||||||
proc `//`(a, b: int): string =
|
proc `//`(a, b: int): string =
|
||||||
result = format("$1/$2 = $3%", a, b, formatFloat(a / b * 100.0, ffDefault, 2))
|
result = format("$1/$2 = $3%", a, b, formatFloat(a / b * 100.0, ffDecimal, 2))
|
||||||
|
|
||||||
proc writeProfile() {.noconv.} =
|
proc writeProfile() {.noconv.} =
|
||||||
|
system.profilingRequestedHook = nil
|
||||||
when declared(system.StackTrace):
|
when declared(system.StackTrace):
|
||||||
system.profilerHook = nil
|
system.profilerHook = nil
|
||||||
const filename = "profile_results.txt"
|
const filename = "profile_results.txt"
|
||||||
|
|
@ -193,14 +208,15 @@ var
|
||||||
proc disableProfiling*() =
|
proc disableProfiling*() =
|
||||||
when declared(system.StackTrace):
|
when declared(system.StackTrace):
|
||||||
atomicDec disabled
|
atomicDec disabled
|
||||||
system.profilerHook = nil
|
system.profilingRequestedHook = nil
|
||||||
|
|
||||||
proc enableProfiling*() =
|
proc enableProfiling*() =
|
||||||
when declared(system.StackTrace):
|
when declared(system.StackTrace):
|
||||||
if atomicInc(disabled) >= 0:
|
if atomicInc(disabled) >= 0:
|
||||||
system.profilerHook = hook
|
system.profilingRequestedHook = requestedHook
|
||||||
|
|
||||||
when declared(system.StackTrace):
|
when declared(system.StackTrace):
|
||||||
|
system.profilingRequestedHook = requestedHook
|
||||||
system.profilerHook = hook
|
system.profilerHook = hook
|
||||||
addQuitProc(writeProfile)
|
addQuitProc(writeProfile)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,9 @@
|
||||||
# distribution, for details about the copyright.
|
# distribution, for details about the copyright.
|
||||||
#
|
#
|
||||||
|
|
||||||
|
## **Warning:** This module will be moved out of the stdlib and into a
|
||||||
|
## Nimble package, don't use it.
|
||||||
|
|
||||||
type OneVarFunction* = proc (x: float): float
|
type OneVarFunction* = proc (x: float): float
|
||||||
|
|
||||||
{.deprecated: [TOneVarFunction: OneVarFunction].}
|
{.deprecated: [TOneVarFunction: OneVarFunction].}
|
||||||
|
|
|
||||||
|
|
@ -28,7 +28,7 @@
|
||||||
##
|
##
|
||||||
## .. code-block:: nim
|
## .. code-block:: nim
|
||||||
##
|
##
|
||||||
## import optionals
|
## import options
|
||||||
##
|
##
|
||||||
## proc find(haystack: string, needle: char): Option[int] =
|
## proc find(haystack: string, needle: char): Option[int] =
|
||||||
## for i, c in haystack:
|
## for i, c in haystack:
|
||||||
|
|
@ -156,7 +156,7 @@ proc `$`*[T]( self: Option[T] ): string =
|
||||||
when isMainModule:
|
when isMainModule:
|
||||||
import unittest, sequtils
|
import unittest, sequtils
|
||||||
|
|
||||||
suite "optionals":
|
suite "options":
|
||||||
# work around a bug in unittest
|
# work around a bug in unittest
|
||||||
let intNone = none(int)
|
let intNone = none(int)
|
||||||
let stringNone = none(string)
|
let stringNone = none(string)
|
||||||
|
|
|
||||||
|
|
@ -810,6 +810,10 @@ type
|
||||||
|
|
||||||
{.deprecated: [TPathComponent: PathComponent].}
|
{.deprecated: [TPathComponent: PathComponent].}
|
||||||
|
|
||||||
|
proc staticWalkDir(dir: string; relative: bool): seq[
|
||||||
|
tuple[kind: PathComponent, path: string]] =
|
||||||
|
discard
|
||||||
|
|
||||||
iterator walkDir*(dir: string; relative=false): tuple[kind: PathComponent, path: string] {.
|
iterator walkDir*(dir: string; relative=false): tuple[kind: PathComponent, path: string] {.
|
||||||
tags: [ReadDirEffect].} =
|
tags: [ReadDirEffect].} =
|
||||||
## walks over the directory `dir` and yields for each directory or file in
|
## walks over the directory `dir` and yields for each directory or file in
|
||||||
|
|
@ -833,49 +837,53 @@ iterator walkDir*(dir: string; relative=false): tuple[kind: PathComponent, path:
|
||||||
## dirA/dirC
|
## dirA/dirC
|
||||||
## dirA/fileA1.txt
|
## dirA/fileA1.txt
|
||||||
## dirA/fileA2.txt
|
## dirA/fileA2.txt
|
||||||
when defined(windows):
|
when nimvm:
|
||||||
var f: WIN32_FIND_DATA
|
for k, v in items(staticWalkDir(dir, relative)):
|
||||||
var h = findFirstFile(dir / "*", f)
|
yield (k, v)
|
||||||
if h != -1:
|
|
||||||
while true:
|
|
||||||
var k = pcFile
|
|
||||||
if not skipFindData(f):
|
|
||||||
if (f.dwFileAttributes and FILE_ATTRIBUTE_DIRECTORY) != 0'i32:
|
|
||||||
k = pcDir
|
|
||||||
if (f.dwFileAttributes and FILE_ATTRIBUTE_REPARSE_POINT) != 0'i32:
|
|
||||||
k = succ(k)
|
|
||||||
let xx = if relative: extractFilename(getFilename(f))
|
|
||||||
else: dir / extractFilename(getFilename(f))
|
|
||||||
yield (k, xx)
|
|
||||||
if findNextFile(h, f) == 0'i32: break
|
|
||||||
findClose(h)
|
|
||||||
else:
|
else:
|
||||||
var d = opendir(dir)
|
when defined(windows):
|
||||||
if d != nil:
|
var f: WIN32_FIND_DATA
|
||||||
while true:
|
var h = findFirstFile(dir / "*", f)
|
||||||
var x = readdir(d)
|
if h != -1:
|
||||||
if x == nil: break
|
while true:
|
||||||
var y = $x.d_name
|
|
||||||
if y != "." and y != "..":
|
|
||||||
var s: Stat
|
|
||||||
if not relative:
|
|
||||||
y = dir / y
|
|
||||||
var k = pcFile
|
var k = pcFile
|
||||||
|
if not skipFindData(f):
|
||||||
|
if (f.dwFileAttributes and FILE_ATTRIBUTE_DIRECTORY) != 0'i32:
|
||||||
|
k = pcDir
|
||||||
|
if (f.dwFileAttributes and FILE_ATTRIBUTE_REPARSE_POINT) != 0'i32:
|
||||||
|
k = succ(k)
|
||||||
|
let xx = if relative: extractFilename(getFilename(f))
|
||||||
|
else: dir / extractFilename(getFilename(f))
|
||||||
|
yield (k, xx)
|
||||||
|
if findNextFile(h, f) == 0'i32: break
|
||||||
|
findClose(h)
|
||||||
|
else:
|
||||||
|
var d = opendir(dir)
|
||||||
|
if d != nil:
|
||||||
|
while true:
|
||||||
|
var x = readdir(d)
|
||||||
|
if x == nil: break
|
||||||
|
var y = $x.d_name
|
||||||
|
if y != "." and y != "..":
|
||||||
|
var s: Stat
|
||||||
|
if not relative:
|
||||||
|
y = dir / y
|
||||||
|
var k = pcFile
|
||||||
|
|
||||||
when defined(linux) or defined(macosx) or defined(bsd):
|
when defined(linux) or defined(macosx) or defined(bsd):
|
||||||
if x.d_type != DT_UNKNOWN:
|
if x.d_type != DT_UNKNOWN:
|
||||||
if x.d_type == DT_DIR: k = pcDir
|
if x.d_type == DT_DIR: k = pcDir
|
||||||
if x.d_type == DT_LNK:
|
if x.d_type == DT_LNK:
|
||||||
if dirExists(y): k = pcLinkToDir
|
if dirExists(y): k = pcLinkToDir
|
||||||
else: k = succ(k)
|
else: k = succ(k)
|
||||||
yield (k, y)
|
yield (k, y)
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if lstat(y, s) < 0'i32: break
|
if lstat(y, s) < 0'i32: break
|
||||||
if S_ISDIR(s.st_mode): k = pcDir
|
if S_ISDIR(s.st_mode): k = pcDir
|
||||||
if S_ISLNK(s.st_mode): k = succ(k)
|
if S_ISLNK(s.st_mode): k = succ(k)
|
||||||
yield (k, y)
|
yield (k, y)
|
||||||
discard closedir(d)
|
discard closedir(d)
|
||||||
|
|
||||||
iterator walkDirRec*(dir: string, filter={pcFile, pcDir}): string {.
|
iterator walkDirRec*(dir: string, filter={pcFile, pcDir}): string {.
|
||||||
tags: [ReadDirEffect].} =
|
tags: [ReadDirEffect].} =
|
||||||
|
|
@ -1353,7 +1361,7 @@ proc getAppFilename*(): string {.rtl, extern: "nos$1", tags: [ReadIOEffect].} =
|
||||||
# /proc/<pid>/file
|
# /proc/<pid>/file
|
||||||
when defined(windows):
|
when defined(windows):
|
||||||
when useWinUnicode:
|
when useWinUnicode:
|
||||||
var buf = cast[WideCString](alloc(256*2))
|
var buf = newWideCString("", 256)
|
||||||
var len = getModuleFileNameW(0, buf, 256)
|
var len = getModuleFileNameW(0, buf, 256)
|
||||||
result = buf$len
|
result = buf$len
|
||||||
else:
|
else:
|
||||||
|
|
|
||||||
|
|
@ -886,7 +886,7 @@ elif not defined(useNimRtl):
|
||||||
discard write(data.pErrorPipe[writeIdx], addr error, sizeof(error))
|
discard write(data.pErrorPipe[writeIdx], addr error, sizeof(error))
|
||||||
exitnow(1)
|
exitnow(1)
|
||||||
|
|
||||||
when defined(macosx) or defined(freebsd):
|
when defined(macosx) or defined(freebsd) or defined(netbsd) or defined(android):
|
||||||
var environ {.importc.}: cstringArray
|
var environ {.importc.}: cstringArray
|
||||||
|
|
||||||
proc startProcessAfterFork(data: ptr StartProcessData) =
|
proc startProcessAfterFork(data: ptr StartProcessData) =
|
||||||
|
|
@ -916,7 +916,7 @@ elif not defined(useNimRtl):
|
||||||
discard fcntl(data.pErrorPipe[writeIdx], F_SETFD, FD_CLOEXEC)
|
discard fcntl(data.pErrorPipe[writeIdx], F_SETFD, FD_CLOEXEC)
|
||||||
|
|
||||||
if data.optionPoUsePath:
|
if data.optionPoUsePath:
|
||||||
when defined(macosx) or defined(freebsd):
|
when defined(macosx) or defined(freebsd) or defined(netbsd) or defined(android):
|
||||||
# MacOSX doesn't have execvpe, so we need workaround.
|
# MacOSX doesn't have execvpe, so we need workaround.
|
||||||
# On MacOSX we can arrive here only from fork, so this is safe:
|
# On MacOSX we can arrive here only from fork, so this is safe:
|
||||||
environ = data.sysEnv
|
environ = data.sysEnv
|
||||||
|
|
@ -937,9 +937,10 @@ elif not defined(useNimRtl):
|
||||||
if p.inStream != nil: close(p.inStream)
|
if p.inStream != nil: close(p.inStream)
|
||||||
if p.outStream != nil: close(p.outStream)
|
if p.outStream != nil: close(p.outStream)
|
||||||
if p.errStream != nil: close(p.errStream)
|
if p.errStream != nil: close(p.errStream)
|
||||||
discard close(p.inHandle)
|
if poParentStreams notin p.options:
|
||||||
discard close(p.outHandle)
|
discard close(p.inHandle)
|
||||||
discard close(p.errHandle)
|
discard close(p.outHandle)
|
||||||
|
discard close(p.errHandle)
|
||||||
|
|
||||||
proc suspend(p: Process) =
|
proc suspend(p: Process) =
|
||||||
if kill(p.id, SIGSTOP) != 0'i32: raiseOsError(osLastError())
|
if kill(p.id, SIGSTOP) != 0'i32: raiseOsError(osLastError())
|
||||||
|
|
|
||||||
27
lib/pure/oswalkdir.nim
Normal file
27
lib/pure/oswalkdir.nim
Normal file
|
|
@ -0,0 +1,27 @@
|
||||||
|
|
||||||
|
## Compile-time only version for walkDir if you need it at compile-time
|
||||||
|
## for JavaScript.
|
||||||
|
|
||||||
|
type
|
||||||
|
PathComponent* = enum ## Enumeration specifying a path component.
|
||||||
|
pcFile, ## path refers to a file
|
||||||
|
pcLinkToFile, ## path refers to a symbolic link to a file
|
||||||
|
pcDir, ## path refers to a directory
|
||||||
|
pcLinkToDir ## path refers to a symbolic link to a directory
|
||||||
|
|
||||||
|
proc staticWalkDir(dir: string; relative: bool): seq[
|
||||||
|
tuple[kind: PathComponent, path: string]] =
|
||||||
|
discard
|
||||||
|
|
||||||
|
iterator walkDir*(dir: string; relative=false): tuple[kind: PathComponent, path: string] =
|
||||||
|
for k, v in items(staticWalkDir(dir, relative)):
|
||||||
|
yield (k, v)
|
||||||
|
|
||||||
|
iterator walkDirRec*(dir: string, filter={pcFile, pcDir}): string =
|
||||||
|
var stack = @[dir]
|
||||||
|
while stack.len > 0:
|
||||||
|
for k,p in walkDir(stack.pop()):
|
||||||
|
if k in filter:
|
||||||
|
case k
|
||||||
|
of pcFile, pcLinkToFile: yield p
|
||||||
|
of pcDir, pcLinkToDir: stack.add(p)
|
||||||
|
|
@ -70,7 +70,7 @@ when not defined(createNimRtl):
|
||||||
## Initializes option parser from current command line arguments.
|
## Initializes option parser from current command line arguments.
|
||||||
return initOptParser(commandLineParams())
|
return initOptParser(commandLineParams())
|
||||||
|
|
||||||
proc next*(p: var OptParser) {.rtl, extern: "npo$1".}
|
proc next*(p: var OptParser) {.rtl, extern: "npo2$1".}
|
||||||
|
|
||||||
proc nextOption(p: var OptParser, token: string, allowEmpty: bool) =
|
proc nextOption(p: var OptParser, token: string, allowEmpty: bool) =
|
||||||
for splitchar in [':', '=']:
|
for splitchar in [':', '=']:
|
||||||
|
|
@ -113,7 +113,7 @@ proc next(p: var OptParser) =
|
||||||
p.key = token
|
p.key = token
|
||||||
p.val = ""
|
p.val = ""
|
||||||
|
|
||||||
proc cmdLineRest*(p: OptParser): TaintedString {.rtl, extern: "npo$1", deprecated.} =
|
proc cmdLineRest*(p: OptParser): TaintedString {.rtl, extern: "npo2$1", deprecated.} =
|
||||||
## Returns part of command line string that has not been parsed yet.
|
## Returns part of command line string that has not been parsed yet.
|
||||||
## Do not use - does not correctly handle whitespace.
|
## Do not use - does not correctly handle whitespace.
|
||||||
return p.cmd[p.pos..p.cmd.len-1].join(" ")
|
return p.cmd[p.pos..p.cmd.len-1].join(" ")
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,7 @@ const
|
||||||
proc toLower(c: char): char {.inline.} =
|
proc toLower(c: char): char {.inline.} =
|
||||||
result = if c in {'A'..'Z'}: chr(ord(c)-ord('A')+ord('a')) else: c
|
result = if c in {'A'..'Z'}: chr(ord(c)-ord('A')+ord('a')) else: c
|
||||||
|
|
||||||
proc parseHex*(s: string, number: var int, start = 0): int {.
|
proc parseHex*(s: string, number: var int, start = 0; maxLen = 0): int {.
|
||||||
rtl, extern: "npuParseHex", noSideEffect.} =
|
rtl, extern: "npuParseHex", noSideEffect.} =
|
||||||
## Parses a hexadecimal number and stores its value in ``number``.
|
## Parses a hexadecimal number and stores its value in ``number``.
|
||||||
##
|
##
|
||||||
|
|
@ -45,11 +45,14 @@ proc parseHex*(s: string, number: var int, start = 0): int {.
|
||||||
## discard parseHex("0x38", value)
|
## discard parseHex("0x38", value)
|
||||||
## assert value == -200
|
## assert value == -200
|
||||||
##
|
##
|
||||||
|
## If 'maxLen==0' the length of the hexadecimal number has no
|
||||||
|
## upper bound. Not more than ```maxLen`` characters are parsed.
|
||||||
var i = start
|
var i = start
|
||||||
var foundDigit = false
|
var foundDigit = false
|
||||||
if s[i] == '0' and (s[i+1] == 'x' or s[i+1] == 'X'): inc(i, 2)
|
if s[i] == '0' and (s[i+1] == 'x' or s[i+1] == 'X'): inc(i, 2)
|
||||||
elif s[i] == '#': inc(i)
|
elif s[i] == '#': inc(i)
|
||||||
while true:
|
let last = if maxLen == 0: s.len else: i+maxLen
|
||||||
|
while i < last:
|
||||||
case s[i]
|
case s[i]
|
||||||
of '_': discard
|
of '_': discard
|
||||||
of '0'..'9':
|
of '0'..'9':
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,9 @@
|
||||||
# distribution, for details about the copyright.
|
# distribution, for details about the copyright.
|
||||||
#
|
#
|
||||||
|
|
||||||
|
## **Warning:** This module will be moved out of the stdlib and into a
|
||||||
|
## Nimble package, don't use it.
|
||||||
|
|
||||||
import math
|
import math
|
||||||
import strutils
|
import strutils
|
||||||
import numeric
|
import numeric
|
||||||
|
|
|
||||||
1096
lib/pure/redis.nim
1096
lib/pure/redis.nim
File diff suppressed because it is too large
Load diff
|
|
@ -9,6 +9,9 @@
|
||||||
|
|
||||||
## Module for converting an integer to a Roman numeral.
|
## Module for converting an integer to a Roman numeral.
|
||||||
## See http://en.wikipedia.org/wiki/Roman_numerals for reference.
|
## See http://en.wikipedia.org/wiki/Roman_numerals for reference.
|
||||||
|
##
|
||||||
|
## **Warning:** This module will be moved out of the stdlib and into a
|
||||||
|
## Nimble package, don't use it.
|
||||||
|
|
||||||
const
|
const
|
||||||
RomanNumeralDigits* = {'I', 'i', 'V', 'v', 'X', 'x', 'L', 'l', 'C', 'c',
|
RomanNumeralDigits* = {'I', 'i', 'V', 'v', 'X', 'x', 'L', 'l', 'C', 'c',
|
||||||
|
|
|
||||||
348
lib/pure/stats.nim
Normal file
348
lib/pure/stats.nim
Normal file
|
|
@ -0,0 +1,348 @@
|
||||||
|
#
|
||||||
|
#
|
||||||
|
# Nim's Runtime Library
|
||||||
|
# (c) Copyright 2015 Andreas Rumpf
|
||||||
|
#
|
||||||
|
# See the file "copying.txt", included in this
|
||||||
|
# distribution, for details about the copyright.
|
||||||
|
#
|
||||||
|
## Statistical analysis framework for performing
|
||||||
|
## basic statistical analysis of data.
|
||||||
|
## The data is analysed in a single pass, when a data value
|
||||||
|
## is pushed to the ``RunningStat`` or ``RunningRegress`` objects
|
||||||
|
##
|
||||||
|
## ``RunningStat`` calculates for a single data set
|
||||||
|
## - n (data count)
|
||||||
|
## - min (smallest value)
|
||||||
|
## - max (largest value)
|
||||||
|
## - sum
|
||||||
|
## - mean
|
||||||
|
## - variance
|
||||||
|
## - varianceS (sample var)
|
||||||
|
## - standardDeviation
|
||||||
|
## - standardDeviationS (sample stddev)
|
||||||
|
## - skewness (the third statistical moment)
|
||||||
|
## - kurtosis (the fourth statistical moment)
|
||||||
|
##
|
||||||
|
## ``RunningRegress`` calculates for two sets of data
|
||||||
|
## - n
|
||||||
|
## - slope
|
||||||
|
## - intercept
|
||||||
|
## - correlation
|
||||||
|
##
|
||||||
|
## Procs have been provided to calculate statistics on arrays and sequences.
|
||||||
|
##
|
||||||
|
## However, if more than a single statistical calculation is required, it is more
|
||||||
|
## efficient to push the data once to the RunningStat object, and
|
||||||
|
## call the numerous statistical procs for the RunningStat object.
|
||||||
|
##
|
||||||
|
## .. code-block:: Nim
|
||||||
|
##
|
||||||
|
## var rs: RunningStat
|
||||||
|
## rs.push(MySeqOfData)
|
||||||
|
## rs.mean()
|
||||||
|
## rs.variance()
|
||||||
|
## rs.skewness()
|
||||||
|
## rs.kurtosis()
|
||||||
|
|
||||||
|
from math import FloatClass, sqrt, pow, round
|
||||||
|
|
||||||
|
{.push debugger:off .} # the user does not want to trace a part
|
||||||
|
# of the standard library!
|
||||||
|
{.push checks:off, line_dir:off, stack_trace:off.}
|
||||||
|
|
||||||
|
type
|
||||||
|
RunningStat* = object ## an accumulator for statistical data
|
||||||
|
n*: int ## number of pushed data
|
||||||
|
min*, max*, sum*: float ## self-explaining
|
||||||
|
mom1, mom2, mom3, mom4: float ## statistical moments, mom1 is mean
|
||||||
|
|
||||||
|
|
||||||
|
RunningRegress* = object ## an accumulator for regression calculations
|
||||||
|
n*: int ## number of pushed data
|
||||||
|
x_stats*: RunningStat ## stats for first set of data
|
||||||
|
y_stats*: RunningStat ## stats for second set of data
|
||||||
|
s_xy: float ## accumulated data for combined xy
|
||||||
|
|
||||||
|
{.deprecated: [TFloatClass: FloatClass, TRunningStat: RunningStat].}
|
||||||
|
|
||||||
|
# ----------- RunningStat --------------------------
|
||||||
|
proc clear*(s: var RunningStat) =
|
||||||
|
## reset `s`
|
||||||
|
s.n = 0
|
||||||
|
s.min = toBiggestFloat(int.high)
|
||||||
|
s.max = 0.0
|
||||||
|
s.sum = 0.0
|
||||||
|
s.mom1 = 0.0
|
||||||
|
s.mom2 = 0.0
|
||||||
|
s.mom3 = 0.0
|
||||||
|
s.mom4 = 0.0
|
||||||
|
|
||||||
|
proc push*(s: var RunningStat, x: float) =
|
||||||
|
## pushes a value `x` for processing
|
||||||
|
if s.n == 0: s.min = x
|
||||||
|
inc(s.n)
|
||||||
|
# See Knuth TAOCP vol 2, 3rd edition, page 232
|
||||||
|
if s.min > x: s.min = x
|
||||||
|
if s.max < x: s.max = x
|
||||||
|
s.sum += x
|
||||||
|
let n = toFloat(s.n)
|
||||||
|
let delta = x - s.mom1
|
||||||
|
let delta_n = delta / toFloat(s.n)
|
||||||
|
let delta_n2 = delta_n * delta_n
|
||||||
|
let term1 = delta * delta_n * toFloat(s.n - 1)
|
||||||
|
s.mom4 += term1 * delta_n2 * (n*n - 3*n + 3) +
|
||||||
|
6*delta_n2*s.mom2 - 4*delta_n*s.mom3
|
||||||
|
s.mom3 += term1 * delta_n * (n - 2) - 3*delta_n*s.mom2
|
||||||
|
s.mom2 += term1
|
||||||
|
s.mom1 += delta_n
|
||||||
|
|
||||||
|
proc push*(s: var RunningStat, x: int) =
|
||||||
|
## pushes a value `x` for processing.
|
||||||
|
##
|
||||||
|
## `x` is simply converted to ``float``
|
||||||
|
## and the other push operation is called.
|
||||||
|
s.push(toFloat(x))
|
||||||
|
|
||||||
|
proc push*(s: var RunningStat, x: openarray[float|int]) =
|
||||||
|
## pushes all values of `x` for processing.
|
||||||
|
##
|
||||||
|
## Int values of `x` are simply converted to ``float`` and
|
||||||
|
## the other push operation is called.
|
||||||
|
for val in x:
|
||||||
|
s.push(val)
|
||||||
|
|
||||||
|
proc mean*(s: RunningStat): float =
|
||||||
|
## computes the current mean of `s`
|
||||||
|
result = s.mom1
|
||||||
|
|
||||||
|
proc variance*(s: RunningStat): float =
|
||||||
|
## computes the current population variance of `s`
|
||||||
|
result = s.mom2 / toFloat(s.n)
|
||||||
|
|
||||||
|
proc varianceS*(s: RunningStat): float =
|
||||||
|
## computes the current sample variance of `s`
|
||||||
|
if s.n > 1: result = s.mom2 / toFloat(s.n - 1)
|
||||||
|
|
||||||
|
proc standardDeviation*(s: RunningStat): float =
|
||||||
|
## computes the current population standard deviation of `s`
|
||||||
|
result = sqrt(variance(s))
|
||||||
|
|
||||||
|
proc standardDeviationS*(s: RunningStat): float =
|
||||||
|
## computes the current sample standard deviation of `s`
|
||||||
|
result = sqrt(varianceS(s))
|
||||||
|
|
||||||
|
proc skewness*(s: RunningStat): float =
|
||||||
|
## computes the current population skewness of `s`
|
||||||
|
result = sqrt(toFloat(s.n)) * s.mom3 / pow(s.mom2, 1.5)
|
||||||
|
|
||||||
|
proc skewnessS*(s: RunningStat): float =
|
||||||
|
## computes the current sample skewness of `s`
|
||||||
|
let s2 = skewness(s)
|
||||||
|
result = sqrt(toFloat(s.n*(s.n-1)))*s2 / toFloat(s.n-2)
|
||||||
|
|
||||||
|
proc kurtosis*(s: RunningStat): float =
|
||||||
|
## computes the current population kurtosis of `s`
|
||||||
|
result = toFloat(s.n) * s.mom4 / (s.mom2 * s.mom2) - 3.0
|
||||||
|
|
||||||
|
proc kurtosisS*(s: RunningStat): float =
|
||||||
|
## computes the current sample kurtosis of `s`
|
||||||
|
result = toFloat(s.n-1) / toFloat((s.n-2)*(s.n-3)) *
|
||||||
|
(toFloat(s.n+1)*kurtosis(s) + 6)
|
||||||
|
|
||||||
|
proc `+`*(a, b: RunningStat): RunningStat =
|
||||||
|
## combine two RunningStats.
|
||||||
|
##
|
||||||
|
## Useful if performing parallel analysis of data series
|
||||||
|
## and need to re-combine parallel result sets
|
||||||
|
result.clear()
|
||||||
|
result.n = a.n + b.n
|
||||||
|
|
||||||
|
let delta = b.mom1 - a.mom1
|
||||||
|
let delta2 = delta*delta
|
||||||
|
let delta3 = delta*delta2
|
||||||
|
let delta4 = delta2*delta2
|
||||||
|
let n = toFloat(result.n)
|
||||||
|
|
||||||
|
result.mom1 = (a.n.float*a.mom1 + b.n.float*b.mom1) / n
|
||||||
|
result.mom2 = a.mom2 + b.mom2 + delta2 * a.n.float * b.n.float / n
|
||||||
|
result.mom3 = a.mom3 + b.mom3 +
|
||||||
|
delta3 * a.n.float * b.n.float * (a.n.float - b.n.float)/(n*n);
|
||||||
|
result.mom3 += 3.0*delta * (a.n.float*b.mom2 - b.n.float*a.mom2) / n
|
||||||
|
result.mom4 = a.mom4 + b.mom4 +
|
||||||
|
delta4*a.n.float*b.n.float * toFloat(a.n*a.n - a.n*b.n + b.n*b.n) /
|
||||||
|
(n*n*n)
|
||||||
|
result.mom4 += 6.0*delta2 * (a.n.float*a.n.float*b.mom2 + b.n.float*b.n.float*a.mom2) /
|
||||||
|
(n*n) +
|
||||||
|
4.0*delta*(a.n.float*b.mom3 - b.n.float*a.mom3) / n
|
||||||
|
result.max = max(a.max, b.max)
|
||||||
|
result.min = max(a.min, b.min)
|
||||||
|
|
||||||
|
proc `+=`*(a: var RunningStat, b: RunningStat) {.inline.} =
|
||||||
|
## add a second RunningStats `b` to `a`
|
||||||
|
a = a + b
|
||||||
|
# ---------------------- standalone array/seq stats ---------------------
|
||||||
|
proc mean*[T](x: openArray[T]): float =
|
||||||
|
## computes the mean of `x`
|
||||||
|
var rs: RunningStat
|
||||||
|
rs.push(x)
|
||||||
|
result = rs.mean()
|
||||||
|
|
||||||
|
proc variance*[T](x: openArray[T]): float =
|
||||||
|
## computes the population variance of `x`
|
||||||
|
var rs: RunningStat
|
||||||
|
rs.push(x)
|
||||||
|
result = rs.variance()
|
||||||
|
|
||||||
|
proc varianceS*[T](x: openArray[T]): float =
|
||||||
|
## computes the sample variance of `x`
|
||||||
|
var rs: RunningStat
|
||||||
|
rs.push(x)
|
||||||
|
result = rs.varianceS()
|
||||||
|
|
||||||
|
proc standardDeviation*[T](x: openArray[T]): float =
|
||||||
|
## computes the population standardDeviation of `x`
|
||||||
|
var rs: RunningStat
|
||||||
|
rs.push(x)
|
||||||
|
result = rs.standardDeviation()
|
||||||
|
|
||||||
|
proc standardDeviationS*[T](x: openArray[T]): float =
|
||||||
|
## computes the sanple standardDeviation of `x`
|
||||||
|
var rs: RunningStat
|
||||||
|
rs.push(x)
|
||||||
|
result = rs.standardDeviationS()
|
||||||
|
|
||||||
|
proc skewness*[T](x: openArray[T]): float =
|
||||||
|
## computes the population skewness of `x`
|
||||||
|
var rs: RunningStat
|
||||||
|
rs.push(x)
|
||||||
|
result = rs.skewness()
|
||||||
|
|
||||||
|
proc skewnessS*[T](x: openArray[T]): float =
|
||||||
|
## computes the sample skewness of `x`
|
||||||
|
var rs: RunningStat
|
||||||
|
rs.push(x)
|
||||||
|
result = rs.skewnessS()
|
||||||
|
|
||||||
|
proc kurtosis*[T](x: openArray[T]): float =
|
||||||
|
## computes the population kurtosis of `x`
|
||||||
|
var rs: RunningStat
|
||||||
|
rs.push(x)
|
||||||
|
result = rs.kurtosis()
|
||||||
|
|
||||||
|
proc kurtosisS*[T](x: openArray[T]): float =
|
||||||
|
## computes the sample kurtosis of `x`
|
||||||
|
var rs: RunningStat
|
||||||
|
rs.push(x)
|
||||||
|
result = rs.kurtosisS()
|
||||||
|
|
||||||
|
# ---------------------- Running Regression -----------------------------
|
||||||
|
|
||||||
|
proc clear*(r: var RunningRegress) =
|
||||||
|
## reset `r`
|
||||||
|
r.x_stats.clear()
|
||||||
|
r.y_stats.clear()
|
||||||
|
r.s_xy = 0.0
|
||||||
|
r.n = 0
|
||||||
|
|
||||||
|
proc push*(r: var RunningRegress, x, y: float) =
|
||||||
|
## pushes two values `x` and `y` for processing
|
||||||
|
r.s_xy += (r.x_stats.mean() - x)*(r.y_stats.mean() - y)*
|
||||||
|
toFloat(r.n) / toFloat(r.n + 1)
|
||||||
|
r.x_stats.push(x)
|
||||||
|
r.y_stats.push(y)
|
||||||
|
inc(r.n)
|
||||||
|
|
||||||
|
proc push*(r: var RunningRegress, x, y: int) {.inline.} =
|
||||||
|
## pushes two values `x` and `y` for processing.
|
||||||
|
##
|
||||||
|
## `x` and `y` are converted to ``float``
|
||||||
|
## and the other push operation is called.
|
||||||
|
r.push(toFloat(x), toFloat(y))
|
||||||
|
|
||||||
|
proc push*(r: var RunningRegress, x, y: openarray[float|int]) =
|
||||||
|
## pushes two sets of values `x` and `y` for processing.
|
||||||
|
assert(x.len == y.len)
|
||||||
|
for i in 0..<x.len:
|
||||||
|
r.push(x[i], y[i])
|
||||||
|
|
||||||
|
proc slope*(r: RunningRegress): float =
|
||||||
|
## computes the current slope of `r`
|
||||||
|
let s_xx = r.x_stats.varianceS()*toFloat(r.n - 1)
|
||||||
|
result = r.s_xy / s_xx
|
||||||
|
|
||||||
|
proc intercept*(r: RunningRegress): float =
|
||||||
|
## computes the current intercept of `r`
|
||||||
|
result = r.y_stats.mean() - r.slope()*r.x_stats.mean()
|
||||||
|
|
||||||
|
proc correlation*(r: RunningRegress): float =
|
||||||
|
## computes the current correlation of the two data
|
||||||
|
## sets pushed into `r`
|
||||||
|
let t = r.x_stats.standardDeviation() * r.y_stats.standardDeviation()
|
||||||
|
result = r.s_xy / ( toFloat(r.n) * t )
|
||||||
|
|
||||||
|
proc `+`*(a, b: RunningRegress): RunningRegress =
|
||||||
|
## combine two `RunningRegress` objects.
|
||||||
|
##
|
||||||
|
## Useful if performing parallel analysis of data series
|
||||||
|
## and need to re-combine parallel result sets
|
||||||
|
result.clear()
|
||||||
|
result.x_stats = a.x_stats + b.x_stats
|
||||||
|
result.y_stats = a.y_stats + b.y_stats
|
||||||
|
result.n = a.n + b.n
|
||||||
|
|
||||||
|
let delta_x = b.x_stats.mean() - a.x_stats.mean()
|
||||||
|
let delta_y = b.y_stats.mean() - a.y_stats.mean()
|
||||||
|
result.s_xy = a.s_xy + b.s_xy +
|
||||||
|
toFloat(a.n*b.n)*delta_x*delta_y/toFloat(result.n)
|
||||||
|
|
||||||
|
proc `+=`*(a: var RunningRegress, b: RunningRegress) =
|
||||||
|
## add RunningRegress `b` to `a`
|
||||||
|
a = a + b
|
||||||
|
|
||||||
|
{.pop.}
|
||||||
|
{.pop.}
|
||||||
|
|
||||||
|
when isMainModule:
|
||||||
|
proc clean(x: float): float =
|
||||||
|
result = round(1.0e8*x).float * 1.0e-8
|
||||||
|
|
||||||
|
var rs: RunningStat
|
||||||
|
rs.push(@[1.0, 2.0, 1.0, 4.0, 1.0, 4.0, 1.0, 2.0])
|
||||||
|
doAssert(rs.n == 8)
|
||||||
|
doAssert(clean(rs.mean) == 2.0)
|
||||||
|
doAssert(clean(rs.variance()) == 1.5)
|
||||||
|
doAssert(clean(rs.varianceS()) == 1.71428571)
|
||||||
|
doAssert(clean(rs.skewness()) == 0.81649658)
|
||||||
|
doAssert(clean(rs.skewnessS()) == 1.01835015)
|
||||||
|
doAssert(clean(rs.kurtosis()) == -1.0)
|
||||||
|
doAssert(clean(rs.kurtosisS()) == -0.7000000000000001)
|
||||||
|
|
||||||
|
var rs1, rs2: RunningStat
|
||||||
|
rs1.push(@[1.0, 2.0, 1.0, 4.0])
|
||||||
|
rs2.push(@[1.0, 4.0, 1.0, 2.0])
|
||||||
|
let rs3 = rs1 + rs2
|
||||||
|
doAssert(clean(rs3.mom2) == clean(rs.mom2))
|
||||||
|
doAssert(clean(rs3.mom3) == clean(rs.mom3))
|
||||||
|
doAssert(clean(rs3.mom4) == clean(rs.mom4))
|
||||||
|
rs1 += rs2
|
||||||
|
doAssert(clean(rs1.mom2) == clean(rs.mom2))
|
||||||
|
doAssert(clean(rs1.mom3) == clean(rs.mom3))
|
||||||
|
doAssert(clean(rs1.mom4) == clean(rs.mom4))
|
||||||
|
rs1.clear()
|
||||||
|
rs1.push(@[1.0, 2.2, 1.4, 4.9])
|
||||||
|
doAssert(rs1.sum == 9.5)
|
||||||
|
doAssert(rs1.mean() == 2.375)
|
||||||
|
|
||||||
|
var rr: RunningRegress
|
||||||
|
rr.push(@[0.0,1.0,2.8,3.0,4.0], @[0.0,1.0,2.3,3.0,4.0])
|
||||||
|
doAssert(rr.slope() == 0.9695585996955861)
|
||||||
|
doAssert(rr.intercept() == -0.03424657534246611)
|
||||||
|
doAssert(rr.correlation() == 0.9905100362239381)
|
||||||
|
var rr1, rr2: RunningRegress
|
||||||
|
rr1.push(@[0.0,1.0], @[0.0,1.0])
|
||||||
|
rr2.push(@[2.8,3.0,4.0], @[2.3,3.0,4.0])
|
||||||
|
let rr3 = rr1 + rr2
|
||||||
|
doAssert(rr3.correlation() == rr.correlation())
|
||||||
|
doAssert(clean(rr3.slope()) == clean(rr.slope()))
|
||||||
|
doAssert(clean(rr3.intercept()) == clean(rr.intercept()))
|
||||||
|
|
@ -1210,22 +1210,21 @@ proc unescape*(s: string, prefix = "\"", suffix = "\""): string {.noSideEffect,
|
||||||
## If `s` does not begin with ``prefix`` and end with ``suffix`` a
|
## If `s` does not begin with ``prefix`` and end with ``suffix`` a
|
||||||
## ValueError exception will be raised.
|
## ValueError exception will be raised.
|
||||||
result = newStringOfCap(s.len)
|
result = newStringOfCap(s.len)
|
||||||
var i = 0
|
var i = prefix.len
|
||||||
if not s.startsWith(prefix):
|
if not s.startsWith(prefix):
|
||||||
raise newException(ValueError,
|
raise newException(ValueError,
|
||||||
"String does not start with a prefix of: " & prefix)
|
"String does not start with a prefix of: " & prefix)
|
||||||
inc(i)
|
|
||||||
while true:
|
while true:
|
||||||
if i == s.len-suffix.len: break
|
if i == s.len-suffix.len: break
|
||||||
case s[i]
|
case s[i]
|
||||||
of '\\':
|
of '\\':
|
||||||
case s[i+1]:
|
case s[i+1]:
|
||||||
of 'x':
|
of 'x':
|
||||||
inc i
|
inc i, 2
|
||||||
var c: int
|
var c: int
|
||||||
i += parseutils.parseHex(s, c, i)
|
i += parseutils.parseHex(s, c, i, maxLen=2)
|
||||||
result.add(chr(c))
|
result.add(chr(c))
|
||||||
inc(i, 2)
|
dec i, 2
|
||||||
of '\\':
|
of '\\':
|
||||||
result.add('\\')
|
result.add('\\')
|
||||||
of '\'':
|
of '\'':
|
||||||
|
|
@ -1281,7 +1280,7 @@ proc editDistance*(a, b: string): int {.noSideEffect,
|
||||||
|
|
||||||
# another special case:
|
# another special case:
|
||||||
if len1 == 1:
|
if len1 == 1:
|
||||||
for j in s..len2-1:
|
for j in s..s+len2-1:
|
||||||
if a[s] == b[j]: return len2 - 1
|
if a[s] == b[j]: return len2 - 1
|
||||||
return len2
|
return len2
|
||||||
|
|
||||||
|
|
@ -1344,8 +1343,8 @@ proc editDistance*(a, b: string): int {.noSideEffect,
|
||||||
|
|
||||||
|
|
||||||
# floating point formating:
|
# floating point formating:
|
||||||
|
when not defined(js):
|
||||||
proc c_sprintf(buf, frmt: cstring): cint {.header: "<stdio.h>",
|
proc c_sprintf(buf, frmt: cstring): cint {.header: "<stdio.h>",
|
||||||
importc: "sprintf", varargs, noSideEffect.}
|
importc: "sprintf", varargs, noSideEffect.}
|
||||||
|
|
||||||
type
|
type
|
||||||
|
|
@ -1370,29 +1369,44 @@ proc formatBiggestFloat*(f: BiggestFloat, format: FloatFormatMode = ffDefault,
|
||||||
## after the decimal point for Nim's ``biggestFloat`` type.
|
## after the decimal point for Nim's ``biggestFloat`` type.
|
||||||
##
|
##
|
||||||
## If ``precision == 0``, it tries to format it nicely.
|
## If ``precision == 0``, it tries to format it nicely.
|
||||||
const floatFormatToChar: array[FloatFormatMode, char] = ['g', 'f', 'e']
|
when defined(js):
|
||||||
var
|
var res: cstring
|
||||||
frmtstr {.noinit.}: array[0..5, char]
|
case format
|
||||||
buf {.noinit.}: array[0..2500, char]
|
of ffDefault:
|
||||||
L: cint
|
{.emit: "`res` = `f`.toString();".}
|
||||||
frmtstr[0] = '%'
|
of ffDecimal:
|
||||||
if precision > 0:
|
{.emit: "`res` = `f`.toFixed(`precision`);".}
|
||||||
frmtstr[1] = '#'
|
of ffScientific:
|
||||||
frmtstr[2] = '.'
|
{.emit: "`res` = `f`.toExponential(`precision`);".}
|
||||||
frmtstr[3] = '*'
|
result = $res
|
||||||
frmtstr[4] = floatFormatToChar[format]
|
for i in 0 ..< result.len:
|
||||||
frmtstr[5] = '\0'
|
# Depending on the locale either dot or comma is produced,
|
||||||
L = c_sprintf(buf, frmtstr, precision, f)
|
# but nothing else is possible:
|
||||||
|
if result[i] in {'.', ','}: result[i] = decimalsep
|
||||||
else:
|
else:
|
||||||
frmtstr[1] = floatFormatToChar[format]
|
const floatFormatToChar: array[FloatFormatMode, char] = ['g', 'f', 'e']
|
||||||
frmtstr[2] = '\0'
|
var
|
||||||
L = c_sprintf(buf, frmtstr, f)
|
frmtstr {.noinit.}: array[0..5, char]
|
||||||
result = newString(L)
|
buf {.noinit.}: array[0..2500, char]
|
||||||
for i in 0 ..< L:
|
L: cint
|
||||||
# Depending on the locale either dot or comma is produced,
|
frmtstr[0] = '%'
|
||||||
# but nothing else is possible:
|
if precision > 0:
|
||||||
if buf[i] in {'.', ','}: result[i] = decimalsep
|
frmtstr[1] = '#'
|
||||||
else: result[i] = buf[i]
|
frmtstr[2] = '.'
|
||||||
|
frmtstr[3] = '*'
|
||||||
|
frmtstr[4] = floatFormatToChar[format]
|
||||||
|
frmtstr[5] = '\0'
|
||||||
|
L = c_sprintf(buf, frmtstr, precision, f)
|
||||||
|
else:
|
||||||
|
frmtstr[1] = floatFormatToChar[format]
|
||||||
|
frmtstr[2] = '\0'
|
||||||
|
L = c_sprintf(buf, frmtstr, f)
|
||||||
|
result = newString(L)
|
||||||
|
for i in 0 ..< L:
|
||||||
|
# Depending on the locale either dot or comma is produced,
|
||||||
|
# but nothing else is possible:
|
||||||
|
if buf[i] in {'.', ','}: result[i] = decimalsep
|
||||||
|
else: result[i] = buf[i]
|
||||||
|
|
||||||
proc formatFloat*(f: float, format: FloatFormatMode = ffDefault,
|
proc formatFloat*(f: float, format: FloatFormatMode = ffDefault,
|
||||||
precision: range[0..32] = 16; decimalSep = '.'): string {.
|
precision: range[0..32] = 16; decimalSep = '.'): string {.
|
||||||
|
|
@ -1706,3 +1720,4 @@ when isMainModule:
|
||||||
doAssert isUpper("ABC")
|
doAssert isUpper("ABC")
|
||||||
doAssert(not isUpper("AAcc"))
|
doAssert(not isUpper("AAcc"))
|
||||||
doAssert(not isUpper("A#$"))
|
doAssert(not isUpper("A#$"))
|
||||||
|
doAssert(unescape(r"\x013", "", "") == "\x013")
|
||||||
|
|
|
||||||
|
|
@ -29,7 +29,7 @@
|
||||||
## echo "epochTime() float value: ", epochTime()
|
## echo "epochTime() float value: ", epochTime()
|
||||||
## echo "getTime() float value: ", toSeconds(getTime())
|
## echo "getTime() float value: ", toSeconds(getTime())
|
||||||
## echo "cpuTime() float value: ", cpuTime()
|
## echo "cpuTime() float value: ", cpuTime()
|
||||||
## echo "An hour from now : ", getLocalTime(getTime()) + initInterval(0,0,0,1)
|
## echo "An hour from now : ", getLocalTime(getTime()) + 1.hours
|
||||||
## echo "An hour from (UTC) now: ", getGmTime(getTime()) + initInterval(0,0,0,1)
|
## echo "An hour from (UTC) now: ", getGmTime(getTime()) + initInterval(0,0,0,1)
|
||||||
|
|
||||||
{.push debugger:off.} # the user does not want to trace a part
|
{.push debugger:off.} # the user does not want to trace a part
|
||||||
|
|
@ -171,11 +171,6 @@ type
|
||||||
{.deprecated: [TMonth: Month, TWeekDay: WeekDay, TTime: Time,
|
{.deprecated: [TMonth: Month, TWeekDay: WeekDay, TTime: Time,
|
||||||
TTimeInterval: TimeInterval, TTimeInfo: TimeInfo].}
|
TTimeInterval: TimeInterval, TTimeInfo: TimeInfo].}
|
||||||
|
|
||||||
proc miliseconds*(t: TimeInterval): int {.deprecated.} = t.milliseconds
|
|
||||||
|
|
||||||
proc `miliseconds=`*(t:var TimeInterval, milliseconds: int) {.deprecated.} =
|
|
||||||
t.milliseconds = milliseconds
|
|
||||||
|
|
||||||
proc getTime*(): Time {.tags: [TimeEffect], benign.}
|
proc getTime*(): Time {.tags: [TimeEffect], benign.}
|
||||||
## gets the current calendar time as a UNIX epoch value (number of seconds
|
## gets the current calendar time as a UNIX epoch value (number of seconds
|
||||||
## elapsed since 1970) with integer precission. Use epochTime for higher
|
## elapsed since 1970) with integer precission. Use epochTime for higher
|
||||||
|
|
@ -245,13 +240,59 @@ proc getStartMilsecs*(): int {.deprecated, tags: [TimeEffect], benign.}
|
||||||
proc initInterval*(milliseconds, seconds, minutes, hours, days, months,
|
proc initInterval*(milliseconds, seconds, minutes, hours, days, months,
|
||||||
years: int = 0): TimeInterval =
|
years: int = 0): TimeInterval =
|
||||||
## creates a new ``TimeInterval``.
|
## creates a new ``TimeInterval``.
|
||||||
result.milliseconds = milliseconds
|
##
|
||||||
result.seconds = seconds
|
## You can also use the convenience procedures called ``milliseconds``,
|
||||||
result.minutes = minutes
|
## ``seconds``, ``minutes``, ``hours``, ``days``, ``months``, and ``years``.
|
||||||
result.hours = hours
|
##
|
||||||
result.days = days
|
## Example:
|
||||||
result.months = months
|
##
|
||||||
result.years = years
|
## .. code-block:: nim
|
||||||
|
##
|
||||||
|
## let day = initInterval(hours=24)
|
||||||
|
## let tomorrow = getTime() + day
|
||||||
|
## echo(tomorrow)
|
||||||
|
var carryO = 0
|
||||||
|
result.milliseconds = `mod`(milliseconds, 1000)
|
||||||
|
carryO = `div`(milliseconds, 1000)
|
||||||
|
result.seconds = `mod`(carryO + seconds, 60)
|
||||||
|
carryO = `div`(seconds, 60)
|
||||||
|
result.minutes = `mod`(carryO + minutes, 60)
|
||||||
|
carryO = `div`(minutes, 60)
|
||||||
|
result.hours = `mod`(carryO + hours, 24)
|
||||||
|
carryO = `div`(hours, 24)
|
||||||
|
result.days = carryO + days
|
||||||
|
carryO = 0
|
||||||
|
result.months = `mod`(months, 12)
|
||||||
|
carryO = `div`(months, 12)
|
||||||
|
result.years = carryO + years
|
||||||
|
|
||||||
|
proc `+`*(ti1, ti2: TimeInterval): TimeInterval =
|
||||||
|
## Adds two ``TimeInterval`` objects together.
|
||||||
|
var carryO = 0
|
||||||
|
result.milliseconds = `mod`(ti1.milliseconds + ti2.milliseconds, 1000)
|
||||||
|
carryO = `div`(ti1.milliseconds + ti2.milliseconds, 1000)
|
||||||
|
result.seconds = `mod`(carryO + ti1.seconds + ti2.seconds, 60)
|
||||||
|
carryO = `div`(ti1.seconds + ti2.seconds, 60)
|
||||||
|
result.minutes = `mod`(carryO + ti1.minutes + ti2.minutes, 60)
|
||||||
|
carryO = `div`(ti1.minutes + ti2.minutes, 60)
|
||||||
|
result.hours = `mod`(carryO + ti1.hours + ti2.hours, 24)
|
||||||
|
carryO = `div`(ti1.hours + ti2.hours, 24)
|
||||||
|
result.days = carryO + ti1.days + ti2.days
|
||||||
|
carryO = 0
|
||||||
|
result.months = `mod`(ti1.months + ti2.months, 12)
|
||||||
|
carryO = `div`(ti1.months + ti2.months, 12)
|
||||||
|
result.years = carryO + ti1.years + ti2.years
|
||||||
|
|
||||||
|
proc `-`*(ti1, ti2: TimeInterval): TimeInterval =
|
||||||
|
## Subtracts TimeInterval ``ti1`` from ``ti2``.
|
||||||
|
result = ti1
|
||||||
|
result.milliseconds -= ti2.milliseconds
|
||||||
|
result.seconds -= ti2.seconds
|
||||||
|
result.minutes -= ti2.minutes
|
||||||
|
result.hours -= ti2.hours
|
||||||
|
result.days -= ti2.days
|
||||||
|
result.months -= ti2.months
|
||||||
|
result.years -= ti2.years
|
||||||
|
|
||||||
proc isLeapYear*(year: int): bool =
|
proc isLeapYear*(year: int): bool =
|
||||||
## returns true if ``year`` is a leap year
|
## returns true if ``year`` is a leap year
|
||||||
|
|
@ -288,13 +329,22 @@ proc toSeconds(a: TimeInfo, interval: TimeInterval): float =
|
||||||
|
|
||||||
newinterv.months += interval.years * 12
|
newinterv.months += interval.years * 12
|
||||||
var curMonth = anew.month
|
var curMonth = anew.month
|
||||||
for mth in 1 .. newinterv.months:
|
if newinterv.months < 0: # subtracting
|
||||||
result += float(getDaysInMonth(curMonth, anew.year) * 24 * 60 * 60)
|
for mth in countDown(-1 * newinterv.months, 1):
|
||||||
if curMonth == mDec:
|
result -= float(getDaysInMonth(curMonth, anew.year) * 24 * 60 * 60)
|
||||||
curMonth = mJan
|
if curMonth == mJan:
|
||||||
anew.year.inc()
|
curMonth = mDec
|
||||||
else:
|
anew.year.dec()
|
||||||
curMonth.inc()
|
else:
|
||||||
|
curMonth.dec()
|
||||||
|
else: # adding
|
||||||
|
for mth in 1 .. newinterv.months:
|
||||||
|
result += float(getDaysInMonth(curMonth, anew.year) * 24 * 60 * 60)
|
||||||
|
if curMonth == mDec:
|
||||||
|
curMonth = mJan
|
||||||
|
anew.year.inc()
|
||||||
|
else:
|
||||||
|
curMonth.inc()
|
||||||
result += float(newinterv.days * 24 * 60 * 60)
|
result += float(newinterv.days * 24 * 60 * 60)
|
||||||
result += float(newinterv.hours * 60 * 60)
|
result += float(newinterv.hours * 60 * 60)
|
||||||
result += float(newinterv.minutes * 60)
|
result += float(newinterv.minutes * 60)
|
||||||
|
|
@ -302,28 +352,39 @@ proc toSeconds(a: TimeInfo, interval: TimeInterval): float =
|
||||||
result += newinterv.milliseconds / 1000
|
result += newinterv.milliseconds / 1000
|
||||||
|
|
||||||
proc `+`*(a: TimeInfo, interval: TimeInterval): TimeInfo =
|
proc `+`*(a: TimeInfo, interval: TimeInterval): TimeInfo =
|
||||||
## adds ``interval`` time.
|
## adds ``interval`` time from TimeInfo ``a``.
|
||||||
##
|
##
|
||||||
## **Note:** This has been only briefly tested and it may not be
|
## **Note:** This has been only briefly tested and it may not be
|
||||||
## very accurate.
|
## very accurate.
|
||||||
let t = toSeconds(timeInfoToTime(a))
|
let t = toSeconds(timeInfoToTime(a))
|
||||||
let secs = toSeconds(a, interval)
|
let secs = toSeconds(a, interval)
|
||||||
#if a.tzname == "UTC":
|
|
||||||
# result = getGMTime(fromSeconds(t + secs))
|
|
||||||
#else:
|
|
||||||
result = getLocalTime(fromSeconds(t + secs))
|
result = getLocalTime(fromSeconds(t + secs))
|
||||||
|
|
||||||
proc `-`*(a: TimeInfo, interval: TimeInterval): TimeInfo =
|
proc `-`*(a: TimeInfo, interval: TimeInterval): TimeInfo =
|
||||||
## subtracts ``interval`` time.
|
## subtracts ``interval`` time from TimeInfo ``a``.
|
||||||
##
|
##
|
||||||
## **Note:** This has been only briefly tested, it is inaccurate especially
|
## **Note:** This has been only briefly tested, it is inaccurate especially
|
||||||
## when you subtract so much that you reach the Julian calendar.
|
## when you subtract so much that you reach the Julian calendar.
|
||||||
let t = toSeconds(timeInfoToTime(a))
|
let t = toSeconds(timeInfoToTime(a))
|
||||||
let secs = toSeconds(a, interval)
|
var intval: TimeInterval
|
||||||
#if a.tzname == "UTC":
|
intval.milliseconds = - interval.milliseconds
|
||||||
# result = getGMTime(fromSeconds(t - secs))
|
intval.seconds = - interval.seconds
|
||||||
#else:
|
intval.minutes = - interval.minutes
|
||||||
result = getLocalTime(fromSeconds(t - secs))
|
intval.hours = - interval.hours
|
||||||
|
intval.days = - interval.days
|
||||||
|
intval.months = - interval.months
|
||||||
|
intval.years = - interval.years
|
||||||
|
let secs = toSeconds(a, intval)
|
||||||
|
result = getLocalTime(fromSeconds(t + secs))
|
||||||
|
|
||||||
|
proc miliseconds*(t: TimeInterval): int {.deprecated.} = t.milliseconds
|
||||||
|
|
||||||
|
proc `miliseconds=`*(t: var TimeInterval, milliseconds: int) {.deprecated.} =
|
||||||
|
## An alias for a misspelled field in ``TimeInterval``.
|
||||||
|
##
|
||||||
|
## **Warning:** This should not be used! It will be removed in the next
|
||||||
|
## version.
|
||||||
|
t.milliseconds = milliseconds
|
||||||
|
|
||||||
when not defined(JS):
|
when not defined(JS):
|
||||||
proc epochTime*(): float {.rtl, extern: "nt$1", tags: [TimeEffect].}
|
proc epochTime*(): float {.rtl, extern: "nt$1", tags: [TimeEffect].}
|
||||||
|
|
@ -603,6 +664,69 @@ proc `$`*(m: Month): string =
|
||||||
"November", "December"]
|
"November", "December"]
|
||||||
return lookup[m]
|
return lookup[m]
|
||||||
|
|
||||||
|
proc milliseconds*(ms: int): TimeInterval {.inline.} =
|
||||||
|
## TimeInterval of `ms` milliseconds
|
||||||
|
##
|
||||||
|
## Note: not all time functions have millisecond resolution
|
||||||
|
initInterval(`mod`(ms,1000), `div`(ms,1000))
|
||||||
|
|
||||||
|
proc seconds*(s: int): TimeInterval {.inline.} =
|
||||||
|
## TimeInterval of `s` seconds
|
||||||
|
##
|
||||||
|
## ``echo getTime() + 5.second``
|
||||||
|
initInterval(0,`mod`(s,60), `div`(s,60))
|
||||||
|
|
||||||
|
proc minutes*(m: int): TimeInterval {.inline.} =
|
||||||
|
## TimeInterval of `m` minutes
|
||||||
|
##
|
||||||
|
## ``echo getTime() + 5.minutes``
|
||||||
|
initInterval(0,0,`mod`(m,60), `div`(m,60))
|
||||||
|
|
||||||
|
proc hours*(h: int): TimeInterval {.inline.} =
|
||||||
|
## TimeInterval of `h` hours
|
||||||
|
##
|
||||||
|
## ``echo getTime() + 2.hours``
|
||||||
|
initInterval(0,0,0,`mod`(h,24),`div`(h,24))
|
||||||
|
|
||||||
|
proc days*(d: int): TimeInterval {.inline.} =
|
||||||
|
## TimeInterval of `d` days
|
||||||
|
##
|
||||||
|
## ``echo getTime() + 2.days``
|
||||||
|
initInterval(0,0,0,0,d)
|
||||||
|
|
||||||
|
proc months*(m: int): TimeInterval {.inline.} =
|
||||||
|
## TimeInterval of `m` months
|
||||||
|
##
|
||||||
|
## ``echo getTime() + 2.months``
|
||||||
|
initInterval(0,0,0,0,0,`mod`(m,12),`div`(m,12))
|
||||||
|
|
||||||
|
proc years*(y: int): TimeInterval {.inline.} =
|
||||||
|
## TimeInterval of `y` years
|
||||||
|
##
|
||||||
|
## ``echo getTime() + 2.years``
|
||||||
|
initInterval(0,0,0,0,0,0,y)
|
||||||
|
|
||||||
|
proc `+=`*(t: var Time, ti: TimeInterval) =
|
||||||
|
## modifies `t` by adding the interval `ti`
|
||||||
|
t = timeInfoToTime(getLocalTime(t) + ti)
|
||||||
|
|
||||||
|
proc `+`*(t: Time, ti: TimeInterval): Time =
|
||||||
|
## adds the interval `ti` to Time `t`
|
||||||
|
## by converting to localTime, adding the interval, and converting back
|
||||||
|
##
|
||||||
|
## ``echo getTime() + 1.day``
|
||||||
|
result = timeInfoToTime(getLocalTime(t) + ti)
|
||||||
|
|
||||||
|
proc `-=`*(t: var Time, ti: TimeInterval) =
|
||||||
|
## modifies `t` by subtracting the interval `ti`
|
||||||
|
t = timeInfoToTime(getLocalTime(t) - ti)
|
||||||
|
|
||||||
|
proc `-`*(t: Time, ti: TimeInterval): Time =
|
||||||
|
## adds the interval `ti` to Time `t`
|
||||||
|
##
|
||||||
|
## ``echo getTime() - 1.day``
|
||||||
|
result = timeInfoToTime(getLocalTime(t) - ti)
|
||||||
|
|
||||||
proc formatToken(info: TimeInfo, token: string, buf: var string) =
|
proc formatToken(info: TimeInfo, token: string, buf: var string) =
|
||||||
## Helper of the format proc to parse individual tokens.
|
## Helper of the format proc to parse individual tokens.
|
||||||
##
|
##
|
||||||
|
|
@ -1192,112 +1316,10 @@ proc timeToTimeInterval*(t: Time): TimeInterval =
|
||||||
# Milliseconds not available from Time
|
# Milliseconds not available from Time
|
||||||
|
|
||||||
when isMainModule:
|
when isMainModule:
|
||||||
# $ date --date='@2147483647'
|
# this is testing non-exported function
|
||||||
# Tue 19 Jan 03:14:07 GMT 2038
|
var
|
||||||
|
t4 = getGMTime(fromSeconds(876124714)) # Mon 6 Oct 08:58:34 BST 1997
|
||||||
var t = getGMTime(fromSeconds(2147483647))
|
t4L = getLocalTime(fromSeconds(876124714))
|
||||||
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"
|
|
||||||
|
|
||||||
assert t.format("d dd ddd dddd h hh H HH m mm M MM MMM MMMM s" &
|
|
||||||
" ss t tt y yy yyy yyyy yyyyy z zz zzz ZZZ") ==
|
|
||||||
"19 19 Tue Tuesday 3 03 3 03 14 14 1 01 Jan January 7 07 A AM 8 38 038 2038 02038 0 00 00:00 UTC"
|
|
||||||
|
|
||||||
assert t.format("yyyyMMddhhmmss") == "20380119031407"
|
|
||||||
|
|
||||||
var t2 = getGMTime(fromSeconds(160070789)) # Mon 27 Jan 16:06:29 GMT 1975
|
|
||||||
assert t2.format("d dd ddd dddd h hh H HH m mm M MM MMM MMMM s" &
|
|
||||||
" ss t tt y yy yyy yyyy yyyyy z zz zzz ZZZ") ==
|
|
||||||
"27 27 Mon Monday 4 04 16 16 6 06 1 01 Jan January 29 29 P PM 5 75 975 1975 01975 0 00 00:00 UTC"
|
|
||||||
|
|
||||||
when not defined(JS):
|
|
||||||
when sizeof(Time) == 8:
|
|
||||||
var t3 = getGMTime(fromSeconds(889067643645)) # Fri 7 Jun 19:20:45 BST 30143
|
|
||||||
assert t3.format("d dd ddd dddd h hh H HH m mm M MM MMM MMMM s" &
|
|
||||||
" ss t tt y yy yyy yyyy yyyyy z zz zzz ZZZ") ==
|
|
||||||
"7 07 Fri Friday 6 06 18 18 20 20 6 06 Jun June 45 45 P PM 3 43 143 0143 30143 0 00 00:00 UTC"
|
|
||||||
assert t3.format(":,[]()-/") == ":,[]()-/"
|
|
||||||
|
|
||||||
var t4 = getGMTime(fromSeconds(876124714)) # Mon 6 Oct 08:58:34 BST 1997
|
|
||||||
assert t4.format("M MM MMM MMMM") == "10 10 Oct October"
|
|
||||||
|
|
||||||
# Interval tests
|
|
||||||
assert((t4 - initInterval(years = 2)).format("yyyy") == "1995")
|
|
||||||
assert((t4 - initInterval(years = 7, minutes = 34, seconds = 24)).format("yyyy mm ss") == "1990 24 10")
|
|
||||||
|
|
||||||
var s = "Tuesday at 09:04am on Dec 15, 2015"
|
|
||||||
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 = "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 = "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 = "Thu Jan 12 15:04:05 -07:00 2006"
|
|
||||||
f = "ddd MMM dd HH:mm:ss zzz yyyy"
|
|
||||||
assert($s.parse(f) == "Thu Jan 12 15:04:05 2006")
|
|
||||||
# RFC822 = "02 Jan 06 15:04 MST"
|
|
||||||
s = "12 Jan 16 15:04 MST"
|
|
||||||
f = "dd MMM yy HH:mm ZZZ"
|
|
||||||
assert($s.parse(f) == "Tue Jan 12 15:04:00 2016")
|
|
||||||
# RFC822Z = "02 Jan 06 15:04 -0700" # RFC822 with numeric zone
|
|
||||||
s = "12 Jan 16 15:04 -07:00"
|
|
||||||
f = "dd MMM yy HH:mm zzz"
|
|
||||||
assert($s.parse(f) == "Tue Jan 12 15:04:00 2016")
|
|
||||||
# RFC850 = "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) == "Thu Jan 12 15:04:05 2006")
|
|
||||||
# RFC1123 = "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) == "Thu Jan 12 15:04:05 2006")
|
|
||||||
# RFC1123Z = "Mon, 02 Jan 2006 15:04:05 -0700" # RFC1123 with numeric zone
|
|
||||||
s = "Thu, 12 Jan 2006 15:04:05 -07:00"
|
|
||||||
f = "ddd, dd MMM yyyy HH:mm:ss zzz"
|
|
||||||
assert($s.parse(f) == "Thu Jan 12 15:04:05 2006")
|
|
||||||
# RFC3339 = "2006-01-02T15:04:05Z07:00"
|
|
||||||
s = "2006-01-12T15:04:05Z-07:00"
|
|
||||||
f = "yyyy-MM-ddTHH:mm:ssZzzz"
|
|
||||||
assert($s.parse(f) == "Thu Jan 12 15:04:05 2006")
|
|
||||||
f = "yyyy-MM-dd'T'HH:mm:ss'Z'zzz"
|
|
||||||
assert($s.parse(f) == "Thu Jan 12 15:04:05 2006")
|
|
||||||
# RFC3339Nano = "2006-01-02T15:04:05.999999999Z07:00"
|
|
||||||
s = "2006-01-12T15:04:05.999999999Z-07:00"
|
|
||||||
f = "yyyy-MM-ddTHH:mm:ss.999999999Zzzz"
|
|
||||||
assert($s.parse(f) == "Thu Jan 12 15:04:05 2006")
|
|
||||||
# Kitchen = "3:04PM"
|
|
||||||
s = "3:04PM"
|
|
||||||
f = "h:mmtt"
|
|
||||||
assert "15:04:00" in $s.parse(f)
|
|
||||||
when not defined(testing):
|
|
||||||
echo "Kitchen: " & $s.parse(f)
|
|
||||||
var ti = timeToTimeInfo(getTime())
|
|
||||||
echo "Todays date after decoding: ", ti
|
|
||||||
var tint = timeToTimeInterval(getTime())
|
|
||||||
echo "Todays date after decoding to interval: ", tint
|
|
||||||
# checking dayOfWeek matches known days
|
|
||||||
assert getDayOfWeek(21, 9, 1900) == dFri
|
|
||||||
assert getDayOfWeek(1, 1, 1970) == dThu
|
|
||||||
assert getDayOfWeek(21, 9, 1970) == dMon
|
|
||||||
assert getDayOfWeek(1, 1, 2000) == dSat
|
|
||||||
assert getDayOfWeek(1, 1, 2021) == dFri
|
|
||||||
# Julian tests
|
|
||||||
assert getDayOfWeekJulian(21, 9, 1900) == dFri
|
|
||||||
assert getDayOfWeekJulian(21, 9, 1970) == dMon
|
|
||||||
assert getDayOfWeekJulian(1, 1, 2000) == dSat
|
|
||||||
assert getDayOfWeekJulian(1, 1, 2021) == dFri
|
|
||||||
|
|
||||||
# toSeconds tests with GM and Local timezones
|
|
||||||
#var t4 = getGMTime(fromSeconds(876124714)) # Mon 6 Oct 08:58:34 BST 1997
|
|
||||||
var t4L = getLocalTime(fromSeconds(876124714))
|
|
||||||
assert toSeconds(timeInfoToTime(t4L)) == 876124714 # fromSeconds is effectively "localTime"
|
|
||||||
assert toSeconds(timeInfoToTime(t4L)) + t4L.timezone.float == toSeconds(timeInfoToTime(t4))
|
|
||||||
|
|
||||||
assert toSeconds(t4, initInterval(seconds=0)) == 0.0
|
assert toSeconds(t4, initInterval(seconds=0)) == 0.0
|
||||||
assert toSeconds(t4L, initInterval(milliseconds=1)) == toSeconds(t4, initInterval(milliseconds=1))
|
assert toSeconds(t4L, initInterval(milliseconds=1)) == toSeconds(t4, initInterval(milliseconds=1))
|
||||||
assert toSeconds(t4L, initInterval(seconds=1)) == toSeconds(t4, initInterval(seconds=1))
|
assert toSeconds(t4L, initInterval(seconds=1)) == toSeconds(t4, initInterval(seconds=1))
|
||||||
|
|
@ -1307,12 +1329,5 @@ when isMainModule:
|
||||||
assert toSeconds(t4L, initInterval(months=1)) == toSeconds(t4, initInterval(months=1))
|
assert toSeconds(t4L, initInterval(months=1)) == toSeconds(t4, initInterval(months=1))
|
||||||
assert toSeconds(t4L, initInterval(years=1)) == toSeconds(t4, initInterval(years=1))
|
assert toSeconds(t4L, initInterval(years=1)) == toSeconds(t4, initInterval(years=1))
|
||||||
|
|
||||||
# adding intervals
|
# Further tests are in tests/stdlib/ttime.nim
|
||||||
var
|
# koch test c stdlib
|
||||||
a1L = toSeconds(timeInfoToTime(t4L + initInterval(hours = 1))) + t4L.timezone.float
|
|
||||||
a1G = toSeconds(timeInfoToTime(t4)) + 60.0 * 60.0
|
|
||||||
assert a1L == a1G
|
|
||||||
# subtracting intervals
|
|
||||||
a1L = toSeconds(timeInfoToTime(t4L - initInterval(hours = 1))) + t4L.timezone.float
|
|
||||||
a1G = toSeconds(timeInfoToTime(t4)) - (60.0 * 60.0)
|
|
||||||
assert a1L == a1G
|
|
||||||
|
|
|
||||||
|
|
@ -114,6 +114,7 @@ proc validateUtf8*(s: string): int =
|
||||||
if ord(s[i]) <=% 127:
|
if ord(s[i]) <=% 127:
|
||||||
inc(i)
|
inc(i)
|
||||||
elif ord(s[i]) shr 5 == 0b110:
|
elif ord(s[i]) shr 5 == 0b110:
|
||||||
|
if ord(s[i]) < 0xc2: return i # Catch overlong ascii representations.
|
||||||
if i+1 < L and ord(s[i+1]) shr 6 == 0b10: inc(i, 2)
|
if i+1 < L and ord(s[i+1]) shr 6 == 0b10: inc(i, 2)
|
||||||
else: return i
|
else: return i
|
||||||
elif ord(s[i]) shr 4 == 0b1110:
|
elif ord(s[i]) shr 4 == 0b1110:
|
||||||
|
|
|
||||||
|
|
@ -96,7 +96,7 @@ proc parse(x: var XmlParser, errors: var seq[string]): XmlNode =
|
||||||
next(x)
|
next(x)
|
||||||
of xmlEntity:
|
of xmlEntity:
|
||||||
## &entity;
|
## &entity;
|
||||||
errors.add(errorMsg(x, "unknown entity: " & x.entityName))
|
result = newEntity(x.entityName)
|
||||||
next(x)
|
next(x)
|
||||||
of xmlEof: discard
|
of xmlEof: discard
|
||||||
|
|
||||||
|
|
@ -143,17 +143,24 @@ proc loadXml*(path: string): XmlNode =
|
||||||
result = loadXml(path, errors)
|
result = loadXml(path, errors)
|
||||||
if errors.len > 0: raiseInvalidXml(errors)
|
if errors.len > 0: raiseInvalidXml(errors)
|
||||||
|
|
||||||
when not defined(testing) and isMainModule:
|
when isMainModule:
|
||||||
import os
|
when not defined(testing):
|
||||||
|
import os
|
||||||
|
|
||||||
var errors: seq[string] = @[]
|
var errors: seq[string] = @[]
|
||||||
var x = loadXml(paramStr(1), errors)
|
var x = loadXml(paramStr(1), errors)
|
||||||
for e in items(errors): echo e
|
for e in items(errors): echo e
|
||||||
|
|
||||||
var f: File
|
var f: File
|
||||||
if open(f, "xmltest.txt", fmWrite):
|
if open(f, "xmltest.txt", fmWrite):
|
||||||
f.write($x)
|
f.write($x)
|
||||||
f.close()
|
f.close()
|
||||||
|
else:
|
||||||
|
quit("cannot write test.txt")
|
||||||
else:
|
else:
|
||||||
quit("cannot write test.txt")
|
block: # correctly parse ../../tests/testdata/doc1.xml
|
||||||
|
let filePath = "tests/testdata/doc1.xml"
|
||||||
|
var errors: seq[string] = @[]
|
||||||
|
var xml = loadXml(filePath, errors)
|
||||||
|
assert(errors.len == 0, "The file tests/testdata/doc1.xml should be parsed without errors.")
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
[Package]
|
[Package]
|
||||||
name = "stdlib"
|
name = "stdlib"
|
||||||
version = "0.9.0"
|
version = "0.13.0"
|
||||||
author = "Dominik Picheta"
|
author = "Dominik Picheta"
|
||||||
description = "Nim's standard library."
|
description = "Nim's standard library."
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
|
|
|
||||||
|
|
@ -232,8 +232,8 @@ proc low*[T](x: T): T {.magic: "Low", noSideEffect.}
|
||||||
##
|
##
|
||||||
## .. code-block:: nim
|
## .. code-block:: nim
|
||||||
## var arr = [1,2,3,4,5,6,7]
|
## var arr = [1,2,3,4,5,6,7]
|
||||||
## high(arr) #=> 0
|
## low(arr) #=> 0
|
||||||
## high(2) #=> -9223372036854775808
|
## low(2) #=> -9223372036854775808
|
||||||
|
|
||||||
type
|
type
|
||||||
range*{.magic: "Range".}[T] ## Generic type to construct range types.
|
range*{.magic: "Range".}[T] ## Generic type to construct range types.
|
||||||
|
|
@ -840,7 +840,7 @@ proc `div` *(x, y: int32): int32 {.magic: "DivI", noSideEffect.}
|
||||||
## 1 div 2 == 0
|
## 1 div 2 == 0
|
||||||
## 2 div 2 == 1
|
## 2 div 2 == 1
|
||||||
## 3 div 2 == 1
|
## 3 div 2 == 1
|
||||||
## 7 div 5 == 2
|
## 7 div 5 == 1
|
||||||
|
|
||||||
when defined(nimnomagic64):
|
when defined(nimnomagic64):
|
||||||
proc `div` *(x, y: int64): int64 {.magic: "DivI", noSideEffect.}
|
proc `div` *(x, y: int64): int64 {.magic: "DivI", noSideEffect.}
|
||||||
|
|
@ -1808,10 +1808,10 @@ const
|
||||||
NimMajor*: int = 0
|
NimMajor*: int = 0
|
||||||
## is the major number of Nim's version.
|
## is the major number of Nim's version.
|
||||||
|
|
||||||
NimMinor*: int = 12
|
NimMinor*: int = 13
|
||||||
## is the minor number of Nim's version.
|
## is the minor number of Nim's version.
|
||||||
|
|
||||||
NimPatch*: int = 1
|
NimPatch*: int = 0
|
||||||
## is the patch number of Nim's version.
|
## is the patch number of Nim's version.
|
||||||
|
|
||||||
NimVersion*: string = $NimMajor & "." & $NimMinor & "." & $NimPatch
|
NimVersion*: string = $NimMajor & "." & $NimMinor & "." & $NimPatch
|
||||||
|
|
@ -2584,11 +2584,7 @@ when not defined(JS): #and not defined(nimscript):
|
||||||
|
|
||||||
when hasAlloc:
|
when hasAlloc:
|
||||||
var
|
var
|
||||||
strDesc: TNimType
|
strDesc = TNimType(size: sizeof(string), kind: tyString, flags: {ntfAcyclic})
|
||||||
|
|
||||||
strDesc.size = sizeof(string)
|
|
||||||
strDesc.kind = tyString
|
|
||||||
strDesc.flags = {ntfAcyclic}
|
|
||||||
|
|
||||||
when not defined(nimscript):
|
when not defined(nimscript):
|
||||||
include "system/ansi_c"
|
include "system/ansi_c"
|
||||||
|
|
|
||||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue