Merge ../Nim into devel; track ttables.nim delete.

This commit is contained in:
Charles Blake 2015-02-13 08:42:41 -05:00
commit 45a2c1b1d1
35 changed files with 1217 additions and 1273 deletions

View file

@ -431,6 +431,18 @@ proc genNamedParamCall(p: BProc, ri: PNode, d: var TLoc) =
var length = sonsLen(ri) var length = sonsLen(ri)
assert(sonsLen(typ) == sonsLen(typ.n)) assert(sonsLen(typ) == sonsLen(typ.n))
# don't call 'ropeToStr' here for efficiency:
let pat = ri.sons[0].sym.loc.r.data
internalAssert pat != nil
var start = 3
if ' ' in pat:
start = 1
app(pl, op.r)
if length > 1:
app(pl, ~": ")
app(pl, genArg(p, ri.sons[1], typ.n.sons[1].sym, ri))
start = 2
else:
if length > 1: if length > 1:
app(pl, genArg(p, ri.sons[1], typ.n.sons[1].sym, ri)) app(pl, genArg(p, ri.sons[1], typ.n.sons[1].sym, ri))
app(pl, ~" ") app(pl, ~" ")
@ -438,7 +450,7 @@ proc genNamedParamCall(p: BProc, ri: PNode, d: var TLoc) =
if length > 2: if length > 2:
app(pl, ~": ") app(pl, ~": ")
app(pl, genArg(p, ri.sons[2], typ.n.sons[2].sym, ri)) app(pl, genArg(p, ri.sons[2], typ.n.sons[2].sym, ri))
for i in countup(3, length-1): for i in countup(start, length-1):
assert(sonsLen(typ) == sonsLen(typ.n)) assert(sonsLen(typ) == sonsLen(typ.n))
if i >= sonsLen(typ): if i >= sonsLen(typ):
internalError(ri.info, "varargs for objective C method?") internalError(ri.info, "varargs for objective C method?")

View file

@ -2108,16 +2108,12 @@ proc expr(p: BProc, n: PNode, d: var TLoc) =
of nkPragma: genPragma(p, n) of nkPragma: genPragma(p, n)
of nkPragmaBlock: expr(p, n.lastSon, d) of nkPragmaBlock: expr(p, n.lastSon, d)
of nkProcDef, nkMethodDef, nkConverterDef: of nkProcDef, nkMethodDef, nkConverterDef:
if (n.sons[genericParamsPos].kind == nkEmpty): if n.sons[genericParamsPos].kind == nkEmpty:
var prc = n.sons[namePos].sym var prc = n.sons[namePos].sym
# due to a bug/limitation in the lambda lifting, unused inner procs # due to a bug/limitation in the lambda lifting, unused inner procs
# are not transformed correctly. We work around this issue (#411) here # are not transformed correctly. We work around this issue (#411) here
# by ensuring it's no inner proc (owner is a module): # by ensuring it's no inner proc (owner is a module):
# if prc.skipGenericOwner.kind == skModule:
# We also check whether the proc captures its environment here to
# prevent issue #1642.
if prc.skipGenericOwner.kind == skModule and
tfCapturesEnv in prc.typ.flags:
if (optDeadCodeElim notin gGlobalOptions and if (optDeadCodeElim notin gGlobalOptions and
sfDeadCodeElim notin getModule(prc).flags) or sfDeadCodeElim notin getModule(prc).flags) or
({sfExportc, sfCompilerProc} * prc.flags == {sfExportc}) or ({sfExportc, sfCompilerProc} * prc.flags == {sfExportc}) or

View file

