Merge branch 'devel' into araq

This commit is contained in:
Andreas Rumpf 2017-11-07 11:17:20 +01:00
commit 617ba1a209
37 changed files with 353 additions and 69 deletions

View file

@ -39,5 +39,26 @@
- Added ``typetraits.$`` as an alias for ``typetraits.name``. - Added ``typetraits.$`` as an alias for ``typetraits.name``.
- ``os.getEnv`` now takes an optional ``default`` parameter that tells ``getEnv`` - ``os.getEnv`` now takes an optional ``default`` parameter that tells ``getEnv``
what to return if the environment variable does not exist. what to return if the environment variable does not exist.
- Removed PDCurses wrapper from the stdlib and published it as a separate - Removed PDCurses wrapper from the stdlib and published it as a separate
Nimble package. Nimble package.
- Bodies of ``for`` loops now get their own scope:
.. code-block:: nim
# now compiles:
for i in 0..4:
let i = i + 1
echo i
- The parsing rules of ``if`` expressions were changed so that multiple
statements are allowed in the branches. We found few code examples that
now fail because of this change, but here is one:
.. code-block:: nim
t[ti] = if exp_negative: '-' else: '+'; inc(ti)
This now needs to be written as:
.. code-block:: nim
t[ti] = (if exp_negative: '-' else: '+'); inc(ti)

View file

@ -1067,7 +1067,7 @@ proc genArrayAddr(p: PProc, n: PNode, r: var TCompRes) =
else: else:
r.res = "chckIndx($1, $2, strlen($3))-$2" % [b.res, rope(first), a.res] r.res = "chckIndx($1, $2, strlen($3))-$2" % [b.res, rope(first), a.res]
else: else:
r.res = "chckIndx($1, $2, $3.length-1)-$2" % [b.res, rope(first), a.res] r.res = "chckIndx($1, $2, $3.length+$2-1)-$2" % [b.res, rope(first), a.res]
elif first != 0: elif first != 0:
r.res = "($1)-$2" % [b.res, rope(first)] r.res = "($1)-$2" % [b.res, rope(first)]
else: else:

View file

@ -764,7 +764,10 @@ proc semCaptureSym*(s, owner: PSym) =
var o = owner.skipGenericOwner var o = owner.skipGenericOwner
while o.kind != skModule and o != nil: while o.kind != skModule and o != nil:
if s.owner == o: if s.owner == o:
owner.typ.callConv = ccClosure if owner.typ.callConv in {ccClosure, ccDefault} or owner.kind == skIterator:
owner.typ.callConv = ccClosure
else:
discard "do not produce an error here, but later"
#echo "computing .closure for ", owner.name.s, " ", owner.info, " because of ", s.name.s #echo "computing .closure for ", owner.name.s, " ", owner.info, " because of ", s.name.s
o = o.skipGenericOwner o = o.skipGenericOwner
# since the analysis is not entirely correct, we don't set 'tfCapturesEnv' # since the analysis is not entirely correct, we don't set 'tfCapturesEnv'

View file

@ -52,17 +52,19 @@ proc lookupParam(params, dest: PNode): PSym =
if params[i].kind == nkSym and params[i].sym.name.id == dest.ident.id: if params[i].kind == nkSym and params[i].sym.name.id == dest.ident.id:
return params[i].sym return params[i].sym
proc liftLocalsIfRequested*(prc: PSym) = proc liftLocalsIfRequested*(prc: PSym; n: PNode): PNode =
let liftDest = getPragmaVal(prc.ast, wLiftLocals) let liftDest = getPragmaVal(prc.ast, wLiftLocals)
if liftDest == nil: return if liftDest == nil: return n
let partialParam = lookupParam(prc.typ.n, liftDest) let partialParam = lookupParam(prc.typ.n, liftDest)
if partialParam.isNil: if partialParam.isNil:
localError(liftDest.info, "'$1' is not a parameter of '$2'" % localError(liftDest.info, "'$1' is not a parameter of '$2'" %
[$liftDest, prc.name.s]) [$liftDest, prc.name.s])
return return n
let objType = partialParam.typ.skipTypes(abstractPtrs) let objType = partialParam.typ.skipTypes(abstractPtrs)
if objType.kind != tyObject or tfPartial notin objType.flags: if objType.kind != tyObject or tfPartial notin objType.flags:
localError(liftDest.info, "parameter '$1' is not a pointer to a partial object" % $liftDest) localError(liftDest.info, "parameter '$1' is not a pointer to a partial object" % $liftDest)
return return n
var c = Ctx(partialParam: partialParam, objType: objType) var c = Ctx(partialParam: partialParam, objType: objType)
liftLocals(prc.ast, bodyPos, c) let w = newTree(nkStmtList, n)
liftLocals(w, 0, c)
result = w[0]

View file

@ -98,6 +98,7 @@ proc resolveDollar(project, source, pkg, subdir: string; info: TLineInfo): strin
proc scriptableImport(pkg, sub: string; info: TLineInfo): string = proc scriptableImport(pkg, sub: string; info: TLineInfo): string =
result = resolveDollar(gProjectFull, info.toFullPath(), pkg, sub, info) result = resolveDollar(gProjectFull, info.toFullPath(), pkg, sub, info)
if result.isNil: result = ""
proc lookupPackage(pkg, subdir: PNode): string = proc lookupPackage(pkg, subdir: PNode): string =
let sub = if subdir != nil: renderTree(subdir, {renderNoComments}).replace(" ") else: "" let sub = if subdir != nil: renderTree(subdir, {renderNoComments}).replace(" ") else: ""
@ -166,7 +167,8 @@ proc checkModuleName*(n: PNode; doLocalError=true): int32 =
let fullPath = findModule(modulename, n.info.toFullPath) let fullPath = findModule(modulename, n.info.toFullPath)
if fullPath.len == 0: if fullPath.len == 0:
if doLocalError: if doLocalError:
localError(n.info, errCannotOpenFile, modulename) let m = if modulename.len > 0: modulename else: $n
localError(n.info, errCannotOpenFile, m)
result = InvalidFileIDX result = InvalidFileIDX
else: else:
result = fullPath.fileInfoIdx result = fullPath.fileInfoIdx

