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

This commit is contained in:
Jonathan Edwards 2015-03-11 16:26:10 -04:00
commit 50ed39fd66
129 changed files with 3195 additions and 2204 deletions

View file

@ -196,6 +196,7 @@ type
nkTypeOfExpr, # type(1+2) nkTypeOfExpr, # type(1+2)
nkObjectTy, # object body nkObjectTy, # object body
nkTupleTy, # tuple body nkTupleTy, # tuple body
nkTupleClassTy, # tuple type class
nkTypeClassTy, # user-defined type class nkTypeClassTy, # user-defined type class
nkStaticTy, # ``static[T]`` nkStaticTy, # ``static[T]``
nkRecList, # list of object parts nkRecList, # list of object parts
@ -458,7 +459,7 @@ type
tfNotNil, # type cannot be 'nil' tfNotNil, # type cannot be 'nil'
tfNeedsInit, # type constains a "not nil" constraint somewhere or some tfNeedsInit, # type constains a "not nil" constraint somewhere or some
# other type so that it requires inititalization # other type so that it requires initalization
tfVarIsPtr, # 'var' type is translated like 'ptr' even in C++ mode tfVarIsPtr, # 'var' type is translated like 'ptr' even in C++ mode
tfHasMeta, # type contains "wildcard" sub-types such as generic params tfHasMeta, # type contains "wildcard" sub-types such as generic params
# or other type classes # or other type classes
@ -946,10 +947,7 @@ template `{}=`*(n: PNode, i: int, s: PNode): stmt =
n.sons[i -| n] = s n.sons[i -| n] = s
when defined(useNodeIds): when defined(useNodeIds):
const nodeIdToDebug* = -1 # 884953 # 612794 const nodeIdToDebug* = -1 # 299750 # 300761 #300863 # 300879
#612840 # 612905 # 614635 # 614637 # 614641
# 423408
#429107 # 430443 # 441048 # 441090 # 441153
var gNodeId: int var gNodeId: int
proc newNode*(kind: TNodeKind): PNode = proc newNode*(kind: TNodeKind): PNode =
@ -1320,7 +1318,7 @@ proc isGCedMem*(t: PType): bool {.inline.} =
t.kind == tyProc and t.callConv == ccClosure t.kind == tyProc and t.callConv == ccClosure
proc propagateToOwner*(owner, elem: PType) = proc propagateToOwner*(owner, elem: PType) =
const HaveTheirOwnEmpty = {tySequence, tySet} const HaveTheirOwnEmpty = {tySequence, tySet, tyPtr, tyRef, tyProc}
owner.flags = owner.flags + (elem.flags * {tfHasMeta}) owner.flags = owner.flags + (elem.flags * {tfHasMeta})
if tfNotNil in elem.flags: if tfNotNil in elem.flags:
if owner.kind in {tyGenericInst, tyGenericBody, tyGenericInvocation}: if owner.kind in {tyGenericInst, tyGenericBody, tyGenericInvocation}:

View file

