Merge remote-tracking branch 'upstream/devel' into devel

This commit is contained in:
PhilipWitte 2015-02-13 11:39:28 -08:00
commit 0488001402
105 changed files with 1902 additions and 1535 deletions

View file

@ -130,8 +130,8 @@ proc skipConvAndClosure*(n: PNode): PNode =
proc sameValue*(a, b: PNode): bool = proc sameValue*(a, b: PNode): bool =
result = false result = false
case a.kind case a.kind
of nkCharLit..nkInt64Lit: of nkCharLit..nkUInt64Lit:
if b.kind in {nkCharLit..nkInt64Lit}: result = a.intVal == b.intVal if b.kind in {nkCharLit..nkUInt64Lit}: result = a.intVal == b.intVal
of nkFloatLit..nkFloat64Lit: of nkFloatLit..nkFloat64Lit:
if b.kind in {nkFloatLit..nkFloat64Lit}: result = a.floatVal == b.floatVal if b.kind in {nkFloatLit..nkFloat64Lit}: result = a.floatVal == b.floatVal
of nkStrLit..nkTripleStrLit: of nkStrLit..nkTripleStrLit:
@ -145,13 +145,13 @@ proc leValue*(a, b: PNode): bool =
# a <= b? # a <= b?
result = false result = false
case a.kind case a.kind
of nkCharLit..nkInt64Lit: of nkCharLit..nkUInt32Lit:
if b.kind in {nkCharLit..nkInt64Lit}: result = a.intVal <= b.intVal if b.kind in {nkCharLit..nkUInt32Lit}: result = a.intVal <= b.intVal
of nkFloatLit..nkFloat64Lit: of nkFloatLit..nkFloat64Lit:
if b.kind in {nkFloatLit..nkFloat64Lit}: result = a.floatVal <= b.floatVal if b.kind in {nkFloatLit..nkFloat64Lit}: result = a.floatVal <= b.floatVal
of nkStrLit..nkTripleStrLit: of nkStrLit..nkTripleStrLit:
if b.kind in {nkStrLit..nkTripleStrLit}: result = a.strVal <= b.strVal if b.kind in {nkStrLit..nkTripleStrLit}: result = a.strVal <= b.strVal
else: else:
# don't raise an internal error for 'nimrod check': # don't raise an internal error for 'nimrod check':
#InternalError(a.info, "leValue") #InternalError(a.info, "leValue")
discard discard

View file

@ -430,15 +430,27 @@ proc genNamedParamCall(p: BProc, ri: PNode, d: var TLoc) =
assert(typ.kind == tyProc) assert(typ.kind == tyProc)
var length = sonsLen(ri) var length = sonsLen(ri)
assert(sonsLen(typ) == sonsLen(typ.n)) assert(sonsLen(typ) == sonsLen(typ.n))
if length > 1: # don't call 'ropeToStr' here for efficiency:
app(pl, genArg(p, ri.sons[1], typ.n.sons[1].sym, ri)) let pat = ri.sons[0].sym.loc.r.data
app(pl, ~" ") internalAssert pat != nil
app(pl, op.r) var start = 3
if length > 2: if ' ' in pat:
app(pl, ~": ") start = 1
app(pl, genArg(p, ri.sons[2], typ.n.sons[2].sym, ri)) app(pl, op.r)
for i in countup(3, length-1): if length > 1:
app(pl, ~": ")
app(pl, genArg(p, ri.sons[1], typ.n.sons[1].sym, ri))
start = 2
else:
if length > 1:
app(pl, genArg(p, ri.sons[1], typ.n.sons[1].sym, ri))
app(pl, ~" ")
app(pl, op.r)
if length > 2:
app(pl, ~": ")
app(pl, genArg(p, ri.sons[2], typ.n.sons[2].sym, ri))
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

@ -83,7 +83,9 @@ proc genLiteral(p: BProc, n: PNode, ty: PType): PRope =
else: else:
result = toRope("NIM_NIL") result = toRope("NIM_NIL")
of nkStrLit..nkTripleStrLit: of nkStrLit..nkTripleStrLit:
if skipTypes(ty, abstractVarRange).kind == tyString: if n.strVal.isNil:
result = ropecg(p.module, "((#NimStringDesc*) NIM_NIL)", [])
elif skipTypes(ty, abstractVarRange).kind == tyString:
var id = nodeTableTestOrSet(p.module.dataCache, n, gBackendId) var id = nodeTableTestOrSet(p.module.dataCache, n, gBackendId)
if id == gBackendId: if id == gBackendId:
# string literal not found in the cache: # string literal not found in the cache:
@ -950,7 +952,7 @@ proc genEcho(p: BProc, n: PNode) =
var a: TLoc var a: TLoc
for i in countup(0, n.len-1): for i in countup(0, n.len-1):
initLocExpr(p, n.sons[i], a) initLocExpr(p, n.sons[i], a)
appf(args, ", ($1)->data", [rdLoc(a)]) appf(args, ", $1? ($1)->data:\"nil\"", [rdLoc(a)])
linefmt(p, cpsStmts, "printf($1$2);$n", linefmt(p, cpsStmts, "printf($1$2);$n",
makeCString(repeatStr(n.len, "%s") & tnl), args) makeCString(repeatStr(n.len, "%s") & tnl), args)
@ -2105,17 +2107,13 @@ proc expr(p: BProc, n: PNode, d: var TLoc) =
discard discard
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
@ -2173,7 +2171,7 @@ proc genConstExpr(p: BProc, n: PNode): PRope =
var cs: TBitSet var cs: TBitSet
toBitSet(n, cs) toBitSet(n, cs)
result = genRawSetData(cs, int(getSize(n.typ))) result = genRawSetData(cs, int(getSize(n.typ)))
of nkBracket, nkPar, nkClosure: of nkBracket, nkPar, nkClosure, nkObjConstr:
var t = skipTypes(n.typ, abstractInst) var t = skipTypes(n.typ, abstractInst)
if t.kind == tySequence: if t.kind == tySequence:
result = genConstSeq(p, n, t) result = genConstSeq(p, n, t)

View file

@ -253,15 +253,6 @@ proc genConstStmt(p: BProc, t: PNode) =
elif c.typ.kind in ConstantDataTypes and lfNoDecl notin c.loc.flags and elif c.typ.kind in ConstantDataTypes and lfNoDecl notin c.loc.flags and
c.ast.len != 0: c.ast.len != 0:
if not emitLazily(c): requestConstImpl(p, c) if not emitLazily(c): requestConstImpl(p, c)
when false:
# generate the data:
fillLoc(c.loc, locData, c.typ, mangleName(c), OnUnknown)
if sfImportc in c.flags:
appf(p.module.s[cfsData], "extern NIM_CONST $1 $2;$n",
[getTypeDesc(p.module, c.typ), c.loc.r])
else:
appf(p.module.s[cfsData], "NIM_CONST $1 $2 = $3;$n",
[getTypeDesc(p.module, c.typ), c.loc.r, genConstExpr(p, c.ast)])
proc genIf(p: BProc, n: PNode, d: var TLoc) = proc genIf(p: BProc, n: PNode, d: var TLoc) =
# #

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)
@ -389,8 +389,8 @@ proc addOpt(dest: var string, src: string) =
if len(dest) == 0 or dest[len(dest)-1] != ' ': add(dest, " ") if len(dest) == 0 or dest[len(dest)-1] != ' ': add(dest, " ")
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)])
@ -626,7 +626,9 @@ proc callCCompiler*(projectfile: string) =
if gNumberOfProcessors == 0: gNumberOfProcessors = countProcessors() if gNumberOfProcessors == 0: gNumberOfProcessors = countProcessors()
var res = 0 var res = 0
if gNumberOfProcessors <= 1: if gNumberOfProcessors <= 1:
for i in countup(0, high(cmds)): res = max(execWithEcho(cmds[i]), res) for i in countup(0, high(cmds)):
res = execWithEcho(cmds[i])
if res != 0: rawMessage(errExecutionOfProgramFailed, [])
elif optListCmd in gGlobalOptions or gVerbosity > 1: elif optListCmd in gGlobalOptions or gVerbosity > 1:
res = execProcesses(cmds, {poEchoCmd, poUseShell, poParentStreams}, res = execProcesses(cmds, {poEchoCmd, poUseShell, poParentStreams},
gNumberOfProcessors) gNumberOfProcessors)
@ -677,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:
@ -714,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

@ -30,47 +30,32 @@ type
PLLStream* = ref TLLStream PLLStream* = ref TLLStream
proc llStreamOpen*(data: string): PLLStream proc llStreamOpen*(data: string): PLLStream =
proc llStreamOpen*(f: var File): PLLStream
proc llStreamOpen*(filename: string, mode: FileMode): PLLStream
proc llStreamOpen*(): PLLStream
proc llStreamOpenStdIn*(): PLLStream
proc llStreamClose*(s: PLLStream)
proc llStreamRead*(s: PLLStream, buf: pointer, bufLen: int): int
proc llStreamReadLine*(s: PLLStream, line: var string): bool
proc llStreamReadAll*(s: PLLStream): string
proc llStreamWrite*(s: PLLStream, data: string)
proc llStreamWrite*(s: PLLStream, data: char)
proc llStreamWrite*(s: PLLStream, buf: pointer, buflen: int)
proc llStreamWriteln*(s: PLLStream, data: string)
# implementation
proc llStreamOpen(data: string): PLLStream =
new(result) new(result)
result.s = data result.s = data
result.kind = llsString result.kind = llsString
proc llStreamOpen(f: var File): PLLStream = proc llStreamOpen*(f: var File): PLLStream =
new(result) new(result)
result.f = f result.f = f
result.kind = llsFile result.kind = llsFile
proc llStreamOpen(filename: string, mode: FileMode): PLLStream = proc llStreamOpen*(filename: string, mode: FileMode): PLLStream =
new(result) new(result)
result.kind = llsFile result.kind = llsFile
if not open(result.f, filename, mode): result = nil if not open(result.f, filename, mode): result = nil
proc llStreamOpen(): PLLStream = proc llStreamOpen*(): PLLStream =
new(result) new(result)
result.kind = llsNone result.kind = llsNone
proc llStreamOpenStdIn(): PLLStream = proc llStreamOpenStdIn*(): PLLStream =
new(result) new(result)
result.kind = llsStdIn result.kind = llsStdIn
result.s = "" result.s = ""
result.lineOffset = -1 result.lineOffset = -1
proc llStreamClose(s: PLLStream) = proc llStreamClose*(s: PLLStream) =
case s.kind case s.kind
of llsNone, llsString, llsStdIn: of llsNone, llsString, llsStdIn:
discard discard
@ -130,7 +115,7 @@ proc llReadFromStdin(s: PLLStream, buf: pointer, bufLen: int): int =
copyMem(buf, addr(s.s[s.rd]), result) copyMem(buf, addr(s.s[s.rd]), result)
inc(s.rd, result) inc(s.rd, result)
proc llStreamRead(s: PLLStream, buf: pointer, bufLen: int): int = proc llStreamRead*(s: PLLStream, buf: pointer, bufLen: int): int =
case s.kind case s.kind
of llsNone: of llsNone:
result = 0 result = 0
@ -144,7 +129,7 @@ proc llStreamRead(s: PLLStream, buf: pointer, bufLen: int): int =
of llsStdIn: of llsStdIn:
result = llReadFromStdin(s, buf, bufLen) result = llReadFromStdin(s, buf, bufLen)
proc llStreamReadLine(s: PLLStream, line: var string): bool = proc llStreamReadLine*(s: PLLStream, line: var string): bool =
setLen(line, 0) setLen(line, 0)
case s.kind case s.kind
of llsNone: of llsNone:
@ -168,7 +153,7 @@ proc llStreamReadLine(s: PLLStream, line: var string): bool =
of llsStdIn: of llsStdIn:
result = readLine(stdin, line) result = readLine(stdin, line)
proc llStreamWrite(s: PLLStream, data: string) = proc llStreamWrite*(s: PLLStream, data: string) =
case s.kind case s.kind
of llsNone, llsStdIn: of llsNone, llsStdIn:
discard discard
@ -178,11 +163,11 @@ proc llStreamWrite(s: PLLStream, data: string) =
of llsFile: of llsFile:
write(s.f, data) write(s.f, data)
proc llStreamWriteln(s: PLLStream, data: string) = proc llStreamWriteln*(s: PLLStream, data: string) =
llStreamWrite(s, data) llStreamWrite(s, data)
llStreamWrite(s, "\n") llStreamWrite(s, "\n")
proc llStreamWrite(s: PLLStream, data: char) = proc llStreamWrite*(s: PLLStream, data: char) =
var c: char var c: char
case s.kind case s.kind
of llsNone, llsStdIn: of llsNone, llsStdIn:
@ -194,7 +179,7 @@ proc llStreamWrite(s: PLLStream, data: char) =
c = data c = data
discard writeBuffer(s.f, addr(c), sizeof(c)) discard writeBuffer(s.f, addr(c), sizeof(c))
proc llStreamWrite(s: PLLStream, buf: pointer, buflen: int) = proc llStreamWrite*(s: PLLStream, buf: pointer, buflen: int) =
case s.kind case s.kind
of llsNone, llsStdIn: of llsNone, llsStdIn:
discard discard
@ -206,7 +191,7 @@ proc llStreamWrite(s: PLLStream, buf: pointer, buflen: int) =
of llsFile: of llsFile:
discard writeBuffer(s.f, buf, buflen) discard writeBuffer(s.f, buf, buflen)
proc llStreamReadAll(s: PLLStream): string = proc llStreamReadAll*(s: PLLStream): string =
const const
bufSize = 2048 bufSize = 2048
case s.kind case s.kind