View file

@ -61,7 +61,7 @@ type
errBaseTypeMustBeOrdinal, errInheritanceOnlyWithNonFinalObjects, errBaseTypeMustBeOrdinal, errInheritanceOnlyWithNonFinalObjects,
errInheritanceOnlyWithEnums, errIllegalRecursionInTypeX, errInheritanceOnlyWithEnums, errIllegalRecursionInTypeX,
errCannotInstantiateX, errExprHasNoAddress, errXStackEscape, errCannotInstantiateX, errExprHasNoAddress, errXStackEscape,
errVarForOutParamNeeded, errVarForOutParamNeededX,
errPureTypeMismatch, errTypeMismatch, errButExpected, errButExpectedX, errPureTypeMismatch, errTypeMismatch, errButExpected, errButExpectedX,
errAmbiguousCallXYZ, errWrongNumberOfArguments, errAmbiguousCallXYZ, errWrongNumberOfArguments,
errWrongNumberOfArgumentsInCall, errWrongNumberOfArgumentsInCall,
@ -268,7 +268,7 @@ const
errCannotInstantiateX: "cannot instantiate: \'$1\'", errCannotInstantiateX: "cannot instantiate: \'$1\'",
errExprHasNoAddress: "expression has no address", errExprHasNoAddress: "expression has no address",
errXStackEscape: "address of '$1' may not escape its stack frame", errXStackEscape: "address of '$1' may not escape its stack frame",
errVarForOutParamNeeded: "for a \'var\' type a variable needs to be passed", errVarForOutParamNeededX: "for a \'var\' type a variable needs to be passed; but '$1' is immutable",
errPureTypeMismatch: "type mismatch", errPureTypeMismatch: "type mismatch",
errTypeMismatch: "type mismatch: got (", errTypeMismatch: "type mismatch: got (",
errButExpected: "but expected one of: ", errButExpected: "but expected one of: ",

View file

@ -785,21 +785,58 @@ proc parseIfExpr(p: var TParser, kind: TNodeKind): PNode =
#| 'else' colcom expr #| 'else' colcom expr
#| ifExpr = 'if' condExpr #| ifExpr = 'if' condExpr
#| whenExpr = 'when' condExpr #| whenExpr = 'when' condExpr
result = newNodeP(kind, p) when true:
while true: result = newNodeP(kind, p)
getTok(p) # skip `if`, `elif` while true:
var branch = newNodeP(nkElifExpr, p) getTok(p) # skip `if`, `when`, `elif`
var branch = newNodeP(nkElifExpr, p)
optInd(p, branch)
addSon(branch, parseExpr(p))
colcom(p, branch)
addSon(branch, parseStmt(p))
skipComment(p, branch)
addSon(result, branch)
if p.tok.tokType != tkElif: break # or not sameOrNoInd(p): break
if p.tok.tokType == tkElse: # and sameOrNoInd(p):
var branch = newNodeP(nkElseExpr, p)
eat(p, tkElse)
colcom(p, branch)
addSon(branch, parseStmt(p))
addSon(result, branch)
else:
var
b: PNode
wasIndented = false
result = newNodeP(kind, p)
getTok(p)
let branch = newNodeP(nkElifExpr, p)
addSon(branch, parseExpr(p)) addSon(branch, parseExpr(p))
colcom(p, branch) colcom(p, branch)
let oldInd = p.currInd
if realInd(p):
p.currInd = p.tok.indent
wasIndented = true
echo result.info, " yes ", p.currInd
addSon(branch, parseExpr(p)) addSon(branch, parseExpr(p))
optInd(p, branch) result.add branch
addSon(result, branch) while sameInd(p) or not wasIndented:
if p.tok.tokType != tkElif: break case p.tok.tokType
var branch = newNodeP(nkElseExpr, p) of tkElif:
eat(p, tkElse) b = newNodeP(nkElifExpr, p)
colcom(p, branch) getTok(p)
addSon(branch, parseExpr(p)) optInd(p, b)
addSon(result, branch) addSon(b, parseExpr(p))
of tkElse:
b = newNodeP(nkElseExpr, p)
getTok(p)
else: break
colcom(p, b)
addSon(b, parseStmt(p))
addSon(result, b)
if b.kind == nkElseExpr: break
if wasIndented:
p.currInd = oldInd
proc parsePragma(p: var TParser): PNode = proc parsePragma(p: var TParser): PNode =
#| pragma = '{.' optInd (exprColonExpr comma?)* optPar ('.}' | '}') #| pragma = '{.' optInd (exprColonExpr comma?)* optPar ('.}' | '}')
@ -2036,8 +2073,13 @@ proc parseStmt(p: var TParser): PNode =
if a.kind != nkEmpty: if a.kind != nkEmpty:
addSon(result, a) addSon(result, a)
else: else:
parMessage(p, errExprExpected, p.tok) # This is done to make the new 'if' expressions work better.
getTok(p) # XXX Eventually we need to be more strict here.
if p.tok.tokType notin {tkElse, tkElif}:
parMessage(p, errExprExpected, p.tok)
getTok(p)
else:
break
if not p.hasProgress and p.tok.tokType == tkEof: break if not p.hasProgress and p.tok.tokType == tkEof: break
else: else:
# the case statement is only needed for better error messages: # the case statement is only needed for better error messages:

View file