@ -203,9 +203,9 @@ proc mustRehash(length, counter: int): bool =
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 spaces(x: int): PRope = proc rspaces(x: int): PRope =
# returns x spaces # returns x spaces
result = toRope(repeatChar(x)) result = toRope(spaces(x))
proc toYamlChar(c: char): string = proc toYamlChar(c: char): string =
case c case c
@ -253,7 +253,7 @@ proc typeToYamlAux(n: PType, marker: var IntSet,
indent, maxRecDepth: int): PRope indent, maxRecDepth: int): PRope
proc strTableToYaml(n: TStrTable, marker: var IntSet, indent: int, proc strTableToYaml(n: TStrTable, marker: var IntSet, indent: int,
maxRecDepth: int): PRope = maxRecDepth: int): PRope =
var istr = spaces(indent + 2) var istr = rspaces(indent + 2)
result = toRope("[") result = toRope("[")
var mycount = 0 var mycount = 0
for i in countup(0, high(n.data)): for i in countup(0, high(n.data)):
@ -262,20 +262,20 @@ proc strTableToYaml(n: TStrTable, marker: var IntSet, indent: int,
appf(result, "$N$1$2", appf(result, "$N$1$2",
[istr, symToYamlAux(n.data[i], marker, indent + 2, maxRecDepth - 1)]) [istr, symToYamlAux(n.data[i], marker, indent + 2, maxRecDepth - 1)])
inc(mycount) inc(mycount)
if mycount > 0: appf(result, "$N$1", [spaces(indent)]) if mycount > 0: appf(result, "$N$1", [rspaces(indent)])
app(result, "]") app(result, "]")
assert(mycount == n.counter) assert(mycount == n.counter)
proc ropeConstr(indent: int, c: openArray[PRope]): PRope = proc ropeConstr(indent: int, c: openArray[PRope]): PRope =
# array of (name, value) pairs # array of (name, value) pairs
var istr = spaces(indent + 2) var istr = rspaces(indent + 2)
result = toRope("{") result = toRope("{")
var i = 0 var i = 0
while i <= high(c): while i <= high(c):
if i > 0: app(result, ",") if i > 0: app(result, ",")
appf(result, "$N$1\"$2\": $3", [istr, c[i], c[i + 1]]) appf(result, "$N$1\"$2\": $3", [istr, c[i], c[i + 1]])
inc(i, 2) inc(i, 2)
appf(result, "$N$1}", [spaces(indent)]) appf(result, "$N$1}", [rspaces(indent)])
proc symToYamlAux(n: PSym, marker: var IntSet, indent: int, proc symToYamlAux(n: PSym, marker: var IntSet, indent: int,
maxRecDepth: int): PRope = maxRecDepth: int): PRope =
@ -310,9 +310,9 @@ proc typeToYamlAux(n: PType, marker: var IntSet, indent: int,
result = toRope("[") result = toRope("[")
for i in countup(0, sonsLen(n) - 1): for i in countup(0, sonsLen(n) - 1):
if i > 0: app(result, ",") if i > 0: app(result, ",")
appf(result, "$N$1$2", [spaces(indent + 4), typeToYamlAux(n.sons[i], appf(result, "$N$1$2", [rspaces(indent + 4), typeToYamlAux(n.sons[i],
marker, indent + 4, maxRecDepth - 1)]) marker, indent + 4, maxRecDepth - 1)])
appf(result, "$N$1]", [spaces(indent + 2)]) appf(result, "$N$1]", [rspaces(indent + 2)])
else: else:
result = toRope("null") result = toRope("null")
result = ropeConstr(indent, [toRope("kind"), result = ropeConstr(indent, [toRope("kind"),
@ -331,7 +331,7 @@ proc treeToYamlAux(n: PNode, marker: var IntSet, indent: int,
if n == nil: if n == nil:
result = toRope("null") result = toRope("null")
else: else:
var istr = spaces(indent + 2) var istr = rspaces(indent + 2)
result = ropef("{$N$1\"kind\": $2", [istr, makeYamlString($n.kind)]) result = ropef("{$N$1\"kind\": $2", [istr, makeYamlString($n.kind)])
if maxRecDepth != 0: if maxRecDepth != 0:
appf(result, ",$N$1\"info\": $2", [istr, lineInfoToStr(n.info)]) appf(result, ",$N$1\"info\": $2", [istr, lineInfoToStr(n.info)])
@ -359,12 +359,12 @@ proc treeToYamlAux(n: PNode, marker: var IntSet, indent: int,
appf(result, ",$N$1\"sons\": [", [istr]) appf(result, ",$N$1\"sons\": [", [istr])
for i in countup(0, sonsLen(n) - 1): for i in countup(0, sonsLen(n) - 1):
if i > 0: app(result, ",") if i > 0: app(result, ",")
appf(result, "$N$1$2", [spaces(indent + 4), treeToYamlAux(n.sons[i], appf(result, "$N$1$2", [rspaces(indent + 4), treeToYamlAux(n.sons[i],
marker, indent + 4, maxRecDepth - 1)]) marker, indent + 4, maxRecDepth - 1)])
appf(result, "$N$1]", [istr]) appf(result, "$N$1]", [istr])
appf(result, ",$N$1\"typ\": $2", appf(result, ",$N$1\"typ\": $2",
[istr, typeToYamlAux(n.typ, marker, indent + 2, maxRecDepth)]) [istr, typeToYamlAux(n.typ, marker, indent + 2, maxRecDepth)])
appf(result, "$N$1}", [spaces(indent)]) appf(result, "$N$1}", [rspaces(indent)])
proc treeToYaml(n: PNode, indent: int = 0, maxRecDepth: int = - 1): PRope = proc treeToYaml(n: PNode, indent: int = 0, maxRecDepth: int = - 1): PRope =
var marker = initIntSet() var marker = initIntSet()
@ -408,7 +408,7 @@ proc debugTree(n: PNode, indent: int, maxRecDepth: int;
if n == nil: if n == nil:
result = toRope("null") result = toRope("null")
else: else:
var istr = spaces(indent + 2) var istr = rspaces(indent + 2)
result = ropef("{$N$1\"kind\": $2", result = ropef("{$N$1\"kind\": $2",
[istr, makeYamlString($n.kind)]) [istr, makeYamlString($n.kind)])
if maxRecDepth != 0: if maxRecDepth != 0:
@ -440,11 +440,11 @@ proc debugTree(n: PNode, indent: int, maxRecDepth: int;
appf(result, ",$N$1\"sons\": [", [istr]) appf(result, ",$N$1\"sons\": [", [istr])
for i in countup(0, sonsLen(n) - 1): for i in countup(0, sonsLen(n) - 1):
if i > 0: app(result, ",") if i > 0: app(result, ",")
appf(result, "$N$1$2", [spaces(indent + 4), debugTree(n.sons[i], appf(result, "$N$1$2", [rspaces(indent + 4), debugTree(n.sons[i],
indent + 4, maxRecDepth - 1, renderType)]) indent + 4, maxRecDepth - 1, renderType)])
appf(result, "$N$1]", [istr]) appf(result, "$N$1]", [istr])
appf(result, ",$N$1\"info\": $2", [istr, lineInfoToStr(n.info)]) appf(result, ",$N$1\"info\": $2", [istr, lineInfoToStr(n.info)])
appf(result, "$N$1}", [spaces(indent)]) appf(result, "$N$1}", [rspaces(indent)])
proc debug(n: PSym) = proc debug(n: PSym) =
if n == nil: if n == nil:
@ -681,9 +681,8 @@ proc initIdentIter(ti: var TIdentIter, tab: TStrTable, s: PIdent): PSym =
else: result = nextIdentIter(ti, tab) else: result = nextIdentIter(ti, tab)
proc nextIdentIter(ti: var TIdentIter, tab: TStrTable): PSym = proc nextIdentIter(ti: var TIdentIter, tab: TStrTable): PSym =
var h, start: THash var h = ti.h and high(tab.data)
h = ti.h and high(tab.data) var start = h
start = h
result = tab.data[h] result = tab.data[h]
while result != nil: while result != nil:
if result.name.id == ti.name.id: break if result.name.id == ti.name.id: break

View file

@ -339,6 +339,7 @@ proc genPatternCall(p: BProc; ri: PNode; pat: string; typ: PType): PRope =
let typ = skipTypes(ri.sons[0].typ, abstractInst) let typ = skipTypes(ri.sons[0].typ, abstractInst)
if pat[i+1] == '+': result.app genArgNoParam(p, ri.sons[0]) if pat[i+1] == '+': result.app genArgNoParam(p, ri.sons[0])
result.app(~"(") result.app(~"(")
if 1 < ri.len:
result.app genOtherArg(p, ri, 1, typ) result.app genOtherArg(p, ri, 1, typ)
for k in j+1 .. < ri.len: for k in j+1 .. < ri.len:
result.app(~", ") result.app(~", ")

View file

@ -56,11 +56,6 @@ proc genLiteral(p: BProc, n: PNode, ty: PType): PRope =
case skipTypes(ty, abstractVarRange).kind case skipTypes(ty, abstractVarRange).kind
of tyChar, tyNil: of tyChar, tyNil:
result = intLiteral(n.intVal) result = intLiteral(n.intVal)
of tyInt:
if n.intVal >= low(int32) and n.intVal <= high(int32):
result = int32Literal(int32(n.intVal))
else:
result = intLiteral(n.intVal)
of tyBool: of tyBool:
if n.intVal != 0: result = ~"NIM_TRUE" if n.intVal != 0: result = ~"NIM_TRUE"
else: result = ~"NIM_FALSE" else: result = ~"NIM_FALSE"
@ -954,7 +949,7 @@ proc genEcho(p: BProc, n: PNode) =
initLocExpr(p, n.sons[i], a) initLocExpr(p, n.sons[i], a)
appf(args, ", $1? ($1)->data:\"nil\"", [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(repeat("%s", n.len) & tnl), args)
proc gcUsage(n: PNode) = proc gcUsage(n: PNode) =
if gSelectedGC == gcNone: message(n.info, warnGcMem, n.renderTree) if gSelectedGC == gcNone: message(n.info, warnGcMem, n.renderTree)

View file

@ -99,7 +99,7 @@ proc getUniqueType*(key: PType): PType =
gCanonicalTypes[k] = key gCanonicalTypes[k] = key
result = key result = key
of tyTypeDesc, tyTypeClasses, tyGenericParam, tyFromExpr, tyFieldAccessor: of tyTypeDesc, tyTypeClasses, tyGenericParam, tyFromExpr, tyFieldAccessor:
internalError("GetUniqueType") internalError("getUniqueType")
of tyDistinct: of tyDistinct:
if key.deepCopy != nil: result = key if key.deepCopy != nil: result = key
else: result = getUniqueType(lastSon(key)) else: result = getUniqueType(lastSon(key))
@ -135,7 +135,7 @@ proc getUniqueType*(key: PType): PType =
if idTableHasObjectAsKey(gTypeTable[k], key): return key if idTableHasObjectAsKey(gTypeTable[k], key): return key
for h in countup(0, high(gTypeTable[k].data)): for h in countup(0, high(gTypeTable[k].data)):
var t = PType(gTypeTable[k].data[h].key) var t = PType(gTypeTable[k].data[h].key)
if t != nil and sameType(t, key): if t != nil and sameBackendType(t, key):
return t return t
idTablePut(gTypeTable[k], key, key) idTablePut(gTypeTable[k], key, key)
result = key result = key

View file

@ -164,6 +164,7 @@ proc packObject(x: PNode, typ: PType, res: pointer) =
let field = getField(typ.n, i) let field = getField(typ.n, i)
pack(it, field.typ, res +! field.offset) pack(it, field.typ, res +! field.offset)
else: else:
# XXX: todo
globalError(x.info, "cannot pack unnamed tuple") globalError(x.info, "cannot pack unnamed tuple")
const maxPackDepth = 20 const maxPackDepth = 20

View file

@ -572,6 +572,9 @@ proc footprint(filename: string): TCrc32 =
getCompileCFileCmd(filename, true) getCompileCFileCmd(filename, true)
proc externalFileChanged(filename: string): bool = proc externalFileChanged(filename: string): bool =
if gCmd notin {cmdCompileToC, cmdCompileToCpp, cmdCompileToOC, cmdCompileToLLVM}:
return false
var crcFile = toGeneratedFile(filename.withPackageName, "crc") var crcFile = toGeneratedFile(filename.withPackageName, "crc")
var currentCrc = int(footprint(filename)) var currentCrc = int(footprint(filename))
var f: File var f: File

View file

@ -37,11 +37,11 @@ const
PatternChars = {'a'..'z', 'A'..'Z', '0'..'9', '\x80'..'\xFF', '.', '_'} PatternChars = {'a'..'z', 'A'..'Z', '0'..'9', '\x80'..'\xFF', '.', '_'}
proc newLine(p: var TTmplParser) = proc newLine(p: var TTmplParser) =
llStreamWrite(p.outp, repeatChar(p.emitPar, ')')) llStreamWrite(p.outp, repeat(')', p.emitPar))
p.emitPar = 0 p.emitPar = 0
if p.info.line > int16(1): llStreamWrite(p.outp, "\n") if p.info.line > int16(1): llStreamWrite(p.outp, "\n")
if p.pendingExprLine: if p.pendingExprLine:
llStreamWrite(p.outp, repeatChar(2)) llStreamWrite(p.outp, spaces(2))
p.pendingExprLine = false p.pendingExprLine = false
proc scanPar(p: var TTmplParser, d: int) = proc scanPar(p: var TTmplParser, d: int) =
@ -88,24 +88,24 @@ proc parseLine(p: var TTmplParser) =
else: else:
p.info.col = int16(j) p.info.col = int16(j)
localError(p.info, errXNotAllowedHere, "end") localError(p.info, errXNotAllowedHere, "end")
llStreamWrite(p.outp, repeatChar(p.indent)) llStreamWrite(p.outp, spaces(p.indent))
llStreamWrite(p.outp, "#end") llStreamWrite(p.outp, "#end")
of wIf, wWhen, wTry, wWhile, wFor, wBlock, wCase, wProc, wIterator, of wIf, wWhen, wTry, wWhile, wFor, wBlock, wCase, wProc, wIterator,
wConverter, wMacro, wTemplate, wMethod: wConverter, wMacro, wTemplate, wMethod:
llStreamWrite(p.outp, repeatChar(p.indent)) llStreamWrite(p.outp, spaces(p.indent))
llStreamWrite(p.outp, substr(p.x, d)) llStreamWrite(p.outp, substr(p.x, d))
inc(p.indent, 2) inc(p.indent, 2)
of wElif, wOf, wElse, wExcept, wFinally: of wElif, wOf, wElse, wExcept, wFinally:
llStreamWrite(p.outp, repeatChar(p.indent - 2)) llStreamWrite(p.outp, spaces(p.indent - 2))
llStreamWrite(p.outp, substr(p.x, d)) llStreamWrite(p.outp, substr(p.x, d))
of wLet, wVar, wConst, wType: of wLet, wVar, wConst, wType:
llStreamWrite(p.outp, repeatChar(p.indent)) llStreamWrite(p.outp, spaces(p.indent))
llStreamWrite(p.outp, substr(p.x, d)) llStreamWrite(p.outp, substr(p.x, d))
if not p.x.contains({':', '='}): if not p.x.contains({':', '='}):
# no inline element --> treat as block: # no inline element --> treat as block:
inc(p.indent, 2) inc(p.indent, 2)
else: else:
llStreamWrite(p.outp, repeatChar(p.indent)) llStreamWrite(p.outp, spaces(p.indent))
llStreamWrite(p.outp, substr(p.x, d)) llStreamWrite(p.outp, substr(p.x, d))
p.state = psDirective p.state = psDirective
else: else:
@ -120,11 +120,11 @@ proc parseLine(p: var TTmplParser) =
# next line of string literal: # next line of string literal:
llStreamWrite(p.outp, p.conc) llStreamWrite(p.outp, p.conc)
llStreamWrite(p.outp, "\n") llStreamWrite(p.outp, "\n")
llStreamWrite(p.outp, repeatChar(p.indent + 2)) llStreamWrite(p.outp, spaces(p.indent + 2))
llStreamWrite(p.outp, "\"") llStreamWrite(p.outp, "\"")
of psDirective: of psDirective:
newLine(p) newLine(p)
llStreamWrite(p.outp, repeatChar(p.indent)) llStreamWrite(p.outp, spaces(p.indent))
llStreamWrite(p.outp, p.emit) llStreamWrite(p.outp, p.emit)
llStreamWrite(p.outp, "(\"") llStreamWrite(p.outp, "(\"")
inc(p.emitPar) inc(p.emitPar)

View file

@ -27,7 +27,7 @@ proc getModuleName*(n: PNode): string =
result = n.ident.s result = n.ident.s
of nkSym: of nkSym:
result = n.sym.name.s result = n.sym.name.s
of nkInfix: of nkInfix, nkPrefix:
if n.sons[0].kind == nkIdent and n.sons[0].ident.id == getIdent("as").id: if n.sons[0].kind == nkIdent and n.sons[0].ident.id == getIdent("as").id:
# XXX hack ahead: # XXX hack ahead:
n.kind = nkImportAs n.kind = nkImportAs

View file

@ -780,7 +780,7 @@ proc genIf(p: PProc, n: PNode, r: var TCompRes) =
moveInto(p, stmt, r) moveInto(p, stmt, r)
appf(p.body, "}$n" | "end$n") appf(p.body, "}$n" | "end$n")
if p.target == targetJS: if p.target == targetJS:
app(p.body, repeatChar(toClose, '}') & tnl) app(p.body, repeat('}', toClose) & tnl)
else: else:
for i in 1..toClose: appf(p.body, "end$n") for i in 1..toClose: appf(p.body, "end$n")

View file

@ -35,7 +35,7 @@ proc llStreamOpen*(data: string): PLLStream =
result.s = data result.s = data
result.kind = llsString result.kind = llsString
proc llStreamOpen*(f: var File): PLLStream = proc llStreamOpen*(f: File): PLLStream =
new(result) new(result)
result.f = f result.f = f
result.kind = llsFile result.kind = llsFile

View file

@ -176,7 +176,7 @@ proc addInterfaceDeclAux(c: PContext, sym: PSym) =
if sfExported in sym.flags: if sfExported in sym.flags:
# add to interface: # add to interface:
if c.module != nil: strTableAdd(c.module.tab, sym) if c.module != nil: strTableAdd(c.module.tab, sym)
else: internalError(sym.info, "AddInterfaceDeclAux") else: internalError(sym.info, "addInterfaceDeclAux")
proc addInterfaceDeclAt*(c: PContext, scope: PScope, sym: PSym) = proc addInterfaceDeclAt*(c: PContext, scope: PScope, sym: PSym) =
addDeclAt(scope, sym) addDeclAt(scope, sym)

View file

@ -117,7 +117,8 @@ type
warnDifferentHeaps, warnWriteToForeignHeap, warnUnsafeCode, warnDifferentHeaps, warnWriteToForeignHeap, warnUnsafeCode,
warnEachIdentIsTuple, warnShadowIdent, warnEachIdentIsTuple, warnShadowIdent,
warnProveInit, warnProveField, warnProveIndex, warnGcUnsafe, warnGcUnsafe2, warnProveInit, warnProveField, warnProveIndex, warnGcUnsafe, warnGcUnsafe2,
warnUninit, warnGcMem, warnDestructor, warnLockLevel, warnUser, warnUninit, warnGcMem, warnDestructor, warnLockLevel, warnResultShadowed,
warnUser,
hintSuccess, hintSuccessX, hintSuccess, hintSuccessX,
hintLineTooLong, hintXDeclaredButNotUsed, hintConvToBaseNotNeeded, hintLineTooLong, hintXDeclaredButNotUsed, hintConvToBaseNotNeeded,
hintConvFromXtoItselfNotNeeded, hintExprAlwaysX, hintQuitCalled, hintConvFromXtoItselfNotNeeded, hintExprAlwaysX, hintQuitCalled,
@ -391,6 +392,7 @@ const
warnGcMem: "'$1' uses GC'ed memory [GcMem]", warnGcMem: "'$1' uses GC'ed memory [GcMem]",
warnDestructor: "usage of a type with a destructor in a non destructible context. This will become a compile time error in the future. [Destructor]", warnDestructor: "usage of a type with a destructor in a non destructible context. This will become a compile time error in the future. [Destructor]",
warnLockLevel: "$1 [LockLevel]", warnLockLevel: "$1 [LockLevel]",
warnResultShadowed: "Special variable 'result' is shadowed. [ResultShadowed]",
warnUser: "$1 [User]", warnUser: "$1 [User]",
hintSuccess: "operation successful [Success]", hintSuccess: "operation successful [Success]",
hintSuccessX: "operation successful ($# lines compiled; $# sec total; $#) [SuccessX]", hintSuccessX: "operation successful ($# lines compiled; $# sec total; $#) [SuccessX]",
@ -411,7 +413,7 @@ const
hintUser: "$1 [User]"] hintUser: "$1 [User]"]
const const
WarningsToStr*: array[0..29, string] = ["CannotOpenFile", "OctalEscape", WarningsToStr*: array[0..30, string] = ["CannotOpenFile", "OctalEscape",
"XIsNeverRead", "XmightNotBeenInit", "XIsNeverRead", "XmightNotBeenInit",
"Deprecated", "ConfigDeprecated", "Deprecated", "ConfigDeprecated",
"SmallLshouldNotBeUsed", "UnknownMagic", "SmallLshouldNotBeUsed", "UnknownMagic",
@ -421,7 +423,7 @@ const
"TypelessParam", "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", "ResultShadowed", "User"]
HintsToStr*: array[0..16, string] = ["Success", "SuccessX", "LineTooLong", HintsToStr*: array[0..16, string] = ["Success", "SuccessX", "LineTooLong",
"XDeclaredButNotUsed", "ConvToBaseNotNeeded", "ConvFromXtoItselfNotNeeded", "XDeclaredButNotUsed", "ConvToBaseNotNeeded", "ConvFromXtoItselfNotNeeded",
@ -784,7 +786,7 @@ proc rawMessage*(msg: TMsgKind, arg: string) =
proc writeSurroundingSrc(info: TLineInfo) = proc writeSurroundingSrc(info: TLineInfo) =
const indent = " " const indent = " "
msgWriteln(indent & info.sourceLine.ropeToStr) msgWriteln(indent & info.sourceLine.ropeToStr)
msgWriteln(indent & repeatChar(info.col, ' ') & '^') msgWriteln(indent & spaces(info.col) & '^')
proc formatMsg*(info: TLineInfo, msg: TMsgKind, arg: string): string = proc formatMsg*(info: TLineInfo, msg: TMsgKind, arg: string): string =
let frmt = case msg let frmt = case msg

View file

@ -18,3 +18,4 @@ define:useStdoutAsStdmsg
cs:partial cs:partial
#define:useNodeIds #define:useNodeIds
symbol:nimfix symbol:nimfix
#gc:markAndSweep

View file

@ -166,4 +166,4 @@ proc getCurrentLine(L: TBaseLexer, marker: bool = true): string =
inc(i) inc(i)
result.add("\n") result.add("\n")
if marker: if marker:
result.add(repeatChar(getColNumber(L, L.bufpos)) & '^' & "\n") result.add(spaces(getColNumber(L, L.bufpos)) & '^' & "\n")

View file

@ -865,6 +865,7 @@ proc parseTuple(p: var TParser, indentAllowed = false): PNode =
#| [' optInd (identColonEquals (comma/semicolon)?)* optPar ']' #| [' optInd (identColonEquals (comma/semicolon)?)* optPar ']'
#| extTupleDecl = 'tuple' #| extTupleDecl = 'tuple'
#| COMMENT? (IND{>} identColonEquals (IND{=} identColonEquals)*)? #| COMMENT? (IND{>} identColonEquals (IND{=} identColonEquals)*)?
#| tupleClass = 'tuple'
result = newNodeP(nkTupleTy, p) result = newNodeP(nkTupleTy, p)
getTok(p) getTok(p)
if p.tok.tokType == tkBracketLe: if p.tok.tokType == tkBracketLe:
@ -894,6 +895,8 @@ proc parseTuple(p: var TParser, indentAllowed = false): PNode =
parMessage(p, errIdentifierExpected, p.tok) parMessage(p, errIdentifierExpected, p.tok)
break break
if not sameInd(p): break if not sameInd(p): break
else:
result = newNodeP(nkTupleClassTy, p)
proc parseParamList(p: var TParser, retColon = true): PNode = proc parseParamList(p: var TParser, retColon = true): PNode =
#| paramList = '(' declColonEquals ^* (comma/semicolon) ')' #| paramList = '(' declColonEquals ^* (comma/semicolon) ')'

View file

@ -172,7 +172,7 @@ proc processModule(module: PSym, stream: PLLStream, rd: PRodReader) =
let filename = fileIdx.toFullPathConsiderDirty let filename = fileIdx.toFullPathConsiderDirty
if module.name.s == "-": if module.name.s == "-":
module.name.s = "stdinfile" module.name.s = "stdinfile"
s = llStreamOpenStdIn() s = llStreamOpen(stdin)
else: else:
s = llStreamOpen(filename, fmRead) s = llStreamOpen(filename, fmRead)
if s == nil: if s == nil:

View file

@ -92,7 +92,7 @@ proc addTok(g: var TSrcGen, kind: TTokType, s: string) =
proc addPendingNL(g: var TSrcGen) = proc addPendingNL(g: var TSrcGen) =
if g.pendingNL >= 0: if g.pendingNL >= 0:
addTok(g, tkSpaces, "\n" & repeatChar(g.pendingNL)) addTok(g, tkSpaces, "\n" & spaces(g.pendingNL))
g.lineLen = g.pendingNL g.lineLen = g.pendingNL
g.pendingNL = - 1 g.pendingNL = - 1
@ -190,7 +190,7 @@ proc putComment(g: var TSrcGen, s: string) =
if not isCode and (g.lineLen + (j - i) > MaxLineLen): if not isCode and (g.lineLen + (j - i) > MaxLineLen):
put(g, tkComment, com) put(g, tkComment, com)
optNL(g, ind) optNL(g, ind)
com = '#' & repeatChar(comIndent) com = '#' & spaces(comIndent)
while s[i] > ' ': while s[i] > ' ':
add(com, s[i]) add(com, s[i])
inc(i) inc(i)
@ -280,7 +280,7 @@ proc gcom(g: var TSrcGen, n: PNode) =
(g.lineLen < LineCommentColumn): (g.lineLen < LineCommentColumn):
var ml = maxLineLength(n.comment) var ml = maxLineLength(n.comment)
if ml + LineCommentColumn <= MaxLineLen: if ml + LineCommentColumn <= MaxLineLen:
put(g, tkSpaces, repeatChar(LineCommentColumn - g.lineLen)) put(g, tkSpaces, spaces(LineCommentColumn - g.lineLen))
putComment(g, n.comment) #assert(g.comStack[high(g.comStack)] = n); putComment(g, n.comment) #assert(g.comStack[high(g.comStack)] = n);
proc gcoms(g: var TSrcGen) = proc gcoms(g: var TSrcGen) =
@ -395,6 +395,7 @@ proc lsub(n: PNode): int =
of nkClosedSymChoice, nkOpenSymChoice: of nkClosedSymChoice, nkOpenSymChoice:
result = lsons(n) + len("()") + sonsLen(n) - 1 result = lsons(n) + len("()") + sonsLen(n) - 1
of nkTupleTy: result = lcomma(n) + len("tuple[]") of nkTupleTy: result = lcomma(n) + len("tuple[]")
of nkTupleClassTy: result = len("tuple")
of nkDotExpr: result = lsons(n) + 1 of nkDotExpr: result = lsons(n) + 1
of nkBind: result = lsons(n) + len("bind_") of nkBind: result = lsons(n) + len("bind_")
of nkBindStmt: result = lcomma(n) + len("bind_") of nkBindStmt: result = lcomma(n) + len("bind_")
@ -1292,10 +1293,11 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) =
gsub(g, n.sons[0]) gsub(g, n.sons[0])
of nkTupleTy: of nkTupleTy:
put(g, tkTuple, "tuple") put(g, tkTuple, "tuple")
if sonsLen(n) > 0:
put(g, tkBracketLe, "[") put(g, tkBracketLe, "[")
gcomma(g, n) gcomma(g, n)
put(g, tkBracketRi, "]") put(g, tkBracketRi, "]")
of nkTupleClassTy:
put(g, tkTuple, "tuple")
of nkMetaNode_Obsolete: of nkMetaNode_Obsolete:
put(g, tkParLe, "(META|") put(g, tkParLe, "(META|")
gsub(g, n.sons[0]) gsub(g, n.sons[0])

View file

@ -41,20 +41,28 @@ proc pickBestCandidate(c: PContext, headSymbol: PNode,
best, alt: var TCandidate, best, alt: var TCandidate,
errors: var CandidateErrors) = errors: var CandidateErrors) =
var o: TOverloadIter var o: TOverloadIter
var sym = initOverloadIter(o, c, headSymbol) # thanks to the lazy semchecking for operands, we need to iterate over the
var symScope = o.lastOverloadScope # symbol table *before* any call to 'initCandidate' which might invoke
# semExpr which might modify the symbol table in cases like
# 'init(a, 1, (var b = new(Type2); b))'.
var symx = initOverloadIter(o, c, headSymbol)
let symScope = o.lastOverloadScope
var syms: seq[tuple[a: PSym, b: int]] = @[]
while symx != nil:
if symx.kind in filter: syms.add((symx, o.lastOverloadScope))
symx = nextOverloadIter(o, c, headSymbol)
if syms.len == 0: return
var z: TCandidate var z: TCandidate
initCandidate(c, best, syms[0][0], initialBinding, symScope)
if sym == nil: return initCandidate(c, alt, syms[0][0], initialBinding, symScope)
initCandidate(c, best, sym, initialBinding, symScope)
initCandidate(c, alt, sym, initialBinding, symScope)
best.state = csNoMatch best.state = csNoMatch
while sym != nil: for i in 0 .. <syms.len:
if sym.kind in filter: let sym = syms[i][0]
determineType(c, sym) determineType(c, sym)
initCandidate(c, z, sym, initialBinding, o.lastOverloadScope) initCandidate(c, z, sym, initialBinding, syms[i][1])
z.calleeSym = sym z.calleeSym = sym
#if sym.name.s == "*" and (n.info ?? "temp5.nim") and n.info.line == 140: #if sym.name.s == "*" and (n.info ?? "temp5.nim") and n.info.line == 140:
@ -75,14 +83,13 @@ proc pickBestCandidate(c: PContext, headSymbol: PNode,
if cmp < 0: best = z # x is better than the best so far if cmp < 0: best = z # x is better than the best so far
elif cmp == 0: alt = z # x is as good as the best so far elif cmp == 0: alt = z # x is as good as the best so far
else: discard else: discard
#if sym.name.s == "*" and (n.info ?? "temp5.nim") and n.info.line == 140: #if sym.name.s == "cmp" and (n.info ?? "rstgen.nim") and n.info.line == 516:
# echo "Matches ", n.info, " ", typeToString(sym.typ) # echo "Matches ", n.info, " ", typeToString(sym.typ)
# debug sym # debug sym
# writeMatches(z) # writeMatches(z)
# for i in 1 .. <len(z.call): # for i in 1 .. <len(z.call):
# z.call[i].typ.debug # z.call[i].typ.debug
# quit 1 # quit 1
sym = nextOverloadIter(o, c, headSymbol)
proc notFoundError*(c: PContext, n: PNode, errors: CandidateErrors) = proc notFoundError*(c: PContext, n: PNode, errors: CandidateErrors) =
# Gives a detailed error message; this is separated from semOverloadedCall, # Gives a detailed error message; this is separated from semOverloadedCall,

View file

@ -1866,6 +1866,14 @@ proc semTuplePositionsConstr(c: PContext, n: PNode, flags: TExprFlags): PNode =
addSonSkipIntLit(typ, n.sons[i].typ) addSonSkipIntLit(typ, n.sons[i].typ)
result.typ = typ result.typ = typ
proc isTupleType(n: PNode): bool =
if n.len == 0:
return false # don't interpret () as type
for i in countup(0, n.len - 1):
if n[i].typ == nil or n[i].typ.kind != tyTypeDesc:
return false
return true
proc checkInitialized(n: PNode, ids: IntSet, info: TLineInfo) = proc checkInitialized(n: PNode, ids: IntSet, info: TLineInfo) =
case n.kind case n.kind
of nkRecList: of nkRecList:
@ -2055,7 +2063,7 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
of nkBind: of nkBind:
message(n.info, warnDeprecated, "bind") message(n.info, warnDeprecated, "bind")
result = semExpr(c, n.sons[0], flags) result = semExpr(c, n.sons[0], flags)
of nkTypeOfExpr, nkTupleTy, nkRefTy..nkEnumTy, nkStaticTy: of nkTypeOfExpr, nkTupleTy, nkTupleClassTy, nkRefTy..nkEnumTy, nkStaticTy:
var typ = semTypeNode(c, n, nil).skipTypes({tyTypeDesc, tyIter}) var typ = semTypeNode(c, n, nil).skipTypes({tyTypeDesc, tyIter})
result.typ = makeTypeDesc(c, typ) result.typ = makeTypeDesc(c, typ)
#result = symNodeFromType(c, typ, n.info) #result = symNodeFromType(c, typ, n.info)
@ -2129,7 +2137,14 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
of nkPar: of nkPar:
case checkPar(n) case checkPar(n)
of paNone: result = errorNode(c, n) of paNone: result = errorNode(c, n)
of paTuplePositions: result = semTuplePositionsConstr(c, n, flags) of paTuplePositions:
var tupexp = semTuplePositionsConstr(c, n, flags)
if isTupleType(tupexp):
# reinterpret as type
var typ = semTypeNode(c, n, nil).skipTypes({tyTypeDesc, tyIter})
result.typ = makeTypeDesc(c, typ)
else:
result = tupexp
of paTupleFields: result = semTupleFieldsConstr(c, n, flags) of paTupleFields: result = semTupleFieldsConstr(c, n, flags)
of paSingle: result = semExpr(c, n.sons[0], flags) of paSingle: result = semExpr(c, n.sons[0], flags)
of nkCurly: result = semSetConstr(c, n) of nkCurly: result = semSetConstr(c, n)

View file

@ -356,7 +356,7 @@ proc semGenericStmt(c: PContext, n: PNode,
of nkIdent: a = n.sons[i] of nkIdent: a = n.sons[i]
else: illFormedAst(n) else: illFormedAst(n)
addDecl(c, newSymS(skUnknown, getIdentNode(a.sons[i]), c)) addDecl(c, newSymS(skUnknown, getIdentNode(a.sons[i]), c))
of nkObjectTy, nkTupleTy: of nkObjectTy, nkTupleTy, nkTupleClassTy:
discard discard
of nkFormalParams: of nkFormalParams:
checkMinSonsLen(n, 1) checkMinSonsLen(n, 1)

View file

@ -138,7 +138,7 @@ proc guardDotAccess(a: PEffects; n: PNode) =
if g.kind == skUnknown: if g.kind == skUnknown:
var field: PSym = nil var field: PSym = nil
var ty = n.sons[0].typ.skipTypes(abstractPtrs) var ty = n.sons[0].typ.skipTypes(abstractPtrs)
if ty.kind == tyTuple: if ty.kind == tyTuple and not ty.n.isNil:
field = lookupInRecord(ty.n, g.name) field = lookupInRecord(ty.n, g.name)
else: else:
while ty != nil and ty.kind == tyObject: while ty != nil and ty.kind == tyObject:

View file

@ -400,6 +400,7 @@ proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode =
elif tup.kind == tyTuple and def.kind == nkPar and elif tup.kind == tyTuple and def.kind == nkPar and
a.kind == nkIdentDefs and a.len > 3: a.kind == nkIdentDefs and a.len > 3:
message(a.info, warnEachIdentIsTuple) message(a.info, warnEachIdentIsTuple)
for j in countup(0, length-3): for j in countup(0, length-3):
var v = semIdentDef(c, a.sons[j], symkind) var v = semIdentDef(c, a.sons[j], symkind)
if sfGenSym notin v.flags: addInterfaceDecl(c, v) if sfGenSym notin v.flags: addInterfaceDecl(c, v)
@ -409,6 +410,8 @@ proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode =
let shadowed = findShadowedVar(c, v) let shadowed = findShadowedVar(c, v)
if shadowed != nil: if shadowed != nil:
shadowed.flags.incl(sfShadowed) shadowed.flags.incl(sfShadowed)
if shadowed.kind == skResult:
message(a.info, warnResultShadowed)
# a shadowed variable is an error unless it appears on the right # a shadowed variable is an error unless it appears on the right
# side of the '=': # side of the '=':
if warnShadowIdent in gNotes and not identWithin(def, v.name): if warnShadowIdent in gNotes and not identWithin(def, v.name):
@ -692,7 +695,7 @@ proc typeSectionFinalPass(c: PContext, n: PNode) =
assignType(s.typ, t) assignType(s.typ, t)
s.typ.id = t.id # same id s.typ.id = t.id # same id
checkConstructedType(s.info, s.typ) checkConstructedType(s.info, s.typ)
if s.typ.kind in {tyObject, tyTuple}: if s.typ.kind in {tyObject, tyTuple} and not s.typ.n.isNil:
checkForMetaFields(s.typ.n) checkForMetaFields(s.typ.n)
let aa = a.sons[2] let aa = a.sons[2]
if aa.kind in {nkRefTy, nkPtrTy} and aa.len == 1 and if aa.kind in {nkRefTy, nkPtrTy} and aa.len == 1 and
@ -786,7 +789,8 @@ proc semProcAnnotation(c: PContext, prc: PNode;
result = semStmt(c, x) result = semStmt(c, x)
# since a proc annotation can set pragmas, we process these here again. # since a proc annotation can set pragmas, we process these here again.
# This is required for SqueakNim-like export pragmas. # This is required for SqueakNim-like export pragmas.
if result[namePos].kind == nkSym and result[pragmasPos].kind != nkEmpty: if result.kind in procDefs and result[namePos].kind == nkSym and
result[pragmasPos].kind != nkEmpty:
pragma(c, result[namePos].sym, result[pragmasPos], validPragmas) pragma(c, result[namePos].sym, result[pragmasPos], validPragmas)
return return

View file

@ -345,8 +345,14 @@ proc semTypeIdent(c: PContext, n: PNode): PSym =
localError(n.info, errIdentifierExpected) localError(n.info, errIdentifierExpected)
result = errorSym(c, n) result = errorSym(c, n)
proc semAnonTuple(c: PContext, n: PNode, prev: PType): PType =
if sonsLen(n) == 0:
localError(n.info, errTypeExpected)
result = newOrPrevType(tyTuple, prev, c)
for i in countup(0, sonsLen(n) - 1):
addSonSkipIntLit(result, semTypeNode(c, n.sons[i], nil))
proc semTuple(c: PContext, n: PNode, prev: PType): PType = proc semTuple(c: PContext, n: PNode, prev: PType): PType =
if n.sonsLen == 0: return newConstraint(c, tyTuple)
var typ: PType var typ: PType
result = newOrPrevType(tyTuple, prev, c) result = newOrPrevType(tyTuple, prev, c)
result.n = newNodeI(nkRecList, n.info) result.n = newNodeI(nkRecList, n.info)
@ -1117,9 +1123,7 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType =
of nkPar: of nkPar:
if sonsLen(n) == 1: result = semTypeNode(c, n.sons[0], prev) if sonsLen(n) == 1: result = semTypeNode(c, n.sons[0], prev)
else: else:
# XXX support anon tuple here result = semAnonTuple(c, n, prev)
localError(n.info, errTypeExpected)
result = newOrPrevType(tyError, prev, c)
of nkCallKinds: of nkCallKinds:
if isRange(n): if isRange(n):
result = semRangeAux(c, n, prev) result = semRangeAux(c, n, prev)
@ -1227,6 +1231,7 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType =
result = newOrPrevType(tyError, prev, c) result = newOrPrevType(tyError, prev, c)
of nkObjectTy: result = semObjectNode(c, n, prev) of nkObjectTy: result = semObjectNode(c, n, prev)
of nkTupleTy: result = semTuple(c, n, prev) of nkTupleTy: result = semTuple(c, n, prev)
of nkTupleClassTy: result = newConstraint(c, tyTuple)
of nkTypeClassTy: result = semTypeClass(c, n, prev) of nkTypeClassTy: result = semTypeClass(c, n, prev)
of nkRefTy: result = semAnyRef(c, n, tyRef, prev) of nkRefTy: result = semAnyRef(c, n, tyRef, prev)
of nkPtrTy: result = semAnyRef(c, n, tyPtr, prev) of nkPtrTy: result = semAnyRef(c, n, tyPtr, prev)

View file

@ -341,6 +341,8 @@ proc skipIntLiteralParams*(t: PType) =
proc propagateFieldFlags(t: PType, n: PNode) = proc propagateFieldFlags(t: PType, n: PNode) =
# This is meant for objects and tuples # This is meant for objects and tuples
# The type must be fully instantiated! # The type must be fully instantiated!
if n.isNil:
return
internalAssert n.kind != nkRecWhen internalAssert n.kind != nkRecWhen
case n.kind case n.kind
of nkSym: of nkSym:

View file

@ -157,13 +157,36 @@ proc sumGeneric(t: PType): int =
result = ord(t.kind == tyGenericInvocation) result = ord(t.kind == tyGenericInvocation)
for i in 0 .. <t.len: result += t.sons[i].sumGeneric for i in 0 .. <t.len: result += t.sons[i].sumGeneric
break break
of tyProc:
# proc matches proc better than 'stmt' to disambiguate 'spawn'
return 1
of tyGenericParam, tyExpr, tyStatic, tyStmt, tyTypeDesc: break of tyGenericParam, tyExpr, tyStatic, tyStmt, tyTypeDesc: break
of tyBool, tyChar, tyEnum, tyObject, tyProc, tyPointer,
tyString, tyCString, tyInt..tyInt64, tyFloat..tyFloat128,
tyUInt..tyUInt64:
return 1
else: return 0 else: return 0
#var ggDebug: bool
proc complexDisambiguation(a, b: PType): int = proc complexDisambiguation(a, b: PType): int =
# 'a' matches better if *every* argument matches better or equal than 'b'.
var winner = 0
for i in 1 .. <min(a.len, b.len):
let x = a.sons[i].sumGeneric
let y = b.sons[i].sumGeneric
#if ggDebug:
# echo "came her ", typeToString(a.sons[i]), " ", typeToString(b.sons[i])
if x != y:
if winner == 0:
if x > y: winner = 1
else: winner = -1
elif x > y:
if winner != 1:
# contradiction
return 0
else:
if winner != -1:
return 0
result = winner
when false:
var x, y: int var x, y: int
for i in 1 .. <a.len: x += a.sons[i].sumGeneric for i in 1 .. <a.len: x += a.sons[i].sumGeneric
for i in 1 .. <b.len: y += b.sons[i].sumGeneric for i in 1 .. <b.len: y += b.sons[i].sumGeneric
@ -338,7 +361,8 @@ proc minRel(a, b: TTypeRelation): TTypeRelation =
proc recordRel(c: var TCandidate, f, a: PType): TTypeRelation = proc recordRel(c: var TCandidate, f, a: PType): TTypeRelation =
result = isNone result = isNone
if sameType(f, a): result = isEqual if sameType(f, a):
result = isEqual
elif sonsLen(a) == sonsLen(f): elif sonsLen(a) == sonsLen(f):
result = isEqual result = isEqual
let firstField = if f.kind == tyTuple: 0 let firstField = if f.kind == tyTuple: 0
@ -380,17 +404,17 @@ proc procParamTypeRel(c: var TCandidate, f, a: PType): TTypeRelation =
# no luck resolving the type, so the inference fails # no luck resolving the type, so the inference fails
return isNone return isNone
let reverseRel = typeRel(c, a, f) let reverseRel = typeRel(c, a, f)
if reverseRel == isGeneric: if reverseRel >= isGeneric:
result = isInferred result = isInferred
inc c.genericMatches #inc c.genericMatches
else: else:
result = typeRel(c, f, a) result = typeRel(c, f, a)
if result <= isSubtype or inconsistentVarTypes(f, a): if result <= isSubtype or inconsistentVarTypes(f, a):
result = isNone result = isNone
if result == isEqual: #if result == isEqual:
inc c.exactMatches # inc c.exactMatches
proc procTypeRel(c: var TCandidate, f, a: PType): TTypeRelation = proc procTypeRel(c: var TCandidate, f, a: PType): TTypeRelation =
case a.kind case a.kind
@ -433,6 +457,7 @@ proc procTypeRel(c: var TCandidate, f, a: PType): TTypeRelation =
return isNone return isNone
when useEffectSystem: when useEffectSystem:
if not compatibleEffects(f, a): return isNone if not compatibleEffects(f, a): return isNone
of tyNil: of tyNil:
result = f.allowsNil result = f.allowsNil
of tyIter: of tyIter:
@ -590,7 +615,6 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
return typeRel(c, f, lastSon(a)) return typeRel(c, f, lastSon(a))
template bindingRet(res) = template bindingRet(res) =
when res == isGeneric:
if doBind: if doBind:
let bound = aOrig.skipTypes({tyRange}).skipIntLit let bound = aOrig.skipTypes({tyRange}).skipIntLit
if doBind: put(c.bindings, f, bound) if doBind: put(c.bindings, f, bound)
@ -605,20 +629,21 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
of tyOr: of tyOr:
# seq[int|string] vs seq[number] # seq[int|string] vs seq[number]
# both int and string must match against number # both int and string must match against number
# but ensure that '[T: A|A]' matches as good as '[T: A]' (bug #2219):
result = isGeneric
for branch in a.sons: for branch in a.sons:
if typeRel(c, f, branch, false) == isNone: let x = typeRel(c, f, branch, false)
return isNone if x == isNone: return isNone
if x < result: result = x
return isGeneric
of tyAnd: of tyAnd:
# seq[Sortable and Iterable] vs seq[Sortable] # seq[Sortable and Iterable] vs seq[Sortable]
# only one match is enough # only one match is enough
for branch in a.sons: for branch in a.sons:
if typeRel(c, f, branch, false) != isNone: let x = typeRel(c, f, branch, false)
return isGeneric if x != isNone:
return if x >= isGeneric: isGeneric else: x
return isNone result = isNone
of tyNot: of tyNot:
case f.kind case f.kind
@ -781,11 +806,11 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
inc(c.inheritancePenalty, depth) inc(c.inheritancePenalty, depth)
result = isSubtype result = isSubtype
of tyDistinct: of tyDistinct:
if (a.kind == tyDistinct) and sameDistinctTypes(f, a): result = isEqual if a.kind == tyDistinct and sameDistinctTypes(f, a): result = isEqual
elif c.coerceDistincts: result = typeRel(c, f.base, a) elif c.coerceDistincts: result = typeRel(c, f.base, a)
of tySet: of tySet:
if a.kind == tySet: if a.kind == tySet:
if (f.sons[0].kind != tyGenericParam) and (a.sons[0].kind == tyEmpty): if f.sons[0].kind != tyGenericParam and a.sons[0].kind == tyEmpty:
result = isSubtype result = isSubtype
else: else:
result = typeRel(c, f.sons[0], a.sons[0]) result = typeRel(c, f.sons[0], a.sons[0])
@ -865,7 +890,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
result = typeRel(c, ff, aa) result = typeRel(c, ff, aa)
if result == isNone: return if result == isNone: return
if ff.kind == tyRange and result != isEqual: return isNone if ff.kind == tyRange and result != isEqual: return isNone
result = isGeneric #result = isGeneric
# XXX See bug #2220. A[int] should match A[int] better than some generic X # XXX See bug #2220. A[int] should match A[int] better than some generic X
else: else:
result = typeRel(c, lastSon(f), a) result = typeRel(c, lastSon(f), a)
@ -904,18 +929,23 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
of tyAnd: of tyAnd:
considerPreviousT: considerPreviousT:
for branch in f.sons: for branch in f.sons:
if typeRel(c, branch, aOrig) < isSubtype: let x = typeRel(c, branch, aOrig)
return isNone if x < isSubtype: return isNone
# 'and' implies minimum matching result:
bindingRet isGeneric if x < result: result = x
bindingRet result
of tyOr: of tyOr:
considerPreviousT: considerPreviousT:
result = isNone
for branch in f.sons: for branch in f.sons:
if typeRel(c, branch, aOrig) >= isSubtype: let x = typeRel(c, branch, aOrig)
bindingRet isGeneric # 'or' implies maximum matching result:
if x > result: result = x
return isNone if result >= isSubtype:
bindingRet result
else:
result = isNone
of tyNot: of tyNot:
considerPreviousT: considerPreviousT:
@ -975,6 +1005,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
internalAssert a.sons != nil and a.sons.len > 0 internalAssert a.sons != nil and a.sons.len > 0
c.typedescMatched = true c.typedescMatched = true
result = typeRel(c, f.base, a.skipTypes({tyGenericParam, tyTypeDesc})) result = typeRel(c, f.base, a.skipTypes({tyGenericParam, tyTypeDesc}))
if result > isGeneric: result = isGeneric
else: else:
result = isNone result = isNone
else: else:
@ -998,12 +1029,15 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
return isNone return isNone
if doBind: if doBind:
put(c.bindings, f, concrete) put(c.bindings, f, concrete)
elif result > isGeneric:
result = isGeneric
elif a.kind == tyEmpty: elif a.kind == tyEmpty:
result = isGeneric result = isGeneric
elif x.kind == tyGenericParam: elif x.kind == tyGenericParam:
result = isGeneric result = isGeneric
else: else:
result = typeRel(c, x, a) # check if it fits result = typeRel(c, x, a) # check if it fits
if result > isGeneric: result = isGeneric
of tyStatic: of tyStatic:
let prev = PType(idTableGet(c.bindings, f)) let prev = PType(idTableGet(c.bindings, f))
@ -1220,8 +1254,9 @@ proc paramTypesMatchAux(m: var TCandidate, f, argType: PType,
of isConvertible, isIntConv: inc(m.convMatches) of isConvertible, isIntConv: inc(m.convMatches)
of isSubtype, isSubrange: inc(m.subtypeMatches) of isSubtype, isSubrange: inc(m.subtypeMatches)
of isGeneric, isInferred: inc(m.genericMatches) of isGeneric, isInferred: inc(m.genericMatches)
of isInferredConvertible: inc(m.genericMatches); inc(m.convMatches)
of isFromIntLit: inc(m.intConvMatches, 256) of isFromIntLit: inc(m.intConvMatches, 256)
of isInferredConvertible:
inc(m.convMatches)
of isEqual: inc(m.exactMatches) of isEqual: inc(m.exactMatches)
of isNone: discard of isNone: discard
@ -1232,7 +1267,7 @@ proc paramTypesMatchAux(m: var TCandidate, f, argType: PType,
elif f.kind == tyStatic: elif f.kind == tyStatic:
return arg.typ.n return arg.typ.n
else: else:
return argOrig return argSemantized # argOrig
if r != isNone and f.isInlineIterator: if r != isNone and f.isInlineIterator:
var inlined = newTypeS(tyStatic, c) var inlined = newTypeS(tyStatic, c)
@ -1244,21 +1279,22 @@ proc paramTypesMatchAux(m: var TCandidate, f, argType: PType,
case r case r
of isConvertible: of isConvertible:
inc(m.convMatches) inc(m.convMatches)
result = implicitConv(nkHiddenStdConv, f, copyTree(arg), m, c) result = implicitConv(nkHiddenStdConv, f, arg, m, c)
of isIntConv: of isIntConv:
# I'm too lazy to introduce another ``*matches`` field, so we conflate # I'm too lazy to introduce another ``*matches`` field, so we conflate
# ``isIntConv`` and ``isIntLit`` here: # ``isIntConv`` and ``isIntLit`` here:
inc(m.intConvMatches) inc(m.intConvMatches)
result = implicitConv(nkHiddenStdConv, f, copyTree(arg), m, c) result = implicitConv(nkHiddenStdConv, f, arg, m, c)
of isSubtype: of isSubtype:
inc(m.subtypeMatches) inc(m.subtypeMatches)
result = implicitConv(nkHiddenSubConv, f, copyTree(arg), m, c) result = implicitConv(nkHiddenSubConv, f, arg, m, c)
of isSubrange: of isSubrange:
inc(m.subtypeMatches) inc(m.subtypeMatches)
#result = copyTree(arg) if f.kind == tyVar:
result = implicitConv(nkHiddenStdConv, f, copyTree(arg), m, c) result = arg
else:
result = implicitConv(nkHiddenStdConv, f, arg, m, c)
of isInferred, isInferredConvertible: of isInferred, isInferredConvertible:
inc(m.genericMatches)
if arg.kind in {nkProcDef, nkIteratorDef} + nkLambdaKinds: if arg.kind in {nkProcDef, nkIteratorDef} + nkLambdaKinds:
result = c.semInferredLambda(c, m.bindings, arg) result = c.semInferredLambda(c, m.bindings, arg)
else: else:
@ -1267,41 +1303,35 @@ proc paramTypesMatchAux(m: var TCandidate, f, argType: PType,
if r == isInferredConvertible: if r == isInferredConvertible:
inc(m.convMatches) inc(m.convMatches)
result = implicitConv(nkHiddenStdConv, f, result, m, c) result = implicitConv(nkHiddenStdConv, f, result, m, c)
else:
inc(m.genericMatches)
of isGeneric: of isGeneric:
inc(m.genericMatches) inc(m.genericMatches)
when true: if arg.typ == nil:
if skipTypes(arg.typ, abstractVar-{tyTypeDesc}).kind == tyTuple: result = arg
elif skipTypes(arg.typ, abstractVar-{tyTypeDesc}).kind == tyTuple:
result = implicitConv(nkHiddenStdConv, f, copyTree(arg), m, c) result = implicitConv(nkHiddenStdConv, f, copyTree(arg), m, c)
elif arg.typ != nil and arg.typ.isEmptyContainer: elif arg.typ.isEmptyContainer:
result = arg.copyTree result = arg.copyTree
result.typ = getInstantiatedType(c, arg, m, f) result.typ = getInstantiatedType(c, arg, m, f)
else: else:
result = arg result = arg
else:
# XXX Why is this ever necessary? arg's type should not be retrofitted
# to match formal's type in this way!
result = copyTree(arg)
result.typ = getInstantiatedType(c, arg, m, f)
# BUG: f may not be the right key!
if skipTypes(result.typ, abstractVar-{tyTypeDesc}).kind in {tyTuple}:
result = implicitConv(nkHiddenStdConv, f, copyTree(arg), m, c)
# BUGFIX: use ``result.typ`` and not `f` here
of isFromIntLit: of isFromIntLit:
# too lazy to introduce another ``*matches`` field, so we conflate # too lazy to introduce another ``*matches`` field, so we conflate
# ``isIntConv`` and ``isIntLit`` here: # ``isIntConv`` and ``isIntLit`` here:
inc(m.intConvMatches, 256) inc(m.intConvMatches, 256)
result = implicitConv(nkHiddenStdConv, f, copyTree(arg), m, c) result = implicitConv(nkHiddenStdConv, f, arg, m, c)
of isEqual: of isEqual:
inc(m.exactMatches) inc(m.exactMatches)
result = copyTree(arg) result = arg
if skipTypes(f, abstractVar-{tyTypeDesc}).kind in {tyTuple}: if skipTypes(f, abstractVar-{tyTypeDesc}).kind in {tyTuple}:
result = implicitConv(nkHiddenStdConv, f, copyTree(arg), m, c) result = implicitConv(nkHiddenStdConv, f, arg, m, c)
of isNone: of isNone:
# do not do this in ``typeRel`` as it then can't infere T in ``ref T``: # do not do this in ``typeRel`` as it then can't infere T in ``ref T``:
if a.kind in {tyProxy, tyUnknown}: if a.kind in {tyProxy, tyUnknown}:
inc(m.genericMatches) inc(m.genericMatches)
m.fauxMatch = a.kind m.fauxMatch = a.kind
return copyTree(arg) return arg
result = userConvMatch(c, m, f, a, arg) result = userConvMatch(c, m, f, a, arg)
# check for a base type match, which supports varargs[T] without [] # check for a base type match, which supports varargs[T] without []
# constructor in a call: # constructor in a call:
@ -1340,7 +1370,16 @@ proc paramTypesMatch*(m: var TCandidate, f, a: PType,
for i in countup(0, sonsLen(arg) - 1): for i in countup(0, sonsLen(arg) - 1):
if arg.sons[i].sym.kind in {skProc, skMethod, skConverter}+skIterators: if arg.sons[i].sym.kind in {skProc, skMethod, skConverter}+skIterators:
copyCandidate(z, m) copyCandidate(z, m)
z.callee = arg.sons[i].typ
z.calleeSym = arg.sons[i].sym
#if arg.sons[i].sym.name.s == "cmp":
# ggDebug = true
# echo "CALLLEEEEEEEE ", typeToString(z.callee)
var r = typeRel(z, f, arg.sons[i].typ) var r = typeRel(z, f, arg.sons[i].typ)
#if arg.sons[i].sym.name.s == "cmp": # and arg.info.line == 606:
# echo "M ", r, " ", arg.info, " ", typeToString(arg.sons[i].sym.typ)
# debug arg.sons[i].sym
# writeMatches(z)
if r != isNone: if r != isNone:
case x.state case x.state
of csEmpty, csNoMatch: of csEmpty, csNoMatch:
@ -1653,7 +1692,7 @@ tests:
setup: setup:
var c: TCandidate var c: TCandidate
InitCandidate(nil, c, nil) initCandidate(nil, c, nil)
template yes(x, y) = template yes(x, y) =
test astToStr(x) & " is " & astToStr(y): test astToStr(x) & " is " & astToStr(y):

View file

@ -507,18 +507,22 @@ proc typeToString(typ: PType, prefer: TPreferedDesc = preferName): string =
if prefer == preferModuleInfo: preferModuleInfo else: preferName) if prefer == preferModuleInfo: preferModuleInfo else: preferName)
of tyTuple: of tyTuple:
# we iterate over t.sons here, because t.n may be nil # we iterate over t.sons here, because t.n may be nil
result = "tuple["
if t.n != nil: if t.n != nil:
result = "tuple["
assert(sonsLen(t.n) == sonsLen(t)) assert(sonsLen(t.n) == sonsLen(t))
for i in countup(0, sonsLen(t.n) - 1): for i in countup(0, sonsLen(t.n) - 1):
assert(t.n.sons[i].kind == nkSym) assert(t.n.sons[i].kind == nkSym)
add(result, t.n.sons[i].sym.name.s & ": " & typeToString(t.sons[i])) add(result, t.n.sons[i].sym.name.s & ": " & typeToString(t.sons[i]))
if i < sonsLen(t.n) - 1: add(result, ", ") if i < sonsLen(t.n) - 1: add(result, ", ")
add(result, ']')
elif sonsLen(t) == 0:
result = "tuple[]"
else: else:
result = "("
for i in countup(0, sonsLen(t) - 1): for i in countup(0, sonsLen(t) - 1):
add(result, typeToString(t.sons[i])) add(result, typeToString(t.sons[i]))
if i < sonsLen(t) - 1: add(result, ", ") if i < sonsLen(t) - 1: add(result, ", ")
add(result, ']') add(result, ')')
of tyPtr, tyRef, tyVar, tyMutable, tyConst: of tyPtr, tyRef, tyVar, tyMutable, tyConst:
result = typeToStr[t.kind] result = typeToStr[t.kind]
if t.len >= 2: if t.len >= 2:
@ -654,7 +658,7 @@ type
dcEqOrDistinctOf ## a equals b or a is distinct of b dcEqOrDistinctOf ## a equals b or a is distinct of b
TTypeCmpFlag* = enum TTypeCmpFlag* = enum
IgnoreTupleFields IgnoreTupleFields ## NOTE: Only set this flag for backends!
IgnoreCC IgnoreCC
ExactTypeDescValues ExactTypeDescValues
ExactGenericParams ExactGenericParams
@ -774,8 +778,8 @@ proc sameTuple(a, b: PType, c: var TSameTypeClosure): bool =
var x = a.sons[i] var x = a.sons[i]
var y = b.sons[i] var y = b.sons[i]
if IgnoreTupleFields in c.flags: if IgnoreTupleFields in c.flags:
x = skipTypes(x, {tyRange}) x = skipTypes(x, {tyRange, tyGenericInst})
y = skipTypes(y, {tyRange}) y = skipTypes(y, {tyRange, tyGenericInst})
result = sameTypeAux(x, y, c) result = sameTypeAux(x, y, c)
if not result: return if not result: return
@ -823,8 +827,13 @@ proc sameEnumTypes*(a, b: PType): bool {.inline.} =
proc sameObjectTree(a, b: PNode, c: var TSameTypeClosure): bool = proc sameObjectTree(a, b: PNode, c: var TSameTypeClosure): bool =
if a == b: if a == b:
result = true result = true
elif (a != nil) and (b != nil) and (a.kind == b.kind): elif a != nil and b != nil and a.kind == b.kind:
if sameTypeOrNilAux(a.typ, b.typ, c): var x = a.typ
var y = b.typ
if IgnoreTupleFields in c.flags:
if x != nil: x = skipTypes(x, {tyRange, tyGenericInst})
if y != nil: y = skipTypes(y, {tyRange, tyGenericInst})
if sameTypeOrNilAux(x, y, c):
case a.kind case a.kind
of nkSym: of nkSym:
# same symbol as string is enough: # same symbol as string is enough:

View file

@ -68,7 +68,6 @@ proc renderType(n: PNode): string =
assert n[i].kind == nkIdent assert n[i].kind == nkIdent
result.add(',' & typeStr) result.add(',' & typeStr)
of nkTupleTy: of nkTupleTy:
assert len(n) > 0
result = "tuple[" result = "tuple["
for i in 0 .. <len(n): result.add(renderType(n[i]) & ',') for i in 0 .. <len(n): result.add(renderType(n[i]) & ',')
result[<len(result)] = ']' result[<len(result)] = ']'

View file

@ -1435,6 +1435,7 @@ proc evalConstExprAux(module, prc: PSym, n: PNode, mode: TEvalMode): PNode =
newSeq(tos.slots, c.prc.maxSlots) newSeq(tos.slots, c.prc.maxSlots)
#for i in 0 .. <c.prc.maxSlots: tos.slots[i] = newNode(nkEmpty) #for i in 0 .. <c.prc.maxSlots: tos.slots[i] = newNode(nkEmpty)
result = rawExecute(c, start, tos).regToNode result = rawExecute(c, start, tos).regToNode
if result.info.line < 0: result.info = n.info
proc evalConstExpr*(module: PSym, e: PNode): PNode = proc evalConstExpr*(module: PSym, e: PNode): PNode =
result = evalConstExprAux(module, nil, e, emConst) result = evalConstExprAux(module, nil, e, emConst)
@ -1496,6 +1497,7 @@ proc evalMacroCall*(module: PSym, n, nOrig: PNode, sym: PSym): PNode =
# temporary storage: # temporary storage:
#for i in L .. <maxSlots: tos.slots[i] = newNode(nkEmpty) #for i in L .. <maxSlots: tos.slots[i] = newNode(nkEmpty)
result = rawExecute(c, start, tos).regToNode result = rawExecute(c, start, tos).regToNode
if result.info.line < 0: result.info = n.info
if cyclicTree(result): globalError(n.info, errCyclicTree) if cyclicTree(result): globalError(n.info, errCyclicTree)
dec(evalMacroCounter) dec(evalMacroCounter)
c.callsite = nil c.callsite = nil

View file

@ -151,7 +151,7 @@ clang.options.size = "-Os"
vcc.options.linker = "/DEBUG /Zi /Fd\"$projectName.pdb\" /F33554432" # set the stack size to 8 MB vcc.options.linker = "/DEBUG /Zi /Fd\"$projectName.pdb\" /F33554432" # set the stack size to 8 MB
vcc.options.debug = "/Zi /Fd\"$projectName.pdb\"" vcc.options.debug = "/Zi /Fd\"$projectName.pdb\""
vcc.options.always = "/nologo" vcc.options.always = "/nologo"
vcc.options.speed = "/Ox /arch:SSE2" vcc.options.speed = "/O2 /arch:SSE2"
vcc.options.size = "/O1" vcc.options.size = "/O1"
# Configuration for the Digital Mars C/C++ compiler: # Configuration for the Digital Mars C/C++ compiler:

View file

@ -175,6 +175,9 @@ Generic Operating System Services
This module implements the ability to monitor a directory/file for changes This module implements the ability to monitor a directory/file for changes
using Posix's inotify API. using Posix's inotify API.
* `asyncfile <asyncfile.html>`_
This module implements asynchronous file reading and writing using
``asyncdispatch``.
Math libraries Math libraries
-------------- --------------

View file

@ -351,7 +351,7 @@ dispatch.
.. code-block:: nim .. code-block:: nim
type type
Expression = object ## abstract base class for an expression Expression = object of RootObj ## abstract base class for an expression
Literal = object of Expression Literal = object of Expression
x: int x: int
PlusExpr = object of Expression PlusExpr = object of Expression
@ -387,7 +387,7 @@ dispatching:
.. code-block:: nim .. code-block:: nim
type type
Thing = object Thing = object of RootObj
Unit = object of Thing Unit = object of Thing
x: int x: int

View file

@ -347,7 +347,7 @@ proc temp(args: string) =
if args.len > 0: exec(finalDest & " " & args) if args.len > 0: exec(finalDest & " " & args)
proc showHelp() = proc showHelp() =
quit(HelpText % [VersionAsString & repeatChar(44-len(VersionAsString)), quit(HelpText % [VersionAsString & spaces(44-len(VersionAsString)),
CompileDate, CompileTime], QuitSuccess) CompileDate, CompileTime], QuitSuccess)
var op = initOptParser() var op = initOptParser()

View file

@ -60,7 +60,7 @@ type
nnkStmtListType, nnkBlockType, nnkStmtListType, nnkBlockType,
nnkWith, nnkWithout, nnkWith, nnkWithout,
nnkTypeOfExpr, nnkObjectTy, nnkTypeOfExpr, nnkObjectTy,
nnkTupleTy, nnkTypeClassTy, nnkStaticTy, nnkTupleTy, nnkTupleClassTy, nnkTypeClassTy, nnkStaticTy,
nnkRecList, nnkRecCase, nnkRecWhen, nnkRecList, nnkRecCase, nnkRecWhen,
nnkRefTy, nnkPtrTy, nnkVarTy, nnkRefTy, nnkPtrTy, nnkVarTy,
nnkConstTy, nnkMutableTy, nnkConstTy, nnkMutableTy,
@ -343,7 +343,7 @@ proc expectKind*(n: PNimrodNode, k: TNimrodNodeKind) {.compileTime.} =
## checks that `n` is of kind `k`. If this is not the case, ## checks that `n` is of kind `k`. If this is not the case,
## compilation aborts with an error message. This is useful for writing ## compilation aborts with an error message. This is useful for writing
## macros that check the AST that is passed to them. ## macros that check the AST that is passed to them.
if n.kind != k: error("macro expects a node of kind: " & $k) if n.kind != k: error("Expected a node of kind " & $k & ", got " & $n.kind)
proc expectMinLen*(n: PNimrodNode, min: int) {.compileTime.} = proc expectMinLen*(n: PNimrodNode, min: int) {.compileTime.} =
## checks that `n` has at least `min` children. If this is not the case, ## checks that `n` has at least `min` children. If this is not the case,
@ -581,10 +581,8 @@ const
CallNodes* = {nnkCall, nnkInfix, nnkPrefix, nnkPostfix, nnkCommand, CallNodes* = {nnkCall, nnkInfix, nnkPrefix, nnkPostfix, nnkCommand,
nnkCallStrLit, nnkHiddenCallConv} nnkCallStrLit, nnkHiddenCallConv}
from strutils import cmpIgnoreStyle, format
proc expectKind*(n: PNimrodNode; k: set[TNimrodNodeKind]) {.compileTime.} = proc expectKind*(n: PNimrodNode; k: set[TNimrodNodeKind]) {.compileTime.} =
assert n.kind in k, "Expected one of $1, got $2".format(k, n.kind) assert n.kind in k, "Expected one of " & $k & ", got " & $n.kind
proc newProc*(name = newEmptyNode(); params: openArray[PNimrodNode] = [newEmptyNode()]; proc newProc*(name = newEmptyNode(); params: openArray[PNimrodNode] = [newEmptyNode()];
body: PNimrodNode = newStmtList(), procType = nnkProcDef): PNimrodNode {.compileTime.} = body: PNimrodNode = newStmtList(), procType = nnkProcDef): PNimrodNode {.compileTime.} =
@ -654,7 +652,7 @@ proc `pragma=`*(someProc: PNimrodNode; val: PNimrodNode){.compileTime.}=
template badNodeKind(k; f): stmt{.immediate.} = template badNodeKind(k; f): stmt{.immediate.} =
assert false, "Invalid node kind $# for macros.`$2`".format(k, f) assert false, "Invalid node kind " & $k & " for macros.`" & $f & "`"
proc body*(someProc: PNimrodNode): PNimrodNode {.compileTime.} = proc body*(someProc: PNimrodNode): PNimrodNode {.compileTime.} =
case someProc.kind: case someProc.kind:
@ -776,6 +774,22 @@ proc copy*(node: PNimrodNode): PNimrodNode {.compileTime.} =
## An alias for copyNimTree(). ## An alias for copyNimTree().
return node.copyNimTree() return node.copyNimTree()
proc cmpIgnoreStyle(a, b: cstring): int {.noSideEffect.} =
proc toLower(c: char): char {.inline.} =
if c in {'A'..'Z'}: result = chr(ord(c) + (ord('a') - ord('A')))
else: result = c
var i = 0
var j = 0
while true:
while a[i] == '_': inc(i)
while b[j] == '_': inc(j) # BUGFIX: typo
var aa = toLower(a[i])
var bb = toLower(b[j])
result = ord(aa) - ord(bb)
if result != 0 or aa == '\0': break
inc(i)
inc(j)
proc eqIdent* (a, b: string): bool = cmpIgnoreStyle(a, b) == 0 proc eqIdent* (a, b: string): bool = cmpIgnoreStyle(a, b) == 0
## Check if two idents are identical. ## Check if two idents are identical.

View file

@ -66,9 +66,9 @@ type
ppointer = ptr pointer ppointer = ptr pointer
pbyteArray = ptr array[0.. 0xffff, int8] pbyteArray = ptr array[0.. 0xffff, int8]
TGenSeq = object TGenericSeq {.importc.} = object
len, space: int len, space: int
PGenSeq = ptr TGenSeq PGenSeq = ptr TGenericSeq
const const
GenericSeqSize = (2 * sizeof(int)) GenericSeqSize = (2 * sizeof(int))

View file

@ -33,27 +33,71 @@ when defined(Windows):
stdout.write(prompt) stdout.write(prompt)
result = readLine(stdin, line) result = readLine(stdin, line)
import winlean
const
VK_SHIFT* = 16
VK_CONTROL* = 17
VK_MENU* = 18
KEY_EVENT* = 1
type
KEY_EVENT_RECORD = object
bKeyDown: WinBool
wRepeatCount: uint16
wVirtualKeyCode: uint16
wVirtualScanCode: uint16
unicodeChar: uint16
dwControlKeyState: uint32
INPUT_RECORD = object
eventType*: int16
reserved*: int16
event*: KEY_EVENT_RECORD
safetyBuffer: array[0..5, DWORD]
proc readConsoleInputW*(hConsoleInput: THANDLE, lpBuffer: var INPUTRECORD,
nLength: uint32,
lpNumberOfEventsRead: var uint32): WINBOOL{.
stdcall, dynlib: "kernel32", importc: "ReadConsoleInputW".}
proc getch(): uint16 =
let hStdin = getStdHandle(STD_INPUT_HANDLE)
var
irInputRecord: INPUT_RECORD
dwEventsRead: uint32
while readConsoleInputW(hStdin, irInputRecord, 1, dwEventsRead) != 0:
if irInputRecord.eventType == KEY_EVENT and
irInputRecord.event.wVirtualKeyCode notin {VK_SHIFT, VK_MENU, VK_CONTROL}:
result = irInputRecord.event.unicodeChar
discard readConsoleInputW(hStdin, irInputRecord, 1, dwEventsRead)
return result
from unicode import toUTF8, Rune, runeLenAt
proc readPasswordFromStdin*(prompt: string, password: var TaintedString): proc readPasswordFromStdin*(prompt: string, password: var TaintedString):
bool {.tags: [ReadIOEffect, WriteIOEffect].} = bool {.tags: [ReadIOEffect, WriteIOEffect].} =
## Reads a `password` from stdin without printing it. `password` must not ## Reads a `password` from stdin without printing it. `password` must not
## be ``nil``! Returns ``false`` if the end of the file has been reached, ## be ``nil``! Returns ``false`` if the end of the file has been reached,
## ``true`` otherwise. ## ``true`` otherwise.
proc getch(): cint {.header: "<conio.h>", importc: "_getch".}
password.setLen(0) password.setLen(0)
var c: char
stdout.write(prompt) stdout.write(prompt)
while true: while true:
c = getch().char let c = getch()
case c case c.char
of '\r', chr(0xA): of '\r', chr(0xA):
break break
of '\b': of '\b':
password.setLen(password.len - 1) # ensure we delete the whole UTF-8 character:
var i = 0
var x = 1
while i < password.len:
x = runeLenAt(password, i)
inc i, x
password.setLen(password.len - x)
else: else:
password.add(c) password.add(toUTF8(c.Rune))
stdout.write "\n" stdout.write "\n"
# TODO: How to detect EOF on Windows?
else: else:
import readline, history, termios, unsigned import readline, history, termios, unsigned

View file

@ -66,7 +66,7 @@ proc rawCompile(pattern: string, flags: cint): PPcre =
offset: cint offset: cint
result = pcre.compile(pattern, flags, addr(msg), addr(offset), nil) result = pcre.compile(pattern, flags, addr(msg), addr(offset), nil)
if result == nil: if result == nil:
raiseInvalidRegex($msg & "\n" & pattern & "\n" & repeatChar(offset) & "^\n") raiseInvalidRegex($msg & "\n" & pattern & "\n" & spaces(offset) & "^\n")
proc finalizeRegEx(x: Regex) = proc finalizeRegEx(x: Regex) =
# XXX This is a hack, but PCRE does not export its "free" function properly. # XXX This is a hack, but PCRE does not export its "free" function properly.
@ -291,7 +291,7 @@ proc replace*(s: string, sub: Regex, by = ""): string =
## accessed in `by`. Examples: ## accessed in `by`. Examples:
## ##
## .. code-block:: nim ## .. code-block:: nim
## "var1=key; var2=key2".replace(re"(\w+)'='(\w+)") ## "var1=key; var2=key2".replace(re"(\w+)=(\w+)")
## ##
## Results in: ## Results in:
## ##
@ -313,7 +313,7 @@ proc replacef*(s: string, sub: Regex, by: string): string =
## with the notation ``$i`` and ``$#`` (see strutils.`%`). Examples: ## with the notation ``$i`` and ``$#`` (see strutils.`%`). Examples:
## ##
## .. code-block:: nim ## .. code-block:: nim
## "var1=key; var2=key2".replacef(re"(\w+)'='(\w+)", "$1<-$2$2") ## "var1=key; var2=key2".replacef(re"(\w+)=(\w+)", "$1<-$2$2")
## ##
## Results in: ## Results in:
## ##

View file

@ -103,7 +103,11 @@ __clang__
# define N_FASTCALL_PTR(rettype, name) rettype (__fastcall *name) # define N_FASTCALL_PTR(rettype, name) rettype (__fastcall *name)
# define N_SAFECALL_PTR(rettype, name) rettype (__safecall *name) # define N_SAFECALL_PTR(rettype, name) rettype (__safecall *name)
# ifdef __cplusplus
# define N_LIB_EXPORT extern "C" __declspec(dllexport)
# else
# define N_LIB_EXPORT extern __declspec(dllexport) # define N_LIB_EXPORT extern __declspec(dllexport)
# endif
# define N_LIB_IMPORT extern __declspec(dllimport) # define N_LIB_IMPORT extern __declspec(dllimport)
#else #else
# define N_CDECL(rettype, name) rettype name # define N_CDECL(rettype, name) rettype name
@ -118,7 +122,11 @@ __clang__
# define N_FASTCALL_PTR(rettype, name) rettype (*name) # define N_FASTCALL_PTR(rettype, name) rettype (*name)
# define N_SAFECALL_PTR(rettype, name) rettype (*name) # define N_SAFECALL_PTR(rettype, name) rettype (*name)
# ifdef __cplusplus
# define N_LIB_EXPORT extern "C"
# else
# define N_LIB_EXPORT extern # define N_LIB_EXPORT extern
# endif
# define N_LIB_IMPORT extern # define N_LIB_IMPORT extern
#endif #endif
@ -398,4 +406,6 @@ typedef int assert_numbits[sizeof(NI) == sizeof(void*) && NIM_INTBITS == sizeof(
# include <sys/types.h> # include <sys/types.h>
# include <types/vxWind.h> # include <types/vxWind.h>
# include <tool/gnu/toolMacros.h> # include <tool/gnu/toolMacros.h>
#elif defined(__FreeBSD__)
# include <sys/types.h>
#endif #endif

View file

@ -189,7 +189,7 @@ proc getIndent(L: var TLexer, tok: var TToken) =
tok.line = L.line tok.line = L.line
L.col = tok.ival L.col = tok.ival
tok.ival = max(tok.ival - L.baseIndent, 0) tok.ival = max(tok.ival - L.baseIndent, 0)
tok.symbol = "\n" & repeatChar(tok.ival) tok.symbol = "\n" & spaces(tok.ival)
proc rawGetTok(L: var TLexer, tok: var TToken) = proc rawGetTok(L: var TLexer, tok: var TToken) =
tok.symbol = "" tok.symbol = ""
@ -963,7 +963,7 @@ proc parseLiteralBlock(p: var TRstParser): PRstNode =
break break
else: else:
add(n.text, "\n") add(n.text, "\n")
add(n.text, repeatChar(p.tok[p.idx].ival - indent)) add(n.text, spaces(p.tok[p.idx].ival - indent))
inc(p.idx) inc(p.idx)
else: else:
add(n.text, p.tok[p.idx].symbol) add(n.text, p.tok[p.idx].symbol)

View file

@ -110,7 +110,7 @@ proc renderRstToRst(d: var TRenderContext, n: PRstNode, result: var string) =
const const
lvlToChar: array[0..8, char] = ['!', '=', '-', '~', '`', '<', '*', '|', '+'] lvlToChar: array[0..8, char] = ['!', '=', '-', '~', '`', '<', '*', '|', '+']
if n == nil: return if n == nil: return
var ind = repeatChar(d.indent) var ind = spaces(d.indent)
case n.kind case n.kind
of rnInner: of rnInner:
renderRstSons(d, n, result) renderRstSons(d, n, result)
@ -124,7 +124,7 @@ proc renderRstToRst(d: var TRenderContext, n: PRstNode, result: var string) =
result.add("\n") result.add("\n")
result.add(ind) result.add(ind)
result.add repeatChar(headlineLen, lvlToChar[n.level]) result.add repeat(lvlToChar[n.level], headlineLen)
of rnOverline: of rnOverline:
result.add("\n") result.add("\n")
result.add(ind) result.add(ind)
@ -132,7 +132,7 @@ proc renderRstToRst(d: var TRenderContext, n: PRstNode, result: var string) =
var headline = "" var headline = ""
renderRstSons(d, n, headline) renderRstSons(d, n, headline)
let lvl = repeatChar(headline.len - d.indent, lvlToChar[n.level]) let lvl = repeat(lvlToChar[n.level], headline.len - d.indent)
result.add(lvl) result.add(lvl)
result.add("\n") result.add("\n")
result.add(headline) result.add(headline)
@ -143,7 +143,7 @@ proc renderRstToRst(d: var TRenderContext, n: PRstNode, result: var string) =
of rnTransition: of rnTransition:
result.add("\n\n") result.add("\n\n")
result.add(ind) result.add(ind)
result.add repeatChar(78-d.indent, '-') result.add repeat('-', 78-d.indent)
result.add("\n\n") result.add("\n\n")
of rnParagraph: of rnParagraph:
result.add("\n\n") result.add("\n\n")
@ -196,7 +196,7 @@ proc renderRstToRst(d: var TRenderContext, n: PRstNode, result: var string) =
result.add ':' result.add ':'
result.add tmp result.add tmp
result.add ':' result.add ':'
result.add repeatChar(L - tmp.len - 2) result.add spaces(L - tmp.len - 2)
renderRstToRst(d, n.sons[1], result) renderRstToRst(d, n.sons[1], result)
dec(d.indent, L) dec(d.indent, L)

View file

@ -461,9 +461,9 @@ proc indentToLevel(level: var int, newLevel: int): string =
if level == newLevel: if level == newLevel:
return return
if newLevel > level: if newLevel > level:
result = repeatStr(newLevel - level, "<ul>") result = repeat("<ul>", newLevel - level)
else: else:
result = repeatStr(level - newLevel, "</ul>") result = repeat("</ul>", level - newLevel)
level = newLevel level = newLevel
proc generateDocumentationTOC(entries: seq[TIndexEntry]): string = proc generateDocumentationTOC(entries: seq[TIndexEntry]): string =
@ -701,7 +701,7 @@ proc renderHeadline(d: PDoc, n: PRstNode, result: var string) =
# Generate index entry using spaces to indicate TOC level for the output HTML. # Generate index entry using spaces to indicate TOC level for the output HTML.
assert n.level >= 0 assert n.level >= 0
setIndexTerm(d, refname, tmp.stripTOCHTML, setIndexTerm(d, refname, tmp.stripTOCHTML,
repeatChar(max(0, n.level), ' ') & tmp) spaces(max(0, n.level)) & tmp)
proc renderOverline(d: PDoc, n: PRstNode, result: var string) = proc renderOverline(d: PDoc, n: PRstNode, result: var string) =
if d.meta[metaTitle].len == 0: if d.meta[metaTitle].len == 0:

View file

@ -75,11 +75,12 @@ const
proc lowerBound*[T](a: openArray[T], key: T, cmp: proc(x,y: T): int {.closure.}): int = proc lowerBound*[T](a: openArray[T], key: T, cmp: proc(x,y: T): int {.closure.}): int =
## same as binarySearch except that if key is not in `a` then this ## same as binarySearch except that if key is not in `a` then this
## returns the location where `key` would be if it were. In other ## returns the location where `key` would be if it were. In other
## words if you have a sorted sequence and you call insert(thing, elm, lowerBound(thing, elm)) ## words if you have a sorted sequence and you call
## the sequence will still be sorted ## insert(thing, elm, lowerBound(thing, elm))
## the sequence will still be sorted.
## ##
## `cmp` is the comparator function to use, the expected return values are the same as ## `cmp` is the comparator function to use, the expected return values are
## that of system.cmp ## the same as that of system.cmp.
## ##
## example:: ## example::
## ##
@ -187,6 +188,35 @@ proc sort*[T](a: var openArray[T],
dec(m, s*2) dec(m, s*2)
s = s*2 s = s*2
proc sorted*[T](a: openArray[T], cmp: proc(x, y: T): int {.closure.},
order = SortOrder.Ascending): seq[T] =
## returns `a` sorted by `cmp` in the specified `order`.
result = newSeq[T](a.len)
for i in 0 .. a.high:
result[i] = a[i]
sort(result, cmp, order)
template sortByIt*(seq1, op: expr): expr =
## Convenience template around the ``sorted`` proc to reduce typing.
##
## The template injects the ``it`` variable which you can use directly in an
## expression. Example:
##
## .. code-block:: nim
##
## var users: seq[tuple[id: int, name: string]] =
## @[(0, "Smith"), (1, "Pratt"), (2, "Sparrow")]
##
## echo users.sortByIt(it.name)
##
var result {.gensym.} = sorted(seq1, proc(x, y: type(seq1[0])): int =
var it {.inject.} = x
let a = op
it = y
let b = op
result = cmp(a, b))
result
proc product*[T](x: openArray[seq[T]]): seq[seq[T]] = proc product*[T](x: openArray[seq[T]]): seq[seq[T]] =
## produces the Cartesian product of the array. Warning: complexity ## produces the Cartesian product of the array. Warning: complexity
## may explode. ## may explode.

View file

@ -1222,7 +1222,7 @@ 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) = template wrapInTry(n, tryBody: expr) =
var temp = n var temp = n
n[0] = tryBody n[0] = tryBody
tryBody = temp tryBody = temp
@ -1323,14 +1323,25 @@ macro async*(prc: stmt): stmt {.immediate.} =
newLit(prc[0].getName)))) # Get type from return type of this proc newLit(prc[0].getName)))) # Get type from return type of this proc
# -> iterator nameIter(): FutureBase {.closure.} = # -> iterator nameIter(): FutureBase {.closure.} =
# -> {.push warning[resultshadowed]: off.}
# -> var result: T # -> var result: T
# -> {.pop.}
# -> <proc_body> # -> <proc_body>
# -> complete(retFuture, result) # -> complete(retFuture, result)
var iteratorNameSym = genSym(nskIterator, $prc[0].getName & "Iter") var iteratorNameSym = genSym(nskIterator, $prc[0].getName & "Iter")
var procBody = prc[6].processBody(retFutureSym, subtypeIsVoid, nil) var procBody = prc[6].processBody(retFutureSym, subtypeIsVoid, nil)
if not subtypeIsVoid: if not subtypeIsVoid:
procBody.insert(0, newNimNode(nnkVarSection, prc[6]).add( procBody.insert(0, newNimNode(nnkPragma).add(newIdentNode("push"),
newNimNode(nnkExprColonExpr).add(newNimNode(nnkBracketExpr).add(
newIdentNode("warning"), newIdentNode("resultshadowed")),
newIdentNode("off")))) # -> {.push warning[resultshadowed]: off.}
procBody.insert(1, newNimNode(nnkVarSection, prc[6]).add(
newIdentDefs(newIdentNode("result"), returnType[1]))) # -> var result: T newIdentDefs(newIdentNode("result"), returnType[1]))) # -> var result: T
procBody.insert(2, newNimNode(nnkPragma).add(
newIdentNode("pop"))) # -> {.pop.})
procBody.add( procBody.add(
newCall(newIdentNode("complete"), newCall(newIdentNode("complete"),
retFutureSym, newIdentNode("result"))) # -> complete(retFuture, result) retFutureSym, newIdentNode("result"))) # -> complete(retFuture, result)

View file

@ -7,7 +7,7 @@
# distribution, for details about the copyright. # distribution, for details about the copyright.
# #
## This module implements asynchronous file handling. ## This module implements asynchronous file reading and writing.
## ##
## .. code-block:: Nim ## .. code-block:: Nim
## import asyncfile, asyncdispatch, os ## import asyncfile, asyncdispatch, os
@ -24,17 +24,17 @@
import asyncdispatch, os import asyncdispatch, os
when defined(windows): when defined(windows) or defined(nimdoc):
import winlean import winlean
else: else:
import posix import posix
type type
AsyncFile = ref object AsyncFile* = ref object
fd: TAsyncFd fd: TAsyncFd
offset: int64 offset: int64
when defined(windows): when defined(windows) or defined(nimdoc):
proc getDesiredAccess(mode: FileMode): int32 = proc getDesiredAccess(mode: FileMode): int32 =
case mode case mode
of fmRead: of fmRead:
@ -70,7 +70,7 @@ else:
proc getFileSize(f: AsyncFile): int64 = proc getFileSize(f: AsyncFile): int64 =
## Retrieves the specified file's size. ## Retrieves the specified file's size.
when defined(windows): when defined(windows) or defined(nimdoc):
var high: DWord var high: DWord
let low = getFileSize(f.fd.THandle, addr high) let low = getFileSize(f.fd.THandle, addr high)
if low == INVALID_FILE_SIZE: if low == INVALID_FILE_SIZE:
@ -81,7 +81,7 @@ proc openAsync*(filename: string, mode = fmRead): AsyncFile =
## Opens a file specified by the path in ``filename`` using ## Opens a file specified by the path in ``filename`` using
## the specified ``mode`` asynchronously. ## the specified ``mode`` asynchronously.
new result new result
when defined(windows): when defined(windows) or defined(nimdoc):
let flags = FILE_FLAG_OVERLAPPED or FILE_ATTRIBUTE_NORMAL let flags = FILE_FLAG_OVERLAPPED or FILE_ATTRIBUTE_NORMAL
let desiredAccess = getDesiredAccess(mode) let desiredAccess = getDesiredAccess(mode)
let creationDisposition = getCreationDisposition(mode, filename) let creationDisposition = getCreationDisposition(mode, filename)
@ -120,7 +120,7 @@ proc read*(f: AsyncFile, size: int): Future[string] =
## returned. ## returned.
var retFuture = newFuture[string]("asyncfile.read") var retFuture = newFuture[string]("asyncfile.read")
when defined(windows): when defined(windows) or defined(nimdoc):
var buffer = alloc0(size) var buffer = alloc0(size)
var ol = PCustomOverlapped() var ol = PCustomOverlapped()
@ -224,7 +224,7 @@ proc setFilePos*(f: AsyncFile, pos: int64) =
## Sets the position of the file pointer that is used for read/write ## Sets the position of the file pointer that is used for read/write
## operations. The file's first byte has the index zero. ## operations. The file's first byte has the index zero.
f.offset = pos f.offset = pos
when not defined(windows): when not defined(windows) and not defined(nimdoc):
let ret = lseek(f.fd.cint, pos, SEEK_SET) let ret = lseek(f.fd.cint, pos, SEEK_SET)
if ret == -1: if ret == -1:
raiseOSError(osLastError()) raiseOSError(osLastError())
@ -245,7 +245,7 @@ proc write*(f: AsyncFile, data: string): Future[void] =
## specified file. ## specified file.
var retFuture = newFuture[void]("asyncfile.write") var retFuture = newFuture[void]("asyncfile.write")
var copy = data var copy = data
when defined(windows): when defined(windows) or defined(nimdoc):
var buffer = alloc0(data.len) var buffer = alloc0(data.len)
copyMem(buffer, addr copy[0], data.len) copyMem(buffer, addr copy[0], data.len)
@ -316,7 +316,7 @@ proc write*(f: AsyncFile, data: string): Future[void] =
proc close*(f: AsyncFile) = proc close*(f: AsyncFile) =
## Closes the file specified. ## Closes the file specified.
when defined(windows): when defined(windows) or defined(nimdoc):
if not closeHandle(f.fd.THandle).bool: if not closeHandle(f.fd.THandle).bool:
raiseOSError(osLastError()) raiseOSError(osLastError())
else: else:

View file

@ -47,6 +47,24 @@ proc concat*[T](seqs: varargs[seq[T]]): seq[T] =
result[i] = itm result[i] = itm
inc(i) inc(i)
proc repeat*[T](s: seq[T], n: Natural): seq[T] =
## Returns a new sequence with the items of `s` repeated `n` times.
##
## Example:
##
## .. code-block:
##
## let
## s = @[1, 2, 3]
## total = s.repeat(3)
## assert total == @[1, 2, 3, 1, 2, 3, 1, 2, 3]
result = newSeq[T](n * s.len)
var o = 0
for x in 1..n:
for e in s:
result[o] = e
inc o
proc deduplicate*[T](seq1: seq[T]): seq[T] = proc deduplicate*[T](seq1: seq[T]): seq[T] =
## Returns a new sequence without duplicates. ## Returns a new sequence without duplicates.
## ##
@ -86,7 +104,7 @@ proc zip*[S, T](seq1: seq[S], seq2: seq[T]): seq[tuple[a: S, b: T]] =
newSeq(result, m) newSeq(result, m)
for i in 0 .. m-1: result[i] = (seq1[i], seq2[i]) for i in 0 .. m-1: result[i] = (seq1[i], seq2[i])
proc distribute*[T](s: seq[T], num: int, spread = true): seq[seq[T]] = proc distribute*[T](s: seq[T], num: Positive, spread = true): seq[seq[T]] =
## Splits and distributes a sequence `s` into `num` sub sequences. ## Splits and distributes a sequence `s` into `num` sub sequences.
## ##
## Returns a sequence of `num` sequences. For some input values this is the ## Returns a sequence of `num` sequences. For some input values this is the
@ -113,11 +131,12 @@ proc distribute*[T](s: seq[T], num: int, spread = true): seq[seq[T]] =
## assert numbers.distribute(6)[0] == @[1, 2] ## assert numbers.distribute(6)[0] == @[1, 2]
## assert numbers.distribute(6)[5] == @[7] ## assert numbers.distribute(6)[5] == @[7]
assert(not s.isNil, "`s` can't be nil") assert(not s.isNil, "`s` can't be nil")
assert(num > 0, "`num` has to be greater than zero")
if num < 2: if num < 2:
result = @[s] result = @[s]
return return
let num = int(num) # XXX probably only needed because of .. bug
# Create the result and calculate the stride size and the remainder if any. # Create the result and calculate the stride size and the remainder if any.
result = newSeq[seq[T]](num) result = newSeq[seq[T]](num)
var var
@ -587,4 +606,14 @@ when isMainModule:
seq2D[0][1] = true seq2D[0][1] = true
doAssert seq2D == @[@[true, true], @[true, false], @[false, false], @[false, false]] doAssert seq2D == @[@[true, true], @[true, false], @[false, false], @[false, false]]
block: # repeat tests
let
a = @[1, 2, 3]
b: seq[int] = @[]
doAssert a.repeat(3) == @[1, 2, 3, 1, 2, 3, 1, 2, 3]
doAssert a.repeat(0) == @[]
#doAssert a.repeat(-1) == @[] # will not compile!
doAssert b.repeat(3) == @[]
echo "Finished doc tests" echo "Finished doc tests"

View file

@ -196,7 +196,11 @@ proc mget*[A, B](t: var Table[A, B], key: A): var B =
var hc: THash var hc: THash
var index = rawGet(t, key, hc) var index = rawGet(t, key, hc)
if index >= 0: result = t.data[index].val if index >= 0: result = t.data[index].val
else: raise newException(KeyError, "key not found: " & $key) else:
when compiles($key):
raise newException(KeyError, "key not found: " & $key)
else:
raise newException(KeyError, "key not found")
iterator allValues*[A, B](t: Table[A, B]; key: A): B = iterator allValues*[A, B](t: Table[A, B]; key: A): B =
## iterates over any value in the table `t` that belongs to the given `key`. ## iterates over any value in the table `t` that belongs to the given `key`.

View file

@ -365,9 +365,9 @@ proc polar*(z: Complex): tuple[r, phi: float] =
result.phi = phase(z) result.phi = phase(z)
proc rect*(r: float, phi: float): Complex = proc rect*(r: float, phi: float): Complex =
## Returns the complex number with poolar coordinates `r` and `phi`. ## Returns the complex number with polar coordinates `r` and `phi`.
result.re = r * cos(phi) result.re = r * cos(phi)
result.im = sin(phi) result.im = r * sin(phi)
proc `$`*(z: Complex): string = proc `$`*(z: Complex): string =
@ -438,5 +438,6 @@ when isMainModule:
assert( arccoth(a) =~ arctanh(1/a) ) assert( arccoth(a) =~ arctanh(1/a) )
assert( phase(a) == 1.1071487177940904 ) assert( phase(a) == 1.1071487177940904 )
assert( polar(a) =~ (2.23606797749979, 1.1071487177940904) ) var t = polar(a)
assert( rect(t.r, t.phi) =~ a )
assert( rect(1.0, 2.0) =~ (-0.4161468365471424, 0.9092974268256817) ) assert( rect(1.0, 2.0) =~ (-0.4161468365471424, 0.9092974268256817) )

View file

@ -33,8 +33,8 @@
## proc hash(x: Something): THash = ## proc hash(x: Something): THash =
## ## Computes a THash from `x`. ## ## Computes a THash from `x`.
## var h: THash = 0 ## var h: THash = 0
## h = h &! hash(x.foo) ## h = h !& hash(x.foo)
## h = h &! hash(x.bar) ## h = h !& hash(x.bar)
## result = !$h ## result = !$h
import import

View file

@ -390,8 +390,11 @@ proc request*(url: string, httpMethod: string, extraHeaders = "",
## server takes longer than specified an ETimeout exception will be raised. ## server takes longer than specified an ETimeout exception will be raised.
var r = if proxy == nil: parseUri(url) else: proxy.url var r = if proxy == nil: parseUri(url) else: proxy.url
var headers = substr(httpMethod, len("http")) var headers = substr(httpMethod, len("http"))
# TODO: Use generateHeaders further down once it supports proxies.
if proxy == nil: if proxy == nil:
headers.add(" " & r.path) headers.add ' '
if r.path[0] != '/': headers.add '/'
headers.add(r.path)
if r.query.len > 0: if r.query.len > 0:
headers.add("?" & r.query) headers.add("?" & r.query)
else: else:
@ -567,9 +570,12 @@ proc downloadFile*(url: string, outputFilename: string,
proc generateHeaders(r: Uri, httpMethod: string, proc generateHeaders(r: Uri, httpMethod: string,
headers: StringTableRef): string = headers: StringTableRef): string =
# TODO: Use this in the blocking HttpClient once it supports proxies.
result = substr(httpMethod, len("http")) result = substr(httpMethod, len("http"))
# TODO: Proxies # TODO: Proxies
result.add(" " & r.path) result.add ' '
if r.path[0] != '/': result.add '/'
result.add(r.path)
if r.query.len > 0: if r.query.len > 0:
result.add("?" & r.query) result.add("?" & r.query)
result.add(" HTTP/1.1\c\L") result.add(" HTTP/1.1\c\L")

View file

@ -816,7 +816,7 @@ proc copy*(p: JsonNode): JsonNode =
# ------------- pretty printing ---------------------------------------------- # ------------- pretty printing ----------------------------------------------
proc indent(s: var string, i: int) = proc indent(s: var string, i: int) =
s.add(repeatChar(i)) s.add(spaces(i))
proc newIndent(curr, indent: int, ml: bool): int = proc newIndent(curr, indent: int, ml: bool): int =
if ml: return curr + indent if ml: return curr + indent

View file

@ -165,5 +165,5 @@ proc getCurrentLine(L: BaseLexer, marker: bool = true): string =
inc(i) inc(i)
add(result, "\n") add(result, "\n")
if marker: if marker:
add(result, repeatChar(getColNumber(L, L.bufpos)) & "^\n") add(result, spaces(getColNumber(L, L.bufpos)) & "^\n")

View file

@ -81,6 +81,23 @@ type
TReadLineResult: ReadLineResult, TSOBool: SOBool, PSocket: Socket, TReadLineResult: ReadLineResult, TSOBool: SOBool, PSocket: Socket,
TSocketImpl: SocketImpl].} TSocketImpl: SocketImpl].}
type
IpAddressFamily* {.pure.} = enum ## Describes the type of an IP address
IPv6, ## IPv6 address
IPv4 ## IPv4 address
TIpAddress* = object ## stores an arbitrary IP address
case family*: IpAddressFamily ## the type of the IP address (IPv4 or IPv6)
of IpAddressFamily.IPv6:
address_v6*: array[0..15, uint8] ## Contains the IP address in bytes in
## case of IPv6
of IpAddressFamily.IPv4:
address_v4*: array[0..3, uint8] ## Contains the IP address in bytes in
## case of IPv4
proc isIpAddress*(address_str: string): bool {.tags: [].}
proc parseIpAddress*(address_str: string): TIpAddress
proc isDisconnectionError*(flags: set[SocketFlag], proc isDisconnectionError*(flags: set[SocketFlag],
lastError: OSErrorCode): bool = lastError: OSErrorCode): bool =
## Determines whether ``lastError`` is a disconnection error. Only does this ## Determines whether ``lastError`` is a disconnection error. Only does this
@ -511,6 +528,12 @@ proc connect*(socket: Socket, address: string, port = Port(0),
when defined(ssl): when defined(ssl):
if socket.isSSL: if socket.isSSL:
# RFC3546 for SNI specifies that IP addresses are not allowed.
if not isIpAddress(address):
# Discard result in case OpenSSL version doesn't support SNI, or we're
# not using TLSv1+
discard SSL_set_tlsext_host_name(socket.sslHandle, address)
let ret = SSLConnect(socket.sslHandle) let ret = SSLConnect(socket.sslHandle)
socketError(socket, ret) socketError(socket, ret)
@ -969,20 +992,6 @@ proc isSsl*(socket: Socket): bool =
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
type
IpAddressFamily* {.pure.} = enum ## Describes the type of an IP address
IPv6, ## IPv6 address
IPv4 ## IPv4 address
TIpAddress* = object ## stores an arbitrary IP address
case family*: IpAddressFamily ## the type of the IP address (IPv4 or IPv6)
of IpAddressFamily.IPv6:
address_v6*: array[0..15, uint8] ## Contains the IP address in bytes in
## case of IPv6
of IpAddressFamily.IPv4:
address_v4*: array[0..3, uint8] ## Contains the IP address in bytes in
## case of IPv4
proc IPv4_any*(): TIpAddress = proc IPv4_any*(): TIpAddress =
## Returns the IPv4 any address, which can be used to listen on all available ## Returns the IPv4 any address, which can be used to listen on all available
## network adapters ## network adapters
@ -1241,7 +1250,7 @@ proc parseIPv6Address(address_str: string): TIpAddress =
raise newException(ValueError, raise newException(ValueError,
"Invalid IP Address. The address consists of too many groups") "Invalid IP Address. The address consists of too many groups")
proc parseIpAddress*(address_str: string): TIpAddress = proc parseIpAddress(address_str: string): TIpAddress =
## Parses an IP address ## Parses an IP address
## Raises EInvalidValue on error ## Raises EInvalidValue on error
if address_str == nil: if address_str == nil:
@ -1250,3 +1259,13 @@ proc parseIpAddress*(address_str: string): TIpAddress =
return parseIPv6Address(address_str) return parseIPv6Address(address_str)
else: else:
return parseIPv4Address(address_str) return parseIPv4Address(address_str)
proc isIpAddress(address_str: string): bool =
## Checks if a string is an IP address
## Returns true if it is, false otherwise
try:
discard parseIpAddress(address_str)
except ValueError:
return false
return true

View file

@ -11,9 +11,12 @@
## It supports one convenience iterator over all command line options and some ## It supports one convenience iterator over all command line options and some
## lower-level features. ## lower-level features.
## ##
## **Deprecated since version 0.9.3:** Use the `parseopt2 <parseopt2.html>`_ ## Supported syntax:
## module instead as this version has issues with spaces in arguments. ##
{.deprecated.} ## 1. short options - ``-abcd``, where a, b, c, d are names
## 2. long option - ``--foo:bar``, ``--foo=bar`` or ``--foo``
## 3. argument - everything else
{.push debugger: off.} {.push debugger: off.}
include "system/inclrtl" include "system/inclrtl"

View file

@ -128,6 +128,7 @@ proc open*(my: var XmlParser, input: Stream, filename: string,
my.kind = xmlError my.kind = xmlError
my.a = "" my.a = ""
my.b = "" my.b = ""
my.c = nil
my.options = options my.options = options
proc close*(my: var XmlParser) {.inline.} = proc close*(my: var XmlParser) {.inline.} =
@ -138,43 +139,43 @@ proc kind*(my: XmlParser): XmlEventKind {.inline.} =
## returns the current event type for the XML parser ## returns the current event type for the XML parser
return my.kind return my.kind
proc charData*(my: XmlParser): string {.inline.} = template charData*(my: XmlParser): string =
## returns the character data for the events: ``xmlCharData``, ## returns the character data for the events: ``xmlCharData``,
## ``xmlWhitespace``, ``xmlComment``, ``xmlCData``, ``xmlSpecial`` ## ``xmlWhitespace``, ``xmlComment``, ``xmlCData``, ``xmlSpecial``
assert(my.kind in {xmlCharData, xmlWhitespace, xmlComment, xmlCData, assert(my.kind in {xmlCharData, xmlWhitespace, xmlComment, xmlCData,
xmlSpecial}) xmlSpecial})
return my.a my.a
proc elementName*(my: XmlParser): string {.inline.} = template elementName*(my: XmlParser): string =
## returns the element name for the events: ``xmlElementStart``, ## returns the element name for the events: ``xmlElementStart``,
## ``xmlElementEnd``, ``xmlElementOpen`` ## ``xmlElementEnd``, ``xmlElementOpen``
assert(my.kind in {xmlElementStart, xmlElementEnd, xmlElementOpen}) assert(my.kind in {xmlElementStart, xmlElementEnd, xmlElementOpen})
return my.a my.a
proc entityName*(my: XmlParser): string {.inline.} = template entityName*(my: XmlParser): string =
## returns the entity name for the event: ``xmlEntity`` ## returns the entity name for the event: ``xmlEntity``
assert(my.kind == xmlEntity) assert(my.kind == xmlEntity)
return my.a my.a
proc attrKey*(my: XmlParser): string {.inline.} = template attrKey*(my: XmlParser): string =
## returns the attribute key for the event ``xmlAttribute`` ## returns the attribute key for the event ``xmlAttribute``
assert(my.kind == xmlAttribute) assert(my.kind == xmlAttribute)
return my.a my.a
proc attrValue*(my: XmlParser): string {.inline.} = template attrValue*(my: XmlParser): string =
## returns the attribute value for the event ``xmlAttribute`` ## returns the attribute value for the event ``xmlAttribute``
assert(my.kind == xmlAttribute) assert(my.kind == xmlAttribute)
return my.b my.b
proc piName*(my: XmlParser): string {.inline.} = template piName*(my: XmlParser): string =
## returns the processing instruction name for the event ``xmlPI`` ## returns the processing instruction name for the event ``xmlPI``
assert(my.kind == xmlPI) assert(my.kind == xmlPI)
return my.a my.a
proc piRest*(my: XmlParser): string {.inline.} = template piRest*(my: XmlParser): string =
## returns the rest of the processing instruction for the event ``xmlPI`` ## returns the rest of the processing instruction for the event ``xmlPI``
assert(my.kind == xmlPI) assert(my.kind == xmlPI)
return my.b my.b
proc rawData*(my: XmlParser): string {.inline.} = proc rawData*(my: XmlParser): string {.inline.} =
## returns the underlying 'data' string by reference. ## returns the underlying 'data' string by reference.
@ -621,7 +622,7 @@ proc next*(my: var XmlParser) =
of stateEmptyElementTag: of stateEmptyElementTag:
my.state = stateNormal my.state = stateNormal
my.kind = xmlElementEnd my.kind = xmlElementEnd
if not isNil(my.c): if not my.c.isNil:
my.a = my.c my.a = my.c
of stateError: of stateError:
my.kind = xmlError my.kind = xmlError

View file

@ -902,7 +902,7 @@ proc replacef*(s: string, sub: Peg, by: string): string {.
## with the notation ``$i`` and ``$#`` (see strutils.`%`). Examples: ## with the notation ``$i`` and ``$#`` (see strutils.`%`). Examples:
## ##
## .. code-block:: nim ## .. code-block:: nim
## "var1=key; var2=key2".replace(peg"{\ident}'='{\ident}", "$1<-$2$2") ## "var1=key; var2=key2".replacef(peg"{\ident}'='{\ident}", "$1<-$2$2")
## ##
## Results in: ## Results in:
## ##

View file

@ -798,6 +798,22 @@ proc zunionstore*(r: Redis, destination: string, numkeys: string,
return r.readInteger() return r.readInteger()
# HyperLogLog
proc pfadd*(r: Redis, key: string, elements: varargs[string]): RedisInteger =
## Add variable number of elements into special 'HyperLogLog' set type
r.sendCommand("PFADD", key, elements)
return r.readInteger()
proc pfcount*(r: Redis, key: string): RedisInteger =
## Count approximate number of elements in 'HyperLogLog'
r.sendCommand("PFCOUNT", key)
return r.readInteger()
proc pfmerge*(r: Redis, destination: string, sources: varargs[string]) =
## Merge several source HyperLogLog's into one specified by destKey
r.sendCommand("PFMERGE", destination, sources)
raiseNoOK(r.readStatus(), r.pipeline.enabled)
# Pub/Sub # Pub/Sub

View file

@ -224,8 +224,10 @@ proc ssReadData(s: Stream, buffer: pointer, bufLen: int): int =
proc ssWriteData(s: Stream, buffer: pointer, bufLen: int) = proc ssWriteData(s: Stream, buffer: pointer, bufLen: int) =
var s = StringStream(s) var s = StringStream(s)
if bufLen > 0: if bufLen <= 0:
setLen(s.data, s.data.len + bufLen) return
if s.pos + bufLen > s.data.len:
setLen(s.data, s.pos + bufLen)
copyMem(addr(s.data[s.pos]), buffer, bufLen) copyMem(addr(s.data[s.pos]), buffer, bufLen)
inc(s.pos, bufLen) inc(s.pos, bufLen)

View file

@ -499,26 +499,47 @@ proc parseEnum*[T: enum](s: string, default: T): T =
return e return e
result = default result = default
proc repeatChar*(count: int, c: char = ' '): string {.noSideEffect, proc repeat*(c: char, count: int): string {.noSideEffect,
rtl, extern: "nsuRepeatChar".} = rtl, extern: "nsuRepeatChar".} =
## Returns a string of length `count` consisting only of ## Returns a string of length `count` consisting only of
## the character `c`. You can use this proc to left align strings. Example: ## the character `c`. You can use this proc to left align strings. Example:
## ##
## .. code-block:: nim ## .. code-block:: nim
## proc tabexpand(indent: int, text: string, tabsize: int = 4) =
## echo '\t'.repeat(indent div tabsize), ' '.repeat(indent mod tabsize), text
##
## tabexpand(4, "At four")
## tabexpand(5, "At five")
## tabexpand(6, "At six")
result = newString(count)
for i in 0..count-1: result[i] = c
proc repeat*(s: string, n: int): string {.noSideEffect,
rtl, extern: "nsuRepeatStr".} =
## Returns String `s` concatenated `n` times. Example:
##
## .. code-block:: nim
## echo "+++ STOP ".repeat(4), "+++"
result = newStringOfCap(n * s.len)
for i in 1..n: result.add(s)
template spaces*(n: int): string = repeat(' ',n)
## Returns a String with `n` space characters. You can use this proc
## to left align strings. Example:
##
## .. code-block:: nim
## let ## let
## width = 15 ## width = 15
## text1 = "Hello user!" ## text1 = "Hello user!"
## text2 = "This is a very long string" ## text2 = "This is a very long string"
## echo text1 & repeatChar(max(0, width - text1.len)) & "|" ## echo text1 & spaces(max(0, width - text1.len)) & "|"
## echo text2 & repeatChar(max(0, width - text2.len)) & "|" ## echo text2 & spaces(max(0, width - text2.len)) & "|"
result = newString(count)
for i in 0..count-1: result[i] = c
proc repeatStr*(count: int, s: string): string {.noSideEffect, proc repeatChar*(count: int, c: char = ' '): string {.deprecated.} = repeat(c, count)
rtl, extern: "nsuRepeatStr".} = ## deprecated: use repeat() or spaces()
## Returns `s` concatenated `count` times.
result = newStringOfCap(count*s.len) proc repeatStr*(count: int, s: string): string {.deprecated.} = repeat(s, count)
for i in 0..count-1: result.add(s) ## deprecated: use repeat(string, count) or string.repeat(count)
proc align*(s: string, count: int, padding = ' '): string {. proc align*(s: string, count: int, padding = ' '): string {.
noSideEffect, rtl, extern: "nsuAlignString".} = noSideEffect, rtl, extern: "nsuAlignString".} =

View file

@ -45,7 +45,6 @@ when defined(windows):
var var
oldAttr = getAttributes() oldAttr = getAttributes()
proc winGetch(): cint {.header: "<conio.h>", importc: "_getch".}
else: else:
import termios, unsigned import termios, unsigned
@ -364,12 +363,11 @@ 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 = when not defined(windows):
proc getch*(): char =
## Read a single character from the terminal, blocking until it is entered. ## Read a single character from the terminal, blocking until it is entered.
## The character is not printed to the terminal. ## The character is not printed to the terminal. This is not available for
when defined(windows): ## Windows.
result = winGetch().char
else:
let fd = getFileHandle(stdin) let fd = getFileHandle(stdin)
var oldMode: Termios var oldMode: Termios
discard fd.tcgetattr(addr oldMode) discard fd.tcgetattr(addr oldMode)

View file

@ -628,25 +628,25 @@ proc formatToken(info: TimeInfo, token: string, buf: var string) =
var fr = ($info.year).len()-2 var fr = ($info.year).len()-2
if fr < 0: fr = 0 if fr < 0: fr = 0
var fyear = ($info.year)[fr .. ($info.year).len()-1] var fyear = ($info.year)[fr .. ($info.year).len()-1]
if fyear.len != 2: fyear = repeatChar(2-fyear.len(), '0') & fyear if fyear.len != 2: fyear = repeat('0', 2-fyear.len()) & fyear
buf.add(fyear) buf.add(fyear)
of "yyy": of "yyy":
var fr = ($info.year).len()-3 var fr = ($info.year).len()-3
if fr < 0: fr = 0 if fr < 0: fr = 0
var fyear = ($info.year)[fr .. ($info.year).len()-1] var fyear = ($info.year)[fr .. ($info.year).len()-1]
if fyear.len != 3: fyear = repeatChar(3-fyear.len(), '0') & fyear if fyear.len != 3: fyear = repeat('0', 3-fyear.len()) & fyear
buf.add(fyear) buf.add(fyear)
of "yyyy": of "yyyy":
var fr = ($info.year).len()-4 var fr = ($info.year).len()-4
if fr < 0: fr = 0 if fr < 0: fr = 0
var fyear = ($info.year)[fr .. ($info.year).len()-1] var fyear = ($info.year)[fr .. ($info.year).len()-1]
if fyear.len != 4: fyear = repeatChar(4-fyear.len(), '0') & fyear if fyear.len != 4: fyear = repeat('0', 4-fyear.len()) & fyear
buf.add(fyear) buf.add(fyear)
of "yyyyy": of "yyyyy":
var fr = ($info.year).len()-5 var fr = ($info.year).len()-5
if fr < 0: fr = 0 if fr < 0: fr = 0
var fyear = ($info.year)[fr .. ($info.year).len()-1] var fyear = ($info.year)[fr .. ($info.year).len()-1]
if fyear.len != 5: fyear = repeatChar(5-fyear.len(), '0') & fyear if fyear.len != 5: fyear = repeat('0', 5-fyear.len()) & fyear
buf.add(fyear) buf.add(fyear)
of "z": of "z":
let hrs = (info.timezone div 60) div 60 let hrs = (info.timezone div 60) div 60

View file

@ -118,21 +118,35 @@ proc toUTF8*(c: Rune): string {.rtl, extern: "nuc$1".} =
elif i <=% 0x07FF: elif i <=% 0x07FF:
result = newString(2) result = newString(2)
result[0] = chr((i shr 6) or 0b110_00000) result[0] = chr((i shr 6) or 0b110_00000)
result[1] = chr((i and ones(6)) or 0b10_000000) result[1] = chr((i and ones(6)) or 0b10_0000_00)
elif i <=% 0xFFFF: elif i <=% 0xFFFF:
result = newString(3) result = newString(3)
result[0] = chr(i shr 12 or 0b1110_0000) result[0] = chr(i shr 12 or 0b1110_0000)
result[1] = chr(i shr 6 and ones(6) or 0b10_0000_00) result[1] = chr(i shr 6 and ones(6) or 0b10_0000_00)
result[2] = chr(i and ones(6) or 0b10_0000_00) result[2] = chr(i and ones(6) or 0b10_0000_00)
elif i <=% 0x0010FFFF: elif i <=% 0x001FFFFF:
result = newString(4) result = newString(4)
result[0] = chr(i shr 18 or 0b1111_0000) result[0] = chr(i shr 18 or 0b1111_0000)
result[1] = chr(i shr 12 and ones(6) or 0b10_0000_00) result[1] = chr(i shr 12 and ones(6) or 0b10_0000_00)
result[2] = chr(i shr 6 and ones(6) or 0b10_0000_00) result[2] = chr(i shr 6 and ones(6) or 0b10_0000_00)
result[3] = chr(i and ones(6) or 0b10_0000_00) result[3] = chr(i and ones(6) or 0b10_0000_00)
elif i <=% 0x03FFFFFF:
result = newString(5)
result[0] = chr(i shr 24 or 0b111110_00)
result[1] = chr(i shr 18 and ones(6) or 0b10_0000_00)
result[2] = chr(i shr 12 and ones(6) or 0b10_0000_00)
result[3] = chr(i shr 6 and ones(6) or 0b10_0000_00)
result[4] = chr(i and ones(6) or 0b10_0000_00)
elif i <=% 0x7FFFFFFF:
result = newString(6)
result[0] = chr(i shr 30 or 0b1111110_0)
result[1] = chr(i shr 24 and ones(6) or 0b10_0000_00)
result[2] = chr(i shr 18 and ones(6) or 0b10_0000_00)
result[3] = chr(i shr 12 and ones(6) or 0b10_0000_00)
result[4] = chr(i shr 6 and ones(6) or 0b10_0000_00)
result[5] = chr(i and ones(6) or 0b10_0000_00)
else: else:
result = newString(1) discard # error, exception?
result[0] = chr(i)
proc `$`*(rune: Rune): string = proc `$`*(rune: Rune): string =
## converts a rune to a string ## converts a rune to a string

View file

@ -285,6 +285,16 @@ proc `$`*(u: Uri): string =
result.add(u.anchor) result.add(u.anchor)
when isMainModule: when isMainModule:
block:
let str = "http://localhost"
let test = parseUri(str)
doAssert test.path == ""
block:
let str = "http://localhost/"
let test = parseUri(str)
doAssert test.path == "/"
block: block:
let str = "http://localhost:8080/test" let str = "http://localhost:8080/test"
let test = parseUri(str) let test = parseUri(str)

View file

@ -1083,7 +1083,7 @@ proc addEscaped(s: string): string =
else: result.add(c) else: result.add(c)
proc nodeToXml(n: PNode, indent: int = 0): string = proc nodeToXml(n: PNode, indent: int = 0): string =
result = repeatChar(indent, ' ') & "<" & n.nodeName result = spaces(indent) & "<" & n.nodeName
if not isNil(n.attributes): if not isNil(n.attributes):
for i in items(n.attributes): for i in items(n.attributes):
result.add(" " & i.name & "=\"" & addEscaped(i.value) & "\"") result.add(" " & i.name & "=\"" & addEscaped(i.value) & "\"")
@ -1098,23 +1098,23 @@ proc nodeToXml(n: PNode, indent: int = 0): string =
of ElementNode: of ElementNode:
result.add(nodeToXml(i, indent + 2)) result.add(nodeToXml(i, indent + 2))
of TextNode: of TextNode:
result.add(repeatChar(indent * 2, ' ')) result.add(spaces(indent * 2))
result.add(addEscaped(i.nodeValue)) result.add(addEscaped(i.nodeValue))
of CDataSectionNode: of CDataSectionNode:
result.add(repeatChar(indent * 2, ' ')) result.add(spaces(indent * 2))
result.add("<![CDATA[" & i.nodeValue & "]]>") result.add("<![CDATA[" & i.nodeValue & "]]>")
of ProcessingInstructionNode: of ProcessingInstructionNode:
result.add(repeatChar(indent * 2, ' ')) result.add(spaces(indent * 2))
result.add("<?" & PProcessingInstruction(i).target & " " & result.add("<?" & PProcessingInstruction(i).target & " " &
PProcessingInstruction(i).data & " ?>") PProcessingInstruction(i).data & " ?>")
of CommentNode: of CommentNode:
result.add(repeatChar(indent * 2, ' ')) result.add(spaces(indent * 2))
result.add("<!-- " & i.nodeValue & " -->") result.add("<!-- " & i.nodeValue & " -->")
else: else:
continue continue
result.add("\n") result.add("\n")
# Add the ending tag - </tag> # Add the ending tag - </tag>
result.add(repeatChar(indent, ' ') & "</" & n.nodeName & ">") result.add(spaces(indent) & "</" & n.nodeName & ">")
proc `$`*(doc: PDocument): string = proc `$`*(doc: PDocument): string =
## Converts a PDocument object into a string representation of it's XML ## Converts a PDocument object into a string representation of it's XML

View file

@ -2021,7 +2021,7 @@ proc `$`*[T: tuple|object](x: T): string =
result = "(" result = "("
var firstElement = true var firstElement = true
for name, value in fieldPairs(x): for name, value in fieldPairs(x):
if not(firstElement): result.add(", ") if not firstElement: result.add(", ")
result.add(name) result.add(name)
result.add(": ") result.add(": ")
result.add($value) result.add($value)

View file

@ -34,9 +34,9 @@ proc genericDeepCopyAux(dest, src: pointer, n: ptr TNimNode) {.benign.} =
proc copyDeepString(src: NimString): NimString {.inline.} = proc copyDeepString(src: NimString): NimString {.inline.} =
if src != nil: if src != nil:
result = rawNewString(src.space) result = rawNewStringNoInit(src.len)
result.len = src.len result.len = src.len
c_memcpy(result.data, src.data, (src.len + 1) * sizeof(char)) c_memcpy(result.data, src.data, src.len + 1)
proc genericDeepCopyAux(dest, src: pointer, mt: PNimType) = proc genericDeepCopyAux(dest, src: pointer, mt: PNimType) =
var var

View file

@ -1,7 +1,7 @@
# #
# #
# Nim's Runtime Library # Nim's Runtime Library
# (c) Copyright 2013 Andreas Rumpf # (c) Copyright 2015 Andreas Rumpf
# #
# See the file "copying.txt", included in this # See the file "copying.txt", included in this
# distribution, for details about the copyright. # distribution, for details about the copyright.
@ -117,7 +117,7 @@ proc usrToCell(usr: pointer): PCell {.inline.} =
# convert pointer to userdata to object (=pointer to refcount) # convert pointer to userdata to object (=pointer to refcount)
result = cast[PCell](cast[ByteAddress](usr)-%ByteAddress(sizeof(TCell))) result = cast[PCell](cast[ByteAddress](usr)-%ByteAddress(sizeof(TCell)))
proc canbeCycleRoot(c: PCell): bool {.inline.} = proc canBeCycleRoot(c: PCell): bool {.inline.} =
result = ntfAcyclic notin c.typ.flags result = ntfAcyclic notin c.typ.flags
proc extGetCellType(c: pointer): PNimType {.compilerproc.} = proc extGetCellType(c: pointer): PNimType {.compilerproc.} =
@ -461,6 +461,9 @@ proc rawNewObj(typ: PNimType, size: int, gch: var TGcHeap): pointer =
{.pop.} {.pop.}
proc newObjNoInit(typ: PNimType, size: int): pointer {.compilerRtl.} =
result = rawNewObj(typ, size, gch)
proc newObj(typ: PNimType, size: int): pointer {.compilerRtl.} = proc newObj(typ: PNimType, size: int): pointer {.compilerRtl.} =
result = rawNewObj(typ, size, gch) result = rawNewObj(typ, size, gch)
zeroMem(result, size) zeroMem(result, size)
@ -1050,7 +1053,11 @@ when useMarkForDebug or useBackupGc:
markGlobals(gch) markGlobals(gch)
proc collectCT(gch: var TGcHeap) = proc collectCT(gch: var TGcHeap) =
if (gch.zct.len >= ZctThreshold or (cycleGC and # stackMarkCosts prevents some pathological behaviour: Stack marking
# becomes more expensive with large stacks and large stacks mean that
# cells with RC=0 are more likely to be kept alive by the stack.
let stackMarkCosts = max(stackSize() div (16*sizeof(int)), ZctThreshold)
if (gch.zct.len >= stackMarkCosts or (cycleGC and
getOccupiedMem(gch.region)>=gch.cycleThreshold) or alwaysGC) and getOccupiedMem(gch.region)>=gch.cycleThreshold) or alwaysGC) and
gch.recGcLock == 0: gch.recGcLock == 0:
when useMarkForDebug: when useMarkForDebug:

View file

@ -593,7 +593,7 @@ proc addNewObjToZCT(res: PCell, gch: var TGcHeap) {.inline.} =
return return
add(gch.zct, res) add(gch.zct, res)
proc rawNewObj(typ: PNimType, size: int, gch: var TGcHeap, rc1: bool): pointer = proc rawNewObj(typ: PNimType, size: int, gch: var TGcHeap, rc1 = false): pointer =
# generates a new object and sets its reference counter to 0 # generates a new object and sets its reference counter to 0
acquire(gch) acquire(gch)
sysAssert(allocInv(gch.region), "rawNewObj begin") sysAssert(allocInv(gch.region), "rawNewObj begin")

View file

@ -261,6 +261,10 @@ proc newObj(typ: PNimType, size: int): pointer {.compilerRtl.} =
zeroMem(result, size) zeroMem(result, size)
when defined(memProfiler): nimProfile(size) when defined(memProfiler): nimProfile(size)
proc newObjNoInit(typ: PNimType, size: int): pointer {.compilerRtl.} =
result = rawNewObj(typ, size, gch)
when defined(memProfiler): nimProfile(size)
proc newSeq(typ: PNimType, len: int): pointer {.compilerRtl.} = proc newSeq(typ: PNimType, len: int): pointer {.compilerRtl.} =
# `newObj` already uses locks, so no need for them here. # `newObj` already uses locks, so no need for them here.
let size = addInt(mulInt(len, typ.base.size), GenericSeqSize) let size = addInt(mulInt(len, typ.base.size), GenericSeqSize)

View file

@ -11,7 +11,7 @@ when declared(NimString):
# we are in system module: # we are in system module:
{.pragma: codegenType, compilerproc.} {.pragma: codegenType, compilerproc.}
else: else:
{.pragma: codegenType.} {.pragma: codegenType, importc.}
type type
# This should be he same as ast.TTypeKind # This should be he same as ast.TTypeKind
@ -65,7 +65,7 @@ type
tyBigNum, tyBigNum,
TNimNodeKind = enum nkNone, nkSlot, nkList, nkCase TNimNodeKind = enum nkNone, nkSlot, nkList, nkCase
TNimNode {.codegenType, final.} = object TNimNode {.codegenType.} = object
kind: TNimNodeKind kind: TNimNodeKind
offset: int offset: int
typ: ptr TNimType typ: ptr TNimType
@ -78,7 +78,7 @@ type
ntfAcyclic = 1, # type cannot form a cycle ntfAcyclic = 1, # type cannot form a cycle
ntfEnumHole = 2 # enum has holes and thus `$` for them needs the slow ntfEnumHole = 2 # enum has holes and thus `$` for them needs the slow
# version # version
TNimType {.codegenType, final.} = object TNimType {.codegenType.} = object
size: int size: int
kind: TNimKind kind: TNimKind
flags: set[TNimTypeFlag] flags: set[TNimTypeFlag]

View file

@ -1,7 +1,7 @@
# #
# #
# Nim's Runtime Library # Nim's Runtime Library
# (c) Copyright 2013 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.
@ -240,6 +240,8 @@ elif defined(nogc) and defined(useMalloc):
result = newObj(typ, addInt(mulInt(len, typ.base.size), GenericSeqSize)) result = newObj(typ, addInt(mulInt(len, typ.base.size), GenericSeqSize))
cast[PGenericSeq](result).len = len cast[PGenericSeq](result).len = len
cast[PGenericSeq](result).reserved = len cast[PGenericSeq](result).reserved = len
proc newObjNoInit(typ: PNimType, size: int): pointer =
result = alloc(size)
proc growObj(old: pointer, newsize: int): pointer = proc growObj(old: pointer, newsize: int): pointer =
result = realloc(old, newsize) result = realloc(old, newsize)
@ -289,6 +291,10 @@ elif defined(nogc):
proc newObj(typ: PNimType, size: int): pointer {.compilerproc.} = proc newObj(typ: PNimType, size: int): pointer {.compilerproc.} =
result = alloc0(size) result = alloc0(size)
proc newObjNoInit(typ: PNimType, size: int): pointer =
result = alloc(size)
proc newSeq(typ: PNimType, len: int): pointer {.compilerproc.} = proc newSeq(typ: PNimType, len: int): pointer {.compilerproc.} =
result = newObj(typ, addInt(mulInt(len, typ.base.size), GenericSeqSize)) result = newObj(typ, addInt(mulInt(len, typ.base.size), GenericSeqSize))
cast[PGenericSeq](result).len = len cast[PGenericSeq](result).len = len

View file

@ -145,8 +145,8 @@ proc readAllFile(file: File): string =
proc readAll(file: File): TaintedString = proc readAll(file: File): TaintedString =
# Separate handling needed because we need to buffer when we # Separate handling needed because we need to buffer when we
# don't know the overall length of the File. # don't know the overall length of the File.
var len = rawFileSize(file) let len = if file != stdin: rawFileSize(file) else: -1
if len >= 0: if len > 0:
result = readAllFile(file, len).TaintedString result = readAllFile(file, len).TaintedString
else: else:
result = readAllBuffer(file).TaintedString result = readAllBuffer(file).TaintedString

View file

@ -30,18 +30,30 @@ proc eqStrings(a, b: NimString): bool {.inline, compilerProc.} =
if a == b: return true if a == b: return true
if a == nil or b == nil: return false if a == nil or b == nil: return false
return a.len == b.len and return a.len == b.len and
c_memcmp(a.data, b.data, a.len * sizeof(char)) == 0'i32 c_memcmp(a.data, b.data, a.len) == 0'i32
when declared(allocAtomic): when declared(allocAtomic):
template allocStr(size: expr): expr = template allocStr(size: expr): expr =
cast[NimString](allocAtomic(size)) cast[NimString](allocAtomic(size))
template allocStrNoInit(size: expr): expr =
cast[NimString](boehmAllocAtomic(size))
else: else:
template allocStr(size: expr): expr = template allocStr(size: expr): expr =
cast[NimString](newObj(addr(strDesc), size)) cast[NimString](newObj(addr(strDesc), size))
template allocStrNoInit(size: expr): expr =
cast[NimString](newObjNoInit(addr(strDesc), size))
proc rawNewStringNoInit(space: int): NimString {.compilerProc.} =
var s = space
if s < 7: s = 7
result = allocStrNoInit(sizeof(TGenericSeq) + s + 1)
result.reserved = s
proc rawNewString(space: int): NimString {.compilerProc.} = proc rawNewString(space: int): NimString {.compilerProc.} =
var s = space var s = space
if s < 8: s = 7 if s < 7: s = 7
result = allocStr(sizeof(TGenericSeq) + s + 1) result = allocStr(sizeof(TGenericSeq) + s + 1)
result.reserved = s result.reserved = s
@ -53,10 +65,10 @@ proc copyStrLast(s: NimString, start, last: int): NimString {.compilerProc.} =
var start = max(start, 0) var start = max(start, 0)
var len = min(last, s.len-1) - start + 1 var len = min(last, s.len-1) - start + 1
if len > 0: if len > 0:
result = rawNewString(len) result = rawNewStringNoInit(len)
result.len = len result.len = len
c_memcpy(result.data, addr(s.data[start]), len * sizeof(char)) c_memcpy(result.data, addr(s.data[start]), len)
#result.data[len] = '\0' result.data[len] = '\0'
else: else:
result = rawNewString(len) result = rawNewString(len)
@ -64,10 +76,9 @@ proc copyStr(s: NimString, start: int): NimString {.compilerProc.} =
result = copyStrLast(s, start, s.len-1) result = copyStrLast(s, start, s.len-1)
proc toNimStr(str: cstring, len: int): NimString {.compilerProc.} = proc toNimStr(str: cstring, len: int): NimString {.compilerProc.} =
result = rawNewString(len) result = rawNewStringNoInit(len)
result.len = len result.len = len
c_memcpy(result.data, str, (len+1) * sizeof(char)) c_memcpy(result.data, str, len + 1)
#result.data[len] = '\0' # readline relies on this!
proc cstrToNimstr(str: cstring): NimString {.compilerRtl.} = proc cstrToNimstr(str: cstring): NimString {.compilerRtl.} =
result = toNimStr(str, c_strlen(str)) result = toNimStr(str, c_strlen(str))
@ -77,23 +88,24 @@ proc copyString(src: NimString): NimString {.compilerRtl.} =
if (src.reserved and seqShallowFlag) != 0: if (src.reserved and seqShallowFlag) != 0:
result = src result = src
else: else:
result = rawNewString(src.space) result = rawNewStringNoInit(src.len)
result.len = src.len result.len = src.len
c_memcpy(result.data, src.data, (src.len + 1) * sizeof(char)) c_memcpy(result.data, src.data, src.len + 1)
proc copyStringRC1(src: NimString): NimString {.compilerRtl.} = proc copyStringRC1(src: NimString): NimString {.compilerRtl.} =
if src != nil: if src != nil:
var s = src.space
if s < 8: s = 7
when declared(newObjRC1): when declared(newObjRC1):
var s = src.len
if s < 7: s = 7
result = cast[NimString](newObjRC1(addr(strDesc), sizeof(TGenericSeq) + result = cast[NimString](newObjRC1(addr(strDesc), sizeof(TGenericSeq) +
s+1)) s+1))
else:
result = allocStr(sizeof(TGenericSeq) + s + 1)
result.reserved = s result.reserved = s
else:
result = rawNewStringNoInit(src.len)
result.len = src.len result.len = src.len
c_memcpy(result.data, src.data, src.len + 1) c_memcpy(result.data, src.data, src.len + 1)
proc hashString(s: string): int {.compilerproc.} = proc hashString(s: string): int {.compilerproc.} =
# the compiler needs exactly the same hash function! # the compiler needs exactly the same hash function!
# this used to be used for efficient generation of string case statements # this used to be used for efficient generation of string case statements
@ -113,7 +125,7 @@ proc addChar(s: NimString, c: char): NimString =
if result.len >= result.space: if result.len >= result.space:
result.reserved = resize(result.space) result.reserved = resize(result.space)
result = cast[NimString](growObj(result, result = cast[NimString](growObj(result,
sizeof(TGenericSeq) + (result.reserved+1) * sizeof(char))) sizeof(TGenericSeq) + result.reserved + 1))
result.data[result.len] = c result.data[result.len] = c
result.data[result.len+1] = '\0' result.data[result.len+1] = '\0'
inc(result.len) inc(result.len)
@ -157,7 +169,7 @@ proc resizeString(dest: NimString, addlen: int): NimString {.compilerRtl.} =
result = cast[NimString](growObj(dest, sizeof(TGenericSeq) + sp + 1)) result = cast[NimString](growObj(dest, sizeof(TGenericSeq) + sp + 1))
result.reserved = sp result.reserved = sp
#result = rawNewString(sp) #result = rawNewString(sp)
#copyMem(result, dest, dest.len * sizeof(char) + sizeof(TGenericSeq)) #copyMem(result, dest, dest.len + sizeof(TGenericSeq))
# DO NOT UPDATE LEN YET: dest.len = newLen # DO NOT UPDATE LEN YET: dest.len = newLen
proc appendString(dest, src: NimString) {.compilerproc, inline.} = proc appendString(dest, src: NimString) {.compilerproc, inline.} =
@ -264,6 +276,16 @@ proc nimFloatToStr(f: float): string {.compilerproc.} =
buf[n] = '.' buf[n] = '.'
buf[n+1] = '0' buf[n+1] = '0'
buf[n+2] = '\0' buf[n+2] = '\0'
# On Windows nice numbers like '1.#INF', '-1.#INF' or '1.#NAN' are produced.
# We want to get rid of these here:
if buf[n-1] == 'N':
result = "nan"
elif buf[n-1] == 'F':
if buf[0] == '-':
result = "-inf"
else:
result = "inf"
else:
result = $buf result = $buf
proc strtod(buf: cstring, endptr: ptr cstring): float64 {.importc, proc strtod(buf: cstring, endptr: ptr cstring): float64 {.importc,

View file

@ -50,7 +50,7 @@ when useWinVersion:
from winlean import SocketHandle from winlean import SocketHandle
else: else:
const const
versions = "(.10|.1.0.1|.1.0.0|.0.9.9|.0.9.8|.0.9.7|.0.9.6|.0.9.5|.0.9.4)" versions = "(.10|.1.0.1|.1.0.0|.0.9.9|.0.9.8)"
when defined(macosx): when defined(macosx):
const const
DLLSSLName = "libssl" & versions & ".dylib" DLLSSLName = "libssl" & versions & ".dylib"
@ -141,6 +141,14 @@ const
SSL_CTRL_GET_MAX_CERT_LIST* = 50 SSL_CTRL_GET_MAX_CERT_LIST* = 50
SSL_CTRL_SET_MAX_CERT_LIST* = 51 #* Allow SSL_write(..., n) to return r with 0 < r < n (i.e. report success SSL_CTRL_SET_MAX_CERT_LIST* = 51 #* Allow SSL_write(..., n) to return r with 0 < r < n (i.e. report success
# * when just a single record has been written): * # * when just a single record has been written): *
SSL_CTRL_SET_TLSEXT_SERVERNAME_CB = 53
SSL_CTRL_SET_TLSEXT_SERVERNAME_ARG = 54
SSL_CTRL_SET_TLSEXT_HOSTNAME = 55
TLSEXT_NAMETYPE_host_name* = 0
SSL_TLSEXT_ERR_OK* = 0
SSL_TLSEXT_ERR_ALERT_WARNING* = 1
SSL_TLSEXT_ERR_ALERT_FATAL* = 2
SSL_TLSEXT_ERR_NOACK* = 3
SSL_MODE_ENABLE_PARTIAL_WRITE* = 1 #* Make it possible to retry SSL_write() with changed buffer location SSL_MODE_ENABLE_PARTIAL_WRITE* = 1 #* Make it possible to retry SSL_write() with changed buffer location
# * (buffer contents must stay the same!); this is not the default to avoid # * (buffer contents must stay the same!); this is not the default to avoid
# * the misconception that non-blocking SSL_write() behaves like # * the misconception that non-blocking SSL_write() behaves like
@ -296,9 +304,41 @@ proc CRYPTO_malloc_init*() =
proc SSL_CTX_ctrl*(ctx: SslCtx, cmd: cInt, larg: int, parg: pointer): int{. proc SSL_CTX_ctrl*(ctx: SslCtx, cmd: cInt, larg: int, parg: pointer): int{.
cdecl, dynlib: DLLSSLName, importc.} cdecl, dynlib: DLLSSLName, importc.}
proc SSL_CTX_callback_ctrl(ctx: SslCtx, typ: cInt, fp: PFunction): int{.
cdecl, dynlib: DLLSSLName, importc.}
proc SSLCTXSetMode*(ctx: SslCtx, mode: int): int = proc SSLCTXSetMode*(ctx: SslCtx, mode: int): int =
result = SSL_CTX_ctrl(ctx, SSL_CTRL_MODE, mode, nil) result = SSL_CTX_ctrl(ctx, SSL_CTRL_MODE, mode, nil)
proc SSL_ctrl*(ssl: SslPtr, cmd: cInt, larg: int, parg: pointer): int{.
cdecl, dynlib: DLLSSLName, importc.}
proc SSL_set_tlsext_host_name*(ssl: SslPtr, name: cstring): int =
result = SSL_ctrl(ssl, SSL_CTRL_SET_TLSEXT_HOSTNAME, TLSEXT_NAMETYPE_host_name, name)
## Set the SNI server name extension to be used in a client hello.
## Returns 1 if SNI was set, 0 if current SSL configuration doesn't support SNI.
proc SSL_get_servername*(ssl: SslPtr, typ: cInt = TLSEXT_NAMETYPE_host_name): cstring {.cdecl, dynlib: DLLSSLName, importc.}
## Retrieve the server name requested in the client hello. This can be used
## in the callback set in `SSL_CTX_set_tlsext_servername_callback` to
## implement virtual hosting. May return `nil`.
proc SSL_CTX_set_tlsext_servername_callback*(ctx: SslCtx, cb: proc(ssl: SslPtr, cb_id: int, arg: pointer): int {.cdecl.}): int =
## Set the callback to be used on listening SSL connections when the client hello is received.
##
## The callback should return one of:
## * SSL_TLSEXT_ERR_OK
## * SSL_TLSEXT_ERR_ALERT_WARNING
## * SSL_TLSEXT_ERR_ALERT_FATAL
## * SSL_TLSEXT_ERR_NOACK
result = SSL_CTX_callback_ctrl(ctx, SSL_CTRL_SET_TLSEXT_SERVERNAME_CB, cast[PFunction](cb))
proc SSL_CTX_set_tlsext_servername_arg*(ctx: SslCtx, arg: pointer): int =
## Set the pointer to be used in the callback registered to ``SSL_CTX_set_tlsext_servername_callback``.
result = SSL_CTX_ctrl(ctx, SSL_CTRL_SET_TLSEXT_SERVERNAME_ARG, 0, arg)
proc bioNew*(b: PBIO_METHOD): BIO{.cdecl, dynlib: DLLUtilName, importc: "BIO_new".} proc bioNew*(b: PBIO_METHOD): BIO{.cdecl, dynlib: DLLUtilName, importc: "BIO_new".}
proc bioFreeAll*(b: BIO){.cdecl, dynlib: DLLUtilName, importc: "BIO_free_all".} proc bioFreeAll*(b: BIO){.cdecl, dynlib: DLLUtilName, importc: "BIO_free_all".}
proc bioSMem*(): PBIO_METHOD{.cdecl, dynlib: DLLUtilName, importc: "BIO_s_mem".} proc bioSMem*(): PBIO_METHOD{.cdecl, dynlib: DLLUtilName, importc: "BIO_s_mem".}
@ -341,8 +381,6 @@ else:
dynlib: DLLSSLName, importc.} dynlib: DLLSSLName, importc.}
proc SslSetFd*(s: PSSL, fd: cInt): cInt{.cdecl, dynlib: DLLSSLName, importc.} proc SslSetFd*(s: PSSL, fd: cInt): cInt{.cdecl, dynlib: DLLSSLName, importc.}
proc SslCtrl*(ssl: PSSL, cmd: cInt, larg: int, parg: Pointer): int{.cdecl,
dynlib: DLLSSLName, importc.}
proc SslCTXCtrl*(ctx: PSSL_CTX, cmd: cInt, larg: int, parg: Pointer): int{. proc SslCTXCtrl*(ctx: PSSL_CTX, cmd: cInt, larg: int, parg: Pointer): int{.
cdecl, dynlib: DLLSSLName, importc.} cdecl, dynlib: DLLSSLName, importc.}

View file

@ -8,10 +8,6 @@ the Nim compiler itself is written in the Nim programming language
the C source of an older version of the compiler are needed to bootstrap the the C source of an older version of the compiler are needed to bootstrap the
latest version. The C sources are available in a separate repo [here](http://github.com/nim-lang/csources). latest version. The C sources are available in a separate repo [here](http://github.com/nim-lang/csources).
Pre-compiled snapshots of the compiler are also available on
[Nimbuild](http://build.nim-lang.org/). Your platform however may not
currently be built for.
The compiler currently supports the following platform and architecture The compiler currently supports the following platform and architecture
combinations: combinations:
@ -39,8 +35,7 @@ $ bin/nim c koch
$ ./koch boot -d:release $ ./koch boot -d:release
``` ```
``koch install [dir]`` may then be used to install Nim, or you can simply ``koch install [dir]`` may then be used to install Nim, but lots of things don't work then so don't do that. Add it to your PATH instead. More ``koch`` related options are documented in
add it to your PATH. More ``koch`` related options are documented in
[doc/koch.txt](doc/koch.txt). [doc/koch.txt](doc/koch.txt).
The above steps can be performed on Windows in a similar fashion, the The above steps can be performed on Windows in a similar fashion, the

View file

@ -1,6 +1,5 @@
discard """ discard """
line: 21 output: "action 3 arg"
errormsg: "invalid type: 'Table[string, proc (string){.gcsafe.}]'"
""" """
import tables import tables

View file

@ -3,14 +3,14 @@ discard """
WARNING: false first assertion from bar WARNING: false first assertion from bar
ERROR: false second assertion from bar ERROR: false second assertion from bar
-1 -1
tests/assert/tfailedassert.nim:27 false assertion from foo tfailedassert.nim:27 false assertion from foo
''' '''
""" """
type type
TLineInfo = tuple[filename: string, line: int] TLineInfo = tuple[filename: string, line: int]
TMyError = object of E_Base TMyError = object of Exception
lineinfo: TLineInfo lineinfo: TLineInfo
EMyError = ref TMyError EMyError = ref TMyError

View file

@ -0,0 +1,30 @@
discard """
output: '''255
1 1
0.5'''
"""
# bug #1181
type
TFoo = object
x: int32
proc mainowar =
var foo: TFoo
foo.x = 0xff
var arr1 = cast[ptr array[4, uint8]](addr foo)[] # Fails.
echo arr1[when cpuEndian == littleEndian: 0 else: 3]
var i = 1i32
let x = addr i
var arr2 = cast[ptr array[4, uint8]](x)[] # Fails.
echo arr2[when cpuEndian == littleEndian: 0 else: 3], " ", i
# bug #1715
var a: array[2, float32] = [0.5'f32, 0.7]
let p = addr a
var b = p[]
echo b[0]
mainowar()

View file

@ -0,0 +1,15 @@
discard """
disabled: "true"
"""
# Now the compiler fails with OOM. yay.
# bug #794
type TRange = range[0..3]
const str = "123456789"
for i in TRange.low .. TRange.high:
echo str[i] #This works fine
echo str[int(i) .. int(TRange.high)] #So does this
echo str[i .. TRange.high] #The compiler complains about this

View file

@ -0,0 +1,80 @@
# bug #2250
import
math, strutils
type
Meters = float
Point2[T] = tuple[x, y: T]
HexState* = enum
hsOn, hsOff
Index = uint16
HexGrid* = object
w, h: int ## Width and height of the hex grid.
radius: Meters ## Radius of circle that circumscribes a hexagon.
grid: seq[HexState] ## Information on what hexes are drawn.
HexVtxIndex = enum
hiA, hiB, hiC, hiD, hiE, hiF
HexCoord* = Point2[int]
const
HexDY = sqrt(1.0 - (0.5 * 0.5)) # dy from center to midpoint of 1-2
HexDX = sqrt(1.0 - (HexDY * HexDY)) # dx from center to midpoint of 1-5 (0.5)
let
hexOffsets : array[HexVtxIndex, Point2[float]] = [
(-1.0, 0.0),
(-HexDX, -HexDY),
(HexDX, -HexDY),
(1.0, 0.0),
(HexDX, HexDY),
(-HexDX, HexDY)]
evenSharingOffsets : array[HexVtxIndex, tuple[hc: HexCoord; idx: HexVtxIndex]] = [
((0,0), hiA),
((0,0), hiB),
((1,-1), hiA),
((1,0), hiB),
((1,0), hiA),
((0,1), hiB)]
oddSharingOffsets : array[HexVtxIndex, tuple[hc: HexCoord; idx: HexVtxIndex]] = [
((0,0), hiA),
((0,0), hiB),
((1,0), hiA),
((1,1), hiB),
((1,1), hiA),
((0,1), hiB)]
template odd*(i: int) : expr =
(i and 1) != 0
proc vidx(hg: HexGrid; col, row: int; i: HexVtxIndex) : Index =
#NOTE: this variation compiles
#var offset : type(evenSharingOffsets[i])
#
#if odd(col):
# offset = oddSharingOffsets[i]
#else:
# offset = evenSharingOffsets[i]
let
#NOTE: this line generates the bad code
offset = (if odd(col): oddSharingOffsets[i] else: evenSharingOffsets[i])
x = col + 1 + offset.hc.x
y = row + 1 + offset.hc.y
result = Index(x*2 + y * (hg.w + 2)*2 + int(offset.idx))
proc go() =
var hg : HexGrid
echo "vidx ", $vidx(hg, 1, 2, hiC)
go()

View file

@ -0,0 +1,8 @@
discard """
file: "tconsttypemismatch.nim"
line: 7
errormsg: "type mismatch"
"""
# bug #2252
const foo: int = 1000 / 30

View file

@ -1,5 +1,5 @@
discard """ discard """
cmd: "nim cpp $target" cmd: "nim cpp $file"
""" """
import rawsockets import rawsockets

View file

@ -1,5 +1,5 @@
discard """ discard """
cmd: "nim cpp $target" cmd: "nim cpp $file"
""" """
import typeinfo import typeinfo

View file

@ -1,6 +1,5 @@
discard """ discard """
line: 9 nimout: "a is deprecated [Deprecated]"
errormsg: "'a' is deprecated [Deprecated]"
""" """
var var
@ -8,4 +7,3 @@ var
a[8] = 1 a[8] = 1

View file

@ -1,6 +1,6 @@
discard """ discard """
line: 20 line: 23
errormsg: " usage of a type with a destructor in a non destructible context" nimout: " usage of a type with a destructor in a non destructible context"
""" """
{.experimental.} {.experimental.}
@ -19,5 +19,9 @@ proc open: TMyObj =
proc `$`(x: TMyObj): string = $x.y proc `$`(x: TMyObj): string = $x.y
echo open() proc foo =
discard open()
# XXX doesn't trigger this yet:
#echo open()

View file

@ -1,6 +1,6 @@
discard """ discard """
line: 7 line: 7
errormsg: "expression 'items' cannot be called" errormsg: "undeclared identifier: 'items'"
""" """
type a = enum b,c,d type a = enum b,c,d

View file

@ -0,0 +1,18 @@
discard """
output: '''hi
hi'''
"""
# bug #1742
template test(): expr =
let a = 0
defer: echo "hi"
a
let i = test()
import strutils
let x = try: parseInt("133a")
except: -1
finally: echo "hi"

View file

@ -0,0 +1,6 @@
discard """
nimout: "Special variable 'result' is shadowed. [ResultShadowed]"
"""
proc test(): string =
var result = "foo"

View file

@ -188,7 +188,7 @@ proc traceTree[T,D](root: PNode[T,D]) =
write stdout, space write stdout, space
proc doTrace(n: PNode[T,D], level: int) = proc doTrace(n: PNode[T,D], level: int) =
var space = repeatChar(2 * level) var space = spaces(2 * level)
traceln(space) traceln(space)
write stdout, "node: " write stdout, "node: "
if n == nil: if n == nil:

View file

@ -1,5 +1,11 @@
# Bug #2022 # Bug #2022
discard """
output: '''@[97, 45]
@[true, false]
@[false, false]'''
"""
## The goal of this snippet is to provide and test a construct for general- ## The goal of this snippet is to provide and test a construct for general-
## purpose, random-access mapping. I use an AST-manipulation-based approach ## purpose, random-access mapping. I use an AST-manipulation-based approach
## because it's more efficient than using procedure pointers and less ## because it's more efficient than using procedure pointers and less
@ -31,6 +37,7 @@ type Mapped[Input; predicate: static[string]] = object
input: Input input: Input
macro map(input, predicate: expr): expr = macro map(input, predicate: expr): expr =
let predicate = callsite()[2]
newNimNode(nnkObjConstr).add( newNimNode(nnkObjConstr).add(
newNimNode(nnkBracketExpr).add( newNimNode(nnkBracketExpr).add(
ident"Mapped", ident"Mapped",

View file

@ -1,6 +1,6 @@
discard """ discard """
file: "toop1.nim"
output: "in globalaux2: 10\ntotal globals: 2\nint value: 100\nstring value: second" output: "in globalaux2: 10\ntotal globals: 2\nint value: 100\nstring value: second"
disabled: "true"
""" """
import globalaux, globalaux2 import globalaux, globalaux2

54
tests/init/tuninit2.nim Normal file
View file

@ -0,0 +1,54 @@
# bug #2316
type
EventType = enum
QuitEvent = 5
AppMain* = ref object of RootObj
width: int
height: int
title: string
running: bool
event_type: EventType
App* = ref object of AppMain
draw_proc: proc(app: AppMain): void {.closure.}
events_proc: proc(app: AppMain): void {.closure.}
update_proc: proc(app: AppMain, dt: float): void {.closure.}
load_proc: proc(app: AppMain): void {.closure.}
proc initApp*(t: string, w, h: int): App =
App(width: w, height: h, title: t, event_type: EventType.QuitEvent)
method getTitle*(self: AppMain): string = self.title
method getWidth*(self: AppMain): int = self.width
method getHeight*(self: AppMain): int = self.height
method draw*(self: App, draw: proc(app: AppMain)): void =
self.draw_proc = draw
method load*(self: App, load: proc(a: AppMain)): void =
self.load_proc = load
method events*(self: App, events: proc(app: AppMain)): void =
self.events_proc = events
method update*(self: App, update: proc(app: AppMain, delta: float)): void =
self.update_proc = update
method run*(self: App): void = discard
var mygame = initApp("Example", 800, 600)
mygame.load(proc(app: AppMain): void =
echo app.getTitle()
echo app.getWidth()
echo app.getHeight()
)
mygame.events(proc(app: AppMain): void =
discard
)
mygame.run()

View file

@ -471,7 +471,7 @@ proc build_help*(expected: seq[Tparameter_specification] = @[],
let width = prefixes.map(proc (x: string): int = 3 + len(x)).max let width = prefixes.map(proc (x: string): int = 3 + len(x)).max
for line in zip(prefixes, helps): for line in zip(prefixes, helps):
result.add(line.a & repeatChar(width - line.a.len) & line.b) result.add(line.a & spaces(width - line.a.len) & line.b)
proc echo_help*(expected: seq[Tparameter_specification] = @[], proc echo_help*(expected: seq[Tparameter_specification] = @[],

View file

@ -1,5 +1,5 @@
import streams import streams
from strutils import repeatChar from strutils import repeat
proc readPaddedStr*(s: PStream, length: int, padChar = '\0'): TaintedString = proc readPaddedStr*(s: PStream, length: int, padChar = '\0'): TaintedString =
var lastChr = length var lastChr = length
@ -10,7 +10,7 @@ proc readPaddedStr*(s: PStream, length: int, padChar = '\0'): TaintedString =
proc writePaddedStr*(s: PStream, str: string, length: int, padChar = '\0') = proc writePaddedStr*(s: PStream, str: string, length: int, padChar = '\0') =
if str.len < length: if str.len < length:
s.write(str) s.write(str)
s.write(repeatChar(length - str.len, padChar)) s.write(repeat(padChar, length - str.len))
elif str.len > length: elif str.len > length:
s.write(str.substr(0, length - 1)) s.write(str.substr(0, length - 1))
else: else:
@ -37,7 +37,7 @@ when isMainModule:
testStream.setPosition 0 testStream.setPosition 0
testStream.writePaddedStr("Sup", 10) testStream.writePaddedStr("Sup", 10)
echo(repr(testStream), testStream.data.len) echo(repr(testStream), testStream.data.len)
doAssert testStream.data == "Sup"&repeatChar(7, '\0') doAssert testStream.data == "Sup"&repeat('\0', 7)
testStream.setPosition 0 testStream.setPosition 0
res = testStream.readPaddedStr(10) res = testStream.readPaddedStr(10)

View file

@ -144,7 +144,7 @@ when isMainModule:
discard """block: discard """block:
var var
TestFile: FileChallengePair TestFile: FileChallengePair
contents = repeatStr(2, "abcdefghijklmnopqrstuvwxyz") contents = repeat("abcdefghijklmnopqrstuvwxyz", 2)
testFile.challenge = newScFileChallenge("foobar.test", FZoneCfg, contents.len.int32) testFile.challenge = newScFileChallenge("foobar.test", FZoneCfg, contents.len.int32)
testFile.file = checksumStr(contents) testFile.file = checksumStr(contents)
myAssets.add testFile""" myAssets.add testFile"""

View file

@ -113,7 +113,7 @@ when defined(recordMode):
isRecording = false isRecording = false
proc zeroPad*(s: string; minLen: int): string = proc zeroPad*(s: string; minLen: int): string =
if s.len < minLen: if s.len < minLen:
result = repeatChar(minLen - s.len, '0') result = repeat(0, minLen - s.len)
result.add s result.add s
else: else:
result = s result = s

View file

@ -27,9 +27,9 @@ when isMainModule:
var res = t.map(proc(z: int): int = result = z * 10) var res = t.map(proc(z: int): int = result = z * 10)
dumpSeq res dumpSeq res
from strutils import toHex, repeatStr from strutils import toHex
var foocakes = t.map(proc(z: int): string = var foocakes = t.map(proc(z: int): string =
result = toHex((z * 23).biggestInt, 4)) result = toHex((z * 23).BiggestInt, 4))
dumpSeq foocakes dumpSeq foocakes
t.mapInPlace(proc(z: int): int = result = z * 30) t.mapInPlace(proc(z: int): int = result = z * 30)

View file

@ -24,17 +24,17 @@ when isMainModule:
import strutils import strutils
var r = compress("Hello") var r = compress("Hello")
echo repr(r) echo repr(r)
var l = "Hello".len var ln = "Hello".len
var rr = uncompress(r, l) var rr = uncompress(r, ln)
echo repr(rr) echo repr(rr)
assert rr == "Hello" assert rr == "Hello"
proc `*`(a: string; b: int): string {.inline.} = result = repeatStr(b, a) proc `*`(a: string; b: int): string {.inline.} = result = repeat(a, b)
var s = "yo dude sup bruh homie" * 50 var s = "yo dude sup bruh homie" * 50
r = compress(s) r = compress(s)
echo s.len, " -> ", r.len echo s.len, " -> ", r.len
l = s.len ln = s.len
rr = uncompress(r, l) rr = uncompress(r, ln)
echo r.len, " -> ", rr.len echo r.len, " -> ", rr.len
assert rr == s assert rr == s

View file

@ -174,7 +174,7 @@ when isMainModule:
block: block:
var var
TestFile: FileChallengePair TestFile: FileChallengePair
contents = repeatStr(2, "abcdefghijklmnopqrstuvwxyz") contents = repeat("abcdefghijklmnopqrstuvwxyz", 2)
testFile.challenge = newScFileChallenge("foobar.test", FZoneCfg, contents.len.int32) testFile.challenge = newScFileChallenge("foobar.test", FZoneCfg, contents.len.int32)
testFile.file = checksumStr(contents) testFile.file = checksumStr(contents)
myAssets.add testFile myAssets.add testFile

105
tests/misc/parsecomb.nim Normal file
View file

@ -0,0 +1,105 @@
type Input[T] = object
toks: seq[T]
index: int
type
ResultKind* = enum rkSuccess, rkFailure
Result*[T, O] = object
case kind*: ResultKind
of rkSuccess:
output*: O
input: Input[T]
of rkFailure:
nil
type
Parser*[T, O] = distinct proc (input: Input[T]): Result[T, O]
proc unit*[T, O](v: O): Parser[T, O] =
Parser(proc (inp: Input[T]): Result[T, O] =
Result[T, O](kind: rkSuccess, output: v, input: inp))
proc fail*[T, O](): Parser[T, O] =
Parser(proc (inp: Input[T]): Result[T, O] =
Result(kind: rkFailure))
method runInput[T, O](self: Parser[T, O], inp: Input[T]): Result[T, O] =
# hmmm ..
type tmp = proc (input: Input[T]): Result[T, O]
# XXX: above needed for now, as without the `tmp` bit below, it compiles to invalid C.
tmp(self)(inp)
method run*[T, O](self: Parser[T, O], toks: seq[T]): Result[T, O] =
self.runInput(Input[T](toks: toks, index: 0))
method chain*[T, O1, O2](self: Parser[T, O1], nextp: proc (v: O1): Parser[T, O2]): Parser[T, O2] =
Parser(proc (inp: Input[T]): Result[T, O2] =
let r = self.runInput(inp)
case r.kind:
of rkSuccess:
nextp(r.output).runInput(r.input)
of rkFailure:
Result[T, O2](kind: rkFailure))
method skip[T](self: Input[T], n: int): Input[T] =
Input[T](toks: self.toks, index: self.index + n)
proc pskip*[T](n: int): Parser[T, tuple[]] =
Parser(proc (inp: Input[T]): Result[T, tuple[]] =
if inp.index + n <= inp.toks.len:
Result[T, tuple[]](kind: rkSuccess, output: (), input: inp.skip(n))
else:
Result[T, tuple[]](kind: rkFailure))
proc tok*[T](t: T): Parser[T, T] =
Parser(proc (inp: Input[T]): Result[T, T] =
if inp.index < inp.toks.len and inp.toks[inp.index] == t:
pskip[T](1).then(unit[T, T](t)).runInput(inp)
else:
Result[T, T](kind: rkFailure))
proc `+`*[T, O](first: Parser[T, O], second: Parser[T, O]): Parser[T, O] =
Parser(proc (inp: Input[T]): Result[T, O] =
let r = first.runInput(inp)
case r.kind
of rkSuccess:
r
else:
second.runInput(inp))
# end of primitives (definitions involving Parser(..))
method map*[T, O1, O2](self: Parser[T, O1], p: proc (v: O1): O2): Parser[T, O2] =
self.chain(proc (v: O1): Parser[T, O2] =
unit[T, O2](p(v)))
method then*[T, O1, O2](self: Parser[T, O1], next: Parser[T, O2]): Parser[T, O2] =
self.chain(proc (v: O1): Parser[T, O2] =
next)
proc `*`*[T, O1, O2](first: Parser[T, O1], second: Parser[T, O2]): Parser[T, (O1, O2)] =
first.chain(proc (v1: O1): Parser[T, (O1, O2)] =
second.map(proc (v2: O2): (O1, O2) =
(v1, v2)))
proc repeat0*[T, O](inner: Parser[T, O]): Parser[T, seq[O]] =
var nothing = unit[T, seq[O]](@[])
inner.chain(proc(v: O): Parser[T, seq[O]] =
repeat0(inner).map(proc(vs: seq[O]): seq[O] =
@[v] & vs)) + nothing
proc repeat1*[T, O](inner: Parser[T, O]): Parser[T, seq[O]] =
inner.chain(proc(v: O): Parser[T, seq[O]] =
repeat0(inner).map(proc(vs: seq[O]): seq[O] =
@[v] & vs))
proc leftRec*[T, O, A](inner: Parser[T, O], after: Parser[T, A], fold: proc(i: O, a: A): O): Parser[T, O] =
(inner*repeat0(after)).map(proc(ias: (O, seq[A])): O =
var (i, asx) = ias
for a in asx:
i = fold(i, a)
i)
proc lazy*[T, O](inner: proc(): Parser[T, O]): Parser[T, O] =
unit[T, tuple[]](()).chain(proc(v: tuple[]): Parser[T, O] =
inner())

View file

@ -1,7 +1,7 @@
discard """ discard """
file: "tissue710.nim" file: "tissue710.nim"
line: 8 line: 8
errorMsg: "expression '||' cannot be called" errorMsg: "undeclared identifier: '||'"
""" """
var sum = 0 var sum = 0
for x in 3..1000: for x in 3..1000:

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