diff --git a/changelog.md b/changelog.md
index 8ac02a388a..6cd0faf528 100644
--- a/changelog.md
+++ b/changelog.md
@@ -1,4 +1,4 @@
-## v0.X.X - XX/XX/2018
+## v0.19.X - XX/XX/2018
### Changes affecting backwards compatibility
@@ -11,9 +11,12 @@
to deal with!
- Indexing into a ``cstring`` for the JS target is now mapped
to ``charCodeAt``.
-- Assignments that would "slice" an object into its supertype are not prevented
+- Assignments that would "slice" an object into its supertype are now prevented
at runtime. Use ``ref object`` with inheritance rather than ``object`` with
inheritance to prevent this issue.
+- The ``not nil`` type annotation now has to be enabled explicitly
+ via ``{.experimental: "notnil"}`` as we are still not pleased with how this
+ feature works with Nim's containers.
#### Breaking changes in the standard library
@@ -81,6 +84,14 @@
Imported exceptions can be raised and caught just like Nim exceptions.
More details in language manual.
+- ``nil`` for strings/seqs is finally gone. Instead the default value for
+ these is ``"" / @[]``.
+- Accessing the binary zero terminator in Nim's native strings
+ is now invalid. Internally a Nim string still has the trailing zero for
+ zero-copy interoperability with ``cstring``. Compile your code with the
+ next switch ``--laxStrings:on`` if you need a transition period.
+
+
### Tool changes
- ``jsondoc2`` has been renamed ``jsondoc``, similar to how ``doc2`` was renamed
diff --git a/compiler/ccgcalls.nim b/compiler/ccgcalls.nim
index 5f14b68046..2ddc88509b 100644
--- a/compiler/ccgcalls.nim
+++ b/compiler/ccgcalls.nim
@@ -125,15 +125,15 @@ proc openArrayLoc(p: BProc, n: PNode): Rope =
of tyString, tySequence:
if skipTypes(n.typ, abstractInst).kind == tyVar and
not compileToCpp(p.module):
- result = "(*$1)->data, (*$1)->$2" % [a.rdLoc, lenField(p)]
+ result = "(*$1)->data, (*$1 ? (*$1)->$2 : 0)" % [a.rdLoc, lenField(p)]
else:
- result = "$1->data, $1->$2" % [a.rdLoc, lenField(p)]
+ result = "$1->data, ($1 ? $1->$2 : 0)" % [a.rdLoc, lenField(p)]
of tyArray:
result = "$1, $2" % [rdLoc(a), rope(lengthOrd(a.t))]
of tyPtr, tyRef:
case lastSon(a.t).kind
of tyString, tySequence:
- result = "(*$1)->data, (*$1)->$2" % [a.rdLoc, lenField(p)]
+ result = "(*$1)->data, (*$1 ? (*$1)->$2 : 0)" % [a.rdLoc, lenField(p)]
of tyArray:
result = "$1, $2" % [rdLoc(a), rope(lengthOrd(lastSon(a.t)))]
else:
@@ -143,7 +143,7 @@ proc openArrayLoc(p: BProc, n: PNode): Rope =
proc genArgStringToCString(p: BProc, n: PNode): Rope {.inline.} =
var a: TLoc
initLocExpr(p, n.sons[0], a)
- result = "$1->data" % [a.rdLoc]
+ result = "($1 ? $1->data : (NCSTRING)\"\")" % [a.rdLoc]
proc genArg(p: BProc, n: PNode, param: PSym; call: PNode): Rope =
var a: TLoc
diff --git a/compiler/ccgexprs.nim b/compiler/ccgexprs.nim
index ea373f5a6a..96f9265f17 100644
--- a/compiler/ccgexprs.nim
+++ b/compiler/ccgexprs.nim
@@ -63,6 +63,10 @@ proc genLiteral(p: BProc, n: PNode, ty: PType): Rope =
of tyNil:
result = genNilStringLiteral(p.module, n.info)
of tyString:
+ # with the new semantics for 'nil' strings, we can map "" to nil and
+ # save tons of allocations:
+ #if n.strVal.len == 0: result = genNilStringLiteral(p.module, n.info)
+ #else:
result = genStringLiteral(p.module, n)
else:
if n.strVal.isNil: result = rope("NIM_NIL")
@@ -882,7 +886,7 @@ proc genIndexCheck(p: BProc; arr, idx: TLoc) =
rdCharLoc(idx), first, intLiteral(lastOrd(ty)))
of tySequence, tyString:
linefmt(p, cpsStmts,
- "if ((NU)($1) >= (NU)($2->$3)) #raiseIndexError();$n",
+ "if (!$2 || (NU)($1) >= (NU)($2->$3)) #raiseIndexError();$n",
rdLoc(idx), rdLoc(arr), lenField(p))
else: discard
@@ -905,14 +909,14 @@ proc genSeqElem(p: BProc, n, x, y: PNode, d: var TLoc) =
if ty.kind in {tyRef, tyPtr}:
ty = skipTypes(ty.lastSon, abstractVarRange) # emit range check:
if optBoundsCheck in p.options:
- if ty.kind == tyString:
+ if ty.kind == tyString and (not defined(nimNoZeroTerminator) or optLaxStrings in p.options):
linefmt(p, cpsStmts,
- "if ((NU)($1) > (NU)($2->$3)) #raiseIndexError();$n",
- rdLoc(b), rdLoc(a), lenField(p))
+ "if (!$2 || (NU)($1) > (NU)($2->$3)) #raiseIndexError();$n",
+ rdLoc(b), rdLoc(a), lenField(p))
else:
linefmt(p, cpsStmts,
- "if ((NU)($1) >= (NU)($2->$3)) #raiseIndexError();$n",
- rdLoc(b), rdLoc(a), lenField(p))
+ "if (!$2 || (NU)($1) >= (NU)($2->$3)) #raiseIndexError();$n",
+ rdLoc(b), rdLoc(a), lenField(p))
if d.k == locNone: d.storage = OnHeap
if skipTypes(a.t, abstractVar).kind in {tyRef, tyPtr}:
a.r = rfmt(nil, "(*$1)", a.r)
@@ -980,10 +984,10 @@ proc genEcho(p: BProc, n: PNode) =
var a: TLoc
for it in n.sons:
if it.skipConv.kind == nkNilLit:
- add(args, ", \"nil\"")
+ add(args, ", \"\"")
else:
initLocExpr(p, it, a)
- addf(args, ", $1? ($1)->data:\"nil\"", [rdLoc(a)])
+ addf(args, ", $1? ($1)->data:\"\"", [rdLoc(a)])
p.module.includeHeader("")
linefmt(p, cpsStmts, """Genode::log(""$1);$n""", args)
else:
@@ -1034,7 +1038,7 @@ proc genStrConcat(p: BProc, e: PNode, d: var TLoc) =
if e.sons[i + 1].kind in {nkStrLit..nkTripleStrLit}:
inc(L, len(e.sons[i + 1].strVal))
else:
- addf(lens, "$1->$2 + ", [rdLoc(a), lenField(p)])
+ addf(lens, "($1 ? $1->$2 : 0) + ", [rdLoc(a), lenField(p)])
add(appends, rfmt(p.module, "#appendString($1, $2);$n", tmp.r, rdLoc(a)))
linefmt(p, cpsStmts, "$1 = #rawNewString($2$3);$n", tmp.r, lens, rope(L))
add(p.s(cpsStmts), appends)
@@ -1073,7 +1077,7 @@ proc genStrAppend(p: BProc, e: PNode, d: var TLoc) =
if e.sons[i + 2].kind in {nkStrLit..nkTripleStrLit}:
inc(L, len(e.sons[i + 2].strVal))
else:
- addf(lens, "$1->$2 + ", [rdLoc(a), lenField(p)])
+ addf(lens, "($1 ? $1->$2 : 0) + ", [rdLoc(a), lenField(p)])
add(appends, rfmt(p.module, "#appendString($1, $2);$n",
rdLoc(dest), rdLoc(a)))
linefmt(p, cpsStmts, "$1 = #resizeString($1, $2$3);$n",
@@ -1086,17 +1090,17 @@ proc genSeqElemAppend(p: BProc, e: PNode, d: var TLoc) =
# seq = (typeof seq) incrSeq(&seq->Sup, sizeof(x));
# seq->data[seq->len-1] = x;
let seqAppendPattern = if not p.module.compileToCpp:
- "$1 = ($2) #incrSeqV2(&($1)->Sup, sizeof($3));$n"
+ "$1 = ($2) #incrSeqV3(&($1)->Sup, $3);$n"
else:
- "$1 = ($2) #incrSeqV2($1, sizeof($3));$n"
+ "$1 = ($2) #incrSeqV3($1, $3);$n"
var a, b, dest, tmpL: TLoc
initLocExpr(p, e.sons[1], a)
initLocExpr(p, e.sons[2], b)
- let bt = skipTypes(e.sons[2].typ, {tyVar})
+ let seqType = skipTypes(e.sons[1].typ, {tyVar})
lineCg(p, cpsStmts, seqAppendPattern, [
rdLoc(a),
getTypeDesc(p.module, e.sons[1].typ),
- getTypeDesc(p.module, bt)])
+ genTypeInfo(p.module, seqType, e.info)])
#if bt != b.t:
# echo "YES ", e.info, " new: ", typeToString(bt), " old: ", typeToString(b.t)
initLoc(dest, locExpr, e.sons[2], OnHeap)
@@ -1417,7 +1421,7 @@ proc genRepr(p: BProc, e: PNode, d: var TLoc) =
putIntoDest(p, b, e, "$1, $1Len_0" % [rdLoc(a)], a.storage)
of tyString, tySequence:
putIntoDest(p, b, e,
- "$1->data, $1->$2" % [rdLoc(a), lenField(p)], a.storage)
+ "$1->data, ($1 ? $1->$2 : 0)" % [rdLoc(a), lenField(p)], a.storage)
of tyArray:
putIntoDest(p, b, e,
"$1, $2" % [rdLoc(a), rope(lengthOrd(a.t))], a.storage)
@@ -1500,13 +1504,13 @@ proc genSetLengthSeq(p: BProc, e: PNode, d: var TLoc) =
initLocExpr(p, e.sons[2], b)
let t = skipTypes(e.sons[1].typ, {tyVar})
let setLenPattern = if not p.module.compileToCpp:
- "$1 = ($3) #setLengthSeq(&($1)->Sup, sizeof($4), $2);$n"
+ "$1 = ($3) #setLengthSeqV2(&($1)->Sup, $4, $2);$n"
else:
- "$1 = ($3) #setLengthSeq($1, sizeof($4), $2);$n"
+ "$1 = ($3) #setLengthSeqV2($1, $4, $2);$n"
lineCg(p, cpsStmts, setLenPattern, [
rdLoc(a), rdLoc(b), getTypeDesc(p.module, t),
- getTypeDesc(p.module, t.skipTypes(abstractInst).sons[0])])
+ genTypeInfo(p.module, t.skipTypes(abstractInst), e.info)])
gcUsage(e)
proc genSetLengthStr(p: BProc, e: PNode, d: var TLoc) =
@@ -1740,7 +1744,7 @@ proc genConv(p: BProc, e: PNode, d: var TLoc) =
proc convStrToCStr(p: BProc, n: PNode, d: var TLoc) =
var a: TLoc
initLocExpr(p, n.sons[0], a)
- putIntoDest(p, d, n, "$1->data" % [rdLoc(a)],
+ putIntoDest(p, d, n, "($1 ? $1->data : (NCSTRING)\"\")" % [rdLoc(a)],
a.storage)
proc convCStrToStr(p: BProc, n: PNode, d: var TLoc) =
@@ -1755,16 +1759,14 @@ proc genStrEquals(p: BProc, e: PNode, d: var TLoc) =
var x: TLoc
var a = e.sons[1]
var b = e.sons[2]
- if (a.kind == nkNilLit) or (b.kind == nkNilLit):
- binaryExpr(p, e, d, "($1 == $2)")
- elif (a.kind in {nkStrLit..nkTripleStrLit}) and (a.strVal == ""):
+ if a.kind in {nkStrLit..nkTripleStrLit} and a.strVal == "":
initLocExpr(p, e.sons[2], x)
putIntoDest(p, d, e,
- rfmt(nil, "(($1) && ($1)->$2 == 0)", rdLoc(x), lenField(p)))
- elif (b.kind in {nkStrLit..nkTripleStrLit}) and (b.strVal == ""):
+ rfmt(nil, "(!($1) || ($1)->$2 == 0)", rdLoc(x), lenField(p)))
+ elif b.kind in {nkStrLit..nkTripleStrLit} and b.strVal == "":
initLocExpr(p, e.sons[1], x)
putIntoDest(p, d, e,
- rfmt(nil, "(($1) && ($1)->$2 == 0)", rdLoc(x), lenField(p)))
+ rfmt(nil, "(!($1) || ($1)->$2 == 0)", rdLoc(x), lenField(p)))
else:
binaryExpr(p, e, d, "#eqStrings($1, $2)")
diff --git a/compiler/commands.nim b/compiler/commands.nim
index 06b487cf04..d8d8ae4b71 100644
--- a/compiler/commands.nim
+++ b/compiler/commands.nim
@@ -54,6 +54,7 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
const
HelpMessage = "Nim Compiler Version $1 [$2: $3]\n" &
+ "Compiled at $4 $5\n" &
"Copyright (c) 2006-" & copyrightYear & " by Andreas Rumpf\n"
const
@@ -68,7 +69,8 @@ const
proc getCommandLineDesc(): string =
result = (HelpMessage % [VersionAsString, platform.OS[platform.hostOS].name,
- CPU[platform.hostCPU].name]) & Usage
+ CPU[platform.hostCPU].name, CompileDate, CompileTime]) &
+ Usage
proc helpOnError(pass: TCmdLinePass) =
if pass == passCmd1:
@@ -79,7 +81,8 @@ proc writeAdvancedUsage(pass: TCmdLinePass) =
if pass == passCmd1:
msgWriteln(`%`(HelpMessage, [VersionAsString,
platform.OS[platform.hostOS].name,
- CPU[platform.hostCPU].name]) & AdvancedUsage,
+ CPU[platform.hostCPU].name, CompileDate, CompileTime]) &
+ AdvancedUsage,
{msgStdout})
msgQuit(0)
@@ -87,7 +90,8 @@ proc writeFullhelp(pass: TCmdLinePass) =
if pass == passCmd1:
msgWriteln(`%`(HelpMessage, [VersionAsString,
platform.OS[platform.hostOS].name,
- CPU[platform.hostCPU].name]) & Usage & AdvancedUsage,
+ CPU[platform.hostCPU].name, CompileDate, CompileTime]) &
+ Usage & AdvancedUsage,
{msgStdout})
msgQuit(0)
@@ -95,7 +99,7 @@ proc writeVersionInfo(pass: TCmdLinePass) =
if pass == passCmd1:
msgWriteln(`%`(HelpMessage, [VersionAsString,
platform.OS[platform.hostOS].name,
- CPU[platform.hostCPU].name]),
+ CPU[platform.hostCPU].name, CompileDate, CompileTime]),
{msgStdout})
const gitHash = gorge("git log -n 1 --format=%H").strip
@@ -188,11 +192,11 @@ proc processSpecificNote*(arg: string, state: TSpecialWord, pass: TCmdLinePass,
if i < len(arg) and (arg[i] in {':', '='}): inc(i)
else: invalidCmdLineOption(pass, orig, info)
if state == wHint:
- var x = findStr(msgs.HintsToStr, id)
+ let x = findStr(msgs.HintsToStr, id)
if x >= 0: n = TNoteKind(x + ord(hintMin))
else: localError(info, "unknown hint: " & id)
else:
- var x = findStr(msgs.WarningsToStr, id)
+ let x = findStr(msgs.WarningsToStr, id)
if x >= 0: n = TNoteKind(x + ord(warnMin))
else: localError(info, "unknown warning: " & id)
case substr(arg, i).normalize
@@ -499,6 +503,7 @@ proc processSwitch(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
undefSymbol("nimOldNewlines")
else:
localError(info, errOnOrOffExpectedButXFound, arg)
+ of "laxstrings": processOnOffSwitch({optLaxStrings}, arg, pass, info)
of "checks", "x": processOnOffSwitch(ChecksOptions, arg, pass, info)
of "floatchecks":
processOnOffSwitch({optNaNCheck, optInfCheck}, arg, pass, info)
diff --git a/compiler/condsyms.nim b/compiler/condsyms.nim
index 028aedb5b5..f8a75e68e3 100644
--- a/compiler/condsyms.nim
+++ b/compiler/condsyms.nim
@@ -82,7 +82,6 @@ proc countDefinedSymbols*(): int =
proc initDefines*() =
gSymbols = newStringTable(modeStyleInsensitive)
- defineSymbol("nimrod") # 'nimrod' is always defined
# for bootstrapping purposes and old code:
defineSymbol("nimhygiene")
defineSymbol("niminheritable")
@@ -115,3 +114,6 @@ proc initDefines*() =
defineSymbol("nimHasNilChecks")
defineSymbol("nimSymKind")
defineSymbol("nimVmEqIdent")
+ defineSymbol("nimNoNil")
+ defineSymbol("nimNoZeroTerminator")
+ defineSymbol("nimNotNil")
diff --git a/compiler/filter_tmpl.nim b/compiler/filter_tmpl.nim
index a1ba9113c1..51ccb8390a 100644
--- a/compiler/filter_tmpl.nim
+++ b/compiler/filter_tmpl.nim
@@ -42,7 +42,8 @@ proc newLine(p: var TTmplParser) =
proc scanPar(p: var TTmplParser, d: int) =
var i = d
- while true:
+ let hi = p.x.len - 1
+ while i <= hi:
case p.x[i]
of '\0': break
of '(': inc(p.par)
diff --git a/compiler/lexer.nim b/compiler/lexer.nim
index 0b1090bb1f..0478ed574c 100644
--- a/compiler/lexer.nim
+++ b/compiler/lexer.nim
@@ -165,13 +165,12 @@ proc isKeyword*(kind: TTokType): bool =
template ones(n): untyped = ((1 shl n)-1) # for utf-8 conversion
proc isNimIdentifier*(s: string): bool =
- if s[0] in SymStartChars:
+ let sLen = s.len
+ if sLen > 0 and s[0] in SymStartChars:
var i = 1
- var sLen = s.len
while i < sLen:
- if s[i] == '_':
- inc(i)
- if s[i] notin SymChars: return
+ if s[i] == '_': inc(i)
+ if i < sLen and s[i] notin SymChars: return
inc(i)
result = true
@@ -311,12 +310,12 @@ template tokenEndPrevious(tok, pos) =
# We need to parse the largest uint literal without overflow checks
proc unsafeParseUInt(s: string, b: var BiggestInt, start = 0): int =
var i = start
- if s[i] in {'0'..'9'}:
+ if i < s.len and s[i] in {'0'..'9'}:
b = 0
- while s[i] in {'0'..'9'}:
+ while i < s.len and s[i] in {'0'..'9'}:
b = b * 10 + (ord(s[i]) - ord('0'))
inc(i)
- while s[i] == '_': inc(i) # underscores are allowed and ignored
+ while i < s.len and s[i] == '_': inc(i) # underscores are allowed and ignored
result = i - start
{.pop.} # overflowChecks
diff --git a/compiler/msgs.nim b/compiler/msgs.nim
index 5ae2c49700..5da375c1c9 100644
--- a/compiler/msgs.nim
+++ b/compiler/msgs.nim
@@ -126,7 +126,7 @@ type
warnSmallLshouldNotBeUsed, warnUnknownMagic, warnRedefinitionOfLabel,
warnUnknownSubstitutionX, warnLanguageXNotSupported,
warnFieldXNotSupported, warnCommentXIgnored,
- warnNilStatement, warnTypelessParam,
+ warnTypelessParam,
warnUseBase, warnWriteToForeignHeap, warnUnsafeCode,
warnEachIdentIsTuple, warnShadowIdent,
warnProveInit, warnProveField, warnProveIndex, warnGcUnsafe, warnGcUnsafe2,
@@ -400,7 +400,6 @@ const
warnLanguageXNotSupported: "language \'$1\' not supported",
warnFieldXNotSupported: "field \'$1\' not supported",
warnCommentXIgnored: "comment \'$1\' ignored",
- warnNilStatement: "'nil' statement is deprecated; use an empty 'discard' statement instead",
warnTypelessParam: "'$1' has no type. Typeless parameters are deprecated; only allowed for 'template'",
warnUseBase: "use {.base.} for base methods; baseless methods are deprecated",
warnWriteToForeignHeap: "write to foreign heap",
@@ -451,7 +450,7 @@ const
"SmallLshouldNotBeUsed", "UnknownMagic",
"RedefinitionOfLabel", "UnknownSubstitutionX",
"LanguageXNotSupported", "FieldXNotSupported",
- "CommentXIgnored", "NilStmt",
+ "CommentXIgnored",
"TypelessParam", "UseBase", "WriteToForeignHeap",
"UnsafeCode", "EachIdentIsTuple", "ShadowIdent",
"ProveInit", "ProveField", "ProveIndex", "GcUnsafe", "GcUnsafe2", "Uninit",
@@ -474,6 +473,10 @@ const
hintMin* = hintSuccess
hintMax* = high(TMsgKind)
+static:
+ doAssert HintsToStr.len == ord(hintMax) - ord(hintMin) + 1
+ doAssert WarningsToStr.len == ord(warnMax) - ord(warnMin) + 1
+
type
TNoteKind* = range[warnMin..hintMax] # "notes" are warnings or hints
TNoteKinds* = set[TNoteKind]
diff --git a/compiler/nimfix/nimfix.nim b/compiler/nimfix/nimfix.nim
index a97d88078e..2ef375b002 100644
--- a/compiler/nimfix/nimfix.nim
+++ b/compiler/nimfix/nimfix.nim
@@ -47,7 +47,7 @@ proc mainCommand =
compileProject(newModuleGraph(), newIdentCache())
pretty.overwriteFiles()
-proc processCmdLine*(pass: TCmdLinePass, cmd: string) =
+proc processCmdLine*(pass: TCmdLinePass, cmd: string, config: ConfigRef) =
var p = parseopt.initOptParser(cmd)
var argsCount = 0
gOnlyMainfile = true
@@ -76,16 +76,16 @@ proc processCmdLine*(pass: TCmdLinePass, cmd: string) =
of "wholeproject": gOnlyMainfile = false
of "besteffort": msgs.gErrorMax = high(int) # don't stop after first error
else:
- processSwitch(pass, p)
+ processSwitch(pass, p, config)
of cmdArgument:
options.gProjectName = unixToNativePath(p.key)
# if processArgument(pass, p, argsCount): break
-proc handleCmdLine() =
+proc handleCmdLine(config: ConfigRef) =
if paramCount() == 0:
stdout.writeLine(Usage)
else:
- processCmdLine(passCmd1, "")
+ processCmdLine(passCmd1, "", config)
if gProjectName != "":
try:
gProjectFull = canonicalizePath(gProjectName)
@@ -96,11 +96,11 @@ proc handleCmdLine() =
gProjectName = p.name
else:
gProjectPath = getCurrentDir()
- loadConfigs(DefaultConfig) # load all config files
+ loadConfigs(DefaultConfig, config) # load all config files
# now process command line arguments again, because some options in the
# command line can overwite the config file's settings
extccomp.initVars()
- processCmdLine(passCmd2, "")
+ processCmdLine(passCmd2, "", config)
mainCommand()
when compileOption("gc", "v2") or compileOption("gc", "refc"):
@@ -108,4 +108,4 @@ when compileOption("gc", "v2") or compileOption("gc", "refc"):
condsyms.initDefines()
defineSymbol "nimfix"
-handleCmdline()
+handleCmdline newConfigRef()
diff --git a/compiler/options.nim b/compiler/options.nim
index a5dfaa81cc..5baaa1bfd5 100644
--- a/compiler/options.nim
+++ b/compiler/options.nim
@@ -37,7 +37,8 @@ type # please make sure we have under 32 options
# evaluation
optPatterns, # en/disable pattern matching
optMemTracker,
- optHotCodeReloading
+ optHotCodeReloading,
+ optLaxStrings
TOptions* = set[TOption]
TGlobalOption* = enum # **keep binary compatible**
@@ -108,7 +109,8 @@ type
dotOperators,
callOperator,
parallel,
- destructor
+ destructor,
+ notnil
ConfigRef* = ref object ## eventually all global configuration should be moved here
cppDefines*: HashSet[string]
diff --git a/compiler/renderer.nim b/compiler/renderer.nim
index 2a65a10a80..95a622d4ea 100644
--- a/compiler/renderer.nim
+++ b/compiler/renderer.nim
@@ -175,10 +175,11 @@ proc put(g: var TSrcGen, kind: TTokType, s: string) =
proc putComment(g: var TSrcGen, s: string) =
if s.isNil: return
var i = 0
+ let hi = len(s) - 1
var isCode = (len(s) >= 2) and (s[1] != ' ')
var ind = g.lineLen
var com = "## "
- while true:
+ while i <= hi:
case s[i]
of '\0':
break
@@ -201,12 +202,12 @@ proc putComment(g: var TSrcGen, s: string) =
# gets too long:
# compute length of the following word:
var j = i
- while s[j] > ' ': inc(j)
+ while j <= hi and s[j] > ' ': inc(j)
if not isCode and (g.lineLen + (j - i) > MaxLineLen):
put(g, tkComment, com)
optNL(g, ind)
com = "## "
- while s[i] > ' ':
+ while i <= hi and s[i] > ' ':
add(com, s[i])
inc(i)
put(g, tkComment, com)
@@ -215,8 +216,9 @@ proc putComment(g: var TSrcGen, s: string) =
proc maxLineLength(s: string): int =
if s.isNil: return 0
var i = 0
+ let hi = len(s) - 1
var lineLen = 0
- while true:
+ while i <= hi:
case s[i]
of '\0':
break
@@ -235,7 +237,7 @@ proc maxLineLength(s: string): int =
proc putRawStr(g: var TSrcGen, kind: TTokType, s: string) =
var i = 0
- var hi = len(s) - 1
+ let hi = len(s) - 1
var str = ""
while i <= hi:
case s[i]
@@ -1064,6 +1066,7 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) =
if n.len > 1:
let opr = if n[0].kind == nkIdent: n[0].ident
elif n[0].kind == nkSym: n[0].sym.name
+ elif n[0].kind in {nkOpenSymChoice, nkClosedSymChoice}: n[0][0].sym.name
else: nil
if n[1].kind == nkPrefix or (opr != nil and renderer.isKeyword(opr)):
put(g, tkSpaces, Space)
diff --git a/compiler/ropes.nim b/compiler/ropes.nim
index 358ce8a53a..05d5e840c3 100644
--- a/compiler/ropes.nim
+++ b/compiler/ropes.nim
@@ -188,11 +188,11 @@ iterator leaves*(r: Rope): string =
var stack = @[r]
while stack.len > 0:
var it = stack.pop
- while isNil(it.data):
+ while it.left != nil:
+ assert it.right != nil
stack.add(it.right)
it = it.left
assert(it != nil)
- assert(it.data != nil)
yield it.data
iterator items*(r: Rope): char =
@@ -251,7 +251,7 @@ proc `%`*(frmt: FormatStr, args: openArray[Rope]): Rope =
while true:
j = j * 10 + ord(frmt[i]) - ord('0')
inc(i)
- if frmt[i] notin {'0'..'9'}: break
+ if i >= frmt.len or frmt[i] notin {'0'..'9'}: break
num = j
if j > high(args) + 1:
errorHandler(rInvalidFormatStr, $(j))
diff --git a/compiler/sem.nim b/compiler/sem.nim
index 041f2e1274..52282d0e49 100644
--- a/compiler/sem.nim
+++ b/compiler/sem.nim
@@ -153,8 +153,9 @@ proc commonType*(x, y: PType): PType =
if a.kind in {tyRef, tyPtr}:
k = a.kind
if b.kind != a.kind: return x
- a = a.lastSon
- b = b.lastSon
+ # bug #7601, array construction of ptr generic
+ a = a.lastSon.skipTypes({tyGenericInst})
+ b = b.lastSon.skipTypes({tyGenericInst})
if a.kind == tyObject and b.kind == tyObject:
result = commonSuperclass(a, b)
# this will trigger an error later:
diff --git a/compiler/semcall.nim b/compiler/semcall.nim
index f443339f59..aa53fda3b4 100644
--- a/compiler/semcall.nim
+++ b/compiler/semcall.nim
@@ -59,7 +59,8 @@ proc pickBestCandidate(c: PContext, headSymbol: PNode,
filter: TSymKinds,
best, alt: var TCandidate,
errors: var CandidateErrors,
- diagnosticsFlag = false) =
+ diagnosticsFlag: bool,
+ errorsEnabled: bool) =
var o: TOverloadIter
var sym = initOverloadIter(o, c, headSymbol)
var scope = o.lastOverloadScope
@@ -68,6 +69,7 @@ proc pickBestCandidate(c: PContext, headSymbol: PNode,
# This can occur in cases like 'init(a, 1, (var b = new(Type2); b))'
let counterInitial = c.currentScope.symbols.counter
var syms: seq[tuple[s: PSym, scope: int]]
+ var noSyms = true
var nextSymIndex = 0
while sym != nil:
if sym.kind in filter:
@@ -102,7 +104,7 @@ proc pickBestCandidate(c: PContext, headSymbol: PNode,
var cmp = cmpCandidates(best, z)
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 errors != nil or z.diagnostics != nil:
+ elif errorsEnabled or z.diagnosticsEnabled:
errors.safeAdd(CandidateError(
sym: sym,
unmatchedVarParam: int z.mutabilityProblem,
@@ -113,7 +115,8 @@ proc pickBestCandidate(c: PContext, headSymbol: PNode,
# before any further candidate init and compare. SLOW, but rare case.
syms = initCandidateSymbols(c, headSymbol, initialBinding, filter,
best, alt, o, diagnosticsFlag)
- if syms == nil:
+ noSyms = false
+ if noSyms:
sym = nextOverloadIter(o, c, headSymbol)
scope = o.lastOverloadScope
elif nextSymIndex < syms.len:
@@ -206,7 +209,7 @@ proc notFoundError*(c: PContext, n: PNode, errors: CandidateErrors) =
if errorOutputs == {}:
# fail fast:
globalError(n.info, errTypeMismatch, "")
- if errors.isNil or errors.len == 0:
+ if errors.len == 0:
localError(n.info, errExprXCannotBeCalled, n[0].renderTree)
return
@@ -227,7 +230,8 @@ proc bracketNotFoundError(c: PContext; n: PNode) =
if symx.kind in routineKinds:
errors.add(CandidateError(sym: symx,
unmatchedVarParam: 0, firstMismatch: 0,
- diagnostics: nil))
+ diagnostics: nil,
+ enabled: false))
symx = nextOverloadIter(o, c, headSymbol)
if errors.len == 0:
localError(n.info, "could not resolve: " & $n)
@@ -236,7 +240,8 @@ proc bracketNotFoundError(c: PContext; n: PNode) =
proc resolveOverloads(c: PContext, n, orig: PNode,
filter: TSymKinds, flags: TExprFlags,
- errors: var CandidateErrors): TCandidate =
+ errors: var CandidateErrors,
+ errorsEnabled: bool): TCandidate =
var initialBinding: PNode
var alt: TCandidate
var f = n.sons[0]
@@ -249,7 +254,8 @@ proc resolveOverloads(c: PContext, n, orig: PNode,
template pickBest(headSymbol) =
pickBestCandidate(c, headSymbol, n, orig, initialBinding,
- filter, result, alt, errors, efExplain in flags)
+ filter, result, alt, errors, efExplain in flags,
+ errorsEnabled)
pickBest(f)
let overloadsState = result.state
@@ -423,9 +429,8 @@ proc tryDeref(n: PNode): PNode =
proc semOverloadedCall(c: PContext, n, nOrig: PNode,
filter: TSymKinds, flags: TExprFlags): PNode =
- var errors: CandidateErrors = if efExplain in flags: @[]
- else: nil
- var r = resolveOverloads(c, n, nOrig, filter, flags, errors)
+ var errors: CandidateErrors = if efExplain in flags: @[] else: nil
+ var r = resolveOverloads(c, n, nOrig, filter, flags, errors, efExplain in flags)
if r.state == csMatch:
# this may be triggered, when the explain pragma is used
if errors.len > 0:
@@ -443,7 +448,7 @@ proc semOverloadedCall(c: PContext, n, nOrig: PNode,
# into sigmatch with hidden conversion produced there
#
n.sons[1] = n.sons[1].tryDeref
- var r = resolveOverloads(c, n, nOrig, filter, flags, errors)
+ var r = resolveOverloads(c, n, nOrig, filter, flags, errors, efExplain in flags)
if r.state == csMatch: result = semResolvedCall(c, n, r)
else:
# get rid of the deref again for a better error message:
diff --git a/compiler/semdata.nim b/compiler/semdata.nim
index 8159abf8f9..fc0488814c 100644
--- a/compiler/semdata.nim
+++ b/compiler/semdata.nim
@@ -63,7 +63,7 @@ type
# to the user.
efWantStmt, efAllowStmt, efDetermineType, efExplain,
efAllowDestructor, efWantValue, efOperand, efNoSemCheck,
- efNoProcvarCheck, efNoEvaluateGeneric, efInCall, efFromHlo,
+ efNoEvaluateGeneric, efInCall, efFromHlo
TExprFlags* = set[TExprFlag]
diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim
index 6ad5d931d4..feca087fce 100644
--- a/compiler/semexprs.nim
+++ b/compiler/semexprs.nim
@@ -51,7 +51,6 @@ proc semExprWithType(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
renderTree(result, {renderNoComments}))
result.typ = errorType(c)
else:
- if efNoProcvarCheck notin flags: semProcvarCheck(c, result)
if result.typ.kind in {tyVar, tyLent}: result = newDeref(result)
proc semExprNoDeref(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
@@ -63,8 +62,6 @@ proc semExprNoDeref(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
localError(n.info, errExprXHasNoType,
renderTree(result, {renderNoComments}))
result.typ = errorType(c)
- else:
- semProcvarCheck(c, result)
proc semSymGenericInstantiation(c: PContext, n: PNode, s: PSym): PNode =
result = symChoice(c, n, s, scClosed)
@@ -308,7 +305,9 @@ proc isOpImpl(c: PContext, n: PNode, flags: TExprFlags): PNode =
maybeLiftType(t2, c, n.info)
var m: TCandidate
initCandidate(c, m, t2)
- if efExplain in flags: m.diagnostics = @[]
+ if efExplain in flags:
+ m.diagnostics = @[]
+ m.diagnosticsEnabled = true
let match = typeRel(m, t2, t1) >= isSubtype # isNone
result = newIntNode(nkIntLit, ord(match))
@@ -540,7 +539,6 @@ proc analyseIfAddressTakenInCall(c: PContext, n: PNode) =
# we need to recurse explicitly here as converters can create nested
# calls and then they wouldn't be analysed otherwise
analyseIfAddressTakenInCall(c, n.sons[i])
- semProcvarCheck(c, n.sons[i])
if i < sonsLen(t) and
skipTypes(t.sons[i], abstractInst-{tyTypeDesc}).kind == tyVar:
if n.sons[i].kind != nkHiddenAddr:
@@ -1245,7 +1243,7 @@ proc semSubscript(c: PContext, n: PNode, flags: TExprFlags): PNode =
checkMinSonsLen(n, 2)
# make sure we don't evaluate generic macros/templates
n.sons[0] = semExprWithType(c, n.sons[0],
- {efNoProcvarCheck, efNoEvaluateGeneric})
+ {efNoEvaluateGeneric})
let arr = skipTypes(n.sons[0].typ, {tyGenericInst,
tyVar, tyLent, tyPtr, tyRef, tyAlias, tySink})
case arr.kind
diff --git a/compiler/semfold.nim b/compiler/semfold.nim
index 6fcc9a0a4b..c4d79a4a3b 100644
--- a/compiler/semfold.nim
+++ b/compiler/semfold.nim
@@ -13,7 +13,7 @@
import
strutils, options, ast, astalgo, trees, treetab, nimsets, times,
nversion, platform, math, msgs, os, condsyms, idents, renderer, types,
- commands, magicsys, saturate
+ commands, magicsys
proc getConstExpr*(m: PSym, n: PNode): PNode
# evaluates the constant expression or returns nil if it is no constant
@@ -24,6 +24,63 @@ proc newIntNodeT*(intVal: BiggestInt, n: PNode): PNode
proc newFloatNodeT(floatVal: BiggestFloat, n: PNode): PNode
proc newStrNodeT*(strVal: string, n: PNode): PNode
+proc checkInRange(n: PNode, res: BiggestInt): bool =
+ if res in firstOrd(n.typ)..lastOrd(n.typ):
+ result = true
+
+proc foldAdd(a, b: BiggestInt, n: PNode): PNode =
+ let res = a +% b
+ if ((res xor a) >= 0'i64 or (res xor b) >= 0'i64) and
+ checkInRange(n, res):
+ result = newIntNodeT(res, n)
+
+proc foldSub*(a, b: BiggestInt, n: PNode): PNode =
+ let res = a -% b
+ if ((res xor a) >= 0'i64 or (res xor not b) >= 0'i64) and
+ checkInRange(n, res):
+ result = newIntNodeT(res, n)
+
+proc foldAbs*(a: BiggestInt, n: PNode): PNode =
+ if a != firstOrd(n.typ):
+ result = newIntNodeT(a, n)
+
+proc foldMod*(a, b: BiggestInt, n: PNode): PNode =
+ if b != 0'i64:
+ result = newIntNodeT(a mod b, n)
+
+proc foldModU*(a, b: BiggestInt, n: PNode): PNode =
+ if b != 0'i64:
+ result = newIntNodeT(a %% b, n)
+
+proc foldDiv*(a, b: BiggestInt, n: PNode): PNode =
+ if b != 0'i64 and (a != firstOrd(n.typ) or b != -1'i64):
+ result = newIntNodeT(a div b, n)
+
+proc foldDivU*(a, b: BiggestInt, n: PNode): PNode =
+ if b != 0'i64:
+ result = newIntNodeT(a /% b, n)
+
+proc foldMul*(a, b: BiggestInt, n: PNode): PNode =
+ let res = a *% b
+ let floatProd = toBiggestFloat(a) * toBiggestFloat(b)
+ let resAsFloat = toBiggestFloat(res)
+
+ # Fast path for normal case: small multiplicands, and no info
+ # is lost in either method.
+ if resAsFloat == floatProd and checkInRange(n, res):
+ return newIntNodeT(res, n)
+
+ # Somebody somewhere lost info. Close enough, or way off? Note
+ # that a != 0 and b != 0 (else resAsFloat == floatProd == 0).
+ # The difference either is or isn't significant compared to the
+ # true value (of which floatProd is a good approximation).
+
+ # abs(diff)/abs(prod) <= 1/32 iff
+ # 32 * abs(diff) <= abs(prod) -- 5 good bits is "close enough"
+ if 32.0 * abs(resAsFloat - floatProd) <= abs(floatProd) and
+ checkInRange(n, res):
+ return newIntNodeT(res, n)
+
# implementation
proc newIntNodeT(intVal: BiggestInt, n: PNode): PNode =
@@ -172,23 +229,22 @@ proc evalOp(m: TMagic, n, a, b, c: PNode): PNode =
of mUnaryPlusI, mUnaryPlusF64: result = a # throw `+` away
of mToFloat, mToBiggestFloat:
result = newFloatNodeT(toFloat(int(getInt(a))), n)
+ # XXX: Hides overflow/underflow
of mToInt, mToBiggestInt: result = newIntNodeT(system.toInt(getFloat(a)), n)
of mAbsF64: result = newFloatNodeT(abs(getFloat(a)), n)
- of mAbsI:
- if getInt(a) >= 0: result = a
- else: result = newIntNodeT(- getInt(a), n)
+ of mAbsI: result = foldAbs(getInt(a), n)
of mZe8ToI, mZe8ToI64, mZe16ToI, mZe16ToI64, mZe32ToI64, mZeIToI64:
# byte(-128) = 1...1..1000_0000'64 --> 0...0..1000_0000'64
result = newIntNodeT(getInt(a) and (`shl`(1, getSize(a.typ) * 8) - 1), n)
of mToU8: result = newIntNodeT(getInt(a) and 0x000000FF, n)
of mToU16: result = newIntNodeT(getInt(a) and 0x0000FFFF, n)
of mToU32: result = newIntNodeT(getInt(a) and 0x00000000FFFFFFFF'i64, n)
- of mUnaryLt: result = newIntNodeT(getOrdValue(a) |-| 1, n)
- of mSucc: result = newIntNodeT(getOrdValue(a) |+| getInt(b), n)
- of mPred: result = newIntNodeT(getOrdValue(a) |-| getInt(b), n)
- of mAddI: result = newIntNodeT(getInt(a) |+| getInt(b), n)
- of mSubI: result = newIntNodeT(getInt(a) |-| getInt(b), n)
- of mMulI: result = newIntNodeT(getInt(a) |*| getInt(b), n)
+ of mUnaryLt: result = foldSub(getOrdValue(a), 1, n)
+ of mSucc: result = foldAdd(getOrdValue(a), getInt(b), n)
+ of mPred: result = foldSub(getOrdValue(a), getInt(b), n)
+ of mAddI: result = foldAdd(getInt(a), getInt(b), n)
+ of mSubI: result = foldSub(getInt(a), getInt(b), n)
+ of mMulI: result = foldMul(getInt(a), getInt(b), n)
of mMinI:
if getInt(a) > getInt(b): result = newIntNodeT(getInt(b), n)
else: result = newIntNodeT(getInt(a), n)
@@ -211,14 +267,8 @@ proc evalOp(m: TMagic, n, a, b, c: PNode): PNode =
of tyInt64, tyInt, tyUInt..tyUInt64:
result = newIntNodeT(`shr`(getInt(a), getInt(b)), n)
else: internalError(n.info, "constant folding for shr")
- of mDivI:
- let y = getInt(b)
- if y != 0:
- result = newIntNodeT(`|div|`(getInt(a), y), n)
- of mModI:
- let y = getInt(b)
- if y != 0:
- result = newIntNodeT(`|mod|`(getInt(a), y), n)
+ of mDivI: result = foldDiv(getInt(a), getInt(b), n)
+ of mModI: result = foldMod(getInt(a), getInt(b), n)
of mAddF64: result = newFloatNodeT(getFloat(a) + getFloat(b), n)
of mSubF64: result = newFloatNodeT(getFloat(a) - getFloat(b), n)
of mMulF64: result = newFloatNodeT(getFloat(a) * getFloat(b), n)
@@ -258,14 +308,8 @@ proc evalOp(m: TMagic, n, a, b, c: PNode): PNode =
of mAddU: result = newIntNodeT(`+%`(getInt(a), getInt(b)), n)
of mSubU: result = newIntNodeT(`-%`(getInt(a), getInt(b)), n)
of mMulU: result = newIntNodeT(`*%`(getInt(a), getInt(b)), n)
- of mModU:
- let y = getInt(b)
- if y != 0:
- result = newIntNodeT(`%%`(getInt(a), y), n)
- of mDivU:
- let y = getInt(b)
- if y != 0:
- result = newIntNodeT(`/%`(getInt(a), y), n)
+ of mModU: result = foldModU(getInt(a), getInt(b), n)
+ of mDivU: result = foldDivU(getInt(a), getInt(b), n)
of mLeSet: result = newIntNodeT(ord(containsSets(a, b)), n)
of mEqSet: result = newIntNodeT(ord(equalSets(a, b)), n)
of mLtSet:
diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim
index 94090852f8..8b466f1da3 100644
--- a/compiler/semstmts.nim
+++ b/compiler/semstmts.nim
@@ -71,37 +71,12 @@ proc toCover(t: PType): BiggestInt =
else:
result = lengthOrd(skipTypes(t, abstractVar-{tyTypeDesc}))
-when false:
- proc performProcvarCheck(c: PContext, info: TLineInfo, s: PSym) =
- ## Checks that the given symbol is a proper procedure variable, meaning
- ## that it
- var smoduleId = getModule(s).id
- if sfProcvar notin s.flags and s.typ.callConv == ccDefault and
- smoduleId != c.module.id:
- block outer:
- for module in c.friendModules:
- if smoduleId == module.id:
- break outer
- localError(info, errXCannotBePassedToProcVar, s.name.s)
-
-template semProcvarCheck(c: PContext, n: PNode) =
- when false:
- var n = n.skipConv
- if n.kind in nkSymChoices:
- for x in n:
- if x.sym.kind in {skProc, skMethod, skConverter, skIterator}:
- performProcvarCheck(c, n.info, x.sym)
- elif n.kind == nkSym and n.sym.kind in {skProc, skMethod, skConverter,
- skIterator}:
- performProcvarCheck(c, n.info, n.sym)
-
proc semProc(c: PContext, n: PNode): PNode
proc semExprBranch(c: PContext, n: PNode): PNode =
result = semExpr(c, n)
if result.typ != nil:
# XXX tyGenericInst here?
- semProcvarCheck(c, result)
if result.typ.kind in {tyVar, tyLent}: result = newDeref(result)
proc semExprBranchScope(c: PContext, n: PNode): PNode =
@@ -132,30 +107,23 @@ proc fixNilType(n: PNode) =
proc discardCheck(c: PContext, result: PNode) =
if c.matchedConcept != nil: return
if result.typ != nil and result.typ.kind notin {tyStmt, tyVoid}:
- if result.kind == nkNilLit:
- result.typ = nil
- message(result.info, warnNilStatement)
- elif implicitlyDiscardable(result):
+ if implicitlyDiscardable(result):
var n = result
result.typ = nil
while n.kind in skipForDiscardable:
n = n.lastSon
n.typ = nil
elif result.typ.kind != tyError and gCmd != cmdInteractive:
- if result.typ.kind == tyNil:
- fixNilType(result)
- message(result.info, warnNilStatement)
- else:
- var n = result
- while n.kind in skipForDiscardable: n = n.lastSon
- var s = "expression '" & $n & "' is of type '" &
- result.typ.typeToString & "' and has to be discarded"
- if result.info.line != n.info.line or
- result.info.fileIndex != n.info.fileIndex:
- s.add "; start of expression here: " & $result.info
- if result.typ.kind == tyProc:
- s.add "; for a function call use ()"
- localError(n.info, s)
+ var n = result
+ while n.kind in skipForDiscardable: n = n.lastSon
+ var s = "expression '" & $n & "' is of type '" &
+ result.typ.typeToString & "' and has to be discarded"
+ if result.info.line != n.info.line or
+ result.info.fileIndex != n.info.fileIndex:
+ s.add "; start of expression here: " & $result.info
+ if result.typ.kind == tyProc:
+ s.add "; for a function call use ()"
+ localError(n.info, s)
proc semIf(c: PContext, n: PNode): PNode =
result = n
@@ -384,7 +352,7 @@ proc checkNilable(v: PSym) =
include semasgn
-proc addToVarSection(c: PContext; result: var PNode; orig, identDefs: PNode) =
+proc addToVarSection(c: PContext; result: PNode; orig, identDefs: PNode) =
let L = identDefs.len
let value = identDefs[L-1]
if result.kind == nkStmtList:
@@ -525,12 +493,11 @@ proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode =
localError(a.info, errXExpected, "tuple")
elif length-2 != sonsLen(tup):
localError(a.info, errWrongNumberOfVariables)
- else:
- b = newNodeI(nkVarTuple, a.info)
- newSons(b, length)
- b.sons[length-2] = a.sons[length-2] # keep type desc for doc generator
- b.sons[length-1] = def
- addToVarSection(c, result, n, b)
+ b = newNodeI(nkVarTuple, a.info)
+ newSons(b, length)
+ b.sons[length-2] = a.sons[length-2] # keep type desc for doc generator
+ b.sons[length-1] = def
+ addToVarSection(c, result, n, b)
elif tup.kind == tyTuple and def.kind in {nkPar, nkTupleConstr} and
a.kind == nkIdentDefs and a.len > 3:
message(a.info, warnEachIdentIsTuple)
@@ -572,7 +539,9 @@ proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode =
addToVarSection(c, result, n, b)
else:
if def.kind in {nkPar, nkTupleConstr}: v.ast = def[j]
- setVarType(v, tup.sons[j])
+ # bug #7663, for 'nim check' this can be a non-tuple:
+ if tup.kind == tyTuple: setVarType(v, tup.sons[j])
+ else: v.typ = tup
b.sons[j] = newSymNode(v)
checkNilable(v)
if sfCompileTime in v.flags: hasCompileTime = true
@@ -1588,9 +1557,11 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind,
if sfOverriden in s.flags or s.name.s[0] == '=': semOverride(c, s, n)
if s.name.s[0] in {'.', '('}:
if s.name.s in [".", ".()", ".="] and {destructor, dotOperators} * c.features == {}:
- message(n.info, warnDeprecated, "overloaded '.' and '()' operators are now .experimental; " & s.name.s)
+ localError(n.info, "the overloaded " & s.name.s &
+ " operator has to be enabled with {.experimental: \"dotOperators\".}")
elif s.name.s == "()" and callOperator notin c.features:
- message(n.info, warnDeprecated, "overloaded '()' operators are now .experimental; " & s.name.s)
+ localError(n.info, "the overloaded " & s.name.s &
+ " operator has to be enabled with {.experimental: \"callOperator\".}")
if n.sons[bodyPos].kind != nkEmpty:
# for DLL generation it is annoying to check for sfImportc!
@@ -1682,11 +1653,6 @@ proc semIterator(c: PContext, n: PNode): PNode =
incl(s.typ.flags, tfCapturesEnv)
else:
s.typ.callConv = ccInline
- when false:
- if s.typ.callConv != ccInline:
- s.typ.callConv = ccClosure
- # and they always at least use the 'env' for the state field:
- incl(s.typ.flags, tfCapturesEnv)
if n.sons[bodyPos].kind == nkEmpty and s.magic == mNone:
localError(n.info, errImplOfXexpected, s.name.s)
@@ -1792,14 +1758,6 @@ proc semStaticStmt(c: PContext, n: PNode): PNode =
evalStaticStmt(c.module, c.cache, a, c.p.owner)
result = newNodeI(nkDiscardStmt, n.info, 1)
result.sons[0] = emptyNode
- when false:
- result = evalStaticStmt(c.module, a, c.p.owner)
- if result.isNil:
- LocalError(n.info, errCannotInterpretNodeX, renderTree(n))
- result = emptyNode
- elif result.kind == nkEmpty:
- result = newNodeI(nkDiscardStmt, n.info, 1)
- result.sons[0] = emptyNode
proc usesResult(n: PNode): bool =
# nkStmtList(expr) properly propagates the void context,
@@ -1841,80 +1799,47 @@ proc semStmtList(c: PContext, n: PNode, flags: TExprFlags): PNode =
# nkNilLit, nkEmpty}:
# dec last
for i in countup(0, length - 1):
- let k = n.sons[i].kind
- case k
- of nkFinally, nkExceptBranch:
- # stand-alone finally and except blocks are
- # transformed into regular try blocks:
- #
- # var f = fopen("somefile") | var f = fopen("somefile")
- # finally: fclose(f) | try:
- # ... | ...
- # | finally:
- # | fclose(f)
- var deferPart: PNode
- if k == nkDefer:
- deferPart = newNodeI(nkFinally, n.sons[i].info)
- deferPart.add n.sons[i].sons[0]
- elif k == nkFinally:
- message(n.info, warnDeprecated,
- "use 'defer'; standalone 'finally'")
- deferPart = n.sons[i]
- else:
- message(n.info, warnDeprecated,
- "use an explicit 'try'; standalone 'except'")
- deferPart = n.sons[i]
- var tryStmt = newNodeI(nkTryStmt, n.sons[i].info)
- var body = newNodeI(nkStmtList, n.sons[i].info)
- if i < n.sonsLen - 1:
- body.sons = n.sons[(i+1)..n.len-1]
- tryStmt.addSon(body)
- tryStmt.addSon(deferPart)
- n.sons[i] = semTry(c, tryStmt)
- n.sons.setLen(i+1)
- n.typ = n.sons[i].typ
- return
- else:
- var expr = semExpr(c, n.sons[i], flags)
- n.sons[i] = expr
- if c.matchedConcept != nil and expr.typ != nil and
- (nfFromTemplate notin n.flags or i != last):
- case expr.typ.kind
- of tyBool:
- if expr.kind == nkInfix and
- expr[0].kind == nkSym and
- expr[0].sym.name.s == "==":
- if expr[1].typ.isUnresolvedStatic:
- inferConceptStaticParam(c, expr[1], expr[2])
- continue
- elif expr[2].typ.isUnresolvedStatic:
- inferConceptStaticParam(c, expr[2], expr[1])
- continue
+ var expr = semExpr(c, n.sons[i], flags)
+ n.sons[i] = expr
+ if c.matchedConcept != nil and expr.typ != nil and
+ (nfFromTemplate notin n.flags or i != last):
+ case expr.typ.kind
+ of tyBool:
+ if expr.kind == nkInfix and
+ expr[0].kind == nkSym and
+ expr[0].sym.name.s == "==":
+ if expr[1].typ.isUnresolvedStatic:
+ inferConceptStaticParam(c, expr[1], expr[2])
+ continue
+ elif expr[2].typ.isUnresolvedStatic:
+ inferConceptStaticParam(c, expr[2], expr[1])
+ continue
- let verdict = semConstExpr(c, n[i])
- if verdict.intVal == 0:
- localError(result.info, "concept predicate failed")
- of tyUnknown: continue
- else: discard
- if n.sons[i].typ == enforceVoidContext: #or usesResult(n.sons[i]):
- voidContext = true
- n.typ = enforceVoidContext
- if i == last and (length == 1 or efWantValue in flags):
- n.typ = n.sons[i].typ
- if not isEmptyType(n.typ): n.kind = nkStmtListExpr
- elif i != last or voidContext:
- discardCheck(c, n.sons[i])
- else:
- n.typ = n.sons[i].typ
- if not isEmptyType(n.typ): n.kind = nkStmtListExpr
- if n.sons[i].kind in LastBlockStmts or
- n.sons[i].kind in nkCallKinds and n.sons[i][0].kind == nkSym and sfNoReturn in n.sons[i][0].sym.flags:
- for j in countup(i + 1, length - 1):
- case n.sons[j].kind
- of nkPragma, nkCommentStmt, nkNilLit, nkEmpty, nkBlockExpr,
- nkBlockStmt, nkState: discard
- else: localError(n.sons[j].info, errStmtInvalidAfterReturn)
+ let verdict = semConstExpr(c, n[i])
+ if verdict.intVal == 0:
+ localError(result.info, "concept predicate failed")
+ of tyUnknown: continue
else: discard
+ if n.sons[i].typ == enforceVoidContext: #or usesResult(n.sons[i]):
+ voidContext = true
+ n.typ = enforceVoidContext
+ if i == last and (length == 1 or efWantValue in flags):
+ n.typ = n.sons[i].typ
+ if not isEmptyType(n.typ): n.kind = nkStmtListExpr
+ elif i != last or voidContext:
+ discardCheck(c, n.sons[i])
+ else:
+ n.typ = n.sons[i].typ
+ if not isEmptyType(n.typ): n.kind = nkStmtListExpr
+ if n.sons[i].kind in LastBlockStmts or
+ n.sons[i].kind in nkCallKinds and n.sons[i][0].kind == nkSym and
+ sfNoReturn in n.sons[i][0].sym.flags:
+ for j in countup(i + 1, length - 1):
+ case n.sons[j].kind
+ of nkPragma, nkCommentStmt, nkNilLit, nkEmpty, nkBlockExpr,
+ nkBlockStmt, nkState: discard
+ else: localError(n.sons[j].info, errStmtInvalidAfterReturn)
+ else: discard
if result.len == 1 and
# concept bodies should be preserved as a stmt list:
@@ -1930,15 +1855,6 @@ proc semStmtList(c: PContext, n: PNode, flags: TExprFlags): PNode =
not (result.comment[0] == '#' and result.comment[1] == '#'):
# it is an old-style comment statement: we replace it with 'discard ""':
prettybase.replaceComment(result.info)
- when false:
- # a statement list (s; e) has the type 'e':
- if result.kind == nkStmtList and result.len > 0:
- var lastStmt = lastSon(result)
- if lastStmt.kind != nkNilLit and not implicitlyDiscardable(lastStmt):
- result.typ = lastStmt.typ
- #localError(lastStmt.info, errGenerated,
- # "Last expression must be explicitly returned if it " &
- # "is discardable or discarded")
proc semStmt(c: PContext, n: PNode): PNode =
# now: simply an alias:
diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim
index 1fc263617b..537319d66f 100644
--- a/compiler/semtypes.nim
+++ b/compiler/semtypes.nim
@@ -1389,6 +1389,8 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType =
n.sons[2].kind == nkNilLit:
result = freshType(result, prev)
result.flags.incl(tfNotNil)
+ if notnil notin c.features:
+ localError(n.info, "enable the 'not nil' annotation with {.experimental: \"notnil\".}")
else:
localError(n.info, errGenerated, "invalid type")
of 2:
diff --git a/compiler/sigmatch.nim b/compiler/sigmatch.nim
index 96d815df72..4263ef5813 100644
--- a/compiler/sigmatch.nim
+++ b/compiler/sigmatch.nim
@@ -26,6 +26,7 @@ type
sym*: PSym
unmatchedVarParam*, firstMismatch*: int
diagnostics*: seq[string]
+ enabled*: bool
CandidateErrors* = seq[CandidateError]
@@ -60,7 +61,8 @@ type
# matching. they will be reset if the matching
# is not successful. may replace the bindings
# table in the future.
- diagnostics*: seq[string] # when this is not nil, the matching process
+ diagnostics*: seq[string] # \
+ # when diagnosticsEnabled, the matching process
# will collect extra diagnostics that will be
# displayed to the user.
# triggered when overload resolution fails
@@ -70,6 +72,7 @@ type
inheritancePenalty: int # to prefer closest father object type
firstMismatch*: int # position of the first type mismatch for
# better error messages
+ diagnosticsEnabled*: bool
TTypeRelFlag* = enum
trDontBind
@@ -124,7 +127,8 @@ proc put(c: var TCandidate, key, val: PType) {.inline.} =
idTablePut(c.bindings, key, val.skipIntLit)
proc initCandidate*(ctx: PContext, c: var TCandidate, callee: PSym,
- binding: PNode, calleeScope = -1, diagnostics = false) =
+ binding: PNode, calleeScope = -1,
+ diagnosticsEnabled = false) =
initCandidateAux(ctx, c, callee.typ)
c.calleeSym = callee
if callee.kind in skProcKinds and calleeScope == -1:
@@ -139,7 +143,8 @@ proc initCandidate*(ctx: PContext, c: var TCandidate, callee: PSym,
c.calleeScope = 1
else:
c.calleeScope = calleeScope
- c.diagnostics = if diagnostics: @[] else: nil
+ c.diagnostics = if diagnosticsEnabled: @[] else: nil
+ c.diagnosticsEnabled = diagnosticsEnabled
c.magic = c.calleeSym.magic
initIdTable(c.bindings)
if binding != nil and callee.kind in routineKinds:
@@ -717,7 +722,7 @@ proc matchUserTypeClass*(m: var TCandidate; ff, a: PType): PType =
diagnostics: seq[string]
errorPrefix: string
flags: TExprFlags = {}
- collectDiagnostics = m.diagnostics != nil or
+ collectDiagnostics = m.diagnosticsEnabled or
sfExplain in typeClass.sym.flags
if collectDiagnostics:
@@ -736,7 +741,9 @@ proc matchUserTypeClass*(m: var TCandidate; ff, a: PType): PType =
if collectDiagnostics:
writelnHook = oldWriteHook
- for msg in diagnostics: m.diagnostics.safeAdd msg
+ for msg in diagnostics:
+ m.diagnostics.safeAdd msg
+ m.diagnosticsEnabled = true
if checkedBody == nil: return nil
@@ -1388,8 +1395,13 @@ proc typeRelImpl(c: var TCandidate, f, aOrig: PType,
var aAsObject = roota.lastSon
- if fKind in {tyRef, tyPtr} and aAsObject.kind == fKind:
- aAsObject = aAsObject.base
+ if fKind in {tyRef, tyPtr}:
+ if aAsObject.kind == tyObject:
+ # bug #7600, tyObject cannot be passed
+ # as argument to tyRef/tyPtr
+ return isNone
+ elif aAsObject.kind == fKind:
+ aAsObject = aAsObject.base
if aAsObject.kind == tyObject:
let baseType = aAsObject.base
diff --git a/compiler/syntaxes.nim b/compiler/syntaxes.nim
index 4014d4c584..5413565e6f 100644
--- a/compiler/syntaxes.nim
+++ b/compiler/syntaxes.nim
@@ -51,15 +51,15 @@ proc parseTopLevelStmt*(p: var TParsers): PNode =
result = ast.emptyNode
proc utf8Bom(s: string): int =
- if s[0] == '\xEF' and s[1] == '\xBB' and s[2] == '\xBF':
+ if s.len >= 3 and s[0] == '\xEF' and s[1] == '\xBB' and s[2] == '\xBF':
result = 3
else:
result = 0
proc containsShebang(s: string, i: int): bool =
- if s[i] == '#' and s[i+1] == '!':
+ if i+1 < s.len and s[i] == '#' and s[i+1] == '!':
var j = i + 2
- while s[j] in Whitespace: inc(j)
+ while j < s.len and s[j] in Whitespace: inc(j)
result = s[j] == '/'
proc parsePipe(filename: string, inputStream: PLLStream; cache: IdentCache): PNode =
@@ -74,9 +74,9 @@ proc parsePipe(filename: string, inputStream: PLLStream; cache: IdentCache): PNo
discard llStreamReadLine(s, line)
i = 0
inc linenumber
- if line[i] == '#' and line[i+1] == '?':
+ if i+1 < line.len and line[i] == '#' and line[i+1] == '?':
inc(i, 2)
- while line[i] in Whitespace: inc(i)
+ while i < line.len and line[i] in Whitespace: inc(i)
var q: TParser
parser.openParser(q, filename, llStreamOpen(substr(line, i)), cache)
result = parser.parseAll(q)
diff --git a/compiler/types.nim b/compiler/types.nim
index 5d60fa9b4e..edf4e5b46b 100644
--- a/compiler/types.nim
+++ b/compiler/types.nim
@@ -612,13 +612,13 @@ proc firstOrd*(t: PType): BiggestInt =
else:
assert(t.n.sons[0].kind == nkSym)
result = t.n.sons[0].sym.position
- of tyGenericInst, tyDistinct, tyTypeDesc, tyAlias:
+ of tyGenericInst, tyDistinct, tyTypeDesc, tyAlias, tyStatic:
result = firstOrd(lastSon(t))
of tyOrdinal:
if t.len > 0: result = firstOrd(lastSon(t))
- else: internalError("invalid kind for first(" & $t.kind & ')')
+ else: internalError("invalid kind for firstOrd(" & $t.kind & ')')
else:
- internalError("invalid kind for first(" & $t.kind & ')')
+ internalError("invalid kind for firstOrd(" & $t.kind & ')')
result = 0
proc lastOrd*(t: PType; fixedUnsigned = false): BiggestInt =
@@ -651,14 +651,14 @@ proc lastOrd*(t: PType; fixedUnsigned = false): BiggestInt =
of tyEnum:
assert(t.n.sons[sonsLen(t.n) - 1].kind == nkSym)
result = t.n.sons[sonsLen(t.n) - 1].sym.position
- of tyGenericInst, tyDistinct, tyTypeDesc, tyAlias:
+ of tyGenericInst, tyDistinct, tyTypeDesc, tyAlias, tyStatic:
result = lastOrd(lastSon(t))
of tyProxy: result = 0
of tyOrdinal:
if t.len > 0: result = lastOrd(lastSon(t))
- else: internalError("invalid kind for last(" & $t.kind & ')')
+ else: internalError("invalid kind for lastOrd(" & $t.kind & ')')
else:
- internalError("invalid kind for last(" & $t.kind & ')')
+ internalError("invalid kind for lastOrd(" & $t.kind & ')')
result = 0
proc lengthOrd*(t: PType): BiggestInt =
diff --git a/compiler/vmdeps.nim b/compiler/vmdeps.nim
index 071cc7706e..ba37237e82 100644
--- a/compiler/vmdeps.nim
+++ b/compiler/vmdeps.nim
@@ -208,7 +208,12 @@ proc mapTypeToAstX(t: PType; info: TLineInfo;
result.add mapTypeToAst(t.sons[0], info)
else:
result = mapTypeToBracket("ref", mRef, t, info)
- of tyVar: result = mapTypeToBracket("var", mVar, t, info)
+ of tyVar:
+ if inst:
+ result = newNodeX(nkVarTy)
+ result.add mapTypeToAst(t.sons[0], info)
+ else:
+ result = mapTypeToBracket("var", mVar, t, info)
of tyLent: result = mapTypeToBracket("lent", mBuiltinType, t, info)
of tySink: result = mapTypeToBracket("sink", mBuiltinType, t, info)
of tySequence: result = mapTypeToBracket("seq", mSeq, t, info)
diff --git a/compiler/vmgen.nim b/compiler/vmgen.nim
index c3eaf89461..0544dc311f 100644
--- a/compiler/vmgen.nim
+++ b/compiler/vmgen.nim
@@ -1553,6 +1553,7 @@ proc getNullValue(typ: PType, info: TLineInfo): PNode =
result = newNodeIT(nkFloatLit, info, t)
of tyCString, tyString:
result = newNodeIT(nkStrLit, info, t)
+ result.strVal = ""
of tyVar, tyLent, tyPointer, tyPtr, tySequence, tyExpr,
tyStmt, tyTypeDesc, tyStatic, tyRef, tyNil:
result = newNodeIT(nkNilLit, info, t)
diff --git a/doc/advopt.txt b/doc/advopt.txt
index d4a4b49613..685c8127d9 100644
--- a/doc/advopt.txt
+++ b/doc/advopt.txt
@@ -66,6 +66,8 @@ Advanced options:
--excessiveStackTrace:on|off
stack traces use full file paths
--oldNewlines:on|off turn on|off the old behaviour of "\n"
+ --laxStrings:on|off when turned on, accessing the zero terminator in
+ strings is allowed; only for backwards compatibility
--skipCfg do not read the general configuration file
--skipUserCfg do not read the user's configuration file
--skipParentCfg do not read the parent dirs' configuration files
diff --git a/doc/manual.rst b/doc/manual.rst
index 636bf796be..fabcf058ad 100644
--- a/doc/manual.rst
+++ b/doc/manual.rst
@@ -1471,7 +1471,7 @@ mysterious crashes.
**Note**: The example only works because the memory is initialized to zero
(``alloc0`` instead of ``alloc`` does this): ``d.s`` is thus initialized to
-``nil`` which the string assignment can handle. One needs to know low level
+binary zero which the string assignment can handle. One needs to know low level
details like this when mixing garbage collected data with unmanaged memory.
.. XXX finalizers for traced objects
@@ -2512,8 +2512,8 @@ char '\\0'
bool false
ref or pointer type nil
procedural type nil
-sequence nil (*not* ``@[]``)
-string nil (*not* "")
+sequence ``@[]``
+string ``""``
tuple[x: A, y: B, ...] (default(A), default(B), ...)
(analogous for objects)
array[0..., T] [default(T), ...]
@@ -4270,7 +4270,7 @@ therefore very useful for type specialization within generic code:
Table[Key, Value] = object
keys: seq[Key]
values: seq[Value]
- when not (Key is string): # nil value for strings used for optimization
+ when not (Key is string): # empty value for strings used for optimization
deletedKeys: seq[bool]
@@ -7434,8 +7434,8 @@ code generation directly, but their presence can be detected by macros.
Custom pragmas are defined using templates annotated with pragma ``pragma``:
.. code-block:: nim
- template dbTable(name: string, table_space: string = nil) {.pragma.}
- template dbKey(name: string = nil, primary_key: bool = false) {.pragma.}
+ template dbTable(name: string, table_space: string = "") {.pragma.}
+ template dbKey(name: string = "", primary_key: bool = false) {.pragma.}
template dbForeignKey(t: typedesc) {.pragma.}
template dbIgnore {.pragma.}
diff --git a/doc/tut1.rst b/doc/tut1.rst
index cbfef183ec..f2251c4637 100644
--- a/doc/tut1.rst
+++ b/doc/tut1.rst
@@ -944,12 +944,8 @@ String variables are **mutable**, so appending to a string
is possible, and quite efficient. Strings in Nim are both zero-terminated and have a
length field. A string's length can be retrieved with the builtin ``len``
procedure; the length never counts the terminating zero. Accessing the
-terminating zero is not an error and often leads to simpler code:
-
-.. code-block:: nim
- if s[i] == 'a' and s[i+1] == 'b':
- # no need to check whether ``i < len(s)``!
- ...
+terminating zero is an error, it only exists so that a Nim string can be converted
+to a ``cstring`` without doing a copy.
The assignment operator for strings copies the string. You can use the ``&``
operator to concatenate strings and ``add`` to append to a string.
@@ -960,11 +956,7 @@ enforced. For example, when reading strings from binary files, they are merely
a sequence of bytes. The index operation ``s[i]`` means the i-th *char* of
``s``, not the i-th *unichar*.
-String variables are initialized with a special value, called ``nil``. However,
-most string operations cannot deal with ``nil`` (leading to an exception being
-raised) for performance reasons. It is best to use empty strings ``""``
-rather than ``nil`` as the *empty* value. But ``""`` often creates a string
-object on the heap, so there is a trade-off to be made here.
+A string variable is initialized with the empty string ``""``.
Integers
@@ -1309,11 +1301,7 @@ Example:
x: seq[int] # a reference to a sequence of integers
x = @[1, 2, 3, 4, 5, 6] # the @ turns the array into a sequence allocated on the heap
-Sequence variables are initialized with ``nil``. However, most sequence
-operations cannot deal with ``nil`` (leading to an exception being
-raised) for performance reasons. Thus one should use empty sequences ``@[]``
-rather than ``nil`` as the *empty* value. But ``@[]`` creates a sequence
-object on the heap, so there is a trade-off to be made here.
+Sequence variables are initialized with ``@[]``.
The ``for`` statement can be used with one or two variables when used with a
sequence. When you use the one variable form, the variable will hold the value
@@ -1355,11 +1343,9 @@ type does not matter.
.. code-block:: nim
:test: "nim c $1"
var
- fruits: seq[string] # reference to a sequence of strings that is initialized with 'nil'
+ fruits: seq[string] # reference to a sequence of strings that is initialized with '@[]'
capitals: array[3, string] # array of strings with a fixed size
- fruits = @[] # creates an empty sequence on the heap that will be referenced by 'fruits'
-
capitals = ["New York", "London", "Berlin"] # array 'capitals' allows assignment of only three elements
fruits.add("Banana") # sequence 'fruits' is dynamically expandable during runtime
fruits.add("Mango")
@@ -1691,7 +1677,7 @@ rules apply:
write(stdout, x(3)) # no error: A.x is called
write(stdout, x("")) # no error: B.x is called
- proc x*(a: int): string = nil
+ proc x*(a: int): string = discard
write(stdout, x(3)) # ambiguous: which `x` is to call?
diff --git a/examples/httpserver2.nim b/examples/httpserver2.nim
index 050684d3e4..1843ff967a 100644
--- a/examples/httpserver2.nim
+++ b/examples/httpserver2.nim
@@ -107,7 +107,7 @@ proc executeCgi(server: var TServer, client: Socket, path, query: string,
dataAvail = recvLine(client, buf)
if buf.len == 0:
break
- var L = toLower(buf)
+ var L = toLowerAscii(buf)
if L.startsWith("content-length:"):
var i = len("content-length:")
while L[i] in Whitespace: inc(i)
@@ -205,7 +205,7 @@ proc acceptRequest(server: var TServer, client: Socket) =
client.close()
else:
when defined(Windows):
- var ext = splitFile(path).ext.toLower
+ var ext = splitFile(path).ext.toLowerAscii
if ext == ".exe" or ext == ".cgi":
# XXX: extract interpreter information here?
cgi = true
diff --git a/lib/impure/nre.nim b/lib/impure/nre.nim
index 3d4afc0ae1..6058128ddf 100644
--- a/lib/impure/nre.nim
+++ b/lib/impure/nre.nim
@@ -10,7 +10,7 @@
from pcre import nil
import nre.private.util
import tables
-from strutils import toLower, `%`
+from strutils import `%`
from math import ceil
import options
from unicode import runeLenAt
@@ -326,15 +326,15 @@ proc `$`*(pattern: RegexMatch): string =
proc `==`*(a, b: Regex): bool =
if not a.isNil and not b.isNil:
- return a.pattern == b.pattern and
- a.pcreObj == b.pcreObj and
+ return a.pattern == b.pattern and
+ a.pcreObj == b.pcreObj and
a.pcreExtra == b.pcreExtra
else:
return system.`==`(a, b)
proc `==`*(a, b: RegexMatch): bool =
return a.pattern == b.pattern and
- a.str == b.str
+ a.str == b.str
# }}}
# Creation & Destruction {{{
@@ -645,7 +645,6 @@ template replaceImpl(str: string, pattern: Regex,
let bounds = match.matchBounds
result.add(str.substr(lastIdx, bounds.a - 1))
let nextVal = replacement
- assert(nextVal != nil)
result.add(nextVal)
lastIdx = bounds.b + 1
diff --git a/lib/packages/docutils/rstgen.nim b/lib/packages/docutils/rstgen.nim
index ee1f0076ce..1547c888d8 100644
--- a/lib/packages/docutils/rstgen.nim
+++ b/lib/packages/docutils/rstgen.nim
@@ -774,7 +774,7 @@ proc renderImage(d: PDoc, n: PRstNode, result: var string) =
Digits + Letters + WhiteSpace)
let
arg = getArgument(n)
- isObject = arg.toLower().endsWith(".svg")
+ isObject = arg.toLowerAscii().endsWith(".svg")
var
options = ""
content = ""
diff --git a/lib/pure/asyncfutures.nim b/lib/pure/asyncfutures.nim
index 6df6527d59..8fedb16fe9 100644
--- a/lib/pure/asyncfutures.nim
+++ b/lib/pure/asyncfutures.nim
@@ -177,7 +177,7 @@ proc fail*[T](future: Future[T], error: ref Exception) =
if getStackTrace(error) == "": getStackTrace() else: getStackTrace(error)
future.callbacks.call()
-proc clearCallbacks(future: FutureBase) =
+proc clearCallbacks*(future: FutureBase) =
future.callbacks.function = nil
future.callbacks.next = nil
diff --git a/lib/pure/asyncnet.nim b/lib/pure/asyncnet.nim
index a75f9daacc..7c354151b3 100644
--- a/lib/pure/asyncnet.nim
+++ b/lib/pure/asyncnet.nim
@@ -201,7 +201,7 @@ when defineSsl:
flags: set[SocketFlag]) {.async.} =
let len = bioCtrlPending(socket.bioOut)
if len > 0:
- var data = newStringOfCap(len)
+ var data = newString(len)
let read = bioRead(socket.bioOut, addr data[0], len)
assert read != 0
if read < 0:
diff --git a/lib/pure/base64.nim b/lib/pure/base64.nim
index 4b0d082928..bfb8a16663 100644
--- a/lib/pure/base64.nim
+++ b/lib/pure/base64.nim
@@ -52,7 +52,7 @@ template encodeInternal(s: typed, lineLen: int, newLine: string): untyped =
if numLines > 0: inc(total, (numLines - 1) * newLine.len)
result = newString(total)
- var
+ var
i = 0
r = 0
currLine = 0
@@ -76,7 +76,7 @@ template encodeInternal(s: typed, lineLen: int, newLine: string): untyped =
currLine = 0
if i < s.len-1:
- let
+ let
a = ord(s[i])
b = ord(s[i+1])
result[r] = cb64[a shr 2]
@@ -130,11 +130,11 @@ proc decode*(s: string): string =
# total is an upper bound, as we will skip arbitrary whitespace:
result = newString(total)
- var
+ var
i = 0
r = 0
while true:
- while s[i] in Whitespace: inc(i)
+ while i < s.len and s[i] in Whitespace: inc(i)
if i < s.len-3:
let
a = s[i].decodeByte
diff --git a/lib/pure/cgi.nim b/lib/pure/cgi.nim
index 5de6aa4870..a5a4044970 100644
--- a/lib/pure/cgi.nim
+++ b/lib/pure/cgi.nim
@@ -97,11 +97,10 @@ iterator decodeData*(data: string): tuple[key, value: TaintedString] =
var name = ""
var value = ""
# decode everything in one pass:
- while data[i] != '\0':
+ while i < data.len:
setLen(name, 0) # reuse memory
- while true:
+ while i < data.len:
case data[i]
- of '\0': break
of '%':
var x = 0
handleHexChar(data[i+1], x)
@@ -112,15 +111,16 @@ iterator decodeData*(data: string): tuple[key, value: TaintedString] =
of '=', '&': break
else: add(name, data[i])
inc(i)
- if data[i] != '=': cgiError("'=' expected")
+ if i >= data.len or data[i] != '=': cgiError("'=' expected")
inc(i) # skip '='
setLen(value, 0) # reuse memory
- while true:
+ while i < data.len:
case data[i]
of '%':
var x = 0
- handleHexChar(data[i+1], x)
- handleHexChar(data[i+2], x)
+ if i+2 < data.len:
+ handleHexChar(data[i+1], x)
+ handleHexChar(data[i+2], x)
inc(i, 2)
add(value, chr(x))
of '+': add(value, ' ')
@@ -128,9 +128,9 @@ iterator decodeData*(data: string): tuple[key, value: TaintedString] =
else: add(value, data[i])
inc(i)
yield (name.TaintedString, value.TaintedString)
- if data[i] == '&': inc(i)
- elif data[i] == '\0': break
- else: cgiError("'&' expected")
+ if i < data.len:
+ if data[i] == '&': inc(i)
+ else: cgiError("'&' expected")
iterator decodeData*(allowedMethods: set[RequestMethod] =
{methodNone, methodPost, methodGet}): tuple[key, value: TaintedString] =
diff --git a/lib/pure/collections/critbits.nim b/lib/pure/collections/critbits.nim
index 34f5c54708..95fffdf88c 100644
--- a/lib/pure/collections/critbits.nim
+++ b/lib/pure/collections/critbits.nim
@@ -74,18 +74,19 @@ proc rawInsert[T](c: var CritBitTree[T], key: string): Node[T] =
var newByte = 0
block blockX:
while newbyte < key.len:
- if it.key[newbyte] != key[newbyte]:
- newotherbits = it.key[newbyte].ord xor key[newbyte].ord
+ let ch = if newbyte < it.key.len: it.key[newbyte] else: '\0'
+ if ch != key[newbyte]:
+ newotherbits = ch.ord xor key[newbyte].ord
break blockX
inc newbyte
- if it.key[newbyte] != '\0':
+ if newbyte < it.key.len:
newotherbits = it.key[newbyte].ord
else:
return it
while (newOtherBits and (newOtherBits-1)) != 0:
newOtherBits = newOtherBits and (newOtherBits-1)
newOtherBits = newOtherBits xor 255
- let ch = it.key[newByte]
+ let ch = if newByte < it.key.len: it.key[newByte] else: '\0'
let dir = (1 + (ord(ch) or newOtherBits)) shr 8
var inner: Node[T]
diff --git a/lib/pure/cookies.nim b/lib/pure/cookies.nim
index aca0ac2b43..eaff86ae6a 100644
--- a/lib/pure/cookies.nim
+++ b/lib/pure/cookies.nim
@@ -25,16 +25,16 @@ proc parseCookies*(s: string): StringTableRef =
result = newStringTable(modeCaseInsensitive)
var i = 0
while true:
- while s[i] == ' ' or s[i] == '\t': inc(i)
+ while i < s.len and (s[i] == ' ' or s[i] == '\t'): inc(i)
var keystart = i
- while s[i] != '=' and s[i] != '\0': inc(i)
+ while i < s.len and s[i] != '=': inc(i)
var keyend = i-1
- if s[i] == '\0': break
+ if i >= s.len: break
inc(i) # skip '='
var valstart = i
- while s[i] != ';' and s[i] != '\0': inc(i)
+ while i < s.len and s[i] != ';': inc(i)
result[substr(s, keystart, keyend)] = substr(s, valstart, i-1)
- if s[i] == '\0': break
+ if i >= s.len: break
inc(i) # skip ';'
proc setCookie*(key, value: string, domain = "", path = "",
diff --git a/lib/pure/encodings.nim b/lib/pure/encodings.nim
index 5840d443db..731fbbc297 100644
--- a/lib/pure/encodings.nim
+++ b/lib/pure/encodings.nim
@@ -36,7 +36,7 @@ when defined(windows):
while i < a.len and j < b.len:
if a[i] in {'-', '_'}: inc i
if b[j] in {'-', '_'}: inc j
- if a[i].toLower != b[j].toLower: return false
+ if i < a.len and j < b.len and a[i].toLowerAscii != b[j].toLowerAscii: return false
inc i
inc j
result = i == a.len and j == b.len
diff --git a/lib/pure/httpclient.nim b/lib/pure/httpclient.nim
index 70fb194341..b7617b0b50 100644
--- a/lib/pure/httpclient.nim
+++ b/lib/pure/httpclient.nim
@@ -73,12 +73,18 @@
## progress of the HTTP request.
##
## .. code-block:: Nim
-## var client = newAsyncHttpClient()
+## import asyncdispatch, httpclient
+##
## proc onProgressChanged(total, progress, speed: BiggestInt) {.async.} =
## echo("Downloaded ", progress, " of ", total)
## echo("Current rate: ", speed div 1000, "kb/s")
-## client.onProgressChanged = onProgressChanged
-## discard await client.getContent("http://speedtest-ams2.digitalocean.com/100mb.test")
+##
+## proc asyncProc() {.async.} =
+## var client = newAsyncHttpClient()
+## client.onProgressChanged = onProgressChanged
+## discard await client.getContent("http://speedtest-ams2.digitalocean.com/100mb.test")
+##
+## waitFor asyncProc()
##
## If you would like to remove the callback simply set it to ``nil``.
##
@@ -241,7 +247,7 @@ proc parseChunks(s: Socket, timeout: int): string =
var i = 0
if chunkSizeStr == "":
httpError("Server terminated connection prematurely")
- while true:
+ while i < chunkSizeStr.len:
case chunkSizeStr[i]
of '0'..'9':
chunkSize = chunkSize shl 4 or (ord(chunkSizeStr[i]) - ord('0'))
@@ -249,8 +255,6 @@ proc parseChunks(s: Socket, timeout: int): string =
chunkSize = chunkSize shl 4 or (ord(chunkSizeStr[i]) - ord('a') + 10)
of 'A'..'F':
chunkSize = chunkSize shl 4 or (ord(chunkSizeStr[i]) - ord('A') + 10)
- of '\0':
- break
of ';':
# http://tools.ietf.org/html/rfc2616#section-3.6.1
# We don't care about chunk-extensions.
@@ -739,10 +743,11 @@ proc downloadFile*(url: string, outputFilename: string,
proc generateHeaders(requestUrl: Uri, httpMethod: string,
headers: HttpHeaders, body: string, proxy: Proxy): string =
# GET
- result = httpMethod.toUpperAscii()
+ let upperMethod = httpMethod.toUpperAscii()
+ result = upperMethod
result.add ' '
- if proxy.isNil or (not proxy.isNil and requestUrl.scheme == "https"):
+ if proxy.isNil or requestUrl.scheme == "https":
# /path?query
if requestUrl.path[0] != '/': result.add '/'
result.add(requestUrl.path)
@@ -768,7 +773,9 @@ proc generateHeaders(requestUrl: Uri, httpMethod: string,
add(result, "Connection: Keep-Alive\c\L")
# Content length header.
- if body.len > 0 and not headers.hasKey("Content-Length"):
+ const requiresBody = ["POST", "PUT", "PATCH"]
+ let needsContentLength = body.len > 0 or upperMethod in requiresBody
+ if needsContentLength and not headers.hasKey("Content-Length"):
add(result, "Content-Length: " & $body.len & "\c\L")
# Proxy auth header.
@@ -929,7 +936,7 @@ proc parseChunks(client: HttpClient | AsyncHttpClient): Future[void]
var i = 0
if chunkSizeStr == "":
httpError("Server terminated connection prematurely")
- while true:
+ while i < chunkSizeStr.len:
case chunkSizeStr[i]
of '0'..'9':
chunkSize = chunkSize shl 4 or (ord(chunkSizeStr[i]) - ord('0'))
@@ -937,8 +944,6 @@ proc parseChunks(client: HttpClient | AsyncHttpClient): Future[void]
chunkSize = chunkSize shl 4 or (ord(chunkSizeStr[i]) - ord('a') + 10)
of 'A'..'F':
chunkSize = chunkSize shl 4 or (ord(chunkSizeStr[i]) - ord('A') + 10)
- of '\0':
- break
of ';':
# http://tools.ietf.org/html/rfc2616#section-3.6.1
# We don't care about chunk-extensions.
diff --git a/lib/pure/httpcore.nim b/lib/pure/httpcore.nim
index f150fa1c1a..5df26fdd58 100644
--- a/lib/pure/httpcore.nim
+++ b/lib/pure/httpcore.nim
@@ -190,11 +190,11 @@ proc len*(headers: HttpHeaders): int = return headers.table.len
proc parseList(line: string, list: var seq[string], start: int): int =
var i = 0
var current = ""
- while line[start + i] notin {'\c', '\l', '\0'}:
+ while start+i < line.len and line[start + i] notin {'\c', '\l'}:
i += line.skipWhitespace(start + i)
i += line.parseUntil(current, {'\c', '\l', ','}, start + i)
list.add(current)
- if line[start + i] == ',':
+ if start+i < line.len and line[start + i] == ',':
i.inc # Skip ,
current.setLen(0)
diff --git a/lib/pure/httpserver.nim b/lib/pure/httpserver.nim
index 632eb198a1..9f54ed9e83 100644
--- a/lib/pure/httpserver.nim
+++ b/lib/pure/httpserver.nim
@@ -126,7 +126,7 @@ when false:
var dataAvail = false
while dataAvail:
dataAvail = recvLine(client, buf) # TODO: This is incorrect.
- var L = toLower(buf.string)
+ var L = toLowerAscii(buf.string)
if L.startsWith("content-length:"):
var i = len("content-length:")
while L[i] in Whitespace: inc(i)
@@ -199,7 +199,7 @@ when false:
notFound(client)
else:
when defined(Windows):
- var ext = splitFile(path).ext.toLower
+ var ext = splitFile(path).ext.toLowerAscii
if ext == ".exe" or ext == ".cgi":
# XXX: extract interpreter information here?
cgi = true
@@ -303,7 +303,7 @@ proc next*(s: var Server) =
if s.reqMethod == "POST":
# Check for Expect header
if s.headers.hasKey("Expect"):
- if s.headers["Expect"].toLower == "100-continue":
+ if s.headers["Expect"].toLowerAscii == "100-continue":
s.client.sendStatus("100 Continue")
else:
s.client.sendStatus("417 Expectation Failed")
@@ -427,7 +427,7 @@ proc nextAsync(s: PAsyncHTTPServer) =
if s.reqMethod == "POST":
# Check for Expect header
if s.headers.hasKey("Expect"):
- if s.headers["Expect"].toLower == "100-continue":
+ if s.headers["Expect"].toLowerAscii == "100-continue":
s.client.sendStatus("100 Continue")
else:
s.client.sendStatus("417 Expectation Failed")
diff --git a/lib/pure/json.nim b/lib/pure/json.nim
index 6740f483d5..574b13fbf2 100644
--- a/lib/pure/json.nim
+++ b/lib/pure/json.nim
@@ -696,7 +696,7 @@ proc getBiggestInt*(n: JsonNode, default: BiggestInt = 0): BiggestInt =
else: return n.num
proc getNum*(n: JsonNode, default: BiggestInt = 0): BiggestInt {.deprecated.} =
- ## Deprecated - use getInt or getBiggestInt instead
+ ## **Deprecated since v0.18.2:** use ``getInt`` or ``getBiggestInt`` instead.
getBiggestInt(n, default)
proc getFloat*(n: JsonNode, default: float = 0.0): float =
@@ -710,7 +710,7 @@ proc getFloat*(n: JsonNode, default: float = 0.0): float =
else: return default
proc getFNum*(n: JsonNode, default: float = 0.0): float {.deprecated.} =
- ## Deprecated - use getFloat instead
+ ## **Deprecated since v0.18.2:** use ``getFloat`` instead.
getFloat(n, default)
proc getBool*(n: JsonNode, default: bool = false): bool =
@@ -721,7 +721,7 @@ proc getBool*(n: JsonNode, default: bool = false): bool =
else: return n.bval
proc getBVal*(n: JsonNode, default: bool = false): bool {.deprecated.} =
- ## Deprecated - use getBVal instead
+ ## **Deprecated since v0.18.2:** use ``getBool`` instead.
getBool(n, default)
proc getFields*(n: JsonNode,
@@ -947,7 +947,7 @@ proc contains*(node: JsonNode, val: JsonNode): bool =
find(node.elems, val) >= 0
proc existsKey*(node: JsonNode, key: string): bool {.deprecated.} = node.hasKey(key)
- ## Deprecated for `hasKey`
+ ## **Deprecated:** use `hasKey` instead.
proc `[]=`*(obj: JsonNode, key: string, val: JsonNode) {.inline.} =
## Sets a field from a `JObject`.
diff --git a/lib/pure/logging.nim b/lib/pure/logging.nim
index 751fc0e8dd..43b5ab3aba 100644
--- a/lib/pure/logging.nim
+++ b/lib/pure/logging.nim
@@ -126,7 +126,7 @@ proc substituteLog*(frmt: string, level: Level, args: varargs[string, `$`]): str
var v = ""
let app = when defined(js): "" else: getAppFilename()
while frmt[i] in IdentChars:
- v.add(toLower(frmt[i]))
+ v.add(toLowerAscii(frmt[i]))
inc(i)
case v
of "date": result.add(getDateStr())
diff --git a/lib/pure/matchers.nim b/lib/pure/matchers.nim
index 685c3b07ac..97223ed019 100644
--- a/lib/pure/matchers.nim
+++ b/lib/pure/matchers.nim
@@ -29,21 +29,21 @@ proc validEmailAddress*(s: string): bool {.noSideEffect,
chars = Letters + Digits + {'!','#','$','%','&',
'\'','*','+','/','=','?','^','_','`','{','}','|','~','-','.'}
var i = 0
- if s[i] notin chars or s[i] == '.': return false
- while s[i] in chars:
- if s[i] == '.' and s[i+1] == '.': return false
+ if i >= s.len or s[i] notin chars or s[i] == '.': return false
+ while i < s.len and s[i] in chars:
+ if i+1 < s.len and s[i] == '.' and s[i+1] == '.': return false
inc(i)
- if s[i] != '@': return false
+ if i >= s.len or s[i] != '@': return false
var j = len(s)-1
- if s[j] notin Letters: return false
+ if j >= 0 and s[j] notin Letters: return false
while j >= i and s[j] in Letters: dec(j)
inc(i) # skip '@'
- while s[i] in {'0'..'9', 'a'..'z', '-', '.'}: inc(i)
- if s[i] != '\0': return false
+ while i < s.len and s[i] in {'0'..'9', 'a'..'z', '-', '.'}: inc(i)
+ if i != s.len: return false
var x = substr(s, j+1)
if len(x) == 2 and x[0] in Letters and x[1] in Letters: return true
- case toLower(x)
+ case toLowerAscii(x)
of "com", "org", "net", "gov", "mil", "biz", "info", "mobi", "name",
"aero", "jobs", "museum": return true
else: return false
diff --git a/lib/pure/net.nim b/lib/pure/net.nim
index ebb59ca6d6..e522d86fef 100644
--- a/lib/pure/net.nim
+++ b/lib/pure/net.nim
@@ -1421,8 +1421,7 @@ proc sendTo*(socket: Socket, address: string, port: Port, data: pointer,
##
## **Note:** This proc is not available for SSL sockets.
assert(not socket.isClosed, "Cannot `sendTo` on a closed socket")
- var aiList = getAddrInfo(address, port, af)
-
+ var aiList = getAddrInfo(address, port, af, socket.sockType, socket.protocol)
# try all possibilities:
var success = false
var it = aiList
@@ -1443,7 +1442,7 @@ proc sendTo*(socket: Socket, address: string, port: Port,
## this function will try each IP of that hostname.
##
## This is the high-level version of the above ``sendTo`` function.
- result = socket.sendTo(address, port, cstring(data), data.len)
+ result = socket.sendTo(address, port, cstring(data), data.len, socket.domain )
proc isSsl*(socket: Socket): bool =
diff --git a/lib/pure/os.nim b/lib/pure/os.nim
index 11be8f0c12..644afee325 100644
--- a/lib/pure/os.nim
+++ b/lib/pure/os.nim
@@ -1060,18 +1060,17 @@ proc parseCmdLine*(c: string): seq[string] {.
while true:
setLen(a, 0)
# eat all delimiting whitespace
- while c[i] == ' ' or c[i] == '\t' or c[i] == '\l' or c[i] == '\r' : inc(i)
+ while i < c.len and c[i] in {' ', '\t', '\l', '\r'}: inc(i)
+ if i >= c.len: break
when defined(windows):
# parse a single argument according to the above rules:
- if c[i] == '\0': break
var inQuote = false
- while true:
+ while i < c.len:
case c[i]
- of '\0': break
of '\\':
var j = i
- while c[j] == '\\': inc(j)
- if c[j] == '"':
+ while j < c.len and c[j] == '\\': inc(j)
+ if j < c.len and c[j] == '"':
for k in 1..(j-i) div 2: a.add('\\')
if (j-i) mod 2 == 0:
i = j
@@ -1084,7 +1083,7 @@ proc parseCmdLine*(c: string): seq[string] {.
of '"':
inc(i)
if not inQuote: inQuote = true
- elif c[i] == '"':
+ elif i < c.len and c[i] == '"':
a.add(c[i])
inc(i)
else:
@@ -1102,13 +1101,12 @@ proc parseCmdLine*(c: string): seq[string] {.
of '\'', '\"':
var delim = c[i]
inc(i) # skip ' or "
- while c[i] != '\0' and c[i] != delim:
+ while i < c.len and c[i] != delim:
add a, c[i]
inc(i)
- if c[i] != '\0': inc(i)
- of '\0': break
+ if i < c.len: inc(i)
else:
- while c[i] > ' ':
+ while i < c.len and c[i] > ' ':
add(a, c[i])
inc(i)
add(result, a)
diff --git a/lib/pure/ospaths.nim b/lib/pure/ospaths.nim
index 0d638abb9b..ee2b715d3a 100644
--- a/lib/pure/ospaths.nim
+++ b/lib/pure/ospaths.nim
@@ -196,7 +196,7 @@ proc joinPath*(head, tail: string): string {.
else:
result = head & tail
else:
- if tail[0] in {DirSep, AltSep}:
+ if tail.len > 0 and tail[0] in {DirSep, AltSep}:
result = head & tail
else:
result = head & DirSep & tail
@@ -477,7 +477,7 @@ proc unixToNativePath*(path: string, drive=""): string {.
var i = start
while i < len(path): # ../../../ --> ::::
- if path[i] == '.' and path[i+1] == '.' and path[i+2] == '/':
+ if i+2 < path.len and path[i] == '.' and path[i+1] == '.' and path[i+2] == '/':
# parent directory
when defined(macos):
if result[high(result)] == ':':
diff --git a/lib/pure/parseopt.nim b/lib/pure/parseopt.nim
index ffb69a72b5..c1a6161f9e 100644
--- a/lib/pure/parseopt.nim
+++ b/lib/pure/parseopt.nim
@@ -57,26 +57,26 @@ type
{.deprecated: [TCmdLineKind: CmdLineKind, TOptParser: OptParser].}
proc parseWord(s: string, i: int, w: var string,
- delim: set[char] = {'\x09', ' ', '\0'}): int =
+ delim: set[char] = {'\x09', ' '}): int =
result = i
- if s[result] == '\"':
+ if result < s.len and s[result] == '\"':
inc(result)
- while not (s[result] in {'\0', '\"'}):
+ while result < s.len and s[result] != '\"':
add(w, s[result])
inc(result)
- if s[result] == '\"': inc(result)
+ if result < s.len and s[result] == '\"': inc(result)
else:
- while not (s[result] in delim):
+ while result < s.len and s[result] notin delim:
add(w, s[result])
inc(result)
when declared(os.paramCount):
proc quote(s: string): string =
- if find(s, {' ', '\t'}) >= 0 and s[0] != '"':
+ if find(s, {' ', '\t'}) >= 0 and s.len > 0 and s[0] != '"':
if s[0] == '-':
result = newStringOfCap(s.len)
- var i = parseWord(s, 0, result, {'\0', ' ', '\x09', ':', '='})
- if s[i] in {':','='}:
+ var i = parseWord(s, 0, result, {' ', '\x09', ':', '='})
+ if i < s.len and s[i] in {':','='}:
result.add s[i]
inc i
result.add '"'
@@ -144,43 +144,45 @@ proc handleShortOption(p: var OptParser) =
add(p.key.string, p.cmd[i])
inc(i)
p.inShortState = true
- while p.cmd[i] in {'\x09', ' '}:
+ while i < p.cmd.len and p.cmd[i] in {'\x09', ' '}:
inc(i)
p.inShortState = false
- if p.cmd[i] in {':', '='} or card(p.shortNoVal) > 0 and p.key.string[0] notin p.shortNoVal:
- if p.cmd[i] in {':', '='}:
+ if i < p.cmd.len and p.cmd[i] in {':', '='} or
+ card(p.shortNoVal) > 0 and p.key.string[0] notin p.shortNoVal:
+ if i < p.cmd.len and p.cmd[i] in {':', '='}:
inc(i)
p.inShortState = false
- while p.cmd[i] in {'\x09', ' '}: inc(i)
+ while i < p.cmd.len and p.cmd[i] in {'\x09', ' '}: inc(i)
i = parseWord(p.cmd, i, p.val.string)
- if p.cmd[i] == '\0': p.inShortState = false
+ if i >= p.cmd.len: p.inShortState = false
p.pos = i
proc next*(p: var OptParser) {.rtl, extern: "npo$1".} =
## parses the first or next option; ``p.kind`` describes what token has been
## parsed. ``p.key`` and ``p.val`` are set accordingly.
var i = p.pos
- while p.cmd[i] in {'\x09', ' '}: inc(i)
+ while i < p.cmd.len and p.cmd[i] in {'\x09', ' '}: inc(i)
p.pos = i
setLen(p.key.string, 0)
setLen(p.val.string, 0)
if p.inShortState:
handleShortOption(p)
return
- case p.cmd[i]
- of '\0':
+ if i >= p.cmd.len:
p.kind = cmdEnd
- of '-':
+ return
+ if p.cmd[i] == '-':
inc(i)
- if p.cmd[i] == '-':
+ if i < p.cmd.len and p.cmd[i] == '-':
p.kind = cmdLongOption
inc(i)
- i = parseWord(p.cmd, i, p.key.string, {'\0', ' ', '\x09', ':', '='})
- while p.cmd[i] in {'\x09', ' '}: inc(i)
- if p.cmd[i] in {':', '='} or len(p.longNoVal) > 0 and p.key.string notin p.longNoVal:
- if p.cmd[i] in {':', '='}:
+ i = parseWord(p.cmd, i, p.key.string, {' ', '\x09', ':', '='})
+ while i < p.cmd.len and p.cmd[i] in {'\x09', ' '}: inc(i)
+ if i < p.cmd.len and p.cmd[i] in {':', '='} or
+ len(p.longNoVal) > 0 and p.key.string notin p.longNoVal:
+ if i < p.cmd.len and p.cmd[i] in {':', '='}:
inc(i)
- while p.cmd[i] in {'\x09', ' '}: inc(i)
+ while i < p.cmd.len and p.cmd[i] in {'\x09', ' '}: inc(i)
p.pos = parseWord(p.cmd, i, p.val.string)
else:
p.pos = i
diff --git a/lib/pure/parseutils.nim b/lib/pure/parseutils.nim
index cecc94e925..1d76234ea0 100644
--- a/lib/pure/parseutils.nim
+++ b/lib/pure/parseutils.nim
@@ -51,9 +51,9 @@ proc parseHex*(s: string, number: var int, start = 0; maxLen = 0): int {.
## upper bound. Not more than ```maxLen`` characters are parsed.
var i = start
var foundDigit = false
- if s[i] == '0' and (s[i+1] == 'x' or s[i+1] == 'X'): inc(i, 2)
- elif s[i] == '#': inc(i)
let last = if maxLen == 0: s.len else: i+maxLen
+ if i+1 < last and s[i] == '0' and (s[i+1] == 'x' or s[i+1] == 'X'): inc(i, 2)
+ elif i < last and s[i] == '#': inc(i)
while i < last:
case s[i]
of '_': discard
@@ -76,8 +76,8 @@ proc parseOct*(s: string, number: var int, start = 0): int {.
## the number of the parsed characters or 0 in case of an error.
var i = start
var foundDigit = false
- if s[i] == '0' and (s[i+1] == 'o' or s[i+1] == 'O'): inc(i, 2)
- while true:
+ if i+1 < s.len and s[i] == '0' and (s[i+1] == 'o' or s[i+1] == 'O'): inc(i, 2)
+ while i < s.len:
case s[i]
of '_': discard
of '0'..'7':
@@ -93,8 +93,8 @@ proc parseBin*(s: string, number: var int, start = 0): int {.
## the number of the parsed characters or 0 in case of an error.
var i = start
var foundDigit = false
- if s[i] == '0' and (s[i+1] == 'b' or s[i+1] == 'B'): inc(i, 2)
- while true:
+ if i+1 < s.len and s[i] == '0' and (s[i+1] == 'b' or s[i+1] == 'B'): inc(i, 2)
+ while i < s.len:
case s[i]
of '_': discard
of '0'..'1':
@@ -108,9 +108,9 @@ proc parseIdent*(s: string, ident: var string, start = 0): int =
## parses an identifier and stores it in ``ident``. Returns
## the number of the parsed characters or 0 in case of an error.
var i = start
- if s[i] in IdentStartChars:
+ if i < s.len and s[i] in IdentStartChars:
inc(i)
- while s[i] in IdentChars: inc(i)
+ while i < s.len and s[i] in IdentChars: inc(i)
ident = substr(s, start, i-1)
result = i-start
@@ -119,11 +119,9 @@ proc parseIdent*(s: string, start = 0): string =
## Returns the parsed identifier or an empty string in case of an error.
result = ""
var i = start
-
- if s[i] in IdentStartChars:
+ if i < s.len and s[i] in IdentStartChars:
inc(i)
- while s[i] in IdentChars: inc(i)
-
+ while i < s.len and s[i] in IdentChars: inc(i)
result = substr(s, start, i-1)
proc parseToken*(s: string, token: var string, validChars: set[char],
@@ -134,24 +132,26 @@ proc parseToken*(s: string, token: var string, validChars: set[char],
##
## **Deprecated since version 0.8.12**: Use ``parseWhile`` instead.
var i = start
- while s[i] in validChars: inc(i)
+ while i < s.len and s[i] in validChars: inc(i)
result = i-start
token = substr(s, start, i-1)
proc skipWhitespace*(s: string, start = 0): int {.inline.} =
## skips the whitespace starting at ``s[start]``. Returns the number of
## skipped characters.
- while s[start+result] in Whitespace: inc(result)
+ while start+result < s.len and s[start+result] in Whitespace: inc(result)
proc skip*(s, token: string, start = 0): int {.inline.} =
## skips the `token` starting at ``s[start]``. Returns the length of `token`
## or 0 if there was no `token` at ``s[start]``.
- while result < token.len and s[result+start] == token[result]: inc(result)
+ while start+result < s.len and result < token.len and
+ s[result+start] == token[result]:
+ inc(result)
if result != token.len: result = 0
proc skipIgnoreCase*(s, token: string, start = 0): int =
## same as `skip` but case is ignored for token matching.
- while result < token.len and
+ while start+result < s.len and result < token.len and
toLower(s[result+start]) == toLower(token[result]): inc(result)
if result != token.len: result = 0
@@ -159,18 +159,18 @@ proc skipUntil*(s: string, until: set[char], start = 0): int {.inline.} =
## Skips all characters until one char from the set `until` is found
## or the end is reached.
## Returns number of characters skipped.
- while s[result+start] notin until and s[result+start] != '\0': inc(result)
+ while start+result < s.len and s[result+start] notin until: inc(result)
proc skipUntil*(s: string, until: char, start = 0): int {.inline.} =
## Skips all characters until the char `until` is found
## or the end is reached.
## Returns number of characters skipped.
- while s[result+start] != until and s[result+start] != '\0': inc(result)
+ while start+result < s.len and s[result+start] != until: inc(result)
proc skipWhile*(s: string, toSkip: set[char], start = 0): int {.inline.} =
## Skips all characters while one char from the set `token` is found.
## Returns number of characters skipped.
- while s[result+start] in toSkip and s[result+start] != '\0': inc(result)
+ while start+result < s.len and s[result+start] in toSkip: inc(result)
proc parseUntil*(s: string, token: var string, until: set[char],
start = 0): int {.inline.} =
@@ -214,7 +214,7 @@ proc parseWhile*(s: string, token: var string, validChars: set[char],
## the number of the parsed characters or 0 in case of an error. A token
## consists of the characters in `validChars`.
var i = start
- while s[i] in validChars: inc(i)
+ while i < s.len and s[i] in validChars: inc(i)
result = i-start
token = substr(s, start, i-1)
@@ -231,16 +231,17 @@ proc rawParseInt(s: string, b: var BiggestInt, start = 0): int =
var
sign: BiggestInt = -1
i = start
- if s[i] == '+': inc(i)
- elif s[i] == '-':
- inc(i)
- sign = 1
- if s[i] in {'0'..'9'}:
+ if i < s.len:
+ if s[i] == '+': inc(i)
+ elif s[i] == '-':
+ inc(i)
+ sign = 1
+ if i < s.len and s[i] in {'0'..'9'}:
b = 0
- while s[i] in {'0'..'9'}:
+ while i < s.len and s[i] in {'0'..'9'}:
b = b * 10 - (ord(s[i]) - ord('0'))
inc(i)
- while s[i] == '_': inc(i) # underscores are allowed and ignored
+ while i < s.len and s[i] == '_': inc(i) # underscores are allowed and ignored
b = b * sign
result = i - start
{.pop.} # overflowChecks
@@ -281,17 +282,17 @@ proc parseSaturatedNatural*(s: string, b: var int, start = 0): int =
## discard parseSaturatedNatural("848", res)
## doAssert res == 848
var i = start
- if s[i] == '+': inc(i)
- if s[i] in {'0'..'9'}:
+ if i < s.len and s[i] == '+': inc(i)
+ if i < s.len and s[i] in {'0'..'9'}:
b = 0
- while s[i] in {'0'..'9'}:
+ while i < s.len and s[i] in {'0'..'9'}:
let c = ord(s[i]) - ord('0')
if b <= (high(int) - c) div 10:
b = b * 10 + c
else:
b = high(int)
inc(i)
- while s[i] == '_': inc(i) # underscores are allowed and ignored
+ while i < s.len and s[i] == '_': inc(i) # underscores are allowed and ignored
result = i - start
# overflowChecks doesn't work with BiggestUInt
@@ -300,16 +301,16 @@ proc rawParseUInt(s: string, b: var BiggestUInt, start = 0): int =
res = 0.BiggestUInt
prev = 0.BiggestUInt
i = start
- if s[i] == '+': inc(i) # Allow
- if s[i] in {'0'..'9'}:
+ if i < s.len and s[i] == '+': inc(i) # Allow
+ if i < s.len and s[i] in {'0'..'9'}:
b = 0
- while s[i] in {'0'..'9'}:
+ while i < s.len and s[i] in {'0'..'9'}:
prev = res
res = res * 10 + (ord(s[i]) - ord('0')).BiggestUInt
if prev > res:
return 0 # overflowChecks emulation
inc(i)
- while s[i] == '_': inc(i) # underscores are allowed and ignored
+ while i < s.len and s[i] == '_': inc(i) # underscores are allowed and ignored
b = res
result = i - start
@@ -389,31 +390,31 @@ iterator interpolatedFragments*(s: string): tuple[kind: InterpolatedKind,
var kind: InterpolatedKind
while true:
var j = i
- if s[j] == '$':
- if s[j+1] == '{':
+ if j < s.len and s[j] == '$':
+ if j+1 < s.len and s[j+1] == '{':
inc j, 2
var nesting = 0
- while true:
- case s[j]
- of '{': inc nesting
- of '}':
- if nesting == 0:
- inc j
- break
- dec nesting
- of '\0':
- raise newException(ValueError,
- "Expected closing '}': " & substr(s, i, s.high))
- else: discard
- inc j
+ block curlies:
+ while j < s.len:
+ case s[j]
+ of '{': inc nesting
+ of '}':
+ if nesting == 0:
+ inc j
+ break curlies
+ dec nesting
+ else: discard
+ inc j
+ raise newException(ValueError,
+ "Expected closing '}': " & substr(s, i, s.high))
inc i, 2 # skip ${
kind = ikExpr
- elif s[j+1] in IdentStartChars:
+ elif j+1 < s.len and s[j+1] in IdentStartChars:
inc j, 2
- while s[j] in IdentChars: inc(j)
+ while j < s.len and s[j] in IdentChars: inc(j)
inc i # skip $
kind = ikVar
- elif s[j+1] == '$':
+ elif j+1 < s.len and s[j+1] == '$':
inc j, 2
inc i # skip $
kind = ikDollar
diff --git a/lib/pure/pegs.nim b/lib/pure/pegs.nim
index 5ae2d9182f..6d415efd06 100644
--- a/lib/pure/pegs.nim
+++ b/lib/pure/pegs.nim
@@ -534,15 +534,15 @@ proc rawMatch*(s: string, p: Peg, start: int, c: var Captures): int {.
case p.kind
of pkEmpty: result = 0 # match of length 0
of pkAny:
- if s[start] != '\0': result = 1
+ if start < s.len: result = 1
else: result = -1
of pkAnyRune:
- if s[start] != '\0':
+ if start < s.len:
result = runeLenAt(s, start)
else:
result = -1
of pkLetter:
- if s[start] != '\0':
+ if start < s.len:
var a: Rune
result = start
fastRuneAt(s, result, a)
@@ -551,7 +551,7 @@ proc rawMatch*(s: string, p: Peg, start: int, c: var Captures): int {.
else:
result = -1
of pkLower:
- if s[start] != '\0':
+ if start < s.len:
var a: Rune
result = start
fastRuneAt(s, result, a)
@@ -560,7 +560,7 @@ proc rawMatch*(s: string, p: Peg, start: int, c: var Captures): int {.
else:
result = -1
of pkUpper:
- if s[start] != '\0':
+ if start < s.len:
var a: Rune
result = start
fastRuneAt(s, result, a)
@@ -569,7 +569,7 @@ proc rawMatch*(s: string, p: Peg, start: int, c: var Captures): int {.
else:
result = -1
of pkTitle:
- if s[start] != '\0':
+ if start < s.len:
var a: Rune
result = start
fastRuneAt(s, result, a)
@@ -578,7 +578,7 @@ proc rawMatch*(s: string, p: Peg, start: int, c: var Captures): int {.
else:
result = -1
of pkWhitespace:
- if s[start] != '\0':
+ if start < s.len:
var a: Rune
result = start
fastRuneAt(s, result, a)
@@ -589,15 +589,15 @@ proc rawMatch*(s: string, p: Peg, start: int, c: var Captures): int {.
of pkGreedyAny:
result = len(s) - start
of pkNewLine:
- if s[start] == '\L': result = 1
- elif s[start] == '\C':
- if s[start+1] == '\L': result = 2
+ if start < s.len and s[start] == '\L': result = 1
+ elif start < s.len and s[start] == '\C':
+ if start+1 < s.len and s[start+1] == '\L': result = 2
else: result = 1
else: result = -1
of pkTerminal:
result = len(p.term)
for i in 0..result-1:
- if p.term[i] != s[start+i]:
+ if start+i >= s.len or p.term[i] != s[start+i]:
result = -1
break
of pkTerminalIgnoreCase:
@@ -606,6 +606,9 @@ proc rawMatch*(s: string, p: Peg, start: int, c: var Captures): int {.
a, b: Rune
result = start
while i < len(p.term):
+ if result >= s.len:
+ result = -1
+ break
fastRuneAt(p.term, i, a)
fastRuneAt(s, result, b)
if toLower(a) != toLower(b):
@@ -621,18 +624,23 @@ proc rawMatch*(s: string, p: Peg, start: int, c: var Captures): int {.
while true:
fastRuneAt(p.term, i, a)
if a != Rune('_'): break
- while true:
+ while result < s.len:
fastRuneAt(s, result, b)
if b != Rune('_'): break
- if toLower(a) != toLower(b):
+ if result >= s.len:
+ if i >= p.term.len: break
+ else:
+ result = -1
+ break
+ elif toLower(a) != toLower(b):
result = -1
break
dec(result, start)
of pkChar:
- if p.ch == s[start]: result = 1
+ if start < s.len and p.ch == s[start]: result = 1
else: result = -1
of pkCharChoice:
- if contains(p.charChoice[], s[start]): result = 1
+ if start < s.len and contains(p.charChoice[], s[start]): result = 1
else: result = -1
of pkNonTerminal:
var oldMl = c.ml
@@ -695,10 +703,10 @@ proc rawMatch*(s: string, p: Peg, start: int, c: var Captures): int {.
of pkGreedyRepChar:
result = 0
var ch = p.ch
- while ch == s[start+result]: inc(result)
+ while start+result < s.len and ch == s[start+result]: inc(result)
of pkGreedyRepSet:
result = 0
- while contains(p.charChoice[], s[start+result]): inc(result)
+ while start+result < s.len and contains(p.charChoice[], s[start+result]): inc(result)
of pkOption:
result = max(0, rawMatch(s, p.sons[0], start, c))
of pkAndPredicate:
diff --git a/lib/pure/strformat.nim b/lib/pure/strformat.nim
index abdb655d7c..a8b1284607 100644
--- a/lib/pure/strformat.nim
+++ b/lib/pure/strformat.nim
@@ -278,7 +278,7 @@ template callFormatOption(res, arg, option) {.dirty.} =
macro `&`*(pattern: string): untyped =
## For a specification of the ``&`` macro, see the module level documentation.
if pattern.kind notin {nnkStrLit..nnkTripleStrLit}:
- error "& only works with string literals", pattern
+ error "string formatting (fmt(), &) only works with string literals", pattern
let f = pattern.strVal
var i = 0
let res = genSym(nskVar, "fmtRes")
diff --git a/lib/pure/strscans.nim b/lib/pure/strscans.nim
index f8a0c43eec..b0af149b53 100644
--- a/lib/pure/strscans.nim
+++ b/lib/pure/strscans.nim
@@ -87,7 +87,7 @@ which we then use in our scanf pattern to help us in the matching process:
proc someSep(input: string; start: int; seps: set[char] = {':','-','.'}): int =
# Note: The parameters and return value must match to what ``scanf`` requires
result = 0
- while input[start+result] in seps: inc result
+ while start+result < input.len and input[start+result] in seps: inc result
if scanf(input, "$w$[someSep]$w", key, value):
...
@@ -231,7 +231,7 @@ is performed.
var i = start
var u = 0
while true:
- if s[i] == '\0' or s[i] == unless:
+ if i >= s.len or s[i] == unless:
return 0
elif s[i] == until[0]:
u = 1
@@ -315,6 +315,8 @@ macro scanf*(input: string; pattern: static[string]; results: varargs[typed]): b
conds.add resLen.notZero
conds.add resLen
+ template at(s: string; i: int): char = (if i < s.len: s[i] else: '\0')
+
var i = 0
var p = 0
var idx = genSym(nskVar, "idx")
@@ -397,7 +399,7 @@ macro scanf*(input: string; pattern: static[string]; results: varargs[typed]): b
var nesting = 0
let start = p
while true:
- case pattern[p]
+ case pattern.at(p)
of '{': inc nesting
of '}':
if nesting == 0: break
@@ -419,7 +421,7 @@ macro scanf*(input: string; pattern: static[string]; results: varargs[typed]): b
var nesting = 0
let start = p
while true:
- case pattern[p]
+ case pattern.at(p)
of '[': inc nesting
of ']':
if nesting == 0: break
@@ -451,10 +453,12 @@ macro scanf*(input: string; pattern: static[string]; results: varargs[typed]): b
template atom*(input: string; idx: int; c: char): bool =
## Used in scanp for the matching of atoms (usually chars).
- input[idx] == c
+ idx < input.len and input[idx] == c
template atom*(input: string; idx: int; s: set[char]): bool =
- input[idx] in s
+ idx < input.len and input[idx] in s
+
+template hasNxt*(input: string; idx: int): bool = idx < input.len
#template prepare*(input: string): int = 0
template success*(x: int): bool = x != 0
@@ -462,7 +466,7 @@ template success*(x: int): bool = x != 0
template nxt*(input: string; idx, step: int = 1) = inc(idx, step)
macro scanp*(input, idx: typed; pattern: varargs[untyped]): bool =
- ## ``scanp`` is currently undocumented.
+ ## See top level documentation of his module of how ``scanf`` works.
type StmtTriple = tuple[init, cond, action: NimNode]
template interf(x): untyped = bindSym(x, brForceOpen)
@@ -508,8 +512,8 @@ macro scanp*(input, idx: typed; pattern: varargs[untyped]): bool =
!!newCall(interf"nxt", input, idx, resLen))
of nnkCallKinds:
# *{'A'..'Z'} !! s.add(!_)
- template buildWhile(init, cond, action): untyped =
- while true:
+ template buildWhile(input, idx, init, cond, action): untyped =
+ while hasNxt(input, idx):
init
if not cond: break
action
@@ -528,7 +532,7 @@ macro scanp*(input, idx: typed; pattern: varargs[untyped]): bool =
!!newCall(interf"nxt", input, idx, it[2]))
elif it.kind == nnkPrefix and it[0].eqIdent"*":
let (init, cond, action) = atm(it[1], input, idx, attached)
- result = (getAst(buildWhile(init, cond, action)),
+ result = (getAst(buildWhile(input, idx, init, cond, action)),
newEmptyNode(), newEmptyNode())
elif it.kind == nnkPrefix and it[0].eqIdent"+":
# x+ is the same as xx*
@@ -621,7 +625,7 @@ macro scanp*(input, idx: typed; pattern: varargs[untyped]): bool =
when isMainModule:
proc twoDigits(input: string; x: var int; start: int): int =
- if input[start] == '0' and input[start+1] == '0':
+ if start+1 < input.len and input[start] == '0' and input[start+1] == '0':
result = 2
x = 13
else:
@@ -629,10 +633,10 @@ when isMainModule:
proc someSep(input: string; start: int; seps: set[char] = {';',',','-','.'}): int =
result = 0
- while input[start+result] in seps: inc result
+ while start+result < input.len and input[start+result] in seps: inc result
proc demangle(s: string; res: var string; start: int): int =
- while s[result+start] in {'_', '@'}: inc result
+ while result+start < s.len and s[result+start] in {'_', '@'}: inc result
res = ""
while result+start < s.len and s[result+start] > ' ' and s[result+start] != '_':
res.add s[result+start]
@@ -652,7 +656,7 @@ when isMainModule:
var info = ""
if scanp(resp, idx, *`whites`, '#', *`digits`, +`whites`, ?("0x", *`hexdigits`, " in "),
demangle($input, prc, $index), *`whites`, '(', * ~ ')', ')',
- *`whites`, "at ", +(~{'\C', '\L', '\0'} -> info.add($_)) ):
+ *`whites`, "at ", +(~{'\C', '\L'} -> info.add($_)) ):
result.add prc & " " & info
else:
break
@@ -713,7 +717,7 @@ when isMainModule:
"NimMainInner c:/users/anwender/projects/nim/lib/system.nim:2605",
"NimMain c:/users/anwender/projects/nim/lib/system.nim:2613",
"main c:/users/anwender/projects/nim/lib/system.nim:2620"]
- doAssert parseGDB(gdbOut) == result
+ #doAssert parseGDB(gdbOut) == result
# bug #6487
var count = 0
diff --git a/lib/pure/strtabs.nim b/lib/pure/strtabs.nim
index 75c5e171d8..75f9b42cd0 100644
--- a/lib/pure/strtabs.nim
+++ b/lib/pure/strtabs.nim
@@ -29,7 +29,7 @@ type
modeCaseSensitive, ## the table is case sensitive
modeCaseInsensitive, ## the table is case insensitive
modeStyleInsensitive ## the table is style insensitive
- KeyValuePair = tuple[key, val: string]
+ KeyValuePair = tuple[key, val: string, hasValue: bool]
KeyValuePairSeq = seq[KeyValuePair]
StringTableObj* = object of RootObj
counter: int
@@ -48,19 +48,19 @@ proc len*(t: StringTableRef): int {.rtlFunc, extern: "nst$1".} =
iterator pairs*(t: StringTableRef): tuple[key, value: string] =
## iterates over every (key, value) pair in the table `t`.
for h in 0..high(t.data):
- if not isNil(t.data[h].key):
+ if t.data[h].hasValue:
yield (t.data[h].key, t.data[h].val)
iterator keys*(t: StringTableRef): string =
## iterates over every key in the table `t`.
for h in 0..high(t.data):
- if not isNil(t.data[h].key):
+ if t.data[h].hasValue:
yield t.data[h].key
iterator values*(t: StringTableRef): string =
## iterates over every value in the table `t`.
for h in 0..high(t.data):
- if not isNil(t.data[h].key):
+ if t.data[h].hasValue:
yield t.data[h].val
type
@@ -102,7 +102,7 @@ proc nextTry(h, maxHash: Hash): Hash {.inline.} =
proc rawGet(t: StringTableRef, key: string): int =
var h: Hash = myhash(t, key) and high(t.data) # start with real hash value
- while not isNil(t.data[h].key):
+ while t.data[h].hasValue:
if myCmp(t, t.data[h].key, key):
return h
h = nextTry(h, high(t.data))
@@ -144,16 +144,17 @@ proc contains*(t: StringTableRef, key: string): bool =
proc rawInsert(t: StringTableRef, data: var KeyValuePairSeq, key, val: string) =
var h: Hash = myhash(t, key) and high(data)
- while not isNil(data[h].key):
+ while data[h].hasValue:
h = nextTry(h, high(data))
data[h].key = key
data[h].val = val
+ data[h].hasValue = true
proc enlarge(t: StringTableRef) =
var n: KeyValuePairSeq
newSeq(n, len(t.data) * growthFactor)
for i in countup(0, high(t.data)):
- if not isNil(t.data[i].key): rawInsert(t, n, t.data[i].key, t.data[i].val)
+ if t.data[i].hasValue: rawInsert(t, n, t.data[i].key, t.data[i].val)
swap(t.data, n)
proc `[]=`*(t: StringTableRef, key, val: string) {.rtlFunc, extern: "nstPut".} =
@@ -198,8 +199,7 @@ proc clear*(s: StringTableRef, mode: StringTableMode) =
s.counter = 0
s.data.setLen(startSize)
for i in 0..`_ for a version that works for any Unicode
## character.
- result = newString(len(s))
- for i in 0..len(s) - 1:
- result[i] = toLowerAscii(s[i])
+ toImpl toLowerAscii
proc toUpperAscii*(c: char): char {.noSideEffect, procvar,
rtl, extern: "nsuToUpperAsciiChar".} =
@@ -239,147 +213,15 @@ proc toUpperAscii*(s: string): string {.noSideEffect, procvar,
## This works only for the letters ``A-Z``. See `unicode.toUpper
## `_ for a version that works for any Unicode
## character.
- result = newString(len(s))
- for i in 0..len(s) - 1:
- result[i] = toUpperAscii(s[i])
+ toImpl toUpperAscii
proc capitalizeAscii*(s: string): string {.noSideEffect, procvar,
rtl, extern: "nsuCapitalizeAscii".} =
## Converts the first character of `s` into upper case.
##
## This works only for the letters ``A-Z``.
- result = toUpperAscii(s[0]) & substr(s, 1)
-
-proc isSpace*(c: char): bool {.noSideEffect, procvar,
- rtl, deprecated, extern: "nsuIsSpaceChar".}=
- ## Checks whether or not `c` is a whitespace character.
- ##
- ## **Deprecated since version 0.15.0**: use ``isSpaceAscii`` instead.
- isSpaceAscii(c)
-
-proc isLower*(c: char): bool {.noSideEffect, procvar,
- rtl, deprecated, extern: "nsuIsLowerChar".}=
- ## Checks whether or not `c` is a lower case character.
- ##
- ## This checks ASCII characters only.
- ##
- ## **Deprecated since version 0.15.0**: use ``isLowerAscii`` instead.
- isLowerAscii(c)
-
-proc isUpper*(c: char): bool {.noSideEffect, procvar,
- rtl, deprecated, extern: "nsuIsUpperChar".}=
- ## Checks whether or not `c` is an upper case character.
- ##
- ## This checks ASCII characters only.
- ##
- ## **Deprecated since version 0.15.0**: use ``isUpperAscii`` instead.
- isUpperAscii(c)
-
-proc isAlpha*(c: char): bool {.noSideEffect, procvar,
- rtl, deprecated, extern: "nsuIsAlphaChar".}=
- ## Checks whether or not `c` is alphabetical.
- ##
- ## This checks a-z, A-Z ASCII characters only.
- ##
- ## **Deprecated since version 0.15.0**: use ``isAlphaAscii`` instead.
- isAlphaAscii(c)
-
-proc isAlpha*(s: string): bool {.noSideEffect, procvar,
- rtl, deprecated, extern: "nsuIsAlphaStr".}=
- ## Checks whether or not `s` is alphabetical.
- ##
- ## This checks a-z, A-Z ASCII characters only.
- ## Returns true if all characters in `s` are
- ## alphabetic and there is at least one character
- ## in `s`.
- ##
- ## **Deprecated since version 0.15.0**: use ``isAlphaAscii`` instead.
- isAlphaAscii(s)
-
-proc isSpace*(s: string): bool {.noSideEffect, procvar,
- rtl, deprecated, extern: "nsuIsSpaceStr".}=
- ## Checks whether or not `s` is completely whitespace.
- ##
- ## Returns true if all characters in `s` are whitespace
- ## characters and there is at least one character in `s`.
- ##
- ## **Deprecated since version 0.15.0**: use ``isSpaceAscii`` instead.
- isSpaceAscii(s)
-
-proc isLower*(s: string): bool {.noSideEffect, procvar,
- rtl, deprecated, extern: "nsuIsLowerStr".}=
- ## Checks whether or not `s` contains all lower case characters.
- ##
- ## This checks ASCII characters only.
- ## Returns true if all characters in `s` are lower case
- ## and there is at least one character in `s`.
- ##
- ## **Deprecated since version 0.15.0**: use ``isLowerAscii`` instead.
- isLowerAscii(s)
-
-proc isUpper*(s: string): bool {.noSideEffect, procvar,
- rtl, deprecated, extern: "nsuIsUpperStr".}=
- ## Checks whether or not `s` contains all upper case characters.
- ##
- ## This checks ASCII characters only.
- ## Returns true if all characters in `s` are upper case
- ## and there is at least one character in `s`.
- ##
- ## **Deprecated since version 0.15.0**: use ``isUpperAscii`` instead.
- isUpperAscii(s)
-
-proc toLower*(c: char): char {.noSideEffect, procvar,
- rtl, deprecated, extern: "nsuToLowerChar".} =
- ## Converts `c` into lower case.
- ##
- ## This works only for the letters ``A-Z``. See `unicode.toLower
- ## `_ for a version that works for any Unicode
- ## character.
- ##
- ## **Deprecated since version 0.15.0**: use ``toLowerAscii`` instead.
- toLowerAscii(c)
-
-proc toLower*(s: string): string {.noSideEffect, procvar,
- rtl, deprecated, extern: "nsuToLowerStr".} =
- ## Converts `s` into lower case.
- ##
- ## This works only for the letters ``A-Z``. See `unicode.toLower
- ## `_ for a version that works for any Unicode
- ## character.
- ##
- ## **Deprecated since version 0.15.0**: use ``toLowerAscii`` instead.
- toLowerAscii(s)
-
-proc toUpper*(c: char): char {.noSideEffect, procvar,
- rtl, deprecated, extern: "nsuToUpperChar".} =
- ## Converts `c` into upper case.
- ##
- ## This works only for the letters ``A-Z``. See `unicode.toUpper
- ## `_ for a version that works for any Unicode
- ## character.
- ##
- ## **Deprecated since version 0.15.0**: use ``toUpperAscii`` instead.
- toUpperAscii(c)
-
-proc toUpper*(s: string): string {.noSideEffect, procvar,
- rtl, deprecated, extern: "nsuToUpperStr".} =
- ## Converts `s` into upper case.
- ##
- ## This works only for the letters ``A-Z``. See `unicode.toUpper
- ## `_ for a version that works for any Unicode
- ## character.
- ##
- ## **Deprecated since version 0.15.0**: use ``toUpperAscii`` instead.
- toUpperAscii(s)
-
-proc capitalize*(s: string): string {.noSideEffect, procvar,
- rtl, deprecated, extern: "nsuCapitalize".} =
- ## Converts the first character of `s` into upper case.
- ##
- ## This works only for the letters ``A-Z``.
- ##
- ## **Deprecated since version 0.15.0**: use ``capitalizeAscii`` instead.
- capitalizeAscii(s)
+ if s.len == 0: result = ""
+ else: result = toUpperAscii(s[0]) & substr(s, 1)
proc normalize*(s: string): string {.noSideEffect, procvar,
rtl, extern: "nsuNormalize".} =
@@ -419,9 +261,9 @@ proc cmpIgnoreCase*(a, b: string): int {.noSideEffect,
proc cmpIgnoreStyle*(a, b: string): int {.noSideEffect,
rtl, extern: "nsuCmpIgnoreStyle", procvar.} =
## Semantically the same as ``cmp(normalize(a), normalize(b))``. It
- ## is just optimized to not allocate temporary strings. This should
+ ## is just optimized to not allocate temporary strings. This should
## NOT be used to compare Nim identifier names. use `macros.eqIdent`
- ## for that. Returns:
+ ## for that. Returns:
##
## | 0 iff a == b
## | < 0 iff a < b
@@ -429,14 +271,22 @@ proc cmpIgnoreStyle*(a, b: string): int {.noSideEffect,
var i = 0
var j = 0
while true:
- while a[i] == '_': inc(i)
- while b[j] == '_': inc(j) # BUGFIX: typo
- var aa = toLowerAscii(a[i])
- var bb = toLowerAscii(b[j])
+ while i < a.len and a[i] == '_': inc i
+ while j < b.len and b[j] == '_': inc j
+ var aa = if i < a.len: toLowerAscii(a[i]) else: '\0'
+ var bb = if j < b.len: toLowerAscii(b[j]) else: '\0'
result = ord(aa) - ord(bb)
- if result != 0 or aa == '\0': break
- inc(i)
- inc(j)
+ if result != 0: return result
+ # the characters are identical:
+ if i >= a.len:
+ # both cursors at the end:
+ if j >= b.len: return 0
+ # not yet at the end of 'b':
+ return -1
+ elif j >= b.len:
+ return 1
+ inc i
+ inc j
proc strip*(s: string, leading = true, trailing = true,
chars: set[char] = Whitespace): string
@@ -451,7 +301,7 @@ proc strip*(s: string, leading = true, trailing = true,
first = 0
last = len(s)-1
if leading:
- while s[first] in chars: inc(first)
+ while first <= last and s[first] in chars: inc(first)
if trailing:
while last >= 0 and s[last] in chars: dec(last)
result = substr(s, first, last)
@@ -467,7 +317,9 @@ proc toOctal*(c: char): string {.noSideEffect, rtl, extern: "nsuToOctal".} =
result[i] = chr(val mod 8 + ord('0'))
val = val div 8
-proc isNilOrEmpty*(s: string): bool {.noSideEffect, procvar, rtl, extern: "nsuIsNilOrEmpty".} =
+proc isNilOrEmpty*(s: string): bool {.noSideEffect, procvar, rtl,
+ extern: "nsuIsNilOrEmpty",
+ deprecated: "use 'x.len == 0' instead".} =
## Checks if `s` is nil or empty.
result = len(s) == 0
@@ -486,7 +338,6 @@ proc substrEq(s: string, pos: int, substr: string): bool =
var length = substr.len
while i < length and s[pos+i] == substr[i]:
inc i
-
return i == length
# --------- Private templates for different split separators -----------
@@ -520,7 +371,7 @@ template oldSplit(s, seps, maxsplit) =
var splits = maxsplit
assert(not ('\0' in seps))
while last < len(s):
- while s[last] in seps: inc(last)
+ while last < len(s) and s[last] in seps: inc(last)
var first = last
while last < len(s) and s[last] notin seps: inc(last)
if first <= last-1:
@@ -571,10 +422,7 @@ iterator split*(s: string, seps: set[char] = Whitespace,
## "08"
## "08.398990"
##
- when defined(nimOldSplit):
- oldSplit(s, seps, maxsplit)
- else:
- splitCommon(s, seps, maxsplit, 1)
+ splitCommon(s, seps, maxsplit, 1)
iterator splitWhitespace*(s: string, maxsplit: int = -1): string =
## Splits the string ``s`` at whitespace stripping leading and trailing
@@ -660,7 +508,6 @@ iterator split*(s: string, sep: string, maxsplit: int = -1): string =
## "is"
## "corrupted"
##
-
splitCommon(s, sep, maxsplit, sep.len)
template rsplitCommon(s, sep, maxsplit, sepLen) =
@@ -670,29 +517,21 @@ template rsplitCommon(s, sep, maxsplit, sepLen) =
first = last
splits = maxsplit
startPos = 0
-
# go to -1 in order to get separators at the beginning
while first >= -1:
while first >= 0 and not stringHasSep(s, first, sep):
dec(first)
-
if splits == 0:
# No more splits means set first to the beginning
first = -1
-
if first == -1:
startPos = 0
else:
startPos = first + sepLen
-
yield substr(s, startPos, last)
-
- if splits == 0:
- break
-
+ if splits == 0: break
dec(splits)
dec(first)
-
last = first
iterator rsplit*(s: string, seps: set[char] = Whitespace,
@@ -712,7 +551,6 @@ iterator rsplit*(s: string, seps: set[char] = Whitespace,
## "foo"
##
## Substrings are separated from the right by the set of chars `seps`
-
rsplitCommon(s, seps, maxsplit, 1)
iterator rsplit*(s: string, sep: char,
@@ -779,14 +617,14 @@ iterator splitLines*(s: string): string =
var first = 0
var last = 0
while true:
- while s[last] notin {'\0', '\c', '\l'}: inc(last)
+ while last < s.len and s[last] notin {'\c', '\l'}: inc(last)
yield substr(s, first, last-1)
# skip newlines:
+ if last >= s.len: break
if s[last] == '\l': inc(last)
elif s[last] == '\c':
inc(last)
- if s[last] == '\l': inc(last)
- else: break # was '\0'
+ if last < s.len and s[last] == '\l': inc(last)
first = last
proc splitLines*(s: string): seq[string] {.noSideEffect,
@@ -811,7 +649,7 @@ proc countLines*(s: string): int {.noSideEffect,
while i < s.len:
case s[i]
of '\c':
- if s[i+1] == '\l': inc i
+ if i+1 < s.len and s[i+1] == '\l': inc i
inc result
of '\l': inc result
else: discard
@@ -1025,9 +863,9 @@ proc parseHexInt*(s: string): int {.noSideEffect, procvar,
## of the following optional prefixes: ``0x``, ``0X``, ``#``. Underscores
## within `s` are ignored.
var i = 0
- if s[i] == '0' and (s[i+1] == 'x' or s[i+1] == 'X'): inc(i, 2)
- elif s[i] == '#': inc(i)
- while true:
+ if i+1 < s.len and s[i] == '0' and (s[i+1] == 'x' or s[i+1] == 'X'): inc(i, 2)
+ elif i < s.len and s[i] == '#': inc(i)
+ while i < s.len:
case s[i]
of '_': inc(i)
of '0'..'9':
@@ -1039,7 +877,6 @@ proc parseHexInt*(s: string): int {.noSideEffect, procvar,
of 'A'..'F':
result = result shl 4 or (ord(s[i]) - ord('A') + 10)
inc(i)
- of '\0': break
else: raise newException(ValueError, "invalid integer: " & s)
proc generateHexCharToValueMap(): string =
@@ -1148,14 +985,6 @@ template spaces*(n: Natural): string = repeat(' ', n)
## echo text1 & spaces(max(0, width - text1.len)) & "|"
## echo text2 & spaces(max(0, width - text2.len)) & "|"
-proc repeatChar*(count: Natural, c: char = ' '): string {.deprecated.} =
- ## deprecated: use repeat() or spaces()
- repeat(c, count)
-
-proc repeatStr*(count: Natural, s: string): string {.deprecated.} =
- ## deprecated: use repeat(string, count) or string.repeat(count)
- repeat(s, count)
-
proc align*(s: string, count: Natural, padding = ' '): string {.
noSideEffect, rtl, extern: "nsuAlignString".} =
## Aligns a string `s` with `padding`, so that it is of length `count`.
@@ -1226,7 +1055,7 @@ iterator tokenize*(s: string, seps: set[char] = Whitespace): tuple[
var i = 0
while true:
var j = i
- var isSep = s[j] in seps
+ var isSep = j < s.len and s[j] in seps
while j < s.len and (s[j] in seps) == isSep: inc(j)
if j > i:
yield (substr(s, i, j-1), isSep)
@@ -1297,7 +1126,7 @@ proc unindent*(s: string, count: Natural, padding: string = " "): string
var indentCount = 0
for j in 0..= line.len or line[j .. j + padding.len-1] != padding:
indentCount = j
break
result.add(line[indentCount*padding.len .. ^1])
@@ -1325,13 +1154,13 @@ proc startsWith*(s, prefix: string): bool {.noSideEffect,
## If ``prefix == ""`` true is returned.
var i = 0
while true:
- if prefix[i] == '\0': return true
- if s[i] != prefix[i]: return false
+ if i >= prefix.len: return true
+ if i >= s.len or s[i] != prefix[i]: return false
inc(i)
proc startsWith*(s: string, prefix: char): bool {.noSideEffect, inline.} =
## Returns true iff ``s`` starts with ``prefix``.
- result = s[0] == prefix
+ result = s.len > 0 and s[0] == prefix
proc endsWith*(s, suffix: string): bool {.noSideEffect,
rtl, extern: "nsuEndsWith".} =
@@ -1343,11 +1172,11 @@ proc endsWith*(s, suffix: string): bool {.noSideEffect,
while i+j <% s.len:
if s[i+j] != suffix[i]: return false
inc(i)
- if suffix[i] == '\0': return true
+ if i >= suffix.len: return true
proc endsWith*(s: string, suffix: char): bool {.noSideEffect, inline.} =
## Returns true iff ``s`` ends with ``suffix``.
- result = s[s.high] == suffix
+ result = s.len > 0 and s[s.high] == suffix
proc continuesWith*(s, substr: string, start: Natural): bool {.noSideEffect,
rtl, extern: "nsuContinuesWith".} =
@@ -1356,8 +1185,8 @@ proc continuesWith*(s, substr: string, start: Natural): bool {.noSideEffect,
## If ``substr == ""`` true is returned.
var i = 0
while true:
- if substr[i] == '\0': return true
- if s[i+start] != substr[i]: return false
+ if i >= substr.len: return true
+ if i+start >= s.len or s[i+start] != substr[i]: return false
inc(i)
proc addSep*(dest: var string, sep = ", ", startLen: Natural = 0)
@@ -1502,12 +1331,8 @@ proc find*(s, sub: string, start: Natural = 0, last: Natural = 0): int {.noSideE
## If `last` is unspecified, it defaults to `s.high`.
##
## Searching is case-sensitive. If `sub` is not in `s`, -1 is returned.
- if sub.len > s.len:
- return -1
-
- if sub.len == 1:
- return find(s, sub[0], start, last)
-
+ if sub.len > s.len: return -1
+ if sub.len == 1: return find(s, sub[0], start, last)
var a {.noinit.}: SkipTable
initSkipTable(a, sub)
result = find(a, s, sub, start, last)
@@ -1564,18 +1389,14 @@ proc center*(s: string, width: int, fillChar: char = ' '): string {.
##
## The original string is returned if `width` is less than or equal
## to `s.len`.
- if width <= s.len:
- return s
-
+ if width <= s.len: return s
result = newString(width)
-
# Left padding will be one fillChar
# smaller if there are an odd number
# of characters
let
charsLeft = (width - s.len)
leftPadding = charsLeft div 2
-
for i in 0 ..< width:
if i >= leftPadding and i < leftPadding + s.len:
# we are where the string should be located
@@ -1593,27 +1414,22 @@ proc count*(s: string, sub: string, overlapping: bool = false): int {.
var i = 0
while true:
i = s.find(sub, i)
- if i < 0:
- break
- if overlapping:
- inc i
- else:
- i += sub.len
+ if i < 0: break
+ if overlapping: inc i
+ else: i += sub.len
inc result
proc count*(s: string, sub: char): int {.noSideEffect,
rtl, extern: "nsuCountChar".} =
## Count the occurrences of the character `sub` in the string `s`.
for c in s:
- if c == sub:
- inc result
+ if c == sub: inc result
proc count*(s: string, subs: set[char]): int {.noSideEffect,
rtl, extern: "nsuCountCharSet".} =
## Count the occurrences of the group of character `subs` in the string `s`.
for c in s:
- if c in subs:
- inc result
+ if c in subs: inc result
proc quoteIfContainsWhite*(s: string): string {.deprecated.} =
## Returns ``'"' & s & '"'`` if `s` contains a space and does not
@@ -1621,10 +1437,8 @@ proc quoteIfContainsWhite*(s: string): string {.deprecated.} =
##
## **DEPRECATED** as it was confused for shell quoting function. For this
## application use `osproc.quoteShell `_.
- if find(s, {' ', '\t'}) >= 0 and s[0] != '"':
- result = '"' & s & '"'
- else:
- result = s
+ if find(s, {' ', '\t'}) >= 0 and s[0] != '"': result = '"' & s & '"'
+ else: result = s
proc contains*(s: string, c: char): bool {.noSideEffect.} =
## Same as ``find(s, c) >= 0``.
@@ -1704,9 +1518,8 @@ proc multiReplace*(s: string, replacements: varargs[(string, string)]): string {
## Same as replace, but specialized for doing multiple replacements in a single
## pass through the input string.
##
- ## Calling replace multiple times after each other is inefficient and result in too many allocations
- ## follwed by immediate deallocations as portions of the string gets replaced.
- ## multiReplace performs all replacements in a single pass.
+ ## multiReplace performs all replacements in a single pass, this means it can be used
+ ## to swap the occurences of "a" and "b", for instance.
##
## If the resulting string is not longer than the original input string, only a single
## memory allocation is required.
@@ -1753,14 +1566,13 @@ proc parseOctInt*(s: string): int {.noSideEffect,
## of the following optional prefixes: ``0o``, ``0O``. Underscores within
## `s` are ignored.
var i = 0
- if s[i] == '0' and (s[i+1] == 'o' or s[i+1] == 'O'): inc(i, 2)
- while true:
+ if i+1 < s.len and s[i] == '0' and (s[i+1] == 'o' or s[i+1] == 'O'): inc(i, 2)
+ while i < s.len:
case s[i]
of '_': inc(i)
of '0'..'7':
result = result shl 3 or (ord(s[i]) - ord('0'))
inc(i)
- of '\0': break
else: raise newException(ValueError, "invalid integer: " & s)
proc toOct*(x: BiggestInt, len: Positive): string {.noSideEffect,
@@ -1849,16 +1661,18 @@ proc unescape*(s: string, prefix = "\"", suffix = "\""): string {.noSideEffect,
## If `s` does not begin with ``prefix`` and end with ``suffix`` a
## ValueError exception will be raised.
##
- ## **Warning:** This procedure is deprecated because it's to easy to missuse.
+ ## **Warning:** This procedure is deprecated because it's to easy to missuse.
result = newStringOfCap(s.len)
var i = prefix.len
if not s.startsWith(prefix):
raise newException(ValueError,
- "String does not start with a prefix of: " & prefix)
+ "String does not start with: " & prefix)
while true:
- if i == s.len-suffix.len: break
- case s[i]
- of '\\':
+ if i >= s.len-suffix.len: break
+ if s[i] == '\\':
+ if i+1 >= s.len:
+ result.add('\\')
+ break
case s[i+1]:
of 'x':
inc i, 2
@@ -1872,15 +1686,15 @@ proc unescape*(s: string, prefix = "\"", suffix = "\""): string {.noSideEffect,
result.add('\'')
of '\"':
result.add('\"')
- else: result.add("\\" & s[i+1])
- inc(i)
- of '\0': break
+ else:
+ result.add("\\" & s[i+1])
+ inc(i, 2)
else:
result.add(s[i])
- inc(i)
+ inc(i)
if not s.endsWith(suffix):
raise newException(ValueError,
- "String does not end with a suffix of: " & suffix)
+ "String does not end in: " & suffix)
proc validIdentifier*(s: string): bool {.noSideEffect,
rtl, extern: "nsuValidIdentifier".} =
@@ -1890,7 +1704,7 @@ proc validIdentifier*(s: string): bool {.noSideEffect,
## and is followed by any number of characters of the set `IdentChars`.
runnableExamples:
doAssert "abc_def08".validIdentifier
- if s[0] in IdentStartChars:
+ if s.len > 0 and s[0] in IdentStartChars:
for i in 1..s.len-1:
if s[i] notin IdentChars: return false
return true
@@ -1909,7 +1723,7 @@ proc editDistance*(a, b: string): int {.noSideEffect,
# strip common prefix:
var s = 0
- while a[s] == b[s] and a[s] != '\0':
+ while s < len1 and a[s] == b[s]:
inc(s)
dec(len1)
dec(len2)
@@ -1982,8 +1796,6 @@ proc editDistance*(a, b: string): int {.noSideEffect,
if x > c3: x = c3
row[p] = x
result = row[e]
- #dealloc(row)
-
# floating point formating:
when not defined(js):
@@ -2092,7 +1904,7 @@ proc trimZeros*(x: var string) {.noSideEffect.} =
var spl: seq[string]
if x.contains('.') or x.contains(','):
if x.contains('e'):
- spl= x.split('e')
+ spl = x.split('e')
x = spl[0]
while x[x.high] == '0':
x.setLen(x.len-1)
@@ -2310,9 +2122,8 @@ proc addf*(s: var string, formatstr: string, a: varargs[string, `$`]) {.
var i = 0
var num = 0
while i < len(formatstr):
- if formatstr[i] == '$':
- case formatstr[i+1] # again we use the fact that strings
- # are zero-terminated here
+ if formatstr[i] == '$' and i+1 < len(formatstr):
+ case formatstr[i+1]
of '#':
if num > a.high: invalidFormatString()
add s, a[num]
@@ -2326,7 +2137,7 @@ proc addf*(s: var string, formatstr: string, a: varargs[string, `$`]) {.
inc(i) # skip $
var negative = formatstr[i] == '-'
if negative: inc i
- while formatstr[i] in Digits:
+ while i < formatstr.len and formatstr[i] in Digits:
j = j * 10 + ord(formatstr[i]) - ord('0')
inc(i)
let idx = if not negative: j-1 else: a.len-j
@@ -2338,7 +2149,7 @@ proc addf*(s: var string, formatstr: string, a: varargs[string, `$`]) {.
var negative = formatstr[j] == '-'
if negative: inc j
var isNumber = 0
- while formatstr[j] notin {'\0', '}'}:
+ while j < formatstr.len and formatstr[j] notin {'\0', '}'}:
if formatstr[j] in Digits:
k = k * 10 + ord(formatstr[j]) - ord('0')
if isNumber == 0: isNumber = 1
@@ -2356,7 +2167,7 @@ proc addf*(s: var string, formatstr: string, a: varargs[string, `$`]) {.
i = j+1
of 'a'..'z', 'A'..'Z', '\128'..'\255', '_':
var j = i+1
- while formatstr[j] in PatternChars: inc(j)
+ while j < formatstr.len and formatstr[j] in PatternChars: inc(j)
var x = findNormalized(substr(formatstr, i+1, j-1), a)
if x >= 0 and x < high(a): add s, a[x+1]
else: invalidFormatString()
@@ -2628,13 +2439,7 @@ when isMainModule:
doAssert isSpaceAscii(" ")
doAssert(not isSpaceAscii("ABc \td"))
- doAssert(isNilOrEmpty(""))
- doAssert(isNilOrEmpty(nil))
- doAssert(not isNilOrEmpty("test"))
- doAssert(not isNilOrEmpty(" "))
-
doAssert(isNilOrWhitespace(""))
- doAssert(isNilOrWhitespace(nil))
doAssert(isNilOrWhitespace(" "))
doAssert(isNilOrWhitespace("\t\l \v\r\f"))
doAssert(not isNilOrWhitespace("ABc \td"))
diff --git a/lib/pure/times.nim b/lib/pure/times.nim
index 7d101beab4..bc8a50fd72 100644
--- a/lib/pure/times.nim
+++ b/lib/pure/times.nim
@@ -1318,24 +1318,23 @@ proc format*(dt: DateTime, f: string): string {.tags: [].}=
result = ""
var i = 0
var currentF = ""
- while true:
+ while i < f.len:
case f[i]
- of ' ', '-', '/', ':', '\'', '\0', '(', ')', '[', ']', ',':
+ of ' ', '-', '/', ':', '\'', '(', ')', '[', ']', ',':
formatToken(dt, currentF, result)
currentF = ""
- if f[i] == '\0': break
if f[i] == '\'':
inc(i) # Skip '
- while f[i] != '\'' and f.len-1 > i:
+ while i < f.len-1 and f[i] != '\'':
result.add(f[i])
inc(i)
else: result.add(f[i])
else:
# Check if the letter being added matches previous accumulated buffer.
- if currentF.len < 1 or currentF[high(currentF)] == f[i]:
+ if currentF.len == 0 or currentF[high(currentF)] == f[i]:
currentF.add(f[i])
else:
formatToken(dt, currentF, result)
@@ -1343,6 +1342,7 @@ proc format*(dt: DateTime, f: string): string {.tags: [].}=
currentF = ""
inc(i)
+ formatToken(dt, currentF, result)
proc `$`*(dt: DateTime): string {.tags: [], raises: [], benign.} =
## Converts a `DateTime` object to a string representation.
@@ -1439,58 +1439,58 @@ proc parseToken(dt: var DateTime; token, value: string; j: var int) =
dt.month = month.Month
of "MMM":
case value[j..j+2].toLowerAscii():
- of "jan": dt.month = mJan
- of "feb": dt.month = mFeb
- of "mar": dt.month = mMar
- of "apr": dt.month = mApr
- of "may": dt.month = mMay
- of "jun": dt.month = mJun
- of "jul": dt.month = mJul
- of "aug": dt.month = mAug
- of "sep": dt.month = mSep
- of "oct": dt.month = mOct
- of "nov": dt.month = mNov
- of "dec": dt.month = mDec
+ of "jan": dt.month = mJan
+ of "feb": dt.month = mFeb
+ of "mar": dt.month = mMar
+ of "apr": dt.month = mApr
+ of "may": dt.month = mMay
+ of "jun": dt.month = mJun
+ of "jul": dt.month = mJul
+ of "aug": dt.month = mAug
+ of "sep": dt.month = mSep
+ of "oct": dt.month = mOct
+ of "nov": dt.month = mNov
+ of "dec": dt.month = mDec
else:
raise newException(ValueError,
"Couldn't parse month (MMM), got: " & value)
j += 3
of "MMMM":
if value.len >= j+7 and value[j..j+6].cmpIgnoreCase("january") == 0:
- dt.month = mJan
+ dt.month = mJan
j += 7
elif value.len >= j+8 and value[j..j+7].cmpIgnoreCase("february") == 0:
- dt.month = mFeb
+ dt.month = mFeb
j += 8
elif value.len >= j+5 and value[j..j+4].cmpIgnoreCase("march") == 0:
- dt.month = mMar
+ dt.month = mMar
j += 5
elif value.len >= j+5 and value[j..j+4].cmpIgnoreCase("april") == 0:
- dt.month = mApr
+ dt.month = mApr
j += 5
elif value.len >= j+3 and value[j..j+2].cmpIgnoreCase("may") == 0:
- dt.month = mMay
+ dt.month = mMay
j += 3
elif value.len >= j+4 and value[j..j+3].cmpIgnoreCase("june") == 0:
- dt.month = mJun
+ dt.month = mJun
j += 4
elif value.len >= j+4 and value[j..j+3].cmpIgnoreCase("july") == 0:
- dt.month = mJul
+ dt.month = mJul
j += 4
elif value.len >= j+6 and value[j..j+5].cmpIgnoreCase("august") == 0:
- dt.month = mAug
+ dt.month = mAug
j += 6
elif value.len >= j+9 and value[j..j+8].cmpIgnoreCase("september") == 0:
- dt.month = mSep
+ dt.month = mSep
j += 9
elif value.len >= j+7 and value[j..j+6].cmpIgnoreCase("october") == 0:
- dt.month = mOct
+ dt.month = mOct
j += 7
elif value.len >= j+8 and value[j..j+7].cmpIgnoreCase("november") == 0:
- dt.month = mNov
+ dt.month = mNov
j += 8
elif value.len >= j+8 and value[j..j+7].cmpIgnoreCase("december") == 0:
- dt.month = mDec
+ dt.month = mDec
j += 8
else:
raise newException(ValueError,
@@ -1521,44 +1521,47 @@ proc parseToken(dt: var DateTime; token, value: string; j: var int) =
j += 4
of "z":
dt.isDst = false
- if value[j] == '+':
+ let ch = if j < value.len: value[j] else: '\0'
+ if ch == '+':
dt.utcOffset = 0 - parseInt($value[j+1]) * secondsInHour
- elif value[j] == '-':
+ elif ch == '-':
dt.utcOffset = parseInt($value[j+1]) * secondsInHour
- elif value[j] == 'Z':
+ elif ch == 'Z':
dt.utcOffset = 0
j += 1
return
else:
raise newException(ValueError,
- "Couldn't parse timezone offset (z), got: " & value[j])
+ "Couldn't parse timezone offset (z), got: " & ch)
j += 2
of "zz":
dt.isDst = false
- if value[j] == '+':
+ let ch = if j < value.len: value[j] else: '\0'
+ if ch == '+':
dt.utcOffset = 0 - value[j+1..j+2].parseInt() * secondsInHour
- elif value[j] == '-':
+ elif ch == '-':
dt.utcOffset = value[j+1..j+2].parseInt() * secondsInHour
- elif value[j] == 'Z':
+ elif ch == 'Z':
dt.utcOffset = 0
j += 1
return
else:
raise newException(ValueError,
- "Couldn't parse timezone offset (zz), got: " & value[j])
+ "Couldn't parse timezone offset (zz), got: " & ch)
j += 3
of "zzz":
dt.isDst = false
var factor = 0
- if value[j] == '+': factor = -1
- elif value[j] == '-': factor = 1
- elif value[j] == 'Z':
+ let ch = if j < value.len: value[j] else: '\0'
+ if ch == '+': factor = -1
+ elif ch == '-': factor = 1
+ elif ch == 'Z':
dt.utcOffset = 0
j += 1
return
else:
raise newException(ValueError,
- "Couldn't parse timezone offset (zzz), got: " & value[j])
+ "Couldn't parse timezone offset (zzz), got: " & ch)
dt.utcOffset = factor * value[j+1..j+2].parseInt() * secondsInHour
j += 4
dt.utcOffset += factor * value[j..j+1].parseInt() * 60
@@ -1620,20 +1623,18 @@ proc parse*(value, layout: string, zone: Timezone = local()): DateTime =
dt.nanosecond = 0
dt.isDst = true # using this is flag for checking whether a timezone has \
# been read (because DST is always false when a tz is parsed)
- while true:
+ while i < layout.len:
case layout[i]
- of ' ', '-', '/', ':', '\'', '\0', '(', ')', '[', ']', ',':
+ of ' ', '-', '/', ':', '\'', '(', ')', '[', ']', ',':
if token.len > 0:
parseToken(dt, token, value, j)
# Reset token
token = ""
- # Break if at end of line
- if layout[i] == '\0': break
# Skip separator and everything between single quotes
# These are literals in both the layout and the value string
if layout[i] == '\'':
inc(i)
- while layout[i] != '\'' and layout.len-1 > i:
+ while i < layout.len-1 and layout[i] != '\'':
inc(i)
inc(j)
inc(i)
@@ -1642,13 +1643,15 @@ proc parse*(value, layout: string, zone: Timezone = local()): DateTime =
inc(j)
else:
# Check if the letter being added matches previous accumulated buffer.
- if token.len < 1 or token[high(token)] == layout[i]:
+ if token.len == 0 or token[high(token)] == layout[i]:
token.add(layout[i])
inc(i)
else:
parseToken(dt, token, value, j)
token = ""
+ if i >= layout.len and token.len > 0:
+ parseToken(dt, token, value, j)
if dt.isDst:
# No timezone parsed - assume timezone is `zone`
result = initDateTime(zone.zoneInfoFromTz(dt.toAdjTime), zone)
diff --git a/lib/pure/uri.nim b/lib/pure/uri.nim
index d2d11253a9..9bf25b86b4 100644
--- a/lib/pure/uri.nim
+++ b/lib/pure/uri.nim
@@ -60,7 +60,7 @@ proc encodeUrl*(s: string): string =
else:
add(result, '%')
add(result, toHex(ord(s[i]), 2))
-
+
proc decodeUrl*(s: string): string =
## Decodes a value from its HTTP representation: This means that a ``'+'``
## is converted to a space, ``'%xx'`` (where ``xx`` denotes a hexadecimal
@@ -72,7 +72,7 @@ proc decodeUrl*(s: string): string =
of 'a'..'f': x = (x shl 4) or (ord(c) - ord('a') + 10)
of 'A'..'F': x = (x shl 4) or (ord(c) - ord('A') + 10)
else: assert(false)
-
+
result = newString(s.len)
var i = 0
var j = 0
@@ -94,7 +94,7 @@ proc parseAuthority(authority: string, result: var Uri) =
var i = 0
var inPort = false
var inIPv6 = false
- while true:
+ while i < authority.len:
case authority[i]
of '@':
swap result.password, result.port
@@ -111,7 +111,6 @@ proc parseAuthority(authority: string, result: var Uri) =
inIPv6 = true
of ']':
inIPv6 = false
- of '\0': break
else:
if inPort:
result.port.add(authority[i])
@@ -128,11 +127,11 @@ proc parsePath(uri: string, i: var int, result: var Uri) =
parseAuthority(result.path, result)
result.path.setLen(0)
- if uri[i] == '?':
+ if i < uri.len and uri[i] == '?':
i.inc # Skip '?'
i.inc parseUntil(uri, result.query, {'#'}, i)
- if uri[i] == '#':
+ if i < uri.len and uri[i] == '#':
i.inc # Skip '#'
i.inc parseUntil(uri, result.anchor, {}, i)
@@ -156,7 +155,7 @@ proc parseUri*(uri: string, result: var Uri) =
# Check if this is a reference URI (relative URI)
let doubleSlash = uri.len > 1 and uri[1] == '/'
- if uri[i] == '/':
+ if i < uri.len and uri[i] == '/':
# Make sure ``uri`` doesn't begin with '//'.
if not doubleSlash:
parsePath(uri, i, result)
@@ -164,7 +163,7 @@ proc parseUri*(uri: string, result: var Uri) =
# Scheme
i.inc parseWhile(uri, result.scheme, Letters + Digits + {'+', '-', '.'}, i)
- if uri[i] != ':' and not doubleSlash:
+ if (i >= uri.len or uri[i] != ':') and not doubleSlash:
# Assume this is a reference URI (relative URI)
i = 0
result.scheme.setLen(0)
@@ -174,7 +173,7 @@ proc parseUri*(uri: string, result: var Uri) =
i.inc # Skip ':'
# Authority
- if uri[i] == '/' and uri[i+1] == '/':
+ if i+1 < uri.len and uri[i] == '/' and uri[i+1] == '/':
i.inc(2) # Skip //
var authority = ""
i.inc parseUntil(uri, authority, {'/', '?', '#'}, i)
@@ -197,13 +196,13 @@ proc removeDotSegments(path: string): string =
let endsWithSlash = path[path.len-1] == '/'
var i = 0
var currentSegment = ""
- while true:
+ while i < path.len:
case path[i]
of '/':
collection.add(currentSegment)
currentSegment = ""
of '.':
- if path[i+1] == '.' and path[i+2] == '/':
+ if i+2 < path.len and path[i+1] == '.' and path[i+2] == '/':
if collection.len > 0:
discard collection.pop()
i.inc 3
@@ -212,13 +211,11 @@ proc removeDotSegments(path: string): string =
i.inc 2
continue
currentSegment.add path[i]
- of '\0':
- if currentSegment != "":
- collection.add currentSegment
- break
else:
currentSegment.add path[i]
i.inc
+ if currentSegment != "":
+ collection.add currentSegment
result = collection.join("/")
if endsWithSlash: result.add '/'
@@ -320,18 +317,18 @@ proc `/`*(x: Uri, path: string): Uri =
result = x
if result.path.len == 0:
- if path[0] != '/':
+ if path.len == 0 or path[0] != '/':
result.path = "/"
result.path.add(path)
return
- if result.path[result.path.len-1] == '/':
- if path[0] == '/':
+ if result.path.len > 0 and result.path[result.path.len-1] == '/':
+ if path.len > 0 and path[0] == '/':
result.path.add(path[1 .. path.len-1])
else:
result.path.add(path)
else:
- if path[0] != '/':
+ if path.len == 0 or path[0] != '/':
result.path.add '/'
result.path.add(path)
@@ -373,7 +370,7 @@ when isMainModule:
const test1 = "abc\L+def xyz"
doAssert encodeUrl(test1) == "abc%0A%2Bdef+xyz"
doAssert decodeUrl(encodeUrl(test1)) == test1
-
+
block:
let str = "http://localhost"
let test = parseUri(str)
@@ -464,7 +461,7 @@ when isMainModule:
doAssert test.hostname == "github.com"
doAssert test.port == "dom96"
doAssert test.path == "/packages"
-
+
block:
let str = "file:///foo/bar/baz.txt"
let test = parseUri(str)
diff --git a/lib/pure/xmldom.nim b/lib/pure/xmldom.nim
index 3c891c81b5..c38d360263 100644
--- a/lib/pure/xmldom.nim
+++ b/lib/pure/xmldom.nim
@@ -232,10 +232,10 @@ proc createAttributeNS*(doc: PDocument, namespaceURI: string, qualifiedName: str
raise newException(EInvalidCharacterErr, "Invalid character")
# Exceptions
if qualifiedName.contains(':'):
- let qfnamespaces = qualifiedName.toLower().split(':')
+ let qfnamespaces = qualifiedName.toLowerAscii().split(':')
if isNil(namespaceURI):
raise newException(ENamespaceErr, "When qualifiedName contains a prefix namespaceURI cannot be nil")
- elif qfnamespaces[0] == "xml" and
+ elif qfnamespaces[0] == "xml" and
namespaceURI != "http://www.w3.org/XML/1998/namespace" and
qfnamespaces[1] notin stdattrnames:
raise newException(ENamespaceErr,
@@ -311,10 +311,10 @@ proc createElement*(doc: PDocument, tagName: string): PElement =
proc createElementNS*(doc: PDocument, namespaceURI: string, qualifiedName: string): PElement =
## Creates an element of the given qualified name and namespace URI.
if qualifiedName.contains(':'):
- let qfnamespaces = qualifiedName.toLower().split(':')
+ let qfnamespaces = qualifiedName.toLowerAscii().split(':')
if isNil(namespaceURI):
raise newException(ENamespaceErr, "When qualifiedName contains a prefix namespaceURI cannot be nil")
- elif qfnamespaces[0] == "xml" and
+ elif qfnamespaces[0] == "xml" and
namespaceURI != "http://www.w3.org/XML/1998/namespace" and
qfnamespaces[1] notin stdattrnames:
raise newException(ENamespaceErr,
@@ -533,13 +533,13 @@ proc `prefix=`*(n: PNode, value: string) =
if isNil(n.fNamespaceURI):
raise newException(ENamespaceErr, "namespaceURI cannot be nil")
- elif value.toLower() == "xml" and n.fNamespaceURI != "http://www.w3.org/XML/1998/namespace":
+ elif value.toLowerAscii() == "xml" and n.fNamespaceURI != "http://www.w3.org/XML/1998/namespace":
raise newException(ENamespaceErr,
"When the namespace prefix is \"xml\" namespaceURI has to be \"http://www.w3.org/XML/1998/namespace\"")
- elif value.toLower() == "xmlns" and n.fNamespaceURI != "http://www.w3.org/2000/xmlns/":
+ elif value.toLowerAscii() == "xmlns" and n.fNamespaceURI != "http://www.w3.org/2000/xmlns/":
raise newException(ENamespaceErr,
"When the namespace prefix is \"xmlns\" namespaceURI has to be \"http://www.w3.org/2000/xmlns/\"")
- elif value.toLower() == "xmlns" and n.fNodeType == AttributeNode:
+ elif value.toLowerAscii() == "xmlns" and n.fNodeType == AttributeNode:
raise newException(ENamespaceErr, "An AttributeNode cannot have a prefix of \"xmlns\"")
n.fNodeName = value & ":" & n.fLocalName
diff --git a/lib/system.nim b/lib/system.nim
index 98c1334281..0c8659fdaf 100644
--- a/lib/system.nim
+++ b/lib/system.nim
@@ -1280,15 +1280,13 @@ proc setLen*[T](s: var seq[T], newlen: Natural) {.
## sets the length of `s` to `newlen`.
## ``T`` may be any sequence type.
## If the current length is greater than the new length,
- ## ``s`` will be truncated. `s` cannot be nil! To initialize a sequence with
- ## a size, use ``newSeq`` instead.
+ ## ``s`` will be truncated.
proc setLen*(s: var string, newlen: Natural) {.
magic: "SetLengthStr", noSideEffect.}
## sets the length of `s` to `newlen`.
## If the current length is greater than the new length,
- ## ``s`` will be truncated. `s` cannot be nil! To initialize a string with
- ## a size, use ``newString`` instead.
+ ## ``s`` will be truncated.
##
## .. code-block:: Nim
## var myS = "Nim is great!!"
@@ -2414,8 +2412,9 @@ proc `==` *[T](x, y: seq[T]): bool {.noSideEffect.} =
if seqToPtr(x) == seqToPtr(y):
return true
- if x.isNil or y.isNil:
- return false
+ when not defined(nimNoNil):
+ if x.isNil or y.isNil:
+ return false
if x.len != y.len:
return false
@@ -3224,7 +3223,7 @@ when not defined(JS): #and not defined(nimscript):
when declared(initGC): initGC()
when not defined(nimscript):
- proc setControlCHook*(hook: proc () {.noconv.} not nil)
+ proc setControlCHook*(hook: proc () {.noconv.})
## allows you to override the behaviour of your application when CTRL+C
## is pressed. Only one such hook is supported.
@@ -4012,18 +4011,18 @@ proc addQuoted*[T](s: var string, x: T) =
when hasAlloc:
# XXX: make these the default (or implement the NilObject optimization)
- proc safeAdd*[T](x: var seq[T], y: T) {.noSideEffect.} =
+ proc safeAdd*[T](x: var seq[T], y: T) {.noSideEffect, deprecated.} =
## Adds ``y`` to ``x`` unless ``x`` is not yet initialized; in that case,
## ``x`` becomes ``@[y]``
if x == nil: x = @[y]
else: x.add(y)
- proc safeAdd*(x: var string, y: char) =
+ proc safeAdd*(x: var string, y: char) {.noSideEffect, deprecated.} =
## Adds ``y`` to ``x``. If ``x`` is ``nil`` it is initialized to ``""``
if x == nil: x = ""
x.add(y)
- proc safeAdd*(x: var string, y: string) =
+ proc safeAdd*(x: var string, y: string) {.noSideEffect, deprecated.} =
## Adds ``y`` to ``x`` unless ``x`` is not yet initalized; in that
## case, ``x`` becomes ``y``
if x == nil: x = y
diff --git a/lib/system/embedded.nim b/lib/system/embedded.nim
index a14f43e7e6..46e84e0569 100644
--- a/lib/system/embedded.nim
+++ b/lib/system/embedded.nim
@@ -40,4 +40,4 @@ proc reraiseException() {.compilerRtl.} =
proc writeStackTrace() = discard
-proc setControlCHook(hook: proc () {.noconv.} not nil) = discard
+proc setControlCHook(hook: proc () {.noconv.}) = discard
diff --git a/lib/system/excpt.nim b/lib/system/excpt.nim
index afeab2b6c3..fb38948f7b 100644
--- a/lib/system/excpt.nim
+++ b/lib/system/excpt.nim
@@ -56,7 +56,7 @@ var
# list of exception handlers
# a global variable for the root of all try blocks
currException {.threadvar.}: ref Exception
- raise_counter {.threadvar.}: uint
+ raise_counter {.threadvar.}: uint
gcFramePtr {.threadvar.}: GcFrame
@@ -126,10 +126,10 @@ proc popCurrentExceptionEx(id: uint) {.compilerRtl.} =
while cur != nil and cur.raise_id != id:
prev = cur
cur = cur.up
- if cur == nil:
+ if cur == nil:
showErrorMessage("popCurrentExceptionEx() exception was not found in the exception stack. Aborting...")
quitOrDebug()
- prev.up = cur.up
+ prev.up = cur.up
# some platforms have native support for stack traces:
const
@@ -503,7 +503,7 @@ when not defined(noSignalHandler) and not defined(useNimRtl):
registerSignalHandler() # call it in initialization section
-proc setControlCHook(hook: proc () {.noconv.} not nil) =
+proc setControlCHook(hook: proc () {.noconv.}) =
# ugly cast, but should work on all architectures:
type SignalHandler = proc (sign: cint) {.noconv, benign.}
c_signal(SIGINT, cast[SignalHandler](hook))
diff --git a/lib/system/sysstr.nim b/lib/system/sysstr.nim
index ea0273340d..bba59e9309 100644
--- a/lib/system/sysstr.nim
+++ b/lib/system/sysstr.nim
@@ -22,21 +22,34 @@ proc resize(old: int): int {.inline.} =
proc cmpStrings(a, b: NimString): int {.inline, compilerProc.} =
if a == b: return 0
- if a == nil: return -1
- if b == nil: return 1
- let minlen = min(a.len, b.len)
+ when defined(nimNoNil):
+ let alen = if a == nil: 0 else: a.len
+ let blen = if b == nil: 0 else: b.len
+ else:
+ if a == nil: return -1
+ if b == nil: return 1
+ let alen = a.len
+ let blen = b.len
+ let minlen = min(alen, blen)
if minlen > 0:
result = c_memcmp(addr a.data, addr b.data, minlen.csize)
if result == 0:
- result = a.len - b.len
+ result = alen - blen
else:
- result = a.len - b.len
+ result = alen - blen
proc eqStrings(a, b: NimString): bool {.inline, compilerProc.} =
if a == b: return true
- if a == nil or b == nil: return false
- return a.len == b.len and
- equalMem(addr(a.data), addr(b.data), a.len)
+ when defined(nimNoNil):
+ let alen = if a == nil: 0 else: a.len
+ let blen = if b == nil: 0 else: b.len
+ else:
+ if a == nil or b == nil: return false
+ let alen = a.len
+ let blen = b.len
+ if alen == blen:
+ if alen == 0: return true
+ return equalMem(addr(a.data), addr(b.data), alen)
when declared(allocAtomic):
template allocStr(size: untyped): untyped =
@@ -101,9 +114,6 @@ proc cstrToNimstr(str: cstring): NimString {.compilerRtl.} =
if str == nil: NimString(nil)
else: toNimStr(str, str.len)
-template wasMoved(x: NimString): bool = false
-# (x.reserved and seqShallowFlag) != 0
-
proc copyString(src: NimString): NimString {.compilerRtl.} =
if src != nil:
if (src.reserved and seqShallowFlag) != 0:
@@ -161,14 +171,16 @@ proc hashString(s: string): int {.compilerproc.} =
proc addChar(s: NimString, c: char): NimString =
# is compilerproc!
- result = s
- if result.len >= result.space:
- let r = resize(result.space)
- result = cast[NimString](growObj(result,
- sizeof(TGenericSeq) + r + 1))
- result.reserved = r
- elif wasMoved(s):
- result = newOwnedString(s, s.len)
+ if s == nil:
+ result = rawNewStringNoInit(1)
+ result.len = 0
+ else:
+ result = s
+ if result.len >= result.space:
+ let r = resize(result.space)
+ result = cast[NimString](growObj(result,
+ sizeof(TGenericSeq) + r + 1))
+ result.reserved = r
result.data[result.len] = c
result.data[result.len+1] = '\0'
inc(result.len)
@@ -205,7 +217,9 @@ proc addChar(s: NimString, c: char): NimString =
# s = rawNewString(0);
proc resizeString(dest: NimString, addlen: int): NimString {.compilerRtl.} =
- if dest.len + addlen <= dest.space and not wasMoved(dest):
+ if dest == nil:
+ result = rawNewStringNoInit(addlen)
+ elif dest.len + addlen <= dest.space:
result = dest
else: # slow path:
var sp = max(resize(dest.space), dest.len + addlen)
@@ -216,8 +230,9 @@ proc resizeString(dest: NimString, addlen: int): NimString {.compilerRtl.} =
# DO NOT UPDATE LEN YET: dest.len = newLen
proc appendString(dest, src: NimString) {.compilerproc, inline.} =
- copyMem(addr(dest.data[dest.len]), addr(src.data), src.len + 1)
- inc(dest.len, src.len)
+ if src != nil:
+ copyMem(addr(dest.data[dest.len]), addr(src.data), src.len + 1)
+ inc(dest.len, src.len)
proc appendChar(dest: NimString, c: char) {.compilerproc, inline.} =
dest.data[dest.len] = c
@@ -226,8 +241,8 @@ proc appendChar(dest: NimString, c: char) {.compilerproc, inline.} =
proc setLengthStr(s: NimString, newLen: int): NimString {.compilerRtl.} =
var n = max(newLen, 0)
- if wasMoved(s):
- result = newOwnedString(s, n)
+ if s == nil:
+ result = mnewString(newLen)
elif n <= s.space:
result = s
else:
@@ -261,6 +276,18 @@ proc incrSeqV2(seq: PGenericSeq, elemSize: int): PGenericSeq {.compilerProc.} =
GenericSeqSize))
result.reserved = r
+proc incrSeqV3(s: PGenericSeq, typ: PNimType): PGenericSeq {.compilerProc.} =
+ if s == nil:
+ result = cast[PGenericSeq](newSeq(typ, 1))
+ result.len = 0
+ else:
+ result = s
+ if result.len >= result.space:
+ let r = resize(result.space)
+ result = cast[PGenericSeq](growObj(result, typ.base.size * r +
+ GenericSeqSize))
+ result.reserved = r
+
proc setLengthSeq(seq: PGenericSeq, elemSize, newLen: int): PGenericSeq {.
compilerRtl, inl.} =
result = seq
@@ -301,6 +328,13 @@ proc setLengthSeq(seq: PGenericSeq, elemSize, newLen: int): PGenericSeq {.
(newLen*%elemSize)), (result.len-%newLen) *% elemSize)
result.len = newLen
+proc setLengthSeqV2(s: PGenericSeq, typ: PNimType, newLen: int): PGenericSeq {.
+ compilerRtl.} =
+ if s == nil:
+ result = cast[PGenericSeq](newSeq(typ, newLen))
+ else:
+ result = setLengthSeq(s, typ.base.size, newLen)
+
# --------------- other string routines ----------------------------------
proc add*(result: var string; x: int64) =
let base = result.len
diff --git a/tests/array/tarraycons_ptr_generic.nim b/tests/array/tarraycons_ptr_generic.nim
new file mode 100644
index 0000000000..eb89a196fc
--- /dev/null
+++ b/tests/array/tarraycons_ptr_generic.nim
@@ -0,0 +1,51 @@
+discard """
+ output: '''apple
+banana
+Fruit
+2
+4
+3
+none
+skin
+paper
+'''
+"""
+type
+ Fruit = object of RootObj
+ name: string
+ Apple = object of Fruit
+ Banana = object of Fruit
+
+var
+ ir = Fruit(name: "Fruit")
+ ia = Apple(name: "apple")
+ ib = Banana(name: "banana")
+
+let x = [ia.addr, ib.addr, ir.addr]
+for c in x: echo c.name
+
+type
+ Vehicle[T] = object of RootObj
+ tire: T
+ Car[T] = object of Vehicle[T]
+ Bike[T] = object of Vehicle[T]
+
+var v = Vehicle[int](tire: 3)
+var c = Car[int](tire: 4)
+var b = Bike[int](tire: 2)
+
+let y = [b.addr, c.addr, v.addr]
+for c in y: echo c.tire
+
+type
+ Book[T] = ref object of RootObj
+ cover: T
+ Hard[T] = ref object of Book[T]
+ Soft[T] = ref object of Book[T]
+
+var bn = Book[string](cover: "none")
+var hs = Hard[string](cover: "skin")
+var bp = Soft[string](cover: "paper")
+
+let z = [bn, hs, bp]
+for c in z: echo c.cover
diff --git a/tests/array/tarraycons_ptr_generic2.nim b/tests/array/tarraycons_ptr_generic2.nim
new file mode 100644
index 0000000000..fce7af6691
--- /dev/null
+++ b/tests/array/tarraycons_ptr_generic2.nim
@@ -0,0 +1,17 @@
+discard """
+ file: "tarraycons_ptr_generic2.nim"
+ line: 17
+ errormsg: "type mismatch: got but expected 'Book[system.string]'"
+"""
+
+type
+ Book[T] = ref object of RootObj
+ cover: T
+ Hard[T] = ref object of Book[T]
+ Soft[T] = ref object of Book[T]
+
+var bn = Book[string](cover: "none")
+var hs = Hard[string](cover: "skin")
+var bp = Soft[string](cover: "paper")
+
+let z = [bn, hs.addr, bp]
diff --git a/tests/async/tlambda.nim b/tests/async/tlambda.nim
index d187c0d50e..8f570689bc 100644
--- a/tests/async/tlambda.nim
+++ b/tests/async/tlambda.nim
@@ -1,7 +1,7 @@
# bug 2007
-import asyncdispatch, asyncnet, logging, json, uri, strutils, future
+import asyncdispatch, asyncnet, logging, json, uri, strutils, sugar
type
Builder = ref object
@@ -27,7 +27,7 @@ proc newBuild*(onProgress: ProgressCB): Build =
result.onProgress = onProgress
proc start(build: Build, repo, hash: string) {.async.} =
- let path = repo.parseUri().path.toLower()
+ let path = repo.parseUri().path.toLowerAscii()
proc onProgress(builder: Builder, message: string) {.async.} =
debug($message)
diff --git a/tests/compiles/trecursive_generic_in_compiles.nim b/tests/compiles/trecursive_generic_in_compiles.nim
index 77bf0bb027..9c7fd10b31 100644
--- a/tests/compiles/trecursive_generic_in_compiles.nim
+++ b/tests/compiles/trecursive_generic_in_compiles.nim
@@ -1,6 +1,6 @@
# bug #3313
-import unittest, future
-
+import unittest, sugar
+{.experimental: "notnil".}
type
ListNodeKind = enum
lnkNil, lnkCons
diff --git a/tests/concepts/tstackconcept.nim b/tests/concepts/tstackconcept.nim
index 2238dacb69..cb8db566d8 100644
--- a/tests/concepts/tstackconcept.nim
+++ b/tests/concepts/tstackconcept.nim
@@ -31,7 +31,7 @@ type
s.pop() is T
type ValueType = T
- const ValueTypeName = T.name.toUpper
+ const ValueTypeName = T.name.toUpperAscii
proc genericAlgorithm[T](s: var Stack[T], y: T) =
static:
diff --git a/tests/constructors/tinvalid_construction.nim b/tests/constructors/tinvalid_construction.nim
index bb3b1bebb9..b3e56eec60 100644
--- a/tests/constructors/tinvalid_construction.nim
+++ b/tests/constructors/tinvalid_construction.nim
@@ -3,12 +3,12 @@ template accept(x) =
template reject(x) =
static: assert(not compiles(x))
-
+{.experimental: "notnil".}
type
TRefObj = ref object
x: int
- THasNotNils = object of TObject
+ THasNotNils = object of RootObj
a: TRefObj not nil
b: TRefObj not nil
c: TRefObj
diff --git a/tests/converter/tconvert.nim b/tests/converter/tconvert.nim
index a371402349..48367a85b1 100644
--- a/tests/converter/tconvert.nim
+++ b/tests/converter/tconvert.nim
@@ -15,6 +15,6 @@ type TFoo = object
converter toPtr*(some: var TFoo): ptr TFoo = (addr some)
-proc zoot(x: ptr TFoo) = nil
+proc zoot(x: ptr TFoo) = discard
var x: Tfoo
zoot(x)
diff --git a/tests/distinct/tnil.nim b/tests/distinct/tnil.nim
index e60437a1f2..759a14657a 100644
--- a/tests/distinct/tnil.nim
+++ b/tests/distinct/tnil.nim
@@ -1,15 +1,11 @@
discard """
file: "tnil.nim"
- output: '''0x1
-
-nil
-
-nil
-
+ output: '''1
+0
+0
'''
- disabled: "windows"
"""
-
+{.experimental: "notnil".}
type
MyPointer = distinct pointer
MyString = distinct string
@@ -17,7 +13,8 @@ type
MyInt = distinct int
proc foo(a: MyPointer) =
- echo a.repr
+ # workaround a Windows 'repr' difference:
+ echo cast[int](a)
foo(cast[MyPointer](1))
foo(cast[MyPointer](nil))
diff --git a/tests/effects/teffects4.nim b/tests/effects/teffects4.nim
index fd5dd49e24..d0960126f1 100644
--- a/tests/effects/teffects4.nim
+++ b/tests/effects/teffects4.nim
@@ -12,7 +12,7 @@ type
EIO2 = ref object of EIO
proc q() {.tags: [FIO].} =
- nil
+ discard
proc raiser(): int =
writeLine stdout, "arg"
diff --git a/tests/errmsgs/tproper_stacktrace2.nim b/tests/errmsgs/tproper_stacktrace2.nim
index 5f312b8703..44b208c87d 100644
--- a/tests/errmsgs/tproper_stacktrace2.nim
+++ b/tests/errmsgs/tproper_stacktrace2.nim
@@ -3,7 +3,7 @@ discard """
exitcode: 1
"""
-proc returnsNil(): string = return nil
+proc returnsNil(): ref int = return nil
iterator fields*(a, b: int): int =
if a == b:
@@ -17,6 +17,6 @@ proc main(): string =
result = ""
for i in fields(0, 1):
let x = returnsNil()
- result &= "string literal " & $x
+ result &= "string literal " & $x[]
echo main()
diff --git a/tests/manyloc/argument_parser/argument_parser.nim b/tests/manyloc/argument_parser/argument_parser.nim
index 14352066d4..1095a893e0 100644
--- a/tests/manyloc/argument_parser/argument_parser.nim
+++ b/tests/manyloc/argument_parser/argument_parser.nim
@@ -209,7 +209,7 @@ proc `$`*(data: Tcommandline_results): string =
# - Parse code
-template raise_or_quit(exception, message: expr): stmt {.immediate.} =
+template raise_or_quit(exception, message: untyped) =
## Avoids repeating if check based on the default quit_on_failure variable.
##
## As a special case, if message has a zero length the call to quit won't
@@ -230,15 +230,15 @@ template run_custom_proc(parsed_parameter: Tparsed_parameter,
##
## Pass in the string of the parameter triggering the call. If the
if not custom_validator.isNil:
+ try:
+ let message = custom_validator(parameter, parsed_parameter)
+ if not message.isNil and message.len > 0:
+ raise_or_quit(ValueError, ("Failed to validate value for " &
+ "parameter $1:\n$2" % [escape(parameter), message]))
except:
raise_or_quit(ValueError, ("Couldn't run custom proc for " &
"parameter $1:\n$2" % [escape(parameter),
getCurrentExceptionMsg()]))
- let message = custom_validator(parameter, parsed_parameter)
- if not message.isNil and message.len > 0:
- raise_or_quit(ValueError, ("Failed to validate value for " &
- "parameter $1:\n$2" % [escape(parameter), message]))
-
proc parse_parameter(quit_on_failure: bool, param, value: string,
param_kind: Tparam_kind): Tparsed_parameter =
diff --git a/tests/manyloc/keineschweine/dependencies/chipmunk/chipmunk.nim b/tests/manyloc/keineschweine/dependencies/chipmunk/chipmunk.nim
index f06c4e0be3..ac425c7a0a 100644
--- a/tests/manyloc/keineschweine/dependencies/chipmunk/chipmunk.nim
+++ b/tests/manyloc/keineschweine/dependencies/chipmunk/chipmunk.nim
@@ -388,13 +388,13 @@ type
cdecl.}
##cp property emulators
-template defGetter(otype: typedesc, memberType: typedesc, memberName: expr, procName: expr): stmt {.immediate.} =
+template defGetter(otype: typedesc, memberType: typedesc, memberName, procName: untyped) =
proc `get procName`*(obj: otype): memberType {.cdecl.} =
return obj.memberName
-template defSetter(otype: typedesc, memberType: typedesc, memberName: expr, procName: expr): stmt {.immediate.} =
+template defSetter(otype: typedesc, memberType: typedesc, memberName, procName: untyped) =
proc `set procName`*(obj: otype, value: memberType) {.cdecl.} =
obj.memberName = value
-template defProp(otype: typedesc, memberType: typedesc, memberName: expr, procName: expr): stmt {.immediate.} =
+template defProp(otype: typedesc, memberType: typedesc, memberName, procName: untyped) =
defGetter(otype, memberType, memberName, procName)
defSetter(otype, memberType, memberName, procName)
@@ -908,7 +908,7 @@ proc getShapes*(arb: PArbiter, a, b: var PShape) {.inline.} =
#/ A macro shortcut for defining and retrieving the shapes from an arbiter.
#define CP_ARBITER_GET_SHAPES(arb, a, b) cpShape *a, *b; cpArbiterGetShapes(arb, &a, &b);
-template getShapes*(arb: PArbiter, name1, name2: expr): stmt {.immediate.} =
+template getShapes*(arb: PArbiter, name1, name2: untyped) =
var name1, name2: PShape
getShapes(arb, name1, name2)
@@ -923,7 +923,7 @@ template getShapes*(arb: PArbiter, name1, name2: expr): stmt {.immediate.} =
#/ A macro shortcut for defining and retrieving the bodies from an arbiter.
#define CP_ARBITER_GET_BODIES(arb, a, b) cpBody *a, *b; cpArbiterGetBodies(arb, &a, &b);
-template getBodies*(arb: PArbiter, name1, name2: expr): stmt {.immediate.} =
+template getBodies*(arb: PArbiter, name1, name2: untyped) =
var name1, name2: PBOdy
getBodies(arb, name1, name2)
@@ -947,11 +947,11 @@ proc getDepth*(arb: PArbiter; i: cint): CpFloat {.
cdecl, importc: "cpArbiterGetDepth", dynlib: Lib.}
##Shapes
-template defShapeSetter(memberType: typedesc, memberName: expr, procName: expr, activates: bool): stmt {.immediate.} =
+template defShapeSetter(memberType: typedesc, memberName: untyped, procName: untyped, activates: bool) =
proc `set procName`*(obj: PShape, value: memberType) {.cdecl.} =
if activates and obj.body != nil: obj.body.activate()
obj.memberName = value
-template defShapeProp(memberType: typedesc, memberName: expr, procName: expr, activates: bool): stmt {.immediate.} =
+template defShapeProp(memberType: typedesc, memberName: untyped, procName: untyped, activates: bool) =
defGetter(PShape, memberType, memberName, procName)
defShapeSetter(memberType, memberName, procName, activates)
@@ -1272,11 +1272,11 @@ proc activateBodies(constraint: PConstraint) {.inline.} =
# cpConstraintActivateBodies(constraint); \
# constraint->member = value; \
# }
-template defConstraintSetter(memberType: typedesc, member: expr, name: expr): stmt {.immediate.} =
+template defConstraintSetter(memberType: typedesc, member, name: untyped) =
proc `set name`*(constraint: PConstraint, value: memberType) {.cdecl.} =
activateBodies(constraint)
constraint.member = value
-template defConstraintProp(memberType: typedesc, member: expr, name: expr): stmt {.immediate.} =
+template defConstraintProp(memberType: typedesc, member, name: untyped) =
defGetter(PConstraint, memberType, member, name)
defConstraintSetter(memberType, member, name)
# CP_DefineConstraintStructGetter(cpSpace*, CP_PRIVATE(space), Space)
@@ -1306,18 +1306,18 @@ proc getImpulse*(constraint: PConstraint): CpFloat {.inline.} =
# cpConstraintActivateBodies(constraint); \
# ((struct *)constraint)->member = value; \
# }
-template constraintCheckCast(constraint: PConstraint, ctype: expr): stmt {.immediate.} =
+template constraintCheckCast(constraint: PConstraint, ctype: untyped) =
assert(constraint.klass == `ctype getClass`(), "Constraint is the wrong class")
-template defCGetter(ctype: expr, memberType: typedesc, member: expr, name: expr): stmt {.immediate.} =
+template defCGetter(ctype: untyped, memberType: typedesc, member, name: untyped) =
proc `get ctype name`*(constraint: PConstraint): memberType {.cdecl.} =
constraintCheckCast(constraint, ctype)
result = cast[`P ctype`](constraint).member
-template defCSetter(ctype: expr, memberType: typedesc, member: expr, name: expr): stmt {.immediate.} =
+template defCSetter(ctype: untyped, memberType: typedesc, member, name: untyped) =
proc `set ctype name`*(constraint: PConstraint, value: memberType) {.cdecl.} =
constraintCheckCast(constraint, ctype)
activateBodies(constraint)
cast[`P ctype`](constraint).member = value
-template defCProp(ctype: expr, memberType: typedesc, member: expr, name: expr): stmt {.immediate.} =
+template defCProp(ctype: untyped, memberType: typedesc, member, name: untyped) =
defCGetter(ctype, memberType, member, name)
defCSetter(ctype, memberType, member, name)
diff --git a/tests/manyloc/keineschweine/dependencies/enet/enet.nim b/tests/manyloc/keineschweine/dependencies/enet/enet.nim
index 3ea8172d5f..07079f2eaa 100644
--- a/tests/manyloc/keineschweine/dependencies/enet/enet.nim
+++ b/tests/manyloc/keineschweine/dependencies/enet/enet.nim
@@ -24,7 +24,7 @@ const
ENET_VERSION_MAJOR* = 1
ENET_VERSION_MINOR* = 3
ENET_VERSION_PATCH* = 3
-template ENET_VERSION_CREATE(major, minor, patch: expr): expr =
+template ENET_VERSION_CREATE(major, minor, patch: untyped): untyped =
(((major) shl 16) or ((minor) shl 8) or (patch))
const
@@ -277,22 +277,22 @@ when defined(Linux) or true:
dataLength*: csize
TENetSocketSet* = Tfd_set
## see if these are different on win32, if not then get rid of these
- template ENET_HOST_TO_NET_16*(value: expr): expr =
+ template ENET_HOST_TO_NET_16*(value: untyped): untyped =
(htons(value))
- template ENET_HOST_TO_NET_32*(value: expr): expr =
+ template ENET_HOST_TO_NET_32*(value: untyped): untyped =
(htonl(value))
- template ENET_NET_TO_HOST_16*(value: expr): expr =
+ template ENET_NET_TO_HOST_16*(value: untyped): untyped =
(ntohs(value))
- template ENET_NET_TO_HOST_32*(value: expr): expr =
+ template ENET_NET_TO_HOST_32*(value: untyped): untyped =
(ntohl(value))
- template ENET_SOCKETSET_EMPTY*(sockset: expr): expr =
+ template ENET_SOCKETSET_EMPTY*(sockset: untyped): untyped =
FD_ZERO(addr((sockset)))
- template ENET_SOCKETSET_ADD*(sockset, socket: expr): expr =
+ template ENET_SOCKETSET_ADD*(sockset, socket: untyped): untyped =
FD_SET(socket, addr((sockset)))
- template ENET_SOCKETSET_REMOVE*(sockset, socket: expr): expr =
+ template ENET_SOCKETSET_REMOVE*(sockset, socket: untyped): untyped =
FD_CLEAR(socket, addr((sockset)))
- template ENET_SOCKETSET_CHECK*(sockset, socket: expr): expr =
+ template ENET_SOCKETSET_CHECK*(sockset, socket: untyped): untyped =
FD_ISSET(socket, addr((sockset)))
when defined(Windows):
@@ -606,7 +606,7 @@ proc protocolCommandSize*(commandNumber: cuchar): csize{.
{.pop.}
-from hashes import `!$`, `!&`, THash, hash
-proc hash*(x: TAddress): THash {.nimcall, noSideEffect.} =
+from hashes import `!$`, `!&`, Hash, hash
+proc hash*(x: TAddress): Hash {.nimcall, noSideEffect.} =
result = !$(hash(x.host.int32) !& hash(x.port.int16))
diff --git a/tests/manyloc/keineschweine/dependencies/genpacket/genpacket_enet.nim b/tests/manyloc/keineschweine/dependencies/genpacket/genpacket_enet.nim
index 142b190abb..3fb4dc7d9f 100644
--- a/tests/manyloc/keineschweine/dependencies/genpacket/genpacket_enet.nim
+++ b/tests/manyloc/keineschweine/dependencies/genpacket/genpacket_enet.nim
@@ -1,15 +1,15 @@
import macros, macro_dsl, estreams
from strutils import format
-template newLenName(): stmt {.immediate.} =
+template newLenName() =
let lenName {.inject.} = ^("len"& $lenNames)
inc(lenNames)
-template defPacketImports*(): stmt {.immediate, dirty.} =
+template defPacketImports*() {.dirty.} =
import macros, macro_dsl, estreams
from strutils import format
-macro defPacket*(typeNameN: expr, typeFields: expr): stmt {.immediate.} =
+macro defPacket*(typeNameN: untyped, typeFields: untyped): untyped =
result = newNimNode(nnkStmtList)
let
typeName = quoted2ident(typeNameN)
@@ -60,7 +60,7 @@ macro defPacket*(typeNameN: expr, typeFields: expr): stmt {.immediate.} =
let
name = typeFields[i][0]
dotName = packetID.dot(name)
- resName = newIdentNode(!"result").dot(name)
+ resName = newIdentNode("result").dot(name)
case typeFields[i][1].kind
of nnkBracketExpr: #ex: paddedstring[32, '\0'], array[range, type]
case $typeFields[i][1][0].ident
@@ -141,7 +141,7 @@ macro defPacket*(typeNameN: expr, typeFields: expr): stmt {.immediate.} =
const emptyFields = {nnkEmpty, nnkNilLit}
var objFields = newNimNode(nnkRecList)
- for i in 0.. < len(typeFields):
+ for i in 0 ..< len(typeFields):
let fname = typeFields[i][0]
constructorParams.add(newNimNode(nnkIdentDefs).und(
fname,
@@ -200,7 +200,7 @@ proc iddefs*(a: string; b: NimNode): NimNode {.compileTime.} =
proc varTy*(a: NimNode): NimNode {.compileTime.} =
result = newNimNode(nnkVarTy).und(a)
-macro forwardPacket*(typeName: expr, underlyingType: expr): stmt {.immediate.} =
+macro forwardPacket*(typeName: untyped, underlyingType: untyped): untyped =
var
packetID = ^"p"
streamID = ^"s"
@@ -234,7 +234,7 @@ macro forwardPacket*(typeName: expr, underlyingType: expr): stmt {.immediate.} =
echo "unknown type:", repr(underlyingtype)
echo(repr(result))
-template forwardPacketT*(typeName: expr; underlyingType: expr): stmt {.dirty, immediate.} =
+template forwardPacketT*(typeName: untyped; underlyingType: untyped) {.dirty.} =
proc `read typeName`*(buffer: PBuffer): typeName =
#discard readData(s, addr result, sizeof(result))
var res: underlyingType
diff --git a/tests/manyloc/keineschweine/dependencies/genpacket/macro_dsl.nim b/tests/manyloc/keineschweine/dependencies/genpacket/macro_dsl.nim
index 86c12fbb09..33d2a71778 100644
--- a/tests/manyloc/keineschweine/dependencies/genpacket/macro_dsl.nim
+++ b/tests/manyloc/keineschweine/dependencies/genpacket/macro_dsl.nim
@@ -10,7 +10,7 @@ proc und*(a: NimNode; b: varargs[NimNode]): NimNode {.compileTime.} =
proc `^`*(a: string): NimNode {.compileTime.} =
## new ident node
- result = newIdentNode(!a)
+ result = newIdentNode(a)
proc `[]`*(a, b: NimNode): NimNode {.compileTime.} =
## new bracket expression: node[node] not to be confused with node[indx]
result = newNimNode(nnkBracketExpr).und(a, b)
@@ -34,7 +34,7 @@ proc emptyNode*(): NimNode {.compileTime.} =
proc dot*(left, right: NimNode): NimNode {.compileTime.} =
result = newNimNode(nnkDotExpr).und(left, right)
proc prefix*(a: string, b: NimNode): NimNode {.compileTime.} =
- result = newNimNode(nnkPrefix).und(newIdentNode(!a), b)
+ result = newNimNode(nnkPrefix).und(newIdentNode(a), b)
proc quoted2ident*(a: NimNode): NimNode {.compileTime.} =
if a.kind != nnkAccQuoted:
@@ -45,13 +45,13 @@ proc quoted2ident*(a: NimNode): NimNode {.compileTime.} =
result = ^pname
-macro `?`(a: expr): expr =
+macro `?`(a: untyped): untyped =
## Character literal ?A #=> 'A'
result = ($a[1].ident)[0].lit
## echo(?F,?a,?t,?t,?y)
when isMainModule:
- macro foo(x: stmt): stmt =
+ macro foo(x: untyped) =
result = newNimNode(nnkStmtList)
result.add(newNimNode(nnkCall).und(!!"echo", "Hello thar".lit))
result.add(newCall("echo", lit("3 * 45 = "), (3.lit.infix("*", 45.lit))))
diff --git a/tests/manyloc/keineschweine/dependencies/nake/nakefile.nim b/tests/manyloc/keineschweine/dependencies/nake/nakefile.nim
index bdf2139c94..5490211de5 100644
--- a/tests/manyloc/keineschweine/dependencies/nake/nakefile.nim
+++ b/tests/manyloc/keineschweine/dependencies/nake/nakefile.nim
@@ -7,7 +7,7 @@ task "install", "compile and install nake binary":
for index, dir in pairs(path):
echo " ", index, ". ", dir
echo "Where to install nake binary? (quit with ^C or quit or exit)"
- let ans = stdin.readLine().toLower
+ let ans = stdin.readLine().toLowerAscii
var index = 0
case ans
of "q", "quit", "x", "exit":
diff --git a/tests/manyloc/keineschweine/lib/estreams.nim b/tests/manyloc/keineschweine/lib/estreams.nim
index bdf9b2bf0c..00a55c6263 100644
--- a/tests/manyloc/keineschweine/lib/estreams.nim
+++ b/tests/manyloc/keineschweine/lib/estreams.nim
@@ -78,7 +78,7 @@ proc write*(buffer: PBuffer; val: var string) =
setLen buffer.data, buffer.pos + length.int
copyMem(addr buffer.data[buffer.pos], addr val[0], length.int)
inc buffer.pos, length.int
-proc write*[T: TNumber|bool|char|byte](buffer: PBuffer; val: T) =
+proc write*[T: SomeNumber|bool|char|byte](buffer: PBuffer; val: T) =
var v: T
shallowCopy v, val
writeBE buffer, v
diff --git a/tests/manyloc/keineschweine/lib/input_helpers.nim b/tests/manyloc/keineschweine/lib/input_helpers.nim
index 1953cb58cb..ff1286c8db 100644
--- a/tests/manyloc/keineschweine/lib/input_helpers.nim
+++ b/tests/manyloc/keineschweine/lib/input_helpers.nim
@@ -5,8 +5,8 @@ type
TInputFinishedProc* = proc()
TKeyCallback = proc()
PKeyClient* = ref object
- onKeyDown: TTable[int32, TKeyCallback]
- onKeyUp: TTable[int32, TKeyCallback]
+ onKeyDown: Table[int32, TKeyCallback]
+ onKeyUp: Table[int32, TKeyCallback]
name: string
PTextInput* = ref object
text*: string
@@ -134,5 +134,5 @@ iterator pollEvents*(window: PRenderWindow): PEvent =
of EvtMouseButtonReleased: addButtonEvent(event.mouseButton.button, up)
of EvtTextEntered: recordText(activeInput, event.text)
of EvtMouseMoved: setMousePos(event.mouseMove.x, event.mouseMove.y)
- else: nil
+ else: discard
yield(addr event)
diff --git a/tests/manyloc/keineschweine/lib/sg_assets.nim b/tests/manyloc/keineschweine/lib/sg_assets.nim
index fbc3c9ab8e..1e8a99c83e 100644
--- a/tests/manyloc/keineschweine/lib/sg_assets.nim
+++ b/tests/manyloc/keineschweine/lib/sg_assets.nim
@@ -59,7 +59,7 @@ type
of Projectile:
bullet*: PBulletRecord
else:
- nil
+ discard
PBulletRecord* = ref TBulletRecord
TBulletRecord* = object
id*: int16
@@ -115,10 +115,10 @@ var
cfg: PZoneSettings
SpriteSheets* = initTable[string, PSpriteSheet](64)
SoundCache * = initTable[string, PSoundRecord](64)
- nameToVehID*: TTable[string, int]
- nameToItemID*: TTable[string, int]
- nameToObjID*: TTable[string, int]
- nameToBulletID*: TTable[string, int]
+ nameToVehID*: Table[string, int]
+ nameToItemID*: Table[string, int]
+ nameToObjID*: Table[string, int]
+ nameToBulletID*: Table[string, int]
activeState = Lobby
proc newSprite*(filename: string; errors: var seq[string]): PSpriteSheet
@@ -126,7 +126,7 @@ proc load*(ss: PSpriteSheet): bool {.discardable.}
proc newSound*(filename: string; errors: var seq[string]): PSoundRecord
proc load*(s: PSoundRecord): bool {.discardable.}
-proc validateSettings*(settings: PJsonNode; errors: var seq[string]): bool
+proc validateSettings*(settings: JsonNode; errors: var seq[string]): bool
proc loadSettings*(rawJson: string, errors: var seq[string]): bool
proc loadSettingsFromFile*(filename: string, errors: var seq[string]): bool
@@ -135,17 +135,17 @@ proc fetchItm*(itm: string): PItemRecord
proc fetchObj*(name: string): PObjectRecord
proc fetchBullet(name: string): PBulletRecord
-proc importLevel(data: PJsonNode; errors: var seq[string]): PLevelSettings
-proc importVeh(data: PJsonNode; errors: var seq[string]): PVehicleRecord
-proc importObject(data: PJsonNode; errors: var seq[string]): PObjectRecord
-proc importItem(data: PJsonNode; errors: var seq[string]): PItemRecord
-proc importPhys(data: PJsonNode): TPhysicsRecord
-proc importAnim(data: PJsonNode; errors: var seq[string]): PAnimationRecord
-proc importHandling(data: PJsonNode): THandlingRecord
-proc importBullet(data: PJsonNode; errors: var seq[string]): PBulletRecord
-proc importSoul(data: PJsonNode): TSoulRecord
-proc importExplosion(data: PJsonNode; errors: var seq[string]): TExplosionRecord
-proc importSound*(data: PJsonNode; errors: var seq[string]; fieldName: string = nil): PSoundRecord
+proc importLevel(data: JsonNode; errors: var seq[string]): PLevelSettings
+proc importVeh(data: JsonNode; errors: var seq[string]): PVehicleRecord
+proc importObject(data: JsonNode; errors: var seq[string]): PObjectRecord
+proc importItem(data: JsonNode; errors: var seq[string]): PItemRecord
+proc importPhys(data: JsonNode): TPhysicsRecord
+proc importAnim(data: JsonNode; errors: var seq[string]): PAnimationRecord
+proc importHandling(data: JsonNode): THandlingRecord
+proc importBullet(data: JsonNode; errors: var seq[string]): PBulletRecord
+proc importSoul(data: JsonNode): TSoulRecord
+proc importExplosion(data: JsonNode; errors: var seq[string]): TExplosionRecord
+proc importSound*(data: JsonNode; errors: var seq[string]; fieldName: string = nil): PSoundRecord
## this is the only pipe between lobby and main.nim
proc getActiveState*(): TGameState =
@@ -203,7 +203,7 @@ iterator playableVehicles*(): PVehicleRecord =
if v.playable:
yield v
-template allAssets*(body: stmt) {.dirty.}=
+template allAssets*(body: untyped) {.dirty.}=
block:
var assetType = FGraphics
for file, asset in pairs(SpriteSheets):
@@ -212,7 +212,7 @@ template allAssets*(body: stmt) {.dirty.}=
for file, asset in pairs(SoundCache):
body
-template cacheImpl(procName, cacheName, resultType: expr; body: stmt) {.dirty, immediate.} =
+template cacheImpl(procName, cacheName, resultType, body: untyped) {.dirty.} =
proc procName*(filename: string; errors: var seq[string]): resulttype =
if hasKey(cacheName, filename):
return cacheName[filename]
@@ -220,7 +220,7 @@ template cacheImpl(procName, cacheName, resultType: expr; body: stmt) {.dirty, i
body
cacheName[filename] = result
-template checkFile(path: expr): stmt {.dirty, immediate.} =
+template checkFile(path: untyped) {.dirty.} =
if not existsFile(path):
errors.add("File missing: "& path)
@@ -243,7 +243,7 @@ proc expandPath*(assetType: TAssetType; fileName: string): string =
case assetType
of FGraphics: result.add "gfx/"
of FSound: result.add "sfx/"
- else: nil
+ else: discard
result.add fileName
proc expandPath*(fc: ScFileChallenge): string {.inline.} =
result = expandPath(fc.assetType, fc.file)
@@ -280,10 +280,10 @@ else:
if not s.soundBuf.isNil:
result = true
-template addError(e: expr): stmt {.immediate.} =
+template addError(e: untyped) =
errors.add(e)
result = false
-proc validateSettings*(settings: PJsonNode, errors: var seq[string]): bool =
+proc validateSettings*(settings: JsonNode, errors: var seq[string]): bool =
result = true
if settings.kind != JObject:
addError("Settings root must be an object")
@@ -328,10 +328,10 @@ proc loadSettingsFromFile*(filename: string, errors: var seq[string]): bool =
result = loadSettings(readFile(filename), errors)
proc loadSettings*(rawJson: string, errors: var seq[string]): bool =
- var settings: PJsonNode
+ var settings: JsonNode
try:
settings = parseJson(rawJson)
- except EJsonParsingError:
+ except JsonParsingError:
errors.add("JSON parsing error: "& getCurrentExceptionMsg())
return
except:
@@ -407,21 +407,21 @@ proc fetchObj*(name: string): PObjectRecord =
proc fetchBullet(name: string): PBulletRecord =
return cfg.bullets[nameToBulletID[name]]
-proc getField(node: PJsonNode, field: string, target: var float) =
+proc getField(node: JsonNode, field: string, target: var float) =
if not node.hasKey(field):
return
if node[field].kind == JFloat:
target = node[field].fnum
elif node[field].kind == JInt:
target = node[field].num.float
-proc getField(node: PJsonNode, field: string, target: var int) =
+proc getField(node: JsonNode, field: string, target: var int) =
if not node.hasKey(field):
return
if node[field].kind == JInt:
target = node[field].num.int
elif node[field].kind == JFloat:
target = node[field].fnum.int
-proc getField(node: PJsonNode; field: string; target: var bool) =
+proc getField(node: JsonNode; field: string; target: var bool) =
if not node.hasKey(field):
return
case node[field].kind
@@ -431,19 +431,19 @@ proc getField(node: PJsonNode; field: string; target: var bool) =
target = (node[field].num != 0)
of JFloat:
target = (node[field].fnum != 0.0)
- else: nil
+ else: discard
-template checkKey(node: expr; key: string): stmt =
+template checkKey(node: untyped; key: string) =
if not hasKey(node, key):
return
-proc importTrail(data: PJsonNode; errors: var seq[string]): TTrailRecord =
+proc importTrail(data: JsonNode; errors: var seq[string]): TTrailRecord =
checkKey(data, "trail")
result.anim = importAnim(data["trail"], errors)
result.timer = 1000.0
getField(data["trail"], "timer", result.timer)
result.timer /= 1000.0
-proc importLevel(data: PJsonNode; errors: var seq[string]): PLevelSettings =
+proc importLevel(data: JsonNode; errors: var seq[string]): PLevelSettings =
new(result)
result.size = vec2i(5000, 5000)
result.starfield = @[]
@@ -456,7 +456,7 @@ proc importLevel(data: PJsonNode; errors: var seq[string]): PLevelSettings =
if level.hasKey("starfield"):
for star in level["starfield"].items:
result.starfield.add(newSprite(star.str, errors))
-proc importPhys(data: PJsonNode): TPhysicsRecord =
+proc importPhys(data: JsonNode): TPhysicsRecord =
result.radius = 20.0
result.mass = 10.0
@@ -466,7 +466,7 @@ proc importPhys(data: PJsonNode): TPhysicsRecord =
phys.getField("mass", result.mass)
when not defined(NoChipmunk):
result.moment = momentForCircle(result.mass, 0.0, result.radius, VectorZero) * MomentMult
-proc importHandling(data: PJsonNode): THandlingRecord =
+proc importHandling(data: JsonNode): THandlingRecord =
result.thrust = 45.0
result.topSpeed = 100.0 #unused
result.reverse = 30.0
@@ -483,7 +483,7 @@ proc importHandling(data: PJsonNode): THandlingRecord =
hand.getField("reverse", result.reverse)
hand.getField("strafe", result.strafe)
hand.getField("rotation", result.rotation)
-proc importAnim(data: PJsonNode, errors: var seq[string]): PAnimationRecord =
+proc importAnim(data: JsonNode, errors: var seq[string]): PAnimationRecord =
new(result)
result.angle = 0.0
result.delay = 1000.0
@@ -502,26 +502,26 @@ proc importAnim(data: PJsonNode, errors: var seq[string]): PAnimationRecord =
result.angle = radians(result.angle) ## comes in as degrees
result.delay /= 1000 ## delay comes in as milliseconds
-proc importSoul(data: PJsonNode): TSoulRecord =
+proc importSoul(data: JsonNode): TSoulRecord =
result.energy = 10000
result.health = 1
checkKey(data, "soul")
let soul = data["soul"]
soul.getField("energy", result.energy)
soul.getField("health", result.health)
-proc importExplosion(data: PJsonNode; errors: var seq[string]): TExplosionRecord =
+proc importExplosion(data: JsonNode; errors: var seq[string]): TExplosionRecord =
checkKey(data, "explode")
let expl = data["explode"]
result.anim = importAnim(expl, errors)
result.sound = importSound(expl, errors, "sound")
-proc importSound*(data: PJsonNode; errors: var seq[string]; fieldName: string = nil): PSoundRecord =
+proc importSound*(data: JsonNode; errors: var seq[string]; fieldName: string = nil): PSoundRecord =
if data.kind == JObject:
checkKey(data, fieldName)
result = newSound(data[fieldName].str, errors)
elif data.kind == JString:
result = newSound(data.str, errors)
-proc importVeh(data: PJsonNode; errors: var seq[string]): PVehicleRecord =
+proc importVeh(data: JsonNode; errors: var seq[string]): PVehicleRecord =
new(result)
result.playable = false
if data.kind != JArray or data.len != 2 or
@@ -538,7 +538,7 @@ proc importVeh(data: PJsonNode; errors: var seq[string]): PVehicleRecord =
vehdata.getField("playable", result.playable)
if result.anim.spriteSheet.isNil and result.playable:
result.playable = false
-proc importObject(data: PJsonNode; errors: var seq[string]): PObjectRecord =
+proc importObject(data: JsonNode; errors: var seq[string]): PObjectRecord =
new(result)
if data.kind != JArray or data.len != 2:
result.name = "(broken)"
@@ -546,7 +546,7 @@ proc importObject(data: PJsonNode; errors: var seq[string]): PObjectRecord =
result.name = data[0].str
result.anim = importAnim(data[1], errors)
result.physics = importPhys(data[1])
-proc importItem(data: PJsonNode; errors: var seq[string]): PItemRecord =
+proc importItem(data: JsonNode; errors: var seq[string]): PItemRecord =
new(result)
if data.kind != JArray or data.len != 3:
result.name = "(broken)"
@@ -562,7 +562,7 @@ proc importItem(data: PJsonNode; errors: var seq[string]): PItemRecord =
result.useSound = importSound(data[2], errors, "useSound")
- case data[1].str.toLower
+ case data[1].str.toLowerAscii
of "projectile":
result.kind = Projectile
if data[2]["bullet"].kind == JString:
@@ -576,15 +576,15 @@ proc importItem(data: PJsonNode; errors: var seq[string]): PItemRecord =
of "ammo":
result.kind = Ammo
of "utility":
- nil
+ discard
else:
errors.add "Invalid item type \""&data[1].str&"\" for item "&result.name
-proc importBullet(data: PJsonNode; errors: var seq[string]): PBulletRecord =
+proc importBullet(data: JsonNode; errors: var seq[string]): PBulletRecord =
new(result)
result.id = -1
- var bdata: PJsonNode
+ var bdata: JsonNode
if data.kind == JArray:
result.name = data[0].str
bdata = data[1]
diff --git a/tests/manyloc/keineschweine/lib/sg_packets.nim b/tests/manyloc/keineschweine/lib/sg_packets.nim
index d84bf72fc4..f3a0e8925c 100644
--- a/tests/manyloc/keineschweine/lib/sg_packets.nim
+++ b/tests/manyloc/keineschweine/lib/sg_packets.nim
@@ -4,14 +4,14 @@ defPacketImports()
type
PacketID* = char
-template idpacket(pktName, id, s2c, c2s: expr): stmt {.immediate, dirty.} =
+template idpacket(pktName, id, s2c, c2s: untyped) {.dirty.} =
let `H pktName`* {.inject.} = id
defPacket(`Sc pktName`, s2c)
defPacket(`Cs pktName`, c2s)
forwardPacketT(uint8, int8)
forwardPacketT(uint16, int16)
-forwardPacketT(TPort, int16)
+forwardPacketT(Port, int16)
idPacket(Login, 'a',
tuple[id: int32; alias: string; sessionKey: string],
@@ -22,7 +22,7 @@ defPacket(CsZoneJoinReq, tuple[session: ScLogin])
defPacket(ScZoneRecord, tuple[
name: string = "", desc: string = "",
- ip: string = "", port: TPort = 0.Tport])
+ ip: string = "", port: Port = 0.Port])
idPacket(ZoneList, 'z',
tuple[network: string = "", zones: seq[ScZoneRecord]],
tuple[time: string])
diff --git a/tests/manyloc/nake/nake.nim b/tests/manyloc/nake/nake.nim
index 1e88fa73b9..728619e471 100644
--- a/tests/manyloc/nake/nake.nim
+++ b/tests/manyloc/nake/nake.nim
@@ -67,7 +67,7 @@ else:
for kind, key, val in getOpt():
case kind
of cmdLongOption, cmdShortOption:
- case key.tolower
+ case key.tolowerAscii
of "tasks", "t":
printTaskList = true
else:
diff --git a/tests/manyloc/nake/nakefile.nim b/tests/manyloc/nake/nakefile.nim
index 97af79a84f..3e8609169b 100644
--- a/tests/manyloc/nake/nakefile.nim
+++ b/tests/manyloc/nake/nakefile.nim
@@ -88,7 +88,7 @@ task "download", "download game assets":
if existsFile(path):
echo "The file already exists\n",
"[R]emove [M]ove [Q]uit [S]kip Source: ", GameAssets
- case stdin.readLine.toLower
+ case stdin.readLine.toLowerAscii
of "r":
removeFile path
of "m":
@@ -120,7 +120,7 @@ task "download", "download game assets":
echo "Download binary libs? Only libs for linux are available currently, enjoy the irony.\n",
"[Y]es [N]o Source: ", BinLibs
- case stdin.readline.toLower
+ case stdin.readline.toLowerAscii
of "y", "yes":
discard ## o_O
else:
diff --git a/tests/metatype/tbindtypedesc.nim b/tests/metatype/tbindtypedesc.nim
index b287aad013..039acfbe9e 100644
--- a/tests/metatype/tbindtypedesc.nim
+++ b/tests/metatype/tbindtypedesc.nim
@@ -46,7 +46,7 @@ type
type1 = typedesc
type2 = typedesc
-proc typePairs(A, B: type1; C, D: type2) = nil
+proc typePairs(A, B: type1; C, D: type2) = discard
accept typePairs(int, int, TFoo, TFOO)
accept typePairs(TBAR, TBar, TBAR, TBAR)
@@ -55,7 +55,7 @@ accept typePairs(int, int, string, string)
reject typePairs(TBAR, TBar, TBar, TFoo)
reject typePairs(string, int, TBAR, TBAR)
-proc typePairs2[T: typedesc, U: typedesc](A, B: T; C, D: U) = nil
+proc typePairs2[T: typedesc, U: typedesc](A, B: T; C, D: U) = discard
accept typePairs2(int, int, TFoo, TFOO)
accept typePairs2(TBAR, TBar, TBAR, TBAR)
@@ -71,12 +71,12 @@ proc dontBind(a: typedesc, b: typedesc) =
accept dontBind(int, float)
accept dontBind(TFoo, TFoo)
-proc dontBind2(a, b: typedesc) = nil
+proc dontBind2(a, b: typedesc) = discard
accept dontBind2(int, float)
accept dontBind2(TBar, int)
-proc bindArg(T: typedesc, U: typedesc, a, b: T, c, d: U) = nil
+proc bindArg(T: typedesc, U: typedesc, a, b: T, c, d: U) = discard
accept bindArg(int, string, 10, 20, "test", "nest")
accept bindArg(int, int, 10, 20, 30, 40)
diff --git a/tests/method/tsimmeth.nim b/tests/method/tsimmeth.nim
index 11ff2674f8..a057c35b7f 100644
--- a/tests/method/tsimmeth.nim
+++ b/tests/method/tsimmeth.nim
@@ -6,7 +6,7 @@ discard """
import strutils
-var x = "hello world!".toLower.toUpper
+var x = "hello world!".toLowerAscii.toUpperAscii
x.echo()
#OUT HELLO WORLD!
diff --git a/tests/misc/tmemoization.nim b/tests/misc/tmemoization.nim
index 180acd89b6..840eb3b0d7 100644
--- a/tests/misc/tmemoization.nim
+++ b/tests/misc/tmemoization.nim
@@ -8,7 +8,7 @@ import strutils
proc foo(s: static[string]): string =
static: echo s
- const R = s.toUpper
+ const R = s.toUpperAscii
return R
echo foo("test 1")
diff --git a/tests/misc/tsemfold.nim b/tests/misc/tsemfold.nim
new file mode 100644
index 0000000000..18c282d9e4
--- /dev/null
+++ b/tests/misc/tsemfold.nim
@@ -0,0 +1,23 @@
+discard """
+ action: run
+"""
+
+doAssertRaises(OverflowError): discard low(int8) - 1'i8
+doAssertRaises(OverflowError): discard high(int8) + 1'i8
+doAssertRaises(OverflowError): discard abs(low(int8))
+doAssertRaises(DivByZeroError): discard 1 mod 0
+doAssertRaises(DivByZeroError): discard 1 div 0
+doAssertRaises(OverflowError): discard low(int8) div -1'i8
+
+doAssertRaises(OverflowError): discard low(int64) - 1'i64
+doAssertRaises(OverflowError): discard high(int64) + 1'i64
+
+type E = enum eA, eB
+doAssertRaises(OverflowError): discard eA.pred
+doAssertRaises(OverflowError): discard eB.succ
+
+doAssertRaises(OverflowError): discard low(int8) * -1
+doAssertRaises(OverflowError): discard low(int64) * -1
+doAssertRaises(OverflowError): discard high(int8) * 2
+doAssertRaises(OverflowError): discard high(int64) * 2
+
diff --git a/tests/notnil/tmust_compile.nim b/tests/notnil/tmust_compile.nim
index 10da154f00..a32c6c7ecd 100644
--- a/tests/notnil/tmust_compile.nim
+++ b/tests/notnil/tmust_compile.nim
@@ -3,6 +3,7 @@ discard """
"""
# bug #6682
+{.experimental: "notnil".}
type
Fields = enum
diff --git a/tests/notnil/tnotnil.nim b/tests/notnil/tnotnil.nim
index f65634ed66..e392b155c6 100644
--- a/tests/notnil/tnotnil.nim
+++ b/tests/notnil/tnotnil.nim
@@ -2,7 +2,7 @@ discard """
line: 22
errormsg: "type mismatch"
"""
-
+{.experimental: "notnil".}
type
PObj = ref TObj not nil
TObj = object
@@ -15,8 +15,8 @@ type
proc p(x: string not nil): int =
result = 45
-proc q(x: MyString) = nil
-proc q2(x: string) = nil
+proc q(x: MyString) = discard
+proc q2(x: string) = discard
q2(nil)
q(nil)
diff --git a/tests/notnil/tnotnil1.nim b/tests/notnil/tnotnil1.nim
index 73472752c7..7f9d022958 100644
--- a/tests/notnil/tnotnil1.nim
+++ b/tests/notnil/tnotnil1.nim
@@ -4,7 +4,7 @@ discard """
"""
import strutils
-
+{.experimental: "notnil".}
type
TObj = object
@@ -18,13 +18,13 @@ proc q(s: superstring) =
echo s
proc p2() =
- var a: string = "I am not nil"
+ var a: string = "I am not nil"
q(a) # but this should and does not
p2()
proc q(x: pointer not nil) =
- nil
+ discard
proc p() =
var x: pointer
diff --git a/tests/notnil/tnotnil2.nim b/tests/notnil/tnotnil2.nim
index bd6b8b6755..6cd08de734 100644
--- a/tests/notnil/tnotnil2.nim
+++ b/tests/notnil/tnotnil2.nim
@@ -4,14 +4,14 @@ discard """
"""
import strutils
-
+{.experimental: "notnil".}
type
TObj = object
x, y: int
proc q(x: pointer not nil) =
- nil
+ discard
proc p() =
var x: pointer
diff --git a/tests/notnil/tnotnil3.nim b/tests/notnil/tnotnil3.nim
index b7c7a811d1..31a4efef78 100644
--- a/tests/notnil/tnotnil3.nim
+++ b/tests/notnil/tnotnil3.nim
@@ -5,7 +5,7 @@ discard """
# bug #584
# Testprogram for 'not nil' check
-
+{.experimental: "notnil".}
const testWithResult = true
type
diff --git a/tests/notnil/tnotnil4.nim b/tests/notnil/tnotnil4.nim
index 2fa888357f..4fd169827d 100644
--- a/tests/notnil/tnotnil4.nim
+++ b/tests/notnil/tnotnil4.nim
@@ -2,6 +2,8 @@ discard ""
type
TObj = ref object
+{.experimental: "notnil".}
+
proc check(a: TObj not nil) =
echo repr(a)
diff --git a/tests/notnil/tnotnil_in_generic.nim b/tests/notnil/tnotnil_in_generic.nim
index 357ab2c7c7..89d20f1826 100644
--- a/tests/notnil/tnotnil_in_generic.nim
+++ b/tests/notnil/tnotnil_in_generic.nim
@@ -3,6 +3,7 @@ discard """
"""
# bug #2216
+{.experimental: "notnil".}
type
A[T] = ref object
diff --git a/tests/notnil/tnotnil_in_objconstr.nim b/tests/notnil/tnotnil_in_objconstr.nim
index 7dce98c29f..d33709906b 100644
--- a/tests/notnil/tnotnil_in_objconstr.nim
+++ b/tests/notnil/tnotnil_in_objconstr.nim
@@ -2,7 +2,7 @@ discard """
errormsg: "fields not initialized: bar"
line: "13"
"""
-
+{.experimental: "notnil".}
# bug #2355
type
Foo = object
diff --git a/tests/objvariant/tcheckedfield1.nim b/tests/objvariant/tcheckedfield1.nim
index 56d784a2bd..a7f232c5b3 100644
--- a/tests/objvariant/tcheckedfield1.nim
+++ b/tests/objvariant/tcheckedfield1.nim
@@ -6,7 +6,7 @@ discard """
import strutils
{.warning[ProveField]: on.}
-
+{.experimental: "notnil".}
type
TNodeKind = enum
nkBinary, nkTernary, nkStr
diff --git a/tests/overload/tissue966.nim b/tests/overload/tissue966.nim
index c5b28e2176..d0a723875f 100644
--- a/tests/overload/tissue966.nim
+++ b/tests/overload/tissue966.nim
@@ -5,7 +5,7 @@ discard """
type
PTest = ref object
-proc test(x: PTest, y: int) = nil
+proc test(x: PTest, y: int) = discard
var buf: PTest
buf.test()
diff --git a/tests/proc/tprocredef.nim b/tests/proc/tprocredef.nim
index 86ed92b629..4ec7715103 100644
--- a/tests/proc/tprocredef.nim
+++ b/tests/proc/tprocredef.nim
@@ -4,6 +4,6 @@ discard """
errormsg: "redefinition of \'foo\'"
"""
-proc foo(a: int, b: string) = nil
-proc foo(a: int, b: string) = nil
+proc foo(a: int, b: string) = discard
+proc foo(a: int, b: string) = discard
diff --git a/tests/range/tsubrange.nim b/tests/range/tsubrange.nim
index 6f88c5a224..914e7c6e7d 100644
--- a/tests/range/tsubrange.nim
+++ b/tests/range/tsubrange.nim
@@ -7,7 +7,7 @@ type
TRange = range[0..40]
proc p(r: TRange) =
- nil
+ discard
var
r: TRange
diff --git a/tests/stdlib/tpegs.nim b/tests/stdlib/tpegs.nim
deleted file mode 100644
index e2a5a1715d..0000000000
--- a/tests/stdlib/tpegs.nim
+++ /dev/null
@@ -1,1770 +0,0 @@
-discard """
- output: '''this
-is
-an
-example
-d
-e
-f
-('keyvalue' 'key'*)'''
-"""
-# PEGs module turned out to be a good test to detect memory management bugs.
-
-include "system/inclrtl"
-
-const
- useUnicode = true ## change this to deactivate proper UTF-8 support
-
-import
- strutils
-
-when useUnicode:
- import unicode
-
-const
- InlineThreshold = 5 ## number of leaves; -1 to disable inlining
- MaxSubpatterns* = 10 ## defines the maximum number of subpatterns that
- ## can be captured. More subpatterns cannot be captured!
-
-type
- TPegKind = enum
- pkEmpty,
- pkAny, ## any character (.)
- pkAnyRune, ## any Unicode character (_)
- pkNewLine, ## CR-LF, LF, CR
- pkLetter, ## Unicode letter
- pkLower, ## Unicode lower case letter
- pkUpper, ## Unicode upper case letter
- pkTitle, ## Unicode title character
- pkWhitespace, ## Unicode whitespace character
- pkTerminal,
- pkTerminalIgnoreCase,
- pkTerminalIgnoreStyle,
- pkChar, ## single character to match
- pkCharChoice,
- pkNonTerminal,
- pkSequence, ## a b c ... --> Internal DSL: peg(a, b, c)
- pkOrderedChoice, ## a / b / ... --> Internal DSL: a / b or /[a, b, c]
- pkGreedyRep, ## a* --> Internal DSL: *a
- ## a+ --> (a a*)
- pkGreedyRepChar, ## x* where x is a single character (superop)
- pkGreedyRepSet, ## [set]* (superop)
- pkGreedyAny, ## .* or _* (superop)
- pkOption, ## a? --> Internal DSL: ?a
- pkAndPredicate, ## &a --> Internal DSL: &a
- pkNotPredicate, ## !a --> Internal DSL: !a
- pkCapture, ## {a} --> Internal DSL: capture(a)
- pkBackRef, ## $i --> Internal DSL: backref(i)
- pkBackRefIgnoreCase,
- pkBackRefIgnoreStyle,
- pkSearch, ## @a --> Internal DSL: !*a
- pkCapturedSearch, ## {@} a --> Internal DSL: !*\a
- pkRule, ## a <- b
- pkList, ## a, b
- pkStartAnchor ## ^ --> Internal DSL: startAnchor()
- TNonTerminalFlag = enum
- ntDeclared, ntUsed
- TNonTerminal {.final.} = object ## represents a non terminal symbol
- name: string ## the name of the symbol
- line: int ## line the symbol has been declared/used in
- col: int ## column the symbol has been declared/used in
- flags: set[TNonTerminalFlag] ## the nonterminal's flags
- rule: TNode ## the rule that the symbol refers to
- TNode {.final, shallow.} = object
- case kind: TPegKind
- of pkEmpty..pkWhitespace: nil
- of pkTerminal, pkTerminalIgnoreCase, pkTerminalIgnoreStyle: term: string
- of pkChar, pkGreedyRepChar: ch: char
- of pkCharChoice, pkGreedyRepSet: charChoice: ref set[char]
- of pkNonTerminal: nt: PNonTerminal
- of pkBackRef..pkBackRefIgnoreStyle: index: range[0..MaxSubpatterns]
- else: sons: seq[TNode]
- PNonTerminal* = ref TNonTerminal
-
- TPeg* = TNode ## type that represents a PEG
-
-proc term*(t: string): TPeg {.rtl, extern: "npegs$1Str".} =
- ## constructs a PEG from a terminal string
- if t.len != 1:
- result.kind = pkTerminal
- result.term = t
- else:
- result.kind = pkChar
- result.ch = t[0]
-
-proc termIgnoreCase*(t: string): TPeg {.
- rtl, extern: "npegs$1".} =
- ## constructs a PEG from a terminal string; ignore case for matching
- result.kind = pkTerminalIgnoreCase
- result.term = t
-
-proc termIgnoreStyle*(t: string): TPeg {.
- rtl, extern: "npegs$1".} =
- ## constructs a PEG from a terminal string; ignore style for matching
- result.kind = pkTerminalIgnoreStyle
- result.term = t
-
-proc term*(t: char): TPeg {.rtl, extern: "npegs$1Char".} =
- ## constructs a PEG from a terminal char
- assert t != '\0'
- result.kind = pkChar
- result.ch = t
-
-proc charSet*(s: set[char]): TPeg {.rtl, extern: "npegs$1".} =
- ## constructs a PEG from a character set `s`
- assert '\0' notin s
- result.kind = pkCharChoice
- new(result.charChoice)
- result.charChoice[] = s
-
-proc len(a: TPeg): int {.inline.} = return a.sons.len
-proc add(d: var TPeg, s: TPeg) {.inline.} = add(d.sons, s)
-
-proc copyPeg(a: TPeg): TPeg =
- result.kind = a.kind
- case a.kind
- of pkEmpty..pkWhitespace: discard
- of pkTerminal, pkTerminalIgnoreCase, pkTerminalIgnoreStyle:
- result.term = a.term
- of pkChar, pkGreedyRepChar:
- result.ch = a.ch
- of pkCharChoice, pkGreedyRepSet:
- new(result.charChoice)
- result.charChoice[] = a.charChoice[]
- of pkNonTerminal: result.nt = a.nt
- of pkBackRef..pkBackRefIgnoreStyle:
- result.index = a.index
- else:
- result.sons = a.sons
-
-proc addChoice(dest: var TPeg, elem: TPeg) =
- var L = dest.len-1
- if L >= 0 and dest.sons[L].kind == pkCharChoice:
- # caution! Do not introduce false aliasing here!
- case elem.kind
- of pkCharChoice:
- dest.sons[L] = charSet(dest.sons[L].charChoice[] + elem.charChoice[])
- of pkChar:
- dest.sons[L] = charSet(dest.sons[L].charChoice[] + {elem.ch})
- else: add(dest, elem)
- else: add(dest, elem)
-
-template multipleOp(k: TPegKind, localOpt) =
- result.kind = k
- result.sons = @[]
- for x in items(a):
- if x.kind == k:
- for y in items(x.sons):
- localOpt(result, y)
- else:
- localOpt(result, x)
- if result.len == 1:
- result = result.sons[0]
-
-proc `/`*(a: varargs[TPeg]): TPeg {.
- rtl, extern: "npegsOrderedChoice".} =
- ## constructs an ordered choice with the PEGs in `a`
- multipleOp(pkOrderedChoice, addChoice)
-
-proc addSequence(dest: var TPeg, elem: TPeg) =
- var L = dest.len-1
- if L >= 0 and dest.sons[L].kind == pkTerminal:
- # caution! Do not introduce false aliasing here!
- case elem.kind
- of pkTerminal:
- dest.sons[L] = term(dest.sons[L].term & elem.term)
- of pkChar:
- dest.sons[L] = term(dest.sons[L].term & elem.ch)
- else: add(dest, elem)
- else: add(dest, elem)
-
-proc sequence*(a: varargs[TPeg]): TPeg {.
- rtl, extern: "npegs$1".} =
- ## constructs a sequence with all the PEGs from `a`
- multipleOp(pkSequence, addSequence)
-
-proc `?`*(a: TPeg): TPeg {.rtl, extern: "npegsOptional".} =
- ## constructs an optional for the PEG `a`
- if a.kind in {pkOption, pkGreedyRep, pkGreedyAny, pkGreedyRepChar,
- pkGreedyRepSet}:
- # a* ? --> a*
- # a? ? --> a?
- result = a
- else:
- result.kind = pkOption
- result.sons = @[a]
-
-proc `*`*(a: TPeg): TPeg {.rtl, extern: "npegsGreedyRep".} =
- ## constructs a "greedy repetition" for the PEG `a`
- case a.kind
- of pkGreedyRep, pkGreedyRepChar, pkGreedyRepSet, pkGreedyAny, pkOption:
- assert false
- # produces endless loop!
- of pkChar:
- result.kind = pkGreedyRepChar
- result.ch = a.ch
- of pkCharChoice:
- result.kind = pkGreedyRepSet
- result.charChoice = a.charChoice # copying a reference suffices!
- of pkAny, pkAnyRune:
- result.kind = pkGreedyAny
- else:
- result.kind = pkGreedyRep
- result.sons = @[a]
-
-proc `!*`*(a: TPeg): TPeg {.rtl, extern: "npegsSearch".} =
- ## constructs a "search" for the PEG `a`
- result.kind = pkSearch
- result.sons = @[a]
-
-proc `!*\`*(a: TPeg): TPeg {.rtl,
- extern: "npgegsCapturedSearch".} =
- ## constructs a "captured search" for the PEG `a`
- result.kind = pkCapturedSearch
- result.sons = @[a]
-
-when false:
- proc contains(a: TPeg, k: TPegKind): bool =
- if a.kind == k: return true
- case a.kind
- of pkEmpty, pkAny, pkAnyRune, pkGreedyAny, pkNewLine, pkTerminal,
- pkTerminalIgnoreCase, pkTerminalIgnoreStyle, pkChar, pkGreedyRepChar,
- pkCharChoice, pkGreedyRepSet: discard
- of pkNonTerminal: return true
- else:
- for i in 0..a.sons.len-1:
- if contains(a.sons[i], k): return true
-
-proc `+`*(a: TPeg): TPeg {.rtl, extern: "npegsGreedyPosRep".} =
- ## constructs a "greedy positive repetition" with the PEG `a`
- return sequence(a, *a)
-
-proc `&`*(a: TPeg): TPeg {.rtl, extern: "npegsAndPredicate".} =
- ## constructs an "and predicate" with the PEG `a`
- result.kind = pkAndPredicate
- result.sons = @[a]
-
-proc `!`*(a: TPeg): TPeg {.rtl, extern: "npegsNotPredicate".} =
- ## constructs a "not predicate" with the PEG `a`
- result.kind = pkNotPredicate
- result.sons = @[a]
-
-proc any*: TPeg {.inline.} =
- ## constructs the PEG `any character`:idx: (``.``)
- result.kind = pkAny
-
-proc anyRune*: TPeg {.inline.} =
- ## constructs the PEG `any rune`:idx: (``_``)
- result.kind = pkAnyRune
-
-proc newLine*: TPeg {.inline.} =
- ## constructs the PEG `newline`:idx: (``\n``)
- result.kind = pkNewline
-
-proc UnicodeLetter*: TPeg {.inline.} =
- ## constructs the PEG ``\letter`` which matches any Unicode letter.
- result.kind = pkLetter
-
-proc UnicodeLower*: TPeg {.inline.} =
- ## constructs the PEG ``\lower`` which matches any Unicode lowercase letter.
- result.kind = pkLower
-
-proc UnicodeUpper*: TPeg {.inline.} =
- ## constructs the PEG ``\upper`` which matches any Unicode lowercase letter.
- result.kind = pkUpper
-
-proc UnicodeTitle*: TPeg {.inline.} =
- ## constructs the PEG ``\title`` which matches any Unicode title letter.
- result.kind = pkTitle
-
-proc UnicodeWhitespace*: TPeg {.inline.} =
- ## constructs the PEG ``\white`` which matches any Unicode
- ## whitespace character.
- result.kind = pkWhitespace
-
-proc startAnchor*: TPeg {.inline.} =
- ## constructs the PEG ``^`` which matches the start of the input.
- result.kind = pkStartAnchor
-
-proc endAnchor*: TPeg {.inline.} =
- ## constructs the PEG ``$`` which matches the end of the input.
- result = !any()
-
-proc capture*(a: TPeg): TPeg {.rtl, extern: "npegsCapture".} =
- ## constructs a capture with the PEG `a`
- result.kind = pkCapture
- result.sons = @[a]
-
-proc backref*(index: range[1..MaxSubPatterns]): TPeg {.
- rtl, extern: "npegs$1".} =
- ## constructs a back reference of the given `index`. `index` starts counting
- ## from 1.
- result.kind = pkBackRef
- result.index = index-1
-
-proc backrefIgnoreCase*(index: range[1..MaxSubPatterns]): TPeg {.
- rtl, extern: "npegs$1".} =
- ## constructs a back reference of the given `index`. `index` starts counting
- ## from 1. Ignores case for matching.
- result.kind = pkBackRefIgnoreCase
- result.index = index-1
-
-proc backrefIgnoreStyle*(index: range[1..MaxSubPatterns]): TPeg {.
- rtl, extern: "npegs$1".}=
- ## constructs a back reference of the given `index`. `index` starts counting
- ## from 1. Ignores style for matching.
- result.kind = pkBackRefIgnoreStyle
- result.index = index-1
-
-proc spaceCost(n: TPeg): int =
- case n.kind
- of pkEmpty: discard
- of pkTerminal, pkTerminalIgnoreCase, pkTerminalIgnoreStyle, pkChar,
- pkGreedyRepChar, pkCharChoice, pkGreedyRepSet,
- pkAny..pkWhitespace, pkGreedyAny:
- result = 1
- of pkNonTerminal:
- # we cannot inline a rule with a non-terminal
- result = InlineThreshold+1
- else:
- for i in 0..n.len-1:
- inc(result, spaceCost(n.sons[i]))
- if result >= InlineThreshold: break
-
-proc nonterminal*(n: PNonTerminal): TPeg {.
- rtl, extern: "npegs$1".} =
- ## constructs a PEG that consists of the nonterminal symbol
- assert n != nil
- if ntDeclared in n.flags and spaceCost(n.rule) < InlineThreshold:
- when false: echo "inlining symbol: ", n.name
- result = n.rule # inlining of rule enables better optimizations
- else:
- result.kind = pkNonTerminal
- result.nt = n
-
-proc newNonTerminal*(name: string, line, column: int): PNonTerminal {.
- rtl, extern: "npegs$1".} =
- ## constructs a nonterminal symbol
- new(result)
- result.name = name
- result.line = line
- result.col = column
-
-template letters*: TPeg =
- ## expands to ``charset({'A'..'Z', 'a'..'z'})``
- charset({'A'..'Z', 'a'..'z'})
-
-template digits*: TPeg =
- ## expands to ``charset({'0'..'9'})``
- charset({'0'..'9'})
-
-template whitespace*: TPeg =
- ## expands to ``charset({' ', '\9'..'\13'})``
- charset({' ', '\9'..'\13'})
-
-template identChars*: TPeg =
- ## expands to ``charset({'a'..'z', 'A'..'Z', '0'..'9', '_'})``
- charset({'a'..'z', 'A'..'Z', '0'..'9', '_'})
-
-template identStartChars*: TPeg =
- ## expands to ``charset({'A'..'Z', 'a'..'z', '_'})``
- charset({'a'..'z', 'A'..'Z', '_'})
-
-template ident*: TPeg =
- ## same as ``[a-zA-Z_][a-zA-z_0-9]*``; standard identifier
- sequence(charset({'a'..'z', 'A'..'Z', '_'}),
- *charset({'a'..'z', 'A'..'Z', '0'..'9', '_'}))
-
-template natural*: TPeg =
- ## same as ``\d+``
- +digits
-
-# ------------------------- debugging -----------------------------------------
-
-proc esc(c: char, reserved = {'\0'..'\255'}): string =
- case c
- of '\b': result = "\\b"
- of '\t': result = "\\t"
- of '\c': result = "\\c"
- of '\L': result = "\\l"
- of '\v': result = "\\v"
- of '\f': result = "\\f"
- of '\e': result = "\\e"
- of '\a': result = "\\a"
- of '\\': result = "\\\\"
- of 'a'..'z', 'A'..'Z', '0'..'9', '_': result = $c
- elif c < ' ' or c >= '\128': result = '\\' & $ord(c)
- elif c in reserved: result = '\\' & c
- else: result = $c
-
-proc singleQuoteEsc(c: char): string = return "'" & esc(c, {'\''}) & "'"
-
-proc singleQuoteEsc(str: string): string =
- result = "'"
- for c in items(str): add result, esc(c, {'\''})
- add result, '\''
-
-proc charSetEscAux(cc: set[char]): string =
- const reserved = {'^', '-', ']'}
- result = ""
- var c1 = 0
- while c1 <= 0xff:
- if chr(c1) in cc:
- var c2 = c1
- while c2 < 0xff and chr(succ(c2)) in cc: inc(c2)
- if c1 == c2:
- add result, esc(chr(c1), reserved)
- elif c2 == succ(c1):
- add result, esc(chr(c1), reserved) & esc(chr(c2), reserved)
- else:
- add result, esc(chr(c1), reserved) & '-' & esc(chr(c2), reserved)
- c1 = c2
- inc(c1)
-
-proc charSetEsc(cc: set[char]): string =
- if card(cc) >= 128+64:
- result = "[^" & charSetEscAux({'\1'..'\xFF'} - cc) & ']'
- else:
- result = '[' & charSetEscAux(cc) & ']'
-
-proc toStrAux(r: TPeg, res: var string) =
- case r.kind
- of pkEmpty: add(res, "()")
- of pkAny: add(res, '.')
- of pkAnyRune: add(res, '_')
- of pkLetter: add(res, "\\letter")
- of pkLower: add(res, "\\lower")
- of pkUpper: add(res, "\\upper")
- of pkTitle: add(res, "\\title")
- of pkWhitespace: add(res, "\\white")
-
- of pkNewline: add(res, "\\n")
- of pkTerminal: add(res, singleQuoteEsc(r.term))
- of pkTerminalIgnoreCase:
- add(res, 'i')
- add(res, singleQuoteEsc(r.term))
- of pkTerminalIgnoreStyle:
- add(res, 'y')
- add(res, singleQuoteEsc(r.term))
- of pkChar: add(res, singleQuoteEsc(r.ch))
- of pkCharChoice: add(res, charSetEsc(r.charChoice[]))
- of pkNonTerminal: add(res, r.nt.name)
- of pkSequence:
- add(res, '(')
- toStrAux(r.sons[0], res)
- for i in 1 .. high(r.sons):
- add(res, ' ')
- toStrAux(r.sons[i], res)
- add(res, ')')
- of pkOrderedChoice:
- add(res, '(')
- toStrAux(r.sons[0], res)
- for i in 1 .. high(r.sons):
- add(res, " / ")
- toStrAux(r.sons[i], res)
- add(res, ')')
- of pkGreedyRep:
- toStrAux(r.sons[0], res)
- add(res, '*')
- of pkGreedyRepChar:
- add(res, singleQuoteEsc(r.ch))
- add(res, '*')
- of pkGreedyRepSet:
- add(res, charSetEsc(r.charChoice[]))
- add(res, '*')
- of pkGreedyAny:
- add(res, ".*")
- of pkOption:
- toStrAux(r.sons[0], res)
- add(res, '?')
- of pkAndPredicate:
- add(res, '&')
- toStrAux(r.sons[0], res)
- of pkNotPredicate:
- add(res, '!')
- toStrAux(r.sons[0], res)
- of pkSearch:
- add(res, '@')
- toStrAux(r.sons[0], res)
- of pkCapturedSearch:
- add(res, "{@}")
- toStrAux(r.sons[0], res)
- of pkCapture:
- add(res, '{')
- toStrAux(r.sons[0], res)
- add(res, '}')
- of pkBackRef:
- add(res, '$')
- add(res, $r.index)
- of pkBackRefIgnoreCase:
- add(res, "i$")
- add(res, $r.index)
- of pkBackRefIgnoreStyle:
- add(res, "y$")
- add(res, $r.index)
- of pkRule:
- toStrAux(r.sons[0], res)
- add(res, " <- ")
- toStrAux(r.sons[1], res)
- of pkList:
- for i in 0 .. high(r.sons):
- toStrAux(r.sons[i], res)
- add(res, "\n")
- of pkStartAnchor:
- add(res, '^')
-
-proc `$` *(r: TPeg): string {.rtl, extern: "npegsToString".} =
- ## converts a PEG to its string representation
- result = ""
- toStrAux(r, result)
-
-# --------------------- core engine -------------------------------------------
-
-type
- TCaptures* {.final.} = object ## contains the captured substrings.
- matches: array[0..MaxSubpatterns-1, tuple[first, last: int]]
- ml: int
- origStart: int
-
-proc bounds*(c: TCaptures,
- i: range[0..MaxSubpatterns-1]): tuple[first, last: int] =
- ## returns the bounds ``[first..last]`` of the `i`'th capture.
- result = c.matches[i]
-
-when not useUnicode:
- type
- Rune = char
- template fastRuneAt(s, i, ch) =
- ch = s[i]
- inc(i)
- template runeLenAt(s, i): untyped = 1
-
- proc isAlpha(a: char): bool {.inline.} = return a in {'a'..'z','A'..'Z'}
- proc isUpper(a: char): bool {.inline.} = return a in {'A'..'Z'}
- proc isLower(a: char): bool {.inline.} = return a in {'a'..'z'}
- proc isTitle(a: char): bool {.inline.} = return false
- proc isWhiteSpace(a: char): bool {.inline.} = return a in {' ', '\9'..'\13'}
-
-proc rawMatch*(s: string, p: TPeg, start: int, c: var TCaptures): int {.
- rtl, extern: "npegs$1".} =
- ## low-level matching proc that implements the PEG interpreter. Use this
- ## for maximum efficiency (every other PEG operation ends up calling this
- ## proc).
- ## Returns -1 if it does not match, else the length of the match
- case p.kind
- of pkEmpty: result = 0 # match of length 0
- of pkAny:
- if s[start] != '\0': result = 1
- else: result = -1
- of pkAnyRune:
- if s[start] != '\0':
- result = runeLenAt(s, start)
- else:
- result = -1
- of pkLetter:
- if s[start] != '\0':
- var a: Rune
- result = start
- fastRuneAt(s, result, a)
- if isAlpha(a): dec(result, start)
- else: result = -1
- else:
- result = -1
- of pkLower:
- if s[start] != '\0':
- var a: Rune
- result = start
- fastRuneAt(s, result, a)
- if isLower(a): dec(result, start)
- else: result = -1
- else:
- result = -1
- of pkUpper:
- if s[start] != '\0':
- var a: Rune
- result = start
- fastRuneAt(s, result, a)
- if isUpper(a): dec(result, start)
- else: result = -1
- else:
- result = -1
- of pkTitle:
- if s[start] != '\0':
- var a: Rune
- result = start
- fastRuneAt(s, result, a)
- if isTitle(a): dec(result, start)
- else: result = -1
- else:
- result = -1
- of pkWhitespace:
- if s[start] != '\0':
- var a: Rune
- result = start
- fastRuneAt(s, result, a)
- if isWhitespace(a): dec(result, start)
- else: result = -1
- else:
- result = -1
- of pkGreedyAny:
- result = len(s) - start
- of pkNewLine:
- if s[start] == '\L': result = 1
- elif s[start] == '\C':
- if s[start+1] == '\L': result = 2
- else: result = 1
- else: result = -1
- of pkTerminal:
- result = len(p.term)
- for i in 0..result-1:
- if p.term[i] != s[start+i]:
- result = -1
- break
- of pkTerminalIgnoreCase:
- var
- i = 0
- a, b: Rune
- result = start
- while i < len(p.term):
- fastRuneAt(p.term, i, a)
- fastRuneAt(s, result, b)
- if toLower(a) != toLower(b):
- result = -1
- break
- dec(result, start)
- of pkTerminalIgnoreStyle:
- var
- i = 0
- a, b: Rune
- result = start
- while i < len(p.term):
- while true:
- fastRuneAt(p.term, i, a)
- if a != Rune('_'): break
- while true:
- fastRuneAt(s, result, b)
- if b != Rune('_'): break
- if toLower(a) != toLower(b):
- result = -1
- break
- dec(result, start)
- of pkChar:
- if p.ch == s[start]: result = 1
- else: result = -1
- of pkCharChoice:
- if contains(p.charChoice[], s[start]): result = 1
- else: result = -1
- of pkNonTerminal:
- var oldMl = c.ml
- when false: echo "enter: ", p.nt.name
- result = rawMatch(s, p.nt.rule, start, c)
- when false: echo "leave: ", p.nt.name
- if result < 0: c.ml = oldMl
- of pkSequence:
- var oldMl = c.ml
- result = 0
- assert(not isNil(p.sons))
- for i in 0..high(p.sons):
- var x = rawMatch(s, p.sons[i], start+result, c)
- if x < 0:
- c.ml = oldMl
- result = -1
- break
- else: inc(result, x)
- of pkOrderedChoice:
- var oldMl = c.ml
- for i in 0..high(p.sons):
- result = rawMatch(s, p.sons[i], start, c)
- if result >= 0: break
- c.ml = oldMl
- of pkSearch:
- var oldMl = c.ml
- result = 0
- while start+result < s.len:
- var x = rawMatch(s, p.sons[0], start+result, c)
- if x >= 0:
- inc(result, x)
- return
- inc(result)
- result = -1
- c.ml = oldMl
- of pkCapturedSearch:
- var idx = c.ml # reserve a slot for the subpattern
- inc(c.ml)
- result = 0
- while start+result < s.len:
- var x = rawMatch(s, p.sons[0], start+result, c)
- if x >= 0:
- if idx < MaxSubpatterns:
- c.matches[idx] = (start, start+result-1)
- #else: silently ignore the capture
- inc(result, x)
- return
- inc(result)
- result = -1
- c.ml = idx
- of pkGreedyRep:
- result = 0
- while true:
- var x = rawMatch(s, p.sons[0], start+result, c)
- # if x == 0, we have an endless loop; so the correct behaviour would be
- # not to break. But endless loops can be easily introduced:
- # ``(comment / \w*)*`` is such an example. Breaking for x == 0 does the
- # expected thing in this case.
- if x <= 0: break
- inc(result, x)
- of pkGreedyRepChar:
- result = 0
- var ch = p.ch
- while ch == s[start+result]: inc(result)
- of pkGreedyRepSet:
- result = 0
- while contains(p.charChoice[], s[start+result]): inc(result)
- of pkOption:
- result = max(0, rawMatch(s, p.sons[0], start, c))
- of pkAndPredicate:
- var oldMl = c.ml
- result = rawMatch(s, p.sons[0], start, c)
- if result >= 0: result = 0 # do not consume anything
- else: c.ml = oldMl
- of pkNotPredicate:
- var oldMl = c.ml
- result = rawMatch(s, p.sons[0], start, c)
- if result < 0: result = 0
- else:
- c.ml = oldMl
- result = -1
- of pkCapture:
- var idx = c.ml # reserve a slot for the subpattern
- inc(c.ml)
- result = rawMatch(s, p.sons[0], start, c)
- if result >= 0:
- if idx < MaxSubpatterns:
- c.matches[idx] = (start, start+result-1)
- #else: silently ignore the capture
- else:
- c.ml = idx
- of pkBackRef..pkBackRefIgnoreStyle:
- if p.index >= c.ml: return -1
- var (a, b) = c.matches[p.index]
- var n: TPeg
- n.kind = succ(pkTerminal, ord(p.kind)-ord(pkBackRef))
- n.term = s.substr(a, b)
- result = rawMatch(s, n, start, c)
- of pkStartAnchor:
- if c.origStart == start: result = 0
- else: result = -1
- of pkRule, pkList: assert false
-
-proc match*(s: string, pattern: TPeg, matches: var openarray[string],
- start = 0): bool {.rtl, extern: "npegs$1Capture".} =
- ## returns ``true`` if ``s[start..]`` matches the ``pattern`` and
- ## the captured substrings in the array ``matches``. If it does not
- ## match, nothing is written into ``matches`` and ``false`` is
- ## returned.
- var c: TCaptures
- c.origStart = start
- result = rawMatch(s, pattern, start, c) == len(s)-start
- if result:
- for i in 0..c.ml-1:
- matches[i] = substr(s, c.matches[i][0], c.matches[i][1])
-
-proc match*(s: string, pattern: TPeg,
- start = 0): bool {.rtl, extern: "npegs$1".} =
- ## returns ``true`` if ``s`` matches the ``pattern`` beginning from ``start``.
- var c: TCaptures
- c.origStart = start
- result = rawMatch(s, pattern, start, c) == len(s)-start
-
-proc matchLen*(s: string, pattern: TPeg, matches: var openarray[string],
- start = 0): int {.rtl, extern: "npegs$1Capture".} =
- ## the same as ``match``, but it returns the length of the match,
- ## if there is no match, -1 is returned. Note that a match length
- ## of zero can happen. It's possible that a suffix of `s` remains
- ## that does not belong to the match.
- var c: TCaptures
- c.origStart = start
- result = rawMatch(s, pattern, start, c)
- if result >= 0:
- for i in 0..c.ml-1:
- matches[i] = substr(s, c.matches[i][0], c.matches[i][1])
-
-proc matchLen*(s: string, pattern: TPeg,
- start = 0): int {.rtl, extern: "npegs$1".} =
- ## the same as ``match``, but it returns the length of the match,
- ## if there is no match, -1 is returned. Note that a match length
- ## of zero can happen. It's possible that a suffix of `s` remains
- ## that does not belong to the match.
- var c: TCaptures
- c.origStart = start
- result = rawMatch(s, pattern, start, c)
-
-proc find*(s: string, pattern: TPeg, matches: var openarray[string],
- start = 0): int {.rtl, extern: "npegs$1Capture".} =
- ## returns the starting position of ``pattern`` in ``s`` and the captured
- ## substrings in the array ``matches``. If it does not match, nothing
- ## is written into ``matches`` and -1 is returned.
- for i in start .. s.len-1:
- if matchLen(s, pattern, matches, i) >= 0: return i
- return -1
- # could also use the pattern here: (!P .)* P
-
-proc findBounds*(s: string, pattern: TPeg, matches: var openarray[string],
- start = 0): tuple[first, last: int] {.
- rtl, extern: "npegs$1Capture".} =
- ## returns the starting position and end position of ``pattern`` in ``s``
- ## and the captured
- ## substrings in the array ``matches``. If it does not match, nothing
- ## is written into ``matches`` and (-1,0) is returned.
- for i in start .. s.len-1:
- var L = matchLen(s, pattern, matches, i)
- if L >= 0: return (i, i+L-1)
- return (-1, 0)
-
-proc find*(s: string, pattern: TPeg,
- start = 0): int {.rtl, extern: "npegs$1".} =
- ## returns the starting position of ``pattern`` in ``s``. If it does not
- ## match, -1 is returned.
- for i in start .. s.len-1:
- if matchLen(s, pattern, i) >= 0: return i
- return -1
-
-iterator findAll*(s: string, pattern: TPeg, start = 0): string =
- ## yields all matching captures of pattern in `s`.
- var matches: array[0..MaxSubpatterns-1, string]
- var i = start
- while i < s.len:
- var L = matchLen(s, pattern, matches, i)
- if L < 0: break
- for k in 0..MaxSubPatterns-1:
- if isNil(matches[k]): break
- yield matches[k]
- inc(i, L)
-
-proc findAll*(s: string, pattern: TPeg, start = 0): seq[string] {.
- rtl, extern: "npegs$1".} =
- ## returns all matching captures of pattern in `s`.
- ## If it does not match, @[] is returned.
- accumulateResult(findAll(s, pattern, start))
-
-template `=~`*(s: string, pattern: TPeg): untyped =
- ## This calls ``match`` with an implicit declared ``matches`` array that
- ## can be used in the scope of the ``=~`` call:
- ##
- ## .. code-block:: nim
- ##
- ## if line =~ peg"\s* {\w+} \s* '=' \s* {\w+}":
- ## # matches a key=value pair:
- ## echo("Key: ", matches[0])
- ## echo("Value: ", matches[1])
- ## elif line =~ peg"\s*{'#'.*}":
- ## # matches a comment
- ## # note that the implicit ``matches`` array is different from the
- ## # ``matches`` array of the first branch
- ## echo("comment: ", matches[0])
- ## else:
- ## echo("syntax error")
- ##
- when not declaredInScope(matches):
- var matches {.inject.}: array[0..MaxSubpatterns-1, string]
- match(s, pattern, matches)
-
-# ------------------------- more string handling ------------------------------
-
-proc contains*(s: string, pattern: TPeg, start = 0): bool {.
- rtl, extern: "npegs$1".} =
- ## same as ``find(s, pattern, start) >= 0``
- return find(s, pattern, start) >= 0
-
-proc contains*(s: string, pattern: TPeg, matches: var openArray[string],
- start = 0): bool {.rtl, extern: "npegs$1Capture".} =
- ## same as ``find(s, pattern, matches, start) >= 0``
- return find(s, pattern, matches, start) >= 0
-
-proc startsWith*(s: string, prefix: TPeg, start = 0): bool {.
- rtl, extern: "npegs$1".} =
- ## returns true if `s` starts with the pattern `prefix`
- result = matchLen(s, prefix, start) >= 0
-
-proc endsWith*(s: string, suffix: TPeg, start = 0): bool {.
- rtl, extern: "npegs$1".} =
- ## returns true if `s` ends with the pattern `prefix`
- for i in start .. s.len-1:
- if matchLen(s, suffix, i) == s.len - i: return true
-
-proc replacef*(s: string, sub: TPeg, by: string): string {.
- rtl, extern: "npegs$1".} =
- ## Replaces `sub` in `s` by the string `by`. Captures can be accessed in `by`
- ## with the notation ``$i`` and ``$#`` (see strutils.`%`). Examples:
- ##
- ## .. code-block:: nim
- ## "var1=key; var2=key2".replace(peg"{\ident}'='{\ident}", "$1<-$2$2")
- ##
- ## Results in:
- ##
- ## .. code-block:: nim
- ##
- ## "var1<-keykey; val2<-key2key2"
- result = ""
- var i = 0
- var caps: array[0..MaxSubpatterns-1, string]
- while i < s.len:
- var x = matchLen(s, sub, caps, i)
- if x <= 0:
- add(result, s[i])
- inc(i)
- else:
- addf(result, by, caps)
- inc(i, x)
- add(result, substr(s, i))
-
-proc replace*(s: string, sub: TPeg, by = ""): string {.
- rtl, extern: "npegs$1".} =
- ## Replaces `sub` in `s` by the string `by`. Captures cannot be accessed
- ## in `by`.
- result = ""
- var i = 0
- var caps: array[0..MaxSubpatterns-1, string]
- while i < s.len:
- var x = matchLen(s, sub, caps, i)
- if x <= 0:
- add(result, s[i])
- inc(i)
- else:
- addf(result, by, caps)
- inc(i, x)
- add(result, substr(s, i))
-
-proc parallelReplace*(s: string, subs: varargs[
- tuple[pattern: TPeg, repl: string]]): string {.
- rtl, extern: "npegs$1".} =
- ## Returns a modified copy of `s` with the substitutions in `subs`
- ## applied in parallel.
- result = ""
- var i = 0
- var caps: array[0..MaxSubpatterns-1, string]
- while i < s.len:
- block searchSubs:
- for j in 0..high(subs):
- var x = matchLen(s, subs[j][0], caps, i)
- if x > 0:
- addf(result, subs[j][1], caps)
- inc(i, x)
- break searchSubs
- add(result, s[i])
- inc(i)
- # copy the rest:
- add(result, substr(s, i))
-
-proc transformFile*(infile, outfile: string,
- subs: varargs[tuple[pattern: TPeg, repl: string]]) {.
- rtl, extern: "npegs$1".} =
- ## reads in the file `infile`, performs a parallel replacement (calls
- ## `parallelReplace`) and writes back to `outfile`. Calls ``quit`` if an
- ## error occurs. This is supposed to be used for quick scripting.
- var x = readFile(infile)
- if not isNil(x):
- var f: File
- if open(f, outfile, fmWrite):
- write(f, x.parallelReplace(subs))
- close(f)
- else:
- quit("cannot open for writing: " & outfile)
- else:
- quit("cannot open for reading: " & infile)
-
-iterator split*(s: string, sep: TPeg): string =
- ## Splits the string `s` into substrings.
- ##
- ## Substrings are separated by the PEG `sep`.
- ## Examples:
- ##
- ## .. code-block:: nim
- ## for word in split("00232this02939is39an22example111", peg"\d+"):
- ## writeLine(stdout, word)
- ##
- ## Results in:
- ##
- ## .. code-block:: nim
- ## "this"
- ## "is"
- ## "an"
- ## "example"
- ##
- var
- first = 0
- last = 0
- while last < len(s):
- var x = matchLen(s, sep, last)
- if x > 0: inc(last, x)
- first = last
- while last < len(s):
- inc(last)
- x = matchLen(s, sep, last)
- if x > 0: break
- if first < last:
- yield substr(s, first, last-1)
-
-proc split*(s: string, sep: TPeg): seq[string] {.
- rtl, extern: "npegs$1".} =
- ## Splits the string `s` into substrings.
- accumulateResult(split(s, sep))
-
-# ------------------- scanner -------------------------------------------------
-
-type
- TModifier = enum
- modNone,
- modVerbatim,
- modIgnoreCase,
- modIgnoreStyle
- TTokKind = enum ## enumeration of all tokens
- tkInvalid, ## invalid token
- tkEof, ## end of file reached
- tkAny, ## .
- tkAnyRune, ## _
- tkIdentifier, ## abc
- tkStringLit, ## "abc" or 'abc'
- tkCharSet, ## [^A-Z]
- tkParLe, ## '('
- tkParRi, ## ')'
- tkCurlyLe, ## '{'
- tkCurlyRi, ## '}'
- tkCurlyAt, ## '{@}'
- tkArrow, ## '<-'
- tkBar, ## '/'
- tkStar, ## '*'
- tkPlus, ## '+'
- tkAmp, ## '&'
- tkNot, ## '!'
- tkOption, ## '?'
- tkAt, ## '@'
- tkBuiltin, ## \identifier
- tkEscaped, ## \\
- tkBackref, ## '$'
- tkDollar, ## '$'
- tkHat ## '^'
-
- TToken {.final.} = object ## a token
- kind: TTokKind ## the type of the token
- modifier: TModifier
- literal: string ## the parsed (string) literal
- charset: set[char] ## if kind == tkCharSet
- index: int ## if kind == tkBackref
-
- TPegLexer {.inheritable.} = object ## the lexer object.
- bufpos: int ## the current position within the buffer
- buf: cstring ## the buffer itself
- lineNumber: int ## the current line number
- lineStart: int ## index of last line start in buffer
- colOffset: int ## column to add
- filename: string
-
-const
- tokKindToStr: array[TTokKind, string] = [
- "invalid", "[EOF]", ".", "_", "identifier", "string literal",
- "character set", "(", ")", "{", "}", "{@}",
- "<-", "/", "*", "+", "&", "!", "?",
- "@", "built-in", "escaped", "$", "$", "^"
- ]
-
-proc HandleCR(L: var TPegLexer, pos: int): int =
- assert(L.buf[pos] == '\c')
- inc(L.linenumber)
- result = pos+1
- if L.buf[result] == '\L': inc(result)
- L.lineStart = result
-
-proc HandleLF(L: var TPegLexer, pos: int): int =
- assert(L.buf[pos] == '\L')
- inc(L.linenumber)
- result = pos+1
- L.lineStart = result
-
-proc init(L: var TPegLexer, input, filename: string, line = 1, col = 0) =
- L.buf = input
- L.bufpos = 0
- L.lineNumber = line
- L.colOffset = col
- L.lineStart = 0
- L.filename = filename
-
-proc getColumn(L: TPegLexer): int {.inline.} =
- result = abs(L.bufpos - L.lineStart) + L.colOffset
-
-proc getLine(L: TPegLexer): int {.inline.} =
- result = L.linenumber
-
-proc errorStr(L: TPegLexer, msg: string, line = -1, col = -1): string =
- var line = if line < 0: getLine(L) else: line
- var col = if col < 0: getColumn(L) else: col
- result = "$1($2, $3) Error: $4" % [L.filename, $line, $col, msg]
-
-proc handleHexChar(c: var TPegLexer, xi: var int) =
- case c.buf[c.bufpos]
- of '0'..'9':
- xi = (xi shl 4) or (ord(c.buf[c.bufpos]) - ord('0'))
- inc(c.bufpos)
- of 'a'..'f':
- xi = (xi shl 4) or (ord(c.buf[c.bufpos]) - ord('a') + 10)
- inc(c.bufpos)
- of 'A'..'F':
- xi = (xi shl 4) or (ord(c.buf[c.bufpos]) - ord('A') + 10)
- inc(c.bufpos)
- else: discard
-
-proc getEscapedChar(c: var TPegLexer, tok: var TToken) =
- inc(c.bufpos)
- case c.buf[c.bufpos]
- of 'r', 'R', 'c', 'C':
- add(tok.literal, '\c')
- inc(c.bufpos)
- of 'l', 'L':
- add(tok.literal, '\L')
- inc(c.bufpos)
- of 'f', 'F':
- add(tok.literal, '\f')
- inc(c.bufpos)
- of 'e', 'E':
- add(tok.literal, '\e')
- inc(c.bufpos)
- of 'a', 'A':
- add(tok.literal, '\a')
- inc(c.bufpos)
- of 'b', 'B':
- add(tok.literal, '\b')
- inc(c.bufpos)
- of 'v', 'V':
- add(tok.literal, '\v')
- inc(c.bufpos)
- of 't', 'T':
- add(tok.literal, '\t')
- inc(c.bufpos)
- of 'x', 'X':
- inc(c.bufpos)
- var xi = 0
- handleHexChar(c, xi)
- handleHexChar(c, xi)
- if xi == 0: tok.kind = tkInvalid
- else: add(tok.literal, chr(xi))
- of '0'..'9':
- var val = ord(c.buf[c.bufpos]) - ord('0')
- inc(c.bufpos)
- var i = 1
- while (i <= 3) and (c.buf[c.bufpos] in {'0'..'9'}):
- val = val * 10 + ord(c.buf[c.bufpos]) - ord('0')
- inc(c.bufpos)
- inc(i)
- if val > 0 and val <= 255: add(tok.literal, chr(val))
- else: tok.kind = tkInvalid
- of '\0'..'\31':
- tok.kind = tkInvalid
- elif c.buf[c.bufpos] in strutils.Letters:
- tok.kind = tkInvalid
- else:
- add(tok.literal, c.buf[c.bufpos])
- inc(c.bufpos)
-
-proc skip(c: var TPegLexer) =
- var pos = c.bufpos
- var buf = c.buf
- while true:
- case buf[pos]
- of ' ', '\t':
- inc(pos)
- of '#':
- while not (buf[pos] in {'\c', '\L', '\0'}): inc(pos)
- of '\c':
- pos = HandleCR(c, pos)
- buf = c.buf
- of '\L':
- pos = HandleLF(c, pos)
- buf = c.buf
- else:
- break # EndOfFile also leaves the loop
- c.bufpos = pos
-
-proc getString(c: var TPegLexer, tok: var TToken) =
- tok.kind = tkStringLit
- var pos = c.bufPos + 1
- var buf = c.buf
- var quote = buf[pos-1]
- while true:
- case buf[pos]
- of '\\':
- c.bufpos = pos
- getEscapedChar(c, tok)
- pos = c.bufpos
- of '\c', '\L', '\0':
- tok.kind = tkInvalid
- break
- elif buf[pos] == quote:
- inc(pos)
- break
- else:
- add(tok.literal, buf[pos])
- inc(pos)
- c.bufpos = pos
-
-proc getDollar(c: var TPegLexer, tok: var TToken) =
- var pos = c.bufPos + 1
- var buf = c.buf
- if buf[pos] in {'0'..'9'}:
- tok.kind = tkBackref
- tok.index = 0
- while buf[pos] in {'0'..'9'}:
- tok.index = tok.index * 10 + ord(buf[pos]) - ord('0')
- inc(pos)
- else:
- tok.kind = tkDollar
- c.bufpos = pos
-
-proc getCharSet(c: var TPegLexer, tok: var TToken) =
- tok.kind = tkCharSet
- tok.charset = {}
- var pos = c.bufPos + 1
- var buf = c.buf
- var caret = false
- if buf[pos] == '^':
- inc(pos)
- caret = true
- while true:
- var ch: char
- case buf[pos]
- of ']':
- inc(pos)
- break
- of '\\':
- c.bufpos = pos
- getEscapedChar(c, tok)
- pos = c.bufpos
- ch = tok.literal[tok.literal.len-1]
- of '\C', '\L', '\0':
- tok.kind = tkInvalid
- break
- else:
- ch = buf[pos]
- inc(pos)
- incl(tok.charset, ch)
- if buf[pos] == '-':
- if buf[pos+1] == ']':
- incl(tok.charset, '-')
- inc(pos)
- else:
- inc(pos)
- var ch2: char
- case buf[pos]
- of '\\':
- c.bufpos = pos
- getEscapedChar(c, tok)
- pos = c.bufpos
- ch2 = tok.literal[tok.literal.len-1]
- of '\C', '\L', '\0':
- tok.kind = tkInvalid
- break
- else:
- ch2 = buf[pos]
- inc(pos)
- for i in ord(ch)+1 .. ord(ch2):
- incl(tok.charset, chr(i))
- c.bufpos = pos
- if caret: tok.charset = {'\1'..'\xFF'} - tok.charset
-
-proc getSymbol(c: var TPegLexer, tok: var TToken) =
- var pos = c.bufpos
- var buf = c.buf
- while true:
- add(tok.literal, buf[pos])
- inc(pos)
- if buf[pos] notin strutils.IdentChars: break
- c.bufpos = pos
- tok.kind = tkIdentifier
-
-proc getBuiltin(c: var TPegLexer, tok: var TToken) =
- if c.buf[c.bufpos+1] in strutils.Letters:
- inc(c.bufpos)
- getSymbol(c, tok)
- tok.kind = tkBuiltin
- else:
- tok.kind = tkEscaped
- getEscapedChar(c, tok) # may set tok.kind to tkInvalid
-
-proc getTok(c: var TPegLexer, tok: var TToken) =
- tok.kind = tkInvalid
- tok.modifier = modNone
- setlen(tok.literal, 0)
- skip(c)
- case c.buf[c.bufpos]
- of '{':
- inc(c.bufpos)
- if c.buf[c.bufpos] == '@' and c.buf[c.bufpos+1] == '}':
- tok.kind = tkCurlyAt
- inc(c.bufpos, 2)
- add(tok.literal, "{@}")
- else:
- tok.kind = tkCurlyLe
- add(tok.literal, '{')
- of '}':
- tok.kind = tkCurlyRi
- inc(c.bufpos)
- add(tok.literal, '}')
- of '[':
- getCharset(c, tok)
- of '(':
- tok.kind = tkParLe
- inc(c.bufpos)
- add(tok.literal, '(')
- of ')':
- tok.kind = tkParRi
- inc(c.bufpos)
- add(tok.literal, ')')
- of '.':
- tok.kind = tkAny
- inc(c.bufpos)
- add(tok.literal, '.')
- of '_':
- tok.kind = tkAnyRune
- inc(c.bufpos)
- add(tok.literal, '_')
- of '\\':
- getBuiltin(c, tok)
- of '\'', '"': getString(c, tok)
- of '$': getDollar(c, tok)
- of '\0':
- tok.kind = tkEof
- tok.literal = "[EOF]"
- of 'a'..'z', 'A'..'Z', '\128'..'\255':
- getSymbol(c, tok)
- if c.buf[c.bufpos] in {'\'', '"'} or
- c.buf[c.bufpos] == '$' and c.buf[c.bufpos+1] in {'0'..'9'}:
- case tok.literal
- of "i": tok.modifier = modIgnoreCase
- of "y": tok.modifier = modIgnoreStyle
- of "v": tok.modifier = modVerbatim
- else: discard
- setLen(tok.literal, 0)
- if c.buf[c.bufpos] == '$':
- getDollar(c, tok)
- else:
- getString(c, tok)
- if tok.modifier == modNone: tok.kind = tkInvalid
- of '+':
- tok.kind = tkPlus
- inc(c.bufpos)
- add(tok.literal, '+')
- of '*':
- tok.kind = tkStar
- inc(c.bufpos)
- add(tok.literal, '+')
- of '<':
- if c.buf[c.bufpos+1] == '-':
- inc(c.bufpos, 2)
- tok.kind = tkArrow
- add(tok.literal, "<-")
- else:
- add(tok.literal, '<')
- of '/':
- tok.kind = tkBar
- inc(c.bufpos)
- add(tok.literal, '/')
- of '?':
- tok.kind = tkOption
- inc(c.bufpos)
- add(tok.literal, '?')
- of '!':
- tok.kind = tkNot
- inc(c.bufpos)
- add(tok.literal, '!')
- of '&':
- tok.kind = tkAmp
- inc(c.bufpos)
- add(tok.literal, '!')
- of '@':
- tok.kind = tkAt
- inc(c.bufpos)
- add(tok.literal, '@')
- if c.buf[c.bufpos] == '@':
- tok.kind = tkCurlyAt
- inc(c.bufpos)
- add(tok.literal, '@')
- of '^':
- tok.kind = tkHat
- inc(c.bufpos)
- add(tok.literal, '^')
- else:
- add(tok.literal, c.buf[c.bufpos])
- inc(c.bufpos)
-
-proc arrowIsNextTok(c: TPegLexer): bool =
- # the only look ahead we need
- var pos = c.bufpos
- while c.buf[pos] in {'\t', ' '}: inc(pos)
- result = c.buf[pos] == '<' and c.buf[pos+1] == '-'
-
-# ----------------------------- parser ----------------------------------------
-
-type
- EInvalidPeg* = object of ValueError ## raised if an invalid
- ## PEG has been detected
- TPegParser = object of TPegLexer ## the PEG parser object
- tok: TToken
- nonterms: seq[PNonTerminal]
- modifier: TModifier
- captures: int
- identIsVerbatim: bool
- skip: TPeg
-
-proc pegError(p: TPegParser, msg: string, line = -1, col = -1) =
- var e: ref EInvalidPeg
- new(e)
- e.msg = errorStr(p, msg, line, col)
- raise e
-
-proc getTok(p: var TPegParser) =
- getTok(p, p.tok)
- if p.tok.kind == tkInvalid: pegError(p, "invalid token")
-
-proc eat(p: var TPegParser, kind: TTokKind) =
- if p.tok.kind == kind: getTok(p)
- else: pegError(p, tokKindToStr[kind] & " expected")
-
-proc parseExpr(p: var TPegParser): TPeg
-
-proc getNonTerminal(p: var TPegParser, name: string): PNonTerminal =
- for i in 0..high(p.nonterms):
- result = p.nonterms[i]
- if cmpIgnoreStyle(result.name, name) == 0: return
- # forward reference:
- result = newNonTerminal(name, getLine(p), getColumn(p))
- add(p.nonterms, result)
-
-proc modifiedTerm(s: string, m: TModifier): TPeg =
- case m
- of modNone, modVerbatim: result = term(s)
- of modIgnoreCase: result = termIgnoreCase(s)
- of modIgnoreStyle: result = termIgnoreStyle(s)
-
-proc modifiedBackref(s: int, m: TModifier): TPeg =
- case m
- of modNone, modVerbatim: result = backRef(s)
- of modIgnoreCase: result = backRefIgnoreCase(s)
- of modIgnoreStyle: result = backRefIgnoreStyle(s)
-
-proc builtin(p: var TPegParser): TPeg =
- # do not use "y", "skip" or "i" as these would be ambiguous
- case p.tok.literal
- of "n": result = newLine()
- of "d": result = charset({'0'..'9'})
- of "D": result = charset({'\1'..'\xff'} - {'0'..'9'})
- of "s": result = charset({' ', '\9'..'\13'})
- of "S": result = charset({'\1'..'\xff'} - {' ', '\9'..'\13'})
- of "w": result = charset({'a'..'z', 'A'..'Z', '_', '0'..'9'})
- of "W": result = charset({'\1'..'\xff'} - {'a'..'z','A'..'Z','_','0'..'9'})
- of "a": result = charset({'a'..'z', 'A'..'Z'})
- of "A": result = charset({'\1'..'\xff'} - {'a'..'z', 'A'..'Z'})
- of "ident": result = tpegs.ident
- of "letter": result = UnicodeLetter()
- of "upper": result = UnicodeUpper()
- of "lower": result = UnicodeLower()
- of "title": result = UnicodeTitle()
- of "white": result = UnicodeWhitespace()
- else: pegError(p, "unknown built-in: " & p.tok.literal)
-
-proc token(terminal: TPeg, p: TPegParser): TPeg =
- if p.skip.kind == pkEmpty: result = terminal
- else: result = sequence(p.skip, terminal)
-
-proc primary(p: var TPegParser): TPeg =
- case p.tok.kind
- of tkAmp:
- getTok(p)
- return &primary(p)
- of tkNot:
- getTok(p)
- return !primary(p)
- of tkAt:
- getTok(p)
- return !*primary(p)
- of tkCurlyAt:
- getTok(p)
- return !*\primary(p).token(p)
- else: discard
- case p.tok.kind
- of tkIdentifier:
- if p.identIsVerbatim:
- var m = p.tok.modifier
- if m == modNone: m = p.modifier
- result = modifiedTerm(p.tok.literal, m).token(p)
- getTok(p)
- elif not arrowIsNextTok(p):
- var nt = getNonTerminal(p, p.tok.literal)
- incl(nt.flags, ntUsed)
- result = nonTerminal(nt).token(p)
- getTok(p)
- else:
- pegError(p, "expression expected, but found: " & p.tok.literal)
- of tkStringLit:
- var m = p.tok.modifier
- if m == modNone: m = p.modifier
- result = modifiedTerm(p.tok.literal, m).token(p)
- getTok(p)
- of tkCharSet:
- if '\0' in p.tok.charset:
- pegError(p, "binary zero ('\\0') not allowed in character class")
- result = charset(p.tok.charset).token(p)
- getTok(p)
- of tkParLe:
- getTok(p)
- result = parseExpr(p)
- eat(p, tkParRi)
- of tkCurlyLe:
- getTok(p)
- result = capture(parseExpr(p)).token(p)
- eat(p, tkCurlyRi)
- inc(p.captures)
- of tkAny:
- result = any().token(p)
- getTok(p)
- of tkAnyRune:
- result = anyRune().token(p)
- getTok(p)
- of tkBuiltin:
- result = builtin(p).token(p)
- getTok(p)
- of tkEscaped:
- result = term(p.tok.literal[0]).token(p)
- getTok(p)
- of tkDollar:
- result = endAnchor()
- getTok(p)
- of tkHat:
- result = startAnchor()
- getTok(p)
- of tkBackref:
- var m = p.tok.modifier
- if m == modNone: m = p.modifier
- result = modifiedBackRef(p.tok.index, m).token(p)
- if p.tok.index < 0 or p.tok.index > p.captures:
- pegError(p, "invalid back reference index: " & $p.tok.index)
- getTok(p)
- else:
- pegError(p, "expression expected, but found: " & p.tok.literal)
- getTok(p) # we must consume a token here to prevent endless loops!
- while true:
- case p.tok.kind
- of tkOption:
- result = ?result
- getTok(p)
- of tkStar:
- result = *result
- getTok(p)
- of tkPlus:
- result = +result
- getTok(p)
- else: break
-
-proc seqExpr(p: var TPegParser): TPeg =
- result = primary(p)
- while true:
- case p.tok.kind
- of tkAmp, tkNot, tkAt, tkStringLit, tkCharset, tkParLe, tkCurlyLe,
- tkAny, tkAnyRune, tkBuiltin, tkEscaped, tkDollar, tkBackref,
- tkHat, tkCurlyAt:
- result = sequence(result, primary(p))
- of tkIdentifier:
- if not arrowIsNextTok(p):
- result = sequence(result, primary(p))
- else: break
- else: break
-
-proc parseExpr(p: var TPegParser): TPeg =
- result = seqExpr(p)
- while p.tok.kind == tkBar:
- getTok(p)
- result = result / seqExpr(p)
-
-proc parseRule(p: var TPegParser): PNonTerminal =
- if p.tok.kind == tkIdentifier and arrowIsNextTok(p):
- result = getNonTerminal(p, p.tok.literal)
- if ntDeclared in result.flags:
- pegError(p, "attempt to redefine: " & result.name)
- result.line = getLine(p)
- result.col = getColumn(p)
- getTok(p)
- eat(p, tkArrow)
- result.rule = parseExpr(p)
- incl(result.flags, ntDeclared) # NOW inlining may be attempted
- else:
- pegError(p, "rule expected, but found: " & p.tok.literal)
-
-proc rawParse(p: var TPegParser): TPeg =
- ## parses a rule or a PEG expression
- while p.tok.kind == tkBuiltin:
- case p.tok.literal
- of "i":
- p.modifier = modIgnoreCase
- getTok(p)
- of "y":
- p.modifier = modIgnoreStyle
- getTok(p)
- of "skip":
- getTok(p)
- p.skip = ?primary(p)
- else: break
- if p.tok.kind == tkIdentifier and arrowIsNextTok(p):
- result = parseRule(p).rule
- while p.tok.kind != tkEof:
- discard parseRule(p)
- else:
- p.identIsVerbatim = true
- result = parseExpr(p)
- if p.tok.kind != tkEof:
- pegError(p, "EOF expected, but found: " & p.tok.literal)
- for i in 0..high(p.nonterms):
- var nt = p.nonterms[i]
- if ntDeclared notin nt.flags:
- pegError(p, "undeclared identifier: " & nt.name, nt.line, nt.col)
- elif ntUsed notin nt.flags and i > 0:
- pegError(p, "unused rule: " & nt.name, nt.line, nt.col)
-
-proc parsePeg*(pattern: string, filename = "pattern", line = 1, col = 0): TPeg =
- ## constructs a TPeg object from `pattern`. `filename`, `line`, `col` are
- ## used for error messages, but they only provide start offsets. `parsePeg`
- ## keeps track of line and column numbers within `pattern`.
- var p: TPegParser
- init(TPegLexer(p), pattern, filename, line, col)
- p.tok.kind = tkInvalid
- p.tok.modifier = modNone
- p.tok.literal = ""
- p.tok.charset = {}
- p.nonterms = @[]
- p.identIsVerbatim = false
- getTok(p)
- result = rawParse(p)
-
-proc peg*(pattern: string): TPeg =
- ## constructs a TPeg object from the `pattern`. The short name has been
- ## chosen to encourage its use as a raw string modifier::
- ##
- ## peg"{\ident} \s* '=' \s* {.*}"
- result = parsePeg(pattern, "pattern")
-
-proc escapePeg*(s: string): string =
- ## escapes `s` so that it is matched verbatim when used as a peg.
- result = ""
- var inQuote = false
- for c in items(s):
- case c
- of '\0'..'\31', '\'', '"', '\\':
- if inQuote:
- result.add('\'')
- inQuote = false
- result.add("\\x")
- result.add(toHex(ord(c), 2))
- else:
- if not inQuote:
- result.add('\'')
- inQuote = true
- result.add(c)
- if inQuote: result.add('\'')
-
-when isMainModule:
- doAssert escapePeg("abc''def'") == r"'abc'\x27\x27'def'\x27"
- #doAssert match("(a b c)", peg"'(' @ ')'")
- doAssert match("W_HI_Le", peg"\y 'while'")
- doAssert(not match("W_HI_L", peg"\y 'while'"))
- doAssert(not match("W_HI_Le", peg"\y v'while'"))
- doAssert match("W_HI_Le", peg"y'while'")
-
- doAssert($ +digits == $peg"\d+")
- doAssert "0158787".match(peg"\d+")
- doAssert "ABC 0232".match(peg"\w+\s+\d+")
- doAssert "ABC".match(peg"\d+ / \w+")
-
- for word in split("00232this02939is39an22example111", peg"\d+"):
- writeLine(stdout, word)
-
- doAssert matchLen("key", ident) == 3
-
- var pattern = sequence(ident, *whitespace, term('='), *whitespace, ident)
- doAssert matchLen("key1= cal9", pattern) == 11
-
- var ws = newNonTerminal("ws", 1, 1)
- ws.rule = *whitespace
-
- var expr = newNonTerminal("expr", 1, 1)
- expr.rule = sequence(capture(ident), *sequence(
- nonterminal(ws), term('+'), nonterminal(ws), nonterminal(expr)))
-
- var c: TCaptures
- var s = "a+b + c +d+e+f"
- doAssert rawMatch(s, expr.rule, 0, c) == len(s)
- var a = ""
- for i in 0..c.ml-1:
- a.add(substr(s, c.matches[i][0], c.matches[i][1]))
- doAssert a == "abcdef"
- #echo expr.rule
-
- #const filename = "lib/devel/peg/grammar.txt"
- #var grammar = parsePeg(newFileStream(filename, fmRead), filename)
- #echo "a <- [abc]*?".match(grammar)
- doAssert find("_____abc_______", term("abc"), 2) == 5
- doAssert match("_______ana", peg"A <- 'ana' / . A")
- doAssert match("abcs%%%", peg"A <- ..A / .A / '%'")
-
- if "abc" =~ peg"{'a'}'bc' 'xyz' / {\ident}":
- doAssert matches[0] == "abc"
- else:
- doAssert false
-
- var g2 = peg"""S <- A B / C D
- A <- 'a'+
- B <- 'b'+
- C <- 'c'+
- D <- 'd'+
- """
- doAssert($g2 == "((A B) / (C D))")
- doAssert match("cccccdddddd", g2)
- doAssert("var1=key; var2=key2".replacef(peg"{\ident}'='{\ident}", "$1<-$2$2") ==
- "var1<-keykey; var2<-key2key2")
- doAssert "var1=key; var2=key2".endsWith(peg"{\ident}'='{\ident}")
-
- if "aaaaaa" =~ peg"'aa' !. / ({'a'})+":
- doAssert matches[0] == "a"
- else:
- doAssert false
-
- block:
- var matches: array[0..2, string]
- if match("abcdefg", peg"c {d} ef {g}", matches, 2):
- doAssert matches[0] == "d"
- doAssert matches[1] == "g"
- else:
- doAssert false
-
- for x in findAll("abcdef", peg"{.}", 3):
- echo x
-
- if "f(a, b)" =~ peg"{[0-9]+} / ({\ident} '(' {@} ')')":
- doAssert matches[0] == "f"
- doAssert matches[1] == "a, b"
- else:
- doAssert false
-
- doAssert match("eine übersicht und außerdem", peg"(\letter \white*)+")
- # ß is not a lower cased letter?!
- doAssert match("eine übersicht und auerdem", peg"(\lower \white*)+")
- doAssert match("EINE ÜBERSICHT UND AUSSERDEM", peg"(\upper \white*)+")
- doAssert(not match("456678", peg"(\letter)+"))
-
- doAssert("var1 = key; var2 = key2".replacef(
- peg"\skip(\s*) {\ident}'='{\ident}", "$1<-$2$2") ==
- "var1<-keykey;var2<-key2key2")
-
- doAssert match("prefix/start", peg"^start$", 7)
-
- # tricky test to check for false aliasing:
- block:
- var a = term"key"
- echo($sequence(sequence(a, term"value"), *a))
-
diff --git a/tests/stdlib/twchartoutf8.nim b/tests/stdlib/twchartoutf8.nim
index b2f68ee327..a6602e3e39 100644
--- a/tests/stdlib/twchartoutf8.nim
+++ b/tests/stdlib/twchartoutf8.nim
@@ -30,7 +30,6 @@ else:
result = newString(size)
let res = WideCharToMultiByte(CP_UTF8, 0'i32, cast[LPWCSTR](addr(wc[0])), wclen,
cstring(result), size, cstring(nil), LPBOOL(nil))
- result[size] = chr(0)
doAssert size == res
proc testCP(wc: WideCString, lo, hi: int) =
diff --git a/tests/system/tnilconcats.nim b/tests/system/tnilconcats.nim
new file mode 100644
index 0000000000..ce059b7b0e
--- /dev/null
+++ b/tests/system/tnilconcats.nim
@@ -0,0 +1,25 @@
+discard """
+ output: '''@[nil, nil, nil, nil, nil, nil, nil, "meh"]'''
+ exitcode: "0"
+"""
+
+when true:
+ var ab: string
+ ab &= "more"
+
+ doAssert ab == "more"
+
+ var x: seq[string]
+
+ setLen(x, 7)
+
+ x.add "meh"
+
+ var s: string
+ var z = "abc"
+ var zz: string
+ s &= "foo" & z & zz
+
+ doAssert s == "fooabc"
+
+ echo x
diff --git a/tests/system/toString.nim b/tests/system/toString.nim
index ea9d6b05b8..37c678a748 100644
--- a/tests/system/toString.nim
+++ b/tests/system/toString.nim
@@ -51,3 +51,59 @@ import strutils
let arr = ['H','e','l','l','o',' ','W','o','r','l','d','!','\0']
doAssert $arr == "['H', 'e', 'l', 'l', 'o', ' ', 'W', 'o', 'r', 'l', 'd', '!', '\\x00']"
doAssert $cstring(unsafeAddr arr) == "Hello World!"
+
+proc takes(c: cstring) =
+ doAssert c == ""
+
+proc testm() =
+ var x: string
+ # nil is mapped to "":
+ takes(x)
+
+testm()
+
+# nil tests
+var xx: seq[string]
+var yy: string
+doAssert xx == @[]
+doAssert yy == ""
+
+proc bar(arg: cstring): void =
+ doAssert arg[0] == '\0'
+
+proc baz(arg: openarray[char]): void =
+ doAssert arg.len == 0
+
+proc stringCompare(): void =
+ var a,b,c,d,e,f,g: string
+ a.add 'a'
+ doAssert a == "a"
+ b.add "bee"
+ doAssert b == "bee"
+ b.add g
+ doAssert b == "bee"
+ c.add 123.456
+ doAssert c == "123.456"
+ d.add 123456
+ doAssert d == "123456"
+
+ doAssert e == ""
+ doAssert "" == e
+ doAssert nil == e
+ doAssert e == nil
+ doAssert f == g
+ doAssert "" == ""
+ doAssert "" == nil
+ doAssert nil == ""
+
+ g.setLen(10)
+ doAssert g == "\0\0\0\0\0\0\0\0\0\0"
+ doAssert "" != "\0\0\0\0\0\0\0\0\0\0"
+
+ var nilstring: string
+ bar(nilstring)
+ baz(nilstring)
+
+stringCompare()
+static:
+ stringCompare()
\ No newline at end of file
diff --git a/tests/typerel/t7600_1.nim b/tests/typerel/t7600_1.nim
new file mode 100644
index 0000000000..e3a5fefa2c
--- /dev/null
+++ b/tests/typerel/t7600_1.nim
@@ -0,0 +1,18 @@
+discard """
+errormsg: "type mismatch: got "
+nimout: '''t7600_1.nim(18, 6) Error: type mismatch: got
+but expected one of:
+proc test[T](x: Paper[T])
+
+expression: test tn'''
+"""
+
+type
+ Paper[T] = ref object of RootObj
+ thickness: T
+ Thin[T] = object of Paper[T]
+
+proc test[T](x: Paper[T]) = discard
+
+var tn = Thin[int]()
+test tn
diff --git a/tests/typerel/t7600_2.nim b/tests/typerel/t7600_2.nim
new file mode 100644
index 0000000000..7badb69cfb
--- /dev/null
+++ b/tests/typerel/t7600_2.nim
@@ -0,0 +1,17 @@
+discard """
+errormsg: "type mismatch: got "
+nimout: '''t7600_2.nim(17, 6) Error: type mismatch: got
+but expected one of:
+proc test(x: Paper)
+
+expression: test tn'''
+"""
+
+type
+ Paper = ref object of RootObj
+ Thin = object of Paper
+
+proc test(x: Paper) = discard
+
+var tn = Thin()
+test tn
diff --git a/tests/typerel/ttypelessemptyset.nim b/tests/typerel/ttypelessemptyset.nim
index 3e171387b8..5f49c33fdc 100644
--- a/tests/typerel/ttypelessemptyset.nim
+++ b/tests/typerel/ttypelessemptyset.nim
@@ -1,5 +1,5 @@
discard """
- errormsg: "internal error: invalid kind for last(tyEmpty)"
+ errormsg: "internal error: invalid kind for lastOrd(tyEmpty)"
"""
var q = false
discard (if q: {} else: {})
diff --git a/tools/finish.nim b/tools/finish.nim
index 2681f7ccf2..b5ef78b65b 100644
--- a/tools/finish.nim
+++ b/tools/finish.nim
@@ -52,14 +52,17 @@ when defined(windows):
proc askBool(m: string): bool =
stdout.write m
while true:
- let answer = stdin.readLine().normalize
- case answer
- of "y", "yes":
- return true
- of "n", "no":
- return false
- else:
- echo "Please type 'y' or 'n'"
+ try:
+ let answer = stdin.readLine().normalize
+ case answer
+ of "y", "yes":
+ return true
+ of "n", "no":
+ return false
+ else:
+ echo "Please type 'y' or 'n'"
+ except EOFError:
+ quit(1)
proc askNumber(m: string; a, b: int): int =
stdout.write m
@@ -99,10 +102,6 @@ when defined(windows):
proc addToPathEnv*(e: string) =
var p = tryGetUnicodeValue(r"Environment", "Path", HKEY_CURRENT_USER)
- if p.len == 0:
- p = tryGetUnicodeValue(
- r"SYSTEM\CurrentControlSet\Control\Session Manager\Environment",
- "Path", HKEY_LOCAL_MACHINE)
let x = if e.contains(Whitespace): "\"" & e & "\"" else: e
if p.len > 0:
p.add ";"
@@ -189,8 +188,10 @@ when defined(windows):
proc main() =
when defined(windows):
let desiredPath = expandFilename(getCurrentDir() / "bin")
- let p = getUnicodeValue(r"Environment", "Path",
- HKEY_CURRENT_USER)
+ let p = tryGetUnicodeValue(r"Environment", "Path",
+ HKEY_CURRENT_USER) & ";" & tryGetUnicodeValue(
+ r"System\CurrentControlSet\Control\Session Manager\Environment", "Path",
+ HKEY_LOCAL_MACHINE)
var alreadyInPath = false
var mingWchoices: seq[string] = @[]
var incompat: seq[string] = @[]
@@ -199,7 +200,7 @@ proc main() =
let y = try: expandFilename(if x[0] == '"' and x[^1] == '"':
substr(x, 1, x.len-2) else: x)
except: ""
- if y == desiredPath: alreadyInPath = true
+ if y.cmpIgnoreCase(desiredPath) == 0: alreadyInPath = true
if y.toLowerAscii.contains("mingw"):
if dirExists(y):
if checkGccArch(y): mingWchoices.add y
diff --git a/tools/nimgrep.nim b/tools/nimgrep.nim
index 4cfcbe9bc2..9cfd7a86f6 100644
--- a/tools/nimgrep.nim
+++ b/tools/nimgrep.nim
@@ -128,7 +128,7 @@ proc highlight(s, match, repl: string, t: tuple[first, last: int],
stdout.write("\n")
stdout.flushFile()
-proc processFile(pattern; filename: string) =
+proc processFile(pattern; filename: string; counter: var int) =
var filenameShown = false
template beforeHighlight =
if not filenameShown and optVerbose notin options and not oneline:
@@ -166,6 +166,7 @@ proc processFile(pattern; filename: string) =
var wholeMatch = buffer.substr(t.first, t.last)
beforeHighlight()
+ inc counter
if optReplace notin options:
highlight(buffer, wholeMatch, "", t, filename, line, showRepl=false)
else:
@@ -241,17 +242,17 @@ proc styleInsensitive(s: string): string =
addx()
else: addx()
-proc walker(pattern; dir: string) =
+proc walker(pattern; dir: string; counter: var int) =
for kind, path in walkDir(dir):
case kind
of pcFile:
if extensions.len == 0 or path.hasRightExt(extensions):
- processFile(pattern, path)
+ processFile(pattern, path, counter)
of pcDir:
if optRecursive in options:
- walker(pattern, path)
+ walker(pattern, path, counter)
else: discard
- if existsFile(dir): processFile(pattern, dir)
+ if existsFile(dir): processFile(pattern, dir, counter)
proc writeHelp() =
stdout.write(Usage)
@@ -321,6 +322,7 @@ if optStdin in options:
if pattern.len == 0:
writeHelp()
else:
+ var counter = 0
if filenames.len == 0:
filenames.add(os.getCurrentDir())
if optRegex notin options:
@@ -332,7 +334,7 @@ else:
pattern = "\\i " & pattern
let pegp = peg(pattern)
for f in items(filenames):
- walker(pegp, f)
+ walker(pegp, f, counter)
else:
var reflags = {reStudy, reExtended}
if optIgnoreStyle in options:
@@ -343,5 +345,6 @@ else:
reflags.incl reIgnoreCase
let rep = re(pattern, reflags)
for f in items(filenames):
- walker(rep, f)
-
+ walker(rep, f, counter)
+ if not oneline:
+ stdout.write($counter & " matches\n")