@ -465,7 +465,7 @@ proc newHiddenAddrTaken(c: PContext, n: PNode): PNode =
result = newNodeIT(nkHiddenAddr, n.info, makeVarType(c, n.typ)) result = newNodeIT(nkHiddenAddr, n.info, makeVarType(c, n.typ))
addSon(result, n) addSon(result, n)
if isAssignable(c, n) notin {arLValue, arLocalLValue}: if isAssignable(c, n) notin {arLValue, arLocalLValue}:
localError(n.info, errVarForOutParamNeeded) localError(n.info, errVarForOutParamNeededX, $n)
proc analyseIfAddressTaken(c: PContext, n: PNode): PNode = proc analyseIfAddressTaken(c: PContext, n: PNode): PNode =
result = n result = n
@ -509,9 +509,10 @@ proc analyseIfAddressTakenInCall(c: PContext, n: PNode) =
for i in countup(1, sonsLen(n) - 1): for i in countup(1, sonsLen(n) - 1):
if i < sonsLen(t) and t.sons[i] != nil and if i < sonsLen(t) and t.sons[i] != nil and
skipTypes(t.sons[i], abstractInst-{tyTypeDesc}).kind == tyVar: skipTypes(t.sons[i], abstractInst-{tyTypeDesc}).kind == tyVar:
if isAssignable(c, n.sons[i]) notin {arLValue, arLocalLValue}: let it = n[i]
if n.sons[i].kind != nkHiddenAddr: if isAssignable(c, it) notin {arLValue, arLocalLValue}:
localError(n.sons[i].info, errVarForOutParamNeeded) if it.kind != nkHiddenAddr:
localError(it.info, errVarForOutParamNeededX, $it)
return return
for i in countup(1, sonsLen(n) - 1): for i in countup(1, sonsLen(n) - 1):
if n.sons[i].kind == nkHiddenCallConv: if n.sons[i].kind == nkHiddenCallConv:

View file

