Merge branch 'devel' of https://github.com/nim-lang/Nim into devel
This commit is contained in:
commit
55c1f3d30c
127 changed files with 3709 additions and 2605 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
|
||||
|
|
@ -500,8 +500,7 @@ type
|
|||
skResult, # special 'result' variable
|
||||
skProc, # a proc
|
||||
skMethod, # a method
|
||||
skIterator, # an inline iterator
|
||||
skClosureIterator, # a resumable closure iterator
|
||||
skIterator, # an iterator
|
||||
skConverter, # a type converter
|
||||
skMacro, # a macro
|
||||
skTemplate, # a template; currently also misused for user-defined
|
||||
|
|
@ -518,7 +517,7 @@ type
|
|||
TSymKinds* = set[TSymKind]
|
||||
|
||||
const
|
||||
routineKinds* = {skProc, skMethod, skIterator, skClosureIterator,
|
||||
routineKinds* = {skProc, skMethod, skIterator,
|
||||
skConverter, skMacro, skTemplate}
|
||||
tfIncompleteStruct* = tfVarargs
|
||||
tfUncheckedArray* = tfVarargs
|
||||
|
|
@ -905,7 +904,7 @@ type
|
|||
# the poor naming choices in the standard library.
|
||||
|
||||
const
|
||||
OverloadableSyms* = {skProc, skMethod, skIterator, skClosureIterator,
|
||||
OverloadableSyms* = {skProc, skMethod, skIterator,
|
||||
skConverter, skModule, skTemplate, skMacro}
|
||||
|
||||
GenericTypes*: TTypeKinds = {tyGenericInvocation, tyGenericBody,
|
||||
|
|
@ -929,11 +928,11 @@ const
|
|||
NilableTypes*: TTypeKinds = {tyPointer, tyCString, tyRef, tyPtr, tySequence,
|
||||
tyProc, tyString, tyError}
|
||||
ExportableSymKinds* = {skVar, skConst, skProc, skMethod, skType,
|
||||
skIterator, skClosureIterator,
|
||||
skIterator,
|
||||
skMacro, skTemplate, skConverter, skEnumField, skLet, skStub, skAlias}
|
||||
PersistentNodeFlags*: TNodeFlags = {nfBase2, nfBase8, nfBase16,
|
||||
nfDotSetter, nfDotField,
|
||||
nfIsRef, nfIsCursor}
|
||||
nfIsRef, nfIsCursor, nfLL}
|
||||
namePos* = 0
|
||||
patternPos* = 1 # empty except for term rewriting macros
|
||||
genericParamsPos* = 2
|
||||
|
|
@ -958,11 +957,9 @@ const
|
|||
nkStrKinds* = {nkStrLit..nkTripleStrLit}
|
||||
|
||||
skLocalVars* = {skVar, skLet, skForVar, skParam, skResult}
|
||||
skProcKinds* = {skProc, skTemplate, skMacro, skIterator, skClosureIterator,
|
||||
skProcKinds* = {skProc, skTemplate, skMacro, skIterator,
|
||||
skMethod, skConverter}
|
||||
|
||||
skIterators* = {skIterator, skClosureIterator}
|
||||
|
||||
var ggDebug* {.deprecated.}: bool ## convenience switch for trying out things
|
||||
|
||||
proc isCallExpr*(n: PNode): bool =
|
||||
|
|
@ -1558,12 +1555,13 @@ proc isGenericRoutine*(s: PSym): bool =
|
|||
else: discard
|
||||
|
||||
proc skipGenericOwner*(s: PSym): PSym =
|
||||
internalAssert s.kind in skProcKinds
|
||||
## Generic instantiations are owned by their originating generic
|
||||
## symbol. This proc skips such owners and goes straight to the owner
|
||||
## of the generic itself (the module or the enclosing proc).
|
||||
result = if sfFromGeneric in s.flags: s.owner.owner
|
||||
else: s.owner
|
||||
result = if s.kind in skProcKinds and sfFromGeneric in s.flags:
|
||||
s.owner.owner
|
||||
else:
|
||||
s.owner
|
||||
|
||||
proc originatingModule*(s: PSym): PSym =
|
||||
result = s.owner
|
||||
|
|
|
|||
|
|
@ -118,6 +118,14 @@ proc openArrayLoc(p: BProc, n: PNode): Rope =
|
|||
result = "$1->data, $1->$2" % [a.rdLoc, lenField(p)]
|
||||
of tyArray, tyArrayConstr:
|
||||
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))
|
||||
|
||||
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)
|
||||
|
||||
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)
|
||||
elif e.sons[0].kind == nkSym and sfInfixCall in e.sons[0].sym.flags:
|
||||
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)
|
||||
|
||||
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)
|
||||
elif ri.sons[0].kind == nkSym and sfInfixCall in ri.sons[0].sym.flags:
|
||||
genInfixCall(p, le, ri, d)
|
||||
|
|
|
|||
|
|
@ -1502,7 +1502,7 @@ proc genSetOp(p: BProc, e: PNode, d: var TLoc, op: TMagic) =
|
|||
else:
|
||||
case op
|
||||
of mIncl: binaryStmtInExcl(p, e, d, "$1[(NU)($2)>>3] |=(1U<<($2&7U));$n")
|
||||
of mExcl: binaryStmtInExcl(p, e, d, "$1[(NU)($2)>>3]] &= ~(1U<<($2&7U));$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 mLtSet, mLeSet:
|
||||
getTemp(p, getSysType(tyInt), i) # our counter
|
||||
|
|
@ -1838,9 +1838,9 @@ proc genClosure(p: BProc, n: PNode, d: var TLoc) =
|
|||
assert n.kind == nkClosure
|
||||
|
||||
if isConstClosure(n):
|
||||
inc(p.labels)
|
||||
var tmp = "LOC" & rope(p.labels)
|
||||
addf(p.module.s[cfsData], "NIM_CONST $1 $2 = $3;$n",
|
||||
inc(p.module.labels)
|
||||
var tmp = "CNSTCLOSURE" & rope(p.module.labels)
|
||||
addf(p.module.s[cfsData], "static NIM_CONST $1 $2 = $3;$n",
|
||||
[getTypeDesc(p.module, n.typ), tmp, genConstExpr(p, n)])
|
||||
putIntoDest(p, d, n.typ, tmp, OnStatic)
|
||||
else:
|
||||
|
|
@ -1965,7 +1965,9 @@ proc expr(p: BProc, n: PNode, d: var TLoc) =
|
|||
else:
|
||||
genProc(p.module, sym)
|
||||
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:
|
||||
localError(n.info, "request to generate code for .compileTime proc: " &
|
||||
sym.name.s)
|
||||
|
|
@ -1987,6 +1989,7 @@ proc expr(p: BProc, n: PNode, d: var TLoc) =
|
|||
if sfGlobal in sym.flags: genVarPrototype(p.module, sym)
|
||||
if sym.loc.r == nil or sym.loc.t == nil:
|
||||
#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
|
||||
if sfThread in sym.flags:
|
||||
accessThreadLocalVar(p, sym)
|
||||
|
|
@ -2004,9 +2007,9 @@ proc expr(p: BProc, n: PNode, d: var TLoc) =
|
|||
putLocIntoDest(p, d, sym.loc)
|
||||
of skParam:
|
||||
if sym.loc.r == nil or sym.loc.t == nil:
|
||||
#echo "FAILED FOR PRCO ", p.prc.name.s
|
||||
#debug p.prc.typ.n
|
||||
#echo renderTree(p.prc.ast, {renderIds})
|
||||
# echo "FAILED FOR PRCO ", p.prc.name.s
|
||||
# debug p.prc.typ.n
|
||||
# echo renderTree(p.prc.ast, {renderIds})
|
||||
internalError(n.info, "expr: param not init " & sym.name.s & "_" & $sym.id)
|
||||
putLocIntoDest(p, d, sym.loc)
|
||||
else: internalError(n.info, "expr(" & $sym.kind & "); unknown symbol")
|
||||
|
|
|
|||
|
|
@ -21,8 +21,8 @@ proc registerGcRoot(p: BProc, v: PSym) =
|
|||
# we register a specialized marked proc here; this has the advantage
|
||||
# that it works out of the box for thread local storage then :-)
|
||||
let prc = genTraverseProcForGlobal(p.module, v)
|
||||
linefmt(p.module.initProc, cpsStmts,
|
||||
"#nimRegisterGlobalMarker($1);$n", prc)
|
||||
appcg(p.module, p.module.initProc.procSec(cpsStmts),
|
||||
"#nimRegisterGlobalMarker($1);$n", [prc])
|
||||
|
||||
proc isAssignedImmediately(n: PNode): bool {.inline.} =
|
||||
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)
|
||||
of nkSym:
|
||||
var sym = t.sons[i].sym
|
||||
if sym.kind in {skProc, skIterator, skClosureIterator, skMethod}:
|
||||
if sym.kind in {skProc, skIterator, skMethod}:
|
||||
var a: TLoc
|
||||
initLocExpr(p, t.sons[i], a)
|
||||
res.add($rdLoc(a))
|
||||
|
|
|
|||
|
|
@ -594,7 +594,7 @@ proc cgsym(m: BModule, name: string): Rope =
|
|||
var sym = magicsys.getCompilerProc(name)
|
||||
if sym != nil:
|
||||
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 skType: discard getTypeDesc(m, sym.typ)
|
||||
else: internalError("cgsym: " & name & ": " & $sym.kind)
|
||||
|
|
|
|||
|
|
@ -19,10 +19,7 @@ proc genConv(n: PNode, d: PType, downcast: bool): PNode =
|
|||
if (source.kind == tyObject) and (dest.kind == tyObject):
|
||||
var diff = inheritanceDiff(dest, source)
|
||||
if diff == high(int):
|
||||
# see bug #3550 which triggers it. XXX This is a hack but I don't know yet
|
||||
# how the real fix looks like:
|
||||
localError(n.info, "there is no subtype relation between " &
|
||||
typeToString(d) & " and " & typeToString(n.typ))
|
||||
# no subtype relation, nothing to do
|
||||
result = n
|
||||
elif diff < 0:
|
||||
result = newNodeIT(nkObjUpConv, n.info, d)
|
||||
|
|
|
|||
|
|
@ -149,10 +149,10 @@ proc ropeFormatNamedVars(frmt: FormatStr, varnames: openArray[string],
|
|||
proc genComment(d: PDoc, n: PNode): string =
|
||||
result = ""
|
||||
var dummyHasToc: bool
|
||||
if n.comment != nil and startsWith(n.comment, "##"):
|
||||
if n.comment != nil:
|
||||
renderRstToOut(d[], parseRst(n.comment, toFilename(n.info),
|
||||
toLinenumber(n.info), toColumn(n.info),
|
||||
dummyHasToc, d.options + {roSkipPounds}), result)
|
||||
dummyHasToc, d.options), result)
|
||||
|
||||
proc genRecComment(d: PDoc, n: PNode): Rope =
|
||||
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) =
|
||||
const sectionNames: array[skModule..skTemplate, string] = [
|
||||
"Imports", "Types", "Vars", "Lets", "Consts", "Vars", "Procs", "Methods",
|
||||
"Iterators", "Iterators", "Converters", "Macros", "Templates"
|
||||
"Iterators", "Converters", "Macros", "Templates"
|
||||
]
|
||||
if d.section[kind] == nil: return
|
||||
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.kind == skParam and sfGenSym notin s.flags:
|
||||
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]
|
||||
else:
|
||||
internalAssert sfGenSym in s.flags
|
||||
|
|
|
|||
|
|
@ -931,7 +931,7 @@ proc isIndirect(v: PSym): bool =
|
|||
result = {sfAddrTaken, sfGlobal} * v.flags != {} and
|
||||
#(mapType(v.typ) != etyObject) and
|
||||
{sfImportc, sfVolatile, sfExportc} * v.flags == {} and
|
||||
v.kind notin {skProc, skConverter, skMethod, skIterator, skClosureIterator,
|
||||
v.kind notin {skProc, skConverter, skMethod, skIterator,
|
||||
skConst, skTemp, skLet}
|
||||
|
||||
proc genAddr(p: PProc, n: PNode, r: var TCompRes) =
|
||||
|
|
@ -1636,7 +1636,10 @@ proc gen(p: PProc, n: PNode, r: var TCompRes) =
|
|||
of nkSym:
|
||||
genSym(p, n, r)
|
||||
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
|
||||
of nkNilLit:
|
||||
if isEmptyType(n.typ):
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -769,24 +769,88 @@ proc getOperator(L: var TLexer, tok: var TToken) =
|
|||
if buf[pos] in {CR, LF, nimlexbase.EndOfFile}:
|
||||
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) =
|
||||
var pos = L.bufpos
|
||||
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
|
||||
# iNumber contains the number of '\n' in the token
|
||||
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):
|
||||
var col = getColNumber(L, pos)
|
||||
while true:
|
||||
|
|
@ -820,6 +884,12 @@ proc scanComment(L: var TLexer, tok: var TToken) =
|
|||
if doContinue():
|
||||
tok.literal.add "\n"
|
||||
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
|
||||
else:
|
||||
if buf[pos] > ' ':
|
||||
|
|
@ -843,9 +913,16 @@ proc skip(L: var TLexer, tok: var TToken) =
|
|||
pos = handleCRLF(L, pos)
|
||||
buf = L.buf
|
||||
var indent = 0
|
||||
while buf[pos] == ' ':
|
||||
inc(pos)
|
||||
inc(indent)
|
||||
while true:
|
||||
if buf[pos] == ' ':
|
||||
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
|
||||
when defined(nimfix):
|
||||
template doBreak(): expr = buf[pos] > ' '
|
||||
|
|
@ -863,8 +940,11 @@ proc skip(L: var TLexer, tok: var TToken) =
|
|||
# do not skip documentation comment:
|
||||
if buf[pos+1] == '#': break
|
||||
if buf[pos+1] == '[':
|
||||
lexMessagePos(L, warnDeprecated, pos, "use '# [' instead; '#['")
|
||||
while buf[pos] notin {CR, LF, nimlexbase.EndOfFile}: inc(pos)
|
||||
skipMultiLineComment(L, tok, pos+2, false)
|
||||
pos = L.bufpos
|
||||
buf = L.buf
|
||||
else:
|
||||
while buf[pos] notin {CR, LF, nimlexbase.EndOfFile}: inc(pos)
|
||||
else:
|
||||
break # EndOfFile also leaves the loop
|
||||
L.bufpos = pos
|
||||
|
|
|
|||
|
|
@ -133,7 +133,7 @@ type
|
|||
|
||||
proc getSymRepr*(s: PSym): string =
|
||||
case s.kind
|
||||
of skProc, skMethod, skConverter, skIterators: result = getProcHeader(s)
|
||||
of skProc, skMethod, skConverter, skIterator: result = getProcHeader(s)
|
||||
else: result = s.name.s
|
||||
|
||||
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]
|
||||
var t = deref.typ.skipTypes(abstractInst)
|
||||
var field: PSym
|
||||
let bb = getIdent(b)
|
||||
while true:
|
||||
assert t.kind == tyObject
|
||||
field = getSymFromList(t.n, getIdent(b))
|
||||
field = getSymFromList(t.n, bb)
|
||||
if field != nil: break
|
||||
t = t.sons[0]
|
||||
if t == nil: break
|
||||
|
|
@ -585,7 +586,7 @@ proc wrapProcForSpawn*(owner: PSym; spawnExpr: PNode; retType: PType;
|
|||
objType.addField(field)
|
||||
result.add newFastAsgnStmt(newDotExpr(scratchObj, field), n[0])
|
||||
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")
|
||||
elif fn.typ.callConv == ccClosure:
|
||||
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)
|
||||
if (b >= len(s) * ElemSize) or not bitSetIn(s, b): break
|
||||
dec(b)
|
||||
let aa = newIntTypeNode(nkIntLit, a + first, elemType)
|
||||
aa.info = info
|
||||
if a == b:
|
||||
addSon(result, newIntTypeNode(nkIntLit, a + first, elemType))
|
||||
addSon(result, aa)
|
||||
else:
|
||||
n = newNodeI(nkRange, info)
|
||||
n.typ = elemType
|
||||
addSon(n, newIntTypeNode(nkIntLit, a + first, elemType))
|
||||
addSon(n, newIntTypeNode(nkIntLit, b + first, elemType))
|
||||
addSon(n, aa)
|
||||
let bb = newIntTypeNode(nkIntLit, b + first, elemType)
|
||||
bb.info = info
|
||||
addSon(n, bb)
|
||||
addSon(result, n)
|
||||
e = b
|
||||
inc(e)
|
||||
|
|
|
|||
|
|
@ -112,12 +112,7 @@ proc rawSkipComment(p: var TParser, node: PNode) =
|
|||
if p.tok.tokType == tkComment:
|
||||
if node != nil:
|
||||
if node.comment == nil: node.comment = ""
|
||||
if p.tok.literal == "[]":
|
||||
node.flags.incl nfIsCursor
|
||||
#echo "parser: "
|
||||
#debug node
|
||||
else:
|
||||
add(node.comment, p.tok.literal)
|
||||
add(node.comment, p.tok.literal)
|
||||
else:
|
||||
parMessage(p, errInternal, "skipComment")
|
||||
getTok(p)
|
||||
|
|
@ -250,12 +245,14 @@ proc isUnary(p: TParser): bool =
|
|||
if p.tok.tokType in {tkOpr, tkDotDot} and
|
||||
p.tok.strongSpaceB == 0 and
|
||||
p.tok.strongSpaceA > 0:
|
||||
# XXX change this after 0.10.4 is out
|
||||
if p.strongSpaces:
|
||||
result = true
|
||||
else:
|
||||
parMessage(p, warnDeprecated,
|
||||
"will be parsed as unary operator; inconsistent spacing")
|
||||
# versions prior to 0.13.0 used to do this:
|
||||
when false:
|
||||
if p.strongSpaces:
|
||||
result = true
|
||||
else:
|
||||
parMessage(p, warnDeprecated,
|
||||
"will be parsed as unary operator; inconsistent spacing")
|
||||
|
||||
proc checkBinary(p: TParser) {.inline.} =
|
||||
## Check if the current parser token is a binary operator.
|
||||
|
|
|
|||
|
|
@ -167,33 +167,24 @@ proc makeNimString(s: string): string =
|
|||
proc putComment(g: var TSrcGen, s: string) =
|
||||
if s.isNil: return
|
||||
var i = 0
|
||||
var comIndent = 1
|
||||
var isCode = (len(s) >= 2) and (s[1] != ' ')
|
||||
var ind = g.lineLen
|
||||
var com = ""
|
||||
var com = "## "
|
||||
while true:
|
||||
case s[i]
|
||||
of '\0':
|
||||
break
|
||||
of '\x0D':
|
||||
put(g, tkComment, com)
|
||||
com = ""
|
||||
com = "## "
|
||||
inc(i)
|
||||
if s[i] == '\x0A': inc(i)
|
||||
optNL(g, ind)
|
||||
of '\x0A':
|
||||
put(g, tkComment, com)
|
||||
com = ""
|
||||
com = "## "
|
||||
inc(i)
|
||||
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':
|
||||
add(com, s[i])
|
||||
inc(i)
|
||||
|
|
@ -206,7 +197,7 @@ proc putComment(g: var TSrcGen, s: string) =
|
|||
if not isCode and (g.lineLen + (j - i) > MaxLineLen):
|
||||
put(g, tkComment, com)
|
||||
optNL(g, ind)
|
||||
com = '#' & spaces(comIndent)
|
||||
com = "## "
|
||||
while s[i] > ' ':
|
||||
add(com, s[i])
|
||||
inc(i)
|
||||
|
|
@ -283,7 +274,7 @@ proc shouldRenderComment(g: var TSrcGen, n: PNode): bool =
|
|||
result = false
|
||||
if n.comment != nil:
|
||||
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) =
|
||||
assert(n != nil)
|
||||
|
|
@ -1330,6 +1321,8 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) =
|
|||
initContext c
|
||||
putWithSpace g, tkSymbol, if n.kind == nkState: "state" else: "goto"
|
||||
gsons(g, n, c)
|
||||
of nkBreakState:
|
||||
put(g, tkTuple, "breakstate")
|
||||
of nkTypeClassTy:
|
||||
gTypeClassTy(g, n)
|
||||
else:
|
||||
|
|
|
|||
|
|
@ -186,6 +186,8 @@ proc newSymG*(kind: TSymKind, n: PNode, c: PContext): PSym =
|
|||
result.owner = getCurrOwner()
|
||||
else:
|
||||
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,
|
||||
allowed: TSymFlags): PSym
|
||||
|
|
@ -202,7 +204,7 @@ proc typeAllowedCheck(info: TLineInfo; typ: PType; kind: TSymKind) =
|
|||
"' in this context: '" & typeToString(typ) & "'")
|
||||
|
||||
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 semDirectOp(c: PContext, n: PNode, flags: TExprFlags): PNode
|
||||
|
|
|
|||
|
|
@ -75,7 +75,7 @@ proc pickBestCandidate(c: PContext, headSymbol: PNode,
|
|||
errors.add(err)
|
||||
if z.state == csMatch:
|
||||
# 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
|
||||
of csEmpty, csNoMatch: best = z
|
||||
of csMatch:
|
||||
|
|
@ -395,7 +395,7 @@ proc explicitGenericInstantiation(c: PContext, n: PNode, s: PSym): PNode =
|
|||
for i in countup(0, len(a)-1):
|
||||
var candidate = a.sons[i].sym
|
||||
if candidate.kind in {skProc, skMethod, skConverter,
|
||||
skIterator, skClosureIterator}:
|
||||
skIterator}:
|
||||
# it suffices that the candidate has the proper number of generic
|
||||
# type parameters:
|
||||
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
|
||||
|
||||
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)
|
||||
# XXX add to 'c' for global analysis
|
||||
|
||||
|
|
|
|||
|
|
@ -753,11 +753,11 @@ proc semOverloadedCallAnalyseEffects(c: PContext, n: PNode, nOrig: PNode,
|
|||
flags: TExprFlags): PNode =
|
||||
if flags*{efInTypeof, efWantIterator} != {}:
|
||||
# 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 ``type(countup(1,3))``, see ``tests/ttoseq``.
|
||||
result = semOverloadedCall(c, n, nOrig,
|
||||
{skProc, skMethod, skConverter, skMacro, skTemplate}+skIterators)
|
||||
{skProc, skMethod, skConverter, skMacro, skTemplate, skIterator})
|
||||
else:
|
||||
result = semOverloadedCall(c, n, nOrig,
|
||||
{skProc, skMethod, skConverter, skMacro, skTemplate})
|
||||
|
|
@ -770,7 +770,7 @@ proc semOverloadedCallAnalyseEffects(c: PContext, n: PNode, nOrig: PNode,
|
|||
case callee.kind
|
||||
of skMacro, skTemplate: discard
|
||||
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)
|
||||
# error correction, prevents endless for loop elimination in transf.
|
||||
# See bug #2051:
|
||||
|
|
@ -1201,7 +1201,7 @@ proc semSubscript(c: PContext, n: PNode, flags: TExprFlags): PNode =
|
|||
let s = if n.sons[0].kind == nkSym: n.sons[0].sym
|
||||
elif n[0].kind in nkSymChoices: n.sons[0][0].sym
|
||||
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
|
||||
n.sons[0] = semSymGenericInstantiation(c, n.sons[0], s)
|
||||
result = explicitGenericInstantiation(c, n, s)
|
||||
|
|
@ -1349,8 +1349,8 @@ proc semAsgn(c: PContext, n: PNode; mode=asgnNormal): PNode =
|
|||
proc semReturn(c: PContext, n: PNode): PNode =
|
||||
result = n
|
||||
checkSonsLen(n, 1)
|
||||
if c.p.owner.kind in {skConverter, skMethod, skProc, skMacro,
|
||||
skClosureIterator}:
|
||||
if c.p.owner.kind in {skConverter, skMethod, skProc, skMacro} or (
|
||||
c.p.owner.kind == skIterator and c.p.owner.typ.callConv == ccClosure):
|
||||
if n.sons[0].kind != nkEmpty:
|
||||
# transform ``return expr`` to ``result = expr; return``
|
||||
if c.p.resultSym != nil:
|
||||
|
|
@ -1426,7 +1426,7 @@ proc semYieldVarResult(c: PContext, n: PNode, restype: PType) =
|
|||
proc semYield(c: PContext, n: PNode): PNode =
|
||||
result = n
|
||||
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)
|
||||
elif c.p.inTryStmt > 0 and c.p.owner.typ.callConv != ccInline:
|
||||
localError(n.info, errYieldNotAllowedInTryStmt)
|
||||
|
|
@ -1817,6 +1817,7 @@ proc semWhen(c: PContext, n: PNode, semCheck = true): PNode =
|
|||
whenNimvm = lookUp(c, exprNode).magic == mNimvm
|
||||
elif exprNode.kind == nkSym:
|
||||
whenNimvm = exprNode.sym.magic == mNimvm
|
||||
if whenNimvm: n.flags.incl nfLL
|
||||
|
||||
for i in countup(0, sonsLen(n) - 1):
|
||||
var it = n.sons[i]
|
||||
|
|
@ -2124,7 +2125,7 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
|
|||
var s = lookUp(c, n)
|
||||
if c.inTypeClass == 0: semCaptureSym(s, c.p.owner)
|
||||
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)
|
||||
result = symChoice(c, n, s, scClosed)
|
||||
if result.kind == nkSym:
|
||||
|
|
@ -2212,7 +2213,7 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
|
|||
localError(n.info, errUseQualifier, s.name.s)
|
||||
elif s.magic == mNone: result = semDirectOp(c, n, 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)
|
||||
else: result = semMagic(c, n, s, flags)
|
||||
else:
|
||||
|
|
|
|||
|
|
@ -58,7 +58,7 @@ proc semGenericStmtSymbol(c: PContext, n: PNode, s: PSym,
|
|||
of skUnknown:
|
||||
# Introduced in this pass! Leave it as an identifier.
|
||||
result = n
|
||||
of skProc, skMethod, skIterators, skConverter, skModule:
|
||||
of skProc, skMethod, skIterator, skConverter, skModule:
|
||||
result = symChoice(c, n, s, scOpen)
|
||||
of skTemplate:
|
||||
if macroToExpand(s):
|
||||
|
|
@ -226,7 +226,7 @@ proc semGenericStmt(c: PContext, n: PNode,
|
|||
of skUnknown, skParam:
|
||||
# Leave it as an identifier.
|
||||
discard
|
||||
of skProc, skMethod, skIterators, skConverter, skModule:
|
||||
of skProc, skMethod, skIterator, skConverter, skModule:
|
||||
result.sons[0] = symChoice(c, fn, s, scOption)
|
||||
# do not check of 's.magic==mRoof' here because it might be some
|
||||
# other '^' but after overload resolution the proper one:
|
||||
|
|
|
|||
|
|
@ -207,7 +207,7 @@ proc magicsAfterOverloadResolution(c: PContext, n: PNode,
|
|||
result = n.sons[1]
|
||||
else:
|
||||
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 n.sons[1]
|
||||
of mPlugin:
|
||||
|
|
|
|||
|
|
@ -84,7 +84,7 @@ proc performProcvarCheck(c: PContext, n: PNode, s: PSym) =
|
|||
proc semProcvarCheck(c: PContext, n: PNode) =
|
||||
let n = n.skipConv
|
||||
if n.kind == nkSym and n.sym.kind in {skProc, skMethod, skConverter,
|
||||
skIterator, skClosureIterator}:
|
||||
skIterator}:
|
||||
performProcvarCheck(c, n, n.sym)
|
||||
|
||||
proc semProc(c: PContext, n: PNode): PNode
|
||||
|
|
@ -326,6 +326,8 @@ proc semIdentDef(c: PContext, n: PNode, kind: TSymKind): PSym =
|
|||
incl(result.flags, sfGlobal)
|
||||
else:
|
||||
result = semIdentWithPragma(c, kind, n, {})
|
||||
if result.owner.kind == skModule:
|
||||
incl(result.flags, sfGlobal)
|
||||
suggestSym(n.info, result)
|
||||
styleCheckDef(result)
|
||||
|
||||
|
|
@ -598,7 +600,7 @@ proc semFor(c: PContext, n: PNode): PNode =
|
|||
# first class iterator:
|
||||
result = semForVars(c, n)
|
||||
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:
|
||||
n.sons[length-2] = implicitIterator(c, "items", n.sons[length-2])
|
||||
elif length == 4:
|
||||
|
|
@ -958,15 +960,17 @@ proc semInferredLambda(c: PContext, pt: TIdTable, n: PNode): PNode =
|
|||
var n = n
|
||||
|
||||
let original = n.sons[namePos].sym
|
||||
let s = copySym(original, false)
|
||||
incl(s.flags, sfFromGeneric)
|
||||
let s = original #copySym(original, false)
|
||||
#incl(s.flags, sfFromGeneric)
|
||||
#s.owner = original
|
||||
|
||||
n = replaceTypesInBody(c, pt, n, original)
|
||||
result = n
|
||||
s.ast = result
|
||||
n.sons[namePos].sym = s
|
||||
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
|
||||
s.typ = n.typ
|
||||
for i in 1..<params.len:
|
||||
|
|
@ -974,6 +978,7 @@ proc semInferredLambda(c: PContext, pt: TIdTable, n: PNode): PNode =
|
|||
tyFromExpr, tyFieldAccessor}+tyTypeClasses:
|
||||
localError(params[i].info, "cannot infer type of parameter: " &
|
||||
params[i].sym.name.s)
|
||||
#params[i].sym.owner = s
|
||||
openScope(c)
|
||||
pushOwner(s)
|
||||
addParams(c, params, skProc)
|
||||
|
|
@ -1006,7 +1011,8 @@ proc activate(c: PContext, n: PNode) =
|
|||
discard
|
||||
|
||||
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)
|
||||
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 n.sons[patternPos].kind != nkEmpty:
|
||||
n.sons[patternPos] = semPattern(c, n.sons[patternPos])
|
||||
if s.kind in skIterators:
|
||||
if s.kind == skIterator:
|
||||
s.typ.flags.incl(tfIterator)
|
||||
|
||||
var proto = searchForProc(c, oldScope, s)
|
||||
if proto == nil:
|
||||
if s.kind == skClosureIterator: s.typ.callConv = ccClosure
|
||||
else: s.typ.callConv = lastOptionEntry(c).defaultCC
|
||||
if s.kind == skIterator and s.typ.callConv == ccClosure:
|
||||
discard
|
||||
else:
|
||||
s.typ.callConv = lastOptionEntry(c).defaultCC
|
||||
# add it here, so that recursive procs are possible:
|
||||
if sfGenSym in s.flags: discard
|
||||
elif kind in OverloadableSyms:
|
||||
|
|
@ -1209,7 +1217,7 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind,
|
|||
n.sons[bodyPos] = transformBody(c.module, semBody, s)
|
||||
popProcCon(c)
|
||||
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))
|
||||
openScope(c)
|
||||
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:
|
||||
c.patterns.add(s)
|
||||
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:
|
||||
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) =
|
||||
if s.typ != nil: return
|
||||
|
|
@ -1240,15 +1248,12 @@ proc determineType(c: PContext, s: PSym) =
|
|||
discard semProcAux(c, s.ast, s.kind, {}, stepDetermineType)
|
||||
|
||||
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?
|
||||
if n[namePos].kind == nkSym:
|
||||
# gensym'ed iterators might need to become closure iterators:
|
||||
n[namePos].sym.owner = getCurrOwner()
|
||||
n[namePos].sym.kind = kind
|
||||
result = semProcAux(c, n, kind, iteratorPragmas)
|
||||
n[namePos].sym.kind = skIterator
|
||||
result = semProcAux(c, n, skIterator, iteratorPragmas)
|
||||
var s = result.sons[namePos].sym
|
||||
var t = s.typ
|
||||
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:
|
||||
result = n
|
||||
of skType:
|
||||
if (s.typ != nil) and (s.typ.kind != tyGenericParam):
|
||||
result = newSymNodeTypeDesc(s, n.info)
|
||||
else:
|
||||
result = n
|
||||
result = newSymNodeTypeDesc(s, n.info)
|
||||
else:
|
||||
result = newSymNode(s, n.info)
|
||||
|
||||
|
|
@ -456,9 +453,7 @@ proc semTemplBody(c: var TemplCtx, n: PNode): PNode =
|
|||
of nkMethodDef:
|
||||
result = semRoutineInTemplBody(c, n, skMethod)
|
||||
of nkIteratorDef:
|
||||
let kind = if hasPragma(n[pragmasPos], wClosure): skClosureIterator
|
||||
else: skIterator
|
||||
result = semRoutineInTemplBody(c, n, kind)
|
||||
result = semRoutineInTemplBody(c, n, skIterator)
|
||||
of nkTemplateDef:
|
||||
result = semRoutineInTemplBody(c, n, skTemplate)
|
||||
of nkMacroDef:
|
||||
|
|
|
|||
|
|
@ -960,10 +960,6 @@ proc semProcTypeNode(c: PContext, n, genericParams: PNode,
|
|||
var r: PType
|
||||
if n.sons[0].kind != nkEmpty:
|
||||
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:
|
||||
# turn explicit 'void' return type into 'nil' because the rest of the
|
||||
|
|
@ -1296,7 +1292,7 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType =
|
|||
child.flags.incl tfIterator
|
||||
result.addSonSkipIntLit(child)
|
||||
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):
|
||||
result.callConv = ccInline
|
||||
|
|
|
|||
|
|
@ -167,12 +167,12 @@ proc sumGeneric(t: PType): int =
|
|||
t = t.lastSon
|
||||
if t.kind == tyEmpty: break
|
||||
inc result
|
||||
of tyGenericInvocation, tyTuple:
|
||||
of tyGenericInvocation, tyTuple, tyProc:
|
||||
result += ord(t.kind == tyGenericInvocation)
|
||||
for i in 0 .. <t.len: result += t.sons[i].sumGeneric
|
||||
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,
|
||||
tyUInt..tyUInt64:
|
||||
return isvar
|
||||
|
|
@ -1442,7 +1442,7 @@ proc paramTypesMatch*(m: var TCandidate, f, a: PType,
|
|||
z.calleeSym = m.calleeSym
|
||||
var best = -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)
|
||||
z.callee = arg.sons[i].typ
|
||||
z.calleeSym = arg.sons[i].sym
|
||||
|
|
|
|||
|
|
@ -45,7 +45,7 @@ type
|
|||
inlining: int # > 0 if we are in inlining context (copy vars)
|
||||
nestedProcs: int # > 0 if we are in a nested proc
|
||||
contSyms, breakSyms: seq[PSym] # to transform 'continue' and 'break'
|
||||
deferDetected: bool
|
||||
deferDetected, tooEarly: bool
|
||||
PTransf = ref TTransfContext
|
||||
|
||||
proc newTransNode(a: PNode): PTransNode {.inline.} =
|
||||
|
|
@ -93,10 +93,15 @@ proc getCurrOwner(c: PTransf): PSym =
|
|||
if c.transCon != nil: result = c.transCon.owner
|
||||
else: result = c.module
|
||||
|
||||
proc newTemp(c: PTransf, typ: PType, info: TLineInfo): PSym =
|
||||
result = newSym(skTemp, getIdent(genPrefix), getCurrOwner(c), info)
|
||||
result.typ = skipTypes(typ, {tyGenericInst})
|
||||
incl(result.flags, sfFromGeneric)
|
||||
proc newTemp(c: PTransf, typ: PType, info: TLineInfo): PNode =
|
||||
let r = newSym(skTemp, getIdent(genPrefix), getCurrOwner(c), info)
|
||||
r.typ = skipTypes(typ, {tyGenericInst})
|
||||
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
|
||||
|
||||
|
|
@ -111,13 +116,22 @@ proc newAsgnStmt(c: PTransf, le: PNode, ri: PTransNode): PTransNode =
|
|||
result[1] = ri
|
||||
|
||||
proc transformSymAux(c: PTransf, n: PNode): PNode =
|
||||
#if n.sym.kind == skClosureIterator:
|
||||
# return liftIterSym(n)
|
||||
let s = n.sym
|
||||
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 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:
|
||||
b = n.sym.getBody
|
||||
b = s.getBody
|
||||
if b.kind != nkSym: internalError(n.info, "wrong AST for borrowed symbol")
|
||||
b = newSymNode(b.sym)
|
||||
b.info = n.info
|
||||
|
|
@ -132,6 +146,16 @@ proc transformSymAux(c: PTransf, n: PNode): PNode =
|
|||
proc transformSym(c: PTransf, n: PNode): PTransNode =
|
||||
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 =
|
||||
result = newTransNode(v)
|
||||
for i in countup(0, sonsLen(v)-1):
|
||||
|
|
@ -141,35 +165,30 @@ proc transformVarSection(c: PTransf, v: PNode): PTransNode =
|
|||
elif it.kind == nkIdentDefs:
|
||||
if it.sons[0].kind == nkSym:
|
||||
internalAssert(it.len == 3)
|
||||
var newVar = copySym(it.sons[0].sym)
|
||||
incl(newVar.flags, sfFromGeneric)
|
||||
# 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))
|
||||
let x = freshVar(c, it.sons[0].sym)
|
||||
idNodeTablePut(c.transCon.mapping, it.sons[0].sym, x)
|
||||
var defs = newTransNode(nkIdentDefs, it.info, 3)
|
||||
if importantComments():
|
||||
# keep documentation information:
|
||||
PNode(defs).comment = it.comment
|
||||
defs[0] = newSymNode(newVar).PTransNode
|
||||
defs[0] = x.PTransNode
|
||||
defs[1] = it.sons[1].PTransNode
|
||||
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
|
||||
else:
|
||||
# has been transformed into 'param.x' for closure iterators, so keep it:
|
||||
result[i] = PTransNode(it)
|
||||
# has been transformed into 'param.x' for closure iterators, so just
|
||||
# transform it:
|
||||
result[i] = transform(c, it)
|
||||
else:
|
||||
if it.kind != nkVarTuple:
|
||||
internalError(it.info, "transformVarSection: not nkVarTuple")
|
||||
var L = sonsLen(it)
|
||||
var defs = newTransNode(it.kind, it.info, L)
|
||||
for j in countup(0, L-3):
|
||||
var newVar = copySym(it.sons[j].sym)
|
||||
incl(newVar.flags, sfFromGeneric)
|
||||
newVar.owner = getCurrOwner(c)
|
||||
idNodeTablePut(c.transCon.mapping, it.sons[j].sym, newSymNode(newVar))
|
||||
defs[j] = newSymNode(newVar).PTransNode
|
||||
let x = freshVar(c, it.sons[j].sym)
|
||||
idNodeTablePut(c.transCon.mapping, it.sons[j].sym, x)
|
||||
defs[j] = x.PTransNode
|
||||
assert(it.sons[L-2].kind == nkEmpty)
|
||||
defs[L-2] = ast.emptyNode.PTransNode
|
||||
defs[L-1] = transform(c, it.sons[L-1])
|
||||
|
|
@ -294,10 +313,18 @@ proc introduceNewLocalVars(c: PTransf, n: PNode): PTransNode =
|
|||
result = PTransNode(n)
|
||||
of nkVarSection, nkLetSection:
|
||||
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:
|
||||
result = newTransNode(n)
|
||||
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 =
|
||||
result = newTransNode(nkStmtList, n.info, 0)
|
||||
|
|
@ -348,6 +375,20 @@ proc transformAddrDeref(c: PTransf, n: PNode, a, b: TNodeKind): PTransNode =
|
|||
# addr ( deref ( x )) --> x
|
||||
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(nkHiddenStdConv, prc.info, dest)
|
||||
conv.add(emptyNode)
|
||||
conv.add(prc)
|
||||
result.add(conv)
|
||||
result.add(newNodeIT(nkNilLit, prc.info, getSysType(tyNil)))
|
||||
|
||||
proc transformConv(c: PTransf, n: PNode): PTransNode =
|
||||
# numeric types need range checks:
|
||||
var dest = skipTypes(n.typ, abstractVarRange)
|
||||
|
|
@ -428,6 +469,10 @@ proc transformConv(c: PTransf, n: PNode): PTransNode =
|
|||
of tyGenericParam, tyOrdinal:
|
||||
result = transform(c, n.sons[1])
|
||||
# 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:
|
||||
result = transformSons(c, n)
|
||||
|
||||
|
|
@ -479,9 +524,13 @@ proc transformFor(c: PTransf, n: PNode): PTransNode =
|
|||
return result
|
||||
c.breakSyms.add(labl)
|
||||
if call.kind notin nkCallKinds or call.sons[0].kind != nkSym or
|
||||
call.sons[0].sym.kind != skIterator:
|
||||
call.sons[0].typ.callConv == ccClosure:
|
||||
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
|
||||
return result
|
||||
|
||||
|
|
@ -517,9 +566,9 @@ proc transformFor(c: PTransf, n: PNode): PTransNode =
|
|||
of paFastAsgn:
|
||||
# generate a temporary and produce an assignment statement:
|
||||
var temp = newTemp(c, formal.typ, formal.info)
|
||||
addVar(v, newSymNode(temp))
|
||||
add(stmtList, newAsgnStmt(c, newSymNode(temp), arg.PTransNode))
|
||||
idNodeTablePut(newC.mapping, formal, newSymNode(temp))
|
||||
addVar(v, temp)
|
||||
add(stmtList, newAsgnStmt(c, temp, arg.PTransNode))
|
||||
idNodeTablePut(newC.mapping, formal, temp)
|
||||
of paVarAsgn:
|
||||
assert(skipTypes(formal.typ, abstractInst).kind == tyVar)
|
||||
idNodeTablePut(newC.mapping, formal, arg)
|
||||
|
|
@ -700,18 +749,13 @@ proc transform(c: PTransf, n: PNode): PTransNode =
|
|||
result = PTransNode(n)
|
||||
of nkBracketExpr: result = transformArrayAccess(c, n)
|
||||
of procDefs:
|
||||
when false:
|
||||
if n.sons[genericParamsPos].kind == nkEmpty:
|
||||
var s = n.sons[namePos].sym
|
||||
n.sons[bodyPos] = PNode(transform(c, s.getBody))
|
||||
if s.ast.sons[bodyPos] != n.sons[bodyPos]:
|
||||
# somehow this can happen ... :-/
|
||||
s.ast.sons[bodyPos] = n.sons[bodyPos]
|
||||
#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)
|
||||
var s = n.sons[namePos].sym
|
||||
if n.typ != nil and s.typ.callConv == ccClosure:
|
||||
result = transformSym(c, n.sons[namePos])
|
||||
# use the same node as before if still a symbol:
|
||||
if result.PNode.kind == nkSym: result = PTransNode(n)
|
||||
else:
|
||||
result = PTransNode(n)
|
||||
of nkMacroDef:
|
||||
# XXX no proper closure support yet:
|
||||
when false:
|
||||
|
|
@ -748,7 +792,7 @@ proc transform(c: PTransf, n: PNode): PTransNode =
|
|||
result = newTransNode(nkCommentStmt, n.info, 0)
|
||||
tryStmt.addSon(deferPart)
|
||||
# disable the original 'defer' statement:
|
||||
n.kind = nkCommentStmt
|
||||
n.kind = nkEmpty
|
||||
of nkContinueStmt:
|
||||
result = PTransNode(newNodeI(nkBreakStmt, n.info))
|
||||
var labl = c.contSyms[c.contSyms.high]
|
||||
|
|
@ -794,7 +838,14 @@ proc transform(c: PTransf, n: PNode): PTransNode =
|
|||
# XXX comment handling really sucks:
|
||||
if importantComments():
|
||||
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:
|
||||
result = transformSons(c, n)
|
||||
when false:
|
||||
|
|
@ -866,11 +917,11 @@ proc transformBody*(module: PSym, n: PNode, prc: PSym): PNode =
|
|||
result = n
|
||||
else:
|
||||
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)
|
||||
result = liftLambdas(prc, result)
|
||||
#if prc.kind == skClosureIterator:
|
||||
# result = lambdalifting.liftIterator(prc, result)
|
||||
#result = liftLambdas(prc, result)
|
||||
incl(result.flags, nfTransf)
|
||||
when useEffectSystem: trackProc(prc, result)
|
||||
#if prc.name.s == "testbody":
|
||||
|
|
@ -883,9 +934,11 @@ proc transformStmt*(module: PSym, n: PNode): PNode =
|
|||
var c = openTransf(module, "")
|
||||
result = processTransf(c, n, module)
|
||||
liftDefer(c, result)
|
||||
result = liftLambdasForTopLevel(module, result)
|
||||
#result = liftLambdasForTopLevel(module, result)
|
||||
incl(result.flags, nfTransf)
|
||||
when useEffectSystem: trackTopLevelStmt(module, result)
|
||||
#if n.info ?? "temp.nim":
|
||||
# echo renderTree(result, {renderIds})
|
||||
|
||||
proc transformExpr*(module: PSym, n: PNode): PNode =
|
||||
if nfTransf in n.flags:
|
||||
|
|
|
|||
|
|
@ -1061,7 +1061,8 @@ proc typeAllowedNode(marker: var IntSet, n: PNode, kind: TSymKind,
|
|||
else:
|
||||
for i in countup(0, sonsLen(n) - 1):
|
||||
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)
|
||||
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,
|
||||
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
|
||||
# evaluation if something is wrong:
|
||||
result = nil
|
||||
|
|
@ -1085,7 +1086,7 @@ proc typeAllowedAux(marker: var IntSet, typ: PType, kind: TSymKind,
|
|||
var t = skipTypes(typ, abstractInst-{tyTypeDesc})
|
||||
case t.kind
|
||||
of tyVar:
|
||||
if kind == skConst: return t
|
||||
if kind in {skProc, skConst}: return t
|
||||
var t2 = skipTypes(t.sons[0], abstractInst-{tyTypeDesc})
|
||||
case t2.kind
|
||||
of tyVar:
|
||||
|
|
@ -1097,6 +1098,7 @@ proc typeAllowedAux(marker: var IntSet, typ: PType, kind: TSymKind,
|
|||
if kind notin {skParam, skResult}: result = t
|
||||
else: result = typeAllowedAux(marker, t2, kind, flags)
|
||||
of tyProc:
|
||||
if kind == skConst and t.callConv == ccClosure: return t
|
||||
for i in countup(1, sonsLen(t) - 1):
|
||||
result = typeAllowedAux(marker, t.sons[i], skParam, flags)
|
||||
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)
|
||||
if result != nil: break
|
||||
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}
|
||||
for i in countup(0, sonsLen(t) - 1):
|
||||
result = typeAllowedAux(marker, t.sons[i], kind, flags)
|
||||
|
|
|
|||
|
|
@ -359,7 +359,14 @@ proc opConv*(dest: var TFullReg, src: TFullReg, desttyp, srctyp: PType): bool =
|
|||
of tyFloat..tyFloat64:
|
||||
dest.intVal = int(src.floatVal)
|
||||
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:
|
||||
if dest.kind != rkFloat:
|
||||
myreset(dest); dest.kind = rkFloat
|
||||
|
|
@ -611,7 +618,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
|
|||
addSon(regs[ra].node, r.copyTree)
|
||||
of opcExcl:
|
||||
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)
|
||||
var r = diffSets(regs[ra].node, b)
|
||||
discardSons(regs[ra].node)
|
||||
|
|
|
|||
|
|
@ -70,7 +70,7 @@ proc atomicTypeX(name: string; t: PType; info: TLineInfo): PNode =
|
|||
proc mapTypeToAst(t: PType, info: TLineInfo; allowRecursion=false): 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)
|
||||
for i in 0 .. < t.len:
|
||||
if t.sons[i] == nil:
|
||||
|
|
@ -92,19 +92,19 @@ proc mapTypeToAst(t: PType, info: TLineInfo; allowRecursion=false): PNode =
|
|||
of tyStmt: result = atomicType("stmt")
|
||||
of tyEmpty: result = atomicType"void"
|
||||
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 mapTypeToAst(t.sons[0], info)
|
||||
result.add mapTypeToAst(t.sons[1], info)
|
||||
of tyTypeDesc:
|
||||
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 mapTypeToAst(t.base, info)
|
||||
else:
|
||||
result = atomicType"typeDesc"
|
||||
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:
|
||||
result.add mapTypeToAst(t.sons[i], info)
|
||||
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 tyObject:
|
||||
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:
|
||||
result.add ast.emptyNode
|
||||
else:
|
||||
|
|
@ -126,7 +126,7 @@ proc mapTypeToAst(t: PType, info: TLineInfo; allowRecursion=false): PNode =
|
|||
else:
|
||||
result = atomicType(t.sym.name.s)
|
||||
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)
|
||||
of tyTuple: result = mapTypeToBracket("tuple", 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 tyOpenArray: result = mapTypeToBracket("openArray", t, info)
|
||||
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 t.n.sons[0].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 tyAnything: result = atomicType"anything"
|
||||
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")
|
||||
if t.n != nil:
|
||||
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:
|
||||
cannotEval(n)
|
||||
elif s.kind in {skProc, skConverter, skMethod,
|
||||
skIterator, skClosureIterator} and sfForward in s.flags:
|
||||
skIterator} and sfForward in s.flags:
|
||||
cannotEval(n)
|
||||
|
||||
proc isTemp(c: PCtx; dest: TDest): bool =
|
||||
|
|
@ -1638,7 +1638,7 @@ proc gen(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags = {}) =
|
|||
case s.kind
|
||||
of skVar, skForVar, skTemp, skLet, skParam, skResult:
|
||||
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:
|
||||
if procIsCallback(c, s): discard
|
||||
elif sfImportc in s.flags: c.importcSym(n.info, s)
|
||||
|
|
|
|||
|
|
@ -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=="/>
|
||||
|
||||
<!-- 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=Source+Code+Pro:400,500,600' 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'/>
|
||||
|
||||
<!-- CSS -->
|
||||
<title>$title</title>
|
||||
|
|
@ -1246,7 +1246,7 @@ dt pre > span.Operator ~ span.Identifier, dt pre > span.Operator ~ span.Operator
|
|||
<div class="row">
|
||||
<div class="twelve-columns footer">
|
||||
<span class="nim-sprite"></span>
|
||||
<br>
|
||||
<br/>
|
||||
<small>Made with Nim. Generated: $date $time UTC</small>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -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!
|
||||
|
||||
|
||||
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
|
||||
----------------------
|
||||
|
||||
|
|
|
|||
|
|
@ -236,8 +236,6 @@ executable code.
|
|||
Do notation
|
||||
-----------
|
||||
|
||||
**Note:** The future of the ``do`` notation is uncertain.
|
||||
|
||||
As a special more convenient notation, proc expressions involved in procedure
|
||||
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.
|
||||
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
|
||||
proc performWithUndo(task: proc(), undo: proc()) = ...
|
||||
macro performWithUndo(task, undo: untyped) = ...
|
||||
|
||||
performWithUndo do:
|
||||
# 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
|
||||
-------------
|
||||
|
||||
|
|
|
|||
14
doc/tut2.txt
14
doc/tut2.txt
|
|
@ -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
|
||||
using the ``dumpTree`` macro, but of course you can call that at any point of
|
||||
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 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:
|
||||
quit("Usage: statcsv filename[.csv]")
|
||||
|
|
|
|||
|
|
@ -1,4 +1,6 @@
|
|||
|
||||
import ospaths
|
||||
|
||||
mode = ScriptMode.Verbose
|
||||
|
||||
var id = 0
|
||||
|
|
@ -10,4 +12,8 @@ exec "git clone https://github.com/nim-lang/nimble.git nimble" & $id
|
|||
withDir "nimble" & $id & "/src":
|
||||
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
|
||||
|
|
|
|||
|
|
@ -97,7 +97,7 @@ type
|
|||
nskUnknown, nskConditional, nskDynLib, nskParam,
|
||||
nskGenericParam, nskTemp, nskModule, nskType, nskVar, nskLet,
|
||||
nskConst, nskResult,
|
||||
nskProc, nskMethod, nskIterator, nskClosureIterator,
|
||||
nskProc, nskMethod, nskIterator,
|
||||
nskConverter, nskMacro, nskTemplate, nskField,
|
||||
nskEnumField, nskForVar, nskLabel,
|
||||
nskStub
|
||||
|
|
|
|||
|
|
@ -61,7 +61,10 @@ type
|
|||
## wrapped value and **must not** live longer than
|
||||
## its wrapped value.
|
||||
value: pointer
|
||||
rawType: PNimType
|
||||
when defined(js):
|
||||
rawType: PNimType
|
||||
else:
|
||||
rawTypePtr: pointer
|
||||
|
||||
ppointer = ptr pointer
|
||||
pbyteArray = ptr array[0.. 0xffff, int8]
|
||||
|
|
@ -71,6 +74,14 @@ type
|
|||
when defined(gogc):
|
||||
elemSize: int
|
||||
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].}
|
||||
|
||||
when defined(gogc):
|
||||
|
|
@ -108,7 +119,7 @@ proc selectBranch(aa: pointer, n: ptr TNimNode): ptr TNimNode =
|
|||
else:
|
||||
result = n.sons[n.len]
|
||||
|
||||
proc newAny(value: pointer, rawType: PNimType): Any =
|
||||
proc newAny(value: pointer, rawType: PNimType): Any {.inline.} =
|
||||
result.value = value
|
||||
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
|
||||
## `x` can be modified with its ``Any`` wrapper! The client needs to ensure
|
||||
## that the wrapper **does not** live longer than `x`!
|
||||
result.value = addr(x)
|
||||
result.rawType = cast[PNimType](getTypeInfo(x))
|
||||
newAny(addr(x), cast[PNimType](getTypeInfo(x)))
|
||||
|
||||
proc kind*(x: Any): AnyKind {.inline.} =
|
||||
## get the type kind
|
||||
|
|
@ -345,7 +355,7 @@ proc `[]`*(x: Any, fieldName: string): Any =
|
|||
result.value = x.value +!! n.offset
|
||||
result.rawType = n.typ
|
||||
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:
|
||||
raise newException(ValueError, "invalid field name: " & fieldName)
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
#
|
||||
#
|
||||
# Nim's Runtime Library
|
||||
# (c) Copyright 2012 Andreas Rumpf
|
||||
# (c) Copyright 2015 Andreas Rumpf
|
||||
#
|
||||
# See the file "copying.txt", included in this
|
||||
# distribution, for details about the copyright.
|
||||
|
|
@ -43,45 +43,26 @@
|
|||
|
||||
import strutils, mysql
|
||||
|
||||
import db_common
|
||||
export db_common
|
||||
|
||||
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
|
||||
## transformed always to the empty string.
|
||||
InstantRow* = tuple[row: cstringArray, len: int] ## a handle that can be
|
||||
## used to get a row's
|
||||
## column text on demand
|
||||
EDb* = object of IOError ## exception that is raised if a database error occurs
|
||||
## converted to nil.
|
||||
InstantRow* = object ## a handle that can be used to get a row's
|
||||
## column text on demand
|
||||
row: cstringArray
|
||||
len: int
|
||||
{.deprecated: [TRow: Row, TDbConn: DbConn].}
|
||||
|
||||
SqlQuery* = distinct string ## an SQL query string
|
||||
|
||||
FDb* = object of IOEffect ## effect that denotes a database operation
|
||||
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
|
||||
proc dbError*(db: DbConn) {.noreturn.} =
|
||||
## raises a DbError exception.
|
||||
var e: ref DbError
|
||||
new(e)
|
||||
e.msg = $mysql.error(db)
|
||||
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:
|
||||
proc dbQueryOpt*(db: DbConn, query: string, args: varargs[string, `$`]) =
|
||||
var stmt = mysql_stmt_init(db)
|
||||
|
|
@ -114,7 +95,7 @@ proc dbFormat(formatstr: SqlQuery, args: varargs[string]): string =
|
|||
add(result, c)
|
||||
|
||||
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.
|
||||
var q = dbFormat(query, args)
|
||||
return mysql.realQuery(db, q, q.len) == 0'i32
|
||||
|
|
@ -124,7 +105,7 @@ proc rawExec(db: DbConn, query: SqlQuery, args: varargs[string, `$`]) =
|
|||
if mysql.realQuery(db, q, q.len) != 0'i32: dbError(db)
|
||||
|
||||
proc exec*(db: DbConn, query: SqlQuery, args: varargs[string, `$`]) {.
|
||||
tags: [FReadDB, FWriteDb].} =
|
||||
tags: [ReadDbEffect, WriteDbEffect].} =
|
||||
## executes the query and raises EDB if not successful.
|
||||
var q = dbFormat(query, args)
|
||||
if mysql.realQuery(db, q, q.len) != 0'i32: dbError(db)
|
||||
|
|
@ -139,7 +120,7 @@ proc properFreeResult(sqlres: mysql.PRES, row: cstringArray) =
|
|||
mysql.freeResult(sqlres)
|
||||
|
||||
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 fast, but potentially dangerous. Use this iterator only
|
||||
|
|
@ -167,9 +148,9 @@ iterator fastRows*(db: DbConn, query: SqlQuery,
|
|||
|
||||
iterator instantRows*(db: DbConn, query: SqlQuery,
|
||||
args: varargs[string, `$`]): InstantRow
|
||||
{.tags: [FReadDb].} =
|
||||
## 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.
|
||||
{.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.
|
||||
rawExec(db, query, args)
|
||||
var sqlres = mysql.useResult(db)
|
||||
if sqlres != nil:
|
||||
|
|
@ -178,20 +159,102 @@ iterator instantRows*(db: DbConn, query: SqlQuery,
|
|||
while true:
|
||||
row = mysql.fetchRow(sqlres)
|
||||
if row == nil: break
|
||||
yield (row: row, len: L)
|
||||
yield InstantRow(row: row, len: L)
|
||||
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.} =
|
||||
## returns text for given column of the row
|
||||
## Returns text for given column of the row.
|
||||
$row.row[col]
|
||||
|
||||
proc len*(row: InstantRow): int {.inline.} =
|
||||
## returns number of columns in the row
|
||||
## Returns number of columns in the row.
|
||||
row.len
|
||||
|
||||
proc getRow*(db: DbConn, query: SqlQuery,
|
||||
args: varargs[string, `$`]): Row {.tags: [FReadDB].} =
|
||||
## retrieves a single row. If the query doesn't return any rows, this proc
|
||||
args: varargs[string, `$`]): Row {.tags: [ReadDbEffect].} =
|
||||
## Retrieves a single row. If the query doesn't return any rows, this proc
|
||||
## will return a Row with empty strings for each column.
|
||||
rawExec(db, query, args)
|
||||
var sqlres = mysql.useResult(db)
|
||||
|
|
@ -209,7 +272,7 @@ proc getRow*(db: DbConn, query: SqlQuery,
|
|||
properFreeResult(sqlres, row)
|
||||
|
||||
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.
|
||||
result = @[]
|
||||
rawExec(db, query, args)
|
||||
|
|
@ -232,19 +295,19 @@ proc getAllRows*(db: DbConn, query: SqlQuery,
|
|||
mysql.freeResult(sqlres)
|
||||
|
||||
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.
|
||||
for r in items(getAllRows(db, query, args)): yield r
|
||||
|
||||
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
|
||||
## result dataset. Returns "" if the dataset contains no rows or the database
|
||||
## value is NULL.
|
||||
result = getRow(db, query, args)[0]
|
||||
|
||||
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
|
||||
## generated ID for the row or -1 in case of an error.
|
||||
var q = dbFormat(query, args)
|
||||
|
|
@ -254,7 +317,7 @@ proc tryInsertId*(db: DbConn, query: SqlQuery,
|
|||
result = mysql.insertId(db)
|
||||
|
||||
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
|
||||
## generated ID for the row.
|
||||
result = tryInsertID(db, query, args)
|
||||
|
|
@ -262,18 +325,18 @@ proc insertId*(db: DbConn, query: SqlQuery,
|
|||
|
||||
proc execAffectedRows*(db: DbConn, query: SqlQuery,
|
||||
args: varargs[string, `$`]): int64 {.
|
||||
tags: [FReadDB, FWriteDb].} =
|
||||
tags: [ReadDbEffect, WriteDbEffect].} =
|
||||
## runs the query (typically "UPDATE") and returns the
|
||||
## number of affected rows
|
||||
rawExec(db, query, args)
|
||||
result = mysql.affectedRows(db)
|
||||
|
||||
proc close*(db: DbConn) {.tags: [FDb].} =
|
||||
proc close*(db: DbConn) {.tags: [DbEffect].} =
|
||||
## closes the database connection.
|
||||
if db != nil: mysql.close(db)
|
||||
|
||||
proc open*(connection, user, password, database: string): DbConn {.
|
||||
tags: [FDb].} =
|
||||
tags: [DbEffect].} =
|
||||
## opens a database connection. Raises `EDb` if the connection could not
|
||||
## be established.
|
||||
result = mysql.init(nil)
|
||||
|
|
@ -291,7 +354,7 @@ proc open*(connection, user, password, database: string): DbConn {.
|
|||
dbError(errmsg)
|
||||
|
||||
proc setEncoding*(connection: DbConn, encoding: string): bool {.
|
||||
tags: [FDb].} =
|
||||
tags: [DbEffect].} =
|
||||
## sets the encoding of a database connection, returns true for
|
||||
## success, false for failure.
|
||||
result = mysql.set_character_set(connection, encoding) == 0
|
||||
|
|
|
|||
463
lib/impure/db_odbc.nim
Normal file
463
lib/impure/db_odbc.nim
Normal file
|
|
@ -0,0 +1,463 @@
|
|||
#
|
||||
#
|
||||
# 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 agains ODBC drivers for
|
||||
## Teradata, Oracle, Sybase, MSSqlvSvr, et. al. databases
|
||||
##
|
||||
## Currently all queries are ANSI calls, not Unicode.
|
||||
##
|
||||
## 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")
|
||||
|
|
@ -62,47 +62,28 @@
|
|||
## "Dominik")
|
||||
import strutils, postgres
|
||||
|
||||
import db_common
|
||||
export db_common
|
||||
|
||||
type
|
||||
DbConn* = PPGconn ## encapsulates a database connection
|
||||
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
|
||||
## used to get a row's
|
||||
## 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
|
||||
|
||||
FDb* = object of IOEffect ## effect that denotes a database operation
|
||||
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,
|
||||
{.deprecated: [TRow: Row, TDbConn: DbConn,
|
||||
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.} =
|
||||
## raises an EDb exception.
|
||||
var e: ref EDb
|
||||
## raises a DbError exception.
|
||||
var e: ref DbError
|
||||
new(e)
|
||||
e.msg = $pqErrorMessage(db)
|
||||
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 =
|
||||
## DB quotes the string.
|
||||
result = "'"
|
||||
|
|
@ -127,7 +108,7 @@ proc dbFormat(formatstr: SqlQuery, args: varargs[string]): string =
|
|||
add(result, c)
|
||||
|
||||
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.
|
||||
var res = pqexecParams(db, dbFormat(query, args), 0, nil, nil,
|
||||
nil, nil, 0)
|
||||
|
|
@ -135,7 +116,8 @@ proc tryExec*(db: DbConn, query: SqlQuery,
|
|||
pqclear(res)
|
||||
|
||||
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.
|
||||
var arr = allocCStringArray(args)
|
||||
var res = pqexecPrepared(db, stmtName.string, int32(args.len), arr,
|
||||
|
|
@ -145,7 +127,7 @@ proc tryExec*(db: DbConn, stmtName: SqlPrepared,
|
|||
pqclear(res)
|
||||
|
||||
proc exec*(db: DbConn, query: SqlQuery, args: varargs[string, `$`]) {.
|
||||
tags: [FReadDB, FWriteDb].} =
|
||||
tags: [ReadDbEffect, WriteDbEffect].} =
|
||||
## executes the query and raises EDB if not successful.
|
||||
var res = pqexecParams(db, dbFormat(query, args), 0, nil, nil,
|
||||
nil, nil, 0)
|
||||
|
|
@ -153,7 +135,7 @@ proc exec*(db: DbConn, query: SqlQuery, args: varargs[string, `$`]) {.
|
|||
pqclear(res)
|
||||
|
||||
proc exec*(db: DbConn, stmtName: SqlPrepared,
|
||||
args: varargs[string]) {.tags: [FReadDB, FWriteDb].} =
|
||||
args: varargs[string]) {.tags: [ReadDbEffect, WriteDbEffect].} =
|
||||
var arr = allocCStringArray(args)
|
||||
var res = pqexecPrepared(db, stmtName.string, int32(args.len), arr,
|
||||
nil, nil, 0)
|
||||
|
|
@ -196,7 +178,7 @@ proc setRow(res: PPGresult, r: var Row, line, cols: int32) =
|
|||
add(r[col], x)
|
||||
|
||||
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
|
||||
## fast, but potenially dangerous: If the for-loop-body executes another
|
||||
## query, the results can be undefined. For Postgres it is safe though.
|
||||
|
|
@ -209,7 +191,7 @@ iterator fastRows*(db: DbConn, query: SqlQuery,
|
|||
pqclear(res)
|
||||
|
||||
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.
|
||||
var res = setupQuery(db, stmtName, args)
|
||||
var L = pqNfields(res)
|
||||
|
|
@ -221,9 +203,9 @@ iterator fastRows*(db: DbConn, stmtName: SqlPrepared,
|
|||
|
||||
iterator instantRows*(db: DbConn, query: SqlQuery,
|
||||
args: varargs[string, `$`]): InstantRow
|
||||
{.tags: [FReadDb].} =
|
||||
{.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 interator body.
|
||||
## on demand using []. Returned handle is valid only within iterator body.
|
||||
var res = setupQuery(db, query, args)
|
||||
for i in 0..pqNtuples(res)-1:
|
||||
yield (res: res, line: i)
|
||||
|
|
@ -231,9 +213,9 @@ iterator instantRows*(db: DbConn, query: SqlQuery,
|
|||
|
||||
iterator instantRows*(db: DbConn, stmtName: SqlPrepared,
|
||||
args: varargs[string, `$`]): InstantRow
|
||||
{.tags: [FReadDb].} =
|
||||
{.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 interator body.
|
||||
## on demand using []. Returned handle is valid only within iterator body.
|
||||
var res = setupQuery(db, stmtName, args)
|
||||
for i in 0..pqNtuples(res)-1:
|
||||
yield (res: res, line: i)
|
||||
|
|
@ -248,7 +230,7 @@ proc len*(row: InstantRow): int32 {.inline.} =
|
|||
pqNfields(row.res)
|
||||
|
||||
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
|
||||
## will return a Row with empty strings for each column.
|
||||
var res = setupQuery(db, query, args)
|
||||
|
|
@ -258,7 +240,7 @@ proc getRow*(db: DbConn, query: SqlQuery,
|
|||
pqclear(res)
|
||||
|
||||
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 L = pqNfields(res)
|
||||
result = newRow(L)
|
||||
|
|
@ -266,31 +248,34 @@ proc getRow*(db: DbConn, stmtName: SqlPrepared,
|
|||
pqClear(res)
|
||||
|
||||
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.
|
||||
result = @[]
|
||||
for r in fastRows(db, query, args):
|
||||
result.add(r)
|
||||
|
||||
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.
|
||||
result = @[]
|
||||
for r in fastRows(db, stmtName, args):
|
||||
result.add(r)
|
||||
|
||||
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.
|
||||
for r in items(getAllRows(db, query, args)): yield r
|
||||
|
||||
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.
|
||||
for r in items(getAllRows(db, stmtName, args)): yield r
|
||||
|
||||
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
|
||||
## result dataset. Returns "" if the dataset contains no rows or the database
|
||||
## value is NULL.
|
||||
|
|
@ -298,7 +283,8 @@ proc getValue*(db: DbConn, query: SqlQuery,
|
|||
result = if isNil(x): "" else: $x
|
||||
|
||||
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
|
||||
## 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
|
||||
|
|
@ -311,7 +297,8 @@ proc tryInsertID*(db: DbConn, query: SqlQuery,
|
|||
result = -1
|
||||
|
||||
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
|
||||
## generated ID for the row. For Postgre this adds
|
||||
## ``RETURNING id`` to the query, so it only works if your primary key is
|
||||
|
|
@ -321,7 +308,7 @@ proc insertID*(db: DbConn, query: SqlQuery,
|
|||
|
||||
proc execAffectedRows*(db: DbConn, query: SqlQuery,
|
||||
args: varargs[string, `$`]): int64 {.tags: [
|
||||
FReadDB, FWriteDb].} =
|
||||
ReadDbEffect, WriteDbEffect].} =
|
||||
## executes the query (typically "UPDATE") and returns the
|
||||
## number of affected rows.
|
||||
var q = dbFormat(query, args)
|
||||
|
|
@ -332,7 +319,7 @@ proc execAffectedRows*(db: DbConn, query: SqlQuery,
|
|||
|
||||
proc execAffectedRows*(db: DbConn, stmtName: SqlPrepared,
|
||||
args: varargs[string, `$`]): int64 {.tags: [
|
||||
FReadDB, FWriteDb].} =
|
||||
ReadDbEffect, WriteDbEffect].} =
|
||||
## executes the query (typically "UPDATE") and returns the
|
||||
## number of affected rows.
|
||||
var arr = allocCStringArray(args)
|
||||
|
|
@ -343,12 +330,12 @@ proc execAffectedRows*(db: DbConn, stmtName: SqlPrepared,
|
|||
result = parseBiggestInt($pqcmdTuples(res))
|
||||
pqclear(res)
|
||||
|
||||
proc close*(db: DbConn) {.tags: [FDb].} =
|
||||
proc close*(db: DbConn) {.tags: [DbEffect].} =
|
||||
## closes the database connection.
|
||||
if db != nil: pqfinish(db)
|
||||
|
||||
proc open*(connection, user, password, database: string): DbConn {.
|
||||
tags: [FDb].} =
|
||||
tags: [DbEffect].} =
|
||||
## opens a database connection. Raises `EDb` if the connection could not
|
||||
## be established.
|
||||
##
|
||||
|
|
@ -370,7 +357,7 @@ proc open*(connection, user, password, database: string): DbConn {.
|
|||
if pqStatus(result) != CONNECTION_OK: dbError(result) # result = nil
|
||||
|
||||
proc setEncoding*(connection: DbConn, encoding: string): bool {.
|
||||
tags: [FDb].} =
|
||||
tags: [DbEffect].} =
|
||||
## sets the encoding of a database connection, returns true for
|
||||
## success, false for failure.
|
||||
return pqsetClientEncoding(connection, encoding) == 0
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
#
|
||||
#
|
||||
# Nim's Runtime Library
|
||||
# (c) Copyright 2012 Andreas Rumpf
|
||||
# (c) Copyright 2015 Andreas Rumpf
|
||||
#
|
||||
# See the file "copying.txt", included in this
|
||||
# distribution, for details about the copyright.
|
||||
|
|
@ -40,47 +40,30 @@
|
|||
##
|
||||
## theDb.close()
|
||||
|
||||
{.deadCodeElim:on.}
|
||||
|
||||
import strutils, sqlite3
|
||||
|
||||
import db_common
|
||||
export db_common
|
||||
|
||||
type
|
||||
DbConn* = PSqlite3 ## encapsulates a database connection
|
||||
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
|
||||
## 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
|
||||
|
||||
FDb* = object of IOEffect ## effect that denotes a database operation
|
||||
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
|
||||
proc dbError*(db: DbConn) {.noreturn.} =
|
||||
## raises a DbError exception.
|
||||
var e: ref DbError
|
||||
new(e)
|
||||
e.msg = $sqlite3.errmsg(db)
|
||||
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.
|
||||
if s.isNil: return "NULL"
|
||||
result = "'"
|
||||
for c in items(s):
|
||||
|
|
@ -99,7 +82,8 @@ proc dbFormat(formatstr: SqlQuery, args: varargs[string]): string =
|
|||
add(result, c)
|
||||
|
||||
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.
|
||||
var q = dbFormat(query, args)
|
||||
var stmt: sqlite3.Pstmt
|
||||
|
|
@ -108,8 +92,8 @@ proc tryExec*(db: DbConn, query: SqlQuery,
|
|||
result = finalize(stmt) == SQLITE_OK
|
||||
|
||||
proc exec*(db: DbConn, query: SqlQuery, args: varargs[string, `$`]) {.
|
||||
tags: [FReadDb, FWriteDb].} =
|
||||
## executes the query and raises EDB if not successful.
|
||||
tags: [ReadDbEffect, WriteDbEffect].} =
|
||||
## executes the query and raises DbError if not successful.
|
||||
if not tryExec(db, query, args): dbError(db)
|
||||
|
||||
proc newRow(L: int): Row =
|
||||
|
|
@ -129,14 +113,14 @@ proc setRow(stmt: Pstmt, r: var Row, cols: cint) =
|
|||
if not isNil(x): add(r[col], x)
|
||||
|
||||
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 fast, but potentially dangerous. Use this iterator only
|
||||
## if you require **ALL** the rows.
|
||||
##
|
||||
## 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 L = (column_count(stmt))
|
||||
var result = newRow(L)
|
||||
|
|
@ -147,14 +131,47 @@ iterator fastRows*(db: DbConn, query: SqlQuery,
|
|||
|
||||
iterator instantRows*(db: DbConn, query: SqlQuery,
|
||||
args: varargs[string, `$`]): InstantRow
|
||||
{.tags: [FReadDb].} =
|
||||
{.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 interator body.
|
||||
## on demand using []. Returned handle is valid only within the iterator body.
|
||||
var stmt = setupQuery(db, query, args)
|
||||
while step(stmt) == SQLITE_ROW:
|
||||
yield stmt
|
||||
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.} =
|
||||
## returns text for given column of the row
|
||||
$column_text(row, col)
|
||||
|
|
@ -164,7 +181,7 @@ proc len*(row: InstantRow): int32 {.inline.} =
|
|||
column_count(row)
|
||||
|
||||
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
|
||||
## will return a Row with empty strings for each column.
|
||||
var stmt = setupQuery(db, query, args)
|
||||
|
|
@ -175,19 +192,19 @@ proc getRow*(db: DbConn, query: SqlQuery,
|
|||
if finalize(stmt) != SQLITE_OK: dbError(db)
|
||||
|
||||
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.
|
||||
result = @[]
|
||||
for r in fastRows(db, query, args):
|
||||
result.add(r)
|
||||
|
||||
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.
|
||||
for r in fastRows(db, query, args): yield r
|
||||
|
||||
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
|
||||
## result dataset. Returns "" if the dataset contains no rows or the database
|
||||
## value is NULL.
|
||||
|
|
@ -205,7 +222,7 @@ proc getValue*(db: DbConn, query: SqlQuery,
|
|||
|
||||
proc tryInsertID*(db: DbConn, query: SqlQuery,
|
||||
args: varargs[string, `$`]): int64
|
||||
{.tags: [FWriteDb], raises: [].} =
|
||||
{.tags: [WriteDbEffect], raises: [].} =
|
||||
## executes the query (typically "INSERT") and returns the
|
||||
## generated ID for the row or -1 in case of an error.
|
||||
var q = dbFormat(query, args)
|
||||
|
|
@ -218,7 +235,7 @@ proc tryInsertID*(db: DbConn, query: SqlQuery,
|
|||
result = -1
|
||||
|
||||
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
|
||||
## generated ID for the row. For Postgre this adds
|
||||
## ``RETURNING id`` to the query, so it only works if your primary key is
|
||||
|
|
@ -228,18 +245,18 @@ proc insertID*(db: DbConn, query: SqlQuery,
|
|||
|
||||
proc execAffectedRows*(db: DbConn, query: SqlQuery,
|
||||
args: varargs[string, `$`]): int64 {.
|
||||
tags: [FReadDb, FWriteDb].} =
|
||||
tags: [ReadDbEffect, WriteDbEffect].} =
|
||||
## executes the query (typically "UPDATE") and returns the
|
||||
## number of affected rows.
|
||||
exec(db, query, args)
|
||||
result = changes(db)
|
||||
|
||||
proc close*(db: DbConn) {.tags: [FDb].} =
|
||||
proc close*(db: DbConn) {.tags: [DbEffect].} =
|
||||
## closes the database connection.
|
||||
if sqlite3.close(db) != SQLITE_OK: dbError(db)
|
||||
|
||||
proc open*(connection, user, password, database: string): DbConn {.
|
||||
tags: [FDb].} =
|
||||
tags: [DbEffect].} =
|
||||
## opens a database connection. Raises `EDb` if the connection could not
|
||||
## be established. Only the ``connection`` parameter is used for ``sqlite``.
|
||||
var db: DbConn
|
||||
|
|
@ -249,7 +266,7 @@ proc open*(connection, user, password, database: string): DbConn {.
|
|||
dbError(db)
|
||||
|
||||
proc setEncoding*(connection: DbConn, encoding: string): bool {.
|
||||
tags: [FDb].} =
|
||||
tags: [DbEffect].} =
|
||||
## sets the encoding of a database connection, returns true for
|
||||
## success, false for failure.
|
||||
##
|
||||
|
|
|
|||
|
|
@ -418,10 +418,6 @@ typedef int assert_numbits[sizeof(NI) == sizeof(void*) && NIM_INTBITS == sizeof(
|
|||
# define NIM_EXTERNC
|
||||
#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 ----------------------- */
|
||||
|
||||
/* VxWorks related includes */
|
||||
|
|
|
|||
|
|
@ -173,7 +173,41 @@ proc nimNextToken(g: var GeneralTokenizer) =
|
|||
while g.buf[pos] in {' ', '\x09'..'\x0D'}: inc(pos)
|
||||
of '#':
|
||||
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':
|
||||
var id = ""
|
||||
while g.buf[pos] in SymChars + {'_'}:
|
||||
|
|
|
|||
|
|
@ -534,7 +534,7 @@ proc generateDocumentationJumps(docs: IndexedDocs): string =
|
|||
for title in titles:
|
||||
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 =
|
||||
## 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:
|
||||
chunks.add("<a href=\"" & name & ".html\">" & name & "</a>")
|
||||
|
||||
result.add(chunks.join(", ") & ".<br>")
|
||||
result.add(chunks.join(", ") & ".<br/>")
|
||||
|
||||
proc readIndexDir(dir: string):
|
||||
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>".}
|
||||
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 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 setegid*(a1: Gid): cint {.importc, header: "<unistd.h>".}
|
||||
|
|
|
|||
|
|
@ -288,7 +288,7 @@ proc defaultOnProgressChanged*(total, progress: BiggestInt,
|
|||
result.complete()
|
||||
|
||||
proc retrFile*(ftp: AsyncFtpClient, file, dest: string,
|
||||
onProgressChanged = defaultOnProgressChanged) {.async.} =
|
||||
onProgressChanged: ProgressChangedProc = defaultOnProgressChanged) {.async.} =
|
||||
## Downloads ``file`` and saves it to ``dest``.
|
||||
## The ``EvRetr`` event is passed to the specified ``handleEvent`` function
|
||||
## 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
|
||||
|
||||
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
|
||||
## function asynchronously is recommended to view the progress of
|
||||
## the download.
|
||||
|
|
|
|||
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.}) =
|
||||
## 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]:
|
||||
handler.handlers.del(i)
|
||||
break
|
||||
|
|
|
|||
|
|
@ -110,7 +110,7 @@ type
|
|||
EInvalidProtocol: ProtocolError, EHttpRequestErr: HttpRequestError
|
||||
].}
|
||||
|
||||
const defUserAgent* = "Nim httpclient/0.1"
|
||||
const defUserAgent* = "Nim httpclient/" & NimVersion
|
||||
|
||||
proc httpError(msg: string) =
|
||||
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
|
||||
## server takes longer than specified an ETimeout exception will be raised.
|
||||
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"))
|
||||
# TODO: Use generateHeaders further down once it supports proxies.
|
||||
if proxy == nil:
|
||||
|
|
@ -402,10 +403,10 @@ proc request*(url: string, httpMethod: string, extraHeaders = "",
|
|||
|
||||
headers.add(" HTTP/1.1\c\L")
|
||||
|
||||
if r.port == "":
|
||||
add(headers, "Host: " & r.hostname & "\c\L")
|
||||
if hostUrl.port == "":
|
||||
add(headers, "Host: " & hostUrl.hostname & "\c\L")
|
||||
else:
|
||||
add(headers, "Host: " & r.hostname & ":" & r.port & "\c\L")
|
||||
add(headers, "Host: " & hostUrl.hostname & ":" & hostUrl.port & "\c\L")
|
||||
|
||||
if userAgent != "":
|
||||
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, extraHeaders)
|
||||
add(headers, "\c\L")
|
||||
|
||||
var s = newSocket()
|
||||
if s == nil: raiseOSError(osLastError())
|
||||
var port = net.Port(80)
|
||||
|
|
|
|||
|
|
@ -28,7 +28,10 @@ type
|
|||
BaseLexer* = object of RootObj ## the base lexer. Inherit your lexer from
|
||||
## this object.
|
||||
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
|
||||
input: Stream ## the input stream
|
||||
lineNumber*: int ## the current line number
|
||||
|
|
@ -43,7 +46,8 @@ const
|
|||
|
||||
proc close*(L: var BaseLexer) =
|
||||
## closes the base lexer. This closes `L`'s associated stream too.
|
||||
dealloc(L.buf)
|
||||
when not defined(js):
|
||||
dealloc(L.buf)
|
||||
close(L.input)
|
||||
|
||||
proc fillBuffer(L: var BaseLexer) =
|
||||
|
|
@ -58,8 +62,11 @@ proc fillBuffer(L: var BaseLexer) =
|
|||
toCopy = L.bufLen - L.sentinel - 1
|
||||
assert(toCopy >= 0)
|
||||
if toCopy > 0:
|
||||
moveMem(L.buf, addr(L.buf[L.sentinel + 1]), toCopy * chrSize)
|
||||
# "moveMem" handles overlapping regions
|
||||
when defined(js):
|
||||
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]),
|
||||
(L.sentinel + 1) * chrSize) div chrSize
|
||||
s = toCopy + charsRead
|
||||
|
|
@ -81,7 +88,10 @@ proc fillBuffer(L: var BaseLexer) =
|
|||
# double the buffer's size and try again:
|
||||
oldBufLen = L.bufLen
|
||||
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)
|
||||
charsRead = readData(L.input, addr(L.buf[oldBufLen]),
|
||||
oldBufLen * chrSize) div chrSize
|
||||
|
|
@ -139,7 +149,10 @@ proc open*(L: var BaseLexer, input: Stream, bufLen: int = 8192;
|
|||
L.bufpos = 0
|
||||
L.bufLen = bufLen
|
||||
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.lineStart = 0
|
||||
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.
|
||||
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.}
|
||||
## Returns a random number in the range 0..max-1. The sequence of
|
||||
## 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`.
|
||||
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.}
|
||||
|
||||
|
|
|
|||
|
|
@ -206,8 +206,9 @@ proc getAddrInfo*(address: string, port: Port, domain: Domain = AF_INET,
|
|||
# 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
|
||||
when not defined(freebsd) or defined(openbsd):
|
||||
hints.ai_flags = AI_V4MAPPED
|
||||
when not defined(freebsd) and not defined(openbsd) and not defined(netbsd):
|
||||
if domain == AF_INET6:
|
||||
hints.ai_flags = AI_V4MAPPED
|
||||
var gaiResult = getaddrinfo(address, $port, addr(hints), result)
|
||||
if gaiResult != 0'i32:
|
||||
when useWinVersion:
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
#
|
||||
#
|
||||
# Nim's Runtime Library
|
||||
# (c) Copyright 2012 Andreas Rumpf
|
||||
# (c) Copyright 2015 Andreas Rumpf
|
||||
#
|
||||
# See the file "copying.txt", included in this
|
||||
# distribution, for details about the copyright.
|
||||
|
|
@ -117,24 +117,38 @@ when defined(memProfiler):
|
|||
var
|
||||
gTicker {.threadvar.}: int
|
||||
|
||||
proc hook(st: StackTrace, size: int) {.nimcall.} =
|
||||
proc requestedHook(): bool {.nimcall.} =
|
||||
if gTicker == 0:
|
||||
gTicker = -1
|
||||
when defined(ignoreAllocationSize):
|
||||
hookAux(st, 1)
|
||||
else:
|
||||
hookAux(st, size)
|
||||
gTicker = SamplingInterval
|
||||
result = true
|
||||
dec gTicker
|
||||
|
||||
proc hook(st: StackTrace, size: int) {.nimcall.} =
|
||||
when defined(ignoreAllocationSize):
|
||||
hookAux(st, 1)
|
||||
else:
|
||||
hookAux(st, size)
|
||||
|
||||
else:
|
||||
var
|
||||
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.} =
|
||||
#echo "profiling! ", interval
|
||||
if interval == 0:
|
||||
hookAux(st, 1)
|
||||
elif int64(t0) == 0 or getTicks() - t0 > interval:
|
||||
else:
|
||||
hookAux(st, 1)
|
||||
t0 = getTicks()
|
||||
|
||||
|
|
@ -145,9 +159,10 @@ proc cmpEntries(a, b: ptr ProfileEntry): int =
|
|||
result = b.getTotal - a.getTotal
|
||||
|
||||
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.} =
|
||||
system.profilingRequestedHook = nil
|
||||
when declared(system.StackTrace):
|
||||
system.profilerHook = nil
|
||||
const filename = "profile_results.txt"
|
||||
|
|
@ -193,14 +208,15 @@ var
|
|||
proc disableProfiling*() =
|
||||
when declared(system.StackTrace):
|
||||
atomicDec disabled
|
||||
system.profilerHook = nil
|
||||
system.profilingRequestedHook = nil
|
||||
|
||||
proc enableProfiling*() =
|
||||
when declared(system.StackTrace):
|
||||
if atomicInc(disabled) >= 0:
|
||||
system.profilerHook = hook
|
||||
system.profilingRequestedHook = requestedHook
|
||||
|
||||
when declared(system.StackTrace):
|
||||
system.profilingRequestedHook = requestedHook
|
||||
system.profilerHook = hook
|
||||
addQuitProc(writeProfile)
|
||||
|
||||
|
|
|
|||
|
|
@ -886,7 +886,7 @@ elif not defined(useNimRtl):
|
|||
discard write(data.pErrorPipe[writeIdx], addr error, sizeof(error))
|
||||
exitnow(1)
|
||||
|
||||
when defined(macosx) or defined(freebsd):
|
||||
when defined(macosx) or defined(freebsd) or defined(netbsd) or defined(android):
|
||||
var environ {.importc.}: cstringArray
|
||||
|
||||
proc startProcessAfterFork(data: ptr StartProcessData) =
|
||||
|
|
@ -916,7 +916,7 @@ elif not defined(useNimRtl):
|
|||
discard fcntl(data.pErrorPipe[writeIdx], F_SETFD, FD_CLOEXEC)
|
||||
|
||||
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.
|
||||
# On MacOSX we can arrive here only from fork, so this is safe:
|
||||
environ = data.sysEnv
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ const
|
|||
proc toLower(c: char): char {.inline.} =
|
||||
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.} =
|
||||
## 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)
|
||||
## 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 foundDigit = false
|
||||
if s[i] == '0' and (s[i+1] == 'x' or s[i+1] == 'X'): inc(i, 2)
|
||||
elif s[i] == '#': inc(i)
|
||||
while true:
|
||||
let last = if maxLen == 0: s.len else: i+maxLen
|
||||
while i < last:
|
||||
case s[i]
|
||||
of '_': discard
|
||||
of '0'..'9':
|
||||
|
|
|
|||
1096
lib/pure/redis.nim
1096
lib/pure/redis.nim
File diff suppressed because it is too large
Load diff
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
|
||||
## ValueError exception will be raised.
|
||||
result = newStringOfCap(s.len)
|
||||
var i = 0
|
||||
var i = prefix.len
|
||||
if not s.startsWith(prefix):
|
||||
raise newException(ValueError,
|
||||
"String does not start with a prefix of: " & prefix)
|
||||
inc(i)
|
||||
while true:
|
||||
if i == s.len-suffix.len: break
|
||||
case s[i]
|
||||
of '\\':
|
||||
case s[i+1]:
|
||||
of 'x':
|
||||
inc i
|
||||
inc i, 2
|
||||
var c: int
|
||||
i += parseutils.parseHex(s, c, i)
|
||||
i += parseutils.parseHex(s, c, i, maxLen=2)
|
||||
result.add(chr(c))
|
||||
inc(i, 2)
|
||||
dec i, 2
|
||||
of '\\':
|
||||
result.add('\\')
|
||||
of '\'':
|
||||
|
|
@ -1721,3 +1720,4 @@ when isMainModule:
|
|||
doAssert isUpper("ABC")
|
||||
doAssert(not isUpper("AAcc"))
|
||||
doAssert(not isUpper("A#$"))
|
||||
doAssert(unescape(r"\x013", "", "") == "\x013")
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@
|
|||
## echo "epochTime() float value: ", epochTime()
|
||||
## echo "getTime() float value: ", toSeconds(getTime())
|
||||
## 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)
|
||||
|
||||
{.push debugger:off.} # the user does not want to trace a part
|
||||
|
|
@ -171,11 +171,6 @@ type
|
|||
{.deprecated: [TMonth: Month, TWeekDay: WeekDay, TTime: Time,
|
||||
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.}
|
||||
## gets the current calendar time as a UNIX epoch value (number of seconds
|
||||
## 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,
|
||||
years: int = 0): TimeInterval =
|
||||
## creates a new ``TimeInterval``.
|
||||
result.milliseconds = milliseconds
|
||||
result.seconds = seconds
|
||||
result.minutes = minutes
|
||||
result.hours = hours
|
||||
result.days = days
|
||||
result.months = months
|
||||
result.years = years
|
||||
##
|
||||
## You can also use the convenience procedures called ``milliseconds``,
|
||||
## ``seconds``, ``minutes``, ``hours``, ``days``, ``months``, and ``years``.
|
||||
##
|
||||
## Example:
|
||||
##
|
||||
## .. 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 =
|
||||
## returns true if ``year`` is a leap year
|
||||
|
|
@ -288,13 +329,22 @@ proc toSeconds(a: TimeInfo, interval: TimeInterval): float =
|
|||
|
||||
newinterv.months += interval.years * 12
|
||||
var curMonth = anew.month
|
||||
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()
|
||||
if newinterv.months < 0: # subtracting
|
||||
for mth in countDown(-1 * newinterv.months, 1):
|
||||
result -= float(getDaysInMonth(curMonth, anew.year) * 24 * 60 * 60)
|
||||
if curMonth == mJan:
|
||||
curMonth = mDec
|
||||
anew.year.dec()
|
||||
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.hours * 60 * 60)
|
||||
result += float(newinterv.minutes * 60)
|
||||
|
|
@ -302,28 +352,39 @@ proc toSeconds(a: TimeInfo, interval: TimeInterval): float =
|
|||
result += newinterv.milliseconds / 1000
|
||||
|
||||
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
|
||||
## very accurate.
|
||||
let t = toSeconds(timeInfoToTime(a))
|
||||
let secs = toSeconds(a, interval)
|
||||
#if a.tzname == "UTC":
|
||||
# result = getGMTime(fromSeconds(t + secs))
|
||||
#else:
|
||||
result = getLocalTime(fromSeconds(t + secs))
|
||||
|
||||
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
|
||||
## when you subtract so much that you reach the Julian calendar.
|
||||
let t = toSeconds(timeInfoToTime(a))
|
||||
let secs = toSeconds(a, interval)
|
||||
#if a.tzname == "UTC":
|
||||
# result = getGMTime(fromSeconds(t - secs))
|
||||
#else:
|
||||
result = getLocalTime(fromSeconds(t - secs))
|
||||
var intval: TimeInterval
|
||||
intval.milliseconds = - interval.milliseconds
|
||||
intval.seconds = - interval.seconds
|
||||
intval.minutes = - interval.minutes
|
||||
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):
|
||||
proc epochTime*(): float {.rtl, extern: "nt$1", tags: [TimeEffect].}
|
||||
|
|
@ -603,6 +664,69 @@ proc `$`*(m: Month): string =
|
|||
"November", "December"]
|
||||
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) =
|
||||
## Helper of the format proc to parse individual tokens.
|
||||
##
|
||||
|
|
|
|||
|
|
@ -840,7 +840,7 @@ proc `div` *(x, y: int32): int32 {.magic: "DivI", noSideEffect.}
|
|||
## 1 div 2 == 0
|
||||
## 2 div 2 == 1
|
||||
## 3 div 2 == 1
|
||||
## 7 div 5 == 2
|
||||
## 7 div 5 == 1
|
||||
|
||||
when defined(nimnomagic64):
|
||||
proc `div` *(x, y: int64): int64 {.magic: "DivI", noSideEffect.}
|
||||
|
|
|
|||
|
|
@ -68,9 +68,10 @@ when defined(posix):
|
|||
|
||||
proc nimLoadLibrary(path: string): LibHandle =
|
||||
result = dlopen(path, RTLD_NOW)
|
||||
let error = dlerror()
|
||||
if error != nil:
|
||||
c_fprintf(c_stdout, "%s\n", error)
|
||||
when defined(nimDebugDlOpen):
|
||||
let error = dlerror()
|
||||
if error != nil:
|
||||
c_fprintf(c_stdout, "%s\n", error)
|
||||
|
||||
proc nimGetProcAddr(lib: LibHandle, name: cstring): ProcAddr =
|
||||
result = dlsym(lib, name)
|
||||
|
|
|
|||
|
|
@ -533,15 +533,20 @@ proc nimCopy(dest, src: JSRef, ti: PNimType): JSRef =
|
|||
nimCopyAux(result, src, ti.node)
|
||||
of tySequence, tyArrayConstr, tyOpenArray, tyArray:
|
||||
asm """
|
||||
if (`dest` === null || `dest` === undefined) {
|
||||
`dest` = new Array(`src`.length);
|
||||
if (`src` === null) {
|
||||
`result` = null;
|
||||
}
|
||||
else {
|
||||
`dest`.length = `src`.length;
|
||||
}
|
||||
`result` = `dest`;
|
||||
for (var i = 0; i < `src`.length; ++i) {
|
||||
`result`[i] = nimCopy(`result`[i], `src`[i], `ti`.base);
|
||||
if (`dest` === null || `dest` === undefined) {
|
||||
`dest` = new Array(`src`.length);
|
||||
}
|
||||
else {
|
||||
`dest`.length = `src`.length;
|
||||
}
|
||||
`result` = `dest`;
|
||||
for (var i = 0; i < `src`.length; ++i) {
|
||||
`result`[i] = nimCopy(`result`[i], `src`[i], `ti`.base);
|
||||
}
|
||||
}
|
||||
"""
|
||||
of tyString:
|
||||
|
|
|
|||
|
|
@ -242,7 +242,7 @@ template task*(name: untyped; description: string; body: untyped): untyped =
|
|||
## .. code-block:: nim
|
||||
## task build, "default build is via the C backend":
|
||||
## setCommand "c"
|
||||
proc `name Task`() = body
|
||||
proc `name Task`*() = body
|
||||
|
||||
let cmd = getCommand()
|
||||
if cmd.len == 0 or cmd ==? "help":
|
||||
|
|
|
|||
|
|
@ -50,10 +50,15 @@ proc captureStackTrace(f: PFrame, st: var StackTrace) =
|
|||
inc(i)
|
||||
b = b.prev
|
||||
|
||||
var
|
||||
profilingRequestedHook*: proc (): bool {.nimcall, benign.}
|
||||
## set this variable to provide a procedure that implements a profiler in
|
||||
## user space. See the `nimprof` module for a reference implementation.
|
||||
|
||||
when defined(memProfiler):
|
||||
type
|
||||
MemProfilerHook* = proc (st: StackTrace, requestedSize: int) {.nimcall, benign.}
|
||||
{.deprecated: [TMemProfilerHook: MemProfilerHook].}
|
||||
|
||||
var
|
||||
profilerHook*: MemProfilerHook
|
||||
## set this variable to provide a procedure that implements a profiler in
|
||||
|
|
@ -65,17 +70,13 @@ when defined(memProfiler):
|
|||
hook(st, requestedSize)
|
||||
|
||||
proc nimProfile(requestedSize: int) =
|
||||
if not isNil(profilerHook):
|
||||
if not isNil(profilingRequestedHook) and profilingRequestedHook():
|
||||
callProfilerHook(profilerHook, requestedSize)
|
||||
else:
|
||||
const
|
||||
SamplingInterval = 50_000
|
||||
# set this to change the default sampling interval
|
||||
var
|
||||
profilerHook*: ProfilerHook
|
||||
## set this variable to provide a procedure that implements a profiler in
|
||||
## user space. See the `nimprof` module for a reference implementation.
|
||||
gTicker {.threadvar.}: int
|
||||
|
||||
proc callProfilerHook(hook: ProfilerHook) {.noinline.} =
|
||||
# 'noinline' so that 'nimProfile' does not perform the stack allocation
|
||||
|
|
@ -86,16 +87,7 @@ else:
|
|||
|
||||
proc nimProfile() =
|
||||
## This is invoked by the compiler in every loop and on every proc entry!
|
||||
if gTicker == 0:
|
||||
gTicker = -1
|
||||
if not isNil(profilerHook):
|
||||
# disable recursive calls: XXX should use try..finally,
|
||||
# but that's too expensive!
|
||||
let oldHook = profilerHook
|
||||
profilerHook = nil
|
||||
callProfilerHook(oldHook)
|
||||
profilerHook = oldHook
|
||||
gTicker = SamplingInterval
|
||||
dec gTicker
|
||||
if not isNil(profilingRequestedHook) and profilingRequestedHook():
|
||||
callProfilerHook(profilerHook)
|
||||
|
||||
{.pop.}
|
||||
|
|
|
|||
|
|
@ -259,8 +259,10 @@ when not defined(useNimRtl):
|
|||
of tyInt16: add result, $int(cast[ptr int16](p)[])
|
||||
of tyInt32: add result, $int(cast[ptr int32](p)[])
|
||||
of tyInt64: add result, $(cast[ptr int64](p)[])
|
||||
of tyUInt8: add result, $ze(cast[ptr int8](p)[])
|
||||
of tyUInt16: add result, $ze(cast[ptr int16](p)[])
|
||||
of tyUInt8: add result, $(cast[ptr uint8](p)[])
|
||||
of tyUInt16: add result, $(cast[ptr uint16](p)[])
|
||||
of tyUInt32: add result, $(cast[ptr uint32](p)[])
|
||||
of tyUInt64: add result, $(cast[ptr uint64](p)[])
|
||||
|
||||
of tyFloat: add result, $(cast[ptr float](p)[])
|
||||
of tyFloat32: add result, $(cast[ptr float32](p)[])
|
||||
|
|
|
|||
|
|
@ -139,7 +139,7 @@ struct linenoiseState {
|
|||
int ofd; /* Terminal stdout file descriptor. */
|
||||
char *buf; /* Edited line buffer. */
|
||||
size_t buflen; /* Edited line buffer size. */
|
||||
const char *prompt; /* Prompt to display. */
|
||||
char *prompt; /* Prompt to display. */
|
||||
size_t plen; /* Prompt length. */
|
||||
size_t pos; /* Current cursor position. */
|
||||
size_t oldpos; /* Previous refresh cursor position. */
|
||||
|
|
@ -172,7 +172,7 @@ enum KEY_ACTION{
|
|||
};
|
||||
|
||||
static void linenoiseAtExit(void);
|
||||
int linenoiseHistoryAdd(const char *line);
|
||||
int linenoiseHistoryAdd(char *line);
|
||||
static void refreshLine(struct linenoiseState *l);
|
||||
|
||||
/* Debugging macro. */
|
||||
|
|
@ -413,14 +413,14 @@ void linenoiseSetCompletionCallback(linenoiseCompletionCallback *fn) {
|
|||
* in order to add completion options given the input string when the
|
||||
* user typed <tab>. See the example.c source code for a very easy to
|
||||
* understand example. */
|
||||
void linenoiseAddCompletion(linenoiseCompletions *lc, const char *str) {
|
||||
void linenoiseAddCompletion(linenoiseCompletions *lc, char *str) {
|
||||
size_t len = strlen(str);
|
||||
char *copy, **cvec;
|
||||
|
||||
copy = malloc(len+1);
|
||||
copy = (char*)malloc(len+1);
|
||||
if (copy == NULL) return;
|
||||
memcpy(copy,str,len+1);
|
||||
cvec = realloc(lc->cvec,sizeof(char*)*(lc->len+1));
|
||||
cvec = (char**)realloc(lc->cvec,sizeof(char*)*(lc->len+1));
|
||||
if (cvec == NULL) {
|
||||
free(copy);
|
||||
return;
|
||||
|
|
@ -445,12 +445,12 @@ static void abInit(struct abuf *ab) {
|
|||
ab->len = 0;
|
||||
}
|
||||
|
||||
static void abAppend(struct abuf *ab, const char *s, int len) {
|
||||
char *new = realloc(ab->b,ab->len+len);
|
||||
static void abAppend(struct abuf *ab, char *s, int len) {
|
||||
char *neww = (char*)realloc(ab->b,ab->len+len);
|
||||
|
||||
if (new == NULL) return;
|
||||
memcpy(new+ab->len,s,len);
|
||||
ab->b = new;
|
||||
if (neww == NULL) return;
|
||||
memcpy(neww+ab->len,s,len);
|
||||
ab->b = neww;
|
||||
ab->len += len;
|
||||
}
|
||||
|
||||
|
|
@ -723,7 +723,7 @@ void linenoiseEditDeletePrevWord(struct linenoiseState *l) {
|
|||
* when ctrl+d is typed.
|
||||
*
|
||||
* The function returns the length of the current buffer. */
|
||||
static int linenoiseEdit(int stdin_fd, int stdout_fd, char *buf, size_t buflen, const char *prompt)
|
||||
static int linenoiseEdit(int stdin_fd, int stdout_fd, char *buf, size_t buflen, char *prompt)
|
||||
{
|
||||
struct linenoiseState l;
|
||||
|
||||
|
|
@ -929,7 +929,7 @@ void linenoisePrintKeyCodes(void) {
|
|||
|
||||
/* This function calls the line editing function linenoiseEdit() using
|
||||
* the STDIN file descriptor set in raw mode. */
|
||||
static int linenoiseRaw(char *buf, size_t buflen, const char *prompt) {
|
||||
static int linenoiseRaw(char *buf, size_t buflen, char *prompt) {
|
||||
int count;
|
||||
|
||||
if (buflen == 0) {
|
||||
|
|
@ -959,7 +959,7 @@ static int linenoiseRaw(char *buf, size_t buflen, const char *prompt) {
|
|||
* for a blacklist of stupid terminals, and later either calls the line
|
||||
* editing function or uses dummy fgets() so that you will be able to type
|
||||
* something even in the most desperate of the conditions. */
|
||||
char *linenoise(const char *prompt) {
|
||||
char *linenoise(char *prompt) {
|
||||
char buf[LINENOISE_MAX_LINE];
|
||||
int count;
|
||||
|
||||
|
|
@ -1009,14 +1009,14 @@ static void linenoiseAtExit(void) {
|
|||
* histories, but will work well for a few hundred of entries.
|
||||
*
|
||||
* Using a circular buffer is smarter, but a bit more complex to handle. */
|
||||
int linenoiseHistoryAdd(const char *line) {
|
||||
int linenoiseHistoryAdd(char *line) {
|
||||
char *linecopy;
|
||||
|
||||
if (history_max_len == 0) return 0;
|
||||
|
||||
/* Initialization on first call. */
|
||||
if (history == NULL) {
|
||||
history = malloc(sizeof(char*)*history_max_len);
|
||||
history = (char**)malloc(sizeof(char*)*history_max_len);
|
||||
if (history == NULL) return 0;
|
||||
memset(history,0,(sizeof(char*)*history_max_len));
|
||||
}
|
||||
|
|
@ -1043,14 +1043,14 @@ int linenoiseHistoryAdd(const char *line) {
|
|||
* just the latest 'len' elements if the new history length value is smaller
|
||||
* than the amount of items already inside the history. */
|
||||
int linenoiseHistorySetMaxLen(int len) {
|
||||
char **new;
|
||||
char **neww;
|
||||
|
||||
if (len < 1) return 0;
|
||||
if (history) {
|
||||
int tocopy = history_len;
|
||||
|
||||
new = malloc(sizeof(char*)*len);
|
||||
if (new == NULL) return 0;
|
||||
neww = (char**)malloc(sizeof(char*)*len);
|
||||
if (neww == NULL) return 0;
|
||||
|
||||
/* If we can't copy everything, free the elements we'll not use. */
|
||||
if (len < tocopy) {
|
||||
|
|
@ -1059,10 +1059,10 @@ int linenoiseHistorySetMaxLen(int len) {
|
|||
for (j = 0; j < tocopy-len; j++) free(history[j]);
|
||||
tocopy = len;
|
||||
}
|
||||
memset(new,0,sizeof(char*)*len);
|
||||
memcpy(new,history+(history_len-tocopy), sizeof(char*)*tocopy);
|
||||
memset(neww,0,sizeof(char*)*len);
|
||||
memcpy(neww,history+(history_len-tocopy), sizeof(char*)*tocopy);
|
||||
free(history);
|
||||
history = new;
|
||||
history = neww;
|
||||
}
|
||||
history_max_len = len;
|
||||
if (history_len > history_max_len)
|
||||
|
|
@ -1072,7 +1072,7 @@ int linenoiseHistorySetMaxLen(int len) {
|
|||
|
||||
/* Save the history in the specified file. On success 0 is returned
|
||||
* otherwise -1 is returned. */
|
||||
int linenoiseHistorySave(const char *filename) {
|
||||
int linenoiseHistorySave(char *filename) {
|
||||
FILE *fp = fopen(filename,"w");
|
||||
int j;
|
||||
|
||||
|
|
@ -1088,7 +1088,7 @@ int linenoiseHistorySave(const char *filename) {
|
|||
*
|
||||
* If the file exists and the operation succeeded 0 is returned, otherwise
|
||||
* on error -1 is returned. */
|
||||
int linenoiseHistoryLoad(const char *filename) {
|
||||
int linenoiseHistoryLoad(char *filename) {
|
||||
FILE *fp = fopen(filename,"r");
|
||||
char buf[LINENOISE_MAX_LINE];
|
||||
|
||||
|
|
|
|||
|
|
@ -39,30 +39,22 @@
|
|||
#ifndef __LINENOISE_H
|
||||
#define __LINENOISE_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef struct linenoiseCompletions {
|
||||
size_t len;
|
||||
char **cvec;
|
||||
} linenoiseCompletions;
|
||||
|
||||
typedef void(linenoiseCompletionCallback)(const char *, linenoiseCompletions *);
|
||||
typedef void(linenoiseCompletionCallback)(char *, linenoiseCompletions *);
|
||||
void linenoiseSetCompletionCallback(linenoiseCompletionCallback *);
|
||||
void linenoiseAddCompletion(linenoiseCompletions *, const char *);
|
||||
void linenoiseAddCompletion(linenoiseCompletions *, char *);
|
||||
|
||||
char *linenoise(const char *prompt);
|
||||
int linenoiseHistoryAdd(const char *line);
|
||||
char *linenoise(char *prompt);
|
||||
int linenoiseHistoryAdd(char *line);
|
||||
int linenoiseHistorySetMaxLen(int len);
|
||||
int linenoiseHistorySave(const char *filename);
|
||||
int linenoiseHistoryLoad(const char *filename);
|
||||
int linenoiseHistorySave(char *filename);
|
||||
int linenoiseHistoryLoad(char *filename);
|
||||
void linenoiseClearScreen(void);
|
||||
void linenoiseSetMultiLine(int ml);
|
||||
void linenoisePrintKeyCodes(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* __LINENOISE_H */
|
||||
|
|
|
|||
|
|
@ -418,6 +418,7 @@ type
|
|||
decimals*: cuint # Number of decimals in field
|
||||
charsetnr*: cuint # Character set
|
||||
ftype*: Enum_field_types # Type of field. See mysql_com.h for types
|
||||
extension*: pointer
|
||||
|
||||
FIELD* = St_mysql_field
|
||||
PFIELD* = ptr FIELD
|
||||
|
|
|
|||
|
|
@ -641,11 +641,42 @@ const
|
|||
ODBC_CONFIG_SYS_DSN* = 5
|
||||
ODBC_REMOVE_SYS_DSN* = 6
|
||||
|
||||
SQL_ACTIVE_CONNECTIONS* = 0 # SQLGetInfo
|
||||
SQL_DATA_SOURCE_NAME* = 2
|
||||
SQL_DATA_SOURCE_READ_ONLY* = 25
|
||||
SQL_DATABASE_NAME* = 2
|
||||
SQL_DBMS_NAME* = 17
|
||||
SQL_DBMS_VERSION* = 18
|
||||
SQL_DRIVER_HDBC* = 3
|
||||
SQL_DRIVER_HENV* = 4
|
||||
SQL_DRIVER_HSTMT* = 5
|
||||
SQL_DRIVER_NAME* = 6
|
||||
SQL_DRIVER_VER* = 7
|
||||
SQL_FETCH_DIRECTION* = 8
|
||||
SQL_ODBC_VER* = 10
|
||||
SQL_DRIVER_ODBC_VER* = 77
|
||||
SQL_SERVER_NAME* = 13
|
||||
SQL_ACTIVE_ENVIRONMENTS* = 116
|
||||
SQL_ACTIVE_STATEMENTS* = 1
|
||||
SQL_SQL_CONFORMANCE* = 118
|
||||
SQL_DATETIME_LITERALS* = 119
|
||||
SQL_ASYNC_MODE* = 10021
|
||||
SQL_BATCH_ROW_COUNT* = 120
|
||||
SQL_BATCH_SUPPORT* = 121
|
||||
SQL_CATALOG_LOCATION* = 114
|
||||
#SQL_CATALOG_NAME* = 10003
|
||||
SQL_CATALOG_NAME_SEPARATOR* = 41
|
||||
SQL_CATALOG_TERM* = 42
|
||||
SQL_CATALOG_USAGE* = 92
|
||||
#SQL_COLLATION_SEQ* = 10004
|
||||
SQL_COLUMN_ALIAS* = 87
|
||||
#SQL_USER_NAME* = 47
|
||||
|
||||
proc SQLAllocHandle*(HandleType: TSqlSmallInt, InputHandle: SqlHandle,
|
||||
OutputHandlePtr: var SqlHandle): TSqlSmallInt{.
|
||||
dynlib: odbclib, importc.}
|
||||
proc SQLSetEnvAttr*(EnvironmentHandle: SqlHEnv, Attribute: TSqlInteger,
|
||||
Value: SqlPointer, StringLength: TSqlInteger): TSqlSmallInt{.
|
||||
Value: TSqlInteger, StringLength: TSqlInteger): TSqlSmallInt{.
|
||||
dynlib: odbclib, importc.}
|
||||
proc SQLGetEnvAttr*(EnvironmentHandle: SqlHEnv, Attribute: TSqlInteger,
|
||||
Value: SqlPointer, BufferLength: TSqlInteger,
|
||||
|
|
@ -807,5 +838,10 @@ proc SQLStatistics*(hstmt: SqlHStmt, CatalogName: PSQLCHAR,
|
|||
NameLength3: TSqlSmallInt, Unique: SqlUSmallInt,
|
||||
Reserved: SqlUSmallInt): TSqlSmallInt {.
|
||||
dynlib: odbclib, importc.}
|
||||
proc SQLErr*(henv: SqlHEnv, hdbc: SqlHDBC, hstmt: SqlHStmt,
|
||||
szSqlState, pfNativeError, szErrorMsg: PSQLCHAR,
|
||||
cbErrorMsgMax: TSqlSmallInt,
|
||||
pcbErrorMsg: PSQLINTEGER): TSqlSmallInt {.
|
||||
dynlib: odbclib, importc: "SQLError".}
|
||||
|
||||
{.pop.}
|
||||
|
|
|
|||
|
|
@ -239,6 +239,8 @@ proc column_count*(pStmt: Pstmt): int32{.cdecl, dynlib: Lib,
|
|||
importc: "sqlite3_column_count".}
|
||||
proc column_name*(para1: Pstmt, para2: int32): cstring{.cdecl, dynlib: Lib,
|
||||
importc: "sqlite3_column_name".}
|
||||
proc column_table_name*(para1: Pstmt; para2: int32): cstring{.cdecl, dynlib: Lib,
|
||||
importc: "sqlite3_column_table_name".}
|
||||
proc column_name16*(para1: Pstmt, para2: int32): pointer{.cdecl, dynlib: Lib,
|
||||
importc: "sqlite3_column_name16".}
|
||||
proc column_decltype*(para1: Pstmt, i: int32): cstring{.cdecl, dynlib: Lib,
|
||||
|
|
|
|||
10
readme.md
10
readme.md
|
|
@ -1,6 +1,14 @@
|
|||
# Nim Compiler
|
||||
|
||||
[](https://gitter.im/nim-lang/Nim?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
|
||||
[](https://webchat.freenode.net/?channels=nim)
|
||||
[](http://forum.nim-lang.org)
|
||||
[](http://stackoverflow.com/questions/tagged/nim?sort=newest&pageSize=15)
|
||||
[](https://twitter.com/nim_lang)
|
||||
|
||||
[](https://travis-ci.org/nim-lang/Nim)
|
||||
|
||||
[](https://gratipay.com/nim/)
|
||||
[](https://www.bountysource.com/teams/nim)
|
||||
|
||||
|
||||
This repo contains the Nim compiler, Nim's stdlib, tools and
|
||||
|
|
|
|||
55
tests/async/tlambda.nim
Normal file
55
tests/async/tlambda.nim
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
|
||||
# bug 2007
|
||||
|
||||
import asyncdispatch, asyncnet, logging, json, uri, strutils, future
|
||||
|
||||
type
|
||||
Builder = ref object
|
||||
client: Client
|
||||
build: Build
|
||||
|
||||
ProgressCB* = proc (message: string): Future[void] {.closure, gcsafe.}
|
||||
|
||||
Build* = ref object
|
||||
onProgress*: ProgressCB
|
||||
|
||||
Client = ref ClientObj
|
||||
ClientObj = object
|
||||
onMessage: proc (client: Client, msg: JsonNode): Future[void]
|
||||
|
||||
proc newClient*(name: string,
|
||||
onMessage: (Client, JsonNode) -> Future[void]): Client =
|
||||
new result
|
||||
result.onMessage = onMessage
|
||||
|
||||
proc newBuild*(onProgress: ProgressCB): Build =
|
||||
new result
|
||||
result.onProgress = onProgress
|
||||
|
||||
proc start(build: Build, repo, hash: string) {.async.} =
|
||||
let path = repo.parseUri().path.toLower()
|
||||
|
||||
proc onProgress(builder: Builder, message: string) {.async.} =
|
||||
debug($message)
|
||||
|
||||
proc onMessage(builder: Builder, message: JsonNode) {.async.} =
|
||||
debug("onMessage")
|
||||
|
||||
proc newBuilder(): Builder =
|
||||
var cres: Builder
|
||||
new cres
|
||||
|
||||
cres.client = newClient("builder", (client, msg) => (onMessage(cres, msg)))
|
||||
cres.build = newBuild(
|
||||
proc (msg: string): Future[void] {.closure, gcsafe.} = onProgress(cres, msg))
|
||||
return cres
|
||||
|
||||
proc main() =
|
||||
# Set up logging.
|
||||
var console = newConsoleLogger(fmtStr = verboseFmtStr)
|
||||
addHandler(console)
|
||||
|
||||
var builder = newBuilder()
|
||||
|
||||
|
||||
main()
|
||||
17
tests/async/tnimcall_to_closure.nim
Normal file
17
tests/async/tnimcall_to_closure.nim
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
|
||||
import asyncdispatch
|
||||
|
||||
proc defaultOnProgressChanged() = discard
|
||||
|
||||
proc ask(x: proc()) = x()
|
||||
|
||||
proc retrFile*(onProgressChanged: proc() {.nimcall.}): Future[void] =
|
||||
var retFuture = newFuture[void]("retrFile")
|
||||
iterator retrFileIter(): FutureBase {.closure.} =
|
||||
ask(onProgressChanged)
|
||||
complete(retFuture)
|
||||
|
||||
var nameIterVar = retrFileIter
|
||||
return retFuture
|
||||
|
||||
discard retrFile(defaultOnProgressChanged)
|
||||
28
tests/ccgbugs/tgeneric_closure.nim
Normal file
28
tests/ccgbugs/tgeneric_closure.nim
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
|
||||
|
||||
# bug 2659
|
||||
|
||||
type
|
||||
GenProcType[T,U] = proc(x:T, y:var U)
|
||||
IntProcType = proc(x:int, y:var int)
|
||||
|
||||
proc mult(x:int, y:var int) =
|
||||
y = 2 * x
|
||||
|
||||
when isMainModule:
|
||||
|
||||
var input = 1
|
||||
var output = 0
|
||||
|
||||
var someIntProc:IntProcType = mult
|
||||
var someGenProc:GenProcType[int,int] = mult
|
||||
|
||||
mult(input, output)
|
||||
echo output
|
||||
|
||||
someIntProc(input, output)
|
||||
echo output
|
||||
|
||||
# Uncommenting causes an error in the C compiler.
|
||||
someGenProc(input, output)
|
||||
echo output
|
||||
87
tests/closure/tclosure0.nim
Normal file
87
tests/closure/tclosure0.nim
Normal file
|
|
@ -0,0 +1,87 @@
|
|||
discard """
|
||||
output: '''foo88
|
||||
23 24foo 88
|
||||
18
|
||||
18
|
||||
99
|
||||
99
|
||||
99
|
||||
99 99
|
||||
99 99
|
||||
12 99 99
|
||||
12 99 99'''
|
||||
"""
|
||||
|
||||
when true:
|
||||
# test simple closure within dummy 'main':
|
||||
proc dummy =
|
||||
proc main2(param: int) =
|
||||
var fooB = 23
|
||||
proc outer(outerParam: string) =
|
||||
var outerVar = 88
|
||||
echo outerParam, outerVar
|
||||
proc inner() =
|
||||
block Test:
|
||||
echo fooB, " ", param, outerParam, " ", outerVar
|
||||
inner()
|
||||
outer("foo")
|
||||
main2(24)
|
||||
|
||||
dummy()
|
||||
|
||||
when true:
|
||||
proc outer2(x:int) : proc(y:int):int = # curry-ed application
|
||||
return proc(y:int):int = x*y
|
||||
|
||||
var fn = outer2(6) # the closure
|
||||
echo fn(3) # it works
|
||||
|
||||
var rawP = fn.rawProc()
|
||||
var rawE = fn.rawEnv()
|
||||
|
||||
# A type to cast the function pointer into a nimcall
|
||||
type
|
||||
TimesClosure = proc(a: int, x: pointer): int {.nimcall.}
|
||||
|
||||
# Call the function with its closure
|
||||
echo cast[TimesClosure](rawP)(3, rawE)
|
||||
|
||||
when true:
|
||||
proc outer =
|
||||
var x, y: int = 99
|
||||
proc innerA = echo x
|
||||
proc innerB =
|
||||
echo y
|
||||
innerA()
|
||||
|
||||
innerA()
|
||||
innerB()
|
||||
|
||||
outer()
|
||||
|
||||
when true:
|
||||
proc indirectDep =
|
||||
var x, y: int = 99
|
||||
proc innerA = echo x, " ", y
|
||||
proc innerB =
|
||||
innerA()
|
||||
|
||||
innerA()
|
||||
innerB()
|
||||
|
||||
indirectDep()
|
||||
|
||||
when true:
|
||||
proc needlessIndirection =
|
||||
var x, y: int = 99
|
||||
proc indirection =
|
||||
var z = 12
|
||||
proc innerA = echo z, " ", x, " ", y
|
||||
proc innerB =
|
||||
innerA()
|
||||
|
||||
innerA()
|
||||
innerB()
|
||||
indirection()
|
||||
|
||||
needlessIndirection()
|
||||
|
|
@ -87,7 +87,7 @@ when true:
|
|||
proc py() {.closure.} =
|
||||
echo "py"
|
||||
|
||||
const
|
||||
let
|
||||
mapping = {
|
||||
"abc": px,
|
||||
"xyz": py
|
||||
|
|
|
|||
|
|
@ -8,8 +8,9 @@ proc main =
|
|||
for iterations in 0..50_000:
|
||||
var s: seq[proc(): string {.closure.}] = @[]
|
||||
for i in 0 .. n-1:
|
||||
let ii = i
|
||||
s.add(proc(): string = return $(ii*ii))
|
||||
(proc () =
|
||||
let ii = i
|
||||
s.add(proc(): string = return $(ii*ii)))()
|
||||
for i in 0 .. n-1:
|
||||
let val = s[i]()
|
||||
if val != $(i*i): echo "bug ", val
|
||||
|
|
|
|||
|
|
@ -19,11 +19,11 @@ proc mustRehash(length, counter: int): bool {.inline.} =
|
|||
assert(length > counter)
|
||||
result = (length * 2 < counter * 3) or (length - counter < 4)
|
||||
|
||||
proc nextTry(h, maxHash: THash): THash {.inline.} =
|
||||
proc nextTry(h, maxHash: Hash): Hash {.inline.} =
|
||||
result = ((5 * h) + 1) and maxHash
|
||||
|
||||
template rawGetImpl() {.dirty.} =
|
||||
var h: THash = hash(key) and high(t.data) # start with real hash value
|
||||
var h: Hash = hash(key) and high(t.data) # start with real hash value
|
||||
while t.data[h].slot != seEmpty:
|
||||
if t.data[h].key == key and t.data[h].slot == seFilled:
|
||||
return h
|
||||
|
|
@ -31,7 +31,7 @@ template rawGetImpl() {.dirty.} =
|
|||
result = -1
|
||||
|
||||
template rawInsertImpl() {.dirty.} =
|
||||
var h: THash = hash(key) and high(data)
|
||||
var h: Hash = hash(key) and high(data)
|
||||
while data[h].slot == seFilled:
|
||||
h = nextTry(h, high(data))
|
||||
data[h].key = key
|
||||
|
|
|
|||
15
tests/closure/tclosureinference3304.nim
Normal file
15
tests/closure/tclosureinference3304.nim
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
discard """
|
||||
output: '''@[1, 2, 5]'''
|
||||
"""
|
||||
|
||||
import future, sequtils
|
||||
|
||||
type
|
||||
List[T] = ref object
|
||||
val: T
|
||||
|
||||
proc foo[T](l: List[T]): seq[int] =
|
||||
@[1,2,3,5].filter(x => x != l.val)
|
||||
|
||||
when isMainModule:
|
||||
echo(foo(List[int](val: 3)))
|
||||
9
tests/closure/tcodegenerr1923.nim
Normal file
9
tests/closure/tcodegenerr1923.nim
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
type
|
||||
Foo[M] = proc() : M
|
||||
|
||||
proc bar[M](f : Foo[M]) =
|
||||
discard f()
|
||||
|
||||
proc baz() : int = 42
|
||||
|
||||
bar(baz)
|
||||
22
tests/closure/texplicit_dummy_closure.nim
Normal file
22
tests/closure/texplicit_dummy_closure.nim
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
|
||||
# This is a regression of the new lambda lifting; detected by Aporia
|
||||
import asyncio, sockets
|
||||
import os
|
||||
|
||||
type
|
||||
Window = object
|
||||
oneInstSock*: PAsyncSocket
|
||||
IODispatcher*: PDispatcher
|
||||
|
||||
var
|
||||
win: Window
|
||||
|
||||
proc initSocket() =
|
||||
win.oneInstSock = asyncSocket()
|
||||
#win.oneInstSock.handleAccept =
|
||||
proc test(s: PAsyncSocket) =
|
||||
var client: PAsyncSocket
|
||||
proc dummy(c: PAsyncSocket) {.closure.} =
|
||||
discard
|
||||
client.handleRead = dummy
|
||||
test(win.oneInstSock)
|
||||
10
tests/closure/tfutclosure2138.nim
Normal file
10
tests/closure/tfutclosure2138.nim
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
import future, sequtils
|
||||
|
||||
proc any[T](list: varargs[T], pred: (T) -> bool): bool =
|
||||
for item in list:
|
||||
if pred(item):
|
||||
result = true
|
||||
break
|
||||
|
||||
proc contains(s: string, words: varargs[string]): bool =
|
||||
any(words, (word) => s.contains(word))
|
||||
|
|
@ -1,9 +1,9 @@
|
|||
discard """
|
||||
line: 12
|
||||
errormsg: "type mismatch: got (proc (x: int){.closure, gcsafe, locks: 0.})"
|
||||
errormsg: "type mismatch: got (proc (x: int){.gcsafe, locks: 0.})"
|
||||
"""
|
||||
|
||||
proc ugh[T](x: T) {.closure.} =
|
||||
proc ugh[T](x: T) {.nimcall.} =
|
||||
echo "ugha"
|
||||
|
||||
|
||||
|
|
|
|||
6
tests/closure/tissue1502def.nim
Normal file
6
tests/closure/tissue1502def.nim
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
import sequtils
|
||||
let xs: seq[tuple[key: string, val: seq[string]]] = @[("foo", @["bar"])]
|
||||
|
||||
let maps = xs.map(
|
||||
proc(x: auto): tuple[typ: string, maps: seq[string]] =
|
||||
(x.key, x.val.map(proc(x: string): string = x)))
|
||||
|
|
@ -1,7 +1,3 @@
|
|||
discard """
|
||||
file: "tissue1642.nim"
|
||||
disabled: true
|
||||
"""
|
||||
block:
|
||||
var i = 0
|
||||
proc p() = inc(i)
|
||||
var i = 0
|
||||
proc p() = inc(i)
|
||||
16
tests/closure/tissue1846.nim
Normal file
16
tests/closure/tissue1846.nim
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
type
|
||||
TBinOp*[T] = proc (x,y: T): bool
|
||||
|
||||
THeap*[T] = object
|
||||
cmp*: TBinOp[T]
|
||||
|
||||
proc less*[T](x,y: T): bool =
|
||||
x < y
|
||||
|
||||
proc initHeap*[T](cmp: TBinOp[T]): THeap[T] =
|
||||
result.cmp = cmp
|
||||
|
||||
when isMainModule:
|
||||
var h = initHeap[int](less[int])
|
||||
|
||||
echo h.cmp(2,3)
|
||||
7
tests/closure/tissue1911.nim
Normal file
7
tests/closure/tissue1911.nim
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
proc foo(x: int) : auto =
|
||||
|
||||
proc helper() : int = x
|
||||
proc bar() : int = helper()
|
||||
proc baz() : int = helper()
|
||||
|
||||
return (bar, baz)
|
||||
4
tests/closure/tissue600.nim
Normal file
4
tests/closure/tissue600.nim
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
for i in 1..1:
|
||||
var reported = false
|
||||
proc report() =
|
||||
reported = true
|
||||
|
|
@ -7,7 +7,7 @@ type
|
|||
data: T
|
||||
callback: proc () {.closure.}
|
||||
|
||||
proc cbOuter(response: string) {.closure, discardable.} =
|
||||
proc cbOuter(response: string) {.discardable.} =
|
||||
iterator cbIter(): Future[int] {.closure.} =
|
||||
for i in 0..7:
|
||||
proc foo(): int =
|
||||
|
|
|
|||
137
tests/closure/tmacrobust1512.nim
Normal file
137
tests/closure/tmacrobust1512.nim
Normal file
|
|
@ -0,0 +1,137 @@
|
|||
import macros, strutils
|
||||
|
||||
# https://github.com/nim-lang/Nim/issues/1512
|
||||
|
||||
proc macrobust0 (raw_input: string) =
|
||||
var output = ""
|
||||
proc p1 (a:string) =
|
||||
output.add (a)
|
||||
|
||||
proc p2 (a:string) = p1 (a)
|
||||
proc p3 (a:string) = p2 (a)
|
||||
proc p4 (a:string) = p3 (a)
|
||||
proc p5 (a:string) = p4 (a)
|
||||
proc p6 (a:string) = p5 (a)
|
||||
proc p7 (a:string) = p6 (a)
|
||||
proc p8 (a:string) = p7 (a)
|
||||
proc p9 (a:string) = p8 (a)
|
||||
proc p10 (a:string) = p9 (a)
|
||||
proc p11 (a:string) = p10 (a)
|
||||
proc p12 (a:string) = p11 (a)
|
||||
proc p13 (a:string) = p12 (a)
|
||||
proc p14 (a:string) = p13 (a)
|
||||
proc p15 (a:string) = p14 (a)
|
||||
proc p16 (a:string) = p15 (a)
|
||||
proc p17 (a:string) = p16 (a)
|
||||
proc p18 (a:string) = p17 (a)
|
||||
proc p19 (a:string) = p18 (a)
|
||||
proc p20 (a:string) = p19 (a)
|
||||
|
||||
let input = $raw_input
|
||||
|
||||
for a in input.split ():
|
||||
p20 (a)
|
||||
p19 (a)
|
||||
|
||||
|
||||
p18 (a)
|
||||
p17 (a)
|
||||
p16 (a)
|
||||
p15 (a)
|
||||
p14 (a)
|
||||
p13 (a)
|
||||
p12 (a)
|
||||
p11 (a)
|
||||
p10 (a)
|
||||
p9 (a)
|
||||
p8 (a)
|
||||
p7 (a)
|
||||
p6 (a)
|
||||
p5 (a)
|
||||
p4 (a)
|
||||
p3 (a)
|
||||
p2 (a)
|
||||
p1 (a)
|
||||
|
||||
|
||||
echo output
|
||||
|
||||
macro macrobust (raw_input: expr) : stmt =
|
||||
|
||||
var output = ""
|
||||
proc p1 (a:string) =
|
||||
output.add (a)
|
||||
|
||||
proc p2 (a:string) = p1 (a)
|
||||
proc p3 (a:string) = p2 (a)
|
||||
proc p4 (a:string) = p3 (a)
|
||||
proc p5 (a:string) = p4 (a)
|
||||
proc p6 (a:string) = p5 (a)
|
||||
proc p7 (a:string) = p6 (a)
|
||||
proc p8 (a:string) = p7 (a)
|
||||
proc p9 (a:string) = p8 (a)
|
||||
proc p10 (a:string) = p9 (a)
|
||||
proc p11 (a:string) = p10 (a)
|
||||
proc p12 (a:string) = p11 (a)
|
||||
proc p13 (a:string) = p12 (a)
|
||||
proc p14 (a:string) = p13 (a)
|
||||
proc p15 (a:string) = p14 (a)
|
||||
proc p16 (a:string) = p15 (a)
|
||||
proc p17 (a:string) = p16 (a)
|
||||
proc p18 (a:string) = p17 (a)
|
||||
proc p19 (a:string) = p18 (a)
|
||||
proc p20 (a:string) = p19 (a)
|
||||
|
||||
let input = $raw_input
|
||||
|
||||
for a in input.split ():
|
||||
p20 (a)
|
||||
p19 (a)
|
||||
|
||||
p18 (a)
|
||||
p17 (a)
|
||||
p16 (a)
|
||||
p15 (a)
|
||||
p14 (a)
|
||||
p13 (a)
|
||||
p12 (a)
|
||||
p11 (a)
|
||||
p10 (a)
|
||||
p9 (a)
|
||||
p8 (a)
|
||||
p7 (a)
|
||||
p6 (a)
|
||||
p5 (a)
|
||||
p4 (a)
|
||||
p3 (a)
|
||||
p2 (a)
|
||||
|
||||
echo output
|
||||
discard result
|
||||
|
||||
macrobust """
|
||||
fdsasadfsdfa sadfsdafsdaf
|
||||
dsfsdafdsfadsfa fsdaasdfasdf
|
||||
fsdafsadfsad asdfasdfasdf
|
||||
fdsasdfasdfa sadfsadfsadf
|
||||
sadfasdfsdaf sadfsdafsdaf dsfasdaf
|
||||
sadfsdafsadf fdsasdafsadf fdsasadfsdaf
|
||||
sdfasadfsdafdfsa sadfsadfsdaf
|
||||
sdafsdaffsda sdfasadfsadf
|
||||
fsdasdafsdfa sdfasdfafsda
|
||||
sdfasdafsadf sdfasdafsdaf sdfasdafsdaf
|
||||
"""
|
||||
|
||||
|
||||
macrobust0 """
|
||||
fdsasadfsdfa sadfsdafsdaf
|
||||
dsfsdafdsfadsfa fsdaasdfasdf
|
||||
fsdafsadfsad asdfasdfasdf
|
||||
fdsasdfasdfa sadfsadfsadf
|
||||
sadfasdfsdaf sadfsdafsdaf dsfasdaf
|
||||
sadfsdafsadf fdsasdafsadf fdsasadfsdaf
|
||||
sdfasadfsdafdfsa sadfsadfsdaf
|
||||
sdafsdaffsda sdfasadfsadf
|
||||
fsdasdafsdfa sdfasdfafsda
|
||||
sdfasdafsadf sdfasdafsdaf sdfasdafsdaf
|
||||
"""
|
||||
|
|
@ -21,13 +21,13 @@ proc main(param: int) =
|
|||
# test simple closure within dummy 'main':
|
||||
proc dummy =
|
||||
proc main2(param: int) =
|
||||
var foo = 23
|
||||
var fooB = 23
|
||||
proc outer(outerParam: string) =
|
||||
var outerVar = 88
|
||||
echo outerParam, outerVar
|
||||
proc inner() =
|
||||
block Test:
|
||||
echo foo, " ", param, outerParam, " ", outerVar
|
||||
echo fooB, " ", param, outerParam, " ", outerVar
|
||||
inner()
|
||||
outer("foo")
|
||||
main2(24)
|
||||
|
|
|
|||
25
tests/closure/tnoclosure.nim
Normal file
25
tests/closure/tnoclosure.nim
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
discard """
|
||||
output: '''@[1]
|
||||
@[1, 1]
|
||||
@[1, 2, 1]
|
||||
@[1, 3, 3, 1]
|
||||
@[1, 4, 6, 4, 1]
|
||||
@[1, 5, 10, 10, 5, 1]
|
||||
@[1, 6, 15, 20, 15, 6, 1]
|
||||
@[1, 7, 21, 35, 35, 21, 7, 1]
|
||||
@[1, 8, 28, 56, 70, 56, 28, 8, 1]
|
||||
@[1, 9, 36, 84, 126, 126, 84, 36, 9, 1]'''
|
||||
"""
|
||||
|
||||
import sequtils
|
||||
|
||||
proc pascal(n: int) =
|
||||
var row = @[1]
|
||||
for r in 1..n:
|
||||
echo row
|
||||
row = zip(row & @[0], @[0] & row).mapIt(it[0] + it[1])
|
||||
|
||||
pascal(10)
|
||||
|
||||
# bug #3499 last snippet fixed
|
||||
# bug 705 last snippet fixed
|
||||
|
|
@ -19,10 +19,11 @@ proc `=`(lhs: var T, rhs: T) =
|
|||
proc `=destroy`(v: var T) =
|
||||
echo "destroy"
|
||||
|
||||
block:
|
||||
proc usedToBeBlock =
|
||||
var v1 : T
|
||||
var v2 : T = v1
|
||||
|
||||
usedToBeBlock()
|
||||
|
||||
# bug #1632
|
||||
|
||||
|
|
|
|||
|
|
@ -143,7 +143,7 @@ proc main() =
|
|||
# Create long-lived array, filling half of it
|
||||
echo(" Creating a long-lived array of " & $kArraySize & " doubles")
|
||||
newSeq(myarray, kArraySize)
|
||||
for i in 0..kArraySize div 2 -1:
|
||||
for i in 0..kArraySize div 2 - 1:
|
||||
myarray[i] = 1.0 / toFloat(i)
|
||||
|
||||
PrintDiagnostics()
|
||||
|
|
|
|||
|
|
@ -69,7 +69,7 @@ proc cmp[T:int8|int16|int32|int64|int] (a,b: T): T {.inline.} =
|
|||
template binSearchImpl *(docmp: expr) {.immediate.} =
|
||||
var bFound = false
|
||||
result = 0
|
||||
var H = haystack.len -1
|
||||
var H = haystack.len - 1
|
||||
while result <= H :
|
||||
var I {.inject.} = (result + H) shr 1
|
||||
var SW = docmp
|
||||
|
|
@ -90,7 +90,7 @@ proc DeleteItem[T,D] (n: PNode[T,D], x: int): PNode[T,D] {.inline.} =
|
|||
return n
|
||||
dec(n.count)
|
||||
if n.count > 0 :
|
||||
for i in countup(x, n.count -1) : n.slots[i] = n.slots[i + 1]
|
||||
for i in countup(x, n.count - 1) : n.slots[i] = n.slots[i + 1]
|
||||
n.slots[n.count] = nil
|
||||
case n.count
|
||||
of cLen1 : setLen(n.slots, cLen1)
|
||||
|
|
@ -121,7 +121,7 @@ proc internalDelete[T,D] (ANode: PNode[T,D], key: T, Avalue: var D): PNode[T,D]
|
|||
if x == 0 :
|
||||
n = n.left
|
||||
else :
|
||||
x = (-x) -1
|
||||
x = (-x) - 1
|
||||
if x < n.count :
|
||||
n = n.slots[x].node
|
||||
else :
|
||||
|
|
@ -132,10 +132,10 @@ proc internalDelete[T,D] (ANode: PNode[T,D], key: T, Avalue: var D): PNode[T,D]
|
|||
Avalue = n.slots[x].value
|
||||
var n2 = DeleteItem(n, x)
|
||||
dec(h)
|
||||
while (n2 != n) and (h >=0) :
|
||||
while (n2 != n) and (h >= 0) :
|
||||
n = n2
|
||||
var w = addr Path[h]
|
||||
x = w.Xi -1
|
||||
x = w.Xi - 1
|
||||
if x >= 0 :
|
||||
if (n == nil) and isClean(w.Nd, x) :
|
||||
n = w.Nd
|
||||
|
|
@ -160,7 +160,7 @@ proc internalFind[T,D] (n: PNode[T,D], key: T): ref TItem[T,D] {.inline.} =
|
|||
if x == 0 :
|
||||
wn = wn.left
|
||||
else :
|
||||
x = (-x) -1
|
||||
x = (-x) - 1
|
||||
if x < wn.count :
|
||||
wn = wn.slots[x].node
|
||||
else :
|
||||
|
|
@ -199,7 +199,7 @@ proc traceTree[T,D](root: PNode[T,D]) =
|
|||
if n.left != nil:
|
||||
traceln(space)
|
||||
write stdout, "left: "
|
||||
doTrace(n.left, level +1)
|
||||
doTrace(n.left, level+1)
|
||||
for i, el in n.slots :
|
||||
if el != nil and not isClean(el):
|
||||
traceln(space)
|
||||
|
|
@ -208,7 +208,7 @@ proc traceTree[T,D](root: PNode[T,D]) =
|
|||
write stdout, "error "
|
||||
else:
|
||||
traceEl(el)
|
||||
if el.node != nil: doTrace(el.node, level +1)
|
||||
if el.node != nil: doTrace(el.node, level+1)
|
||||
else : write stdout, " empty "
|
||||
elif i < n.count :
|
||||
traceln(space)
|
||||
|
|
@ -217,7 +217,7 @@ proc traceTree[T,D](root: PNode[T,D]) =
|
|||
when T is string :
|
||||
if el.key != nil: write stdout, el.key
|
||||
else : write stdout, el.key
|
||||
if el.node != nil: doTrace(el.node, level +1)
|
||||
if el.node != nil: doTrace(el.node, level+1)
|
||||
else : write stdout, " empty "
|
||||
writeLine stdout,""
|
||||
|
||||
|
|
@ -245,25 +245,25 @@ proc SplitPage[T,D](n, left: PNode[T,D], xi: int, Akey:var T, Avalue:var D): PNo
|
|||
result.slots.newSeq(cLenCenter)
|
||||
result.count = cCenter
|
||||
if x == cCenter:
|
||||
for i in 0..cCenter -1: shallowCopy(it1[i], left.slots[i])
|
||||
for i in 0..cCenter -1: shallowCopy(result.slots[i], left.slots[cCenter + i])
|
||||
for i in 0..cCenter-1: shallowCopy(it1[i], left.slots[i])
|
||||
for i in 0..cCenter-1: shallowCopy(result.slots[i], left.slots[cCenter + i])
|
||||
result.left = n
|
||||
else :
|
||||
if x < cCenter :
|
||||
for i in 0..x-1: shallowCopy(it1[i], left.slots[i])
|
||||
it1[x] = setItem(Akey, Avalue, n)
|
||||
for i in x+1 .. cCenter -1: shallowCopy(it1[i], left.slots[i-1])
|
||||
var w = left.slots[cCenter -1]
|
||||
for i in x+1 .. cCenter-1: shallowCopy(it1[i], left.slots[i-1])
|
||||
var w = left.slots[cCenter-1]
|
||||
Akey = w.key
|
||||
Avalue = w.value
|
||||
result.left = w.node
|
||||
for i in 0..cCenter -1: shallowCopy(result.slots[i], left.slots[cCenter + i])
|
||||
for i in 0..cCenter-1: shallowCopy(result.slots[i], left.slots[cCenter + i])
|
||||
else :
|
||||
for i in 0..cCenter -1: shallowCopy(it1[i], left.slots[i])
|
||||
for i in 0..cCenter-1: shallowCopy(it1[i], left.slots[i])
|
||||
x = x - (cCenter + 1)
|
||||
for i in 0..x-1: shallowCopy(result.slots[i], left.slots[cCenter + i + 1])
|
||||
result.slots[x] = setItem(Akey, Avalue, n)
|
||||
for i in x+1 .. cCenter -1: shallowCopy(result.slots[i], left.slots[cCenter + i])
|
||||
for i in x+1 .. cCenter-1: shallowCopy(result.slots[i], left.slots[cCenter + i])
|
||||
var w = left.slots[cCenter]
|
||||
Akey = w.key
|
||||
Avalue = w.value
|
||||
|
|
@ -290,7 +290,7 @@ proc internalPut[T,D](ANode: ref TNode[T,D], Akey: T, Avalue: D, Oldvalue: var D
|
|||
if x == 0 :
|
||||
n = n.left
|
||||
else :
|
||||
x = (-x) -1
|
||||
x = (-x)-1
|
||||
if x < n.count :
|
||||
n = n.slots[x].node
|
||||
else :
|
||||
|
|
|
|||
|
|
@ -1,12 +1,21 @@
|
|||
discard """
|
||||
output: '''0
|
||||
123'''
|
||||
"""
|
||||
|
||||
template tmp[T](x: var seq[T]) =
|
||||
#var yz: T # XXX doesn't work yet
|
||||
# bug #3498
|
||||
|
||||
template defaultOf[T](t: T): expr = (var d: T; d)
|
||||
|
||||
echo defaultOf(1) #<- excpected 0
|
||||
|
||||
# assignment using template
|
||||
|
||||
template tassign[T](x: var seq[T]) =
|
||||
x = @[1, 2, 3]
|
||||
|
||||
macro tmp2[T](x: var seq[T]): stmt =
|
||||
nil
|
||||
|
||||
var y: seq[int]
|
||||
tmp(y)
|
||||
tmp(y)
|
||||
echo y.repr
|
||||
tassign(y) #<- x is expected = @[1, 2, 3]
|
||||
tassign(y)
|
||||
|
||||
echo y[0], y[1], y[2]
|
||||
|
|
|
|||
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