@ -141,7 +141,7 @@ proc expectNoArg(switch, arg: string, pass: TCmdLinePass, info: TLineInfo) =
if arg != "": localError(info, errCmdLineNoArgExpected, addPrefix(switch)) if arg != "": localError(info, errCmdLineNoArgExpected, addPrefix(switch))
proc processSpecificNote(arg: string, state: TSpecialWord, pass: TCmdLinePass, proc processSpecificNote(arg: string, state: TSpecialWord, pass: TCmdLinePass,
info: TLineInfo) = info: TLineInfo; orig: string) =
var id = "" # arg = "X]:on|off" var id = "" # arg = "X]:on|off"
var i = 0 var i = 0
var n = hintMin var n = hintMin
@ -149,17 +149,17 @@ proc processSpecificNote(arg: string, state: TSpecialWord, pass: TCmdLinePass,
add(id, arg[i]) add(id, arg[i])
inc(i) inc(i)
if i < len(arg) and (arg[i] == ']'): inc(i) if i < len(arg) and (arg[i] == ']'): inc(i)
else: invalidCmdLineOption(pass, arg, info) else: invalidCmdLineOption(pass, orig, info)
if i < len(arg) and (arg[i] in {':', '='}): inc(i) if i < len(arg) and (arg[i] in {':', '='}): inc(i)
else: invalidCmdLineOption(pass, arg, info) else: invalidCmdLineOption(pass, orig, info)
if state == wHint: if state == wHint:
var x = findStr(msgs.HintsToStr, id) var x = findStr(msgs.HintsToStr, id)
if x >= 0: n = TNoteKind(x + ord(hintMin)) if x >= 0: n = TNoteKind(x + ord(hintMin))
else: invalidCmdLineOption(pass, arg, info) else: localError(info, "unknown hint: " & id)
else: else:
var x = findStr(msgs.WarningsToStr, id) var x = findStr(msgs.WarningsToStr, id)
if x >= 0: n = TNoteKind(x + ord(warnMin)) if x >= 0: n = TNoteKind(x + ord(warnMin))
else: invalidCmdLineOption(pass, arg, info) else: localError(info, "unknown warning: " & id)
case whichKeyword(substr(arg, i)) case whichKeyword(substr(arg, i))
of wOn: incl(gNotes, n) of wOn: incl(gNotes, n)
of wOff: excl(gNotes, n) of wOff: excl(gNotes, n)
@ -368,8 +368,8 @@ proc processSwitch(switch, arg: string, pass: TCmdLinePass, info: TLineInfo) =
defineSymbol("nogc") defineSymbol("nogc")
else: localError(info, errNoneBoehmRefcExpectedButXFound, arg) else: localError(info, errNoneBoehmRefcExpectedButXFound, arg)
of "warnings", "w": processOnOffSwitch({optWarns}, arg, pass, info) of "warnings", "w": processOnOffSwitch({optWarns}, arg, pass, info)
of "warning": processSpecificNote(arg, wWarning, pass, info) of "warning": processSpecificNote(arg, wWarning, pass, info, switch)
of "hint": processSpecificNote(arg, wHint, pass, info) of "hint": processSpecificNote(arg, wHint, pass, info, switch)
of "hints": processOnOffSwitch({optHints}, arg, pass, info) of "hints": processOnOffSwitch({optHints}, arg, pass, info)
of "threadanalysis": processOnOffSwitchG({optThreadAnalysis}, arg, pass, info) of "threadanalysis": processOnOffSwitchG({optThreadAnalysis}, arg, pass, info)
of "stacktrace": processOnOffSwitch({optStackTrace}, arg, pass, info) of "stacktrace": processOnOffSwitch({optStackTrace}, arg, pass, info)

View file

@ -380,7 +380,7 @@ proc setCC*(ccname: string) =
cCompiler = nameToCC(ccname) cCompiler = nameToCC(ccname)
if cCompiler == ccNone: rawMessage(errUnknownCcompiler, ccname) if cCompiler == ccNone: rawMessage(errUnknownCcompiler, ccname)
compileOptions = getConfigVar(cCompiler, ".options.always") compileOptions = getConfigVar(cCompiler, ".options.always")
linkOptions = getConfigVar(cCompiler, ".options.linker") linkOptions = ""
ccompilerpath = getConfigVar(cCompiler, ".path") ccompilerpath = getConfigVar(cCompiler, ".path")
for i in countup(low(CC), high(CC)): undefSymbol(CC[i].name) for i in countup(low(CC), high(CC)): undefSymbol(CC[i].name)
defineSymbol(CC[cCompiler].name) defineSymbol(CC[cCompiler].name)
@ -390,7 +390,7 @@ proc addOpt(dest: var string, src: string) =
add(dest, src) add(dest, src)
proc addLinkOption*(option: string) = proc addLinkOption*(option: string) =
if find(linkOptions, option, 0) < 0: addOpt(linkOptions, option) addOpt(linkOptions, option)
proc addCompileOption*(option: string) = proc addCompileOption*(option: string) =
if strutils.find(compileOptions, option, 0) < 0: if strutils.find(compileOptions, option, 0) < 0:
@ -401,7 +401,7 @@ proc initVars*() =
for i in countup(low(CC), high(CC)): undefSymbol(CC[i].name) for i in countup(low(CC), high(CC)): undefSymbol(CC[i].name)
defineSymbol(CC[cCompiler].name) defineSymbol(CC[cCompiler].name)
addCompileOption(getConfigVar(cCompiler, ".options.always")) addCompileOption(getConfigVar(cCompiler, ".options.always"))
addLinkOption(getConfigVar(cCompiler, ".options.linker")) #addLinkOption(getConfigVar(cCompiler, ".options.linker"))
if len(ccompilerpath) == 0: if len(ccompilerpath) == 0:
ccompilerpath = getConfigVar(cCompiler, ".path") ccompilerpath = getConfigVar(cCompiler, ".path")
@ -553,13 +553,13 @@ proc getCompileCFileCmd*(cfilename: string, isExternal = false): string =
cfile = quoteShell(cfile) cfile = quoteShell(cfile)
result = quoteShell(compilePattern % [ result = quoteShell(compilePattern % [
"file", cfile, "objfile", objfile, "options", options, "file", cfile, "objfile", objfile, "options", options,
"include", includeCmd, "nimrod", getPrefixDir(), "include", includeCmd, "nim", getPrefixDir(),
"nim", getPrefixDir(), "lib", libpath]) "nim", getPrefixDir(), "lib", libpath])
add(result, ' ') add(result, ' ')
addf(result, CC[c].compileTmpl, [ addf(result, CC[c].compileTmpl, [
"file", cfile, "objfile", objfile, "file", cfile, "objfile", objfile,
"options", options, "include", includeCmd, "options", options, "include", includeCmd,
"nimrod", quoteShell(getPrefixDir()), "nim", quoteShell(getPrefixDir()),
"nim", quoteShell(getPrefixDir()), "nim", quoteShell(getPrefixDir()),
"lib", quoteShell(libpath)]) "lib", quoteShell(libpath)])
@ -679,15 +679,16 @@ proc callCCompiler*(projectfile: string) =
if not exefile.isAbsolute(): if not exefile.isAbsolute():
exefile = joinPath(splitFile(projectfile).dir, exefile) exefile = joinPath(splitFile(projectfile).dir, exefile)
exefile = quoteShell(exefile) exefile = quoteShell(exefile)
let linkOptions = getLinkOptions() let linkOptions = getLinkOptions() & " " &
getConfigVar(cCompiler, ".options.linker")
linkCmd = quoteShell(linkCmd % ["builddll", builddll, linkCmd = quoteShell(linkCmd % ["builddll", builddll,
"buildgui", buildgui, "options", linkOptions, "objfiles", objfiles, "buildgui", buildgui, "options", linkOptions, "objfiles", objfiles,
"exefile", exefile, "nimrod", getPrefixDir(), "lib", libpath]) "exefile", exefile, "nim", getPrefixDir(), "lib", libpath])
linkCmd.add ' ' linkCmd.add ' '
addf(linkCmd, CC[c].linkTmpl, ["builddll", builddll, addf(linkCmd, CC[c].linkTmpl, ["builddll", builddll,
"buildgui", buildgui, "options", linkOptions, "buildgui", buildgui, "options", linkOptions,
"objfiles", objfiles, "exefile", exefile, "objfiles", objfiles, "exefile", exefile,
"nimrod", quoteShell(getPrefixDir()), "nim", quoteShell(getPrefixDir()),
"lib", quoteShell(libpath)]) "lib", quoteShell(libpath)])
if optCompileOnly notin gGlobalOptions: if optCompileOnly notin gGlobalOptions:
if gVerbosity == 1: if gVerbosity == 1:
@ -716,7 +717,8 @@ proc writeMapping*(gSymbolMapping: PRope) =
app(code, strutils.escape(getCompileOptions())) app(code, strutils.escape(getCompileOptions()))
app(code, "\n[Linker]\nFlags=") app(code, "\n[Linker]\nFlags=")
app(code, strutils.escape(getLinkOptions())) app(code, strutils.escape(getLinkOptions() & " " &
getConfigVar(cCompiler, ".options.linker")))
app(code, "\n[Environment]\nlibpath=") app(code, "\n[Environment]\nlibpath=")
app(code, strutils.escape(libpath)) app(code, strutils.escape(libpath))

View file

@ -735,7 +735,6 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: int,
incl(sym.flags, sfProcvar) incl(sym.flags, sfProcvar)
if sym.typ != nil: incl(sym.typ.flags, tfThread) if sym.typ != nil: incl(sym.typ.flags, tfThread)
of wGcSafe: of wGcSafe:
if optThreadAnalysis in gGlobalOptions:
noVal(it) noVal(it)
if sym.kind != skType: incl(sym.flags, sfThread) if sym.kind != skType: incl(sym.flags, sfThread)
if sym.typ != nil: incl(sym.typ.flags, tfGcSafe) if sym.typ != nil: incl(sym.typ.flags, tfGcSafe)

View file

@ -686,7 +686,9 @@ proc evalAtCompileTime(c: PContext, n: PNode): PNode =
# implicit statics. # implicit statics.
if n.len > 1: if n.len > 1:
for i in 1 .. <n.len: for i in 1 .. <n.len:
if n[i].typ.kind != tyStatic or tfUnresolved notin n[i].typ.flags: # see bug #2113, it's possible that n[i].typ for errornous code:
if n[i].typ.isNil or n[i].typ.kind != tyStatic or
tfUnresolved notin n[i].typ.flags:
break maybeLabelAsStatic break maybeLabelAsStatic
n.typ = newTypeWithSons(c, tyStatic, @[n.typ]) n.typ = newTypeWithSons(c, tyStatic, @[n.typ])
n.typ.flags.incl tfUnresolved n.typ.flags.incl tfUnresolved
@ -1337,6 +1339,11 @@ proc semProcBody(c: PContext, n: PNode): PNode =
if c.p.owner.kind notin {skMacro, skTemplate} and if c.p.owner.kind notin {skMacro, skTemplate} and
c.p.resultSym != nil and c.p.resultSym.typ.isMetaType: c.p.resultSym != nil and c.p.resultSym.typ.isMetaType:
if isEmptyType(result.typ):
# we inferred a 'void' return type:
c.p.resultSym.typ = nil
c.p.owner.typ.sons[0] = nil
else:
localError(c.p.resultSym.info, errCannotInferReturnType) localError(c.p.resultSym.info, errCannotInferReturnType)
closeScope(c) closeScope(c)

View file

@ -194,6 +194,9 @@ proc warnAboutGcUnsafe(n: PNode) =
#assert false #assert false
message(n.info, warnGcUnsafe, renderTree(n)) message(n.info, warnGcUnsafe, renderTree(n))
template markGcUnsafe(a: PEffects) =
a.gcUnsafe = true
proc useVar(a: PEffects, n: PNode) = proc useVar(a: PEffects, n: PNode) =
let s = n.sym let s = n.sym
if isLocalVar(a, s): if isLocalVar(a, s):
@ -209,7 +212,7 @@ proc useVar(a: PEffects, n: PNode) =
if (tfHasGCedMem in s.typ.flags or s.typ.isGCedMem) and if (tfHasGCedMem in s.typ.flags or s.typ.isGCedMem) and
tfGcSafe notin s.typ.flags: tfGcSafe notin s.typ.flags:
if warnGcUnsafe in gNotes: warnAboutGcUnsafe(n) if warnGcUnsafe in gNotes: warnAboutGcUnsafe(n)
a.gcUnsafe = true markGcUnsafe(a)
type type
TIntersection = seq[tuple[id, count: int]] # a simple count table TIntersection = seq[tuple[id, count: int]] # a simple count table
@ -448,7 +451,7 @@ proc propagateEffects(tracked: PEffects, n: PNode, s: PSym) =
if notGcSafe(s.typ) and sfImportc notin s.flags: if notGcSafe(s.typ) and sfImportc notin s.flags:
if warnGcUnsafe in gNotes: warnAboutGcUnsafe(n) if warnGcUnsafe in gNotes: warnAboutGcUnsafe(n)
tracked.gcUnsafe = true markGcUnsafe(tracked)
mergeLockLevels(tracked, n, s.getLockLevel) mergeLockLevels(tracked, n, s.getLockLevel)
proc notNilCheck(tracked: PEffects, n: PNode, paramType: PType) = proc notNilCheck(tracked: PEffects, n: PNode, paramType: PType) =
@ -502,13 +505,13 @@ proc trackOperand(tracked: PEffects, n: PNode, paramType: PType) =
# assume GcUnsafe unless in its type; 'forward' does not matter: # assume GcUnsafe unless in its type; 'forward' does not matter:
if notGcSafe(op) and not isOwnedProcVar(a, tracked.owner): if notGcSafe(op) and not isOwnedProcVar(a, tracked.owner):
if warnGcUnsafe in gNotes: warnAboutGcUnsafe(n) if warnGcUnsafe in gNotes: warnAboutGcUnsafe(n)
tracked.gcUnsafe = true markGcUnsafe(tracked)
else: else:
mergeEffects(tracked, effectList.sons[exceptionEffects], n) mergeEffects(tracked, effectList.sons[exceptionEffects], n)
mergeTags(tracked, effectList.sons[tagEffects], n) mergeTags(tracked, effectList.sons[tagEffects], n)
if notGcSafe(op): if notGcSafe(op):
if warnGcUnsafe in gNotes: warnAboutGcUnsafe(n) if warnGcUnsafe in gNotes: warnAboutGcUnsafe(n)
tracked.gcUnsafe = true markGcUnsafe(tracked)
notNilCheck(tracked, n, paramType) notNilCheck(tracked, n, paramType)
proc breaksBlock(n: PNode): bool = proc breaksBlock(n: PNode): bool =
@ -656,7 +659,7 @@ proc track(tracked: PEffects, n: PNode) =
# and it's not a recursive call: # and it's not a recursive call:
if not (a.kind == nkSym and a.sym == tracked.owner): if not (a.kind == nkSym and a.sym == tracked.owner):
warnAboutGcUnsafe(n) warnAboutGcUnsafe(n)
tracked.gcUnsafe = true markGcUnsafe(tracked)
for i in 1 .. <len(n): trackOperand(tracked, n.sons[i], paramType(op, i)) for i in 1 .. <len(n): trackOperand(tracked, n.sons[i], paramType(op, i))
if a.kind == nkSym and a.sym.magic in {mNew, mNewFinalize, mNewSeq}: if a.kind == nkSym and a.sym.magic in {mNew, mNewFinalize, mNewSeq}:
# may not look like an assignment, but it is: # may not look like an assignment, but it is:
@ -849,17 +852,18 @@ proc trackProc*(s: PSym, body: PNode) =
# after the check, use the formal spec: # after the check, use the formal spec:
effects.sons[tagEffects] = tagsSpec effects.sons[tagEffects] = tagsSpec
if optThreadAnalysis in gGlobalOptions:
if sfThread in s.flags and t.gcUnsafe: if sfThread in s.flags and t.gcUnsafe:
if optThreads in gGlobalOptions: if optThreads in gGlobalOptions and optThreadAnalysis in gGlobalOptions:
localError(s.info, "'$1' is not GC-safe" % s.name.s) localError(s.info, "'$1' is not GC-safe" % s.name.s)
else: else:
localError(s.info, warnGcUnsafe2, s.name.s) localError(s.info, warnGcUnsafe2, s.name.s)
if not t.gcUnsafe: s.typ.flags.incl tfGcSafe if not t.gcUnsafe:
s.typ.flags.incl tfGcSafe
if s.typ.lockLevel == UnspecifiedLockLevel: if s.typ.lockLevel == UnspecifiedLockLevel:
s.typ.lockLevel = t.maxLockLevel s.typ.lockLevel = t.maxLockLevel
elif t.maxLockLevel > s.typ.lockLevel: elif t.maxLockLevel > s.typ.lockLevel:
localError(s.info, #localError(s.info,
message(s.info, warnLockLevel,
"declared lock level is $1, but real lock level is $2" % "declared lock level is $1, but real lock level is $2" %
[$s.typ.lockLevel, $t.maxLockLevel]) [$s.typ.lockLevel, $t.maxLockLevel])

View file

@ -418,7 +418,8 @@ proc procTypeRel(c: var TCandidate, f, a: PType): TTypeRelation =
if tfNoSideEffect in f.flags and tfNoSideEffect notin a.flags: if tfNoSideEffect in f.flags and tfNoSideEffect notin a.flags:
return isNone return isNone
elif tfThread in f.flags and a.flags * {tfThread, tfNoSideEffect} == {}: elif tfThread in f.flags and a.flags * {tfThread, tfNoSideEffect} == {} and
optThreadAnalysis in gGlobalOptions:
# noSideEffect implies ``tfThread``! # noSideEffect implies ``tfThread``!
return isNone return isNone
elif f.flags * {tfIterator} != a.flags * {tfIterator}: elif f.flags * {tfIterator} != a.flags * {tfIterator}:
@ -1441,13 +1442,14 @@ proc matchesAux(c: PContext, n, nOrig: PNode,
return return
checkConstraint(n.sons[a].sons[1]) checkConstraint(n.sons[a].sons[1])
if m.baseTypeMatch: if m.baseTypeMatch:
assert(container == nil) #assert(container == nil)
container = newNodeIT(nkBracket, n.sons[a].info, arrayConstr(c, arg)) container = newNodeIT(nkBracket, n.sons[a].info, arrayConstr(c, arg))
addSon(container, arg) addSon(container, arg)
setSon(m.call, formal.position + 1, container) setSon(m.call, formal.position + 1, container)
if f != formalLen - 1: container = nil if f != formalLen - 1: container = nil
else: else:
setSon(m.call, formal.position + 1, arg) setSon(m.call, formal.position + 1, arg)
inc f
else: else:
# unnamed param # unnamed param
if f >= formalLen: if f >= formalLen:
@ -1466,7 +1468,7 @@ proc matchesAux(c: PContext, n, nOrig: PNode,
n.sons[a] = prepareOperand(c, formal.typ, n.sons[a]) n.sons[a] = prepareOperand(c, formal.typ, n.sons[a])
var arg = paramTypesMatch(m, formal.typ, n.sons[a].typ, var arg = paramTypesMatch(m, formal.typ, n.sons[a].typ,
n.sons[a], nOrig.sons[a]) n.sons[a], nOrig.sons[a])
if (arg != nil) and m.baseTypeMatch and (container != nil): if arg != nil and m.baseTypeMatch and container != nil:
addSon(container, arg) addSon(container, arg)
incrIndexType(container.typ) incrIndexType(container.typ)
else: else:
@ -1480,7 +1482,7 @@ proc matchesAux(c: PContext, n, nOrig: PNode,
internalError(n.sons[a].info, "matches") internalError(n.sons[a].info, "matches")
return return
formal = m.callee.n.sons[f].sym formal = m.callee.n.sons[f].sym
if containsOrIncl(marker, formal.position): if containsOrIncl(marker, formal.position) and container.isNil:
# already in namedParams: # already in namedParams:
localError(n.sons[a].info, errCannotBindXTwice, formal.name.s) localError(n.sons[a].info, errCannotBindXTwice, formal.name.s)
m.state = csNoMatch m.state = csNoMatch
@ -1493,17 +1495,22 @@ proc matchesAux(c: PContext, n, nOrig: PNode,
m.state = csNoMatch m.state = csNoMatch
return return
if m.baseTypeMatch: if m.baseTypeMatch:
assert(container == nil) #assert(container == nil)
if container.isNil:
container = newNodeIT(nkBracket, n.sons[a].info, arrayConstr(c, arg)) container = newNodeIT(nkBracket, n.sons[a].info, arrayConstr(c, arg))
addSon(container, arg) addSon(container, arg)
setSon(m.call, formal.position + 1, setSon(m.call, formal.position + 1,
implicitConv(nkHiddenStdConv, formal.typ, container, m, c)) implicitConv(nkHiddenStdConv, formal.typ, container, m, c))
if f != formalLen - 1: container = nil #if f != formalLen - 1: container = nil
# pick the formal from the end, so that 'x, y, varargs, z' works:
f = max(f, formalLen - n.len + a + 1)
else: else:
setSon(m.call, formal.position + 1, arg) setSon(m.call, formal.position + 1, arg)
inc(f)
container = nil
checkConstraint(n.sons[a]) checkConstraint(n.sons[a])
inc(a) inc(a)
inc(f)
proc semFinishOperands*(c: PContext, n: PNode) = proc semFinishOperands*(c: PContext, n: PNode) =
# this needs to be called to ensure that after overloading resolution every # this needs to be called to ensure that after overloading resolution every

View file

@ -10,6 +10,12 @@
cc = gcc cc = gcc
# additional options always passed to the compiler:
--parallel_build: "0" # 0 to auto-detect number of processors
hint[LineTooLong]=off
#hint[XDeclaredButNotUsed]=off
# example of how to setup a cross-compiler: # example of how to setup a cross-compiler:
arm.linux.gcc.exe = "arm-linux-gcc" arm.linux.gcc.exe = "arm-linux-gcc"
arm.linux.gcc.linkerexe = "arm-linux-gcc" arm.linux.gcc.linkerexe = "arm-linux-gcc"
@ -66,12 +72,6 @@ path="$lib/pure/unidecode"
opt:speed opt:speed
@end @end
# additional options always passed to the compiler:
--parallel_build: "0" # 0 to auto-detect number of processors
hint[LineTooLong]=off
#hint[XDeclaredButNotUsed]=off
@if unix: @if unix:
@if not bsd: @if not bsd:
# -fopenmp # -fopenmp

View file

@ -60,11 +60,6 @@ ConfigDeprecated The project makes use of a deprecated config
file. file.
SmallLshouldNotBeUsed The letter 'l' should not be used as an SmallLshouldNotBeUsed The letter 'l' should not be used as an
identifier. identifier.
AnalysisLoophole The thread analysis was incomplete due to
an indirect call.
DifferentHeaps The code mixes different local heaps in a
very dangerous way.
WriteToForeignHeap The code contains a threading error.
EachIdentIsTuple The code contains a confusing ``var`` EachIdentIsTuple The code contains a confusing ``var``
declaration. declaration.
ShadowIdent A local variable shadows another local ShadowIdent A local variable shadows another local

View file

@ -136,12 +136,12 @@ proc len*(n: PNimrodNode): int {.magic: "NLen", noSideEffect.}
## returns the number of children of `n`. ## returns the number of children of `n`.
proc add*(father, child: PNimrodNode): PNimrodNode {.magic: "NAdd", discardable, proc add*(father, child: PNimrodNode): PNimrodNode {.magic: "NAdd", discardable,
noSideEffect.} noSideEffect, locks: 0.}
## Adds the `child` to the `father` node. Returns the ## Adds the `child` to the `father` node. Returns the
## father node so that calls can be nested. ## father node so that calls can be nested.
proc add*(father: PNimrodNode, children: varargs[PNimrodNode]): PNimrodNode {. proc add*(father: PNimrodNode, children: varargs[PNimrodNode]): PNimrodNode {.
magic: "NAddMultiple", discardable, noSideEffect.} magic: "NAddMultiple", discardable, noSideEffect, locks: 0.}
## Adds each child of `children` to the `father` node. ## Adds each child of `children` to the `father` node.
## Returns the `father` node so that calls can be nested. ## Returns the `father` node so that calls can be nested.
@ -177,13 +177,13 @@ proc newNimNode*(kind: TNimrodNodeKind,
proc copyNimNode*(n: PNimrodNode): PNimrodNode {.magic: "NCopyNimNode", noSideEffect.} proc copyNimNode*(n: PNimrodNode): PNimrodNode {.magic: "NCopyNimNode", noSideEffect.}
proc copyNimTree*(n: PNimrodNode): PNimrodNode {.magic: "NCopyNimTree", noSideEffect.} proc copyNimTree*(n: PNimrodNode): PNimrodNode {.magic: "NCopyNimTree", noSideEffect.}
proc error*(msg: string) {.magic: "NError", gcsafe.} proc error*(msg: string) {.magic: "NError", benign.}
## writes an error message at compile time ## writes an error message at compile time
proc warning*(msg: string) {.magic: "NWarning", gcsafe.} proc warning*(msg: string) {.magic: "NWarning", benign.}
## writes a warning message at compile time ## writes a warning message at compile time
proc hint*(msg: string) {.magic: "NHint", gcsafe.} proc hint*(msg: string) {.magic: "NHint", benign.}
## writes a hint message at compile time ## writes a hint message at compile time
proc newStrLitNode*(s: string): PNimrodNode {.compileTime, noSideEffect.} = proc newStrLitNode*(s: string): PNimrodNode {.compileTime, noSideEffect.} =
@ -237,7 +237,7 @@ proc genSym*(kind: TNimrodSymKind = nskLet; ident = ""): PNimrodNode {.
## generates a fresh symbol that is guaranteed to be unique. The symbol ## generates a fresh symbol that is guaranteed to be unique. The symbol
## needs to occur in a declaration context. ## needs to occur in a declaration context.
proc callsite*(): PNimrodNode {.magic: "NCallSite", gcsafe.} proc callsite*(): PNimrodNode {.magic: "NCallSite", benign.}
## returns the AST of the invocation expression that invoked this macro. ## returns the AST of the invocation expression that invoked this macro.
proc toStrLit*(n: PNimrodNode): PNimrodNode {.compileTime.} = proc toStrLit*(n: PNimrodNode): PNimrodNode {.compileTime.} =
@ -387,11 +387,11 @@ proc nestList*(theProc: TNimrodIdent,
# This could easily user code and so should be fixed in evals.nim somehow. # This could easily user code and so should be fixed in evals.nim somehow.
result = newCall(theProc, x[i], copyNimTree(result)) result = newCall(theProc, x[i], copyNimTree(result))
proc treeRepr*(n: PNimrodNode): string {.compileTime.} = proc treeRepr*(n: PNimrodNode): string {.compileTime, benign.} =
## Convert the AST `n` to a human-readable tree-like string. ## Convert the AST `n` to a human-readable tree-like string.
## ##
## See also `repr` and `lispRepr`. ## See also `repr` and `lispRepr`.
proc traverse(res: var string, level: int, n: PNimrodNode) = proc traverse(res: var string, level: int, n: PNimrodNode) {.benign.} =
for i in 0..level-1: res.add " " for i in 0..level-1: res.add " "
res.add(($n.kind).substr(3)) res.add(($n.kind).substr(3))
@ -412,7 +412,7 @@ proc treeRepr*(n: PNimrodNode): string {.compileTime.} =
result = "" result = ""
traverse(result, 0, n) traverse(result, 0, n)
proc lispRepr*(n: PNimrodNode): string {.compileTime.} = proc lispRepr*(n: PNimrodNode): string {.compileTime, benign.} =
## Convert the AST `n` to a human-readable lisp-like string, ## Convert the AST `n` to a human-readable lisp-like string,
## ##
## See also `repr` and `treeRepr`. ## See also `repr` and `treeRepr`.
@ -651,7 +651,7 @@ proc `body=`*(someProc: PNimrodNode, val: PNimrodNode) {.compileTime.} =
else: else:
badNodeKind someProc.kind, "body=" badNodeKind someProc.kind, "body="
proc basename*(a: PNimrodNode): PNimrodNode {.compiletime.} proc basename*(a: PNimrodNode): PNimrodNode {.compiletime, benign.}
proc `$`*(node: PNimrodNode): string {.compileTime.} = proc `$`*(node: PNimrodNode): string {.compileTime.} =

View file

@ -33,14 +33,16 @@ when defined(Windows):
stdout.write(prompt) stdout.write(prompt)
result = readLine(stdin, line) result = readLine(stdin, line)
proc readPasswordFromStdin*(prompt: string, password: var TaintedString) = proc readPasswordFromStdin*(prompt: string, password: var TaintedString):
bool {.tags: [ReadIOEffect, WriteIOEffect].} =
## Reads a `password` from stdin without printing it. `password` must not ## Reads a `password` from stdin without printing it. `password` must not
## be ``nil``! ## be ``nil``! Returns ``false`` if the end of the file has been reached,
## ``true`` otherwise.
proc getch(): cint {.header: "<conio.h>", importc: "_getch".} proc getch(): cint {.header: "<conio.h>", importc: "_getch".}
password.setLen(0) password.setLen(0)
var c: char var c: char
echo prompt stdout.write(prompt)
while true: while true:
c = getch().char c = getch().char
case c case c
@ -50,6 +52,8 @@ when defined(Windows):
password.setLen(password.len - 1) password.setLen(password.len - 1)
else: else:
password.add(c) password.add(c)
stdout.write "\n"
# TODO: How to detect EOF on Windows?
else: else:
import readline, history, termios, unsigned import readline, history, termios, unsigned
@ -80,7 +84,8 @@ else:
discard readline.bind_key('\t'.ord, doNothing) discard readline.bind_key('\t'.ord, doNothing)
proc readPasswordFromStdin*(prompt: string, password: var TaintedString) = proc readPasswordFromStdin*(prompt: string, password: var TaintedString):
bool {.tags: [ReadIOEffect, WriteIOEffect].} =
password.setLen(0) password.setLen(0)
let fd = stdin.getFileHandle() let fd = stdin.getFileHandle()
var cur, old: Termios var cur, old: Termios
@ -89,10 +94,11 @@ else:
cur.lflag = cur.lflag and not Tcflag(ECHO) cur.lflag = cur.lflag and not Tcflag(ECHO)
discard fd.tcsetattr(TCSADRAIN, cur.addr) discard fd.tcsetattr(TCSADRAIN, cur.addr)
stdout.write prompt stdout.write prompt
discard stdin.readLine(password) result = stdin.readLine(password)
stdout.write "\n"
discard fd.tcsetattr(TCSADRAIN, old.addr) discard fd.tcsetattr(TCSADRAIN, old.addr)
proc readPasswordFromStdin*(prompt: string): TaintedString = proc readPasswordFromStdin*(prompt: string): TaintedString =
## Reads a password from stdin without printing it. ## Reads a password from stdin without printing it.
result = TaintedString("") result = TaintedString("")
readPasswordFromStdin(prompt, result) discard readPasswordFromStdin(prompt, result)

View file

@ -70,16 +70,9 @@ const
STDIN_FILENO* = 0 ## File number of stdin; STDIN_FILENO* = 0 ## File number of stdin;
STDOUT_FILENO* = 1 ## File number of stdout; STDOUT_FILENO* = 1 ## File number of stdout;
when defined(endb):
# to not break bootstrapping again ...
type type
TDIR* {.importc: "DIR", header: "<dirent.h>", TDIR* {.importc: "DIR", header: "<dirent.h>",
final, pure, incompleteStruct.} = object incompleteStruct.} = object
## A type representing a directory stream.
else:
type
TDIR* {.importc: "DIR", header: "<dirent.h>",
final, pure.} = object
## A type representing a directory stream. ## A type representing a directory stream.
type type

View file

@ -415,7 +415,7 @@ proc `$`*[A, B](t: TableRef[A, B]): string =
proc `==`*[A, B](s, t: TableRef[A, B]): bool = proc `==`*[A, B](s, t: TableRef[A, B]): bool =
if isNil(s): result = isNil(t) if isNil(s): result = isNil(t)
elif isNil(t): result = false elif isNil(t): result = false
else: result = equalsImpl() else: equalsImpl()
proc newTableFrom*[A, B, C](collection: A, index: proc(x: B): C): TableRef[C, B] = proc newTableFrom*[A, B, C](collection: A, index: proc(x: B): C): TableRef[C, B] =
## Index the collection with the proc provided. ## Index the collection with the proc provided.

View file

@ -93,9 +93,9 @@ proc nextPowerOfTwo*(x: int): int {.noSideEffect.} =
result = x - 1 result = x - 1
when defined(cpu64): when defined(cpu64):
result = result or (result shr 32) result = result or (result shr 32)
when sizeof(int) > 16: when sizeof(int) > 2:
result = result or (result shr 16) result = result or (result shr 16)
when sizeof(int) > 8: when sizeof(int) > 1:
result = result or (result shr 8) result = result or (result shr 8)
result = result or (result shr 4) result = result or (result shr 4)
result = result or (result shr 2) result = result or (result shr 2)
@ -129,25 +129,25 @@ proc variance*(x: openArray[float]): float {.noSideEffect.} =
result = result + diff*diff result = result + diff*diff
result = result / toFloat(len(x)) result = result / toFloat(len(x))
proc random*(max: int): int {.gcsafe.} proc random*(max: int): int {.benign.}
## returns a random number in the range 0..max-1. The sequence of ## returns a random number in the range 0..max-1. The sequence of
## random number is always the same, unless `randomize` is called ## random number is always the same, unless `randomize` is called
## which initializes the random number generator with a "random" ## which initializes the random number generator with a "random"
## number, i.e. a tickcount. ## number, i.e. a tickcount.
proc random*(max: float): float {.gcsafe.} proc random*(max: float): float {.benign.}
## returns a random number in the range 0..<max. The sequence of ## returns a random number in the range 0..<max. The sequence of
## random number is always the same, unless `randomize` is called ## random number is always the same, unless `randomize` is called
## which initializes the random number generator with a "random" ## which initializes the random number generator with a "random"
## number, i.e. a tickcount. This has a 16-bit resolution on windows ## number, i.e. a tickcount. This has a 16-bit resolution on windows
## and a 48-bit resolution on other platforms. ## and a 48-bit resolution on other platforms.
proc randomize*() {.gcsafe.} proc randomize*() {.benign.}
## initializes the random number generator with a "random" ## initializes the random number generator with a "random"
## number, i.e. a tickcount. Note: Does nothing for the JavaScript target, ## number, i.e. a tickcount. Note: Does nothing for the JavaScript target,
## as JavaScript does not support this. ## as JavaScript does not support this.
proc randomize*(seed: int) {.gcsafe.} proc randomize*(seed: int) {.benign.}
## initializes the random number generator with a specific seed. ## initializes the random number generator with a specific seed.
## Note: Does nothing for the JavaScript target, ## Note: Does nothing for the JavaScript target,
## as JavaScript does not support this. ## as JavaScript does not support this.

View file

@ -321,7 +321,8 @@ proc bindAddr*(socket: Socket, port = Port(0), address = "") {.
dealloc(aiList) dealloc(aiList)
proc acceptAddr*(server: Socket, client: var Socket, address: var string, proc acceptAddr*(server: Socket, client: var Socket, address: var string,
flags = {SocketFlag.SafeDisconn}) {.tags: [ReadIOEffect].} = flags = {SocketFlag.SafeDisconn}) {.
tags: [ReadIOEffect], gcsafe, locks: 0.} =
## Blocks until a connection is being made from a client. When a connection ## Blocks until a connection is being made from a client. When a connection
## is made sets ``client`` to the client socket and ``address`` to the address ## is made sets ``client`` to the client socket and ``address`` to the address
## of the connecting client. ## of the connecting client.
@ -938,8 +939,12 @@ proc connect*(socket: Socket, address: string, port = Port(0), timeout: int,
doAssert socket.handshake() doAssert socket.handshake()
socket.fd.setBlocking(true) socket.fd.setBlocking(true)
proc isSsl*(socket: Socket): bool = return socket.isSSL proc isSsl*(socket: Socket): bool =
## Determines whether ``socket`` is a SSL socket. ## Determines whether ``socket`` is a SSL socket.
when defined(ssl):
result = socket.isSSL
else:
result = false
proc getFd*(socket: Socket): SocketHandle = return socket.fd proc getFd*(socket: Socket): SocketHandle = return socket.fd
## Returns the socket's file descriptor ## Returns the socket's file descriptor

View file

@ -1010,6 +1010,14 @@ proc copyFile*(source, dest: string) {.rtl, extern: "nos$1",
proc moveFile*(source, dest: string) {.rtl, extern: "nos$1", proc moveFile*(source, dest: string) {.rtl, extern: "nos$1",
tags: [ReadIOEffect, WriteIOEffect].} = tags: [ReadIOEffect, WriteIOEffect].} =
## Moves a file from `source` to `dest`. If this fails, `OSError` is raised. ## Moves a file from `source` to `dest`. If this fails, `OSError` is raised.
when defined(Windows):
when useWinUnicode:
let s = newWideCString(source)
let d = newWideCString(dest)
if moveFileW(s, d, 0'i32) == 0'i32: raiseOSError(osLastError())
else:
if moveFileA(source, dest, 0'i32) == 0'i32: raiseOSError(osLastError())
else:
if c_rename(source, dest) != 0'i32: if c_rename(source, dest) != 0'i32:
raise newException(OSError, $strerror(errno)) raise newException(OSError, $strerror(errno))
@ -1339,7 +1347,7 @@ proc rawRemoveDir(dir: string) =
if rmdir(dir) != 0'i32 and errno != ENOENT: raiseOSError(osLastError()) if rmdir(dir) != 0'i32 and errno != ENOENT: raiseOSError(osLastError())
proc removeDir*(dir: string) {.rtl, extern: "nos$1", tags: [ proc removeDir*(dir: string) {.rtl, extern: "nos$1", tags: [
WriteDirEffect, ReadDirEffect].} = WriteDirEffect, ReadDirEffect], benign.} =
## Removes the directory `dir` including all subdirectories and files ## Removes the directory `dir` including all subdirectories and files
## in `dir` (recursively). ## in `dir` (recursively).
## ##
@ -1385,7 +1393,7 @@ proc createDir*(dir: string) {.rtl, extern: "nos$1", tags: [WriteDirEffect].} =
rawCreateDir(dir) rawCreateDir(dir)
proc copyDir*(source, dest: string) {.rtl, extern: "nos$1", proc copyDir*(source, dest: string) {.rtl, extern: "nos$1",
tags: [WriteIOEffect, ReadIOEffect].} = tags: [WriteIOEffect, ReadIOEffect], benign.} =
## Copies a directory from `source` to `dest`. ## Copies a directory from `source` to `dest`.
## ##
## If this fails, `OSError` is raised. On the Windows platform this proc will ## If this fails, `OSError` is raised. On the Windows platform this proc will
@ -1558,7 +1566,7 @@ proc copyFileWithPermissions*(source, dest: string,
proc copyDirWithPermissions*(source, dest: string, proc copyDirWithPermissions*(source, dest: string,
ignorePermissionErrors = true) {.rtl, extern: "nos$1", ignorePermissionErrors = true) {.rtl, extern: "nos$1",
tags: [WriteIOEffect, ReadIOEffect].} = tags: [WriteIOEffect, ReadIOEffect], benign.} =
## Copies a directory from `source` to `dest` preserving file permissions. ## Copies a directory from `source` to `dest` preserving file permissions.
## ##
## If this fails, `OSError` is raised. This is a wrapper proc around `copyDir() ## If this fails, `OSError` is raised. This is a wrapper proc around `copyDir()

View file

@ -299,7 +299,7 @@ when isMainModule and not defined(nimdoc):
sock: Socket sock: Socket
var sock = socket() var sock = socket()
if sock == sockets.InvalidSocket: raiseOSError(osLastError()) if sock == sockets.invalidSocket: raiseOSError(osLastError())
#sock.setBlocking(false) #sock.setBlocking(false)
sock.connect("irc.freenode.net", Port(6667)) sock.connect("irc.freenode.net", Port(6667))

View file

@ -16,7 +16,7 @@
# of the standard library! # of the standard library!
import import
strutils strutils, parseutils
include "system/inclrtl" include "system/inclrtl"
@ -63,44 +63,44 @@ elif defined(windows):
elif defined(JS): elif defined(JS):
type type
Time* {.importc.} = object Time* {.importc.} = object
getDay: proc (): int {.tags: [], raises: [], gcsafe.} getDay: proc (): int {.tags: [], raises: [], benign.}
getFullYear: proc (): int {.tags: [], raises: [], gcsafe.} getFullYear: proc (): int {.tags: [], raises: [], benign.}
getHours: proc (): int {.tags: [], raises: [], gcsafe.} getHours: proc (): int {.tags: [], raises: [], benign.}
getMilliseconds: proc (): int {.tags: [], raises: [], gcsafe.} getMilliseconds: proc (): int {.tags: [], raises: [], benign.}
getMinutes: proc (): int {.tags: [], raises: [], gcsafe.} getMinutes: proc (): int {.tags: [], raises: [], benign.}
getMonth: proc (): int {.tags: [], raises: [], gcsafe.} getMonth: proc (): int {.tags: [], raises: [], benign.}
getSeconds: proc (): int {.tags: [], raises: [], gcsafe.} getSeconds: proc (): int {.tags: [], raises: [], benign.}
getTime: proc (): int {.tags: [], raises: [], gcsafe.} getTime: proc (): int {.tags: [], raises: [], benign.}
getTimezoneOffset: proc (): int {.tags: [], raises: [], gcsafe.} getTimezoneOffset: proc (): int {.tags: [], raises: [], benign.}
getDate: proc (): int {.tags: [], raises: [], gcsafe.} getDate: proc (): int {.tags: [], raises: [], benign.}
getUTCDate: proc (): int {.tags: [], raises: [], gcsafe.} getUTCDate: proc (): int {.tags: [], raises: [], benign.}
getUTCFullYear: proc (): int {.tags: [], raises: [], gcsafe.} getUTCFullYear: proc (): int {.tags: [], raises: [], benign.}
getUTCHours: proc (): int {.tags: [], raises: [], gcsafe.} getUTCHours: proc (): int {.tags: [], raises: [], benign.}
getUTCMilliseconds: proc (): int {.tags: [], raises: [], gcsafe.} getUTCMilliseconds: proc (): int {.tags: [], raises: [], benign.}
getUTCMinutes: proc (): int {.tags: [], raises: [], gcsafe.} getUTCMinutes: proc (): int {.tags: [], raises: [], benign.}
getUTCMonth: proc (): int {.tags: [], raises: [], gcsafe.} getUTCMonth: proc (): int {.tags: [], raises: [], benign.}
getUTCSeconds: proc (): int {.tags: [], raises: [], gcsafe.} getUTCSeconds: proc (): int {.tags: [], raises: [], benign.}
getUTCDay: proc (): int {.tags: [], raises: [], gcsafe.} getUTCDay: proc (): int {.tags: [], raises: [], benign.}
getYear: proc (): int {.tags: [], raises: [], gcsafe.} getYear: proc (): int {.tags: [], raises: [], benign.}
parse: proc (s: cstring): Time {.tags: [], raises: [], gcsafe.} parse: proc (s: cstring): Time {.tags: [], raises: [], benign.}
setDate: proc (x: int) {.tags: [], raises: [], gcsafe.} setDate: proc (x: int) {.tags: [], raises: [], benign.}
setFullYear: proc (x: int) {.tags: [], raises: [], gcsafe.} setFullYear: proc (x: int) {.tags: [], raises: [], benign.}
setHours: proc (x: int) {.tags: [], raises: [], gcsafe.} setHours: proc (x: int) {.tags: [], raises: [], benign.}
setMilliseconds: proc (x: int) {.tags: [], raises: [], gcsafe.} setMilliseconds: proc (x: int) {.tags: [], raises: [], benign.}
setMinutes: proc (x: int) {.tags: [], raises: [], gcsafe.} setMinutes: proc (x: int) {.tags: [], raises: [], benign.}
setMonth: proc (x: int) {.tags: [], raises: [], gcsafe.} setMonth: proc (x: int) {.tags: [], raises: [], benign.}
setSeconds: proc (x: int) {.tags: [], raises: [], gcsafe.} setSeconds: proc (x: int) {.tags: [], raises: [], benign.}
setTime: proc (x: int) {.tags: [], raises: [], gcsafe.} setTime: proc (x: int) {.tags: [], raises: [], benign.}
setUTCDate: proc (x: int) {.tags: [], raises: [], gcsafe.} setUTCDate: proc (x: int) {.tags: [], raises: [], benign.}
setUTCFullYear: proc (x: int) {.tags: [], raises: [], gcsafe.} setUTCFullYear: proc (x: int) {.tags: [], raises: [], benign.}
setUTCHours: proc (x: int) {.tags: [], raises: [], gcsafe.} setUTCHours: proc (x: int) {.tags: [], raises: [], benign.}
setUTCMilliseconds: proc (x: int) {.tags: [], raises: [], gcsafe.} setUTCMilliseconds: proc (x: int) {.tags: [], raises: [], benign.}
setUTCMinutes: proc (x: int) {.tags: [], raises: [], gcsafe.} setUTCMinutes: proc (x: int) {.tags: [], raises: [], benign.}
setUTCMonth: proc (x: int) {.tags: [], raises: [], gcsafe.} setUTCMonth: proc (x: int) {.tags: [], raises: [], benign.}
setUTCSeconds: proc (x: int) {.tags: [], raises: [], gcsafe.} setUTCSeconds: proc (x: int) {.tags: [], raises: [], benign.}
setYear: proc (x: int) {.tags: [], raises: [], gcsafe.} setYear: proc (x: int) {.tags: [], raises: [], benign.}
toGMTString: proc (): cstring {.tags: [], raises: [], gcsafe.} toGMTString: proc (): cstring {.tags: [], raises: [], benign.}
toLocaleString: proc (): cstring {.tags: [], raises: [], gcsafe.} toLocaleString: proc (): cstring {.tags: [], raises: [], benign.}
type type
TimeInfo* = object of RootObj ## represents a time in different parts TimeInfo* = object of RootObj ## represents a time in different parts
@ -139,42 +139,42 @@ type
{.deprecated: [TMonth: Month, TWeekDay: WeekDay, TTime: Time, {.deprecated: [TMonth: Month, TWeekDay: WeekDay, TTime: Time,
TTimeInterval: TimeInterval, TTimeInfo: TimeInfo].} TTimeInterval: TimeInterval, TTimeInfo: TimeInfo].}
proc getTime*(): Time {.tags: [TimeEffect], gcsafe.} proc getTime*(): Time {.tags: [TimeEffect], benign.}
## gets the current calendar time as a UNIX epoch value (number of seconds ## gets the current calendar time as a UNIX epoch value (number of seconds
## elapsed since 1970) with integer precission. Use epochTime for higher ## elapsed since 1970) with integer precission. Use epochTime for higher
## resolution. ## resolution.
proc getLocalTime*(t: Time): TimeInfo {.tags: [TimeEffect], raises: [], gcsafe.} proc getLocalTime*(t: Time): TimeInfo {.tags: [TimeEffect], raises: [], benign.}
## converts the calendar time `t` to broken-time representation, ## converts the calendar time `t` to broken-time representation,
## expressed relative to the user's specified time zone. ## expressed relative to the user's specified time zone.
proc getGMTime*(t: Time): TimeInfo {.tags: [TimeEffect], raises: [], gcsafe.} proc getGMTime*(t: Time): TimeInfo {.tags: [TimeEffect], raises: [], benign.}
## converts the calendar time `t` to broken-down time representation, ## converts the calendar time `t` to broken-down time representation,
## expressed in Coordinated Universal Time (UTC). ## expressed in Coordinated Universal Time (UTC).
proc timeInfoToTime*(timeInfo: TimeInfo): Time {.tags: [], gcsafe.} proc timeInfoToTime*(timeInfo: TimeInfo): Time {.tags: [], benign.}
## converts a broken-down time structure to ## converts a broken-down time structure to
## calendar time representation. The function ignores the specified ## calendar time representation. The function ignores the specified
## contents of the structure members `weekday` and `yearday` and recomputes ## contents of the structure members `weekday` and `yearday` and recomputes
## them from the other information in the broken-down time structure. ## them from the other information in the broken-down time structure.
proc fromSeconds*(since1970: float): Time {.tags: [], raises: [], gcsafe.} proc fromSeconds*(since1970: float): Time {.tags: [], raises: [], benign.}
## Takes a float which contains the number of seconds since the unix epoch and ## Takes a float which contains the number of seconds since the unix epoch and
## returns a time object. ## returns a time object.
proc fromSeconds*(since1970: int64): Time {.tags: [], raises: [], gcsafe.} = proc fromSeconds*(since1970: int64): Time {.tags: [], raises: [], benign.} =
## Takes an int which contains the number of seconds since the unix epoch and ## Takes an int which contains the number of seconds since the unix epoch and
## returns a time object. ## returns a time object.
fromSeconds(float(since1970)) fromSeconds(float(since1970))
proc toSeconds*(time: Time): float {.tags: [], raises: [], gcsafe.} proc toSeconds*(time: Time): float {.tags: [], raises: [], benign.}
## Returns the time in seconds since the unix epoch. ## Returns the time in seconds since the unix epoch.
proc `$` *(timeInfo: TimeInfo): string {.tags: [], raises: [], gcsafe.} proc `$` *(timeInfo: TimeInfo): string {.tags: [], raises: [], benign.}
## converts a `TimeInfo` object to a string representation. ## converts a `TimeInfo` object to a string representation.
proc `$` *(time: Time): string {.tags: [], raises: [], gcsafe.} proc `$` *(time: Time): string {.tags: [], raises: [], benign.}
## converts a calendar time to a string representation. ## converts a calendar time to a string representation.
proc `-`*(a, b: Time): int64 {. proc `-`*(a, b: Time): int64 {.
rtl, extern: "ntDiffTime", tags: [], raises: [].} rtl, extern: "ntDiffTime", tags: [], raises: [], benign.}
## computes the difference of two calendar times. Result is in seconds. ## computes the difference of two calendar times. Result is in seconds.
proc `<`*(a, b: Time): bool {. proc `<`*(a, b: Time): bool {.
@ -194,14 +194,14 @@ proc `==`*(a, b: Time): bool {.
when not defined(JS): when not defined(JS):
proc getTzname*(): tuple[nonDST, DST: string] {.tags: [TimeEffect], raises: [], proc getTzname*(): tuple[nonDST, DST: string] {.tags: [TimeEffect], raises: [],
gcsafe.} benign.}
## returns the local timezone; ``nonDST`` is the name of the local non-DST ## returns the local timezone; ``nonDST`` is the name of the local non-DST
## timezone, ``DST`` is the name of the local DST timezone. ## timezone, ``DST`` is the name of the local DST timezone.
proc getTimezone*(): int {.tags: [TimeEffect], raises: [], gcsafe.} proc getTimezone*(): int {.tags: [TimeEffect], raises: [], benign.}
## returns the offset of the local (non-DST) timezone in seconds west of UTC. ## returns the offset of the local (non-DST) timezone in seconds west of UTC.
proc getStartMilsecs*(): int {.deprecated, tags: [TimeEffect], gcsafe.} proc getStartMilsecs*(): int {.deprecated, tags: [TimeEffect], benign.}
## get the miliseconds from the start of the program. **Deprecated since ## get the miliseconds from the start of the program. **Deprecated since
## version 0.8.10.** Use ``epochTime`` or ``cpuTime`` instead. ## version 0.8.10.** Use ``epochTime`` or ``cpuTime`` instead.
@ -744,6 +744,285 @@ proc format*(info: TimeInfo, f: string): string =
{.pop.} {.pop.}
proc parseToken(info: var TimeInfo; token, value: string; j: var int) =
## Helper of the parse proc to parse individual tokens.
var sv: int
case token
of "d":
var pd = parseInt(value[j..j+1], sv)
info.monthday = sv
j += pd
of "dd":
info.monthday = value[j..j+1].parseInt()
j += 2
of "ddd":
case value[j..j+2].toLower():
of "sun":
info.weekday = dSun
of "mon":
info.weekday = dMon
of "tue":
info.weekday = dTue
of "wed":
info.weekday = dWed
of "thu":
info.weekday = dThu
of "fri":
info.weekday = dFri
of "sat":
info.weekday = dSat
else:
raise newException(ValueError, "invalid day of week ")
j += 3
of "dddd":
if value.len >= j+6 and value[j..j+5].cmpIgnoreCase("sunday") == 0:
info.weekday = dSun
j += 6
elif value.len >= j+6 and value[j..j+5].cmpIgnoreCase("monday") == 0:
info.weekday = dMon
j += 6
elif value.len >= j+7 and value[j..j+6].cmpIgnoreCase("tuesday") == 0:
info.weekday = dTue
j += 7
elif value.len >= j+9 and value[j..j+8].cmpIgnoreCase("wednesday") == 0:
info.weekday = dWed
j += 9
elif value.len >= j+8 and value[j..j+7].cmpIgnoreCase("thursday") == 0:
info.weekday = dThu
j += 8
elif value.len >= j+6 and value[j..j+5].cmpIgnoreCase("friday") == 0:
info.weekday = dFri
j += 6
elif value.len >= j+8 and value[j..j+7].cmpIgnoreCase("saturday") == 0:
info.weekday = dSat
j += 8
else:
raise newException(ValueError, "invalid day of week ")
of "h", "H":
var pd = parseInt(value[j..j+1], sv)
info.hour = sv
j += pd
of "hh", "HH":
info.hour = value[j..j+1].parseInt()
j += 2
of "m":
var pd = parseInt(value[j..j+1], sv)
info.minute = sv
j += pd
of "mm":
info.minute = value[j..j+1].parseInt()
j += 2
of "M":
var pd = parseInt(value[j..j+1], sv)
info.month = Month(sv-1)
info.monthday = sv
j += pd
of "MM":
var month = value[j..j+1].parseInt()
j += 2
info.month = Month(month-1)
of "MMM":
case value[j..j+2].toLower():
of "jan":
info.month = mJan
of "feb":
info.month = mFeb
of "mar":
info.month = mMar
of "apr":
info.month = mApr
of "may":
info.month = mMay
of "jun":
info.month = mJun
of "jul":
info.month = mJul
of "aug":
info.month = mAug
of "sep":
info.month = mSep
of "oct":
info.month = mOct
of "nov":
info.month = mNov
of "dec":
info.month = mDec
else:
raise newException(ValueError, "invalid month")
j += 3
of "MMMM":
if value.len >= j+7 and value[j..j+6].cmpIgnoreCase("january") == 0:
info.month = mJan
j += 7
elif value.len >= j+8 and value[j..j+7].cmpIgnoreCase("february") == 0:
info.month = mFeb
j += 8
elif value.len >= j+5 and value[j..j+4].cmpIgnoreCase("march") == 0:
info.month = mMar
j += 5
elif value.len >= j+5 and value[j..j+4].cmpIgnoreCase("april") == 0:
info.month = mApr
j += 5
elif value.len >= j+3 and value[j..j+2].cmpIgnoreCase("may") == 0:
info.month = mMay
j += 3
elif value.len >= j+4 and value[j..j+3].cmpIgnoreCase("june") == 0:
info.month = mJun
j += 4
elif value.len >= j+4 and value[j..j+3].cmpIgnoreCase("july") == 0:
info.month = mJul
j += 4
elif value.len >= j+6 and value[j..j+5].cmpIgnoreCase("august") == 0:
info.month = mAug
j += 6
elif value.len >= j+9 and value[j..j+8].cmpIgnoreCase("september") == 0:
info.month = mSep
j += 9
elif value.len >= j+7 and value[j..j+6].cmpIgnoreCase("october") == 0:
info.month = mOct
j += 7
elif value.len >= j+8 and value[j..j+7].cmpIgnoreCase("november") == 0:
info.month = mNov
j += 8
elif value.len >= j+8 and value[j..j+7].cmpIgnoreCase("december") == 0:
info.month = mDec
j += 8
else:
raise newException(ValueError, "invalid month")
of "s":
var pd = parseInt(value[j..j+1], sv)
info.second = sv
j += pd
of "ss":
info.second = value[j..j+1].parseInt()
j += 2
of "t":
if value[j] == 'P' and info.hour > 0 and info.hour < 12:
info.hour += 12
j += 1
of "tt":
if value[j..j+1] == "PM" and info.hour > 0 and info.hour < 12:
info.hour += 12
j += 2
of "yy":
# Assumes current century
var year = value[j..j+1].parseInt()
var thisCen = getLocalTime(getTime()).year div 100
info.year = thisCen*100 + year
j += 2
of "yyyy":
info.year = value[j..j+3].parseInt()
j += 4
of "z":
if value[j] == '+':
info.timezone = parseInt($value[j+1])
elif value[j] == '-':
info.timezone = 0-parseInt($value[j+1])
else:
raise newException(ValueError, "Sign for timezone " & value[j])
j += 2
of "zz":
if value[j] == '+':
info.timezone = value[j+1..j+2].parseInt()
elif value[j] == '-':
info.timezone = 0-value[j+1..j+2].parseInt()
else:
raise newException(ValueError, "Sign for timezone " & value[j])
j += 3
of "zzz":
if value[j] == '+':
info.timezone = value[j+1..j+2].parseInt()
elif value[j] == '-':
info.timezone = 0-value[j+1..j+2].parseInt()
else:
raise newException(ValueError, "Sign for timezone " & value[j])
j += 6
of "ZZZ":
info.tzname = value[j..j+2].toUpper()
j += 3
else:
# Ignore the token and move forward in the value string by the same length
j += token.len
proc parse*(value, layout: string): TimeInfo =
## This function parses a date/time string using the standard format identifiers (below)
## The function defaults information not provided in the format string from the running program (timezone, month, year, etc)
##
## ========== ================================================================================= ================================================
## Specifier Description Example
## ========== ================================================================================= ================================================
## d Numeric value of the day of the month, it will be one or two digits long. ``1/04/2012 -> 1``, ``21/04/2012 -> 21``
## dd Same as above, but always two digits. ``1/04/2012 -> 01``, ``21/04/2012 -> 21``
## ddd Three letter string which indicates the day of the week. ``Saturday -> Sat``, ``Monday -> Mon``
## dddd Full string for the day of the week. ``Saturday -> Saturday``, ``Monday -> Monday``
## h The hours in one digit if possible. Ranging from 0-12. ``5pm -> 5``, ``2am -> 2``
## hh The hours in two digits always. If the hour is one digit 0 is prepended. ``5pm -> 05``, ``11am -> 11``
## H The hours in one digit if possible, randing from 0-24. ``5pm -> 17``, ``2am -> 2``
## HH The hours in two digits always. 0 is prepended if the hour is one digit. ``5pm -> 17``, ``2am -> 02``
## m The minutes in 1 digit if possible. ``5:30 -> 30``, ``2:01 -> 1``
## mm Same as above but always 2 digits, 0 is prepended if the minute is one digit. ``5:30 -> 30``, ``2:01 -> 01``
## M The month in one digit if possible. ``September -> 9``, ``December -> 12``
## MM The month in two digits always. 0 is prepended. ``September -> 09``, ``December -> 12``
## MMM Abbreviated three-letter form of the month. ``September -> Sep``, ``December -> Dec``
## MMMM Full month string, properly capitalized. ``September -> September``
## s Seconds as one digit if possible. ``00:00:06 -> 6``
## ss Same as above but always two digits. 0 is prepended. ``00:00:06 -> 06``
## t ``A`` when time is in the AM. ``P`` when time is in the PM.
## tt Same as above, but ``AM`` and ``PM`` instead of ``A`` and ``P`` respectively.
## yy Displays the year to two digits. ``2012 -> 12``
## yyyy Displays the year to four digits. ``2012 -> 2012``
## z Displays the timezone offset from UTC. ``GMT+7 -> +7``, ``GMT-5 -> -5``
## zz Same as above but with leading 0. ``GMT+7 -> +07``, ``GMT-5 -> -05``
## zzz Same as above but with ``:00``. ``GMT+7 -> +07:00``, ``GMT-5 -> -05:00``
## ZZZ Displays the name of the timezone. ``GMT -> GMT``, ``EST -> EST``
## ========== ================================================================================= ================================================
##
## Other strings can be inserted by putting them in ``''``. For example
## ``hh'->'mm`` will give ``01->56``. The following characters can be
## inserted without quoting them: ``:`` ``-`` ``(`` ``)`` ``/`` ``[`` ``]``
## ``,``. However you don't need to necessarily separate format specifiers, a
## unambiguous format string like ``yyyyMMddhhmmss`` is valid too.
var i = 0 # pointer for format string
var j = 0 # pointer for value string
var token = ""
# Assumes current day of month, month and year, but time is reset to 00:00:00. Weekday will be reset after parsing.
var info = getLocalTime(getTime())
info.hour = 0
info.minute = 0
info.second = 0
while true:
case layout[i]
of ' ', '-', '/', ':', '\'', '\0', '(', ')', '[', ']', ',':
if token.len > 0:
parseToken(info, token, value, j)
# Reset token
token = ""
# Break if at end of line
if layout[i] == '\0': break
# Skip separator and everything between single quotes
# These are literals in both the layout and the value string
if layout[i] == '\'':
inc(i)
inc(j)
while layout[i] != '\'' and layout.len-1 > i:
inc(i)
inc(j)
else:
inc(i)
inc(j)
else:
# Check if the letter being added matches previous accumulated buffer.
if token.len < 1 or token[high(token)] == layout[i]:
token.add(layout[i])
inc(i)
else:
parseToken(info, token, value, j)
token = ""
# Reset weekday as it might not have been provided and the default may be wrong
info.weekday = getLocalTime(timeInfoToTime(info)).weekday
return info
when isMainModule: when isMainModule:
# $ date --date='@2147483647' # $ date --date='@2147483647'
# Tue 19 Jan 03:14:07 GMT 2038 # Tue 19 Jan 03:14:07 GMT 2038
@ -778,3 +1057,51 @@ when isMainModule:
# Interval tests # Interval tests
assert((t4 - initInterval(years = 2)).format("yyyy") == "1995") assert((t4 - initInterval(years = 2)).format("yyyy") == "1995")
assert((t4 - initInterval(years = 7, minutes = 34, seconds = 24)).format("yyyy mm ss") == "1990 24 10") assert((t4 - initInterval(years = 7, minutes = 34, seconds = 24)).format("yyyy mm ss") == "1990 24 10")
var s = "Tuesday at 09:04am on Dec 15, 2015"
var f = "dddd at hh:mmtt on MMM d, yyyy"
assert($s.parse(f) == "Tue Dec 15 09:04:00 2015")
# ANSIC = "Mon Jan _2 15:04:05 2006"
s = "Mon Jan 2 15:04:05 2006"
f = "ddd MMM d HH:mm:ss yyyy"
assert($s.parse(f) == "Mon Jan 2 15:04:05 2006")
# UnixDate = "Mon Jan _2 15:04:05 MST 2006"
s = "Mon Jan 2 15:04:05 MST 2006"
f = "ddd MMM d HH:mm:ss ZZZ yyyy"
assert($s.parse(f) == "Mon Jan 2 15:04:05 2006")
# RubyDate = "Mon Jan 02 15:04:05 -0700 2006"
s = "Mon Jan 02 15:04:05 -07:00 2006"
f = "ddd MMM dd HH:mm:ss zzz yyyy"
assert($s.parse(f) == "Mon Jan 2 15:04:05 2006")
# RFC822 = "02 Jan 06 15:04 MST"
s = "02 Jan 06 15:04 MST"
f = "dd MMM yy HH:mm ZZZ"
assert($s.parse(f) == "Mon Jan 2 15:04:00 2006")
# RFC822Z = "02 Jan 06 15:04 -0700" # RFC822 with numeric zone
s = "02 Jan 06 15:04 -07:00"
f = "dd MMM yy HH:mm zzz"
assert($s.parse(f) == "Mon Jan 2 15:04:00 2006")
# RFC850 = "Monday, 02-Jan-06 15:04:05 MST"
s = "Monday, 02-Jan-06 15:04:05 MST"
f = "dddd, dd-MMM-yy HH:mm:ss ZZZ"
assert($s.parse(f) == "Mon Jan 2 15:04:05 2006")
# RFC1123 = "Mon, 02 Jan 2006 15:04:05 MST"
s = "Mon, 02 Jan 2006 15:04:05 MST"
f = "ddd, dd MMM yyyy HH:mm:ss ZZZ"
assert($s.parse(f) == "Mon Jan 2 15:04:05 2006")
# RFC1123Z = "Mon, 02 Jan 2006 15:04:05 -0700" # RFC1123 with numeric zone
s = "Mon, 02 Jan 2006 15:04:05 -07:00"
f = "ddd, dd MMM yyyy HH:mm:ss zzz"
assert($s.parse(f) == "Mon Jan 2 15:04:05 2006")
# RFC3339 = "2006-01-02T15:04:05Z07:00"
s = "2006-01-02T15:04:05Z-07:00"
f = "yyyy-MM-ddTHH:mm:ssZzzz"
assert($s.parse(f) == "Mon Jan 2 15:04:05 2006")
# RFC3339Nano = "2006-01-02T15:04:05.999999999Z07:00"
s = "2006-01-02T15:04:05.999999999Z-07:00"
f = "yyyy-MM-ddTHH:mm:ss.999999999Zzzz"
assert($s.parse(f) == "Mon Jan 2 15:04:05 2006")
# Kitchen = "3:04PM"
s = "3:04PM"
f = "h:mmtt"
echo "Kitchen: " & $s.parse(f)

View file

@ -2098,17 +2098,16 @@ when not defined(nimrodVM) and hostOS != "standalone":
## returns an informative string about the GC's activity. This may be useful ## returns an informative string about the GC's activity. This may be useful
## for tweaking. ## for tweaking.
# XXX mark these as 'locks: 0' once 0.10.0 has been released proc GC_ref*[T](x: ref T) {.magic: "GCref", benign.}
proc GC_ref*[T](x: ref T) {.magic: "GCref", gcsafe.} proc GC_ref*[T](x: seq[T]) {.magic: "GCref", benign.}
proc GC_ref*[T](x: seq[T]) {.magic: "GCref", gcsafe.} proc GC_ref*(x: string) {.magic: "GCref", benign.}
proc GC_ref*(x: string) {.magic: "GCref", gcsafe.}
## marks the object `x` as referenced, so that it will not be freed until ## marks the object `x` as referenced, so that it will not be freed until
## it is unmarked via `GC_unref`. If called n-times for the same object `x`, ## it is unmarked via `GC_unref`. If called n-times for the same object `x`,
## n calls to `GC_unref` are needed to unmark `x`. ## n calls to `GC_unref` are needed to unmark `x`.
proc GC_unref*[T](x: ref T) {.magic: "GCunref", gcsafe.} proc GC_unref*[T](x: ref T) {.magic: "GCunref", benign.}
proc GC_unref*[T](x: seq[T]) {.magic: "GCunref", gcsafe.} proc GC_unref*[T](x: seq[T]) {.magic: "GCunref", benign.}
proc GC_unref*(x: string) {.magic: "GCunref", gcsafe.} proc GC_unref*(x: string) {.magic: "GCunref", benign.}
## see the documentation of `GC_ref`. ## see the documentation of `GC_ref`.
template accumulateResult*(iter: expr) = template accumulateResult*(iter: expr) =
@ -2248,12 +2247,7 @@ when not declared(sysFatal):
e.msg = message & arg e.msg = message & arg
raise e raise e
when defined(nimlocks): proc getTypeInfo*[T](x: T): pointer {.magic: "GetTypeInfo", benign.}
proc getTypeInfo*[T](x: T): pointer {.magic: "GetTypeInfo", gcsafe, locks: 0.}
## get type information for `x`. Ordinary code should not use this, but
## the `typeinfo` module instead.
else:
proc getTypeInfo*[T](x: T): pointer {.magic: "GetTypeInfo", gcsafe.}
## get type information for `x`. Ordinary code should not use this, but ## get type information for `x`. Ordinary code should not use this, but
## the `typeinfo` module instead. ## the `typeinfo` module instead.
@ -2455,14 +2449,10 @@ when not defined(JS): #and not defined(NimrodVM):
## Returns ``false`` if the end of the file has been reached, ``true`` ## Returns ``false`` if the end of the file has been reached, ``true``
## otherwise. If ``false`` is returned `line` contains no new data. ## otherwise. If ``false`` is returned `line` contains no new data.
when not defined(booting):
proc writeln*[Ty](f: File, x: varargs[Ty, `$`]) {.inline, proc writeln*[Ty](f: File, x: varargs[Ty, `$`]) {.inline,
tags: [WriteIOEffect], gcsafe, locks: 0.} tags: [WriteIOEffect], benign.}
## writes the values `x` to `f` and then writes "\n". ## writes the values `x` to `f` and then writes "\n".
## May throw an IO exception. ## May throw an IO exception.
else:
proc writeln*[Ty](f: File, x: varargs[Ty, `$`]) {.inline,
tags: [WriteIOEffect].}
proc getFileSize*(f: File): int64 {.tags: [ReadIOEffect], benign.} proc getFileSize*(f: File): int64 {.tags: [ReadIOEffect], benign.}
## retrieves the file size (in bytes) of `f`. ## retrieves the file size (in bytes) of `f`.
@ -2576,7 +2566,7 @@ when not defined(JS): #and not defined(NimrodVM):
initAllocator() initAllocator()
when hasThreadSupport: when hasThreadSupport:
include "system/syslocks" include "system/syslocks"
include "system/threads" when hostOS != "standalone": include "system/threads"
elif not defined(nogc) and not defined(NimrodVM) and hostOS != "standalone": elif not defined(nogc) and not defined(NimrodVM) and hostOS != "standalone":
when not defined(useNimRtl) and not defined(createNimRtl): initStackBottom() when not defined(useNimRtl) and not defined(createNimRtl): initStackBottom()
initGC() initGC()

View file

@ -78,7 +78,7 @@ proc reprEnum(e: int, typ: PNimType): string {.compilerRtl.} =
type type
PByteArray = ptr array[0.. 0xffff, int8] PByteArray = ptr array[0.. 0xffff, int8]
proc addSetElem(result: var string, elem: int, typ: PNimType) {.gcsafe.} = proc addSetElem(result: var string, elem: int, typ: PNimType) {.benign.} =
case typ.kind case typ.kind
of tyEnum: add result, reprEnum(elem, typ) of tyEnum: add result, reprEnum(elem, typ)
of tyBool: add result, reprBool(bool(elem)) of tyBool: add result, reprBool(bool(elem))
@ -147,7 +147,7 @@ when not defined(useNimRtl):
for i in 0..cl.indent-1: add result, ' ' for i in 0..cl.indent-1: add result, ' '
proc reprAux(result: var string, p: pointer, typ: PNimType, proc reprAux(result: var string, p: pointer, typ: PNimType,
cl: var TReprClosure) {.gcsafe.} cl: var TReprClosure) {.benign.}
proc reprArray(result: var string, p: pointer, typ: PNimType, proc reprArray(result: var string, p: pointer, typ: PNimType,
cl: var TReprClosure) = cl: var TReprClosure) =
@ -172,7 +172,7 @@ when not defined(useNimRtl):
add result, "]" add result, "]"
proc reprRecordAux(result: var string, p: pointer, n: ptr TNimNode, proc reprRecordAux(result: var string, p: pointer, n: ptr TNimNode,
cl: var TReprClosure) {.gcsafe.} = cl: var TReprClosure) {.benign.} =
case n.kind case n.kind
of nkNone: sysAssert(false, "reprRecordAux") of nkNone: sysAssert(false, "reprRecordAux")
of nkSlot: of nkSlot:

View file

@ -284,6 +284,10 @@ when useWinUnicode:
bFailIfExists: cint): cint {. bFailIfExists: cint): cint {.
importc: "CopyFileW", stdcall, dynlib: "kernel32".} importc: "CopyFileW", stdcall, dynlib: "kernel32".}
proc moveFileW*(lpExistingFileName, lpNewFileName: WideCString,
bFailIfExists: cint): cint {.
importc: "MoveFileW", stdcall, dynlib: "kernel32".}
proc getEnvironmentStringsW*(): WideCString {. proc getEnvironmentStringsW*(): WideCString {.
stdcall, dynlib: "kernel32", importc: "GetEnvironmentStringsW".} stdcall, dynlib: "kernel32", importc: "GetEnvironmentStringsW".}
proc freeEnvironmentStringsW*(para1: WideCString): int32 {. proc freeEnvironmentStringsW*(para1: WideCString): int32 {.
@ -308,6 +312,10 @@ else:
bFailIfExists: cint): cint {. bFailIfExists: cint): cint {.
importc: "CopyFileA", stdcall, dynlib: "kernel32".} importc: "CopyFileA", stdcall, dynlib: "kernel32".}
proc moveFileA*(lpExistingFileName, lpNewFileName: cstring,
bFailIfExists: cint): cint {.
importc: "MoveFileA", stdcall, dynlib: "kernel32".}
proc getEnvironmentStringsA*(): cstring {. proc getEnvironmentStringsA*(): cstring {.
stdcall, dynlib: "kernel32", importc: "GetEnvironmentStringsA".} stdcall, dynlib: "kernel32", importc: "GetEnvironmentStringsA".}
proc freeEnvironmentStringsA*(para1: cstring): int32 {. proc freeEnvironmentStringsA*(para1: cstring): int32 {.

View file

@ -0,0 +1,22 @@
import tables
doAssert indexBy(newSeq[int](), proc(x: int):int = x) == initTable[int, int](), "empty int table"
var tbl1 = initTable[int, int]()
tbl1.add(1,1)
tbl1.add(2,2)
doAssert indexBy(@[1,2], proc(x: int):int = x) == tbl1, "int table"
type
TElem = object
foo: int
bar: string
let
elem1 = TElem(foo: 1, bar: "bar")
elem2 = TElem(foo: 2, bar: "baz")
var tbl2 = initTable[string, TElem]()
tbl2.add("bar", elem1)
tbl2.add("baz", elem2)
doAssert indexBy(@[elem1,elem2], proc(x: TElem): string = x.bar) == tbl2, "element table"

View file

@ -1,22 +1,128 @@
import tables discard """
output: '''true'''
"""
doAssert indexBy(newSeq[int](), proc(x: int):int = x) == initTable[int, int](), "empty int table" import hashes, tables
var tbl1 = initTable[int, int]() const
tbl1.add(1,1) data = {
tbl1.add(2,2) "34": 123456, "12": 789,
doAssert indexBy(@[1,2], proc(x: int):int = x) == tbl1, "int table" "90": 343, "0": 34404,
"1": 344004, "2": 344774,
"3": 342244, "4": 3412344,
"5": 341232144, "6": 34214544,
"7": 3434544, "8": 344544,
"9": 34435644, "---00": 346677844,
"10": 34484, "11": 34474, "19": 34464,
"20": 34454, "30": 34141244, "40": 344114,
"50": 344490, "60": 344491, "70": 344492,
"80": 344497}
type sorteddata = {
TElem = object "---00": 346677844,
foo: int "0": 34404,
bar: string "1": 344004,
"10": 34484,
"11": 34474,
"12": 789,
"19": 34464,
"2": 344774, "20": 34454,
"3": 342244, "30": 34141244,
"34": 123456,
"4": 3412344, "40": 344114,
"5": 341232144, "50": 344490,
"6": 34214544, "60": 344491,
"7": 3434544, "70": 344492,
"8": 344544, "80": 344497,
"9": 34435644,
"90": 343}
let block tableTest1:
elem1 = TElem(foo: 1, bar: "bar") var t = initTable[tuple[x, y: int], string]()
elem2 = TElem(foo: 2, bar: "baz") t[(0,0)] = "00"
t[(1,0)] = "10"
t[(0,1)] = "01"
t[(1,1)] = "11"
for x in 0..1:
for y in 0..1:
assert t[(x,y)] == $x & $y
assert($t ==
"{(x: 0, y: 0): 00, (x: 0, y: 1): 01, (x: 1, y: 0): 10, (x: 1, y: 1): 11}")
block tableTest2:
var t = initTable[string, float]()
t["test"] = 1.2345
t["111"] = 1.000043
t["123"] = 1.23
t.del("111")
t["012"] = 67.9
t["123"] = 1.5 # test overwriting
assert t["123"] == 1.5
assert t["111"] == 0.0 # deleted
assert(not hasKey(t, "111"))
for key, val in items(data): t[key] = val.toFloat
for key, val in items(data): assert t[key] == val.toFloat
block orderedTableTest1:
var t = initOrderedTable[string, int](2)
for key, val in items(data): t[key] = val
for key, val in items(data): assert t[key] == val
var i = 0
# `pairs` needs to yield in insertion order:
for key, val in pairs(t):
assert key == data[i][0]
assert val == data[i][1]
inc(i)
for key, val in mpairs(t): val = 99
for val in mvalues(t): assert val == 99
block countTableTest1:
var s = data.toTable
var t = initCountTable[string]()
for k in s.keys: t.inc(k)
for k in t.keys: assert t[k] == 1
t.inc("90", 3)
t.inc("12", 2)
t.inc("34", 1)
assert t.largest()[0] == "90"
t.sort()
var i = 0
for k, v in t.pairs:
case i
of 0: assert k == "90" and v == 4
of 1: assert k == "12" and v == 3
of 2: assert k == "34" and v == 2
else: break
inc i
block SyntaxTest:
var x = toTable[int, string]({:})
proc orderedTableSortTest() =
var t = initOrderedTable[string, int](2)
for key, val in items(data): t[key] = val
for key, val in items(data): assert t[key] == val
t.sort(proc (x, y: tuple[key: string, val: int]): int = cmp(x.key, y.key))
var i = 0
# `pairs` needs to yield in sorted order:
for key, val in pairs(t):
doAssert key == sorteddata[i][0]
doAssert val == sorteddata[i][1]
inc(i)
# check that lookup still works:
for key, val in pairs(t):
doAssert val == t[key]
# check that insert still works:
t["newKeyHere"] = 80
orderedTableSortTest()
echo "true"
var tbl2 = initTable[string, TElem]()
tbl2.add("bar", elem1)
tbl2.add("baz", elem2)
doAssert indexBy(@[elem1,elem2], proc(x: TElem): string = x.bar) == tbl2, "element table"

View file

@ -84,7 +84,7 @@ block orderedTableTest1:
block countTableTest1: block countTableTest1:
var s = data.toTable var s = data.toTable
var t = newCountTable[string]() var t = newCountTable[string]()
for k in s.Keys: t.inc(k) for k in s.keys: t.inc(k)
for k in t.keys: assert t[k] == 1 for k in t.keys: assert t[k] == 1
t.inc("90", 3) t.inc("90", 3)
t.inc("12", 2) t.inc("12", 2)

View file

@ -0,0 +1,17 @@
discard """
output: '''a 1 b 2 x @[3, 4, 5] y 6 z 7
yay
12'''
"""
proc test(a, b: int, x: varargs[int]; y, z: int) =
echo "a ", a, " b ", b, " x ", @x, " y ", y, " z ", z
test 1, 2, 3, 4, 5, 6, 7
template takesBlock(a, b: int, x: varargs[expr]; blck: stmt) =
blck
echo a, b
takesBlock 1, 2, "some", 0.90, "random stuff":
echo "yay"

View file

@ -1,453 +0,0 @@
import irc, sockets, asyncio, json, os, strutils, times, redis
type
TDb* = object
r*: Redis
lastPing: float
TBuildResult* = enum
bUnknown, bFail, bSuccess
TTestResult* = enum
tUnknown, tFail, tSuccess
TEntry* = tuple[c: TCommit, p: seq[TPlatform]]
TCommit* = object
commitMsg*, username*, hash*: string
date*: Time
TPlatform* = object
buildResult*: TBuildResult
testResult*: TTestResult
failReason*, platform*: string
total*, passed*, skipped*, failed*: BiggestInt
csources*: bool
const
listName = "commits"
failOnExisting = false
proc open*(host = "localhost", port: Port): TDb =
result.r = redis.open(host, port)
result.lastPing = epochTime()
discard """proc customHSet(database: TDb, name, field, value: string) =
if database.r.hSet(name, field, value).int == 0:
if failOnExisting:
assert(false)
else:
echo("[Warning:REDIS] ", field, " already exists in ", name)"""
proc updateProperty*(database: TDb, commitHash, platform, property,
value: string) =
var name = platform & ":" & commitHash
if database.r.hSet(name, property, value).int == 0:
echo("[INFO:REDIS] '$1' field updated in hash" % [property])
else:
echo("[INFO:REDIS] '$1' new field added to hash" % [property])
proc globalProperty*(database: TDb, commitHash, property, value: string) =
if database.r.hSet(commitHash, property, value).int == 0:
echo("[INFO:REDIS] '$1' field updated in hash" % [property])
else:
echo("[INFO:REDIS] '$1' new field added to hash" % [property])
proc addCommit*(database: TDb, commitHash, commitMsg, user: string) =
# Add the commit hash to the `commits` list.
discard database.r.lPush(listName, commitHash)
# Add the commit message, current date and username as a property
globalProperty(database, commitHash, "commitMsg", commitMsg)
globalProperty(database, commitHash, "date", $int(getTime()))
globalProperty(database, commitHash, "username", user)
proc keepAlive*(database: var TDb) =
## Keep the connection alive. Ping redis in this case. This functions does
## not guarantee that redis will be pinged.
var t = epochTime()
if t - database.lastPing >= 60.0:
echo("PING -> redis")
assert(database.r.ping() == "PONG")
database.lastPing = t
proc getCommits*(database: TDb,
plStr: var seq[string]): seq[TEntry] =
result = @[]
var commitsRaw = database.r.lrange("commits", 0, -1)
for c in items(commitsRaw):
var commit: TCommit
commit.hash = c
for key, value in database.r.hPairs(c):
case normalize(key)
of "commitmsg": commit.commitMsg = value
of "date": commit.date = Time(parseInt(value))
of "username": commit.username = value
else:
echo(key)
assert(false)
var platformsRaw = database.r.lrange(c & ":platforms", 0, -1)
var platforms: seq[TPlatform] = @[]
for p in items(platformsRaw):
var platform: TPlatform
for key, value in database.r.hPairs(p & ":" & c):
case normalize(key)
of "buildresult":
platform.buildResult = parseInt(value).TBuildResult
of "testresult":
platform.testResult = parseInt(value).TTestResult
of "failreason":
platform.failReason = value
of "total":
platform.total = parseBiggestInt(value)
of "passed":
platform.passed = parseBiggestInt(value)
of "skipped":
platform.skipped = parseBiggestInt(value)
of "failed":
platform.failed = parseBiggestInt(value)
of "csources":
platform.csources = if value == "t": true else: false
else:
echo(normalize(key))
assert(false)
platform.platform = p
platforms.add(platform)
if p notin plStr:
plStr.add(p)
result.add((commit, platforms))
proc commitExists*(database: TDb, commit: string, starts = false): bool =
# TODO: Consider making the 'commits' list a set.
for c in items(database.r.lrange("commits", 0, -1)):
if starts:
if c.startsWith(commit): return true
else:
if c == commit: return true
return false
proc platformExists*(database: TDb, commit: string, platform: string): bool =
for p in items(database.r.lrange(commit & ":" & "platforms", 0, -1)):
if p == platform: return true
proc expandHash*(database: TDb, commit: string): string =
for c in items(database.r.lrange("commits", 0, -1)):
if c.startsWith(commit): return c
assert false
proc isNewest*(database: TDb, commit: string): bool =
return database.r.lIndex("commits", 0) == commit
proc getNewest*(database: TDb): string =
return database.r.lIndex("commits", 0)
proc addPlatform*(database: TDb, commit: string, platform: string) =
assert database.commitExists(commit)
assert (not database.platformExists(commit, platform))
var name = platform & ":" & commit
if database.r.exists(name):
if failOnExisting: quit("[FAIL] " & name & " already exists!", 1)
else: echo("[Warning] " & name & " already exists!")
discard database.r.lPush(commit & ":" & "platforms", platform)
proc `[]`*(p: seq[TPlatform], name: string): TPlatform =
for platform in items(p):
if platform.platform == name:
return platform
raise newException(ValueError, name & " platforms not found in commits.")
proc contains*(p: seq[TPlatform], s: string): bool =
for i in items(p):
if i.platform == s:
return true
type
PState = ref TState
TState = object of RootObj
dispatcher: Dispatcher
sock: AsyncSocket
ircClient: PAsyncIRC
hubPort: Port
database: TDb
dbConnected: bool
TSeenType = enum
PSeenJoin, PSeenPart, PSeenMsg, PSeenNick, PSeenQuit
TSeen = object
nick: string
channel: string
timestamp: Time
case kind*: TSeenType
of PSeenJoin: nil
of PSeenPart, PSeenQuit, PSeenMsg:
msg: string
of PSeenNick:
newNick: string
const
ircServer = "irc.freenode.net"
joinChans = @["#nim"]
botNickname = "NimBot"
proc setSeen(d: TDb, s: TSeen) =
discard d.r.del("seen:" & s.nick)
var hashToSet = @[("type", $s.kind.int), ("channel", s.channel),
("timestamp", $s.timestamp.int)]
case s.kind
of PSeenJoin: discard
of PSeenPart, PSeenMsg, PSeenQuit:
hashToSet.add(("msg", s.msg))
of PSeenNick:
hashToSet.add(("newnick", s.newNick))
d.r.hMSet("seen:" & s.nick, hashToSet)
proc getSeen(d: TDb, nick: string, s: var TSeen): bool =
if d.r.exists("seen:" & nick):
result = true
s.nick = nick
# Get the type first
s.kind = d.r.hGet("seen:" & nick, "type").parseInt.TSeenType
for key, value in d.r.hPairs("seen:" & nick):
case normalize(key)
of "type":
discard
#s.kind = value.parseInt.TSeenType
of "channel":
s.channel = value
of "timestamp":
s.timestamp = Time(value.parseInt)
of "msg":
s.msg = value
of "newnick":
s.newNick = value
template createSeen(typ: TSeenType, n, c: string): stmt {.immediate, dirty.} =
var seenNick: TSeen
seenNick.kind = typ
seenNick.nick = n
seenNick.channel = c
seenNick.timestamp = getTime()
proc parseReply(line: string, expect: string): bool =
var jsonDoc = parseJson(line)
return jsonDoc["reply"].str == expect
proc limitCommitMsg(m: string): string =
## Limits the message to 300 chars and adds ellipsis.
var m1 = m
if NewLines in m1:
m1 = m1.splitLines()[0]
if m1.len >= 300:
m1 = m1[0..300]
if m1.len >= 300 or NewLines in m: m1.add("... ")
if NewLines in m: m1.add($m.splitLines().len & " more lines")
return m1
proc handleWebMessage(state: PState, line: string) =
echo("Got message from hub: " & line)
var json = parseJson(line)
if json.hasKey("payload"):
for i in 0..min(4, json["payload"]["commits"].len-1):
var commit = json["payload"]["commits"][i]
# Create the message
var message = ""
message.add(json["payload"]["repository"]["owner"]["name"].str & "/" &
json["payload"]["repository"]["name"].str & " ")
message.add(commit["id"].str[0..6] & " ")
message.add(commit["author"]["name"].str & " ")
message.add("[+" & $commit["added"].len & " ")
message.add("±" & $commit["modified"].len & " ")
message.add("-" & $commit["removed"].len & "]: ")
message.add(limitCommitMsg(commit["message"].str))
# Send message to #nim.
discard state.ircClient.privmsg(joinChans[0], message)
elif json.hasKey("redisinfo"):
assert json["redisinfo"].hasKey("port")
#let redisPort = json["redisinfo"]["port"].num
state.dbConnected = true
proc hubConnect(state: PState)
proc handleConnect(s: AsyncSocket, state: PState) =
try:
# Send greeting
var obj = newJObject()
obj["name"] = newJString("irc")
obj["platform"] = newJString("?")
state.sock.send($obj & "\c\L")
# Wait for reply.
var line = ""
sleep(1500)
if state.sock.recvLine(line):
assert(line != "")
doAssert parseReply(line, "OK")
echo("The hub accepted me!")
else:
raise newException(ValueError,
"Hub didn't accept me. Waited 1.5 seconds.")
# ask for the redis info
var riobj = newJObject()
riobj["do"] = newJString("redisinfo")
state.sock.send($riobj & "\c\L")
except OsError:
echo(getCurrentExceptionMsg())
s.close()
echo("Waiting 5 seconds...")
sleep(5000)
state.hubConnect()
proc handleRead(s: AsyncSocket, state: PState) =
var line = ""
if state.sock.recvLine(line):
if line != "":
# Handle the message
state.handleWebMessage(line)
else:
echo("Disconnected from hub: ", osErrorMsg())
s.close()
echo("Reconnecting...")
state.hubConnect()
else:
echo(osErrorMsg())
proc hubConnect(state: PState) =
state.sock = asyncSocket()
state.sock.connect("127.0.0.1", state.hubPort)
state.sock.handleConnect =
proc (s: AsyncSocket) =
handleConnect(s, state)
state.sock.handleRead =
proc (s: AsyncSocket) =
handleRead(s, state)
state.dispatcher.register(state.sock)
proc handleIrc(irc: PAsyncIRC, event: TIRCEvent, state: PState) =
case event.typ
of EvConnected: discard
of EvDisconnected:
while not state.ircClient.isConnected:
try:
state.ircClient.connect()
except:
echo("Error reconnecting: ", getCurrentExceptionMsg())
echo("Waiting 5 seconds...")
sleep(5000)
echo("Reconnected successfully!")
of EvMsg:
echo("< ", event.raw)
case event.cmd
of MPrivMsg:
let msg = event.params[event.params.len-1]
let words = msg.split(' ')
template pm(msg: string): stmt =
state.ircClient.privmsg(event.origin, msg)
case words[0]
of "!ping": pm("pong")
of "!lag":
if state.ircClient.getLag != -1.0:
var lag = state.ircClient.getLag
lag = lag * 1000.0
pm($int(lag) & "ms between me and the server.")
else:
pm("Unknown.")
of "!seen":
if words.len > 1:
let nick = words[1]
if nick == botNickname:
pm("Yes, I see myself.")
echo(nick)
var seenInfo: TSeen
if state.database.getSeen(nick, seenInfo):
#var mSend = ""
case seenInfo.kind
of PSeenMsg:
pm("$1 was last seen on $2 in $3 saying: $4" %
[seenInfo.nick, $seenInfo.timestamp,
seenInfo.channel, seenInfo.msg])
of PSeenJoin:
pm("$1 was last seen on $2 joining $3" %
[seenInfo.nick, $seenInfo.timestamp, seenInfo.channel])
of PSeenPart:
pm("$1 was last seen on $2 leaving $3 with message: $4" %
[seenInfo.nick, $seenInfo.timestamp, seenInfo.channel,
seenInfo.msg])
of PSeenQuit:
pm("$1 was last seen on $2 quitting with message: $3" %
[seenInfo.nick, $seenInfo.timestamp, seenInfo.msg])
of PSeenNick:
pm("$1 was last seen on $2 changing nick to $3" %
[seenInfo.nick, $seenInfo.timestamp, seenInfo.newNick])
else:
pm("I have not seen " & nick)
else:
pm("Syntax: !seen <nick>")
# TODO: ... commands
# -- Seen
# Log this as activity.
createSeen(PSeenMsg, event.nick, event.origin)
seenNick.msg = msg
state.database.setSeen(seenNick)
of MJoin:
createSeen(PSeenJoin, event.nick, event.origin)
state.database.setSeen(seenNick)
of MPart:
createSeen(PSeenPart, event.nick, event.origin)
let msg = event.params[event.params.high]
seenNick.msg = msg
state.database.setSeen(seenNick)
of MQuit:
createSeen(PSeenQuit, event.nick, event.origin)
let msg = event.params[event.params.high]
seenNick.msg = msg
state.database.setSeen(seenNick)
of MNick:
createSeen(PSeenNick, event.nick, "#nim")
seenNick.newNick = event.params[0]
state.database.setSeen(seenNick)
else:
discard # TODO: ?
proc open(port: Port = Port(5123)): PState =
var res: PState
new(res)
res.dispatcher = newDispatcher()
res.hubPort = port
res.hubConnect()
let hirc =
proc (a: PAsyncIRC, ev: TIRCEvent) =
handleIrc(a, ev, res)
# Connect to the irc server.
res.ircClient = AsyncIrc(ircServer, nick = botNickname, user = botNickname,
joinChans = joinChans, ircEvent = hirc)
res.ircClient.connect()
res.dispatcher.register(res.ircClient)
res.dbConnected = false
result = res
var state = tircbot.open() # Connect to the website and the IRC server.
while state.dispatcher.poll():
if state.dbConnected:
state.database.keepAlive()

View file

@ -1,128 +0,0 @@
discard """
output: '''true'''
"""
import hashes, tables
const
data = {
"34": 123456, "12": 789,
"90": 343, "0": 34404,
"1": 344004, "2": 344774,
"3": 342244, "4": 3412344,
"5": 341232144, "6": 34214544,
"7": 3434544, "8": 344544,
"9": 34435644, "---00": 346677844,
"10": 34484, "11": 34474, "19": 34464,
"20": 34454, "30": 34141244, "40": 344114,
"50": 344490, "60": 344491, "70": 344492,
"80": 344497}
sorteddata = {
"---00": 346677844,
"0": 34404,
"1": 344004,
"10": 34484,
"11": 34474,
"12": 789,
"19": 34464,
"2": 344774, "20": 34454,
"3": 342244, "30": 34141244,
"34": 123456,
"4": 3412344, "40": 344114,
"5": 341232144, "50": 344490,
"6": 34214544, "60": 344491,
"7": 3434544, "70": 344492,
"8": 344544, "80": 344497,
"9": 34435644,
"90": 343}
block tableTest1:
var t = initTable[tuple[x, y: int], string]()
t[(0,0)] = "00"
t[(1,0)] = "10"
t[(0,1)] = "01"
t[(1,1)] = "11"
for x in 0..1:
for y in 0..1:
assert t[(x,y)] == $x & $y
assert($t ==
"{(x: 0, y: 1): 01, (x: 0, y: 0): 00, (x: 1, y: 0): 10, (x: 1, y: 1): 11}")
block tableTest2:
var t = initTable[string, float]()
t["test"] = 1.2345
t["111"] = 1.000043
t["123"] = 1.23
t.del("111")
t["012"] = 67.9
t["123"] = 1.5 # test overwriting
assert t["123"] == 1.5
assert t["111"] == 0.0 # deleted
assert(not hasKey(t, "111"))
for key, val in items(data): t[key] = val.toFloat
for key, val in items(data): assert t[key] == val.toFloat
block orderedTableTest1:
var t = initOrderedTable[string, int](2)
for key, val in items(data): t[key] = val
for key, val in items(data): assert t[key] == val
var i = 0
# `pairs` needs to yield in insertion order:
for key, val in pairs(t):
assert key == data[i][0]
assert val == data[i][1]
inc(i)
for key, val in mpairs(t): val = 99
for val in mvalues(t): assert val == 99
block countTableTest1:
var s = data.toTable
var t = initCountTable[string]()
for k in s.keys: t.inc(k)
for k in t.keys: assert t[k] == 1
t.inc("90", 3)
t.inc("12", 2)
t.inc("34", 1)
assert t.largest()[0] == "90"
t.sort()
var i = 0
for k, v in t.pairs:
case i
of 0: assert k == "90" and v == 4
of 1: assert k == "12" and v == 3
of 2: assert k == "34" and v == 2
else: break
inc i
block SyntaxTest:
var x = toTable[int, string]({:})
proc orderedTableSortTest() =
var t = initOrderedTable[string, int](2)
for key, val in items(data): t[key] = val
for key, val in items(data): assert t[key] == val
t.sort(proc (x, y: tuple[key: string, val: int]): int = cmp(x.key, y.key))
var i = 0
# `pairs` needs to yield in sorted order:
for key, val in pairs(t):
doAssert key == sorteddata[i][0]
doAssert val == sorteddata[i][1]
inc(i)
# check that lookup still works:
for key, val in pairs(t):
doAssert val == t[key]
# check that insert still works:
t["newKeyHere"] = 80
orderedTableSortTest()
echo "true"

View file

@ -0,0 +1,9 @@
import future
template tempo(s: expr) =
s("arg")
tempo((s: string)->auto => echo(s))
tempo((s: string) => echo(s))

View file

@ -1,9 +1,10 @@
version 0.10.4 version 0.10.4
============== ==============
- make 'nil' work for 'add' and 'len'
- improve GC-unsafety warnings - improve GC-unsafety warnings
- make 'nil' work for 'add' and 'len'
- get rid of 'mget'; aka priority of 'var' needs to be 'var{lvalue}' - get rid of 'mget'; aka priority of 'var' needs to be 'var{lvalue}'
- 'result' shadowing warning
version 1.0 version 1.0
@ -41,7 +42,6 @@ Misc
- make tuple unpacking work in a non-var/let context - make tuple unpacking work in a non-var/let context
- built-in 'getImpl' - built-in 'getImpl'
- prevent 'alloc(TypeWithGCedMemory)' - prevent 'alloc(TypeWithGCedMemory)'
- some table related tests are wrong (memory usage checks)
Bugs Bugs

View file

@ -43,6 +43,18 @@ News
foo = {"ah": "finally", "this": "is", "possible.": "nice!"}.toTable() foo = {"ah": "finally", "this": "is", "possible.": "nice!"}.toTable()
- Ordinary parameters can follow after a varargs parameter. This means the
following is finally accepted by the compiler:
.. code-block:: nim
template takesBlock(a, b: int, x: varargs[expr]; blck: stmt) =
blck
echo a, b
takesBlock 1, 2, "some", 0.90, "random stuff":
echo "yay"
2014-12-29 Version 0.10.2 released 2014-12-29 Version 0.10.2 released
================================== ==================================