View file

@ -113,7 +113,7 @@ type
warnSmallLshouldNotBeUsed, warnUnknownMagic, warnRedefinitionOfLabel, warnSmallLshouldNotBeUsed, warnUnknownMagic, warnRedefinitionOfLabel,
warnUnknownSubstitutionX, warnLanguageXNotSupported, warnUnknownSubstitutionX, warnLanguageXNotSupported,
warnFieldXNotSupported, warnCommentXIgnored, warnFieldXNotSupported, warnCommentXIgnored,
warnNilStatement, warnAnalysisLoophole, warnNilStatement, warnTypelessParam,
warnDifferentHeaps, warnWriteToForeignHeap, warnUnsafeCode, warnDifferentHeaps, warnWriteToForeignHeap, warnUnsafeCode,
warnEachIdentIsTuple, warnShadowIdent, warnEachIdentIsTuple, warnShadowIdent,
warnProveInit, warnProveField, warnProveIndex, warnGcUnsafe, warnGcUnsafe2, warnProveInit, warnProveField, warnProveIndex, warnGcUnsafe, warnGcUnsafe2,
@ -376,7 +376,7 @@ const
warnFieldXNotSupported: "field \'$1\' not supported [FieldXNotSupported]", warnFieldXNotSupported: "field \'$1\' not supported [FieldXNotSupported]",
warnCommentXIgnored: "comment \'$1\' ignored [CommentXIgnored]", warnCommentXIgnored: "comment \'$1\' ignored [CommentXIgnored]",
warnNilStatement: "'nil' statement is deprecated; use an empty 'discard' statement instead [NilStmt]", warnNilStatement: "'nil' statement is deprecated; use an empty 'discard' statement instead [NilStmt]",
warnAnalysisLoophole: "thread analysis incomplete due to unknown call '$1' [AnalysisLoophole]", warnTypelessParam: "'$1' has no type. Typeless parameters are deprecated; only allowed for 'template' [TypelessParam]",
warnDifferentHeaps: "possible inconsistency of thread local heaps [DifferentHeaps]", warnDifferentHeaps: "possible inconsistency of thread local heaps [DifferentHeaps]",
warnWriteToForeignHeap: "write to foreign heap [WriteToForeignHeap]", warnWriteToForeignHeap: "write to foreign heap [WriteToForeignHeap]",
warnUnsafeCode: "unsafe code: '$1' [UnsafeCode]", warnUnsafeCode: "unsafe code: '$1' [UnsafeCode]",
@ -418,7 +418,7 @@ const
"RedefinitionOfLabel", "UnknownSubstitutionX", "RedefinitionOfLabel", "UnknownSubstitutionX",
"LanguageXNotSupported", "FieldXNotSupported", "LanguageXNotSupported", "FieldXNotSupported",
"CommentXIgnored", "NilStmt", "CommentXIgnored", "NilStmt",
"AnalysisLoophole", "DifferentHeaps", "WriteToForeignHeap", "TypelessParam", "DifferentHeaps", "WriteToForeignHeap",
"UnsafeCode", "EachIdentIsTuple", "ShadowIdent", "UnsafeCode", "EachIdentIsTuple", "ShadowIdent",
"ProveInit", "ProveField", "ProveIndex", "GcUnsafe", "GcUnsafe2", "Uninit", "ProveInit", "ProveField", "ProveIndex", "GcUnsafe", "GcUnsafe2", "Uninit",
"GcMem", "Destructor", "LockLevel", "User"] "GcMem", "Destructor", "LockLevel", "User"]
@ -720,7 +720,10 @@ type
proc handleError(msg: TMsgKind, eh: TErrorHandling, s: string) = proc handleError(msg: TMsgKind, eh: TErrorHandling, s: string) =
template quit = template quit =
if defined(debug) or gVerbosity >= 3 or msg == errInternal: if defined(debug) or gVerbosity >= 3 or msg == errInternal:
writeStackTrace() if stackTraceAvailable():
writeStackTrace()
else:
msgWriteln("No stack traceback available\nTo create a stacktrace, rerun compilation with ./koch temp c <file>")
quit 1 quit 1
if msg >= fatalMin and msg <= fatalMax: if msg >= fatalMin and msg <= fatalMax:

View file

@ -198,8 +198,8 @@ proc isSigilLike(tok: TToken): bool {.inline.} =
proc isRightAssociative(tok: TToken): bool {.inline.} = proc isRightAssociative(tok: TToken): bool {.inline.} =
## Determines whether the token is right assocative. ## Determines whether the token is right assocative.
result = tok.tokType == tkOpr and (tok.ident.s[0] == '^' or result = tok.tokType == tkOpr and tok.ident.s[0] == '^'
(let L = tok.ident.s.len; L > 1 and tok.ident.s[L-1] == '>')) # or (let L = tok.ident.s.len; L > 1 and tok.ident.s[L-1] == '>'))
proc getPrecedence(tok: TToken, strongSpaces: bool): int = proc getPrecedence(tok: TToken, strongSpaces: bool): int =
## Calculates the precedence of the given token. ## Calculates the precedence of the given token.

View file