@ -335,8 +335,10 @@ proc semGenericStmt(c: PContext, n: PNode,
n.sons[L - 2] = semGenericStmt(c, n.sons[L-2], flags, ctx) n.sons[L - 2] = semGenericStmt(c, n.sons[L-2], flags, ctx)
for i in countup(0, L - 3): for i in countup(0, L - 3):
addTempDecl(c, n.sons[i], skForVar) addTempDecl(c, n.sons[i], skForVar)
openScope(c)
n.sons[L - 1] = semGenericStmt(c, n.sons[L-1], flags, ctx) n.sons[L - 1] = semGenericStmt(c, n.sons[L-1], flags, ctx)
closeScope(c) closeScope(c)
closeScope(c)
of nkBlockStmt, nkBlockExpr, nkBlockType: of nkBlockStmt, nkBlockExpr, nkBlockType:
checkSonsLen(n, 2) checkSonsLen(n, 2)
openScope(c) openScope(c)

View file

@ -697,7 +697,9 @@ proc semForVars(c: PContext, n: PNode): PNode =
if sfGenSym notin v.flags and not isDiscardUnderscore(v): if sfGenSym notin v.flags and not isDiscardUnderscore(v):
addForVarDecl(c, v) addForVarDecl(c, v)
inc(c.p.nestedLoopCounter) inc(c.p.nestedLoopCounter)
openScope(c)
n.sons[length-1] = semStmt(c, n.sons[length-1]) n.sons[length-1] = semStmt(c, n.sons[length-1])
closeScope(c)
dec(c.p.nestedLoopCounter) dec(c.p.nestedLoopCounter)
proc implicitIterator(c: PContext, it: string, arg: PNode): PNode = proc implicitIterator(c: PContext, it: string, arg: PNode): PNode =

View file

@ -366,8 +366,10 @@ proc semTemplBody(c: var TemplCtx, n: PNode): PNode =
n.sons[L-2] = semTemplBody(c, n.sons[L-2]) n.sons[L-2] = semTemplBody(c, n.sons[L-2])
for i in countup(0, L - 3): for i in countup(0, L - 3):
addLocalDecl(c, n.sons[i], skForVar) addLocalDecl(c, n.sons[i], skForVar)
openScope(c)
n.sons[L-1] = semTemplBody(c, n.sons[L-1]) n.sons[L-1] = semTemplBody(c, n.sons[L-1])
closeScope(c) closeScope(c)
closeScope(c)
of nkBlockStmt, nkBlockExpr, nkBlockType: of nkBlockStmt, nkBlockExpr, nkBlockType:
checkSonsLen(n, 2) checkSonsLen(n, 2)
openScope(c) openScope(c)

View file

@ -1057,9 +1057,10 @@ proc typeRelImpl(c: var TCandidate, f, aOrig: PType,
else: isNone else: isNone
of tyUserTypeClass, tyUserTypeClassInst: of tyUserTypeClass, tyUserTypeClassInst:
if c.c.matchedConcept != nil: if c.c.matchedConcept != nil and c.c.matchedConcept.depth <= 4:
# consider this: 'var g: Node' *within* a concept where 'Node' # consider this: 'var g: Node' *within* a concept where 'Node'
# is a concept too (tgraph) # is a concept too (tgraph)
inc c.c.matchedConcept.depth
let x = typeRel(c, a, f, flags + {trDontBind}) let x = typeRel(c, a, f, flags + {trDontBind})
if x >= isGeneric: if x >= isGeneric:
return isGeneric return isGeneric

View file

@ -978,7 +978,7 @@ proc transformBody*(module: PSym, n: PNode, prc: PSym): PNode =
liftDefer(c, result) liftDefer(c, result)
#result = liftLambdas(prc, result) #result = liftLambdas(prc, result)
when useEffectSystem: trackProc(prc, result) when useEffectSystem: trackProc(prc, result)
liftLocalsIfRequested(prc) result = liftLocalsIfRequested(prc, result)
if c.needsDestroyPass and newDestructors: if c.needsDestroyPass and newDestructors:
result = injectDestructorCalls(prc, result) result = injectDestructorCalls(prc, result)
incl(result.flags, nfTransf) incl(result.flags, nfTransf)

View file

@ -98,7 +98,6 @@ path="$lib/pure"
clang.options.linker = "-landroid-glob" clang.options.linker = "-landroid-glob"
clang.cpp.options.linker = "-landroid-glob" clang.cpp.options.linker = "-landroid-glob"
tcc.options.linker = "-landroid-glob" tcc.options.linker = "-landroid-glob"
define:"useShPath:/system/bin/sh"
@end @end
@end @end

View file

@ -316,7 +316,7 @@ factor.
immediate pragma immediate pragma
---------------- ----------------
See `Ordinary vs immediate templates`_. See `Typed vs untyped parameters`_.
compilation option pragmas compilation option pragmas

View file

@ -262,7 +262,7 @@ proc socket*(domain: Domain = AF_INET, typ: SockType = SOCK_STREAM,
# TODO: Perhaps this should just raise EOS when an error occurs. # TODO: Perhaps this should just raise EOS when an error occurs.
when defined(Windows): when defined(Windows):
result = newTSocket(winlean.socket(ord(domain), ord(typ), ord(protocol)), buffered) result = newTSocket(winlean.socket(cint(domain), cint(typ), cint(protocol)), buffered)
else: else:
result = newTSocket(posix.socket(toInt(domain), toInt(typ), toInt(protocol)), buffered) result = newTSocket(posix.socket(toInt(domain), toInt(typ), toInt(protocol)), buffered)

View file

@ -798,7 +798,8 @@ proc renderImage(d: PDoc, n: PRstNode, result: var string) =
if arg.valid: if arg.valid:
let htmlOut = if isObject: let htmlOut = if isObject:
"<object data=\"$1\" type=\"image/svg+xml\"$2 >" & content & "</object>" "<object data=\"$1\" type=\"image/svg+xml\"$2 >" & content & "</object>"
else: "<img src=\"$1\"$2 />" else:
"<img src=\"$1\"$2 />"
dispA(d.target, result, htmlOut, "\\includegraphics$2{$1}", dispA(d.target, result, htmlOut, "\\includegraphics$2{$1}",
[arg, options]) [arg, options])
if len(n) >= 3: renderRstToOut(d, n.sons[2], result) if len(n) >= 3: renderRstToOut(d, n.sons[2], result)

View file

@ -767,7 +767,9 @@ elif not defined(useNimRtl):
var sysCommand: string var sysCommand: string
var sysArgsRaw: seq[string] var sysArgsRaw: seq[string]
if poEvalCommand in options: if poEvalCommand in options:
const useShPath {.strdefine.} = "/bin/sh" const useShPath {.strdefine.} =
when not defined(android): "/bin/sh"
else: "/system/bin/sh"
sysCommand = useShPath sysCommand = useShPath
sysArgsRaw = @[sysCommand, "-c", command] sysArgsRaw = @[sysCommand, "-c", command]
assert args.len == 0, "`args` has to be empty when using poEvalCommand." assert args.len == 0, "`args` has to be empty when using poEvalCommand."

View file

@ -17,6 +17,7 @@
## runtime efficiency. ## runtime efficiency.
include "system/inclrtl" include "system/inclrtl"
import streams
{.deadCodeElim: on.} {.deadCodeElim: on.}
@ -130,7 +131,7 @@ proc insertInCache(s: string, tree: Rope): Rope =
result.left = t result.left = t
t.right = nil t.right = nil
proc rope*(s: string): Rope {.rtl, extern: "nro$1Str".} = proc rope*(s: string = nil): Rope {.rtl, extern: "nro$1Str".} =
## Converts a string to a rope. ## Converts a string to a rope.
if s.len == 0: if s.len == 0:
result = nil result = nil
@ -242,10 +243,13 @@ proc write*(f: File, r: Rope) {.rtl, extern: "nro$1".} =
## writes a rope to a file. ## writes a rope to a file.
for s in leaves(r): write(f, s) for s in leaves(r): write(f, s)
proc write*(s: Stream, r: Rope) {.rtl, extern: "nroWriteStream".} =
## writes a rope to a stream.
for rs in leaves(r): write(s, rs)
proc `$`*(r: Rope): string {.rtl, extern: "nroToString".}= proc `$`*(r: Rope): string {.rtl, extern: "nroToString".}=
## converts a rope back to a string. ## converts a rope back to a string.
result = newString(r.len) result = newStringOfCap(r.len)
setLen(result, 0)
for s in leaves(r): add(result, s) for s in leaves(r): add(result, s)
when false: when false:

View file

@ -578,15 +578,46 @@ iterator split*(s: string, seps: set[char] = Whitespace,
else: else:
splitCommon(s, seps, maxsplit, 1) splitCommon(s, seps, maxsplit, 1)
iterator splitWhitespace*(s: string): string = iterator splitWhitespace*(s: string, maxsplit: int = -1): string =
## Splits at whitespace. ## Splits the string ``s`` at whitespace stripping leading and trailing
oldSplit(s, Whitespace, -1) ## whitespace if necessary. If ``maxsplit`` is specified and is positive,
## no more than ``maxsplit`` splits is made.
##
## The following code:
##
## .. code-block:: nim
## let s = " foo \t bar baz "
## for ms in [-1, 1, 2, 3]:
## echo "------ maxsplit = ", ms, ":"
## for item in s.splitWhitespace(maxsplit=ms):
## echo '"', item, '"'
##
## ...results in:
##
## .. code-block::
## ------ maxsplit = -1:
## "foo"
## "bar"
## "baz"
## ------ maxsplit = 1:
## "foo"
## "bar baz "
## ------ maxsplit = 2:
## "foo"
## "bar"
## "baz "
## ------ maxsplit = 3:
## "foo"
## "bar"
## "baz"
##
oldSplit(s, Whitespace, maxsplit)
proc splitWhitespace*(s: string): seq[string] {.noSideEffect, proc splitWhitespace*(s: string, maxsplit: int = -1): seq[string] {.noSideEffect,
rtl, extern: "nsuSplitWhitespace".} = rtl, extern: "nsuSplitWhitespace".} =
## The same as the `splitWhitespace <#splitWhitespace.i,string>`_ ## The same as the `splitWhitespace <#splitWhitespace.i,string,int>`_
## iterator, but is a proc that returns a sequence of substrings. ## iterator, but is a proc that returns a sequence of substrings.
accumulateResult(splitWhitespace(s)) accumulateResult(splitWhitespace(s, maxsplit))
iterator split*(s: string, sep: char, maxsplit: int = -1): string = iterator split*(s: string, sep: char, maxsplit: int = -1): string =
## Splits the string `s` into substrings using a single separator. ## Splits the string `s` into substrings using a single separator.
@ -671,7 +702,7 @@ iterator rsplit*(s: string, seps: set[char] = Whitespace,
maxsplit: int = -1): string = maxsplit: int = -1): string =
## Splits the string `s` into substrings from the right using a ## Splits the string `s` into substrings from the right using a
## string separator. Works exactly the same as `split iterator ## string separator. Works exactly the same as `split iterator
## <#split.i,string,char>`_ except in reverse order. ## <#split.i,string,char,int>`_ except in reverse order.
## ##
## .. code-block:: nim ## .. code-block:: nim
## for piece in "foo bar".rsplit(WhiteSpace): ## for piece in "foo bar".rsplit(WhiteSpace):
@ -691,7 +722,7 @@ iterator rsplit*(s: string, sep: char,
maxsplit: int = -1): string = maxsplit: int = -1): string =
## Splits the string `s` into substrings from the right using a ## Splits the string `s` into substrings from the right using a
## string separator. Works exactly the same as `split iterator ## string separator. Works exactly the same as `split iterator
## <#split.i,string,char>`_ except in reverse order. ## <#split.i,string,char,int>`_ except in reverse order.
## ##
## .. code-block:: nim ## .. code-block:: nim
## for piece in "foo:bar".rsplit(':'): ## for piece in "foo:bar".rsplit(':'):
@ -710,7 +741,7 @@ iterator rsplit*(s: string, sep: string, maxsplit: int = -1,
keepSeparators: bool = false): string = keepSeparators: bool = false): string =
## Splits the string `s` into substrings from the right using a ## Splits the string `s` into substrings from the right using a
## string separator. Works exactly the same as `split iterator ## string separator. Works exactly the same as `split iterator
## <#split.i,string,string>`_ except in reverse order. ## <#split.i,string,string,int>`_ except in reverse order.
## ##
## .. code-block:: nim ## .. code-block:: nim
## for piece in "foothebar".rsplit("the"): ## for piece in "foothebar".rsplit("the"):
@ -791,13 +822,13 @@ proc countLines*(s: string): int {.noSideEffect,
proc split*(s: string, seps: set[char] = Whitespace, maxsplit: int = -1): seq[string] {. proc split*(s: string, seps: set[char] = Whitespace, maxsplit: int = -1): seq[string] {.
noSideEffect, rtl, extern: "nsuSplitCharSet".} = noSideEffect, rtl, extern: "nsuSplitCharSet".} =
## The same as the `split iterator <#split.i,string,set[char]>`_, but is a ## The same as the `split iterator <#split.i,string,set[char],int>`_, but is a
## proc that returns a sequence of substrings. ## proc that returns a sequence of substrings.
accumulateResult(split(s, seps, maxsplit)) accumulateResult(split(s, seps, maxsplit))
proc split*(s: string, sep: char, maxsplit: int = -1): seq[string] {.noSideEffect, proc split*(s: string, sep: char, maxsplit: int = -1): seq[string] {.noSideEffect,
rtl, extern: "nsuSplitChar".} = rtl, extern: "nsuSplitChar".} =
## The same as the `split iterator <#split.i,string,char>`_, but is a proc ## The same as the `split iterator <#split.i,string,char,int>`_, but is a proc
## that returns a sequence of substrings. ## that returns a sequence of substrings.
accumulateResult(split(s, sep, maxsplit)) accumulateResult(split(s, sep, maxsplit))
@ -806,7 +837,7 @@ proc split*(s: string, sep: string, maxsplit: int = -1): seq[string] {.noSideEff
## Splits the string `s` into substrings using a string separator. ## Splits the string `s` into substrings using a string separator.
## ##
## Substrings are separated by the string `sep`. This is a wrapper around the ## Substrings are separated by the string `sep`. This is a wrapper around the
## `split iterator <#split.i,string,string>`_. ## `split iterator <#split.i,string,string,int>`_.
doAssert(sep.len > 0) doAssert(sep.len > 0)
accumulateResult(split(s, sep, maxsplit)) accumulateResult(split(s, sep, maxsplit))
@ -814,7 +845,7 @@ proc split*(s: string, sep: string, maxsplit: int = -1): seq[string] {.noSideEff
proc rsplit*(s: string, seps: set[char] = Whitespace, proc rsplit*(s: string, seps: set[char] = Whitespace,
maxsplit: int = -1): seq[string] maxsplit: int = -1): seq[string]
{.noSideEffect, rtl, extern: "nsuRSplitCharSet".} = {.noSideEffect, rtl, extern: "nsuRSplitCharSet".} =
## The same as the `rsplit iterator <#rsplit.i,string,set[char]>`_, but is a ## The same as the `rsplit iterator <#rsplit.i,string,set[char],int>`_, but is a
## proc that returns a sequence of substrings. ## proc that returns a sequence of substrings.
## ##
## A possible common use case for `rsplit` is path manipulation, ## A possible common use case for `rsplit` is path manipulation,
@ -836,7 +867,7 @@ proc rsplit*(s: string, seps: set[char] = Whitespace,
proc rsplit*(s: string, sep: char, maxsplit: int = -1): seq[string] proc rsplit*(s: string, sep: char, maxsplit: int = -1): seq[string]
{.noSideEffect, rtl, extern: "nsuRSplitChar".} = {.noSideEffect, rtl, extern: "nsuRSplitChar".} =
## The same as the `split iterator <#rsplit.i,string,char>`_, but is a proc ## The same as the `rsplit iterator <#rsplit.i,string,char,int>`_, but is a proc
## that returns a sequence of substrings. ## that returns a sequence of substrings.
## ##
## A possible common use case for `rsplit` is path manipulation, ## A possible common use case for `rsplit` is path manipulation,
@ -858,7 +889,7 @@ proc rsplit*(s: string, sep: char, maxsplit: int = -1): seq[string]
proc rsplit*(s: string, sep: string, maxsplit: int = -1): seq[string] proc rsplit*(s: string, sep: string, maxsplit: int = -1): seq[string]
{.noSideEffect, rtl, extern: "nsuRSplitString".} = {.noSideEffect, rtl, extern: "nsuRSplitString".} =
## The same as the `split iterator <#rsplit.i,string,string>`_, but is a proc ## The same as the `rsplit iterator <#rsplit.i,string,string,int>`_, but is a proc
## that returns a sequence of substrings. ## that returns a sequence of substrings.
## ##
## A possible common use case for `rsplit` is path manipulation, ## A possible common use case for `rsplit` is path manipulation,
@ -2599,6 +2630,12 @@ bar
doAssert s.split(' ', maxsplit=1) == @["", "this is an example "] doAssert s.split(' ', maxsplit=1) == @["", "this is an example "]
doAssert s.split(" ", maxsplit=4) == @["", "this", "is", "an", "example "] doAssert s.split(" ", maxsplit=4) == @["", "this", "is", "an", "example "]
doAssert s.splitWhitespace() == @["this", "is", "an", "example"]
doAssert s.splitWhitespace(maxsplit=1) == @["this", "is an example "]
doAssert s.splitWhitespace(maxsplit=2) == @["this", "is", "an example "]
doAssert s.splitWhitespace(maxsplit=3) == @["this", "is", "an", "example "]
doAssert s.splitWhitespace(maxsplit=4) == @["this", "is", "an", "example"]
block: # formatEng tests block: # formatEng tests
doAssert formatEng(0, 2, trim=false) == "0.00" doAssert formatEng(0, 2, trim=false) == "0.00"
doAssert formatEng(0, 2) == "0" doAssert formatEng(0, 2) == "0"

View file

@ -3531,14 +3531,14 @@ proc `[]`*[Idx, T, U, V](a: array[Idx, T], x: HSlice[U, V]): seq[T] =
let xa = a ^^ x.a let xa = a ^^ x.a
let L = (a ^^ x.b) - xa + 1 let L = (a ^^ x.b) - xa + 1
result = newSeq[T](L) result = newSeq[T](L)
for i in 0..<L: result[i] = a[Idx(i + xa + int low(a))] for i in 0..<L: result[i] = a[Idx(i + xa)]
proc `[]=`*[Idx, T, U, V](a: var array[Idx, T], x: HSlice[U, V], b: openArray[T]) = proc `[]=`*[Idx, T, U, V](a: var array[Idx, T], x: HSlice[U, V], b: openArray[T]) =
## slice assignment for arrays. ## slice assignment for arrays.
let xa = a ^^ x.a let xa = a ^^ x.a
let L = (a ^^ x.b) - xa + 1 let L = (a ^^ x.b) - xa + 1
if L == b.len: if L == b.len:
for i in 0..<L: a[Idx(i + xa + int low(a))] = b[i] for i in 0..<L: a[Idx(i + xa)] = b[i]
else: else:
sysFatal(RangeError, "different lengths for slice assignment") sysFatal(RangeError, "different lengths for slice assignment")
@ -3565,18 +3565,20 @@ proc `[]=`*[T, U, V](s: var seq[T], x: HSlice[U, V], b: openArray[T]) =
else: else:
spliceImpl(s, a, L, b) spliceImpl(s, a, L, b)
proc `[]`*[T](s: openArray[T]; i: BackwardsIndex): T {.inline.} = s[s.len - int(i)] proc `[]`*[T](s: openArray[T]; i: BackwardsIndex): T {.inline.} =
system.`[]`(s, s.len - int(i))
proc `[]`*[Idx, T](a: array[Idx, T]; i: BackwardsIndex): T {.inline.} = proc `[]`*[Idx, T](a: array[Idx, T]; i: BackwardsIndex): T {.inline.} =
a[Idx(a.len - int(i) + int low(a))] a[Idx(a.len - int(i) + int low(a))]
proc `[]`*(s: string; i: BackwardsIndex): char {.inline.} = s[s.len - int(i)] proc `[]`*(s: string; i: BackwardsIndex): char {.inline.} = s[s.len - int(i)]
proc `[]`*[T](s: var openArray[T]; i: BackwardsIndex): var T {.inline.} = proc `[]`*[T](s: var openArray[T]; i: BackwardsIndex): var T {.inline.} =
s[s.len - int(i)] system.`[]`(s, s.len - int(i))
proc `[]`*[Idx, T](a: var array[Idx, T]; i: BackwardsIndex): var T {.inline.} = proc `[]`*[Idx, T](a: var array[Idx, T]; i: BackwardsIndex): var T {.inline.} =
a[Idx(a.len - int(i) + int low(a))] a[Idx(a.len - int(i) + int low(a))]
proc `[]=`*[T](s: var openArray[T]; i: BackwardsIndex; x: T) {.inline.} = proc `[]=`*[T](s: var openArray[T]; i: BackwardsIndex; x: T) {.inline.} =
s[s.len - int(i)] = x system.`[]=`(s, s.len - int(i), x)
proc `[]=`*[Idx, T](a: var array[Idx, T]; i: BackwardsIndex; x: T) {.inline.} = proc `[]=`*[Idx, T](a: var array[Idx, T]; i: BackwardsIndex; x: T) {.inline.} =
a[Idx(a.len - int(i) + int low(a))] = x a[Idx(a.len - int(i) + int low(a))] = x
proc `[]=`*(s: var string; i: BackwardsIndex; x: char) {.inline.} = proc `[]=`*(s: var string; i: BackwardsIndex; x: char) {.inline.} =

View file

@ -509,7 +509,7 @@ proc nimParseBiggestFloat(s: string, number: var BiggestFloat,
# insert exponent # insert exponent
t[ti] = 'E'; inc(ti) t[ti] = 'E'; inc(ti)
t[ti] = if exp_negative: '-' else: '+'; inc(ti) t[ti] = (if exp_negative: '-' else: '+'); inc(ti)
inc(ti, 3) inc(ti, 3)
# insert adjusted exponent # insert adjusted exponent

View file

@ -127,6 +127,7 @@ However, if you are short on time, you can just run the tests specific to your
changes by only running the corresponding categories of tests. Travis CI verifies changes by only running the corresponding categories of tests. Travis CI verifies
that all tests pass before allowing the pull request to be accepted, so only that all tests pass before allowing the pull request to be accepted, so only
running specific tests should be harmless. running specific tests should be harmless.
Integration tests should go in ``tests/untestable``.
If you're looking for ways to contribute, please look at our [issue tracker][nim-issues]. If you're looking for ways to contribute, please look at our [issue tracker][nim-issues].
There are always plenty of issues labelled [``Easy``][nim-issues-easy]; these should There are always plenty of issues labelled [``Easy``][nim-issues-easy]; these should

View file

@ -4,7 +4,8 @@ discard """
3 3
@[(Field0: 1, Field1: 2), (Field0: 3, Field1: 5)] @[(Field0: 1, Field1: 2), (Field0: 3, Field1: 5)]
2 2
@[a, new one, c]''' @[a, new one, c]
@[1, 2, 3]'''
""" """
proc foo[T](x, y: T): T = x proc foo[T](x, y: T): T = x
@ -35,3 +36,8 @@ useOpenarray([1, 2, 3])
var z = @["a", "b", "c"] var z = @["a", "b", "c"]
mutOpenarray(z) mutOpenarray(z)
echo z echo z
# bug #6675
var y: array[1..5, int] = [1,2,3,4,5]
y[3..5] = [1, 2, 3]
echo y[3..5]

View file

@ -0,0 +1,46 @@
###############################
#### part from Arraymancer
type
MetadataArray* = object
data*: array[8, int]
len*: int
# Commenting the converter removes the error "lib/system.nim(3536, 3) Error: for a 'var' type a variable needs to be passed"
converter toMetadataArray*(se: varargs[int]): MetadataArray {.inline.} =
result.len = se.len
for i in 0..<se.len:
result.data[i] = se[i]
when NimVersion >= "0.17.3":
type Index = int or BackwardsIndex
template `^^`(s, i: untyped): untyped =
when i is BackwardsIndex:
s.len - int(i)
else: i
else:
type Index = int
template `^^`(s, i: untyped): untyped =
i
## With Nim devel from the start of the week (~Oct30) I managed to trigger "lib/system.nim(3536, 4) Error: expression has no address"
## but I can't anymore after updating Nim (Nov5)
## Now commenting this plain compiles and removes the error "lib/system.nim(3536, 3) Error: for a 'var' type a variable needs to be passed"
proc `[]`*(a: var MetadataArray, idx: Index): var int {.inline.} =
a.data[a ^^ idx]
##############################
### Completely unrelated lib that triggers the issue
type
MySeq[T] = ref object
data: seq[T]
proc test[T](sx: MySeq[T]) =
# Removing the backward index removes the error "lib/system.nim(3536, 3) Error: for a 'var' type a variable needs to be passed"
echo sx.data[^1] # error here
let s = MySeq[int](data: @[1, 2, 3])
s.test()

View file

@ -4,6 +4,7 @@ discard """
OK AF_INET OK AF_INET
OK AF_INET6 OK AF_INET6
''' '''
disabled: "travis"
""" """
import import

View file

@ -0,0 +1,12 @@
discard """
line: 9
errormsg: "illegal capture 'x'"
"""
proc outer(arg: string) =
var x = 0
proc inner {.inline.} =
echo "inner", x
inner()
outer("abc")

View file

@ -0,0 +1,13 @@
# bug #6691
type
ConceptA = concept c
ConceptB = concept c
c.myProc(ConceptA)
Obj = object
proc myProc(obj: Obj, x: ConceptA) = discard
echo Obj is ConceptB

44
tests/js/tarrayboundscheck.nim Executable file
View file

@ -0,0 +1,44 @@
discard """
output: '''idx out of bounds: -1
month out of bounds: 0
Jan
Feb
Mar
Apr
May
Jun
Jul
Aug
Sep
Oct
Nov
Dec
month out of bounds: 13
idx out of bounds: 14
'''
"""
{.push boundChecks:on.}
# see issue #6532:
# js backend 0.17.3: array bounds check for non zero based arrays is buggy
proc test_arrayboundscheck() =
var months: array[1..12, string] =
["Jan", "Feb", "Mar", "Apr", "May", "Jun",
"Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
var indices = [0,1,2,3,4,5,6,7,8,9,10,11,12,13]
for i in -1 .. 14:
try:
let idx = indices[i]
try:
echo months[idx]
except:
echo "month out of bounds: ", idx
except:
echo "idx out of bounds: ", i
test_arrayboundscheck()
{.pop.}

View file

@ -1,6 +1,6 @@
discard """ discard """
line: 24 line: 24
errormsg: "expression expected, but found 'keyword else'" errormsg: "invalid indentation"
""" """
import macros import macros
@ -11,7 +11,7 @@ var x = if 4 != 5:
else: else:
"no" "no"
macro mymacro(n): untyped {.immediate.} = macro mymacro(n, b): untyped =
discard discard
mymacro: mymacro:

View file

@ -32,3 +32,21 @@ let other = x:
echo "no" echo "no"
let outer = y(5): let outer = y(5):
echo "yes" echo "yes"
# bug #6609
type
TextureInternalFormat = enum RED, RGB, RGBA
const channels = 4
let format =
if channels == 1:
TextureInternalFormat.RED
elif channels == 3:
TextureInternalFormat.RGB
elif channels == 4:
TextureInternalFormat.RGBA
else:
echo "Texture Format Unknown, assuming RGB" #This echo causes an error
TextureInternalFormat.RGB

View file

@ -2,6 +2,7 @@ discard """
cmd: "nim c --threads:on -d:ssl $file" cmd: "nim c --threads:on -d:ssl $file"
exitcode: 0 exitcode: 0
output: "OK" output: "OK"
disabled: "travis"
""" """
import strutils import strutils

View file

@ -2,6 +2,7 @@ discard """
cmd: "nim c --threads:on $file" cmd: "nim c --threads:on $file"
exitcode: 0 exitcode: 0
output: "OK" output: "OK"
disabled: "travis"
""" """
import os, net, nativesockets, asyncdispatch import os, net, nativesockets, asyncdispatch

View file

@ -418,8 +418,9 @@ proc `&?.`(a, b: string): string =
proc processSingleTest(r: var TResults, cat: Category, options, test: string) = proc processSingleTest(r: var TResults, cat: Category, options, test: string) =
let test = "tests" & DirSep &.? cat.string / test let test = "tests" & DirSep &.? cat.string / test
let target = if cat.string.normalize == "js": targetJS else: targetC
if existsFile(test): testSpec r, makeTest(test, options, cat) if existsFile(test): testSpec r, makeTest(test, options, cat, target = target)
else: echo "[Warning] - ", test, " test does not exist" else: echo "[Warning] - ", test, " test does not exist"
proc processCategory(r: var TResults, cat: Category, options: string) = proc processCategory(r: var TResults, cat: Category, options: string) =

View file

@ -12,6 +12,8 @@ import parseutils, strutils, os, osproc, streams, parsecfg
var compilerPrefix* = "compiler" / "nim " var compilerPrefix* = "compiler" / "nim "
let isTravis = existsEnv("TRAVIS")
proc cmdTemplate*(): string = proc cmdTemplate*(): string =
compilerPrefix & "$target --lib:lib --hints:on -d:testing $options $file" compilerPrefix & "$target --lib:lib --hints:on -d:testing $options $file"
@ -174,6 +176,8 @@ proc parseSpec*(filename: string): TSpec =
when defined(unix): result.err = reIgnored when defined(unix): result.err = reIgnored
of "posix": of "posix":
when defined(posix): result.err = reIgnored when defined(posix): result.err = reIgnored
of "travis":
if isTravis: result.err = reIgnored
else: else:
raise newException(ValueError, "cannot interpret as a bool: " & e.value) raise newException(ValueError, "cannot interpret as a bool: " & e.value)
of "cmd": of "cmd":

View file

@ -439,6 +439,8 @@ proc main() =
var optPrintResults = false var optPrintResults = false
var optFailing = false var optFailing = false
var targetsStr = ""
var p = initOptParser() var p = initOptParser()
p.next() p.next()
while p.kind == cmdLongoption: while p.kind == cmdLongoption:
@ -446,7 +448,9 @@ proc main() =
of "print", "verbose": optPrintResults = true of "print", "verbose": optPrintResults = true
of "failing": optFailing = true of "failing": optFailing = true
of "pedantic": discard "now always enabled" of "pedantic": discard "now always enabled"
of "targets": targets = parseTargets(p.val.string) of "targets":
targetsStr = p.val.string
targets = parseTargets(targetsStr)
of "nim": compilerPrefix = p.val.string of "nim": compilerPrefix = p.val.string
else: quit Usage else: quit Usage
p.next() p.next()
@ -457,7 +461,9 @@ proc main() =
case action case action
of "all": of "all":
let testsDir = "tests" & DirSep let testsDir = "tests" & DirSep
let myself = quoteShell(findExe("tests" / "testament" / "tester")) var myself = quoteShell(findExe("tests" / "testament" / "tester"))
if targetsStr.len > 0:
myself &= " '--targets:" & targetsStr & "'"
var cmds: seq[string] = @[] var cmds: seq[string] = @[]
let rest = if p.cmdLineRest.string.len > 0: " " & p.cmdLineRest.string else: "" let rest = if p.cmdLineRest.string.len > 0: " " & p.cmdLineRest.string else: ""
for kind, dir in walkDir(testsDir): for kind, dir in walkDir(testsDir):

View file

@ -1,2 +1,9 @@
This directory contains tests which are not automatically executed This directory contains integration tests which are not automatically executed
for various reasons. Mainly due to dependencies on external services. for various reasons:
- dependency on external services
- dependency on files / configuration / state of the local host
- tests that are extremely slow or require large amounts of memory or storage
- tests that spawn local daemons
Integration tests can become stale very quickly. Automated ./koch tests are
strongly recommended.