@ -138,7 +138,7 @@ const
props: {ospNeedsPIC, ospPosix, ospLacksThreadVars}), props: {ospNeedsPIC, ospPosix, ospLacksThreadVars}),
(name: "VxWorks", parDir: "..", dllFrmt: "lib$1.so", altDirSep: "/", (name: "VxWorks", parDir: "..", dllFrmt: "lib$1.so", altDirSep: "/",
objExt: ".o", newLine: "\x0A", pathSep: ";", dirSep: "\\", objExt: ".o", newLine: "\x0A", pathSep: ";", dirSep: "\\",
scriptExt: ".sh", curDir: ".", exeExt: "", extSep: ".", scriptExt: ".sh", curDir: ".", exeExt: ".vxe", extSep: ".",
props: {ospNeedsPIC, ospPosix, ospLacksThreadVars}), props: {ospNeedsPIC, ospPosix, ospLacksThreadVars}),
(name: "JS", parDir: "..", (name: "JS", parDir: "..",
dllFrmt: "lib$1.so", altDirSep: "/", dllFrmt: "lib$1.so", altDirSep: "/",

View file

@ -735,11 +735,10 @@ 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) else: invalidPragma(it)
else: invalidPragma(it)
of wPacked: of wPacked:
noVal(it) noVal(it)
if sym.typ == nil: invalidPragma(it) if sym.typ == nil: invalidPragma(it)

View file

@ -130,9 +130,11 @@ proc commonType*(x, y: PType): PType =
elif a.kind == tyTuple and b.kind == tyTuple and a.len == b.len: elif a.kind == tyTuple and b.kind == tyTuple and a.len == b.len:
var nt: PType var nt: PType
for i in 0.. <a.len: for i in 0.. <a.len:
if isEmptyContainer(a.sons[i]) and not isEmptyContainer(b.sons[i]): let aEmpty = isEmptyContainer(a.sons[i])
let bEmpty = isEmptyContainer(b.sons[i])
if aEmpty != bEmpty:
if nt.isNil: nt = copyType(a, a.owner, false) if nt.isNil: nt = copyType(a, a.owner, false)
nt.sons[i] = b.sons[i] nt.sons[i] = if aEmpty: b.sons[i] else: a.sons[i]
if not nt.isNil: result = nt if not nt.isNil: result = nt
#elif b.sons[idx].kind == tyEmpty: return x #elif b.sons[idx].kind == tyEmpty: return x
elif a.kind == tyRange and b.kind == tyRange: elif a.kind == tyRange and b.kind == tyRange:

View file

@ -221,6 +221,7 @@ proc makeTypeSymNode*(c: PContext, typ: PType, info: TLineInfo): PNode =
proc makeTypeFromExpr*(c: PContext, n: PNode): PType = proc makeTypeFromExpr*(c: PContext, n: PNode): PType =
result = newTypeS(tyFromExpr, c) result = newTypeS(tyFromExpr, c)
assert n != nil
result.n = n result.n = n
proc newTypeWithSons*(c: PContext, kind: TTypeKind, proc newTypeWithSons*(c: PContext, kind: TTypeKind,

View file

@ -103,28 +103,31 @@ proc semSym(c: PContext, n: PNode, s: PSym, flags: TExprFlags): PNode =
result = newSymNode(s, n.info) result = newSymNode(s, n.info)
of skMacro: result = semMacroExpr(c, n, n, s, flags) of skMacro: result = semMacroExpr(c, n, n, s, flags)
of skTemplate: result = semTemplateExpr(c, n, s, flags) of skTemplate: result = semTemplateExpr(c, n, s, flags)
of skVar, skLet, skResult, skParam, skForVar: of skParam:
markUsed(n.info, s)
styleCheckUse(n.info, s)
if s.typ.kind == tyStatic and s.typ.n != nil:
# XXX see the hack in sigmatch.nim ...
return s.typ.n
elif sfGenSym in s.flags:
if c.p.wasForwarded:
# gensym'ed parameters that nevertheless have been forward declared
# need a special fixup:
let realParam = c.p.owner.typ.n[s.position+1]
internalAssert realParam.kind == nkSym and realParam.sym.kind == skParam
return newSymNode(c.p.owner.typ.n[s.position+1].sym, n.info)
elif c.p.owner.kind == skMacro:
# gensym'ed macro parameters need a similar hack (see bug #1944):
var u = searchInScopes(c, s.name)
internalAssert u != nil and u.kind == skParam and u.owner == s.owner
return newSymNode(u, n.info)
result = newSymNode(s, n.info)
of skVar, skLet, skResult, skForVar:
markUsed(n.info, s) markUsed(n.info, s)
styleCheckUse(n.info, s) styleCheckUse(n.info, s)
# if a proc accesses a global variable, it is not side effect free: # if a proc accesses a global variable, it is not side effect free:
if sfGlobal in s.flags: if sfGlobal in s.flags:
incl(c.p.owner.flags, sfSideEffect) incl(c.p.owner.flags, sfSideEffect)
elif s.kind == skParam:
if s.typ.kind == tyStatic and s.typ.n != nil:
# XXX see the hack in sigmatch.nim ...
return s.typ.n
elif sfGenSym in s.flags:
if c.p.wasForwarded:
# gensym'ed parameters that nevertheless have been forward declared
# need a special fixup:
let realParam = c.p.owner.typ.n[s.position+1]
internalAssert realParam.kind == nkSym and realParam.sym.kind == skParam
return newSymNode(c.p.owner.typ.n[s.position+1].sym, n.info)
elif c.p.owner.kind == skMacro:
# gensym'ed macro parameters need a similar hack (see bug #1944):
var u = searchInScopes(c, s.name)
internalAssert u != nil and u.kind == skParam and u.owner == s.owner
return newSymNode(u, n.info)
result = newSymNode(s, n.info) result = newSymNode(s, n.info)
# We cannot check for access to outer vars for example because it's still # We cannot check for access to outer vars for example because it's still
# not sure the symbol really ends up being used: # not sure the symbol really ends up being used:
@ -445,25 +448,30 @@ proc changeType(n: PNode, newType: PType, check: bool) =
let tup = newType.skipTypes({tyGenericInst}) let tup = newType.skipTypes({tyGenericInst})
if tup.kind != tyTuple: if tup.kind != tyTuple:
internalError(n.info, "changeType: no tuple type for constructor") internalError(n.info, "changeType: no tuple type for constructor")
elif newType.n == nil: discard elif sonsLen(n) > 0 and n.sons[0].kind == nkExprColonExpr:
elif sonsLen(n) > 0 and n.sons[0].kind == nkExprColonExpr: # named tuple?
for i in countup(0, sonsLen(n) - 1): for i in countup(0, sonsLen(n) - 1):
var m = n.sons[i].sons[0] var m = n.sons[i].sons[0]
if m.kind != nkSym: if m.kind != nkSym:
internalError(m.info, "changeType(): invalid tuple constr") internalError(m.info, "changeType(): invalid tuple constr")
return return
var f = getSymFromList(newType.n, m.sym.name) if tup.n != nil:
if f == nil: var f = getSymFromList(newType.n, m.sym.name)
internalError(m.info, "changeType(): invalid identifier") if f == nil:
return internalError(m.info, "changeType(): invalid identifier")
changeType(n.sons[i].sons[1], f.typ, check) return
changeType(n.sons[i].sons[1], f.typ, check)
else:
changeType(n.sons[i].sons[1], tup.sons[i], check)
else: else:
for i in countup(0, sonsLen(n) - 1): for i in countup(0, sonsLen(n) - 1):
var m = n.sons[i] changeType(n.sons[i], tup.sons[i], check)
var a = newNodeIT(nkExprColonExpr, m.info, newType.sons[i]) when false:
addSon(a, newSymNode(newType.n.sons[i].sym)) var m = n.sons[i]
addSon(a, m) var a = newNodeIT(nkExprColonExpr, m.info, newType.sons[i])
changeType(m, tup.sons[i], check) addSon(a, newSymNode(newType.n.sons[i].sym))
addSon(a, m)
changeType(m, tup.sons[i], check)
of nkCharLit..nkUInt64Lit: of nkCharLit..nkUInt64Lit:
if check: if check:
let value = n.intVal let value = n.intVal
@ -678,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
@ -1329,7 +1339,12 @@ 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:
localError(c.p.resultSym.info, errCannotInferReturnType) 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)
closeScope(c) closeScope(c)

View file

@ -77,11 +77,16 @@ proc removeDefaultParamValues(n: PNode) =
proc freshGenSyms(n: PNode, owner: PSym, symMap: var TIdTable) = proc freshGenSyms(n: PNode, owner: PSym, symMap: var TIdTable) =
# we need to create a fresh set of gensym'ed symbols: # we need to create a fresh set of gensym'ed symbols:
if n.kind == nkSym and sfGenSym in n.sym.flags: if n.kind == nkSym and sfGenSym in n.sym.flags:
var x = PSym(idTableGet(symMap, n.sym)) let s = n.sym
var x = PSym(idTableGet(symMap, s))
if x == nil: if x == nil:
x = copySym(n.sym, false) if s.kind == skParam:
x.owner = owner x = owner.typ.n[s.position+1].sym
idTablePut(symMap, n.sym, x) internalAssert x.kind == skParam
else:
x = copySym(s, false)
x.owner = owner
idTablePut(symMap, s, x)
n.sym = x n.sym = x
else: else:
for i in 0 .. <safeLen(n): freshGenSyms(n.sons[i], owner, symMap) for i in 0 .. <safeLen(n): freshGenSyms(n.sons[i], owner, symMap)

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:
@ -825,7 +828,7 @@ proc trackProc*(s: PSym, body: PNode) =
# effects already computed? # effects already computed?
if sfForward in s.flags: return if sfForward in s.flags: return
if effects.len == effectListLen: return if effects.len == effectListLen: return
var t: TEffects var t: TEffects
initEffects(effects, s, t) initEffects(effects, s, t)
track(t, body) track(t, body)
@ -849,19 +852,20 @@ 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 and optThreadAnalysis in gGlobalOptions:
if optThreads 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:
if not t.gcUnsafe: s.typ.flags.incl tfGcSafe 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,
"declared lock level is $1, but real lock level is $2" % message(s.info, warnLockLevel,
[$s.typ.lockLevel, $t.maxLockLevel]) "declared lock level is $1, but real lock level is $2" %
[$s.typ.lockLevel, $t.maxLockLevel])
proc trackTopLevelStmt*(module: PSym; n: PNode) = proc trackTopLevelStmt*(module: PSym; n: PNode) =
if n.kind in {nkPragma, nkMacroDef, nkTemplateDef, nkProcDef, if n.kind in {nkPragma, nkMacroDef, nkTemplateDef, nkProcDef,

View file

@ -359,6 +359,10 @@ proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode =
var def: PNode var def: PNode
if a.sons[length-1].kind != nkEmpty: if a.sons[length-1].kind != nkEmpty:
def = semExprWithType(c, a.sons[length-1], {efAllowDestructor}) def = semExprWithType(c, a.sons[length-1], {efAllowDestructor})
if def.typ.kind == tyTypeDesc and c.p.owner.kind != skMacro:
# prevent the all too common 'var x = int' bug:
localError(def.info, "'typedesc' metatype is not valid here; typed '=' instead of ':'?")
def.typ = errorType(c)
if typ != nil: if typ != nil:
if typ.isMetaType: if typ.isMetaType:
def = inferWithMetatype(c, typ, def) def = inferWithMetatype(c, typ, def)
@ -825,9 +829,9 @@ proc semLambda(c: PContext, n: PNode, flags: TExprFlags): PNode =
if gp.len == 0 or (gp.len == 1 and tfRetType in gp[0].typ.flags): if gp.len == 0 or (gp.len == 1 and tfRetType in gp[0].typ.flags):
pushProcCon(c, s) pushProcCon(c, s)
addResult(c, s.typ.sons[0], n.info, skProc) addResult(c, s.typ.sons[0], n.info, skProc)
addResultNode(c, n)
let semBody = hloBody(c, semProcBody(c, n.sons[bodyPos])) let semBody = hloBody(c, semProcBody(c, n.sons[bodyPos]))
n.sons[bodyPos] = transformBody(c.module, semBody, s) n.sons[bodyPos] = transformBody(c.module, semBody, s)
addResultNode(c, n)
popProcCon(c) popProcCon(c)
elif efOperand notin flags: elif efOperand notin flags:
localError(n.info, errGenericLambdaNotAllowed) localError(n.info, errGenericLambdaNotAllowed)
@ -860,9 +864,9 @@ proc semInferredLambda(c: PContext, pt: TIdTable, n: PNode): PNode =
addParams(c, n.typ.n, skProc) addParams(c, n.typ.n, skProc)
pushProcCon(c, s) pushProcCon(c, s)
addResult(c, n.typ.sons[0], n.info, skProc) addResult(c, n.typ.sons[0], n.info, skProc)
addResultNode(c, n)
let semBody = hloBody(c, semProcBody(c, n.sons[bodyPos])) let semBody = hloBody(c, semProcBody(c, n.sons[bodyPos]))
n.sons[bodyPos] = transformBody(c.module, semBody, n.sons[namePos].sym) n.sons[bodyPos] = transformBody(c.module, semBody, n.sons[namePos].sym)
addResultNode(c, n)
popProcCon(c) popProcCon(c)
popOwner() popOwner()
closeScope(c) closeScope(c)

View file

@ -781,9 +781,10 @@ proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode,
result.rawAddSon(paramType) result.rawAddSon(paramType)
for i in 0 .. paramType.sonsLen - 2: for i in 0 .. paramType.sonsLen - 2:
let dummyType = if paramType.sons[i].kind == tyStatic: tyUnknown if paramType.sons[i].kind == tyStatic:
else: tyAnything result.rawAddSon makeTypeFromExpr(c, ast.emptyNode) # aka 'tyUnkown'
result.rawAddSon newTypeS(dummyType, c) else:
result.rawAddSon newTypeS(tyAnything, c)
if paramType.lastSon.kind == tyUserTypeClass: if paramType.lastSon.kind == tyUserTypeClass:
result.kind = tyUserTypeClassInst result.kind = tyUserTypeClassInst
@ -911,6 +912,8 @@ proc semProcTypeNode(c: PContext, n, genericParams: PNode,
if not hasType and not hasDefault: if not hasType and not hasDefault:
if isType: localError(a.info, "':' expected") if isType: localError(a.info, "':' expected")
let tdef = if kind in {skTemplate, skMacro}: tyExpr else: tyAnything let tdef = if kind in {skTemplate, skMacro}: tyExpr else: tyAnything
if tdef == tyAnything:
message(a.info, warnTypelessParam, renderTree(n))
typ = newTypeS(tdef, c) typ = newTypeS(tdef, c)
if skipTypes(typ, {tyGenericInst}).kind == tyEmpty: continue if skipTypes(typ, {tyGenericInst}).kind == tyEmpty: continue

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}:
@ -1056,7 +1057,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
of tyFromExpr: of tyFromExpr:
# fix the expression, so it contains the already instantiated types # fix the expression, so it contains the already instantiated types
if f.n == nil: return isGeneric if f.n == nil or f.n.kind == nkEmpty: return isGeneric
let reevaluated = tryResolvingStaticExpr(c, f.n) let reevaluated = tryResolvingStaticExpr(c, f.n)
case reevaluated.typ.kind case reevaluated.typ.kind
of tyTypeDesc: of tyTypeDesc:
@ -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)
container = newNodeIT(nkBracket, n.sons[a].info, arrayConstr(c, arg)) if container.isNil:
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

@ -28,8 +28,9 @@ proc hashTree(n: PNode): THash =
of nkFloatLit..nkFloat64Lit: of nkFloatLit..nkFloat64Lit:
if (n.floatVal >= - 1000000.0) and (n.floatVal <= 1000000.0): if (n.floatVal >= - 1000000.0) and (n.floatVal <= 1000000.0):
result = result !& toInt(n.floatVal) result = result !& toInt(n.floatVal)
of nkStrLit..nkTripleStrLit: of nkStrLit..nkTripleStrLit:
result = result !& hash(n.strVal) if not n.strVal.isNil:
result = result !& hash(n.strVal)
else: else:
for i in countup(0, sonsLen(n) - 1): for i in countup(0, sonsLen(n) - 1):
result = result !& hashTree(n.sons[i]) result = result !& hashTree(n.sons[i])

View file

@ -1029,16 +1029,18 @@ proc typeAllowedAux(marker: var IntSet, typ: PType, kind: TSymKind,
proc typeAllowedNode(marker: var IntSet, n: PNode, kind: TSymKind, proc typeAllowedNode(marker: var IntSet, n: PNode, kind: TSymKind,
flags: TTypeAllowedFlags = {}): PType = flags: TTypeAllowedFlags = {}): PType =
if n != nil: if n != nil:
result = typeAllowedAux(marker, n.typ, kind, flags) result = typeAllowedAux(marker, n.typ, kind, flags)
#if not result: debug(n.typ) #if not result: debug(n.typ)
if result == nil: if result == nil:
case n.kind case n.kind
of nkNone..nkNilLit: of nkNone..nkNilLit:
discard discard
else: else:
for i in countup(0, sonsLen(n) - 1): for i in countup(0, sonsLen(n) - 1):
result = typeAllowedNode(marker, n.sons[i], kind, flags) let it = n.sons[i]
if it.kind == nkRecCase and kind == skConst: return n.typ
result = typeAllowedNode(marker, it, kind, flags)
if result != nil: break if result != nil: break
proc matchType*(a: PType, pattern: openArray[tuple[k:TTypeKind, i:int]], proc matchType*(a: PType, pattern: openArray[tuple[k:TTypeKind, i:int]],
@ -1118,7 +1120,7 @@ proc typeAllowedAux(marker: var IntSet, typ: PType, kind: TSymKind,
result = typeAllowedAux(marker, t.sons[i], kind, flags) result = typeAllowedAux(marker, t.sons[i], kind, flags)
if result != nil: break if result != nil: break
of tyObject, tyTuple: of tyObject, tyTuple:
if kind == skConst and t.kind == tyObject: return t if kind == skConst and t.kind == tyObject and t.sons[0] != nil: return t
let flags = flags+{taField} let flags = flags+{taField}
for i in countup(0, sonsLen(t) - 1): for i in countup(0, sonsLen(t) - 1):
result = typeAllowedAux(marker, t.sons[i], kind, flags) result = typeAllowedAux(marker, t.sons[i], kind, flags)

View file

@ -1438,7 +1438,9 @@ proc evalMacroCall*(module: PSym, n, nOrig: PNode, sym: PSym): PNode =
# immediate macros can bypass any type and arity checking so we check the # immediate macros can bypass any type and arity checking so we check the
# arity here too: # arity here too:
if sym.typ.len > n.safeLen and sym.typ.len > 1: if sym.typ.len > n.safeLen and sym.typ.len > 1:
globalError(n.info, "got $#, but expected $# argument(s)" % [$ <n.safeLen, $ <sym.typ.len]) globalError(n.info, "in call '$#' got $#, but expected $# argument(s)" % [
n.renderTree,
$ <n.safeLen, $ <sym.typ.len])
setupGlobalCtx(module) setupGlobalCtx(module)
var c = globalCtx var c = globalCtx

View file

@ -604,7 +604,8 @@ proc genNarrowU(c: PCtx; n: PNode; dest: TDest) =
let t = skipTypes(n.typ, abstractVar-{tyTypeDesc}) let t = skipTypes(n.typ, abstractVar-{tyTypeDesc})
# uint is uint64 in the VM, we we only need to mask the result for # uint is uint64 in the VM, we we only need to mask the result for
# other unsigned types: # other unsigned types:
if t.kind in {tyUInt8..tyUInt32, tyInt8..tyInt32}: if t.kind in {tyUInt8..tyUInt32, tyInt8..tyInt32} or
(t.kind == tyInt and t.size == 4):
c.gABC(n, opcNarrowU, dest, TRegister(t.size*8)) c.gABC(n, opcNarrowU, dest, TRegister(t.size*8))
proc genBinaryABCnarrow(c: PCtx; n: PNode; dest: var TDest; opc: TOpcode) = proc genBinaryABCnarrow(c: PCtx; n: PNode; dest: var TDest; opc: TOpcode) =

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
@ -112,6 +112,20 @@ hint[LineTooLong]=off
gcc.cpp.options.always = "-w -fpermissive" gcc.cpp.options.always = "-w -fpermissive"
@end @end
# Configuration for the VxWorks
# This has been tested with VxWorks 6.9 only
@if vxworks:
# For now we only support compiling RTPs applications (i.e. no DKMs)
gcc.options.always = "-mrtp -fno-strict-aliasing -D_C99 -D_HAS_C9X -std=c99 -fasm -Wall -Wno-write-strings"
# The linker config must add the VxWorks common library for the selected
# processor which is usually found in:
# "$WIND_BASE/target/lib/usr/lib/PROCESSOR_FAMILY/PROCESSOR_TYPE/common",
# where PROCESSOR_FAMILY and PROCESSOR_TYPE are those supported by the VxWorks
# compiler (e.g. ppc/PPC32 or mips/MIPSI64, etc)
# For now we only support the PowerPC CPU
gcc.options.linker %= "-L $WIND_BASE/target/lib/usr/lib/ppc/PPC32/common -mrtp -fno-strict-aliasing -D_C99 -D_HAS_C9X -std=c99 -fasm -Wall -Wno-write-strings"
@end
gcc.options.speed = "-O3 -fno-strict-aliasing" gcc.options.speed = "-O3 -fno-strict-aliasing"
gcc.options.size = "-Os" gcc.options.size = "-Os"
gcc.options.debug = "-g3 -O0" gcc.options.debug = "-g3 -O0"

View file

@ -1,7 +1,7 @@
===================================================== =====================================================
Nim -- a Compiler for Nim. http://nim-lang.org/ Nim -- a Compiler for Nim. http://nim-lang.org/
Copyright (C) 2006-2014 Andreas Rumpf. All rights reserved. Copyright (C) 2006-2015 Andreas Rumpf. All rights reserved.
Permission is hereby granted, free of charge, to any person obtaining a copy Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal of this software and associated documentation files (the "Software"), to deal

View file

@ -66,7 +66,6 @@ Advanced options:
--threadanalysis:on|off turn thread analysis on|off --threadanalysis:on|off turn thread analysis on|off
--tlsEmulation:on|off turn thread local storage emulation on|off --tlsEmulation:on|off turn thread local storage emulation on|off
--taintMode:on|off turn taint mode on|off --taintMode:on|off turn taint mode on|off
--symbolFiles:on|off turn symbol files on|off (experimental)
--implicitStatic:on|off turn implicit compile time evaluation on|off --implicitStatic:on|off turn implicit compile time evaluation on|off
--patterns:on|off turn pattern matching on|off --patterns:on|off turn pattern matching on|off
--skipCfg do not read the general configuration file --skipCfg do not read the general configuration file

View file

@ -14,7 +14,6 @@ Options:
-p, --path:PATH add path to search paths -p, --path:PATH add path to search paths
-d, --define:SYMBOL define a conditional symbol -d, --define:SYMBOL define a conditional symbol
-u, --undef:SYMBOL undefine a conditional symbol -u, --undef:SYMBOL undefine a conditional symbol
--symbol:SYMBOL declare a conditional symbol
-f, --forceBuild force rebuilding of all modules -f, --forceBuild force rebuilding of all modules
--stackTrace:on|off turn stack tracing on|off --stackTrace:on|off turn stack tracing on|off
--lineTrace:on|off turn line tracing on|off --lineTrace:on|off turn line tracing on|off

View file

@ -12,10 +12,8 @@ Binary operators have 11 different levels of precedence.
Associativity Associativity
------------- -------------
Binary operators whose first character is ``^`` or its last character Binary operators whose first character is ``^`` are right-associative, all
is ``>`` are right-associative, all other binary operators are left-associative. other binary operators are left-associative.
Exception: The single "greater than" ``>`` operator is left-associative too.
Operators ending in ``>`` but longer than a single character are Operators ending in ``>`` but longer than a single character are
called `arrow like`:idx:. called `arrow like`:idx:.

File diff suppressed because it is too large Load diff

View file

@ -190,6 +190,6 @@ Real world example
The installers for the Nim compiler itself are generated by niminst. Have a The installers for the Nim compiler itself are generated by niminst. Have a
look at its configuration file: look at its configuration file:
.. include:: compiler/nim.ini .. include:: compiler/installer.ini
:literal: :literal:

View file

@ -97,13 +97,13 @@ const
compileNimInst = "-d:useLibzipSrc tools/niminst/niminst" compileNimInst = "-d:useLibzipSrc tools/niminst/niminst"
proc csource(args: string) = proc csource(args: string) =
exec("$4 cc $1 -r $3 --var:version=$2 --var:mingw=none csource compiler/nim.ini $1" % exec("$4 cc $1 -r $3 --var:version=$2 --var:mingw=none csource compiler/installer.ini $1" %
[args, VersionAsString, compileNimInst, findNim()]) [args, VersionAsString, compileNimInst, findNim()])
proc zip(args: string) = proc zip(args: string) =
exec("$3 cc -r $2 --var:version=$1 --var:mingw=none scripts compiler/nim.ini" % exec("$3 cc -r $2 --var:version=$1 --var:mingw=none scripts compiler/installer.ini" %
[VersionAsString, compileNimInst, findNim()]) [VersionAsString, compileNimInst, findNim()])
exec("$# --var:version=$# --var:mingw=none zip compiler/nim.ini" % exec("$# --var:version=$# --var:mingw=none zip compiler/installer.ini" %
["tools/niminst/niminst".exe, VersionAsString]) ["tools/niminst/niminst".exe, VersionAsString])
proc buildTool(toolname, args: string) = proc buildTool(toolname, args: string) =
@ -121,20 +121,20 @@ proc nsis(args: string) =
" nsis compiler/nim") % [VersionAsString, $(sizeof(pointer)*8)]) " nsis compiler/nim") % [VersionAsString, $(sizeof(pointer)*8)])
proc install(args: string) = proc install(args: string) =
exec("$# cc -r $# --var:version=$# --var:mingw=none scripts compiler/nim.ini" % exec("$# cc -r $# --var:version=$# --var:mingw=none scripts compiler/installer.ini" %
[findNim(), compileNimInst, VersionAsString]) [findNim(), compileNimInst, VersionAsString])
exec("sh ./install.sh $#" % args) exec("sh ./install.sh $#" % args)
proc web(args: string) = proc web(args: string) =
exec("$# cc -r tools/nimweb.nim $# web/nim --putenv:nimversion=$#" % exec("$# cc -r tools/nimweb.nim $# web/website.ini --putenv:nimversion=$#" %
[findNim(), args, VersionAsString]) [findNim(), args, VersionAsString])
proc website(args: string) = proc website(args: string) =
exec("$# cc -r tools/nimweb.nim $# --website web/nim --putenv:nimversion=$#" % exec("$# cc -r tools/nimweb.nim $# --website web/website.ini --putenv:nimversion=$#" %
[findNim(), args, VersionAsString]) [findNim(), args, VersionAsString])
proc pdf(args="") = proc pdf(args="") =
exec("$# cc -r tools/nimweb.nim $# --pdf web/nim --putenv:nimversion=$#" % exec("$# cc -r tools/nimweb.nim $# --pdf web/website.ini --putenv:nimversion=$#" %
[findNim(), args, VersionAsString]) [findNim(), args, VersionAsString])
# -------------- boot --------------------------------------------------------- # -------------- boot ---------------------------------------------------------

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

@ -16,11 +16,11 @@ type
TDbConn* = PMySQL ## encapsulates a database connection TDbConn* = PMySQL ## encapsulates a database connection
TRow* = seq[string] ## a row of a dataset. NULL database values will be TRow* = seq[string] ## a row of a dataset. NULL database values will be
## transformed always to the empty string. ## transformed always to the empty string.
EDb* = object of EIO ## exception that is raised if a database error occurs EDb* = object of IOError ## exception that is raised if a database error occurs
TSqlQuery* = distinct string ## an SQL query string TSqlQuery* = distinct string ## an SQL query string
FDb* = object of FIO ## effect that denotes a database operation FDb* = object of IOEffect ## effect that denotes a database operation
FReadDb* = object of FDb ## effect that denotes a read operation FReadDb* = object of FDb ## effect that denotes a read operation
FWriteDb* = object of FDb ## effect that denotes a write operation FWriteDb* = object of FDb ## effect that denotes a write operation

View file

@ -1,7 +1,7 @@
# #
# #
# Nim's Runtime Library # Nim's Runtime Library
# (c) Copyright 2012 Andreas Rumpf # (c) Copyright 2015 Andreas Rumpf
# #
# See the file "copying.txt", included in this # See the file "copying.txt", included in this
# distribution, for details about the copyright. # distribution, for details about the copyright.
@ -13,6 +13,8 @@
## is used. This suffices because Windows' console already provides the ## is used. This suffices because Windows' console already provides the
## wanted functionality. ## wanted functionality.
{.deadCodeElim: on.}
when defined(Windows): when defined(Windows):
proc readLineFromStdin*(prompt: string): TaintedString {. proc readLineFromStdin*(prompt: string): TaintedString {.
tags: [ReadIOEffect, WriteIOEffect].} = tags: [ReadIOEffect, WriteIOEffect].} =
@ -31,23 +33,27 @@ when defined(Windows):
stdout.write(prompt) stdout.write(prompt)
result = readLine(stdin, line) result = readLine(stdin, line)
proc getch(): cint {.header: "<conio.h>", importc: "_getch".} proc readPasswordFromStdin*(prompt: string, password: var TaintedString):
bool {.tags: [ReadIOEffect, WriteIOEffect].} =
proc readPasswordFromStdin*(prompt: string, password: var TaintedString) =
## 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".}
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
of '\r', chr(0xA): of '\r', chr(0xA):
break break
of '\b': of '\b':
password.setLen(result.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
@ -78,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
@ -87,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

@ -379,7 +379,7 @@ static inline void GCGuard (void *ptr) { asm volatile ("" :: "X" (ptr)); }
# define GC_GUARD # define GC_GUARD
#endif #endif
/* Test to see if nimrod and the C compiler agree on the size of a pointer. /* Test to see if Nim and the C compiler agree on the size of a pointer.
On disagreement, your C compiler will say something like: On disagreement, your C compiler will say something like:
"error: 'assert_numbits' declared as an array with a negative size" */ "error: 'assert_numbits' declared as an array with a negative size" */
typedef int assert_numbits[sizeof(NI) == sizeof(void*) && NIM_INTBITS == sizeof(NI)*8 ? 1 : -1]; typedef int assert_numbits[sizeof(NI) == sizeof(void*) && NIM_INTBITS == sizeof(NI)*8 ? 1 : -1];
@ -390,3 +390,12 @@ typedef int assert_numbits[sizeof(NI) == sizeof(void*) && NIM_INTBITS == sizeof(
#else #else
# define NIM_EXTERNC # define NIM_EXTERNC
#endif #endif
/* ---------------- platform specific includes ----------------------- */
/* VxWorks related includes */
#if defined(__VXWORKS__)
# include <sys/types.h>
# include <types/vxWind.h>
# include <tool/gnu/toolMacros.h>
#endif

View file

@ -70,17 +70,10 @@ 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): type
# to not break bootstrapping again ... TDIR* {.importc: "DIR", header: "<dirent.h>",
type incompleteStruct.} = object
TDIR* {.importc: "DIR", header: "<dirent.h>", ## A type representing a directory stream.
final, pure, 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.
type type
SocketHandle* = distinct cint # The type used to represent socket descriptors SocketHandle* = distinct cint # The type used to represent socket descriptors

View file

@ -1064,6 +1064,17 @@ proc accept*(socket: TAsyncFD,
# -- Await Macro # -- Await Macro
proc skipUntilStmtList(node: PNimrodNode): PNimrodNode {.compileTime.} =
# Skips a nest of StmtList's.
result = node
if node[0].kind == nnkStmtList:
result = skipUntilStmtList(node[0])
proc skipStmtList(node: PNimrodNode): PNimrodNode {.compileTime.} =
result = node
if node[0].kind == nnkStmtList:
result = node[0]
template createCb(retFutureSym, iteratorNameSym, template createCb(retFutureSym, iteratorNameSym,
name: expr): stmt {.immediate.} = name: expr): stmt {.immediate.} =
var nameIterVar = iteratorNameSym var nameIterVar = iteratorNameSym
@ -1211,26 +1222,53 @@ proc processBody(node, retFutureSym: PNimrodNode,
of nnkTryStmt: of nnkTryStmt:
# try: await x; except: ... # try: await x; except: ...
result = newNimNode(nnkStmtList, node) result = newNimNode(nnkStmtList, node)
template wrapInTry(n, tryBody: PNimrodNode) =
var temp = n
n[0] = tryBody
tryBody = temp
# Transform ``except`` body.
# TODO: Could we perform some ``await`` transformation here to get it
# working in ``except``?
tryBody[1] = processBody(n[1], retFutureSym, subTypeIsVoid, nil)
proc processForTry(n: PNimrodNode, i: var int, proc processForTry(n: PNimrodNode, i: var int,
res: PNimrodNode): bool {.compileTime.} = res: PNimrodNode): bool {.compileTime.} =
## Transforms the body of the tryStmt. Does not transform the
## body in ``except``.
## Returns true if the tryStmt node was transformed into an ifStmt.
result = false result = false
while i < n[0].len: var skipped = n.skipStmtList()
var processed = processBody(n[0][i], retFutureSym, subTypeIsVoid, n) while i < skipped.len:
if processed.kind != n[0][i].kind or processed.len != n[0][i].len: var processed = processBody(skipped[i], retFutureSym,
subTypeIsVoid, n)
# Check if we transformed the node into an exception check.
# This suggests skipped[i] contains ``await``.
if processed.kind != skipped[i].kind or processed.len != skipped[i].len:
processed = processed.skipUntilStmtList()
expectKind(processed, nnkStmtList) expectKind(processed, nnkStmtList)
expectKind(processed[2][1], nnkElse) expectKind(processed[2][1], nnkElse)
i.inc i.inc
discard processForTry(n, i, processed[2][1][0])
if not processForTry(n, i, processed[2][1][0]):
# We need to wrap the nnkElse nodes back into a tryStmt.
# As they are executed if an exception does not happen
# inside the awaited future.
# The following code will wrap the nodes inside the
# original tryStmt.
wrapInTry(n, processed[2][1][0])
res.add processed res.add processed
result = true result = true
else: else:
res.add n[0][i] res.add skipped[i]
i.inc i.inc
var i = 0 var i = 0
if not processForTry(node, i, result): if not processForTry(node, i, result):
var temp = node # If the tryStmt hasn't been transformed we can just put the body
temp[0] = result # back into it.
result = temp wrapInTry(node, result)
return return
else: discard else: discard
@ -1329,8 +1367,8 @@ macro async*(prc: stmt): stmt {.immediate.} =
result[6] = outerProcBody result[6] = outerProcBody
#echo(treeRepr(result)) #echo(treeRepr(result))
#if prc[0].getName == "catch": if prc[0].getName == "test3":
# echo(toStrLit(result)) echo(toStrLit(result))
proc recvLine*(socket: TAsyncFD): Future[string] {.async.} = proc recvLine*(socket: TAsyncFD): Future[string] {.async.} =
## Reads a line of data from ``socket``. Returned future will complete once ## Reads a line of data from ``socket``. Returned future will complete once

View file

@ -24,9 +24,12 @@ import
when not defined(nimhygiene): when not defined(nimhygiene):
{.pragma: dirty.} {.pragma: dirty.}
# For "integer-like A" that are too big for intsets/bit-vectors to be practical,
# it would be best to shrink hcode to the same size as the integer. Larger
# codes should never be needed, and this can pack more entries per cache-line.
# Losing hcode entirely is also possible - if some element value is forbidden.
type type
SlotEnum = enum seEmpty, seFilled, seDeleted KeyValuePair[A] = tuple[hcode: THash, key: A]
KeyValuePair[A] = tuple[slot: SlotEnum, key: A]
KeyValuePairSeq[A] = seq[KeyValuePair[A]] KeyValuePairSeq[A] = seq[KeyValuePair[A]]
HashSet* {.myShallow.}[A] = object ## \ HashSet* {.myShallow.}[A] = object ## \
## A generic hash set. ## A generic hash set.
@ -38,6 +41,14 @@ type
{.deprecated: [TSet: HashSet].} {.deprecated: [TSet: HashSet].}
# hcode for real keys cannot be zero. hcode==0 signifies an empty slot. These
# two procs retain clarity of that encoding without the space cost of an enum.
proc isEmpty(hcode: THash): bool {.inline.} =
result = hcode == 0
proc isFilled(hcode: THash): bool {.inline.} =
result = hcode != 0
proc isValid*[A](s: HashSet[A]): bool = proc isValid*[A](s: HashSet[A]): bool =
## Returns `true` if the set has been initialized with `initSet <#initSet>`_. ## Returns `true` if the set has been initialized with `initSet <#initSet>`_.
## ##
@ -94,7 +105,7 @@ iterator items*[A](s: HashSet[A]): A =
## # --> {(a: 1, b: 3), (a: 0, b: 4)} ## # --> {(a: 1, b: 3), (a: 0, b: 4)}
assert s.isValid, "The set needs to be initialized." assert s.isValid, "The set needs to be initialized."
for h in 0..high(s.data): for h in 0..high(s.data):
if s.data[h].slot == seFilled: yield s.data[h].key if isFilled(s.data[h].hcode): yield s.data[h].key
const const
growthFactor = 2 growthFactor = 2
@ -103,25 +114,44 @@ proc mustRehash(length, counter: int): bool {.inline.} =
assert(length > counter) assert(length > counter)
result = (length * 2 < counter * 3) or (length - counter < 4) result = (length * 2 < counter * 3) or (length - counter < 4)
proc nextTry(h, maxHash: THash): THash {.inline.} = proc rightSize*(count: int): int {.inline.} =
result = ((5 * h) + 1) and maxHash ## Return the value of `initialSize` to support `count` items.
##
## If more items are expected to be added, simply add that
## expected extra amount to the parameter before calling this.
##
## Internally, we want mustRehash(rightSize(x), x) == false.
result = nextPowerOfTwo(count * 3 div 2 + 4)
template rawGetImpl() {.dirty.} = proc nextTry(h, maxHash: THash): THash {.inline.} =
var h: THash = hash(key) and high(s.data) # start with real hash value result = (h + 1) and maxHash
while s.data[h].slot != seEmpty:
if s.data[h].key == key and s.data[h].slot == seFilled: template rawGetKnownHCImpl() {.dirty.} =
var h: THash = hc and high(s.data) # start with real hash value
while isFilled(s.data[h].hcode):
# Compare hc THEN key with boolean short circuit. This makes the common case
# zero ==key's for missing (e.g.inserts) and exactly one ==key for present.
# It does slow down succeeding lookups by one extra THash cmp&and..usually
# just a few clock cycles, generally worth it for any non-integer-like A.
if s.data[h].hcode == hc and s.data[h].key == key: # compare hc THEN key
return h return h
h = nextTry(h, high(s.data)) h = nextTry(h, high(s.data))
result = -1 result = -1 - h # < 0 => MISSING; insert idx = -1 - result
template rawGetImpl() {.dirty.} =
hc = hash(key)
if hc == 0: # This almost never taken branch should be very predictable.
hc = 314159265 # Value doesn't matter; Any non-zero favorite is fine.
rawGetKnownHCImpl()
template rawInsertImpl() {.dirty.} = template rawInsertImpl() {.dirty.} =
var h: THash = hash(key) and high(data)
while data[h].slot == seFilled:
h = nextTry(h, high(data))
data[h].key = key data[h].key = key
data[h].slot = seFilled data[h].hcode = hc
proc rawGet[A](s: HashSet[A], key: A): int = proc rawGetKnownHC[A](s: HashSet[A], key: A, hc: THash): int {.inline.} =
rawGetKnownHCImpl()
proc rawGet[A](s: HashSet[A], key: A, hc: var THash): int {.inline.} =
rawGetImpl() rawGetImpl()
proc mget*[A](s: var HashSet[A], key: A): var A = proc mget*[A](s: var HashSet[A], key: A): var A =
@ -130,7 +160,8 @@ proc mget*[A](s: var HashSet[A], key: A): var A =
## when one overloaded 'hash' and '==' but still needs reference semantics ## when one overloaded 'hash' and '==' but still needs reference semantics
## for sharing. ## for sharing.
assert s.isValid, "The set needs to be initialized." assert s.isValid, "The set needs to be initialized."
var index = rawGet(s, key) var hc: THash
var index = rawGet(s, key, hc)
if index >= 0: result = s.data[index].key if index >= 0: result = s.data[index].key
else: raise newException(KeyError, "key not found: " & $key) else: raise newException(KeyError, "key not found: " & $key)
@ -147,33 +178,43 @@ proc contains*[A](s: HashSet[A], key: A): bool =
## values.excl(2) ## values.excl(2)
## assert(not values.contains(2)) ## assert(not values.contains(2))
assert s.isValid, "The set needs to be initialized." assert s.isValid, "The set needs to be initialized."
var index = rawGet(s, key) var hc: THash
var index = rawGet(s, key, hc)
result = index >= 0 result = index >= 0
proc rawInsert[A](s: var HashSet[A], data: var KeyValuePairSeq[A], key: A) = proc rawInsert[A](s: var HashSet[A], data: var KeyValuePairSeq[A], key: A,
hc: THash, h: THash) =
rawInsertImpl() rawInsertImpl()
proc enlarge[A](s: var HashSet[A]) = proc enlarge[A](s: var HashSet[A]) =
var n: KeyValuePairSeq[A] var n: KeyValuePairSeq[A]
newSeq(n, len(s.data) * growthFactor) newSeq(n, len(s.data) * growthFactor)
for i in countup(0, high(s.data)): swap(s.data, n) # n is now old seq
if s.data[i].slot == seFilled: rawInsert(s, n, s.data[i].key) for i in countup(0, high(n)):
swap(s.data, n) if isFilled(n[i].hcode):
var j = -1 - rawGetKnownHC(s, n[i].key, n[i].hcode)
rawInsert(s, s.data, n[i].key, n[i].hcode, j)
template inclImpl() {.dirty.} = template inclImpl() {.dirty.} =
var index = rawGet(s, key) var hc: THash
var index = rawGet(s, key, hc)
if index < 0: if index < 0:
if mustRehash(len(s.data), s.counter): enlarge(s) if mustRehash(len(s.data), s.counter):
rawInsert(s, s.data, key) enlarge(s)
index = rawGetKnownHC(s, key, hc)
rawInsert(s, s.data, key, hc, -1 - index)
inc(s.counter) inc(s.counter)
template containsOrInclImpl() {.dirty.} = template containsOrInclImpl() {.dirty.} =
var index = rawGet(s, key) var hc: THash
var index = rawGet(s, key, hc)
if index >= 0: if index >= 0:
result = true result = true
else: else:
if mustRehash(len(s.data), s.counter): enlarge(s) if mustRehash(len(s.data), s.counter):
rawInsert(s, s.data, key) enlarge(s)
index = rawGetKnownHC(s, key, hc)
rawInsert(s, s.data, key, hc, -1 - index)
inc(s.counter) inc(s.counter)
proc incl*[A](s: var HashSet[A], key: A) = proc incl*[A](s: var HashSet[A], key: A) =
@ -204,6 +245,11 @@ proc incl*[A](s: var HashSet[A], other: HashSet[A]) =
assert other.isValid, "The set `other` needs to be initialized." assert other.isValid, "The set `other` needs to be initialized."
for item in other: incl(s, item) for item in other: incl(s, item)
template doWhile(a: expr, b: stmt): stmt =
while true:
b
if not a: break
proc excl*[A](s: var HashSet[A], key: A) = proc excl*[A](s: var HashSet[A], key: A) =
## Excludes `key` from the set `s`. ## Excludes `key` from the set `s`.
## ##
@ -215,10 +261,22 @@ proc excl*[A](s: var HashSet[A], key: A) =
## s.excl(2) ## s.excl(2)
## assert s.len == 3 ## assert s.len == 3
assert s.isValid, "The set needs to be initialized." assert s.isValid, "The set needs to be initialized."
var index = rawGet(s, key) var hc: THash
if index >= 0: var i = rawGet(s, key, hc)
s.data[index].slot = seDeleted var msk = high(s.data)
if i >= 0:
s.data[i].hcode = 0
dec(s.counter) dec(s.counter)
while true: # KnuthV3 Algo6.4R adapted for i=i+1 instead of i=i-1
var j = i # The correctness of this depends on (h+1) in nextTry,
var r = j # though may be adaptable to other simple sequences.
s.data[i].hcode = 0 # mark current EMPTY
doWhile ((i >= r and r > j) or (r > j and j > i) or (j > i and i >= r)):
i = (i + 1) and msk # increment mod table size
if isEmpty(s.data[i].hcode): # end of collision cluster; So all done
return
r = s.data[i].hcode and msk # "home" location of key@i
s.data[j] = s.data[i] # data[j] will be marked EMPTY next loop
proc excl*[A](s: var HashSet[A], other: HashSet[A]) = proc excl*[A](s: var HashSet[A], other: HashSet[A]) =
## Excludes everything in `other` from `s`. ## Excludes everything in `other` from `s`.
@ -295,7 +353,7 @@ proc toSet*[A](keys: openArray[A]): HashSet[A] =
## var numbers = toSet([1, 2, 3, 4, 5]) ## var numbers = toSet([1, 2, 3, 4, 5])
## assert numbers.contains(2) ## assert numbers.contains(2)
## assert numbers.contains(4) ## assert numbers.contains(4)
result = initSet[A](nextPowerOfTwo(keys.len+10)) result = initSet[A](rightSize(keys.len))
for key in items(keys): result.incl(key) for key in items(keys): result.incl(key)
template dollarImpl(): stmt {.dirty.} = template dollarImpl(): stmt {.dirty.} =
@ -494,7 +552,7 @@ proc map*[A, B](data: HashSet[A], op: proc (x: A): B {.closure.}): HashSet[B] =
type type
OrderedKeyValuePair[A] = tuple[ OrderedKeyValuePair[A] = tuple[
slot: SlotEnum, next: int, key: A] hcode: THash, next: int, key: A]
OrderedKeyValuePairSeq[A] = seq[OrderedKeyValuePair[A]] OrderedKeyValuePairSeq[A] = seq[OrderedKeyValuePair[A]]
OrderedSet* {.myShallow.}[A] = object ## \ OrderedSet* {.myShallow.}[A] = object ## \
## A generic hash set that remembers insertion order. ## A generic hash set that remembers insertion order.
@ -546,7 +604,7 @@ template forAllOrderedPairs(yieldStmt: stmt) {.dirty, immediate.} =
var h = s.first var h = s.first
while h >= 0: while h >= 0:
var nxt = s.data[h].next var nxt = s.data[h].next
if s.data[h].slot == seFilled: yieldStmt if isFilled(s.data[h].hcode): yieldStmt
h = nxt h = nxt
iterator items*[A](s: OrderedSet[A]): A = iterator items*[A](s: OrderedSet[A]): A =
@ -571,7 +629,10 @@ iterator items*[A](s: OrderedSet[A]): A =
forAllOrderedPairs: forAllOrderedPairs:
yield s.data[h].key yield s.data[h].key
proc rawGet[A](s: OrderedSet[A], key: A): int = proc rawGetKnownHC[A](s: OrderedSet[A], key: A, hc: THash): int {.inline.} =
rawGetKnownHCImpl()
proc rawGet[A](s: OrderedSet[A], key: A, hc: var THash): int {.inline.} =
rawGetImpl() rawGetImpl()
proc contains*[A](s: OrderedSet[A], key: A): bool = proc contains*[A](s: OrderedSet[A], key: A): bool =
@ -585,11 +646,12 @@ proc contains*[A](s: OrderedSet[A], key: A): bool =
## values.incl(2) ## values.incl(2)
## assert values.contains(2) ## assert values.contains(2)
assert s.isValid, "The set needs to be initialized." assert s.isValid, "The set needs to be initialized."
var index = rawGet(s, key) var hc: THash
var index = rawGet(s, key, hc)
result = index >= 0 result = index >= 0
proc rawInsert[A](s: var OrderedSet[A], proc rawInsert[A](s: var OrderedSet[A], data: var OrderedKeyValuePairSeq[A],
data: var OrderedKeyValuePairSeq[A], key: A) = key: A, hc: THash, h: THash) =
rawInsertImpl() rawInsertImpl()
data[h].next = -1 data[h].next = -1
if s.first < 0: s.first = h if s.first < 0: s.first = h
@ -602,12 +664,13 @@ proc enlarge[A](s: var OrderedSet[A]) =
var h = s.first var h = s.first
s.first = -1 s.first = -1
s.last = -1 s.last = -1
while h >= 0:
var nxt = s.data[h].next
if s.data[h].slot == seFilled:
rawInsert(s, n, s.data[h].key)
h = nxt
swap(s.data, n) swap(s.data, n)
while h >= 0:
var nxt = n[h].next
if isFilled(n[h].hcode):
var j = -1 - rawGetKnownHC(s, n[h].key, n[h].hcode)
rawInsert(s, s.data, n[h].key, n[h].hcode, j)
h = nxt
proc incl*[A](s: var OrderedSet[A], key: A) = proc incl*[A](s: var OrderedSet[A], key: A) =
## Includes an element `key` in `s`. ## Includes an element `key` in `s`.
@ -655,7 +718,7 @@ proc containsOrIncl*[A](s: var OrderedSet[A], key: A): bool =
proc init*[A](s: var OrderedSet[A], initialSize=64) = proc init*[A](s: var OrderedSet[A], initialSize=64) =
## Initializes an ordered hash set. ## Initializes an ordered hash set.
## ##
## The `initialSize` parameter needs to be a power of too. You can use ## The `initialSize` parameter needs to be a power of two. You can use
## `math.nextPowerOfTwo() <math.html#nextPowerOfTwo>`_ to guarantee that at ## `math.nextPowerOfTwo() <math.html#nextPowerOfTwo>`_ to guarantee that at
## runtime. All set variables have to be initialized before you can use them ## runtime. All set variables have to be initialized before you can use them
## with other procs from this module with the exception of `isValid() ## with other procs from this module with the exception of `isValid()
@ -698,7 +761,7 @@ proc toOrderedSet*[A](keys: openArray[A]): OrderedSet[A] =
## var numbers = toOrderedSet([1, 2, 3, 4, 5]) ## var numbers = toOrderedSet([1, 2, 3, 4, 5])
## assert numbers.contains(2) ## assert numbers.contains(2)
## assert numbers.contains(4) ## assert numbers.contains(4)
result = initOrderedSet[A](nextPowerOfTwo(keys.len+10)) result = initOrderedSet[A](rightSize(keys.len))
for key in items(keys): result.incl(key) for key in items(keys): result.incl(key)
proc `$`*[A](s: OrderedSet[A]): string = proc `$`*[A](s: OrderedSet[A]): string =
@ -726,7 +789,7 @@ proc `==`*[A](s, t: OrderedSet[A]): bool =
while h >= 0 and g >= 0: while h >= 0 and g >= 0:
var nxh = s.data[h].next var nxh = s.data[h].next
var nxg = t.data[g].next var nxg = t.data[g].next
if s.data[h].slot == seFilled and s.data[g].slot == seFilled: if isFilled(s.data[h].hcode) and isFilled(s.data[g].hcode):
if s.data[h].key == s.data[g].key: if s.data[h].key == s.data[g].key:
inc compared inc compared
else: else:
@ -901,6 +964,11 @@ proc testModule() =
b.incl(2) b.incl(2)
assert b.len == 1 assert b.len == 1
for i in 0 .. 32:
var s = rightSize(i)
if s <= i or mustRehash(s, i):
echo "performance issue: rightSize() will not elide enlarge() at ", i
echo "Micro tests run successfully." echo "Micro tests run successfully."
when isMainModule and not defined(release): testModule() when isMainModule and not defined(release): testModule()

View file

@ -347,7 +347,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,8 +1010,16 @@ 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.
if c_rename(source, dest) != 0'i32: when defined(Windows):
raise newException(OSError, $strerror(errno)) 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:
raise newException(OSError, $strerror(errno))
when not declared(ENOENT) and not defined(Windows): when not declared(ENOENT) and not defined(Windows):
when NoFakeVars: when NoFakeVars:
@ -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

@ -45,6 +45,21 @@ when defined(windows):
var var
oldAttr = getAttributes() oldAttr = getAttributes()
proc winGetch(): cint {.header: "<conio.h>", importc: "_getch".}
else:
import termios, unsigned
proc setRaw(fd: FileHandle, time: cint = TCSAFLUSH) =
var mode: Termios
discard fd.tcgetattr(addr mode)
mode.iflag = mode.iflag and not Tcflag(BRKINT or ICRNL or INPCK or ISTRIP or IXON)
mode.oflag = mode.oflag and not Tcflag(OPOST)
mode.cflag = (mode.cflag and not Tcflag(CSIZE or PARENB)) or CS8
mode.lflag = mode.lflag and not Tcflag(ECHO or ICANON or IEXTEN or ISIG)
mode.cc[VMIN] = 1.cuchar
mode.cc[VTIME] = 0.cuchar
discard fd.tcsetattr(time, addr mode)
proc setCursorPos*(x, y: int) = proc setCursorPos*(x, y: int) =
## sets the terminal's cursor to the (x,y) position. (0,0) is the ## sets the terminal's cursor to the (x,y) position. (0,0) is the
## upper left of the screen. ## upper left of the screen.
@ -349,6 +364,19 @@ macro styledEcho*(m: varargs[expr]): stmt =
result.add(newCall(bindSym"write", bindSym"stdout", newStrLitNode("\n"))) result.add(newCall(bindSym"write", bindSym"stdout", newStrLitNode("\n")))
result.add(newCall(bindSym"resetAttributes")) result.add(newCall(bindSym"resetAttributes"))
proc getch*(): char =
## Read a single character from the terminal, blocking until it is entered.
## The character is not printed to the terminal.
when defined(windows):
result = winGetch().char
else:
let fd = getFileHandle(stdin)
var oldMode: Termios
discard fd.tcgetattr(addr oldMode)
fd.setRaw()
result = stdin.readChar()
discard fd.tcsetattr(TCSADRAIN, addr oldMode)
when isMainModule: when isMainModule:
system.addQuitProc(resetAttributes) system.addQuitProc(resetAttributes)
write(stdout, "never mind") write(stdout, "never mind")

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,14 +2247,9 @@ 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
## get type information for `x`. Ordinary code should not use this, but ## the `typeinfo` module instead.
## 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
## the `typeinfo` module instead.
{.push stackTrace: off.} {.push stackTrace: off.}
proc abs*(x: int): int {.magic: "AbsI", noSideEffect.} = proc abs*(x: int): int {.magic: "AbsI", noSideEffect.} =
@ -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], benign.}
tags: [WriteIOEffect], gcsafe, locks: 0.} ## 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()
@ -3161,7 +3151,7 @@ when hostOS != "standalone":
x[j+i] = item[j] x[j+i] = item[j]
inc(j) inc(j)
proc compiles*(x): bool {.magic: "Compiles", noSideEffect.} = proc compiles*(x: expr): bool {.magic: "Compiles", noSideEffect.} =
## Special compile-time procedure that checks whether `x` can be compiled ## Special compile-time procedure that checks whether `x` can be compiled
## without any semantic error. ## without any semantic error.
## This can be used to check whether a type supports some operation: ## This can be used to check whether a type supports some operation:

View file

@ -27,7 +27,7 @@ proc genericAssignAux(dest, src: pointer, n: ptr TNimNode,
var m = selectBranch(src, n) var m = selectBranch(src, n)
# reset if different branches are in use; note different branches also # reset if different branches are in use; note different branches also
# imply that's not self-assignment (``x = x``)! # imply that's not self-assignment (``x = x``)!
if m != dd and dd != nil: if m != dd and dd != nil:
genericResetAux(dest, dd) genericResetAux(dest, dd)
copyMem(cast[pointer](d +% n.offset), cast[pointer](s +% n.offset), copyMem(cast[pointer](d +% n.offset), cast[pointer](s +% n.offset),
n.typ.size) n.typ.size)
@ -205,9 +205,13 @@ proc genericReset(dest: pointer, mt: PNimType) =
case mt.kind case mt.kind
of tyString, tyRef, tySequence: of tyString, tyRef, tySequence:
unsureAsgnRef(cast[PPointer](dest), nil) unsureAsgnRef(cast[PPointer](dest), nil)
of tyObject, tyTuple: of tyTuple:
# we don't need to reset m_type field for tyObject
genericResetAux(dest, mt.node) genericResetAux(dest, mt.node)
of tyObject:
genericResetAux(dest, mt.node)
# also reset the type field for tyObject, for correct branch switching!
var pint = cast[ptr PNimType](dest)
pint[] = nil
of tyArray, tyArrayConstr: of tyArray, tyArrayConstr:
for i in 0..(mt.size div mt.base.size)-1: for i in 0..(mt.size div mt.base.size)-1:
genericReset(cast[pointer](d +% i*% mt.base.size), mt.base) genericReset(cast[pointer](d +% i*% mt.base.size), mt.base)

View file

@ -175,6 +175,8 @@ proc auxWriteStackTrace(f: PFrame, s: var string) =
add(s, tempFrames[j].procname) add(s, tempFrames[j].procname)
add(s, "\n") add(s, "\n")
proc stackTraceAvailable*(): bool
when hasSomeStackTrace: when hasSomeStackTrace:
proc rawWriteStackTrace(s: var string) = proc rawWriteStackTrace(s: var string) =
when NimStackTrace: when NimStackTrace:
@ -188,6 +190,18 @@ when hasSomeStackTrace:
auxWriteStackTraceWithBacktrace(s) auxWriteStackTraceWithBacktrace(s)
else: else:
add(s, "No stack traceback available\n") add(s, "No stack traceback available\n")
proc stackTraceAvailable(): bool =
when NimStackTrace:
if framePtr == nil:
result = false
else:
result = true
elif defined(nativeStackTrace) and nativeStackTraceSupported:
result = true
else:
result = false
else:
proc stackTraceAvailable*(): bool = result = false
proc quitOrDebug() {.inline.} = proc quitOrDebug() {.inline.} =
when not defined(endb): when not defined(endb):

View file

@ -528,20 +528,9 @@ proc growObj(old: pointer, newsize: int, gch: var TGcHeap): pointer =
zeroMem(cast[pointer](cast[ByteAddress](res)+% oldsize +% sizeof(TCell)), zeroMem(cast[pointer](cast[ByteAddress](res)+% oldsize +% sizeof(TCell)),
newsize-oldsize) newsize-oldsize)
sysAssert((cast[ByteAddress](res) and (MemAlign-1)) == 0, "growObj: 3") sysAssert((cast[ByteAddress](res) and (MemAlign-1)) == 0, "growObj: 3")
sysAssert(res.refcount shr rcShift <=% 1, "growObj: 4") # This can be wrong for intermediate temps that are nevertheless on the
#if res.refcount <% rcIncrement: # heap because of lambda lifting:
# add(gch.zct, res) #gcAssert(res.refcount shr rcShift <=% 1, "growObj: 4")
#else: # XXX: what to do here?
# decRef(ol)
if (ol.refcount and ZctFlag) != 0:
var j = gch.zct.len-1
var d = gch.zct.d
while j >= 0:
if d[j] == ol:
d[j] = res
break
dec(j)
if canbeCycleRoot(ol): excl(gch.cycleRoots, ol)
when logGC: when logGC:
writeCell("growObj old cell", ol) writeCell("growObj old cell", ol)
writeCell("growObj new cell", res) writeCell("growObj new cell", res)
@ -549,7 +538,26 @@ proc growObj(old: pointer, newsize: int, gch: var TGcHeap): pointer =
gcTrace(res, csAllocated) gcTrace(res, csAllocated)
when reallyDealloc: when reallyDealloc:
sysAssert(allocInv(gch.region), "growObj before dealloc") sysAssert(allocInv(gch.region), "growObj before dealloc")
rawDealloc(gch.region, ol) if ol.refcount shr rcShift <=% 1:
# free immediately to save space:
if (ol.refcount and ZctFlag) != 0:
var j = gch.zct.len-1
var d = gch.zct.d
while j >= 0:
if d[j] == ol:
d[j] = res
break
dec(j)
if canbeCycleRoot(ol): excl(gch.cycleRoots, ol)
rawDealloc(gch.region, ol)
else:
# we split the old refcount in 2 parts. XXX This is still not entirely
# correct if the pointer that receives growObj's result is on the stack.
# A better fix would be to emit the location specific write barrier for
# 'growObj', but this is lost of more work and who knows what new problems
# this would create.
res.refcount = rcIncrement
decRef(ol)
else: else:
sysAssert(ol.typ != nil, "growObj: 5") sysAssert(ol.typ != nil, "growObj: 5")
zeroMem(ol, sizeof(TCell)) zeroMem(ol, sizeof(TCell))

View file

@ -297,10 +297,12 @@ proc growObj(old: pointer, newsize: int, gch: var TGcHeap): pointer =
zeroMem(cast[pointer](cast[ByteAddress](res)+% oldsize +% sizeof(TCell)), zeroMem(cast[pointer](cast[ByteAddress](res)+% oldsize +% sizeof(TCell)),
newsize-oldsize) newsize-oldsize)
sysAssert((cast[ByteAddress](res) and (MemAlign-1)) == 0, "growObj: 3") sysAssert((cast[ByteAddress](res) and (MemAlign-1)) == 0, "growObj: 3")
when withBitvectors: excl(gch.allocated, ol) when false:
when reallyDealloc: rawDealloc(gch.region, ol) # this is wrong since seqs can be shared via 'shallow':
else: when withBitvectors: excl(gch.allocated, ol)
zeroMem(ol, sizeof(TCell)) when reallyDealloc: rawDealloc(gch.region, ol)
else:
zeroMem(ol, sizeof(TCell))
when withBitvectors: incl(gch.allocated, res) when withBitvectors: incl(gch.allocated, res)
when useCellIds: when useCellIds:
inc gch.idGenerator inc gch.idGenerator

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

@ -13,7 +13,7 @@
when defined(Unix): when defined(Unix):
when defined(macosx): when defined(macosx):
const const
lib = "libmysqlclient.(15|16|17[18).dylib" lib = "libmysqlclient.(15|16|17|18).dylib"
else: else:
const const
lib = "libmysqlclient.so.(15|16|17|18)" lib = "libmysqlclient.so.(15|16|17|18)"

View file

@ -66,8 +66,24 @@ Copyright (c) 2006-2014 Andreas Rumpf.
All rights reserved. All rights reserved.
# Build Status # Build Status
| |Linux|Windows|Mac| [**Build Waterfall**][waterfall]
|---|---|---|---|
| x86 | ![](http://178.62.143.63:8010/buildstatusimage?builder=linux-x32-builder) | ![](http://178.62.143.63:8010/buildstatusimage?builder=windows-x32-builder) | ![](http://178.62.143.63:8010/buildstatusimage?builder=mac-x32-builder) | | Linux | Windows | Mac |
| x86_64 | ![](http://178.62.143.63:8010/buildstatusimage?builder=linux-x64-builder) | ![](http://178.62.143.63:8010/buildstatusimage?builder=windows-x64-builder) | ![](http://178.62.143.63:8010/buildstatusimage?builder=mac-x64-builder) | ------ | ----- | ------- | --- |
| arm | ![](http://178.62.143.63:8010/buildstatusimage?builder=linux-arm5-builder) | | x86 | ![linux-x86][linux-x86-img] | ![windows-x86][windows-x86-img] | ![mac-x86][mac-x86-img] |
| x86_64 | ![linux-x86_64][linux-x86_64-img] | ![windows-x86_64][windows-x86_64-img] | ![mac-x86_64][mac-x86_64-img] |
| arm | ![linux-armv5][linux-arm5-img]<br/> ![linux-armv6][linux-arm6-img]<br/> ![linux-armv7][linux-arm7-img] | | |
[linux-x86-img]: http://buildbot.nim-lang.org/buildstatusimage?builder=linux-x32-builder
[linux-x86_64-img]: http://buildbot.nim-lang.org/buildstatusimage?builder=linux-x64-builder
[linux-arm5-img]: http://buildbot.nim-lang.org/buildstatusimage?builder=linux-arm5-builder
[linux-arm6-img]: http://buildbot.nim-lang.org/buildstatusimage?builder=linux-arm6-builder
[linux-arm7-img]: http://buildbot.nim-lang.org/buildstatusimage?builder=linux-arm7-builder
[windows-x86-img]: http://buildbot.nim-lang.org/buildstatusimage?builder=windows-x32-builder
[windows-x86_64-img]: http://buildbot.nim-lang.org/buildstatusimage?builder=windows-x64-builder
[mac-x86-img]: http://buildbot.nim-lang.org/buildstatusimage?builder=mac-x32-builder
[mac-x86_64-img]: http://buildbot.nim-lang.org/buildstatusimage?builder=mac-x64-builder
[waterfall]: http://buildbot.nim-lang.org/waterfall

View file

@ -19,7 +19,6 @@ proc processClient(fd: int) {.async.} =
var foo = line[0] var foo = line[0]
if foo == 'g': if foo == 'g':
raise newException(EBase, "foobar") raise newException(EBase, "foobar")
proc serve() {.async.} = proc serve() {.async.} =

View file

@ -49,3 +49,44 @@ proc catch() {.async.} =
assert false assert false
asyncCheck catch() asyncCheck catch()
proc test(): Future[bool] {.async.} =
result = false
try:
raise newException(OSError, "Foobar")
except:
result = true
return
proc foo(): Future[bool] {.async.} = discard
proc test2(): Future[bool] {.async.} =
result = false
try:
discard await foo()
raise newException(OSError, "Foobar")
except:
result = true
return
proc test3(): Future[int] {.async.} =
result = 0
try:
try:
discard await foo()
raise newException(OSError, "Hello")
except:
result = 1
raise
except:
result = 2
return
var x = test()
assert x.read
x = test2()
assert x.read
var y = test3()
assert y.read == 2

View file

@ -0,0 +1,15 @@
# bug #2073
import sequtils
import times
# 1
proc f(n: int): TimeInfo =
TimeInfo(year: n, month: mJan, monthday: 1)
echo toSeq(2000 || 2015).map(f)
# 2
echo toSeq(2000 || 2015).map(proc (n: int): TimeInfo =
TimeInfo(year: n, month: mJan, monthday: 1)
)

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,
let "11": 34474,
elem1 = TElem(foo: 1, bar: "bar") "12": 789,
elem2 = TElem(foo: 2, bar: "baz") "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: 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")
var tbl2 = initTable[string, TElem]() t["012"] = 67.9
tbl2.add("bar", elem1) t["123"] = 1.5 # test overwriting
tbl2.add("baz", elem2)
doAssert indexBy(@[elem1,elem2], proc(x: TElem): string = x.bar) == tbl2, "element table" 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

@ -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

@ -38,12 +38,14 @@ proc newPlus(a, b: ref TExpr): ref TPlusExpr =
result.b = b result.b = b
result.op2 = $getOccupiedMem() result.op2 = $getOccupiedMem()
const Limit = when compileOption("gc", "markAndSweep"): 5*1024*1024 else: 500_000
for i in 0..100_000: for i in 0..100_000:
var s: array[0..11, ref TExpr] var s: array[0..11, ref TExpr]
for j in 0..high(s): for j in 0..high(s):
s[j] = newPlus(newPlus(newLit(j), newLit(2)), newLit(4)) s[j] = newPlus(newPlus(newLit(j), newLit(2)), newLit(4))
if eval(s[j]) != j+6: if eval(s[j]) != j+6:
quit "error: wrong result" quit "error: wrong result"
if getOccupiedMem() > 500_000: quit("still a leak!") if getOccupiedMem() > Limit: quit("still a leak!")
echo "no leak: ", getOccupiedMem() echo "no leak: ", getOccupiedMem()

29
tests/gc/growobjcrash.nim Normal file
View file

@ -0,0 +1,29 @@
discard """
output: "works"
"""
import cgi, strtabs
proc handleRequest(query: string): StringTableRef =
iterator foo(): StringTableRef {.closure.} =
var params = {:}.newStringTable()
for key, val in cgi.decodeData(query):
params[key] = val
yield params
let x = foo
result = x()
const Limit = when compileOption("gc", "markAndSweep"): 5*1024*1024 else: 700_000
proc main =
var counter = 0
for i in 0 .. 100_000:
for k, v in handleRequest("nick=Elina2&type=activate"):
inc counter
if counter mod 100 == 0:
if getOccupiedMem() > Limit:
quit "but now a leak"
main()
echo "works"

View file

@ -1,6 +1,5 @@
discard """ discard """
output: '''TMatrix[3, 3, system.int] output: '''TMatrix[3, 3, system.int]
3
3''' 3'''
""" """
@ -22,5 +21,5 @@ proc echoMat2(a: TMat2) =
var m = TMatrix[3,3,int](data: [1,2,3,4,5,6,7,8,9]) var m = TMatrix[3,3,int](data: [1,2,3,4,5,6,7,8,9])
echoMatrix m echoMatrix m
echoMat2 m #echoMat2 m

View file

@ -1,9 +1,10 @@
discard """ discard """
output: "2" output: '''2
88'''
""" """
type type
TValue* {.pure, final.} = object of TObject TValue* {.pure, final.} = object of RootObj
a: int a: int
PValue = ref TValue PValue = ref TValue
PPValue = ptr PValue PPValue = ptr PValue
@ -16,3 +17,19 @@ var sp: PPValue = addr x
sp.a = 2 sp.a = 2
if sp.a == 2: echo 2 # with sp[].a the error is gone if sp.a == 2: echo 2 # with sp[].a the error is gone
# Test the new auto-deref a little
{.experimental.}
proc p(x: var int; y: int) = x += y
block:
var x: ref int
new(x)
x.p(44)
var indirect = p
x.indirect(44)
echo x[]

View file

@ -1,3 +1,7 @@
discard """
errormsg: "expression 'generate(builder)' has no type (or is ambiguous)"
"""
# bug #898 # bug #898
proc measureTime(e: auto) = proc measureTime(e: auto) =

View file

@ -30,7 +30,7 @@ accept bar(vbar)
accept baz(vbar) accept baz(vbar)
accept baz(vbaz) accept baz(vbaz)
reject baz(vnotbaz) #reject baz(vnotbaz) # XXX this really shouldn't compile
reject bar(vfoo) reject bar(vfoo)
# https://github.com/Araq/Nim/issues/517 # https://github.com/Araq/Nim/issues/517

View file

@ -14,6 +14,7 @@ AST b
20Test 20Test
20 20
''' '''
disabled: true
""" """
import macros import macros

View file

@ -1,6 +1,7 @@
discard """ discard """
msg: "int\nstring\nTBar[int]" msg: "int\nstring\nTBar[int]"
output: "int\nstring\nTBar[int]\nint\nrange 0..2(int)\nstring" output: "int\nstring\nTBar[int]\nint\nrange 0..2(int)\nstring"
disabled: true
""" """
import typetraits import typetraits

View file

@ -1,6 +1,7 @@
discard """ discard """
errormsg: "type mismatch: got (string) but expected 'ptr'" errormsg: "type mismatch: got (string) but expected 'ptr'"
line: 20 line: 20
disabled: true
""" """
import typetraits import typetraits

View file

@ -0,0 +1,11 @@
discard """
errormsg: "'typedesc' metatype is not valid here; typed '=' instead of ':'?"
"""
var x = int
echo x

View file

@ -1,7 +1,7 @@
discard """ discard """
file: "tests/reject/trecincb.nim" file: "tests/reject/trecincb.nim"
line: 9 line: 9
errormsg: "recursive dependency: 'tests/module/trecincb.nim'" errormsg: "recursive dependency: 'trecincb.nim'"
""" """
# Test recursive includes # Test recursive includes

View file

@ -1,7 +1,7 @@
discard """ discard """
file: "trecincb.nim" file: "trecincb.nim"
line: 9 line: 9
errormsg: "recursive dependency: 'tests/module/trecincb.nim'" errormsg: "recursive dependency: 'trecincb.nim'"
""" """
# Test recursive includes # Test recursive includes

View file

@ -0,0 +1,27 @@
discard """
output: '''wohoo
baz'''
"""
# Test to ensure the popular 'ref T' syntax works everywhere
type
Foo = object
a, b: int
s: string
FooBar = object of RootObj
n, m: string
Baz = object of FooBar
proc invoke(a: ref Baz) =
echo "baz"
# check object construction:
let x = (ref Foo)(a: 0, b: 45, s: "wohoo")
echo x.s
var y: ref FooBar = (ref Baz)(n: "n", m: "m")
invoke((ref Baz)(y))

View file

@ -0,0 +1,27 @@
discard """
output: "SUCCESS"
"""
type
BasicNumber = object of RootObj
value: float32
RefChild* = ref object
curr*: TokenObject
Token* {.pure.} = enum
foo,
bar,
TokenObject = object
case kind*: Token
of Token.foo:
foo*: string
of Token.bar:
bar*: BasicNumber
var t = RefChild()
t.curr = TokenObject(kind: Token.bar, bar: BasicNumber(value: 12.34))
t.curr = TokenObject(kind: Token.foo, foo: "foo")
echo "SUCCESS"

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

@ -11,8 +11,8 @@ fpqeew
[11, 12, 13] [11, 12, 13]
[11, 12, 13] [11, 12, 13]
[11, 12, 13] [11, 12, 13]
{ "key1": 11, "key2": 12, "key3": 13} {"key1": 11, "key2": 12, "key3": 13}
[ 11, 12, 13] [11, 12, 13]
<Students> <Students>
<Student Name="Aprilfoo" /> <Student Name="Aprilfoo" />
<Student Name="bar" /> <Student Name="bar" />

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: 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"

View file

@ -12,3 +12,22 @@ template genNodeKind(kind, name: expr): stmt =
result.add(c) result.add(c)
genNodeKind(nnkNone, None) genNodeKind(nnkNone, None)
# Test that generics in templates still work (regression to fix #1915)
# bug #2004
type Something = object
proc testA(x: Something) = discard
template def(name: expr) {.immediate.} =
proc testB[T](reallyUniqueName: T) =
`test name`(reallyUniqueName)
def A
var x: Something
testB(x)

View file

@ -120,7 +120,8 @@ proc gcTests(r: var TResults, cat: Category, options: string) =
" --gc:markAndSweep", cat, actionRun) " --gc:markAndSweep", cat, actionRun)
testSpec r, makeTest("tests/gc" / filename, options & testSpec r, makeTest("tests/gc" / filename, options &
" -d:release --gc:markAndSweep", cat, actionRun) " -d:release --gc:markAndSweep", cat, actionRun)
test "growobjcrash"
test "gcbench" test "gcbench"
test "gcleak" test "gcleak"
test "gcleak2" test "gcleak2"

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

@ -5,7 +5,7 @@ discard """
# bug #1708 # bug #1708
let foo = { let foo = {
"1" : (bar: @["1"]), "1" : (bar: @["1"]),
"2" : (baz: @[]) "2" : (bar: @[])
} }
# bug #871 # bug #871

Some files were not shown because too many files have changed in this diff Show more